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

406 lines
14 KiB
PHP
Raw Permalink 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\Park;
use App\Models\RegularContract;
use App\Models\Batch\BatchLog;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
/**
* SHJ-4C 室割当処理サービス
*
* ゾーン情報取得及び割当処理を実行するビジネスロジック
* バッチ処理「SHJ-4C室割当」の核となる処理を担当
*/
class ShjFourCService
{
/**
* Park モデル
*
* @var Park
*/
protected $parkModel;
/**
* RegularContract モデル
*
* @var RegularContract
*/
protected $contractModel;
/**
* BatchLog モデル
*
* @var BatchLog
*/
protected $batchLogModel;
/**
* コンストラクタ
*
* @param Park $parkModel
* @param RegularContract $contractModel
* @param BatchLog $batchLogModel
*/
public function __construct(
Park $parkModel,
RegularContract $contractModel,
BatchLog $batchLogModel
) {
$this->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' => '割当実行完了'
];
}
}