From db57c967cf317b1c823b3da9c36790f071df1977 Mon Sep 17 00:00:00 2001 From: dohun Date: Fri, 22 Nov 2024 15:30:03 +0900 Subject: [PATCH] =?UTF-8?q?=EB=8F=84=EC=84=9C=20=EB=8C=80=EC=B6=9C=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 4 ++ .../com/study/bookcafe/common/ApiResult.java | 28 ++++++++ .../java/com/study/bookcafe/dao/BookDAO.java | 22 ++++++ .../com/study/bookcafe/dao/BorrowDAO.java | 25 +++++++ .../study/bookcafe/dao/GeneralMemberDAO.java | 23 +++++++ .../com/study/bookcafe/dao/MemberDAO.java | 7 ++ .../java/com/study/bookcafe/dto/BookDTO.java | 51 ++++++++++++++ .../com/study/bookcafe/dto/BorrowDTO.java | 22 ++++++ .../java/com/study/bookcafe/dto/Level.java | 28 ++++++++ .../com/study/bookcafe/dto/MemberDTO.java | 27 ++++++++ .../study/bookcafe/service/BorrowService.java | 7 ++ .../bookcafe/service/BorrowServiceImpl.java | 68 +++++++++++++++++++ .../com/study/bookcafe/book/BorrowTest.java | 28 ++------ .../com/study/bookcafe/borrow/BorrowTest.java | 37 ++++++++++ .../com/study/bookcafe/member/BorrowTest.java | 46 +++---------- 15 files changed, 362 insertions(+), 61 deletions(-) create mode 100644 src/main/java/com/study/bookcafe/common/ApiResult.java create mode 100644 src/main/java/com/study/bookcafe/dao/BookDAO.java create mode 100644 src/main/java/com/study/bookcafe/dao/BorrowDAO.java create mode 100644 src/main/java/com/study/bookcafe/dao/GeneralMemberDAO.java create mode 100644 src/main/java/com/study/bookcafe/dao/MemberDAO.java create mode 100644 src/main/java/com/study/bookcafe/dto/BookDTO.java create mode 100644 src/main/java/com/study/bookcafe/dto/BorrowDTO.java create mode 100644 src/main/java/com/study/bookcafe/dto/Level.java create mode 100644 src/main/java/com/study/bookcafe/dto/MemberDTO.java create mode 100644 src/main/java/com/study/bookcafe/service/BorrowService.java create mode 100644 src/main/java/com/study/bookcafe/service/BorrowServiceImpl.java create mode 100644 src/test/java/com/study/bookcafe/borrow/BorrowTest.java diff --git a/build.gradle b/build.gradle index 7d6b1bb..7a553ac 100644 --- a/build.gradle +++ b/build.gradle @@ -23,6 +23,10 @@ dependencies { developmentOnly 'org.springframework.boot:spring-boot-devtools' testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + + // https://mvnrepository.com/artifact/com.google.code.gson/gson + implementation group: 'com.google.code.gson', name: 'gson', version: '2.10.1' + } tasks.named('test') { diff --git a/src/main/java/com/study/bookcafe/common/ApiResult.java b/src/main/java/com/study/bookcafe/common/ApiResult.java new file mode 100644 index 0000000..b215197 --- /dev/null +++ b/src/main/java/com/study/bookcafe/common/ApiResult.java @@ -0,0 +1,28 @@ +package com.study.bookcafe.common; + +import com.google.gson.Gson; + +public class ApiResult { + private boolean isSuccess; + private String message; + private Object data; + + public ApiResult(boolean isSuccess, String message) { + this.isSuccess = isSuccess; + this.message = message; + } + public ApiResult(boolean isSuccess, Object data, String message) { + this.isSuccess = isSuccess; + this.data = data; + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + @Override + public String toString() { + return new Gson().toJson(this); + } +} diff --git a/src/main/java/com/study/bookcafe/dao/BookDAO.java b/src/main/java/com/study/bookcafe/dao/BookDAO.java new file mode 100644 index 0000000..8f5eeb1 --- /dev/null +++ b/src/main/java/com/study/bookcafe/dao/BookDAO.java @@ -0,0 +1,22 @@ +package com.study.bookcafe.dao; + +import com.study.bookcafe.dto.BookDTO; +import org.springframework.stereotype.Component; +import java.sql.Date; +import java.time.LocalDate; +import java.util.HashMap; +import java.util.Map; + +@Component +public class BookDAO { + + Map books = new HashMap<>(){{ + put(1L, new BookDTO(9788936433598L, "채식주의자", "한강", "창비", Date.valueOf(LocalDate.of(2007, 10, 30)), 35000)); + put(2L, new BookDTO(9788936433598L, "채식주의자", "한강", "창비", Date.valueOf(LocalDate.of(2018, 4, 25)), 13000)); + }}; + + public BookDTO findById(long bookId) { + // 추후 DB 연결 후 재작성 + return books.get(bookId); + } +} diff --git a/src/main/java/com/study/bookcafe/dao/BorrowDAO.java b/src/main/java/com/study/bookcafe/dao/BorrowDAO.java new file mode 100644 index 0000000..6d4fd26 --- /dev/null +++ b/src/main/java/com/study/bookcafe/dao/BorrowDAO.java @@ -0,0 +1,25 @@ +package com.study.bookcafe.dao; + +import com.study.bookcafe.dto.BorrowDTO; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +@Component +public class BorrowDAO { + Map borrows = new HashMap<>(){{ + put(1L, new BorrowDTO(1, 1, 9788936433598L)); + put(2L, new BorrowDTO(1, 2, 9788936433598L)); + put(3L, null); + }}; + + public BorrowDTO find(long memberId, long bookId, long ISBN) { + // 추후 DB 연결 후 재작성 + return borrows.get(memberId); + } + + public void save(BorrowDTO borrowDTO) { + // 추후 DB 연결 후 작성 + } +} diff --git a/src/main/java/com/study/bookcafe/dao/GeneralMemberDAO.java b/src/main/java/com/study/bookcafe/dao/GeneralMemberDAO.java new file mode 100644 index 0000000..cc5d151 --- /dev/null +++ b/src/main/java/com/study/bookcafe/dao/GeneralMemberDAO.java @@ -0,0 +1,23 @@ +package com.study.bookcafe.dao; + +import com.study.bookcafe.dto.Level; +import com.study.bookcafe.dto.MemberDTO; +import org.springframework.stereotype.Component; +import java.util.HashMap; +import java.util.Map; + +@Component +public class GeneralMemberDAO implements MemberDAO { + + Map members = new HashMap<>(){{ + put(1L, new MemberDTO("슈카", Level.BASIC, 0)); + put(2L, new MemberDTO("머스크", Level.WORM, 3)); + put(3L, new MemberDTO("트럼프", Level.LIBRARIAN, 5)); + }}; + + @Override + public MemberDTO findById(long memberId) { + // 추후 DB 연결 후 재작성 + return members.get(memberId); + } +} diff --git a/src/main/java/com/study/bookcafe/dao/MemberDAO.java b/src/main/java/com/study/bookcafe/dao/MemberDAO.java new file mode 100644 index 0000000..41681f5 --- /dev/null +++ b/src/main/java/com/study/bookcafe/dao/MemberDAO.java @@ -0,0 +1,7 @@ +package com.study.bookcafe.dao; + +import com.study.bookcafe.dto.MemberDTO; + +public interface MemberDAO { + MemberDTO findById(long memberId); +} diff --git a/src/main/java/com/study/bookcafe/dto/BookDTO.java b/src/main/java/com/study/bookcafe/dto/BookDTO.java new file mode 100644 index 0000000..ce8c746 --- /dev/null +++ b/src/main/java/com/study/bookcafe/dto/BookDTO.java @@ -0,0 +1,51 @@ +package com.study.bookcafe.dto; + +import java.sql.Date; + +public class BookDTO { + // 도서 한 권에 대한 정보 + private long bookId; // 도서 번호 + + // 같은 도서가 여러 권일 때, 재고나 대출 권수를 상태로 저장해야할지? or 각 도서를 다른 레코드로 저장해야할지? + + // 같은 도서 여러 권을 ISBN으로 묶어 하나의 도서에 대한 정보 + private long ISBN; // 국제표준도서번호 + private String title; // 도서 명 + private String author; // 저자 명 + private String publisher; // 출판사 + private Date publishDate; // 출판일 + private double price; // 도서 가격 + + private boolean isBorrow = true; // 현재 대출 여부 + private int inventory; // 재고 + private int borrowed; // 대출 중인 권수 + private int reservationCount; // 예약 수 + + public BookDTO(long ISBN, String title, String author, String publisher, Date publishDate, double price) { + this.ISBN = ISBN; + this.title = title; + this.author = author; + this.publisher = publisher; + this.publishDate = publishDate; + this.price = price; + } + + public BookDTO(String title, int inventory, int borrowed, int reservationCount) { + this.title = title; + this.inventory = inventory; + this.borrowed = borrowed; + this.reservationCount = reservationCount; + } + + public long getISBN() { + return ISBN; + } + + private int getBorrowed() { + return borrowed; + } + + public boolean canBorrow() { + return isBorrow; + } +} diff --git a/src/main/java/com/study/bookcafe/dto/BorrowDTO.java b/src/main/java/com/study/bookcafe/dto/BorrowDTO.java new file mode 100644 index 0000000..26aaaad --- /dev/null +++ b/src/main/java/com/study/bookcafe/dto/BorrowDTO.java @@ -0,0 +1,22 @@ +package com.study.bookcafe.dto; + +import java.sql.Timestamp; +import java.time.LocalDateTime; + +public class BorrowDTO { + private long borrowId; // 대출 ID + private long memberId; // 회원 ID + private long bookId; // 도서 ID + private long ISBN; // 국제표준도서번호 + + private Timestamp borrowDate; // 대출 날짜 + private Timestamp returnDate; // 반납 날짜 + + public BorrowDTO(long memberId, long bookId, long ISBN) { + this.memberId = memberId; + this.bookId = bookId; + this.ISBN = ISBN; + this.borrowDate = Timestamp.valueOf(LocalDateTime.now()); + this.returnDate = Timestamp.valueOf(LocalDateTime.now().plusWeeks(1)); + } +} diff --git a/src/main/java/com/study/bookcafe/dto/Level.java b/src/main/java/com/study/bookcafe/dto/Level.java new file mode 100644 index 0000000..4e08312 --- /dev/null +++ b/src/main/java/com/study/bookcafe/dto/Level.java @@ -0,0 +1,28 @@ +package com.study.bookcafe.dto; + +public enum Level { + + LIBRARIAN(2, 10,null), // 사서 회원 + WORM(1, 5,null), // 책벌레 회원 + BASIC(0, 3, WORM); // 일반 회원 + + private final int value; // 등급 값 + private final int maximumBorrowCount; // 등급 별 최대 대출 권수 + private final Level next; // 다음 등급 + + Level(int value, int maximumBorrowCount, Level next) { + this.value = value; + this.maximumBorrowCount = maximumBorrowCount; + this.next = next; + } + + /** + * 회원의 대출 가능 권수를 알려준다. + * + * @param borrowCount (현재 회원의 현재대출권수) + * @return 회원의 최대 대출 권수 - 회원의 현재 대출 권수 + */ + public boolean isBookBorrowCountLeft(int borrowCount) { + return this.maximumBorrowCount - borrowCount > 0; + } +} diff --git a/src/main/java/com/study/bookcafe/dto/MemberDTO.java b/src/main/java/com/study/bookcafe/dto/MemberDTO.java new file mode 100644 index 0000000..3939612 --- /dev/null +++ b/src/main/java/com/study/bookcafe/dto/MemberDTO.java @@ -0,0 +1,27 @@ +package com.study.bookcafe.dto; + +public class MemberDTO { + private long memberId; // 회원 ID + private String memberName; // 회원 이름 + Level level; // 회원 등급 + int borrowCount; // 현재 대출 권수 + + public MemberDTO(String memberName, Level level, int borrowCount) { + this.memberName = memberName; + this.level = level; + this.borrowCount = borrowCount; + } + + private int getBorrowCount() { + return borrowCount; + } + + /** + * 회원이 대출 가능한 상태인지 알려준다. + * + * @return 현재 회원의 대출 가능 권수가 남았는지 여부 + */ + public boolean canBorrow() { + return level.isBookBorrowCountLeft(getBorrowCount()); + } +} diff --git a/src/main/java/com/study/bookcafe/service/BorrowService.java b/src/main/java/com/study/bookcafe/service/BorrowService.java new file mode 100644 index 0000000..8b20e05 --- /dev/null +++ b/src/main/java/com/study/bookcafe/service/BorrowService.java @@ -0,0 +1,7 @@ +package com.study.bookcafe.service; + +import com.study.bookcafe.common.ApiResult; + +public interface BorrowService { + ApiResult borrowBook(long memberId, long bookId); +} diff --git a/src/main/java/com/study/bookcafe/service/BorrowServiceImpl.java b/src/main/java/com/study/bookcafe/service/BorrowServiceImpl.java new file mode 100644 index 0000000..673e22e --- /dev/null +++ b/src/main/java/com/study/bookcafe/service/BorrowServiceImpl.java @@ -0,0 +1,68 @@ +package com.study.bookcafe.service; + +import com.study.bookcafe.common.ApiResult; +import com.study.bookcafe.dao.BookDAO; +import com.study.bookcafe.dao.BorrowDAO; +import com.study.bookcafe.dao.MemberDAO; +import com.study.bookcafe.dto.BookDTO; +import com.study.bookcafe.dto.BorrowDTO; +import com.study.bookcafe.dto.MemberDTO; +import org.springframework.stereotype.Component; + +@Component +public class BorrowServiceImpl implements BorrowService { + + private final MemberDAO memberDAO; + private final BookDAO bookDAO; + private final BorrowDAO borrowDAO; + + public BorrowServiceImpl(MemberDAO memberDAO, BookDAO bookDAO, BorrowDAO borrowDAO) { + this.memberDAO = memberDAO; + this.bookDAO = bookDAO; + this.borrowDAO = borrowDAO; + } + + /** + * 회원이 도서를 대출한다. + * + * @param memberId 회원 ID + * @param bookId 도서 ID + * @return 도서 대출 결과(성공여부, 데이터, 결과메세지) + */ + @Override + public ApiResult borrowBook(long memberId, long bookId) { + + /* + 1. 회원이 대출 가능한 상태인지 확인 + 2. 회원이 대출하려는 도서가 대출 가능한 상태인지 확인 + 3. 회원이 도서를 대출 + 4. 회원-도서(도서번호, ISBN) 매핑된 대출 데이터 추가 + + 동시성 이슈 + 트랜잭션 처리 필요 + */ + + MemberDTO member = memberDAO.findById(memberId); + if(!member.canBorrow()) { + return new ApiResult(false, "현재 회원님의 대출 가능 권수가 없습니다."); + } + + BookDTO book = bookDAO.findById(bookId); + if(!book.canBorrow()) { + return new ApiResult(false, "현재 해당 도서는 모두 대출 중입니다."); + } + + BorrowDTO borrowDTO = borrowDAO.find(memberId, bookId, book.getISBN()); + if(borrowDTO != null) { + return new ApiResult(false, "이미 해당 도서를 대출 중입니다."); + } + + BorrowDTO newBorrowDTO = new BorrowDTO(memberId, bookId, book.getISBN()); + borrowDAO.save(newBorrowDTO); + + return new ApiResult(true, newBorrowDTO,"해당 도서에 대한 대출이 완료되었습니다."); + } + + + +} diff --git a/src/test/java/com/study/bookcafe/book/BorrowTest.java b/src/test/java/com/study/bookcafe/book/BorrowTest.java index 3659075..b2bf31e 100644 --- a/src/test/java/com/study/bookcafe/book/BorrowTest.java +++ b/src/test/java/com/study/bookcafe/book/BorrowTest.java @@ -1,6 +1,6 @@ package com.study.bookcafe.book; -import org.assertj.core.api.Assertions; +import com.study.bookcafe.dto.BookDTO; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -12,29 +12,11 @@ public class BorrowTest { @DisplayName("도서가 현재 대출가능한 상태인지 확인한다. (도서의 현재 대출가능수량 확인") public void checkBookStatusForBorrow() { - Book book1 = new Book("채식주의자", 3, 1, 0); - Book book2 = new Book("흰", 2, 2, 4); + BookDTO book1 = new BookDTO("채식주의자", 3, 1, 0); + BookDTO book2 = new BookDTO("흰", 2, 2, 4); - assertThat(book1.canBorrowed()).isEqualTo(true); - assertThat(book2.canBorrowed()).isEqualTo(false); + assertThat(book1.canBorrow()).isEqualTo(true); + assertThat(book2.canBorrow()).isEqualTo(false); } -} - -class Book { - String title; - int inventory; - int borrowed; - int reservationCount; - - public Book(String title, int inventory, int borrowed, int reservationCount) { - this.title = title; - this.inventory = inventory; - this.borrowed = borrowed; - this.reservationCount = reservationCount; - } - - public boolean canBorrowed() { - return inventory - borrowed > 0; - } } \ No newline at end of file diff --git a/src/test/java/com/study/bookcafe/borrow/BorrowTest.java b/src/test/java/com/study/bookcafe/borrow/BorrowTest.java new file mode 100644 index 0000000..bd52c82 --- /dev/null +++ b/src/test/java/com/study/bookcafe/borrow/BorrowTest.java @@ -0,0 +1,37 @@ +package com.study.bookcafe.borrow; + +import com.study.bookcafe.common.ApiResult; +import com.study.bookcafe.dao.BookDAO; +import com.study.bookcafe.dao.BorrowDAO; +import com.study.bookcafe.dao.GeneralMemberDAO; +import com.study.bookcafe.dao.MemberDAO; +import com.study.bookcafe.service.BorrowService; +import com.study.bookcafe.service.BorrowServiceImpl; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; + + +public class BorrowTest { + + BookDAO bookDAO = new BookDAO(); + BorrowDAO borrowDAO = new BorrowDAO(); + MemberDAO memberDAO = new GeneralMemberDAO(); + + @Test + @DisplayName("회원이 도서를 대출한다.") + public void testBorrowBook() { + + BorrowService borrowService = new BorrowServiceImpl(memberDAO, bookDAO, borrowDAO); + + ApiResult result1 = borrowService.borrowBook(1L, 1L); + System.out.println(result1); + ApiResult result2 = borrowService.borrowBook(3L, 1L); + System.out.println(result2); + + assertThat(result1.isSuccess()).isFalse(); + assertThat(result2.isSuccess()).isTrue(); + + } +} diff --git a/src/test/java/com/study/bookcafe/member/BorrowTest.java b/src/test/java/com/study/bookcafe/member/BorrowTest.java index 786c475..18eb373 100644 --- a/src/test/java/com/study/bookcafe/member/BorrowTest.java +++ b/src/test/java/com/study/bookcafe/member/BorrowTest.java @@ -1,5 +1,7 @@ package com.study.bookcafe.member; +import com.study.bookcafe.dto.Level; +import com.study.bookcafe.dto.MemberDTO; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -16,12 +18,12 @@ public void checkMemberStatusForBorrow() { 회원의 등급(enum) */ - Member member1 = new Member(Level.BASIC, 3); - Member member2 = new Member(Level.BASIC, 2); - Member member3 = new Member(Level.WORM, 5); - Member member4 = new Member(Level.WORM, 4); - Member member5 = new Member(Level.LIBRARIAN, 10); - Member member6 = new Member(Level.LIBRARIAN, 9); + MemberDTO member1 = new MemberDTO("김도훈", Level.BASIC, 3); + MemberDTO member2 = new MemberDTO("슈카", Level.BASIC, 2); + MemberDTO member3 = new MemberDTO("트럼프", Level.WORM, 5); + MemberDTO member4 = new MemberDTO("머스크", Level.WORM, 4); + MemberDTO member5 = new MemberDTO("이상혁", Level.LIBRARIAN, 10); + MemberDTO member6 = new MemberDTO("손흥민", Level.LIBRARIAN, 9); assertThat(member1.canBorrow()).isEqualTo(false); assertThat(member2.canBorrow()).isEqualTo(true); @@ -32,35 +34,3 @@ public void checkMemberStatusForBorrow() { } } - -class Member { - Level level; - int borrowCount; - - public Member(Level level, int borrowCount) { - this.level = level; - this.borrowCount = borrowCount; - } - - public boolean canBorrow() { - return level.getMaximumBorrowCount() - borrowCount > 0; - } -} - -enum Level { - LIBRARIAN(3, 10,null), WORM(2, 5,null), BASIC(1, 3, WORM); - - private int value; - private int maximumBorrowCount; - private Level next; - - Level(int value, int maximumBorrowCount, Level next) { - this.value = value; - this.maximumBorrowCount = maximumBorrowCount; - this.next = next; - } - - public int getMaximumBorrowCount() { - return maximumBorrowCount; - } -}