개발 기록/도서관 관리 시스템

[Library Management System] 25.09.11 (44일)

dev.jelee 2025. 9. 11. 14:53

[ 작업한 내용 ]

<도서 대출 연장 기능 추가>

1. messages.properties

- 도서 대출 연장 관련 메시지 정의.

// 성공메시지
success.loan.extended=도서 대출 기간이 성공적으로 연장되었습니다.

// 에러메시지
error.loan.already_extended=해당 대출은 이미 연장되었습니다.
error.loan.cannot_extended=이 대출은 연장할 수 없습니다.

2. LoanSuccessCode

- 도서 대출 연장 성공 코드 정의.

LOAN_EXTENDED_SUCCESS(HttpStatus.OK, "LOAN_202", "success.loan.extended");

3. LoanErrorCode

- 도서 대출 연장 에러 코드 정의.

LOAN_ALREADY_EXTENDED(HttpStatus.BAD_REQUEST, "LOAN_402", "error.loan.already_extended");
LOAN_CANNOT_BE_EXTENDED(HttpStatus.BAD_REQUEST, "LOAN_403","error.loan.cannot_extended");

4. AdminLoanExtendedResDTO

- 도서 대출 연장 응답 DTO 정의.

- id, username, bookTitle, loanDate, dueDate, extended, status 필드

- Loan 엔티티를 파라미터로 받는 AdminLoanExtendedResDTO 생성자 함수 정의

@Getter
public class AdminLoanExtendedResDTO {
    private Long id;
    private String username;
    private String bookTitle;
    private LocalDateTime loanDate;
    private LocalDateTime dueDate;
    private boolean extended;
    private LoanStatus status;

    public AdminLoanExtendedResDTO(Loan loan) {
        this.id = loan.getId();
        this.username = loan.getUser().getUsername();
        this.bookTitle = loan.getBook().getTitle();
        this.loanDate = loan.getLoanDate();
        this.dueDate = loan.getDueDate();
        this.extended = loan.isExtended();
        this.status = loan.getStatus();
    }
}

5. AdminLoanController

- PATH /api/v1/admin/loans/{loanId}/extend 로 요청한다.

- @PathVariable로 loanId를 파라미터로 받는다.

- Service 계층으로 loanId를 전달하여 도서 대출 연장 로직을 처리하고, 결과를 AdminLoanExtendedResDTO 형태로 저장한다.

- 성공 메시지와 응답 DTO를 ApiResponse.success()로 랩핑하여 응답 바디로 반환한다.

@PatchMapping("/{loanId}/extend")
public ResponseEntity<?> extendLoan(@PathVariable("loanId") Long loanId) {

    // 서비스로직
    AdminLoanExtendedResDTO responseDTO = adminLoanService.extendLoan(loanId);

    // 성공메시지
    String message = messageProvider.getMessage(LoanSuccessCode.LOAN_EXTENDED_SUCCESS.getMessage());

    // 응답
    return ResponseEntity
              .status(LoanSuccessCode.LOAN_EXTENDED_SUCCESS.getHttpStatus())
              .body(ApiResponse.success(
                LoanSuccessCode.LOAN_EXTENDED_SUCCESS, 
                message, 
                responseDTO));
}

6. AdminLoanService

- loanId로 Repository.findById() 메서드를 호출해 해당 도서 대출 내역을 조회하여 Loan 엔티티로 저장한다. 도서 대출 내역이 없다면 예외를 던진다.

- Loan 상태가 대출중인 것만 연장을 할 수 있기 때문에 if문을 사용하여 Loan의 status가 LOANED이 아니면 LoanErrorCode.LOAN_CANNOT_BE_EXTENDED 예외를 던진다.

- 도서 대출 연장은 1회만 가능하기 때문에 Loan의 대출 연장 여부를 체크하고 연장이 1번이라도 있다면 LoanErrorCode.LOAN_ALREADY_EXTENDED 예외를 던진다.

- Loan의 대출 연장 여부를 setExtended() 메서드를 사용하여 true로 변경해주고, 대출 기간을 현재 대출 기간에 +7을 해준다.

- Controller로 반환할 때에는 Loan을 기반으로 AdminLoanExtendedResDTO 객체를 생성하여 반환한다.

@Transactional
public AdminLoanExtendedResDTO extendLoan(Long loanId) {

    // loanId로 조회 및 예외처리
    Loan loan = loanRepository.findById(loanId)
        .orElseThrow(() -> new BaseException(LoanErrorCode.LOAN_NOT_FOUND));

    // loan 상태가 대출중인 것만 가능
    // 연체, 반납, 분실 상태는 불가
    if (loan.getStatus() != LoanStatus.LOANED) {
      throw new BaseException(LoanErrorCode.LOAN_CANNOT_BE_EXTENDED);
    }

    // loan 대출 여부 체크 및 예외처리
    if (loan.isExtended() != false) {
      throw new BaseException(LoanErrorCode.LOAN_ALREADY_EXTENDED);
    }

    // 대출 연장 및 반납 기간 업데이트
    // 대출 연장은 1회 가능, 7일 추가
    loan.setExtended(true);
    loan.setDueDate(loan.getDueDate().plusDays(7));

    // 반환
    return new AdminLoanExtendedResDTO(loan);
}

commit
도서 대출 성공(좌), 도서 대출 연장 성공(가운데), 도서 대출 연장 실패(우)
도서 대출 연장 하기 전(좌), 도서 대출 연장 후(우)