so-manager-dev.com/app/Services/ShjFourCService.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

418 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\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' => '割当実行完了'
];
}
}