api.so-manager-dev.com/app/Services/ShjFourCService.php
Your Name 0b4acd7475
All checks were successful
Deploy api / deploy (push) Successful in 22s
Batch & API
2026-01-16 19:28:13 +09:00

418 lines
14 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\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);
// JOB3: ゾーンID, 車室番号, 異常情報を返却
return [
'success' => false,
'zone_id' => null,
'pplace_no' => null,
'error_info' => $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);
// JOB3: ゾーンID, 車室番号, 異常情報を返却割当NG = 空き車室なし)
return [
'success' => true,
'zone_id' => null,
'pplace_no' => null,
'error_info' => $allocationResult['reason']
];
}
// 【処理2】バッチログ作成
$statusComment = sprintf('室割当処理完了 (park_id:%d, ptype_id:%d, psection_id:%d, zone_id:%d, pplace_no:%d)',
$parkId, $ptypeId, $psectionId, $allocationResult['zone_id'], $allocationResult['pplace_no']);
$this->createBatchLog($status, $statusComment);
Log::info('SHJ-4C 室割当処理完了', [
'zone_id' => $allocationResult['zone_id'],
'pplace_no' => $allocationResult['pplace_no']
]);
// 【処理3】処理結果返却
// JOB3: ゾーンID, 車室番号, 異常情報を返却
return [
'success' => true,
'message' => 'SHJ-4C 室割当処理が正常に完了しました',
'zone_id' => $allocationResult['zone_id'],
'pplace_no' => $allocationResult['pplace_no'],
'error_info' => null
];
} 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()
]);
// JOB3: ゾーンID, 車室番号, 異常情報を返却
return [
'success' => false,
'zone_id' => null,
'pplace_no' => null,
'error_info' => $errorMessage
];
}
}
/**
* 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_number as zone_number',
'T1.zone_standard as zone_standard',
'T1.zone_tolerance as zone_tolerance',
'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】割当判定処理
*
* ゾーンIDごとにわか順インクリメント、内部変数.対象番号とする)に
* ゾーン内標準台数に達するまで、定期契約マスタから該当する車室番号で
* 契約済みの情報の有無を確認する
*
* @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) {
// ゾーン内標準台数まで車室番号をインクリメントして検索
for ($pplaceNo = 1; $pplaceNo <= $zone->zone_standard; $pplaceNo++) {
// 該当する車室番号で契約済みの情報の有無を確認
$contractInfo = $this->getRegularContractInfo(
$parkId,
$zone->zone_id,
$pplaceNo,
(int) $zone->update_grace_period_start_date,
(int) $zone->update_grace_period_end_date
);
// 契約情報が存在しない場合、この車室番号は空き
if ($contractInfo === null) {
Log::info('SHJ-4C 割当OK', [
'zone_id' => $zone->zone_id,
'zone_name' => $zone->zone_name,
'pplace_no' => $pplaceNo,
]);
return [
'can_allocate' => true,
'zone_id' => $zone->zone_id,
'zone_name' => $zone->zone_name,
'pplace_no' => $pplaceNo,
'reason' => "割当OK: ゾーンID {$zone->zone_id}, 車室番号 {$pplaceNo}"
];
}
}
Log::info('SHJ-4C ゾーン満杯', [
'zone_id' => $zone->zone_id,
'zone_name' => $zone->zone_name,
]);
}
// 全ゾーンで割当NGの場合
$targetRoomNumber = $this->generateTargetRoomNumber($parkId, $ptypeId, $psectionId);
Log::warning('SHJ-4C 割当NG', [
'target_room_number' => $targetRoomNumber,
]);
return [
'can_allocate' => false,
'target_room_number' => $targetRoomNumber,
'reason' => "車室割り当てNG: " . $targetRoomNumber
];
} catch (\Exception $e) {
Log::error('割当判定処理エラー', [
'error' => $e->getMessage()
]);
throw $e;
}
}
/**
* 定期契約情報取得
*
* 設計書のSQLクエリに基づく定期契約マスタ検索
* 特定の車室番号で契約済みの情報の有無を確認する
*
* @param int $parkId 駐輪場ID
* @param int $zoneId ゾーンID
* @param int $pplaceNo 車室番号(対象番号)
* @param int $updateGracePeriodStartDate 更新期間開始日
* @param int $updateGracePeriodEndDate 更新期間終了日
* @return array|null 定期契約情報存在しない場合はnull
*/
private function getRegularContractInfo(
int $parkId,
int $zoneId,
int $pplaceNo,
int $updateGracePeriodStartDate,
int $updateGracePeriodEndDate
): ?array {
$currentDate = Carbon::now()->format('y.%m.%d');
$query = DB::table('regular_contract as T1')
->select(['T1.contract_id'])
->where('T1.park_id', $parkId)
->where('T1.zone_id', $zoneId)
->where('T1.pplace_no', $pplaceNo);
// 【「JOB1.更新期間開始日」<=「JOB1. 更新期間終了日」の場合】
if ($updateGracePeriodStartDate <= $updateGracePeriodEndDate) {
// パターンA: T1.有効期間E >= date_format(now(), '%y.%m.%d')
$query->whereRaw("date_format(T1.contract_periode, '%y.%m.%d') >= ?", [$currentDate]);
} else {
// 【その他の場合】
// パターンB: T1.有効期間E + 「JOB1. 更新期間終了日」>= date_format(now(), '%y.%m.%d')
$query->whereRaw("DATE_ADD(T1.contract_periode, INTERVAL ? DAY) >= ?", [$updateGracePeriodEndDate, $currentDate]);
}
$query->where('T1.contract_flag', 1);
$result = $query->first();
return $result ? (array) $result : null;
}
/**
* 対象事室番号生成
*
* @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
]);
// 実際の事室番号設定ロジックをここに実装
// 具体的な仕様が必要な場合は後で追加実装
}
}