Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 26 additions & 22 deletions laravel-app/app/Http/Controllers/BookController.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,31 +54,35 @@ public function store(Request $request)
'thumbnail_url' => 'nullable|url',
]);

// 同じISBNの最大copy_numberを取得して+1
$maxCopy = Book::where('isbn', $request->isbn)->max('copy_number') ?? 0;
$copyNumber = $maxCopy + 1;

// データベースに保存
$book = Book::create([
'title' => $request->title,
'author' => $request->author,
'isbn' => $request->isbn,
'copy_number' => $copyNumber,
'publisher' => $request->publisher,
'published_date' => $request->published_date,
'description' => $request->description,
'thumbnail_url' => $request->thumbnail_url,
]);
return \Illuminate\Support\Facades\DB::transaction(function () use ($request) {
// 同じISBNの最大copy_numberを取得して+1(排他ロック)
$maxCopy = Book::where('isbn', $request->isbn)
->lockForUpdate()
->max('copy_number') ?? 0;
$copyNumber = $maxCopy + 1;

// データベースに保存
$book = Book::create([
'title' => $request->title,
'author' => $request->author,
'isbn' => $request->isbn,
'copy_number' => $copyNumber,
'publisher' => $request->publisher,
'published_date' => $request->published_date,
'description' => $request->description,
'thumbnail_url' => $request->thumbnail_url,
]);

$copyLabel = $copyNumber > 1 ? "(冊{$copyNumber})" : '';
Log::info("Book registered: {$book->title}{$copyLabel} (ID: {$book->id})");
AuditLog::log('book_created', $book, "{$book->title}{$copyLabel}");
$copyLabel = $copyNumber > 1 ? "(冊{$copyNumber})" : '';
Log::info("Book registered: {$book->title}{$copyLabel} (ID: {$book->id})");
AuditLog::log('book_created', $book, "{$book->title}{$copyLabel}");

$message = $copyNumber > 1
? "書籍を登録しました({$copyNumber}冊目)"
: '書籍を登録しました';
$message = $copyNumber > 1
? "書籍を登録しました({$copyNumber}冊目)"
: '書籍を登録しました';

return redirect()->route('books.index')->with('success', $message);
return redirect()->route('books.index')->with('success', $message);
});
}

// ISBN検索(拡張版)
Expand Down
5 changes: 5 additions & 0 deletions laravel-app/app/Http/Controllers/LoanController.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ public function returnBook(Loan $loan)
->with('error', '他人の貸出記録は操作できません');
}

// 既に返却済みかチェック
if ($loan->returned_at) {
return redirect()->route('loans.my')->with('error', 'この貸出は既に返却済みです');
}

// 返却処理
$loan->update([
'returned_at' => now(),
Expand Down
7 changes: 7 additions & 0 deletions laravel-app/app/Http/Controllers/ProfileController.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ public function destroy(Request $request): RedirectResponse
'password' => ['required', 'current_password'],
]);

// 貸出中の本があれば削除を拒否
$activeLoanCount = $request->user()->loans()
->whereNull('returned_at')->count();
if ($activeLoanCount > 0) {
return back()->with('error', '貸出中の本があるため、アカウントを削除できません。先に返却してください。');
}

$user = $request->user();

Auth::logout();
Expand Down
4 changes: 4 additions & 0 deletions laravel-app/app/Http/Middleware/SecurityHeadersMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ public function handle(Request $request, Closure $next): Response
$response->headers->set('Referrer-Policy', 'no-referrer-when-downgrade');
$response->headers->set('Content-Security-Policy', "default-src 'self' http: https: data: blob: 'unsafe-inline' 'unsafe-eval'");

if (app()->environment('production')) {
$response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
}

return $response;
}
}
4 changes: 2 additions & 2 deletions laravel-app/routes/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
return $request->user();
});

// ISBN検索API
Route::get('/book/info/{isbn}', function (Request $request, $isbn) {
// ISBN検索API(1分あたり30回まで)
Route::middleware('throttle:30,1')->get('/book/info/{isbn}', function (Request $request, $isbn) {
try {
$service = app(BookSearchService::class);
$bookData = $service->fetchByIsbn($isbn);
Expand Down
Loading