運営元ごとのマルチテナント対応 #53

Merged
y.higashide merged 1 commits from main_higashide into main 2026-01-16 15:21:47 +09:00
5 changed files with 167 additions and 78 deletions
Showing only changes of commit 4947caed39 - Show all commits

View File

@ -24,6 +24,6 @@ class LoginController extends Controller
// セッションにユーザ情報を保存してマイページトップへ遷移
session(['user_id' => $existingMember->user_id]);
return redirect()->route('mypage');
return redirect()->intended(route('mypage'));
}
}

View File

@ -0,0 +1,52 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Http\Response;
class ManagementMiddleware
{
/**
* リクエストから運営元コードを取得し、DBから運営元情報を検証・セッションに保存する。
* 運営元コードがない場合やDBに存在しない場合は404を返す。
*
* @param Request $request 受信したHTTPリクエスト
* @param Closure $next 次のミドルウェア処理
* @return Response 次の処理のレスポンス
* @throws \Symfony\Component\HttpKernel\Exception\HttpException 運営元コードが見つからない場合
*/
public function handle(Request $request, Closure $next): Response
{
// マルチテナント対応のため、URLの最初のセグメントを運営元コードとして扱う
$path = $request->getPathInfo(); // パス全体を取得
$segments = explode('/', trim($path, '/')); // '/'で分割
$managementCode = $segments[0] ?? null; // 最初の部分を運営元コードとする
if (!$managementCode) {
// 不正アクセス防止のため、エラーログでシステム例外を記録
Log::error("[ERROR] " . now()->format('Y-m-d H:i:s') . " 運営元コードが見つからない: " . $request->getPathInfo());
abort(404, '運営元コードが見つかりません');
}
// 運営元コードの正当性検証のため、DBで存在確認
$management = DB::table('management')->where('management_code', $managementCode)->first();
if (!$management) {
// 不正アクセス防止のため、エラーログでシステム例外を記録
Log::error("[ERROR] " . now()->format('Y-m-d H:i:s') . " 運営元が見つからない: " . $managementCode);
abort(404, '運営元が見つかりません');
}
// システム操作追跡のため、成功した運営元取得を記録
Log::info("[INFO] " . now()->format('Y-m-d H:i:s') . " 運営元コード取得成功: " . $managementCode);
// 運営元情報を他の処理で利用可能にするため、セッションに保存
session(['management' => $management]);
return $next($request);
}
}

View File

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('management', function (Blueprint $table) {
$table->id('management_id'); // 主キー、AUTO_INCREMENT, NOT NULL
$table->string('management_name', 255); // varchar(255), NOT NULL
$table->string('management_code', 10)->unique(); // varchar(10), NOT NULL, UNIQUE
$table->boolean('municipality_flag')->nullable(); // tinyint(1), NULL
$table->boolean('government_approval_required')->nullable(); // tinyint(1), NULL
$table->boolean('valid_flag '); // tinyint(1)
$table->timestamps(); // created_at, updated_at (datetime), NOT NULL
$table->unsignedInteger('operator_id')->nullable(); // int(10), NULL
$table->foreign('operator_id')->references('ope_id')->on('ope'); // 外部キーopeテーブルのope_idを参照
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('management');
}
};

View File

@ -1,5 +1,7 @@
@php
if (!isset($active_menu)) $active_menu = '';
$management = session('management');
$management_code = $management ? $management->management_code : '';
@endphp
<section id="my-menu" class="bg-success">
<style>
@ -15,14 +17,14 @@ if (!isset($active_menu)) $active_menu = '';
<a id="my-menu-btn" class="navbar-toggler text-center" href="#navbarSupportedContent" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> マイメニューを開く </a>
<div class="collapse navbar-collapse justify-content-around" id="navbarSupportedContent">
<ul class="navbar-nav">
<li class="nav-item {{ $active_menu == 'SWO-4-1' ? 'active' : '' }}"><a class="nav-link" href="{{ url('/mypage') }}">マイページトップ<span class="sr-only">(current)</span></a></li>
<li class="nav-item {{ $active_menu == 'SWC-1-1' ? 'active' : '' }}"><a class="nav-link" href="{{ url('/user/info') }}">ユーザー情報確認</a></li>
<li class="nav-item {{ $active_menu == 'SWC-3-1' ? 'active' : '' }}"><a class="nav-link" href="{{ url('/regular_contract/info') }}">定期契約情報</a></li>
<li class="nav-item {{ $active_menu == 'SWC-4-1' ? 'active' : '' }}"><a class="nav-link" href="{{ url('/regular_contract/update') }}">契約更新</a></li>
<li class="nav-item {{ $active_menu == 'SWC-6-1' ? 'active' : '' }}"><a class="nav-link" href="{{ url('/regular_contract/history') }}">契約履歴</a></li>
<li class="nav-item {{ $active_menu == 'SWC-8-1' ? 'active' : '' }}"><a class="nav-link" href="{{ url('/regular_contract/create') }}">新規定期契約</a></li>
<li class="nav-item {{ $active_menu == 'SWC-11-1' ? 'active' : '' }}"><a class="nav-link" href="{{ url('/park_waitlist') }}">空き待ち状況確認</a></li>
<li class="nav-item {{ $active_menu == 'SWC-10-1' ? 'active' : '' }}"><a class="nav-link" href="{{ url('/park_search') }}">駐輪場検索</a></li>
<li class="nav-item {{ $active_menu == 'SWO-4-1' ? 'active' : '' }}"><a class="nav-link" href="{{ route('mypage', ['management_code' => $management_code]) }}">マイページトップ</a></li>
<li class="nav-item {{ $active_menu == 'SWC-1-1' ? 'active' : '' }}"><a class="nav-link" href="{{ route('user.info', ['management_code' => $management_code]) }}">ユーザー情報確認</a></li>
<li class="nav-item {{ $active_menu == 'SWC-3-1' ? 'active' : '' }}"><a class="nav-link" href="{{ route('regular_contract.info', ['management_code' => $management_code]) }}">定期契約情報</a></li>
<li class="nav-item {{ $active_menu == 'SWC-4-1' ? 'active' : '' }}"><a class="nav-link" href="{{ route('regular_contract.update', ['management_code' => $management_code]) }}">契約更新</a></li>
<li class="nav-item {{ $active_menu == 'SWC-6-1' ? 'active' : '' }}"><a class="nav-link" href="{{ route('regular_contract.history', ['management_code' => $management_code]) }}">契約履歴</a></li>
<li class="nav-item {{ $active_menu == 'SWC-8-1' ? 'active' : '' }}"><a class="nav-link" href="{{ route('regular_contract.create', ['management_code' => $management_code]) }}">新規定期契約</a></li>
<li class="nav-item {{ $active_menu == 'SWC-11-1' ? 'active' : '' }}"><a class="nav-link" href="{{ route('park_waitlist.index', ['management_code' => $management_code]) }}">空き待ち状況確認</a></li>
<li class="nav-item {{ $active_menu == 'SWC-10-1' ? 'active' : '' }}"><a class="nav-link" href="{{ route('park_search', ['management_code' => $management_code]) }}">駐輪場検索</a></li>
<li class="nav-item"><a class="nav-link" href="{{ route('swo16_1') }}" target="_blank">このページの使い方</a></li>
</ul>
</div>

View File

@ -77,88 +77,89 @@ Route::get('/login', function () {
return redirect()->route('swo8_1');
})->name('login');
Route::middleware([\App\Http\Middleware\ManagementMiddleware::class])->group(function () {
// マイページ
Route::get('/mypage', [MypageController::class, 'index'])->name('mypage');
Route::get('/{management_code}/mypage', [MypageController::class, 'index'])->name('mypage');
// ユーザー情報確認・編集
Route::get('/user/info', [UserInfoController::class, 'show'])->name('user.info');
Route::get('/user/edit', [UserEditController::class, 'show'])->name('user.edit');
Route::post('/user/edit', [UserEditController::class, 'update'])->name('user.edit.post');
Route::get('/user/edit/confirm', [UserEditConfirmController::class, 'show'])->name('user.confirm');
Route::post('/user/edit/submit', [UserEditConfirmController::class, 'submit'])->name('user.edit.submit');
Route::get('/user/edit/verify', [UserEditConfirmController::class, 'verify'])->name('user.edit.verify');
Route::get('/{management_code}/user/info', [UserInfoController::class, 'show'])->name('user.info');
Route::get('/{management_code}/user/edit', [UserEditController::class, 'show'])->name('user.edit');
Route::post('/{management_code}/user/edit', [UserEditController::class, 'update'])->name('user.edit.post');
Route::get('/{management_code}/user/edit/confirm', [UserEditConfirmController::class, 'show'])->name('user.confirm');
Route::post('/{management_code}/user/edit/submit', [UserEditConfirmController::class, 'submit'])->name('user.edit.submit');
Route::get('/{management_code}/user/edit/verify', [UserEditConfirmController::class, 'verify'])->name('user.edit.verify');
// 退会
Route::get('/user/withdraw', [UserWithdrawController::class, 'showConfirm'])->name('user.withdraw');
Route::post('/user/withdraw/confirm', [UserWithdrawController::class, 'withdraw'])->name('user.withdraw.confirm');
Route::get('/{management_code}/user/withdraw', [UserWithdrawController::class, 'showConfirm'])->name('user.withdraw');
Route::post('/{management_code}/user/withdraw/confirm', [UserWithdrawController::class, 'withdraw'])->name('user.withdraw.confirm');
// タグ再発行
Route::get('/user/tag_reissue', [UserTagReissueController::class, 'index'])->name('user.tag_reissue');
Route::get('/user/tag_reissue/complete', [UserTagReissueController::class, 'complete'])->name('user.tag_reissue.complete');
Route::get('/{management_code}/user/tag_reissue', [UserTagReissueController::class, 'index'])->name('user.tag_reissue');
Route::get('/{management_code}/user/tag_reissue/complete', [UserTagReissueController::class, 'complete'])->name('user.tag_reissue.complete');
// 定期契約情報確認
Route::get('regular_contract/info', [RegularContractController::class, 'showInfo'])->name('regular_contract.info');
Route::get('/{management_code}/regular_contract/info', [RegularContractController::class, 'showInfo'])->name('regular_contract.info');
// 領収書発行
Route::get('receipt/input/{contract_id}', [ReceiptController::class, 'input'])->name('receipt.input');
Route::get('receipt/download/{contract_id}', [ReceiptController::class, 'download'])->name('receipt.download');
Route::post('receipt/issue/{contract_id}', [ReceiptController::class, 'issue']);
Route::get('/{management_code}/receipt/input/{contract_id}', [ReceiptController::class, 'input'])->name('receipt.input');
Route::get('/{management_code}/receipt/download/{contract_id}', [ReceiptController::class, 'download'])->name('receipt.download');
Route::post('/{management_code}/receipt/issue/{contract_id}', [ReceiptController::class, 'issue']);
// シール再発行
Route::get('/seal/reissue/{contract_id}', [SealReissueController::class, 'index'])->name('seal.reissue');
Route::get('/seal/reissue/reason/{contract_id}', [SealReissueController::class, 'reason'])->name('seal.reissue.reason');
Route::post('/seal/reissue/complete/{contract_id}', [SealReissueController::class, 'complete'])->name('seal.reissue.complete');
Route::get('/{management_code}/seal/reissue/{contract_id}', [SealReissueController::class, 'index'])->name('seal.reissue');
Route::get('/{management_code}/seal/reissue/reason/{contract_id}', [SealReissueController::class, 'reason'])->name('seal.reissue.reason');
Route::post('/{management_code}/seal/reissue/complete/{contract_id}', [SealReissueController::class, 'complete'])->name('seal.reissue.complete');
// 新規定期契約
Route::get('regular_contract/create', [RegularContractCreateController::class, 'show'])->name('regular_contract.create');
Route::get('/api/park-detail/{park_id}', [ParkDetailController::class, 'show']);
Route::get('/regular-contract/regulationCheck', [RegularContractCreateController::class, 'regulationCheck']);
Route::get('/regular-contract/regulation', [RegularContractCreateController::class, 'showRegulation'])->name('regular_contract.regulation');
Route::post('/regular-contract/insertRegulation', [RegularContractCreateController::class, 'insertRegulation']);
Route::get('/regular-contract/input', [RegularContractCreateController::class, 'showContractForm'])->name('regular_contract.input');
Route::post('/regular_contract/input/check', [RegularContractCreateController::class, 'inputCheck'])->name('regular_contract.input.check');
Route::get('/regular-contract/upload_identity_create', [RegularContractCreateController::class, 'showUploadIdentityCreate'])->name('regular_contract.upload_identity_create');
Route::post('regular_contract/confirm_upload_identity/{contract_id}', [RegularContractCreateController::class, 'confirmUploadIdentity'])->name('regular_contract.confirm_upload_identity');
Route::get('/{management_code}/regular_contract/create', [RegularContractCreateController::class, 'show'])->name('regular_contract.create');
Route::get('/{management_code}/api/park-detail/{park_id}', [ParkDetailController::class, 'show']);
Route::get('/{management_code}/regular-contract/regulationCheck', [RegularContractCreateController::class, 'regulationCheck']);
Route::get('/{management_code}/regular-contract/regulation', [RegularContractCreateController::class, 'showRegulation'])->name('regular_contract.regulation');
Route::post('/{management_code}/regular-contract/insertRegulation', [RegularContractCreateController::class, 'insertRegulation']);
Route::get('/{management_code}/regular-contract/input', [RegularContractCreateController::class, 'showContractForm'])->name('regular_contract.input');
Route::post('/{management_code}/regular_contract/input/check', [RegularContractCreateController::class, 'inputCheck'])->name('regular_contract.input.check');
Route::get('/{management_code}/regular-contract/upload_identity_create', [RegularContractCreateController::class, 'showUploadIdentityCreate'])->name('regular_contract.upload_identity_create');
Route::post('/{management_code}/regular_contract/confirm_upload_identity/{contract_id}', [RegularContractCreateController::class, 'confirmUploadIdentity'])->name('regular_contract.confirm_upload_identity');
// SHJ-1バッチ処理結果表示ページ
Route::get('/regular-contract/upload_identity_success', [RegularContractCreateController::class, 'showUploadIdentitySuccess'])->name('regular_contract.upload_identity_success');
Route::get('/regular-contract/upload_identity_fail', [RegularContractCreateController::class, 'showUploadIdentityFail'])->name('regular_contract.upload_identity_fail');
Route::get('regular_contract/create_confirm', [RegularContractCreateController::class, 'createConfirm'])->name('regular_contract.create_confirm');
Route::post('/regular_contract/create_confirm_next/{contract_id}', [RegularContractCreateController::class, 'createConfirmNext'])->name('regular_contract.create_confirm_next');
Route::get('/regular_contract/create_confirm_error/{contract_id}', [RegularContractCreateController::class, 'createConfirmNext'])->name('regular_contract.create_confirm_error');
Route::post('/regular_contract/create_select_period', [RegularContractCreateController::class, 'selectPeriod'])->name('regular_contract.create_select_period');
Route::get('/{management_code}/regular-contract/upload_identity_success', [RegularContractCreateController::class, 'showUploadIdentitySuccess'])->name('regular_contract.upload_identity_success');
Route::get('/{management_code}/regular-contract/upload_identity_fail', [RegularContractCreateController::class, 'showUploadIdentityFail'])->name('regular_contract.upload_identity_fail');
Route::get('/{management_code}/regular_contract/create_confirm', [RegularContractCreateController::class, 'createConfirm'])->name('regular_contract.create_confirm');
Route::post('/{management_code}/regular_contract/create_confirm_next/{contract_id}', [RegularContractCreateController::class, 'createConfirmNext'])->name('regular_contract.create_confirm_next');
Route::get('/{management_code}/regular_contract/create_confirm_error/{contract_id}', [RegularContractCreateController::class, 'createConfirmNext'])->name('regular_contract.create_confirm_error');
Route::post('/{management_code}/regular_contract/create_select_period', [RegularContractCreateController::class, 'selectPeriod'])->name('regular_contract.create_select_period');
// 定期契約更新
Route::get('regular_contract/update', [RegularContractController::class, 'showInfo'])->name('regular_contract.update');
Route::get('regular_contract/update/{contract_id}', [RegularContractController::class, 'update']);
Route::get('regular_contract/confirm_category/{contract_id}', [RegularContractController::class, 'confirmCategory'])->name('regular_contract.confirm_category');
Route::get('regular_contract/confirm_category_next/{contract_id}', [RegularContractController::class, 'confirmCategoryNext'])->name('regular_contract.confirm_category_next');
Route::get('regular_contract/upload_identity/{contract_id}', [RegularContractController::class, 'uploadIdentity'])->name('regular_contract.upload_identity');
Route::post('regular_contract/upload_identity/{contract_id}', [RegularContractController::class, 'uploadIdentitySubmit'])->name('regular_contract.upload_identity_submit');
Route::get('regular_contract/select_period/{contract_id}', [RegularContractController::class, 'selectPeriod'])->name('regular_contract.select_period');
Route::post('regular_contract/update_period', [RegularContractController::class, 'updatePeriod'])->name('regular_contract.update_period');
Route::get('/{management_code}/regular_contract/update', [RegularContractController::class, 'showInfo'])->name('regular_contract.update');
Route::get('/{management_code}/regular_contract/update/{contract_id}', [RegularContractController::class, 'update']);
Route::get('/{management_code}/regular_contract/confirm_category/{contract_id}', [RegularContractController::class, 'confirmCategory'])->name('regular_contract.confirm_category');
Route::get('/{management_code}/regular_contract/confirm_category_next/{contract_id}', [RegularContractController::class, 'confirmCategoryNext'])->name('regular_contract.confirm_category_next');
Route::get('/{management_code}/regular_contract/upload_identity/{contract_id}', [RegularContractController::class, 'uploadIdentity'])->name('regular_contract.upload_identity');
Route::post('/{management_code}/regular_contract/upload_identity/{contract_id}', [RegularContractController::class, 'uploadIdentitySubmit'])->name('regular_contract.upload_identity_submit');
Route::get('/{management_code}/regular_contract/select_period/{contract_id}', [RegularContractController::class, 'selectPeriod'])->name('regular_contract.select_period');
Route::post('/{management_code}/regular_contract/update_period', [RegularContractController::class, 'updatePeriod'])->name('regular_contract.update_period');
// 定期契約履歴
Route::get('regular_contract/history', [RegularContractController::class, 'showHistory'])->name('regular_contract.history');
Route::get('/{management_code}/regular_contract/history', [RegularContractController::class, 'showHistory'])->name('regular_contract.history');
// 駐輪場検索
Route::get('park_search', [RegularContractCreateController::class, 'show'])->name('park_search');
Route::get('/{management_code}/park_search', [RegularContractCreateController::class, 'show'])->name('park_search');
// 空き待ち状況確認
Route::get('park_waitlist', [ParkWaitlistController::class, 'index'])->name('park_waitlist.index');
Route::get('/park-waitlist/check', [ParkWaitlistController::class, 'check'])->name('park_waitlist.check');
Route::get('/park-waitlist/create', [ParkWaitlistController::class, 'create'])->name('park_waitlist.create');
Route::get('/api/park-detail-wait/{reserve_id}', [ParkDetailController::class, 'showWait']);
Route::get('/park_waitlist/cancel/{reserve_id}', [ParkWaitlistController::class, 'cancelConfirm'])->name('park_waitlist.cancel');
Route::post('/park_waitlist/cancel/{reserve_id}', [ParkWaitlistController::class, 'cancel'])->name('park_waitlist.cancel.post');
Route::get('/park_waitlist/cancel/complete', function () {
Route::get('/{management_code}/park_waitlist', [ParkWaitlistController::class, 'index'])->name('park_waitlist.index');
Route::get('/{management_code}/park-waitlist/check', [ParkWaitlistController::class, 'check'])->name('park_waitlist.check');
Route::get('/{management_code}/park-waitlist/create', [ParkWaitlistController::class, 'create'])->name('park_waitlist.create');
Route::get('/{management_code}/api/park-detail-wait/{reserve_id}', [ParkDetailController::class, 'showWait']);
Route::get('/{management_code}/park_waitlist/cancel/{reserve_id}', [ParkWaitlistController::class, 'cancelConfirm'])->name('park_waitlist.cancel');
Route::post('/{management_code}/park_waitlist/cancel/{reserve_id}', [ParkWaitlistController::class, 'cancel'])->name('park_waitlist.cancel.post');
Route::get('/{management_code}/park_waitlist/cancel/complete', function () {
return view('park_waitlist.cancel_complete');
})->name('park_waitlist.cancel.complete');
// 会員へのお知らせ
Route::get('/user_information', [UserInformationController::class, 'index'])->name('user_information.index');
Route::get('/user_information/history', [UserInformationController::class, 'history'])->name('user_information.history');
Route::get('/{management_code}/user_information', [UserInformationController::class, 'index'])->name('user_information.index');
Route::get('/{management_code}/user_information/history', [UserInformationController::class, 'history'])->name('user_information.history');
});
// ウェルネット決済画面(仮)
Route::get('/wellnet/payment', function (): mixed {
$html = <<<HTML