package com.example.library.service;
import com.example.library.model.BookItem;
import com.example.library.model.BorrowRecord;
import com.example.library.model.User;
import com.example.library.repository.BookItemRepository;
import com.example.library.repository.BorrowRecordRepository;
import com.example.library.repository.UserRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
/**
* 図書管理の主要なビジネスロジック(貸出・返却など)を担当するサービスクラス。
*/
@Service
public class LibraryService {
private final BorrowRecordRepository borrowRecordRepository;
private final BookItemRepository bookItemRepository;
private final UserRepository userRepository;
public LibraryService(BorrowRecordRepository borrowRecordRepository,
BookItemRepository bookItemRepository,
UserRepository userRepository) {
this.borrowRecordRepository = borrowRecordRepository;
this.bookItemRepository = bookItemRepository;
this.userRepository = userRepository;
}
/**
* 蔵書を借りるロジック
* @param userHrid ユーザーID
* @param serialNumber 蔵書のシリアルナンバー
*/
@Transactional
public BorrowRecord borrowBook(String userHrid, String serialNumber) {
// 1. ユーザーと対象の書籍が存在するか確認する
User user = userRepository.findById(userHrid)
.orElseThrow(() -> new IllegalArgumentException("指定されたユーザーが存在しません。"));
BookItem bookItem = bookItemRepository.findById(serialNumber)
.orElseThrow(() -> new IllegalArgumentException("指定された書籍が存在しません。"));
// 2. すでに他の利用者が借りていないか(貸出中ではないか)の確認
// returnDateが未設定(null)のレコードが存在する場合、その本はまだ返却されていません。
boolean isAlreadyBorrowed = borrowRecordRepository.findBySerialNumberAndReturnDateIsNull(serialNumber).isPresent();
if (isAlreadyBorrowed) {
throw new IllegalStateException("この書籍はすでに貸し出し中です。");
}
// 3. ユーザーの現在の貸出状況を取得
List<BorrowRecord> currentBorrows = borrowRecordRepository.findByUserHridAndReturnDateIsNull(userHrid);
// ビジネスルール: 1人の利用者が同時に借りられる冊数は5冊まで
if (currentBorrows.size() >= 5) {
throw new IllegalStateException("同時に借りられる書籍は最大5冊までです。");
}
// ビジネスルール: 1人の利用者に同書籍タイトルを同時に貸し出すことはできない
String targetTitleId = bookItem.getBookTitle().getId();
for (BorrowRecord record : currentBorrows) {
// 現在貸出中のシリアルナンバーから書籍情報を取得し、タイトルIDを比較する
BookItem borrowedItem = bookItemRepository.findById(record.getSerialNumber()).orElseThrow();
if (borrowedItem.getBookTitle().getId().equals(targetTitleId)) {
throw new IllegalStateException("すでに同じタイトルの書籍を借りています。");
}
}
// 4. 貸出記録の作成と保存
BorrowRecord newRecord = new BorrowRecord();
newRecord.setUserHrid(user.getHrid());
newRecord.setSerialNumber(bookItem.getSerialNumber());
newRecord.setBorrowDate(LocalDateTime.now());
// ビジネスルール: 貸し出された書籍は2週間後が返却期限となる
newRecord.setDueDate(LocalDateTime.now().plusWeeks(2));
// returnDateはセットしない(nullのまま)ことで「貸出中」を表現します
return borrowRecordRepository.save(newRecord);
}
/**
* 蔵書を返却するロジック(※要件に従い詳細な解説コメントを記載)
*
* @param userHrid 返却操作を行うユーザーID
* @param serialNumber 返却する書籍のシリアルナンバー
*/
@Transactional
public void returnBook(String userHrid, String serialNumber) {
/*
* 【返却ロジックの解説】
*
* 1. 貸出中レコードの特定
* まず、指定されたユーザーが指定された書籍(シリアルナンバー)を「現在借りているか」を確認します。
* これを判定するためには、DBから「userHrid」と「serialNumber」が一致し、
* かつ「returnDate(返却日)がnull」であるBorrowRecord(貸出履歴レコード)を検索します。
* ※ returnDateがnullである = まだ返却されていない(現在貸出中である)ことを意味します。
*/
BorrowRecord currentRecord = borrowRecordRepository.findBySerialNumberAndReturnDateIsNull(serialNumber)
.orElseThrow(() -> new IllegalStateException("この書籍は現在貸し出しされていません。"));
/*
* 2. 操作権限(借りている本人か)の検証
* 返却手続きは、その本を借りている本人(または管理者)しか行えないべきです。
* 上記で取得した貸出中レコードのuserHridと、操作しているユーザーのuserHridが一致するかチェックします。
*/
if (!currentRecord.getUserHrid().equals(userHrid)) {
throw new IllegalStateException("この書籍は別の利用者が借りているため、返却できません。");
}
/*
* 3. 返却日時の記録とステータス更新
* 貸出中(未返却)であることを表していた `returnDate` に、現在日時をセットします。
* これにより、このレコードは「返却済み」扱いとなり、
* 次回の `findBySerialNumberAndReturnDateIsNull` 検索にはヒットしなくなります。
* 結果として、この書籍(シリアルナンバー)は他のユーザーが新たに借りられる状態(貸出可能)に戻ります。
*/
currentRecord.setReturnDate(LocalDateTime.now());
/*
* 4. データベースへの保存
* 更新したレコードをDBに保存(UPDATE)して返却処理を完了させます。
* ※ @Transactional アノテーションがついているため、途中でエラーが起きた場合は全ての変更がロールバックされ、
* データの不整合(返却日だけ入ってしまった等)を防ぐ安全な設計になっています。
*/
borrowRecordRepository.save(currentRecord);
}
}
1

Register as a new user and use Qiita more conveniently
- You get articles that match your needs
- You can efficiently read back useful information
- You can use dark theme

