parkModel = $parkModel; $this->contractModel = $contractModel; $this->batchLogModel = $batchLogModel; } /** * SHJ-4C 室割当処理メイン実行 * * 処理フロー: * 【処理1】ゾーン情報取得 * 【判断1】割当判定 * 【処理2】バッチログ作成 * 【処理3】処理結果返却 * * @param int $parkId 駐輪場ID * @param int $ptypeId 駐輪分類ID * @param int $psectionId 車種区分ID * @return array 処理結果 */ public function executeRoomAllocation(int $parkId, int $ptypeId, int $psectionId): array { $batchLogId = null; try { // バッチ処理開始ログ作成(実際のコマンド名を記録) $batchLog = BatchLog::createBatchLog( 'shj4c', BatchLog::STATUS_START, [ 'park_id' => $parkId, 'ptype_id' => $ptypeId, 'psection_id' => $psectionId ], 'SHJ-4C 室割当処理開始' ); $batchLogId = $batchLog->id; Log::info('SHJ-4C 室割当処理開始', [ 'batch_log_id' => $batchLogId, 'park_id' => $parkId, 'ptype_id' => $ptypeId, 'psection_id' => $psectionId ]); // 【処理1】ゾーン情報取得 $zoneInfo = $this->getZoneInformation($parkId, $ptypeId, $psectionId); if (empty($zoneInfo)) { $message = '対象のゾーン情報が見つかりません'; // バッチログ更新(通用方法使用) $batchLog->update([ 'status' => BatchLog::STATUS_ERROR, 'end_time' => now(), 'message' => $message, 'error_details' => $message, 'error_count' => 1 ]); return [ 'success' => false, 'message' => $message, 'batch_log_id' => $batchLogId ]; } // 【判断1】割当判定処理 $allocationResult = $this->performAllocationJudgment($zoneInfo, $parkId, $ptypeId, $psectionId); if (!$allocationResult['can_allocate']) { // 割当NGの場合、対象事室番号を設定 $this->setTargetRoomNumber($allocationResult['target_room_number']); // バッチログ更新(警告) $batchLog->update([ 'status' => BatchLog::STATUS_WARNING, 'end_time' => now(), 'message' => '割当処理NG: ' . $allocationResult['reason'], 'success_count' => 1 // 処理は成功したが割当NGのため ]); return [ 'success' => true, 'message' => '割当判定完了(割当NG)', 'allocation_result' => $allocationResult, 'batch_log_id' => $batchLogId ]; } // 【処理2】割当実行(割当OKの場合) $executionResult = $this->executeAllocation($zoneInfo, $allocationResult); // バッチ処理完了ログ更新 $batchLog->update([ 'status' => BatchLog::STATUS_SUCCESS, 'end_time' => now(), 'message' => 'SHJ-4C 室割当処理正常完了', 'success_count' => 1 ]); Log::info('SHJ-4C 室割当処理完了', [ 'batch_log_id' => $batchLogId, 'execution_result' => $executionResult ]); // 【処理3】処理結果返却 return [ 'success' => true, 'message' => 'SHJ-4C 室割当処理が正常に完了しました', 'zone_info' => $zoneInfo, 'allocation_result' => $allocationResult, 'execution_result' => $executionResult, 'batch_log_id' => $batchLogId ]; } catch (\Exception $e) { $errorMessage = 'SHJ-4C 室割当処理でエラーが発生: ' . $e->getMessage(); if (isset($batchLog) && $batchLog) { $batchLog->update([ 'status' => BatchLog::STATUS_ERROR, 'end_time' => now(), 'message' => $errorMessage, 'error_details' => $e->getMessage(), 'error_count' => 1 ]); } Log::error('SHJ-4C 室割当処理エラー', [ 'batch_log_id' => $batchLogId, 'exception' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); return [ 'success' => false, 'message' => $errorMessage, 'details' => $e->getMessage(), 'batch_log_id' => $batchLogId ]; } } /** * 【処理1】ゾーン情報取得 * * 駐輪場ID、駐輪分類ID、車種区分IDに紐づくゾーン情報を取得する * SQLクエリは設計書の仕様に基づく * * @param int $parkId 駐輪場ID * @param int $ptypeId 駐輪分類ID * @param int $psectionId 車種区分ID * @return array ゾーン情報 */ private function getZoneInformation(int $parkId, int $ptypeId, int $psectionId): array { try { // 設計書に記載されたSQLクエリに基づくゾーン情報取得 $zoneInfo = DB::table('zone as T1') ->select([ 'T1.zone_id', 'T1.zone_name', 'T1.zone_contracted_count as zone_contracted_count', 'T1.zone_capacity as zone_capacity', 'T1.zone_permitted_count as zone_permitted_count', 'T1.zone_sort', 'T2.update_grace_period_start_date', 'T2.update_grace_period_end_date' ]) ->join('park as T2', 'T1.park_id', '=', 'T2.park_id') ->where('T1.park_id', $parkId) ->where('T1.ptype_id', $ptypeId) ->where('T1.psection_id', $psectionId) ->where('T1.delete_flag', 0) ->orderBy('T1.zone_sort') ->get() ->toArray(); Log::info('ゾーン情報取得完了', [ 'park_id' => $parkId, 'ptype_id' => $ptypeId, 'psection_id' => $psectionId, 'zone_count' => count($zoneInfo) ]); return $zoneInfo; } catch (\Exception $e) { Log::error('ゾーン情報取得エラー', [ 'park_id' => $parkId, 'ptype_id' => $ptypeId, 'psection_id' => $psectionId, 'error' => $e->getMessage() ]); throw $e; } } /** * 【判断1】割当判定処理 * * ゾーン内の定期契約マスタから該当レコード数を取得し、 * 定期契約マスタから最適な車室番号を選定する割当判定を実行 * * @param array $zoneInfo ゾーン情報 * @param int $parkId 駐輪場ID * @param int $ptypeId 駐輪分類ID * @param int $psectionId 車種区分ID * @return array 割当判定結果 */ private function performAllocationJudgment(array $zoneInfo, int $parkId, int $ptypeId, int $psectionId): array { try { foreach ($zoneInfo as $zone) { // 各ゾーンに対する定期契約情報取得 $contractInfo = $this->getRegularContractInfo($parkId, $zone->zone_id, $ptypeId, $psectionId); // 割当可能性判定 if ($this->canAllocateToZone($zone, $contractInfo)) { return [ 'can_allocate' => true, 'zone_id' => $zone->zone_id, 'zone_name' => $zone->zone_name, 'contract_info' => $contractInfo, 'reason' => '割当OK: ゾーンID ' . $zone->zone_id ]; } } // 全ゾーンで割当NGの場合 $targetRoomNumber = $this->generateTargetRoomNumber($parkId, $ptypeId, $psectionId); return [ 'can_allocate' => false, 'target_room_number' => $targetRoomNumber, 'reason' => '全ゾーンで割当できませんでした' ]; } catch (\Exception $e) { Log::error('割当判定処理エラー', [ 'error' => $e->getMessage() ]); throw $e; } } /** * 定期契約情報取得 * * 設計書のSQLクエリに基づく定期契約マスタ検索 * * @param int $parkId 駐輪場ID * @param int $zoneId ゾーンID * @param int $ptypeId 駐輪分類ID * @param int $psectionId 車種区分ID * @return array 定期契約情報 */ private function getRegularContractInfo(int $parkId, int $zoneId, int $ptypeId, int $psectionId): array { $currentDate = Carbon::now()->format('Y-m-d'); $contractInfo = DB::table('regular_contract as T1') ->select(['T1.contract_id']) ->where('T1.park_id', $parkId) ->where('T1.zone_id', $zoneId) ->where('T1.ptype_id', $ptypeId) ->where('T1.psection_id', $psectionId) ->where('T1.place_no', '!=', '') ->where(function ($query) use ($currentDate) { $query->where(function ($subQuery) use ($currentDate) { // パターンA: 処理1.更新期間開始日 <= 処理1.更新期間終了日 の場合 $subQuery->whereRaw("date_format(now(), '%y-%m-%d') >= date_format(now(), '%y-%m-%d')") ->whereRaw("date_format(now(), '%y-%m-%d') <= date_format(now(), '%y-%m-%d')"); }) ->orWhere(function ($subQuery) use ($currentDate) { // パターンB: その他の場合 $subQuery->whereRaw("date_format(now(), '%y-%m-%d') >= date_format(now(), '%y-%m-%d')"); }); }) ->where('T1.contract_flag', 1) ->get() ->toArray(); return $contractInfo; } /** * ゾーン割当可能性判定 * * @param object $zone ゾーン情報 * @param array $contractInfo 契約情報 * @return bool 割当可能かどうか */ private function canAllocateToZone($zone, array $contractInfo): bool { $contractedCount = count($contractInfo); $capacity = $zone->zone_capacity; // 空きがある場合は割当可能 return $contractedCount < $capacity; } /** * 対象事室番号生成 * * @param int $parkId 駐輪場ID * @param int $ptypeId 駐輪分類ID * @param int $psectionId 車種区分ID * @return string 対象事室番号 */ private function generateTargetRoomNumber(int $parkId, int $ptypeId, int $psectionId): string { return sprintf('%d_%d_%d', $parkId, $ptypeId, $psectionId); } /** * 対象事室番号設定 * * @param string $targetRoomNumber 対象事室番号 * @return void */ private function setTargetRoomNumber(string $targetRoomNumber): void { Log::info('対象事室番号設定', [ 'target_room_number' => $targetRoomNumber ]); // 実際の事室番号設定ロジックをここに実装 // 具体的な仕様が必要な場合は後で追加実装 } /** * 割当実行 * * @param array $zoneInfo ゾーン情報 * @param array $allocationResult 割当判定結果 * @return array 実行結果 */ private function executeAllocation(array $zoneInfo, array $allocationResult): array { // 割当実行の具体的なロジックを実装 // 設計書に詳細仕様があれば追加実装 return [ 'executed' => true, 'zone_id' => $allocationResult['zone_id'], 'message' => '割当実行完了' ]; } }