handleSessionExpired('駐輪場選択')) { return $redirect; } $userId = session('user_id'); $user = DB::table('user')->where('user_id', $userId)->first(); // マルチテナント対応のため、運営元情報をセッションから取得 $management = session('management'); if (!$management) { \Log::error("[ERROR] " . now()->format('Y-m-d H:i:s') . " 運営元情報が見つからない user_id=" . $userId); abort(404); } // 検索条件・ページング取得 $cityId = $request->input('city_id'); $stationName = $request->input('station_neighbor_station'); $parkId = $request->input('park_id'); $page = (int)$request->input('page', 1); $perPage = 10; // ルート名で画面種別判定(新規定期契約 or 駐輪場検索) $isRegularContract = $request->route()->getName() === 'regular_contract.create'; $activeMenu = $isRegularContract ? 'SWC-8-1' : 'SWC-10-1'; // マスタデータ取得(運営元フィルタ適用) $cities = $this->getCitiesList($management->management_id); $stations = $this->getStationsList($management->management_id); $parks = $this->getParksList($management->management_id); // 運営元が取り扱う車種一覧を取得 $availableVehicles = $this->getAvailableVehicles($management->management_id); // 駐輪場テーブルデータ取得(検索・ページング、運営元フィルタ適用) $parksTableData = $this->getParksTableData($management->management_id, $cityId, $stationName, $parkId, $page, $perPage); // zone・reserve マスタ取得(運営元フィルタ適用) $zones = $this->getZonesByParkId($management->management_id); $reserve = $this->getReserveByParkId($management->management_id); // 駐輪場ごとの更新可能期間取得(運営元フィルタ適用) $parkGracePeriods = $this->getParkGracePeriods($management->management_id); // 駐輪場ごと・車種ごとの状態を事前計算 $parksTableData['data'] = $this->calculateVehicleStatus( $parksTableData['data'], $zones, $reserve, $parkGracePeriods, $availableVehicles ); // アクセスログ記録 $screenName = $isRegularContract ? '新規定期契約登録' : '駐輪場検索'; \Log::info("[INFO] " . now()->format('Y-m-d H:i:s') . " {$screenName}画面アクセス user_id=" . $userId . ", management_id=" . $management->management_id . ", city_id=" . ($cityId ?? 'null') . ", park_id=" . ($parkId ?? 'null')); return view('regular_contract.create', [ 'active_menu' => $activeMenu, 'user_name' => $user->user_name ?? '', 'cities' => $cities, 'stations' => $stations, 'parks' => $parks, 'parks_table' => $parksTableData['data'], 'parks_table_total' => $parksTableData['total'], 'parks_table_page' => $page, 'parks_table_perPage' => $perPage, 'available_vehicles' => $availableVehicles, 'reserve' => $reserve, 'isRegularContract' => $isRegularContract, ]); } /** * 市町村一覧取得(運営元フィルタ適用) * マルチテナント対応のため、運営元に紐づく駐輪場の市町村のみ表示 * * @param int $managementId * @return \Illuminate\Support\Collection */ private function getCitiesList(int $managementId) { try { return DB::table('park') ->join('city', 'park.city_id', '=', 'city.city_id') ->where('park.management_id', $managementId) ->select('city.city_id', 'city.city_name') ->distinct() ->get(); } catch (\Exception $e) { \Log::error("[ERROR] " . now()->format('Y-m-d H:i:s') . " 市町村一覧取得失敗 management_id=" . $managementId . ", error=" . $e->getMessage()); return collect(); } } /** * 駅名一覧取得(運営元フィルタ適用) * * なぜ: マルチテナント対応のため、運営元に紐づく駐輪場の駅のみ表示 * * @param int $managementId * @return \Illuminate\Support\Collection */ private function getStationsList(int $managementId) { try { return DB::table('station') ->join('park', 'station.park_id', '=', 'park.park_id') ->where('park.management_id', $managementId) ->select('station.station_neighbor_station') ->distinct() ->get(); } catch (\Exception $e) { \Log::error("[ERROR] " . now()->format('Y-m-d H:i:s') . " 駅名一覧取得失敗 management_id=" . $managementId . ", error=" . $e->getMessage()); return collect(); } } /** * 駐輪場名一覧取得(運営元フィルタ適用) * * なぜ: マルチテナント対応のため、運営元に紐づく駐輪場のみ表示 * * @param int $managementId * @return \Illuminate\Support\Collection */ private function getParksList(int $managementId) { try { return DB::table('park') ->where('management_id', $managementId) ->select('park_id', 'park_name') ->distinct() ->get(); } catch (\Exception $e) { \Log::error("[ERROR] " . now()->format('Y-m-d H:i:s') . " 駐輪場名一覧取得失敗 management_id=" . $managementId . ", error=" . $e->getMessage()); return collect(); } } /** * 駐輪場テーブルデータ取得(検索・ページング適用、運営元フィルタ適用) * * なぜ: マルチテナント対応のため、運営元に紐づく駐輪場のみ表示 * * @param int $managementId * @param int|null $cityId * @param string|null $stationName * @param int|null $parkId * @param int $page * @param int $perPage * @return array ['data' => Collection, 'total' => int] */ private function getParksTableData(int $managementId, $cityId, $stationName, $parkId, int $page, int $perPage) { try { $query = DB::table('park') ->join('city', 'park.city_id', '=', 'city.city_id') ->leftJoin('station', 'park.park_id', '=', 'station.park_id') ->where('park.management_id', $managementId) ->select( 'park.park_id', 'park.park_name', 'park.park_ruby', 'city.city_name', 'city.city_id', 'station.station_neighbor_station', 'station.station_name_ruby' ); // 検索条件適用 if ($cityId) { $query->where('city.city_id', $cityId); } if ($stationName) { $query->where('station.station_neighbor_station', $stationName); } if ($parkId) { $query->where('park.park_id', $parkId); } $query->orderBy('park.park_id', 'asc'); $total = $query->count(); // ページング適用 $data = $query ->skip(($page - 1) * $perPage) ->take($perPage) ->get(); \Log::info("[INFO] " . now()->format('Y-m-d H:i:s') . " 駐輪場データ取得成功 management_id=" . $managementId . ", 件数=" . $total . ", page=" . $page); return [ 'data' => $data, 'total' => $total, ]; } catch (\Exception $e) { \Log::error("[ERROR] " . now()->format('Y-m-d H:i:s') . " 駐輪場テーブルデータ取得失敗 management_id=" . $managementId . ", error=" . $e->getMessage()); return [ 'data' => collect(), 'total' => 0, ]; } } /** * zone データを park_id でグループ化して取得(運営元フィルタ適用) * * なぜ: マルチテナント対応のため、運営元に紐づく駐輪場のzoneのみ取得 * * @param int $managementId * @return \Illuminate\Support\Collection */ private function getZonesByParkId(int $managementId) { try { return DB::table('zone') ->join('park', 'zone.park_id', '=', 'park.park_id') ->leftJoin('psection', 'zone.psection_id', '=', 'psection.psection_id') ->where('park.management_id', $managementId) ->select( 'zone.zone_id', 'zone.park_id', 'zone.ptype_id', 'zone.psection_id', 'zone.zone_number', 'zone.zone_tolerance', 'psection.psection_subject' ) ->get() ->groupBy('park_id'); } catch (\Exception $e) { \Log::error("[ERROR] " . now()->format('Y-m-d H:i:s') . " zone データ取得失敗 management_id=" . $managementId . ", error=" . $e->getMessage()); return collect(); } } /** * reserve データを park_id でグループ化して取得(運営元フィルタ適用) * * なぜ: マルチテナント対応のため、運営元に紐づく駐輪場のreserveのみ取得 * * @param int $managementId * @return \Illuminate\Support\Collection */ private function getReserveByParkId(int $managementId) { try { return DB::table('reserve') ->join('park', 'reserve.park_id', '=', 'park.park_id') ->where('park.management_id', $managementId) ->where('reserve.valid_flag', 1) ->select('reserve.reserve_id', 'reserve.park_id', 'reserve.ptype_id', 'reserve.psection_id') ->get() ->groupBy('park_id'); } catch (\Exception $e) { \Log::error("[ERROR] " . now()->format('Y-m-d H:i:s') . " reserve データ取得失敗 management_id=" . $managementId . ", error=" . $e->getMessage()); return collect(); } } /** * 駐輪場ごとの更新可能期間を取得(運営元フィルタ適用) * * なぜ: 更新可能期間がcityテーブルからparkテーブルに変更されたため * * @param int $managementId * @return \Illuminate\Support\Collection */ private function getParkGracePeriods(int $managementId) { try { return DB::table('park') ->where('management_id', $managementId) ->select( 'park_id', 'update_grace_period_start_date', 'update_grace_period_start_time', 'update_grace_period_end_date', 'update_grace_period_end_time' ) ->get() ->keyBy('park_id'); } catch (\Exception $e) { \Log::error("[ERROR] " . now()->format('Y-m-d H:i:s') . " 駐輪場更新可能期間取得失敗 management_id=" . $managementId . ", error=" . $e->getMessage()); return collect(); } } /** * 運営元が取り扱う車種一覧を取得 * * なぜ: マルチテナント対応のため、運営元ごとに表示する車種を可変にする * * @param int $managementId * @return array */ private function getAvailableVehicles(int $managementId) { try { return DB::table('zone') ->join('park', 'zone.park_id', '=', 'park.park_id') ->leftJoin('psection', 'zone.psection_id', '=', 'psection.psection_id') ->where('park.management_id', $managementId) ->whereNotNull('psection.psection_subject') ->select('psection.psection_subject') ->distinct() ->orderByRaw("FIELD(psection.psection_subject, '自転車', '原付', '自動二輪', '自動車')") ->pluck('psection_subject') ->toArray(); } catch (\Exception $e) { \Log::error("[ERROR] " . now()->format('Y-m-d H:i:s') . " 取り扱い車種取得失敗 management_id=" . $managementId . ", error=" . $e->getMessage()); return []; } } /** * 駐輪場ごと・車種ごとの状態を計算 * * なぜ: Bladeでのビジネスロジック実行を避け、Fat Controller回避のため事前計算 * * @param \Illuminate\Support\Collection $parks * @param \Illuminate\Support\Collection $zones * @param \Illuminate\Support\Collection $reserve * @param \Illuminate\Support\Collection $parkGracePeriods * @param array $availableVehicles * @return \Illuminate\Support\Collection */ private function calculateVehicleStatus($parks, $zones, $reserve, $parkGracePeriods, $availableVehicles) { $now = \Carbon\Carbon::now(); foreach ($parks as $park) { $park->vehicle_status = []; foreach ($availableVehicles as $vehicle) { $zonesForType = ($zones[$park->park_id] ?? collect())->where('psection_subject', $vehicle); // 空き台数計算 $hasVacancy = false; foreach ($zonesForType as $zone) { $reserveCount = ($reserve[$park->park_id] ?? collect()) ->where('psection_id', $zone->psection_id) ->where('ptype_id', $zone->ptype_id) ->count(); $vacancy = $zone->zone_tolerance - $zone->zone_number - $reserveCount; if ($vacancy > 0) { $hasVacancy = true; break; } } // 猶予期間判定 $grace = $parkGracePeriods[$park->park_id] ?? null; $inGrace = $this->isInGracePeriod($grace, $now); // 状態判定 if ($zonesForType->isEmpty() || !$grace) { $park->vehicle_status[$vehicle] = 'none'; } elseif ($inGrace && $hasVacancy) { $park->vehicle_status[$vehicle] = 'available'; } elseif (!$inGrace) { $park->vehicle_status[$vehicle] = 'out_of_period'; } elseif (!$hasVacancy) { $park->vehicle_status[$vehicle] = 'waiting'; } else { $park->vehicle_status[$vehicle] = 'none'; } } } return $parks; } /** * 猶予期間内かどうかを判定 * * なぜ: 複雑な日時判定ロジックを分離し、可読性向上 * * @param object|null $grace * @param \Carbon\Carbon $now * @return bool */ private function isInGracePeriod($grace, $now) { if (!$grace) { return false; } // バリデーション if ( !is_numeric($grace->update_grace_period_start_date) || !preg_match('/^\d{1,2}:\d{2}$/', $grace->update_grace_period_start_time) || !is_numeric($grace->update_grace_period_end_date) || !preg_match('/^\d{1,2}:\d{2}$/', $grace->update_grace_period_end_time) ) { return false; } $year = $now->year; $month = $now->month; $startDay = (int)$grace->update_grace_period_start_date; $endDay = (int)$grace->update_grace_period_end_date; if ($startDay > $endDay) { // 月またぎ(例: 25日~5日) $prevMonth = $now->copy()->subMonth(); $startPrev = \Carbon\Carbon::createFromFormat('Y-m-d H:i', sprintf('%04d-%02d-%02d %s', $prevMonth->year, $prevMonth->month, $startDay, $grace->update_grace_period_start_time)); $endCurr = \Carbon\Carbon::createFromFormat('Y-m-d H:i', sprintf('%04d-%02d-%02d %s', $year, $month, $endDay, $grace->update_grace_period_end_time)); $startCurr = \Carbon\Carbon::createFromFormat('Y-m-d H:i', sprintf('%04d-%02d-%02d %s', $year, $month, $startDay, $grace->update_grace_period_start_time)); $nextMonth = $month == 12 ? 1 : $month + 1; $nextYear = $month == 12 ? $year + 1 : $year; $endNext = \Carbon\Carbon::createFromFormat('Y-m-d H:i', sprintf('%04d-%02d-%02d %s', $nextYear, $nextMonth, $endDay, $grace->update_grace_period_end_time)); return $now->between($startPrev, $endCurr) || $now->between($startCurr, $endNext); } else { // 同月(例: 5日~25日) $start = \Carbon\Carbon::createFromFormat('Y-m-d H:i', sprintf('%04d-%02d-%02d %s', $year, $month, $startDay, $grace->update_grace_period_start_time)); $end = \Carbon\Carbon::createFromFormat('Y-m-d H:i', sprintf('%04d-%02d-%02d %s', $year, $month, $endDay, $grace->update_grace_period_end_time)); return $now->between($start, $end); } } public function regulationCheck(Request $request) { // GETパラメータを取得 $parkId = $request->query('park_id'); $psectionId = $request->query('psection_id'); $ptypeId = $request->query('ptype_id'); // 必要なDB処理やロジック $park_regulation = DB::table('park')->where('park_id', $parkId)->value('parking_regulations_flag'); if ($park_regulation == 1) { // 駐輪規定画面へ return redirect()->route('regular_contract.regulation', [ 'park_id' => $parkId, 'psection_id' => $psectionId, 'ptype_id' => $ptypeId, ]); } else { // 契約情報入力画面へ return redirect()->route('regular_contract.input', [ 'park_id' => $parkId, 'psection_id' => $psectionId, 'ptype_id' => $ptypeId, ]); } } public function showRegulation(Request $request) { $user_id = session('user_id'); if (!$user_id) { return redirect('/login'); } $user_name = DB::table('user')->where('user_id', $user_id)->value('user_name'); // 必要なパラメータ取得 $parkId = $request->query('park_id'); $psectionId = $request->query('psection_id'); $ptypeId = $request->query('ptype_id'); $park_name = DB::table('park')->where('park_id', $parkId)->value('park_name'); $psection_subject = DB::table('psection')->where('psection_id', $psectionId)->value('psection_subject'); $ptype_subject = DB::table('ptype')->where('ptype_id', $ptypeId)->value('ptype_subject'); $regulations_text = DB::table('parking_regulations') ->where('park_id', $parkId) ->where('psection_id', $psectionId) ->where('ptype_id', $ptypeId) ->value('regulations_text'); \Log::info('駐輪規定確認画面にアクセス', [ 'user_id' => $user_id, ]); return view('regular_contract.regulation', [ 'park_id' => $parkId, 'park_name' => $park_name, 'psection_id' => $psectionId, 'psection_subject' => $psection_subject, 'ptype_id' => $ptypeId, 'ptype_subject' => $ptype_subject, 'regulations_text' => $regulations_text, 'active_menu' => 'SWC-8-1', // この画面のID 'user_name' => $user_name, // ユーザー名(ヘッダー用) ]); } public function insertRegulation(Request $request) { $user_id = session('user_id'); if (!$user_id) { return redirect('/login'); } $park_id = $request->input('park_id'); $psection_id = $request->input('psection_id'); $ptype_id = $request->input('ptype_id'); DB::table('parking_regulations_read')->insert([ 'park_id' => $park_id, 'psection_id' => $psection_id, 'ptype_id' => $ptype_id, 'user_id' => $user_id, 'created_at' => now(), ]); // 契約入力画面へリダイレクト return redirect()->route('regular_contract.input', [ 'park_id' => $park_id, 'psection_id' => $psection_id, 'ptype_id' => $ptype_id, ]); } // 契約入力画面表示 public function showContractForm(Request $request) { $user_id = session('user_id'); if (!$user_id) { return redirect('/login'); } $user = DB::table('user')->where('user_id', $user_id)->first(); $park_id = $request->query('park_id'); $park = DB::table('park')->where('park_id', $park_id)->first(); $psection_id = $request->query('psection_id'); $ptype_id = $request->query('ptype_id'); $city_id = DB::table('park')->where('park_id', $park_id)->value('city_id'); $terms_text = DB::table('terms')->where('city_id', $city_id)->value('terms_text'); // 利用者区分をusertypeテーブルから取得 $user_category = ''; if (isset($user->user_categoryid)) { $usertype = DB::table('usertype') ->where('user_categoryid', $user->user_categoryid) ->first(); if ($usertype && isset($usertype->usertype_subject1)) { $user_category = $usertype->usertype_subject1; } } $user->user_homephone = explode('-', $user->user_homephone ?? ''); $user->user_mobile = explode('-', $user->user_mobile ?? ''); $user->user_regident_zip_1 = substr($user->user_regident_zip ?? '', 0, 3); $user->user_regident_zip_2 = substr($user->user_regident_zip ?? '', 3, 4); $user->user_relate_zip_1 = substr($user->user_relate_zip ?? '', 0, 3); $user->user_relate_zip_2 = substr($user->user_relate_zip ?? '', 3, 4); \Log::info('新規定期契約-契約情報入力画面にアクセス', [ 'user_id' => $user_id, ]); session()->forget('show_terms_modal'); return view('regular_contract.input', [ 'park' => $park, 'psection_id' => $psection_id, 'ptype_id' => $ptype_id, 'terms_text' => $terms_text, 'user' => $user, 'user_name' => $user->user_name, // ユーザー名(ヘッダー用) 'active_menu' => 'SWC-8-1', // この画面のID 'user_category' => $user_category, 'show_terms_modal' => true, ]); } public function inputCheck(Request $request) { $user_id = session('user_id'); if (!$user_id) { return redirect('/login'); } $user = DB::table('user')->where('user_id', $user_id)->first(); $park_id = $request->input('park_id'); $park = DB::table('park')->where('park_id', $park_id)->first(); // バリデーションルール $rules = [ 'user_phonetic' => ['required', 'regex:/^[ァ-ヶー \s]+$/u'], 'user_regident_zip_1' => 'required|digits:3', 'user_regident_zip_2' => 'required|digits:4', 'user_regident_pre' => [ 'required', Rule::in([ '北海道', '青森県', '岩手県', '宮城県', '秋田県', '山形県', '福島県', '茨城県', '栃木県', '群馬県', '埼玉県', '千葉県', '東京都', '神奈川県', '新潟県', '富山県', '石川県', '福井県', '山梨県', '長野県', '岐阜県', '静岡県', '愛知県', '三重県', '滋賀県', '京都府', '大阪府', '兵庫県', '奈良県', '和歌山県', '鳥取県', '島根県', '岡山県', '広島県', '山口県', '徳島県', '香川県', '愛媛県', '高知県', '福岡県', '佐賀県', '長崎県', '熊本県', '大分県', '宮崎県', '鹿児島県', '沖縄県' ]), ], 'user_regident_city' => ['required', 'string', 'max:20', 'regex:/^(?:(?![\xF0-\xF7][\x80-\xBF]{3}).)*$/'], 'user_regident_add' => ['required', 'string', 'max:50', 'regex:/^(?:(?![\xF0-\xF7][\x80-\xBF]{3}).)*$/'], 'user_homephone.*' => 'nullable|digits_between:1,5', 'user_mobile.*' => 'nullable|digits_between:1,5', 'user_submail' => 'nullable|email|different:user_primemail|max:80', 'user_category' => ['required', Rule::in(['一般', '学生'])], 'contract_reduction' => ['required', Rule::in(['はい', 'いいえ'])], 'user_relate_zip_1' => 'nullable|digits:3', 'user_relate_zip_2' => 'nullable|digits:4', 'user_relate_pre' => [ 'nullable', Rule::in([ '北海道', '青森県', '岩手県', '宮城県', '秋田県', '山形県', '福島県', '茨城県', '栃木県', '群馬県', '埼玉県', '千葉県', '東京都', '神奈川県', '新潟県', '富山県', '石川県', '福井県', '山梨県', '長野県', '岐阜県', '静岡県', '愛知県', '三重県', '滋賀県', '京都府', '大阪府', '兵庫県', '奈良県', '和歌山県', '鳥取県', '島根県', '岡山県', '広島県', '山口県', '徳島県', '香川県', '愛媛県', '高知県', '福岡県', '佐賀県', '長崎県', '熊本県', '大分県', '宮崎県', '鹿児島県', '沖縄県' ]), ], 'user_relate_city' => ['nullable', 'string', 'max:20', 'regex:/^(?:(?![\xF0-\xF7][\x80-\xBF]{3}).)*$/'], 'user_relate_add' => ['nullable', 'string', 'max:50', 'regex:/^(?:(?![\xF0-\xF7][\x80-\xBF]{3}).)*$/'], ]; // 性別欄が表示されている場合のみ必須 if ((int)$park->gender_display_flag === 1) { $rules['user_gender'] = ['required', Rule::in(['男性', '女性'])]; } // 生年月日欄が表示されている場合のみ必須 if ((int)$park->bd_display_flag === 1) { $rules['user_birthdate'] = ['required', 'date']; } // 防犯登録番号欄が表示されている場合のみ必須 if ((int)$park->securityreg_display_flag === 1) { $rules['user_securitynum'] = ['required', 'max:50', 'regex:/^[a-zA-Z0-9]+$/']; } // 利用者区分ごとの追加バリデーション if ($request->input('user_category') === '学生') { $rules['user_school'] = ['required', 'string', 'max:50', 'regex:/^(?:(?![\xF0-\xF7][\x80-\xBF]{3}).)*$/']; $rules['user_graduate'] = ['required', 'date']; } else { $rules['user_workplace'] = ['nullable', 'string', 'max:50', 'regex:/^(?:(?![\xF0-\xF7][\x80-\xBF]{3}).)*$/']; } $messages = [ 'user_phonetic.required' => 'フリガナが入力されていません。', 'user_phonetic.regex' => 'フリガナはカタカナでご入力ください。', 'user_gender.required' => '性別が入力されていません。', 'user_gender.in' => '性別は「男性」または「女性」を選択してください。', 'user_regident_zip_1.required' => '居住所の郵便番号(前半3桁)が入力されていません。', 'user_regident_zip_2.required' => '居住所の郵便番号(後半4桁)が入力されていません。', 'user_regident_zip_1.digits' => '居住所の郵便番号(前半3桁)は3桁の数字で入力してください。', 'user_regident_zip_2.digits' => '居住所の郵便番号(後半4桁)は4桁の数字で入力してください。', 'user_regident_pre.required' => '居住所の都道府県が選択されていません。', 'user_regident_pre.in' => '都道府県は選択肢から選んでください。', 'user_regident_city.required' => '居住所の市区町村が入力されていません。', 'user_regident_city.max' => '居住所の市区町村は20文字以内で入力してください。', 'user_regident_city.regex' => '居住所の市区町村に絵文字などの特殊文字は使用できません。', 'user_regident_add.required' => '居住所の住所が入力されていません。', 'user_regident_add.max' => '居住所の住所は50文字以内で入力してください。', 'user_regident_add.regex' => '居住所の住所に絵文字などの特殊文字は使用できません。', 'user_birthdate.required' => '生年月日が入力されていません。', 'user_birthdate.date' => '生年月日は正しい日付で入力してください。', 'user_homephone.*.digits_between' => '自宅電話番号はそれぞれ1~5桁の数字で入力してください。', 'user_mobile.*.digits_between' => '携帯電話番号はそれぞれ1~5桁の数字で入力してください。', 'user_submail.email' => '予備メールアドレスは正しい形式で入力してください。', 'user_submail.max' => '予備メールアドレスは80文字以内で入力してください。', 'user_submail.different' => 'メールアドレスと予備メールアドレスに同じアドレスを入力できません。', 'user_category.required' => '利用者区分は必須です。', 'user_category.in' => '利用者区分の値が不正です。', 'contract_reduction.required' => '減免が選択されていません。', 'contract_reduction.in' => '減免の値が不正です。', 'user_workplace.max' => '勤務先は50文字以内で入力してください。', 'user_workplace.regex' => '勤務先に絵文字などの特殊文字は使用できません。', 'user_school.required' => '学校名が入力されていません。', 'user_school.max' => '学校名は50文字以内で入力してください。', 'user_school.regex' => '学校名に絵文字などの特殊文字は使用できません。', 'user_graduate.required' => '卒業年月日が入力されていません。', 'user_graduate.date' => '卒業年月日は正しい日付で入力してください。', 'user_relate_zip_1.digits' => '住所の郵便番号(前半3桁)は3桁の数字で入力してください。', 'user_relate_zip_2.digits' => '住所の郵便番号(後半4桁)は4桁の数字で入力してください。', 'user_relate_pre.in' => '住所の都道府県は選択肢から選んでください。', 'user_relate_city.max' => '住所の市区町村は20文字以内で入力してください。', 'user_relate_city.regex' => '住所の市区町村に絵文字などの特殊文字は使用できません。', 'user_relate_add.max' => '住所は50文字以内で入力してください。', 'user_relate_add.regex' => '住所に絵文字などの特殊文字は使用できません。', 'user_securitynum.required' => '防犯登録番号が入力されていません。', 'user_securitynum.max' => '防犯登録番号は50文字以内で入力してください。', 'user_securitynum.regex' => '防犯登録番号は英数字のみで入力してください。', ]; try { $validated = $request->validate($rules, $messages); } catch (ValidationException $e) { return Redirect()->back() ->withErrors($e->validator) ->withInput($request->except('_token') + ['show_terms_modal' => false]); } if (empty(implode('', $request->input('user_homephone', []))) && empty(implode('', $request->input('user_mobile', [])))) { return redirect()->back() ->withErrors(['user_homephone' => '自宅電話番号または携帯電話番号のいずれかは必須です']) ->withInput($request->except('_token') + ['show_terms_modal' => false]); } $city = DB::table('city')->where('city_id', $park->city_id)->first(); $matched = (mb_strpos($request->user_regident_city, $city->city_name) !== false); if ($matched === true) { $ward_residents = 1; DB::table('user') ->where('user_id', $user->user_id) ->update(['ward_residents' => $ward_residents]); } else { $contract_allowable_city_name = DB::table('contract_allowable_city')->where('city_id', $city->city_id)->value('contract_allowable_city_name'); $matched_allowable = (mb_strpos($request->user_regident_city, $contract_allowable_city_name) !== false); if ($matched_allowable) { $ward_residents = 0; DB::table('user') ->where('user_id', $user->user_id) ->update(['ward_residents' => $ward_residents]); } else { return redirect()->back()->withErrors(['契約対象地域にお住まいでないためお申込みできません'])->withInput()->with(['show_terms_modal' => false]); } } if ($ward_residents == 1) { $usertype_subject2 = '区民'; } else { $usertype_subject2 = '区民外'; } $user_categoryid = DB::table('usertype') ->where('usertype_subject1', $request->user_category) ->where('usertype_subject2', $usertype_subject2) ->value('user_categoryid'); $updateData = [ 'user_categoryid' => $user_categoryid, 'user_phonetic' => $request->user_phonetic, 'user_mobile' => implode('-', $request->input('user_mobile', [])), 'user_homephone' => implode('-', $request->input('user_homephone', [])), 'user_submail' => $request->filled('user_submail') ? $request->user_submail : null, 'user_regident_zip' => $request->user_regident_zip_1 . $request->user_regident_zip_2, 'user_regident_pre' => $request->user_regident_pre, 'user_regident_city' => $request->user_regident_city, 'user_regident_add' => $request->user_regident_add, 'user_relate_zip' => ($request->filled('user_relate_zip_1') && $request->filled('user_relate_zip_2')) ? ($request->user_relate_zip_1 . $request->user_relate_zip_2) : null, 'user_relate_pre' => $request->filled('user_relate_pre') ? $request->user_relate_pre : null, 'user_relate_city' => $request->filled('user_relate_city') ? $request->user_relate_city : null, 'user_relate_add' => $request->filled('user_relate_add') ? $request->user_relate_add : null, 'ward_residents' => $ward_residents, 'user_workplace' => $request->user_category === '一般' ? $request->user_workplace : null, 'user_school' => $request->user_category === '学生' ? $request->user_school : null, 'user_graduate' => $request->user_category === '学生' ? $request->user_graduate : null, 'updated_at' => now() ]; // 性別欄が表示されている場合のみ追加 if (!empty($park->gender_display_flag) && $park->gender_display_flag == 1) { $updateData['user_gender'] = $request->user_gender; } // 生年月日欄が表示されている場合のみ追加 if (!empty($park->bd_display_flag) && $park->bd_display_flag == 1) { $updateData['user_birthdate'] = $request->user_birthdate; $updateData['user_age'] = $request->user_age; } DB::table('user') ->where('user_id', $user->user_id) ->update($updateData); $zone_id = DB::table('zone') ->where('park_id', $request->park_id) ->where('ptype_id', $request->ptype_id) ->where('psection_id', $request->psection_id) ->orderBy('zone_sort', 'asc') ->value('zone_id'); $contract_id = DB::table('regular_contract')->insertGetId([ 'created_at' => now(), 'updated_at' => now(), 'user_id' => $user->user_id, 'user_categoryid' => $user_categoryid, 'park_id' => $park->park_id, 'contract_created_at' => now(), 'contract_reduction' => $request->contract_reduction === 'はい' ? 1 : 0, 'update_flag' => 2, 'contract_cancel_flag' => 0, 'psection_id' => $request->psection_id, 'ptype_id' => $request->ptype_id, 'zone_id' => $zone_id ]); $contractUpdateData = [ 'contract_qr_id' => DB::raw("TO_BASE64(AES_ENCRYPT($contract_id, 'LJLASR4FAS34SAADFA72ASDFALLSDRGT'))") ]; // 防犯登録番号が表示されている場合のみ追加 if (!empty($park->securityreg_display_flag) && $park->securityreg_display_flag == 1) { $contractUpdateData['user_securitynum'] = $request->user_securitynum; } DB::table('regular_contract') ->where('contract_id', $contract_id) ->update($contractUpdateData); return redirect()->route('regular_contract.upload_identity_create', [ 'contract_id' => $contract_id, ]); } public function showUploadIdentityCreate(Request $request) { $user_id = session('user_id'); if (!$user_id) { return redirect('/login'); } $user_name = DB::table('user')->where('user_id', $user_id)->value('user_name'); $contract = DB::table('regular_contract')->where('contract_id', $request->contract_id)->first(); \Log::info('新規定期契約-本人確認書類アップロード画面にアクセス', [ 'user_id' => $user_id, ]); return view('regular_contract.upload_identity_create', [ 'contract_id' => $request->contract_id, 'park_id' => $contract->park_id, 'psection_id' => $contract->psection_id, 'ptype_id' => $contract->ptype_id, 'user_name' => $user_name, 'active_menu' => 'SWC-8-1' ]); } public function confirmUploadIdentity(Request $request, $contract_id) { $user_id = session('user_id'); if (!$user_id) { return redirect('/login'); } $validator = Validator::make($request->all(), [ 'idcard_type' => 'required', 'user_idcard' => 'required|file|mimes:jpg,jpeg,png,pdf', ], [ 'idcard_type.required' => '本人確認書類の種類を選択してください。', 'user_idcard.required' => '本人確認書類のおもて画像をアップロードしてください。', 'user_idcard.file' => '本人確認書類のおもて画像はファイルで指定してください。', 'user_idcard.mimes' => 'アップロードできるファイル形式はjpg、jpeg、png、pdfのみです。', ]); if ($validator->fails()) { return redirect()->route('regular_contract.upload_identity_create', [ 'contract_id' => $contract_id, ]) ->withErrors($validator) ->withInput(); } // おもて画像保存(Laravel Storageを使用) $front = $request->file('user_idcard'); $filename_front = uniqid('photo1_') . '.' . $front->getClientOriginalExtension(); $front->storeAs('photo', $filename_front, 'public'); // userテーブルに保存(チェック済フラグはSHJ-1処理後に設定) $updateData = [ 'photo_filename1' => $filename_front, 'user_idcard' => $request->idcard_type, 'updated_at' => now(), ]; // ウラ画像がある場合保存し更新項目に追加 if ($request->hasFile('user_idcard2')) { $back = $request->file('user_idcard2'); $filename_back = uniqid('photo2_') . '.' . $back->getClientOriginalExtension(); $back->storeAs('photo', $filename_back, 'public'); $updateData['photo_filename2'] = $filename_back; } DB::table('user')->where('user_id', $user_id)->update($updateData); // SHJ-1 本人確認自動処理を実行 $user = DB::table('user')->where('user_id', $user_id)->first(); $park = DB::table('park')->where('park_id', $request->park_id)->first(); $psection = DB::table('psection')->where('psection_id', $request->psection_id)->first(); $usertype = DB::table('usertype')->where('user_categoryid', $user->user_categoryid)->first(); // user_idからuser_seqを取得してSHJ-1に渡す $user_seq = $user->user_seq; $park_id = $request->park_id; \Log::info('SHJ-1バッチ処理開始', [ 'user_id' => $user_id, 'user_seq' => $user_seq, 'park_id' => $park_id, 'contract_id' => $contract_id ]); try { // SHJ-1 コマンドを同期実行 $exitCode = Artisan::call('shj:one', [ 'user_id' => $user_seq, 'park_id' => $park_id ]); \Log::info('SHJ-1バッチ処理完了', [ 'exit_code' => $exitCode, 'user_seq' => $user_seq, 'park_id' => $park_id ]); } catch (\Exception $e) { \Log::error('SHJ-1バッチ処理でエラー発生', [ 'error' => $e->getMessage(), 'user_seq' => $user_seq, 'park_id' => $park_id ]); } // 処理結果に基づいて遷移 return view('regular_contract.create_confirm', [ 'contract_id' => $request->contract_id, 'user' => $user, 'park' => $park, 'psection' => $psection, 'usertype' => $usertype, 'user_name' => $user->user_name, 'active_menu' => 'SWC-8-1' ]); } public function createConfirmNext($contract_id) { $user_id = session('user_id'); if (!$user_id) { return redirect('/login'); } $user = DB::table('user')->where('user_id', $user_id)->first(); // 本人確認自動処理結果を取得 if ($user && $user->user_idcard_chk_flag == 2) { // 本人確認OKの場合は利用期間選択画面へ // 必要な各マスタ情報を取得 $contract = DB::table('regular_contract')->where('contract_id', $contract_id)->first(); $park = DB::table('park')->where('park_id', $contract->park_id)->first(); $city = DB::table('city')->where('city_id', $park->city_id)->first(); $regular_type = DB::table('regular_type')->where('city_id', $city->city_id)->first(); $usertype = DB::table('usertype')->where('user_categoryid', $contract->user_categoryid)->first(); // 2重化しているマスタのため現在のテーブル名を取得 $master_setting = DB::table('setting')->value('web_master'); $tableName = 'price' . $master_setting; // 利用者区分に応じた逆利用フラグを取得 $inverse_use_flag_column = ($usertype->usertype_subject1 == '一般') ? 'inverse_use_flag1' : 'inverse_use_flag2'; $inverse_use_flag = $park->$inverse_use_flag_column; if ($inverse_use_flag == 0) { // regident_cityまたはrelate_cityが一致するか $is_same = ( strpos($user->user_regident_city, $city->city_name) !== false || strpos($user->user_relate_city, $city->city_name) !== false ); } else { // regident_cityのみ一致するか $is_same = (strpos($user->user_regident_city, $city->city_name) !== false); } $target_subject2 = $is_same ? '区民' : '区民外'; $user_categoryid = DB::table('usertype') ->where('usertype_subject1', $usertype->usertype_subject1) ->where('usertype_subject2', $target_subject2) ->where('usertype_subject3', $usertype->usertype_subject3) ->value('user_categoryid'); // 駐輪場所マスタから料金を取得 $prices = DB::table($tableName) ->where('park_id', $contract->park_id) ->where('psection_id', $contract->psection_id) ->where('ptype_id', $contract->ptype_id) ->where('user_categoryid', $user_categoryid) ->get(); \Log::info('利用期間選択画面にアクセス', [ 'user_id' => $user_id, ]); // 利用期間選択画面へ遷移 return view('regular_contract.create_select_period', [ 'active_menu' => 'SWC-8-1', // マイページメニューの選択状態用 'user_name' => $user->user_name, // ユーザー名(ヘッダー用) 'contract_id' => $contract_id, 'regular_type' => $regular_type, 'prices' => $prices, ]); } else { // NGの場合は本人確認書類確認中画面へ \Log::info('本人確認書類確認中画面にアクセス', [ 'user_id' => $user_id, ]); return view('regular_contract.create_idcard_checking', [ 'active_menu' => 'SWC-8-1', // マイページメニューの選択状態用 'user_name' => $user->user_name, // ユーザー名(ヘッダー用) ]); } } public function selectPeriod(Request $request) { $user_id = session('user_id'); if (!$user_id) { return redirect('/login'); } $contract_id = $request->input('contract_id'); $validator = Validator::make($request->all(), [ 'month' => 'required', ], [ 'month.required' => '契約期間が選択されていません。', ]); if ($validator->fails()) { return redirect()->route('regular_contract.create_confirm_error', ['contract_id' => $contract_id]) ->withErrors($validator) ->withInput(); } $month = $request->input('month'); $price = $request->input('price_' . $month); $today = now(); $day = $today->day; if ($day <= 19) { // 今月の1日 $contract_periods = $today->copy()->startOfMonth()->format('Y-m-d'); } else { // 翌月の1日 $contract_periods = $today->copy()->addMonth()->startOfMonth()->format('Y-m-d'); } $contract_periode = Carbon::parse($contract_periods)->addMonths($month - 1)->endOfMonth()->format('Y-m-d'); // 契約更新 DB::table('regular_contract')->where('contract_id', $contract_id)->update([ 'enable_months' => $month, 'billing_amount' => $price, 'contract_periods' => $contract_periods, 'contract_periode' => $contract_periode, 'updated_at' => now(), ]); // 完了後はウェルネット決済画面(仮)へリダイレクト return redirect()->route('wellnet.payment'); } }