418 lines
14 KiB
PHP
418 lines
14 KiB
PHP
<?php
|
||
|
||
namespace App\Services;
|
||
|
||
use App\Models\Park;
|
||
use App\Models\RegularContract;
|
||
use App\Models\Device;
|
||
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;
|
||
|
||
/**
|
||
* ShjEightService
|
||
*
|
||
* @var ShjEightService
|
||
*/
|
||
protected $shjEightService;
|
||
|
||
/**
|
||
* コンストラクタ
|
||
*
|
||
* @param Park $parkModel
|
||
* @param RegularContract $contractModel
|
||
* @param ShjEightService $shjEightService
|
||
*/
|
||
public function __construct(
|
||
Park $parkModel,
|
||
RegularContract $contractModel,
|
||
ShjEightService $shjEightService
|
||
) {
|
||
$this->parkModel = $parkModel;
|
||
$this->contractModel = $contractModel;
|
||
$this->shjEightService = $shjEightService;
|
||
}
|
||
|
||
/**
|
||
* 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
|
||
{
|
||
$statusComment = '';
|
||
$status = 'success';
|
||
|
||
try {
|
||
Log::info('SHJ-4C 室割当処理開始', [
|
||
'park_id' => $parkId,
|
||
'ptype_id' => $ptypeId,
|
||
'psection_id' => $psectionId
|
||
]);
|
||
|
||
// 【処理1】ゾーン情報取得
|
||
$zoneInfo = $this->getZoneInformation($parkId, $ptypeId, $psectionId);
|
||
|
||
if (empty($zoneInfo)) {
|
||
$message = '対象のゾーン情報が見つかりません';
|
||
$status = 'error';
|
||
$statusComment = sprintf('エラー: %s (park_id:%d, ptype_id:%d, psection_id:%d)',
|
||
$message, $parkId, $ptypeId, $psectionId);
|
||
|
||
// バッチログ作成
|
||
$this->createBatchLog($status, $statusComment);
|
||
|
||
return [
|
||
'success' => false,
|
||
'message' => $message
|
||
];
|
||
}
|
||
|
||
// 【判断1】割当判定処理
|
||
$allocationResult = $this->performAllocationJudgment($zoneInfo, $parkId, $ptypeId, $psectionId);
|
||
|
||
if (!$allocationResult['can_allocate']) {
|
||
// 割当NGの場合、対象事室番号を設定
|
||
$this->setTargetRoomNumber($allocationResult['target_room_number']);
|
||
|
||
$status = 'warning';
|
||
$statusComment = sprintf('割当NG: %s (park_id:%d, ptype_id:%d, psection_id:%d)',
|
||
$allocationResult['reason'], $parkId, $ptypeId, $psectionId);
|
||
|
||
// バッチログ作成
|
||
$this->createBatchLog($status, $statusComment);
|
||
|
||
return [
|
||
'success' => true,
|
||
'message' => '割当判定完了(割当NG)',
|
||
'allocation_result' => $allocationResult
|
||
];
|
||
}
|
||
|
||
// 【処理2】割当実行(割当OKの場合)
|
||
$executionResult = $this->executeAllocation($zoneInfo, $allocationResult);
|
||
|
||
$statusComment = sprintf('室割当処理完了 (park_id:%d, ptype_id:%d, psection_id:%d, zone_id:%s)',
|
||
$parkId, $ptypeId, $psectionId, $allocationResult['target_room_number'] ?? 'N/A');
|
||
|
||
// バッチログ作成
|
||
$this->createBatchLog($status, $statusComment);
|
||
|
||
Log::info('SHJ-4C 室割当処理完了', [
|
||
'execution_result' => $executionResult
|
||
]);
|
||
|
||
// 【処理3】処理結果返却
|
||
return [
|
||
'success' => true,
|
||
'message' => 'SHJ-4C 室割当処理が正常に完了しました',
|
||
'zone_info' => $zoneInfo,
|
||
'allocation_result' => $allocationResult,
|
||
'execution_result' => $executionResult
|
||
];
|
||
|
||
} catch (\Exception $e) {
|
||
$errorMessage = 'SHJ-4C 室割当処理でエラーが発生: ' . $e->getMessage();
|
||
$status = 'error';
|
||
$statusComment = sprintf('例外エラー: %s (park_id:%d, ptype_id:%d, psection_id:%d)',
|
||
$e->getMessage(), $parkId, $ptypeId, $psectionId);
|
||
|
||
// バッチログ作成
|
||
$this->createBatchLog($status, $statusComment);
|
||
|
||
Log::error('SHJ-4C 室割当処理エラー', [
|
||
'exception' => $e->getMessage(),
|
||
'trace' => $e->getTraceAsString()
|
||
]);
|
||
|
||
return [
|
||
'success' => false,
|
||
'message' => $errorMessage,
|
||
'details' => $e->getMessage()
|
||
];
|
||
}
|
||
}
|
||
|
||
/**
|
||
* SHJ-8バッチ処理ログ作成
|
||
*
|
||
* @param string $status ステータス
|
||
* @param string $statusComment ステータスコメント
|
||
* @return void
|
||
*/
|
||
private function createBatchLog(string $status, string $statusComment): void
|
||
{
|
||
try {
|
||
$device = Device::orderBy('device_id')->first();
|
||
$deviceId = $device ? $device->device_id : 1;
|
||
|
||
$today = now()->format('Y/m/d');
|
||
|
||
Log::info('SHJ-8バッチ処理ログ作成', [
|
||
'device_id' => $deviceId,
|
||
'process_name' => 'SHJ-4C',
|
||
'job_name' => 'SHJ-4C室割当',
|
||
'status' => $status,
|
||
'status_comment' => $statusComment
|
||
]);
|
||
|
||
// SHJ-8サービスを呼び出し
|
||
$this->shjEightService->execute(
|
||
$deviceId,
|
||
'SHJ-4C',
|
||
'SHJ-4C室割当',
|
||
$status,
|
||
$statusComment,
|
||
$today,
|
||
$today
|
||
);
|
||
|
||
} catch (\Exception $e) {
|
||
Log::error('SHJ-8 バッチログ作成エラー', [
|
||
'error' => $e->getMessage()
|
||
]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 【処理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' => '割当実行完了'
|
||
];
|
||
}
|
||
|
||
|
||
} |