未発行
| {{ $seal->psection_subject ?? '' }} | +{{ $seal->usertype_subject1 ?? '' }} | +
|---|---|
| {{ $seal->ptype_subject ?? '' }} | +{{ $seal->pplace_no ?? '' }} | +
| 定期契約ID | +{{ $seal->contract_id }} | +
| + {{ $seal->enable_months }}ヶ月 + | +|
diff --git a/app/Console/Commands/ShjFiveCommand.php b/app/Console/Commands/ShjFiveCommand.php
new file mode 100644
index 0000000..4fd5930
--- /dev/null
+++ b/app/Console/Commands/ShjFiveCommand.php
@@ -0,0 +1,207 @@
+shjFiveService = $shjFiveService;
+ }
+
+ /**
+ * コンソールコマンドを実行
+ *
+ * 処理フロー:
+ * 1. バッチログ開始記録
+ * 2. 駐輪場の空き状況を取得する
+ * 3. 空き状況判定
+ * 4. 空き待ち者の情報を取得する
+ * 5. 取得件数判定
+ * 6. 空き待ち者への通知、またはオペレーターキュー追加処理
+ * 7. バッチ処理ログを作成する
+ * 8. 処理結果返却
+ *
+ * @return int
+ */
+ public function handle()
+ {
+ $batchLog = null;
+
+ try {
+ // 開始ログ出力
+ $startTime = now();
+ $this->info('SHJ-5 空き待ち通知処理を開始します。');
+
+ Log::info('SHJ-5 空き待ち通知処理開始', [
+ 'start_time' => $startTime
+ ]);
+
+ // SHJ-8共通処理呼び出し - 仕様書準拠
+ $batchLog = BatchLog::createBatchLog(
+ 'SHJ-5空き待ち通知処理',
+ BatchLog::STATUS_START,
+ [
+ 'device_id' => 1, // 仕様書:device_id=1
+ 'process_code' => 1, // 仕様書:process_code=1
+ 'status_comment' => '処理開始' // 仕様書:内部変数.ステータスコメント
+ ],
+ 'SHJ-5処理開始: 駐輪場空き状況確認と空き待ち通知処理'
+ );
+
+ // SHJ-5メイン処理実行
+ $result = $this->shjFiveService->executeParkVacancyNotification();
+
+ $endTime = now();
+ $this->info('SHJ-5 空き待ち通知処理が完了しました。');
+ $this->info("処理時間: {$startTime->diffInSeconds($endTime)}秒");
+
+ // 処理結果表示
+ $this->displayProcessResult($result);
+
+ // SHJ-8共通処理 - バッチログ完了記録(仕様書準拠)
+ $logStatus = $result['success'] ? BatchLog::STATUS_SUCCESS : BatchLog::STATUS_ERROR;
+
+ // 仕様書準拠:サービス側で作成した完全なステータスコメントを使用
+ $statusComment = $result['success'] ?
+ ($result['status_comment'] ?? 'ステータスコメント生成エラー') :
+ sprintf('処理失敗: %s', $result['message'] ?? 'エラー');
+
+ $batchLog->update([
+ 'status' => $logStatus,
+ 'end_time' => $endTime,
+ 'message' => $result['message'],
+ 'parameters' => [
+ 'device_id' => 1, // 仕様書:device_id=1
+ 'process_code' => 1, // 仕様書:process_code=1
+ 'status_comment' => $statusComment, // 仕様書:内部変数.ステータスコメント(完全版)
+ 'processed_parks_count' => $result['processed_parks_count'] ?? 0,
+ 'vacant_parks_count' => $result['vacant_parks_count'] ?? 0,
+ 'total_waiting_users' => $result['total_waiting_users'] ?? 0,
+ 'notification_success_count' => $result['notification_success_count'] ?? 0,
+ 'operator_queue_count' => $result['operator_queue_count'] ?? 0,
+ 'error_count' => $result['error_count'] ?? 0,
+ 'duration_seconds' => $result['duration_seconds'] ?? 0
+ ],
+ 'execution_count' => 1,
+ 'success_count' => $result['notification_success_count'] ?? 0,
+ 'error_count' => $result['error_count'] ?? 0
+ ]);
+
+ Log::info('SHJ-5 空き待ち通知処理完了', [
+ 'end_time' => $endTime,
+ 'duration_seconds' => $startTime->diffInSeconds($endTime),
+ 'result' => $result
+ ]);
+
+ return $result['success'] ? self::SUCCESS : self::FAILURE;
+
+ } catch (\Exception $e) {
+ $this->error('SHJ-5 空き待ち通知処理で予期しないエラーが発生しました: ' . $e->getMessage());
+
+ // SHJ-8共通処理 - エラーログ記録(仕様書準拠)
+ if ($batchLog) {
+ $statusComment = sprintf(
+ 'メール正常終了件数:0/メール異常終了件数:1/キュー登録正常終了件数:0/キュー登録異常終了件数:1 - 処理エラー: %s',
+ $e->getMessage()
+ );
+ $batchLog->update([
+ 'status' => BatchLog::STATUS_ERROR,
+ 'end_time' => now(),
+ 'message' => 'SHJ-5処理中にエラーが発生: ' . $e->getMessage(),
+ 'parameters' => [
+ 'device_id' => 1, // 仕様書:device_id=1
+ 'process_code' => 1, // 仕様書:process_code=1
+ 'status_comment' => $statusComment // 仕様書:内部変数.ステータスコメント(完全版)
+ ],
+ 'error_details' => $e->getMessage(),
+ 'error_count' => 1
+ ]);
+ }
+
+ Log::error('SHJ-5 空き待ち通知処理例外エラー', [
+ 'exception' => $e->getMessage(),
+ 'trace' => $e->getTraceAsString()
+ ]);
+
+ return self::FAILURE;
+ }
+ }
+
+ /**
+ * 処理結果を表示
+ *
+ * @param array $result 処理結果
+ * @return void
+ */
+ private function displayProcessResult(array $result): void
+ {
+ $this->line('');
+ $this->info('=== 処理結果 ===');
+
+ if ($result['success']) {
+ $this->info("✓ 処理実行成功: " . $result['message']);
+ } else {
+ $this->error("✗ 処理実行失敗: " . $result['message']);
+ }
+
+ $this->line('');
+ $this->info('=== 処理統計 ===');
+ $this->line(" 処理対象駐輪場数: " . ($result['processed_parks_count'] ?? 0));
+ $this->line(" 空きあり駐輪場数: " . ($result['vacant_parks_count'] ?? 0));
+ $this->line(" 空き待ち者総数: " . ($result['total_waiting_users'] ?? 0));
+ $this->line(" 通知送信成功: " . ($result['notification_success_count'] ?? 0) . "件");
+ $this->line(" オペレーターキュー追加: " . ($result['operator_queue_count'] ?? 0) . "件");
+ $this->line(" エラー件数: " . ($result['error_count'] ?? 0) . "件");
+ $this->line(" 処理時間: " . ($result['duration_seconds'] ?? 0) . "秒");
+
+ // エラー詳細があれば表示
+ if (!empty($result['errors'])) {
+ $this->line('');
+ $this->warn('=== エラー詳細 ===');
+ foreach ($result['errors'] as $index => $error) {
+ $this->line(" " . ($index + 1) . ". " . $error);
+ }
+ }
+ }
+}
diff --git a/app/Console/Commands/ShjThirteenCommand.php b/app/Console/Commands/ShjThirteenCommand.php
new file mode 100644
index 0000000..dc0eb95
--- /dev/null
+++ b/app/Console/Commands/ShjThirteenCommand.php
@@ -0,0 +1,154 @@
+shjThirteenService = $shjThirteenService;
+ }
+
+ /**
+ * コンソールコマンドを実行
+ *
+ * 処理フロー:
+ * 1. 引数取得・検証
+ * 2. ShjThirteenService実行
+ * 3. 結果表示
+ *
+ * @return int
+ */
+ public function handle()
+ {
+ try {
+ // 開始ログ出力
+ $startTime = now();
+ $this->info('SHJ-13 契約台数追加処理を開始します。');
+
+ // 引数取得
+ $contractData = [
+ 'park_id' => (int) $this->argument('park_id'),
+ 'psection_id' => (int) $this->argument('psection_id'),
+ 'ptype_id' => (int) $this->argument('ptype_id'),
+ 'zone_id' => (int) $this->argument('zone_id'),
+ 'contract_id' => $this->option('contract_id'),
+ ];
+
+ Log::info('SHJ-13 契約台数追加処理開始', [
+ 'start_time' => $startTime,
+ 'contract_data' => $contractData,
+ ]);
+
+ $this->info("駐輪場ID: {$contractData['park_id']}");
+ $this->info("車種区分ID: {$contractData['psection_id']}");
+ $this->info("駐輪分類ID: {$contractData['ptype_id']}");
+ $this->info("ゾーンID: {$contractData['zone_id']}");
+ if ($contractData['contract_id']) {
+ $this->info("契約ID: {$contractData['contract_id']}");
+ }
+
+ // SHJ-13処理実行
+ $this->info('【処理実行】契約台数追加処理を実行しています...');
+ $result = $this->shjThirteenService->execute($contractData);
+
+ // 処理結果確認・表示
+ if ($result['result'] === 0) {
+ $endTime = now();
+ $this->info('SHJ-13 契約台数追加処理が正常に完了しました。');
+ $this->info("処理時間: {$startTime->diffInSeconds($endTime)}秒");
+
+ Log::info('SHJ-13 契約台数追加処理完了', [
+ 'end_time' => $endTime,
+ 'duration_seconds' => $startTime->diffInSeconds($endTime),
+ 'contract_data' => $contractData,
+ ]);
+
+ // 成功時の結果出力
+ $this->line('処理結果: 0'); // 0 = 正常終了
+ $this->line('異常情報: '); // 正常時は空文字
+
+ return self::SUCCESS;
+ } else {
+ $this->error('SHJ-13 契約台数追加処理でエラーが発生しました。');
+ $this->error("エラーコード: {$result['error_code']}");
+ $this->error("エラーメッセージ: {$result['error_message']}");
+
+ Log::error('SHJ-13 契約台数追加処理エラー', [
+ 'contract_data' => $contractData,
+ 'error_result' => $result,
+ ]);
+
+ // エラー時の結果出力
+ $this->line('処理結果: 1'); // 1 = 異常終了
+ $this->line('異常情報: ' . $result['error_message']);
+
+ return self::FAILURE;
+ }
+
+ } catch (\Exception $e) {
+ $this->error('SHJ-13 契約台数追加処理で予期しないエラーが発生しました: ' . $e->getMessage());
+ Log::error('SHJ-13 契約台数追加処理例外エラー', [
+ 'exception' => $e->getMessage(),
+ 'trace' => $e->getTraceAsString(),
+ ]);
+
+ // 例外時の結果出力
+ $this->line('処理結果: 1'); // 1 = 異常終了
+ $this->line('異常情報: システムエラー: ' . $e->getMessage());
+
+ return self::FAILURE;
+ }
+ }
+}
diff --git a/app/Http/Controllers/MypageController.php b/app/Http/Controllers/MypageController.php
new file mode 100644
index 0000000..a64f0a6
--- /dev/null
+++ b/app/Http/Controllers/MypageController.php
@@ -0,0 +1,100 @@
+where('user_id', $user_id)->first();
+
+ $today = date('Y-m-d');
+ // 定期契約情報を取得(park/usertype/psection/ptypeテーブルもJOIN)
+ $contracts = DB::table('regular_contract')
+ ->join('park', 'regular_contract.park_id', '=', 'park.park_id')
+ ->join('usertype', 'regular_contract.user_categoryid', '=', 'usertype.user_categoryid')
+ ->leftJoin('city', 'park.city_id', '=', 'city.city_id')
+ ->leftJoin('psection', 'regular_contract.psection_id', '=', 'psection.psection_id')
+ ->leftJoin('ptype', 'regular_contract.ptype_id', '=', 'ptype.ptype_id')
+ ->where('regular_contract.user_id', $user_id)
+ ->where('regular_contract.contract_flag', 1)
+ ->where('regular_contract.contract_cancel_flag', 0)
+ ->where(function ($query) use ($today) {
+ $query->where('regular_contract.contract_periode', '>', $today)
+ ->orWhere(function ($q) use ($today) {
+ $q->where('regular_contract.contract_periode', '<=', $today)
+ ->whereRaw('DATEDIFF(?, regular_contract.contract_periode) <= 5', [$today]);
+ });
+ })
+ ->select(
+ 'regular_contract.contract_id',
+ 'park.park_name',
+ 'usertype.usertype_subject1',
+ 'regular_contract.contract_periods',
+ 'regular_contract.contract_periode',
+ 'regular_contract.enable_months',
+ 'regular_contract.contract_renewal',
+ 'regular_contract.park_id',
+ 'city.update_grace_period_start_date',
+ 'city.update_grace_period_start_time',
+ 'city.update_grace_period_end_date',
+ 'city.update_grace_period_end_time',
+ 'psection.psection_subject',
+ 'ptype.ptype_subject',
+ 'regular_contract.pplace_no'
+ )
+ ->get();
+
+ // シール情報を取得
+ $seals = DB::table('regular_contract')
+ ->join('usertype', 'regular_contract.user_categoryid', '=', 'usertype.user_categoryid')
+ ->leftJoin('psection', 'regular_contract.psection_id', '=', 'psection.psection_id')
+ ->leftJoin('ptype', 'regular_contract.ptype_id', '=', 'ptype.ptype_id')
+ ->where('regular_contract.user_id', $user_id)
+ ->where('regular_contract.contract_flag', 1)
+ ->where('regular_contract.contract_cancel_flag', 0)
+ ->where('regular_contract.contract_periode', '>=', $today)
+ ->where('regular_contract.contract_permission', 1)
+ ->select(
+ 'regular_contract.contract_id',
+ 'regular_contract.contract_qr_id',
+ 'usertype.usertype_subject1',
+ 'regular_contract.contract_periods',
+ 'regular_contract.contract_periode',
+ 'regular_contract.enable_months',
+ 'regular_contract.contract_renewal',
+ 'regular_contract.park_id',
+ 'psection.psection_subject',
+ 'ptype.ptype_subject',
+ 'regular_contract.pplace_no'
+ )
+ ->get();
+
+ // お知らせ情報を取得
+ $information = DB::table('user_information_history')
+ ->where('user_id', $user_id)
+ ->orderBy('user_information_history_id', 'desc')
+ ->select('entry_date', 'user_information_history')
+ ->first();
+
+ \Log::info('マイページにアクセス', [
+ 'user_id' => $user_id,
+ ]);
+
+ return view('mypage.index', [
+ 'active_menu' => 'SWO-4-1', // マイページメニューの選択状態用
+ 'user_name' => $user->user_name, // ユーザー名(ヘッダー用)
+ 'contracts' => $contracts,
+ 'seals' => $seals,
+ 'information' => $information
+ ]);
+ }
+}
diff --git a/app/Services/ShjFiveService.php b/app/Services/ShjFiveService.php
new file mode 100644
index 0000000..4b4b57a
--- /dev/null
+++ b/app/Services/ShjFiveService.php
@@ -0,0 +1,650 @@
+getParkVacancyStatus();
+ Log::info('駐輪場空き状況取得完了', [
+ 'total_parks' => count($parkVacancyList)
+ ]);
+
+ // 各駐輪場に対する処理
+ foreach ($parkVacancyList as $parkVacancyData) {
+ // 配列をオブジェクトに変換
+ $parkVacancy = (object) $parkVacancyData;
+ $processedParksCount++;
+
+ Log::info('駐輪場処理開始', [
+ 'park_id' => $parkVacancy->park_id,
+ 'park_name' => $parkVacancy->park_name,
+ 'psection_id' => $parkVacancy->psection_id,
+ 'ptype_id' => $parkVacancy->ptype_id,
+ 'vacant_count' => $parkVacancy->vacant_count
+ ]);
+
+ // 【判断1】空き状況判定
+ if ($parkVacancy->vacant_count < 1) {
+ Log::info('空きなし - 処理スキップ', [
+ 'park_id' => $parkVacancy->park_id,
+ 'vacant_count' => $parkVacancy->vacant_count
+ ]);
+ continue;
+ }
+
+ $vacantParksCount++;
+
+ // 【処理2】空き待ち者の情報を取得する
+ $waitingUsers = $this->getWaitingUsersInfo(
+ $parkVacancy->park_id,
+ $parkVacancy->psection_id,
+ $parkVacancy->ptype_id
+ );
+
+ // 【判断2】取得件数判定
+ if (empty($waitingUsers)) {
+ Log::info('空き待ち者なし', [
+ 'park_id' => $parkVacancy->park_id
+ ]);
+ continue;
+ }
+
+ $totalWaitingUsers += count($waitingUsers);
+ Log::info('空き待ち者情報取得完了', [
+ 'park_id' => $parkVacancy->park_id,
+ 'waiting_users_count' => count($waitingUsers)
+ ]);
+
+ // 【処理3】空き待ち者への通知、またはオペレーターキュー追加処理
+ $notificationResult = $this->processWaitingUsersNotification(
+ $waitingUsers,
+ $parkVacancy
+ );
+
+ $notificationSuccessCount += $notificationResult['notification_success_count'];
+ $operatorQueueCount += $notificationResult['operator_queue_count'];
+
+ if (!empty($notificationResult['errors'])) {
+ $mailErrors = array_merge($mailErrors, $notificationResult['errors']);
+ $errors = array_merge($errors, $notificationResult['errors']);
+ }
+
+ // オペレーターキュー作成用データを収集
+ if (!empty($notificationResult['queue_items'])) {
+ $allQueueItems = array_merge($allQueueItems ?? [], $notificationResult['queue_items']);
+ }
+ }
+
+ // 【処理4】仕様書準拠:先に呼び出し、成功時のみ内部変数を更新
+ $queueErrorCount = 0; // キュー登録異常終了件数(累計)
+ $queueSuccessCount = 0; // キュー登録正常終了件数(累計)
+
+ foreach ($allQueueItems as $queueItem) {
+ // 仕様書準拠:在呼叫前先計算"如果這次成功會是第幾件",確保記錄反映最新件數
+ $predictedSuccessCount = $queueSuccessCount + 1;
+ $predictedErrorCount = $queueErrorCount; // 暂时保持当前错误计数
+
+ $queueResult = $this->addToOperatorQueue(
+ $queueItem['waiting_user'],
+ $queueItem['park_vacancy'],
+ $queueItem['batch_comment'],
+ $notificationSuccessCount, // 最終メール正常終了件数
+ $predictedSuccessCount, // 預測成功時的件數(包含本次)
+ count($mailErrors), // 現在のメール異常終了件数(動態計算)
+ $predictedErrorCount // 現在のキュー登録異常終了件数
+ );
+
+ // 仕様書:根据实际结果决定是否采用预测值
+ if ($queueResult['success']) {
+ $queueSuccessCount = $predictedSuccessCount; // 采用预测的成功计数
+ } else {
+ $queueErrorCount++; // 失败时递增错误计数
+
+ // 仕様書:包含具体错误消息,满足"エラーメッセージ/スタックトレースを保持"要求
+ $errorDetail = $queueResult['error'] ?? 'Unknown error';
+ $queueErrorInfo = sprintf('キュー登録失敗:予約ID:%d - %s',
+ $queueItem['waiting_user']->reserve_id ?? 0,
+ $errorDetail
+ );
+ $errors[] = $queueErrorInfo; // 加入总错误统计(包含具体原因)
+
+ Log::error('オペレーターキュー作成失敗', [
+ 'user_id' => $queueItem['waiting_user']->user_id,
+ 'reserve_id' => $queueItem['waiting_user']->reserve_id ?? 0,
+ 'error' => $errorDetail
+ ]);
+ }
+ }
+
+ $endTime = now();
+ $duration = $startTime->diffInSeconds($endTime);
+
+ Log::info('SHJ-5 空き待ち通知処理完了', [
+ 'duration_seconds' => $duration,
+ 'processed_parks_count' => $processedParksCount,
+ 'vacant_parks_count' => $vacantParksCount,
+ 'total_waiting_users' => $totalWaitingUsers,
+ 'notification_success_count' => $notificationSuccessCount,
+ 'operator_queue_success_count' => $queueSuccessCount, // 仕様書:正常完了件数
+ 'queue_error_count' => $queueErrorCount,
+ 'mail_error_count' => count($mailErrors), // メール異常終了件数(分離)
+ 'total_error_count' => count($errors) // 全体エラー件数
+ ]);
+
+ // 仕様書に基づく内部変数.ステータスコメント生成
+ $statusComment = sprintf(
+ 'メール正常終了件数:%d/メール異常終了件数:%d/キュー登録正常終了件数:%d/キュー登録異常終了件数:%d',
+ $notificationSuccessCount,
+ count($mailErrors), // メール異常終了件数(キュー失敗を除外)
+ $queueSuccessCount ?? 0, // 実際のキュー登録成功件数
+ $queueErrorCount ?? 0 // 実際のキュー登録失敗件数
+ );
+
+ return [
+ 'success' => true,
+ 'message' => 'SHJ-5 空き待ち通知処理が正常に完了しました',
+ 'processed_parks_count' => $processedParksCount,
+ 'vacant_parks_count' => $vacantParksCount,
+ 'total_waiting_users' => $totalWaitingUsers,
+ 'notification_success_count' => $notificationSuccessCount,
+ 'operator_queue_count' => $queueSuccessCount ?? 0, // 仕様書:正常完了件数を使用
+ 'error_count' => count($errors),
+ 'errors' => $errors,
+ 'duration_seconds' => $duration,
+ 'status_comment' => $statusComment // SHJ-8用の完全なステータスコメント
+ ];
+
+ } catch (Exception $e) {
+ Log::error('SHJ-5 空き待ち通知処理でエラーが発生', [
+ 'error' => $e->getMessage(),
+ 'trace' => $e->getTraceAsString()
+ ]);
+
+ return [
+ 'success' => false,
+ 'message' => 'SHJ-5 空き待ち通知処理でエラーが発生: ' . $e->getMessage(),
+ 'error_details' => $e->getMessage()
+ ];
+ }
+ }
+
+ /**
+ * 【処理1】駐輪場の空き状況を取得する
+ *
+ * 仕様書に基づくSQL:
+ * - zone表から標準台数と現在の契約台数を比較
+ * - 空きがある駐輪場の情報を取得
+ *
+ * @return array 駐輪場空き状況リスト
+ */
+ private function getParkVacancyStatus(): array
+ {
+ try {
+ // ゾーン毎の契約台数を取得
+ $contractCounts = DB::table('regular_contract as T1')
+ ->select([
+ 'T1.park_id',
+ 'T1.psection_id',
+ 'T5.ptype_id',
+ DB::raw('count(T1.contract_id) as contract_count')
+ ])
+ ->join('park as T2', 'T1.park_id', '=', 'T2.park_id')
+ ->join('price_a as T5', function($join) {
+ $join->on('T1.park_id', '=', 'T5.park_id')
+ ->on('T1.price_parkplaceid', '=', 'T5.price_parkplaceid')
+ ->on('T1.psection_id', '=', 'T5.psection_id');
+ })
+ ->where([
+ ['T1.contract_flag', '=', 1], // 有効契約
+ ['T2.park_close_flag', '=', 0], // 駐輪場未閉鎖
+ ])
+ // 契約有効期間内の条件
+ ->whereRaw("date_format(now(), '%Y%m%d') BETWEEN T1.contract_periods AND T1.contract_periode")
+ ->groupBy(['T1.park_id', 'T1.psection_id', 'T5.ptype_id'])
+ ->get()
+ ->keyBy(function($item) {
+ return $item->park_id . '_' . $item->psection_id . '_' . $item->ptype_id;
+ });
+
+ // ゾーン情報と照合して空き状況を算出
+ $vacancyList = DB::table('zone as T1')
+ ->select([
+ 'T1.park_id',
+ 'T2.park_name',
+ 'T1.psection_id',
+ 'T1.ptype_id',
+ 'T1.zone_standard',
+ 'T3.psection_subject',
+ 'T4.ptype_subject'
+ ])
+ ->join('park as T2', 'T1.park_id', '=', 'T2.park_id')
+ ->join('psection as T3', 'T1.psection_id', '=', 'T3.psection_id')
+ ->join('ptype as T4', 'T1.ptype_id', '=', 'T4.ptype_id')
+ ->where([
+ ['T1.delete_flag', '=', 0], // ゾーン有効
+ ['T2.park_close_flag', '=', 0], // 駐輪場開設
+ ])
+ ->get()
+ ->map(function($zone) use ($contractCounts) {
+ $key = $zone->park_id . '_' . $zone->psection_id . '_' . $zone->ptype_id;
+ $contractCount = isset($contractCounts[$key]) ? $contractCounts[$key]->contract_count : 0;
+
+ $zone->contract_count = $contractCount;
+ $zone->vacant_count = max(0, $zone->zone_standard - $contractCount);
+
+ return $zone;
+ })
+ ->filter(function($zone) {
+ return $zone->vacant_count > 0; // 空きがあるもののみ
+ })
+ ->values();
+
+ Log::info('駐輪場空き状況算出完了', [
+ 'total_zones' => count($vacancyList),
+ 'vacant_zones' => $vacancyList->filter(function($v) {
+ return $v->vacant_count > 0;
+ })->count()
+ ]);
+
+ return $vacancyList->toArray();
+
+ } catch (Exception $e) {
+ Log::error('駐輪場空き状況取得エラー', [
+ 'error' => $e->getMessage()
+ ]);
+ throw $e;
+ }
+ }
+
+ /**
+ * 【処理2】空き待ち者の情報を取得する
+ *
+ * 仕様書に基づく取得条件:
+ * - 契約未紐付(contract_id IS NULL)
+ * - 退会でない(user_quit_flag <> 1)
+ * - 有効な予約(valid_flag = 1)
+ * - 予約日時順で取得(reserve_date昇順)
+ *
+ * @param int $parkId 駐輪場ID
+ * @param int $psectionId 車種区分ID
+ * @param int $ptypeId 駐輪分類ID
+ * @return array 空き待ち者情報リスト
+ */
+ private function getWaitingUsersInfo(int $parkId, int $psectionId, int $ptypeId): array
+ {
+ try {
+ $waitingUsers = DB::table('reserve as T1')
+ ->select([
+ 'T1.reserve_id',
+ 'T1.user_id',
+ 'T1.park_id',
+ 'T1.psection_id',
+ 'T1.ptype_id',
+ 'T1.reserve_order',
+ 'T1.reserve_date',
+ 'T1.reserve_manual', // 手動通知フラグ
+ 'T1.contract_id', // 契約紐付確認用
+ 'T2.user_name',
+ 'T2.user_primemail',
+ 'T2.user_submail', // 副メールアドレス
+ 'T2.user_manual_regist_flag', // 手動登録フラグ
+ 'T2.user_quit_flag', // 退会フラグ
+ 'T3.park_name',
+ 'T4.psection_subject',
+ 'T5.ptype_subject'
+ ])
+ ->join('user as T2', 'T1.user_id', '=', 'T2.user_id')
+ ->join('park as T3', 'T1.park_id', '=', 'T3.park_id')
+ ->join('psection as T4', 'T1.psection_id', '=', 'T4.psection_id')
+ ->join('ptype as T5', 'T1.ptype_id', '=', 'T5.ptype_id')
+ ->where([
+ ['T1.park_id', '=', $parkId],
+ ['T1.psection_id', '=', $psectionId],
+ ['T1.ptype_id', '=', $ptypeId],
+ ['T1.valid_flag', '=', 1], // 有効な予約
+ ['T2.user_quit_flag', '<>', 1] // 退会でない
+ ])
+ ->whereNull('T1.contract_id') // 契約未紐付
+ ->orderBy('T1.reserve_date', 'asc') // 仕様書に基づく予約日時順
+ ->get()
+ ->toArray();
+
+ Log::info('空き待ち者情報取得完了', [
+ 'park_id' => $parkId,
+ 'psection_id' => $psectionId,
+ 'ptype_id' => $ptypeId,
+ 'waiting_users_count' => count($waitingUsers)
+ ]);
+
+ return $waitingUsers;
+
+ } catch (Exception $e) {
+ Log::error('空き待ち者情報取得エラー', [
+ 'park_id' => $parkId,
+ 'psection_id' => $psectionId,
+ 'ptype_id' => $ptypeId,
+ 'error' => $e->getMessage()
+ ]);
+ throw $e;
+ }
+ }
+
+ /**
+ * 【処理3】空き待ち者への通知、またはオペレーターキュー追加処理
+ *
+ * 仕様書に基づく分岐処理:
+ * - 手動通知フラグ判定(reserve_manual)
+ * - メール送信成功時のreserve.sent_date更新
+ * - 失敗時のオペレーターキュー追加(最終統計で処理)
+ *
+ * @param array $waitingUsers 空き待ち者リスト
+ * @param object $parkVacancy 駐輪場空き情報
+ * @return array 通知処理結果
+ */
+ private function processWaitingUsersNotification(array $waitingUsers, object $parkVacancy): array
+ {
+ $notificationSuccessCount = 0;
+ $operatorQueueCount = 0;
+ $errors = [];
+ $queueItems = []; // オペレーターキュー作成用データ収集
+
+ try {
+ // 空きがある分だけ処理(先着順)
+ $availableSpots = min($parkVacancy->vacant_count, count($waitingUsers));
+
+ for ($i = 0; $i < $availableSpots; $i++) {
+ $waitingUserData = $waitingUsers[$i];
+ // 配列をオブジェクトに変換
+ $waitingUser = (object) $waitingUserData;
+
+ try {
+ // 【仕様判断】手動通知フラグチェック
+ if ($waitingUser->reserve_manual == 1) {
+ // 手動通知 → オペレーターキュー作成データ収集
+ $batchComment = '手動通知フラグ設定のため予約ID:' . $waitingUser->reserve_id;
+ $queueItems[] = [
+ 'waiting_user' => $waitingUser,
+ 'park_vacancy' => $parkVacancy,
+ 'batch_comment' => $batchComment
+ ];
+ $operatorQueueCount++;
+
+ Log::info('手動通知フラグによりオペレーターキュー登録予定', [
+ 'user_id' => $waitingUser->user_id,
+ 'reserve_id' => $waitingUser->reserve_id
+ ]);
+ } else {
+ // 自動通知 → メール送信を試行
+ $mailResult = $this->sendVacancyNotificationMail($waitingUser, $parkVacancy);
+
+ if ($mailResult['success']) {
+ // メール送信成功 → reserve.sent_date更新
+ $this->updateReserveSentDate($waitingUser->reserve_id);
+ $notificationSuccessCount++;
+
+ Log::info('空き待ち通知メール送信成功', [
+ 'user_id' => $waitingUser->user_id,
+ 'reserve_id' => $waitingUser->reserve_id,
+ 'park_id' => $parkVacancy->park_id
+ ]);
+ } else {
+ // メール送信失敗 → オペレーターキュー作成データ収集
+ $shjSevenError = $mailResult['error'] ?? $mailResult['message'] ?? 'SHJ-7メール送信エラー';
+ $batchComment = $shjSevenError . '予約ID:' . $waitingUser->reserve_id;
+ $queueItems[] = [
+ 'waiting_user' => $waitingUser,
+ 'park_vacancy' => $parkVacancy,
+ 'batch_comment' => $batchComment
+ ];
+ $operatorQueueCount++;
+ $errors[] = $shjSevenError;
+ }
+ }
+
+ } catch (Exception $e) {
+ Log::error('空き待ち者通知処理エラー', [
+ 'user_id' => $waitingUser->user_id,
+ 'reserve_id' => $waitingUser->reserve_id,
+ 'error' => $e->getMessage()
+ ]);
+
+ // エラー発生時もオペレーターキュー作成データ収集
+ $batchComment = 'システムエラー:' . $e->getMessage() . '予約ID:' . $waitingUser->reserve_id;
+ $queueItems[] = [
+ 'waiting_user' => $waitingUser,
+ 'park_vacancy' => $parkVacancy,
+ 'batch_comment' => $batchComment
+ ];
+ $operatorQueueCount++;
+ $errors[] = $e->getMessage();
+ }
+ }
+
+ return [
+ 'notification_success_count' => $notificationSuccessCount,
+ 'operator_queue_count' => $operatorQueueCount,
+ 'errors' => $errors,
+ 'queue_items' => $queueItems // 後でキュー作成用
+ ];
+
+ } catch (Exception $e) {
+ Log::error('空き待ち者通知処理全体エラー', [
+ 'park_id' => $parkVacancy->park_id,
+ 'error' => $e->getMessage()
+ ]);
+ throw $e;
+ }
+ }
+
+ /**
+ * 空き待ち通知メールを送信
+ *
+ * 仕様書に基づくSHJ-7呼び出し:
+ * - 主メールアドレス・副メールアドレスを正しく渡す
+ * - 必要なパラメータを全て設定
+ *
+ * @param object $waitingUser 空き待ち者情報
+ * @param object $parkVacancy 駐輪場空き情報
+ * @return array 送信結果
+ */
+ private function sendVacancyNotificationMail(object $waitingUser, object $parkVacancy): array
+ {
+ try {
+ // ShjMailSendServiceを利用してメール送信
+ $mailService = app(ShjMailSendService::class);
+
+ // 空き待ち通知用のメールテンプレートID(予約告知通知)
+ // OperatorQueの定数と合わせて4番を使用
+ $mailTemplateId = 4; // 予約告知通知のテンプレートID
+
+ // 仕様書No1/No2に基づく主メール・副メール設定
+ $mainEmail = $waitingUser->user_primemail ?? '';
+ $subEmail = $waitingUser->user_submail ?? '';
+
+ // メール送信実行(仕様書準拠)
+ $mailResult = $mailService->executeMailSend(
+ $mainEmail,
+ $subEmail,
+ $mailTemplateId
+ );
+
+ Log::info('空き待ち通知メール送信試行完了', [
+ 'user_id' => $waitingUser->user_id,
+ 'main_email' => $mainEmail,
+ 'sub_email' => $subEmail,
+ 'mail_template_id' => $mailTemplateId,
+ 'result_success' => $mailResult['success'] ?? false
+ ]);
+
+ return $mailResult;
+
+ } catch (Exception $e) {
+ Log::error('空き待ち通知メール送信エラー', [
+ 'user_id' => $waitingUser->user_id,
+ 'main_email' => $waitingUser->user_primemail ?? '',
+ 'sub_email' => $waitingUser->user_submail ?? '',
+ 'error' => $e->getMessage()
+ ]);
+
+ return [
+ 'success' => false,
+ 'error' => $e->getMessage()
+ ];
+ }
+ }
+
+ /**
+ * reserve.sent_date及びvalid_flag更新
+ *
+ * 仕様書準拠:メール送信成功時にreserve.sent_dateとvalid_flag=0を同時更新
+ * 重複通知を防ぎ、処理済みマークを設定
+ *
+ * @param int $reserveId 予約ID
+ * @return void
+ */
+ private function updateReserveSentDate(int $reserveId): void
+ {
+ try {
+ DB::table('reserve')
+ ->where('reserve_id', $reserveId)
+ ->update([
+ 'sent_date' => now()->format('Y-m-d H:i:s'),
+ 'valid_flag' => 0, // 仕様書:メール送信成功時に0に更新
+ 'updated_at' => now()
+ ]);
+
+ Log::info('reserve.sent_date及びvalid_flag更新完了', [
+ 'reserve_id' => $reserveId,
+ 'sent_date' => now()->format('Y-m-d H:i:s'),
+ 'valid_flag' => 0
+ ]);
+
+ } catch (Exception $e) {
+ Log::error('reserve.sent_date及びvalid_flag更新エラー', [
+ 'reserve_id' => $reserveId,
+ 'error' => $e->getMessage()
+ ]);
+ throw $e;
+ }
+ }
+
+ /**
+ * オペレーターキューに追加
+ *
+ * 仕様書に基づくキュー登録:
+ * - que_comment: 空文字列
+ * - que_status_comment: 仕様書完全準拠形式(統計情報含む)
+ * - operator_id: 9999999固定
+ *
+ * @param object $waitingUser 空き待ち者情報
+ * @param object $parkVacancy 駐輪場空き情報
+ * @param string $batchComment 内部変数.バッチコメント
+ * @param int $mailSuccessCount メール正常終了件数
+ * @param int $queueSuccessCount キュー登録正常終了件数
+ * @param int $mailErrorCount メール異常終了件数
+ * @param int $queueErrorCount キュー登録異常終了件数
+ * @return array 追加結果
+ */
+ private function addToOperatorQueue(object $waitingUser, object $parkVacancy, string $batchComment, int $mailSuccessCount, int $queueSuccessCount, int $mailErrorCount, int $queueErrorCount): array
+ {
+ try {
+ // 仕様書完全準拠:駐輪場名/駐輪分類名/車種区分名/空き台数…/対象予約ID…/内部変数.バッチコメント/内部変数.メール正常終了件数…メール異常終了件数…キュー登録正常終了件数…キュー登録異常終了件数…
+ $statusComment = sprintf(
+ '%s/%s/%s/空き台数:%d台/対象予約ID:%d/%s/メール正常終了件数:%d/メール異常終了件数:%d/キュー登録正常終了件数:%d/キュー登録異常終了件数:%d',
+ $waitingUser->park_name ?? '',
+ $waitingUser->ptype_subject ?? '', // 駐輪分類名
+ $waitingUser->psection_subject ?? '', // 車種区分名
+ $parkVacancy->vacant_count ?? 0,
+ $waitingUser->reserve_id ?? 0,
+ $batchComment, // 内部変数.バッチコメント
+ $mailSuccessCount, // 内部変数.メール正常終了件数
+ $mailErrorCount,
+ $queueSuccessCount,
+ $queueErrorCount
+ );
+
+ OperatorQue::create([
+ 'que_class' => 4, // 予約告知通知
+ 'user_id' => $waitingUser->user_id,
+ 'contract_id' => null,
+ 'park_id' => $waitingUser->park_id,
+ 'que_comment' => '', // 仕様書:空文字列
+ 'que_status' => 1, // キュー発生
+ 'que_status_comment' => $statusComment, // 仕様書:完全準拠形式
+ 'work_instructions' => '空き待ち者への連絡をお願いします。',
+ 'operator_id' => 9999999, // 仕様書:固定値9999999
+ ]);
+
+ Log::info('オペレーターキュー追加成功', [
+ 'user_id' => $waitingUser->user_id,
+ 'park_id' => $waitingUser->park_id,
+ 'reserve_id' => $waitingUser->reserve_id,
+ 'que_class' => 4,
+ 'operator_id' => 9999999,
+ 'batch_comment' => $batchComment,
+ 'mail_success_count' => $mailSuccessCount,
+ 'mail_error_count' => $mailErrorCount,
+ 'queue_success_count' => $queueSuccessCount,
+ 'queue_error_count' => $queueErrorCount,
+ 'status_comment' => $statusComment
+ ]);
+
+ return ['success' => true];
+
+ } catch (Exception $e) {
+ Log::error('オペレーターキュー追加エラー', [
+ 'user_id' => $waitingUser->user_id,
+ 'park_id' => $waitingUser->park_id,
+ 'reserve_id' => $waitingUser->reserve_id ?? null,
+ 'batch_comment' => $batchComment,
+ 'error' => $e->getMessage()
+ ]);
+
+ return [
+ 'success' => false,
+ 'error' => $e->getMessage()
+ ];
+ }
+ }
+}
diff --git a/app/Services/ShjFourBService.php b/app/Services/ShjFourBService.php
index 491cb42..ebb5a7b 100644
--- a/app/Services/ShjFourBService.php
+++ b/app/Services/ShjFourBService.php
@@ -7,6 +7,7 @@ use App\Models\RegularContract;
use App\Models\Park;
use App\Models\PriceA;
use App\Models\Batch\BatchLog;
+use App\Services\ShjThirteenService;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
@@ -170,6 +171,7 @@ class ShjFourBService
'T1.billing_amount',
'T4.price_ptypeid as ptype_id',
'T1.psection_id',
+ 'T1.zone_id',
'T1.update_flag',
'T1.reserve_id',
'T1.contract_payment_number',
@@ -764,27 +766,57 @@ class ShjFourBService
/**
* SHJ-13実行処理(新規のみ)
+ *
+ * ShjThirteenServiceを使用した契約台数追加処理
*
* @param object $contract
* @return array
*/
private function triggerShjThirteen($contract): array
{
- // TODO: SHJ-13の具体的な処理を実装
- // 現在はプレースホルダー
-
Log::info('SHJ-4B SHJ-13実行処理', [
'contract_id' => $contract->contract_id,
- 'user_id' => $contract->user_id,
'park_id' => $contract->park_id,
+ 'psection_id' => $contract->psection_id,
+ 'ptype_id' => $contract->ptype_id,
+ 'zone_id' => $contract->zone_id,
]);
-
- return [
- 'triggered' => true,
- 'method' => 'placeholder',
- 'message' => 'SHJ-13処理は実装予定です',
- 'contract_id' => $contract->contract_id,
- ];
+
+ try {
+ // 契約データ準備
+ $contractData = [
+ 'contract_id' => $contract->contract_id,
+ 'park_id' => $contract->park_id,
+ 'psection_id' => $contract->psection_id,
+ 'ptype_id' => $contract->ptype_id,
+ 'zone_id' => $contract->zone_id,
+ ];
+
+ // ShjThirteenService実行
+ $shjThirteenService = app(ShjThirteenService::class);
+ $result = $shjThirteenService->execute($contractData);
+
+ Log::info('SHJ-4B SHJ-13実行完了', [
+ 'contract_id' => $contract->contract_id,
+ 'result' => $result,
+ ]);
+
+ return $result;
+
+ } catch (\Throwable $e) {
+ Log::error('SHJ-4B SHJ-13実行エラー', [
+ 'contract_id' => $contract->contract_id,
+ 'error' => $e->getMessage(),
+ 'trace' => $e->getTraceAsString(),
+ ]);
+
+ return [
+ 'result' => 1,
+ 'error_code' => $e->getCode() ?: 1999,
+ 'error_message' => $e->getMessage(),
+ 'stack_trace' => $e->getTraceAsString(),
+ ];
+ }
}
/**
diff --git a/app/Services/ShjThirteenService.php b/app/Services/ShjThirteenService.php
new file mode 100644
index 0000000..cc3f276
--- /dev/null
+++ b/app/Services/ShjThirteenService.php
@@ -0,0 +1,346 @@
+ $contractData['contract_id'] ?? null,
+ 'park_id' => $contractData['park_id'] ?? null,
+ 'psection_id' => $contractData['psection_id'] ?? null,
+ 'ptype_id' => $contractData['ptype_id'] ?? null,
+ 'zone_id' => $contractData['zone_id'] ?? null,
+ ]);
+
+ try {
+ // パラメータ検証
+ $validationResult = $this->validateParameters($contractData);
+ if (!$validationResult['valid']) {
+ return $this->createErrorResult(
+ 1001,
+ 'パラメータエラー: ' . $validationResult['message']
+ );
+ }
+
+ // 【処理1・2】契約台数反映とバッチログ作成を一体で実行
+ $processResult = $this->executeProcessWithLogging($contractData);
+ if (!$processResult['success']) {
+ return $this->createErrorResult(
+ $processResult['error_code'],
+ $processResult['error_message'],
+ $processResult['stack_trace'] ?? ''
+ );
+ }
+
+ $statusComment = $processResult['status_comment'];
+
+ $endTime = now();
+ Log::info('SHJ-13 契約台数追加処理完了', [
+ 'contract_id' => $contractData['contract_id'],
+ 'execution_time' => $startTime->diffInSeconds($endTime),
+ 'updated_count' => $processResult['updated_count'],
+ 'status_comment' => $statusComment,
+ ]);
+
+ return ['result' => 0];
+
+ } catch (\Throwable $e) {
+ Log::error('SHJ-13 契約台数追加処理例外エラー', [
+ 'contract_data' => $contractData,
+ 'error' => $e->getMessage(),
+ 'trace' => $e->getTraceAsString(),
+ ]);
+
+ return $this->createErrorResult(
+ $e->getCode() ?: 1999,
+ $e->getMessage(),
+ $e->getTraceAsString()
+ );
+ }
+ }
+
+ /**
+ * パラメータ検証
+ *
+ * @param array $contractData
+ * @return array
+ */
+ private function validateParameters(array $contractData): array
+ {
+ $errors = [];
+
+ if (empty($contractData['park_id'])) {
+ $errors[] = '駐輪場IDが設定されていません';
+ }
+
+ if (empty($contractData['psection_id'])) {
+ $errors[] = '車種区分IDが設定されていません';
+ }
+
+ if (empty($contractData['ptype_id'])) {
+ $errors[] = '駐輪分類IDが設定されていません';
+ }
+
+ if (empty($contractData['zone_id'])) {
+ $errors[] = 'ゾーンIDが設定されていません';
+ }
+
+ if (!empty($errors)) {
+ return [
+ 'valid' => false,
+ 'message' => implode(', ', $errors),
+ 'details' => $errors,
+ ];
+ }
+
+ return ['valid' => true];
+ }
+
+ /**
+ * 契約台数反映とバッチログ作成の一体処理
+ *
+ * park_number・zone テーブルの契約台数を+1更新し、バッチログを作成
+ * 全てを1つのトランザクション内で実行
+ *
+ * @param array $contractData
+ * @return array
+ */
+ private function executeProcessWithLogging(array $contractData): array
+ {
+ try {
+ return DB::transaction(function() use ($contractData) {
+ // 各テーブルの名称取得
+ $names = $this->getTableNames($contractData);
+ if (!$names['success']) {
+ throw new \Exception('名称取得エラー: ' . $names['message'], 1002);
+ }
+
+ // park_number テーブル更新
+ $parkNumberUpdated = DB::table('park_number')
+ ->where('park_id', $contractData['park_id'])
+ ->where('psection_id', $contractData['psection_id'])
+ ->where('ptype_id', $contractData['ptype_id'])
+ ->increment('park_number', 1, [
+ 'updated_at' => now(),
+ 'operator_id' => 'SHJ-13',
+ ]);
+
+ if ($parkNumberUpdated === 0) {
+ throw new \Exception('park_numberテーブルの対象レコードが存在しません', 1011);
+ }
+
+ // zone テーブル更新
+ $zoneUpdated = DB::table('zone')
+ ->where('zone_id', $contractData['zone_id'])
+ ->increment('zone_number', 1, [
+ 'updated_at' => now(),
+ 'ope_id' => 'SHJ-13',
+ ]);
+
+ if ($zoneUpdated === 0) {
+ throw new \Exception('zoneテーブルの対象レコードが存在しません', 1012);
+ }
+
+ // 更新後の契約台数取得
+ $updatedZone = DB::table('zone')
+ ->where('zone_id', $contractData['zone_id'])
+ ->first(['zone_number']);
+
+ // ステータスコメント構築
+ $statusComment = $this->buildStatusComment($names['names'], $updatedZone->zone_number);
+
+ // バッチ処理ログ作成(同一トランザクション内)
+ $currentDate = now()->format('Y/m/d');
+
+ $batchLog = BatchLog::createBatchLog(
+ 'SHJ-13', // process_name
+ BatchLog::STATUS_SUCCESS,
+ [
+ 'device_id' => 1,
+ 'job_name' => 'SHJ-13',
+ 'status' => 'success',
+ 'status_comment' => $statusComment,
+ 'created_date' => $currentDate,
+ 'updated_date' => $currentDate,
+ 'contract_id' => $contractData['contract_id'] ?? null,
+ 'park_id' => $contractData['park_id'],
+ 'psection_id' => $contractData['psection_id'],
+ 'ptype_id' => $contractData['ptype_id'],
+ 'zone_id' => $contractData['zone_id'],
+ ],
+ $statusComment
+ );
+
+ Log::info('SHJ-13 契約台数更新・ログ作成完了', [
+ 'contract_id' => $contractData['contract_id'],
+ 'park_number_updated' => $parkNumberUpdated,
+ 'zone_updated' => $zoneUpdated,
+ 'updated_count' => $updatedZone->zone_number,
+ 'batch_log_id' => $batchLog->id,
+ 'status_comment' => $statusComment,
+ ]);
+
+ return [
+ 'success' => true,
+ 'updated_count' => $updatedZone->zone_number,
+ 'status_comment' => $statusComment,
+ ];
+ });
+
+ } catch (\Throwable $e) {
+ Log::error('SHJ-13 契約台数更新・ログ作成エラー', [
+ 'contract_data' => $contractData,
+ 'error_code' => $e->getCode(),
+ 'error' => $e->getMessage(),
+ 'trace' => $e->getTraceAsString(),
+ ]);
+
+ return [
+ 'success' => false,
+ 'error_code' => $e->getCode() ?: 1010,
+ 'error_message' => $e->getMessage(),
+ 'stack_trace' => $e->getTraceAsString(),
+ ];
+ }
+ }
+
+ /**
+ * テーブル名称取得
+ *
+ * @param array $contractData
+ * @return array
+ */
+ private function getTableNames(array $contractData): array
+ {
+ try {
+ // 駐輪場名取得
+ $park = DB::table('park')
+ ->where('park_id', $contractData['park_id'])
+ ->first(['park_name']);
+
+ if (!$park) {
+ return [
+ 'success' => false,
+ 'message' => "駐輪場が見つかりません: park_id={$contractData['park_id']}",
+ ];
+ }
+
+ // 駐輪分類名取得
+ $ptype = DB::table('ptype')
+ ->where('ptype_id', $contractData['ptype_id'])
+ ->first(['ptype_subject']);
+
+ if (!$ptype) {
+ return [
+ 'success' => false,
+ 'message' => "駐輪分類が見つかりません: ptype_id={$contractData['ptype_id']}",
+ ];
+ }
+
+ // 車種区分名取得
+ $psection = DB::table('psection')
+ ->where('psection_id', $contractData['psection_id'])
+ ->first(['psection_subject']);
+
+ if (!$psection) {
+ return [
+ 'success' => false,
+ 'message' => "車種区分が見つかりません: psection_id={$contractData['psection_id']}",
+ ];
+ }
+
+ // ゾーン名取得
+ $zone = DB::table('zone')
+ ->where('zone_id', $contractData['zone_id'])
+ ->first(['zone_name']);
+
+ if (!$zone) {
+ return [
+ 'success' => false,
+ 'message' => "ゾーンが見つかりません: zone_id={$contractData['zone_id']}",
+ ];
+ }
+
+ return [
+ 'success' => true,
+ 'names' => [
+ 'park_name' => $park->park_name,
+ 'ptype_subject' => $ptype->ptype_subject,
+ 'psection_subject' => $psection->psection_subject,
+ 'zone_name' => $zone->zone_name,
+ ],
+ ];
+
+ } catch (\Throwable $e) {
+ return [
+ 'success' => false,
+ 'message' => 'テーブル名称取得エラー: ' . $e->getMessage(),
+ 'details' => $e->getTraceAsString(),
+ ];
+ }
+ }
+
+ /**
+ * ステータスコメント構築
+ *
+ * 形式:駐輪場名/駐輪分類名/車種区分名/ゾーン名/現在契約台数(更新後):{数値}/
+ *
+ * @param array $names
+ * @param int $updatedCount
+ * @return string
+ */
+ private function buildStatusComment(array $names, int $updatedCount): string
+ {
+ return sprintf(
+ '%s/%s/%s/%s/現在契約台数(更新後):%d/',
+ $names['park_name'],
+ $names['ptype_subject'],
+ $names['psection_subject'],
+ $names['zone_name'],
+ $updatedCount
+ );
+ }
+
+
+ /**
+ * エラー結果作成
+ *
+ * @param int $errorCode
+ * @param string $errorMessage
+ * @param string $stackTrace
+ * @return array
+ */
+ private function createErrorResult(int $errorCode, string $errorMessage, string $stackTrace = ''): array
+ {
+ return [
+ 'result' => 1,
+ 'error_code' => $errorCode,
+ 'error_message' => $errorMessage,
+ 'stack_trace' => $stackTrace,
+ ];
+ }
+}
diff --git a/public/assets/css/mypage/all.css b/public/assets/css/mypage/all.css
deleted file mode 100644
index 6439b74..0000000
--- a/public/assets/css/mypage/all.css
+++ /dev/null
@@ -1,61 +0,0 @@
-/* iCheck plugin skins
------------------------------------ */
-@import url("minimal/_all.css");
-/*
-@import url("minimal/minimal.css");
-@import url("minimal/red.css");
-@import url("minimal/green.css");
-@import url("minimal/blue.css");
-@import url("minimal/aero.css");
-@import url("minimal/grey.css");
-@import url("minimal/orange.css");
-@import url("minimal/yellow.css");
-@import url("minimal/pink.css");
-@import url("minimal/purple.css");
-*/
-
-@import url("square/_all.css");
-/*
-@import url("square/square.css");
-@import url("square/red.css");
-@import url("square/green.css");
-@import url("square/blue.css");
-@import url("square/aero.css");
-@import url("square/grey.css");
-@import url("square/orange.css");
-@import url("square/yellow.css");
-@import url("square/pink.css");
-@import url("square/purple.css");
-*/
-
-@import url("flat/_all.css");
-/*
-@import url("flat/flat.css");
-@import url("flat/red.css");
-@import url("flat/green.css");
-@import url("flat/blue.css");
-@import url("flat/aero.css");
-@import url("flat/grey.css");
-@import url("flat/orange.css");
-@import url("flat/yellow.css");
-@import url("flat/pink.css");
-@import url("flat/purple.css");
-*/
-
-@import url("line/_all.css");
-/*
-@import url("line/line.css");
-@import url("line/red.css");
-@import url("line/green.css");
-@import url("line/blue.css");
-@import url("line/aero.css");
-@import url("line/grey.css");
-@import url("line/orange.css");
-@import url("line/yellow.css");
-@import url("line/pink.css");
-@import url("line/purple.css");
-*/
-
-@import url("polaris/polaris.css");
-
-@import url("futurico/futurico.css");
\ No newline at end of file
diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php
index 7a4c2a0..8d2fd2c 100644
--- a/resources/views/layouts/app.blade.php
+++ b/resources/views/layouts/app.blade.php
@@ -12,7 +12,6 @@
-
diff --git a/resources/views/mypage/index.blade.php b/resources/views/mypage/index.blade.php
new file mode 100644
index 0000000..f31a0b1
--- /dev/null
+++ b/resources/views/mypage/index.blade.php
@@ -0,0 +1,260 @@
+@extends('layouts.app')
+@section('content')
+定期契約情報
+ シール発行
+ {{ $user_name }}さんへのお知らせ
+ お知らせ一覧を見る
+
+