From 339adc143380b56ae0b574bf62879c3b906d7ab2 Mon Sep 17 00:00:00 2001 From: Beakjiyeon Date: Sat, 6 Apr 2024 11:31:26 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=EB=9E=9C=EB=8D=A4=20=EA=B8=80=20?= =?UTF-8?q?=EB=B0=B0=EC=B9=98=20=EB=B9=84=EC=A7=80=EB=8B=88=EC=8A=A4=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81,=20=EB=9E=9C=EB=8D=A4=20=EA=B8=80=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../board/controller/BoardController.java | 9 ++- .../domain/board/entity/ReadBoard.java | 17 ++++- .../board/repository/BoardRepositoryImpl.java | 31 ++++++++-- .../board/repository/ReadBoardRepository.java | 10 +++ .../repository/ReadBoardRepositoryCustom.java | 4 ++ .../repository/ReadBoardRepositoryImpl.java | 48 ++++++++++++++ .../domain/board/service/BoardService.java | 62 ++++++++++++++++++- 7 files changed, 171 insertions(+), 10 deletions(-) create mode 100644 src/main/java/com/unit/daybook/domain/board/repository/ReadBoardRepository.java create mode 100644 src/main/java/com/unit/daybook/domain/board/repository/ReadBoardRepositoryCustom.java create mode 100644 src/main/java/com/unit/daybook/domain/board/repository/ReadBoardRepositoryImpl.java diff --git a/src/main/java/com/unit/daybook/domain/board/controller/BoardController.java b/src/main/java/com/unit/daybook/domain/board/controller/BoardController.java index ec5e0c0..39b3a1b 100644 --- a/src/main/java/com/unit/daybook/domain/board/controller/BoardController.java +++ b/src/main/java/com/unit/daybook/domain/board/controller/BoardController.java @@ -31,7 +31,7 @@ public List getMyBoards() { @GetMapping("/random") public List getRandomBoards() { // 사용자가 작성한 일지 목록 조회 - // TODO api 호출 시점마다 랜덤x. 12시에 사용자가 읽지 않은 글을 today_user_table에 저장한다. 전날 건 삭제 + // api 호출 시점마다 랜덤x. 12시에 사용자가 읽지 않은 글을 today_user_table에 저장한다. 전날 건 삭제 // todo api는 today_user_table 에 있는 정보를 read. 계산 x Long memberId = 1L; // TODO 인증 return boardService.getRandomBoards(memberId); @@ -53,4 +53,11 @@ public String modifyBoard(@PathVariable("boardId") Long boardId) { public String deleteBoard(@PathVariable("boardId") Long boardId) { return "삭제 성공"; } + + + @GetMapping("/test") + public String test() { + boardService.batchReadBoard(1L); + return "test"; + } } diff --git a/src/main/java/com/unit/daybook/domain/board/entity/ReadBoard.java b/src/main/java/com/unit/daybook/domain/board/entity/ReadBoard.java index 9a511c4..e8c32f5 100644 --- a/src/main/java/com/unit/daybook/domain/board/entity/ReadBoard.java +++ b/src/main/java/com/unit/daybook/domain/board/entity/ReadBoard.java @@ -1,9 +1,11 @@ package com.unit.daybook.domain.board.entity; +import com.unit.daybook.domain.board.dto.request.AddBoardRequestDto; import com.unit.daybook.domain.common.model.BaseTimeEntity; import com.unit.daybook.domain.member.domain.Member; import jakarta.persistence.*; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -11,7 +13,7 @@ @Entity @Table(name = "read_board") @NoArgsConstructor(access = AccessLevel.PROTECTED) -class ReadBoard extends BaseTimeEntity { +public class ReadBoard extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -26,4 +28,17 @@ class ReadBoard extends BaseTimeEntity { @JoinColumn(name = "board_id") private Board board; + @Builder(access = AccessLevel.PRIVATE) + public ReadBoard(Member member, Board board) { + this.member = member; + this.board = board; + } + + public static ReadBoard createReadBoard(Member member, Board board) { + return ReadBoard.builder() + .member(member) + .board(board) + .build(); + } + } \ No newline at end of file diff --git a/src/main/java/com/unit/daybook/domain/board/repository/BoardRepositoryImpl.java b/src/main/java/com/unit/daybook/domain/board/repository/BoardRepositoryImpl.java index 17dd363..bbcecc2 100644 --- a/src/main/java/com/unit/daybook/domain/board/repository/BoardRepositoryImpl.java +++ b/src/main/java/com/unit/daybook/domain/board/repository/BoardRepositoryImpl.java @@ -2,8 +2,6 @@ import com.querydsl.jpa.impl.JPAQueryFactory; import com.unit.daybook.domain.board.entity.Board; -import com.unit.daybook.domain.board.entity.QBoard; -import com.unit.daybook.domain.member.domain.QMember; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; @@ -14,12 +12,12 @@ @Repository @RequiredArgsConstructor -public class BoardRepositoryImpl implements BoardRepositoryCustom { +public class BoardRepositoryImpl implements BoardRepositoryCustom { private final JPAQueryFactory queryFactory; public List findBoardsByMemberId(Long memberId) { - List boards = queryFactory + return queryFactory .select(board) .from(board) .join(board.memeber, member).fetchJoin() @@ -28,6 +26,29 @@ public List findBoardsByMemberId(Long memberId) { ) .fetch(); - return boards; + } + + public List findNotReadBoardsByMemberId(Long memberId, List aleadyReadBoardIds) { + return queryFactory + .select(board) + .from(board) + .join(board.memeber, member).fetchJoin() + .where( + member.id.eq(memberId) + .and(board.boardId.notIn(aleadyReadBoardIds)) + ) + .fetch(); + + } + + public List findBoardInBoardIds(List todayBoards) { + return queryFactory + .select(board) + .from(board) + .where( + (board.boardId.in(todayBoards)) + ) + .fetch(); + } } diff --git a/src/main/java/com/unit/daybook/domain/board/repository/ReadBoardRepository.java b/src/main/java/com/unit/daybook/domain/board/repository/ReadBoardRepository.java new file mode 100644 index 0000000..fe16e9a --- /dev/null +++ b/src/main/java/com/unit/daybook/domain/board/repository/ReadBoardRepository.java @@ -0,0 +1,10 @@ +package com.unit.daybook.domain.board.repository; + +import com.unit.daybook.domain.board.entity.ReadBoard; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ReadBoardRepository extends JpaRepository { + +} diff --git a/src/main/java/com/unit/daybook/domain/board/repository/ReadBoardRepositoryCustom.java b/src/main/java/com/unit/daybook/domain/board/repository/ReadBoardRepositoryCustom.java new file mode 100644 index 0000000..ef89535 --- /dev/null +++ b/src/main/java/com/unit/daybook/domain/board/repository/ReadBoardRepositoryCustom.java @@ -0,0 +1,4 @@ +package com.unit.daybook.domain.board.repository; + +public interface ReadBoardRepositoryCustom { +} diff --git a/src/main/java/com/unit/daybook/domain/board/repository/ReadBoardRepositoryImpl.java b/src/main/java/com/unit/daybook/domain/board/repository/ReadBoardRepositoryImpl.java new file mode 100644 index 0000000..65ae1e8 --- /dev/null +++ b/src/main/java/com/unit/daybook/domain/board/repository/ReadBoardRepositoryImpl.java @@ -0,0 +1,48 @@ +package com.unit.daybook.domain.board.repository; + +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; + +import static com.unit.daybook.domain.board.entity.QBoard.board; +import static com.unit.daybook.domain.board.entity.QReadBoard.readBoard; +import static com.unit.daybook.domain.member.domain.QMember.member; + +@Repository +@RequiredArgsConstructor +public class ReadBoardRepositoryImpl implements BoardRepositoryCustom { + private final JPAQueryFactory queryFactory; + + public List findBoardsByMemberId(Long memberId) { + + List boards = queryFactory + .select(readBoard.board.boardId) + .from(readBoard) + .innerJoin(readBoard.member, member) + .where( + member.id.eq(memberId) + ) + .fetch(); + + return boards; + } + + public List findTodayBoardsByMemberId(Long memberId) { + LocalDate currentDate = LocalDateTime.now().toLocalDate(); // 현재 날짜의 일자만 추출 + + return queryFactory + .select(readBoard.readBoardId) + .from(readBoard) + .innerJoin(readBoard.board, board) + .innerJoin(readBoard.member, member) + .where( + member.id.eq(memberId) + .and(readBoard.createdAt.between(currentDate.atStartOfDay(), currentDate.atStartOfDay().plusDays(1).minusNanos(1)) + )) + .fetch(); + } +} diff --git a/src/main/java/com/unit/daybook/domain/board/service/BoardService.java b/src/main/java/com/unit/daybook/domain/board/service/BoardService.java index b249973..b782b56 100644 --- a/src/main/java/com/unit/daybook/domain/board/service/BoardService.java +++ b/src/main/java/com/unit/daybook/domain/board/service/BoardService.java @@ -3,17 +3,21 @@ import com.unit.daybook.domain.board.dto.request.AddBoardRequestDto; import com.unit.daybook.domain.board.dto.response.AddBoardResponseDto; import com.unit.daybook.domain.board.entity.Board; +import com.unit.daybook.domain.board.entity.ReadBoard; import com.unit.daybook.domain.board.repository.BoardRepository; import com.unit.daybook.domain.board.repository.BoardRepositoryImpl; +import com.unit.daybook.domain.board.repository.ReadBoardRepository; +import com.unit.daybook.domain.board.repository.ReadBoardRepositoryImpl; import com.unit.daybook.domain.member.domain.Member; import com.unit.daybook.domain.member.repository.MemberRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; +import java.util.Random; @Transactional @Service @@ -23,6 +27,8 @@ public class BoardService { private final BoardRepository boardRepository; private final BoardRepositoryImpl boardRepositoryImpl; private final MemberRepository memberRepository; + private final ReadBoardRepositoryImpl readBoardRepositoryImpl; + private final ReadBoardRepository readBoardRepository; public AddBoardResponseDto addBoard(AddBoardRequestDto addBoardRequestDto, Long memberId) { Member member = memberRepository.findById(memberId).orElseThrow(() -> new RuntimeException(memberId + "not found")); @@ -36,12 +42,13 @@ public AddBoardResponseDto addBoard(AddBoardRequestDto addBoardRequestDto, Long return AddBoardResponseDto.from(boardRepository.save(Board.createBoard(addBoardRequestDto, member))); } - + @Transactional(readOnly = true) public AddBoardResponseDto getBoard(Long boardId) { return AddBoardResponseDto.from( boardRepository.findById(boardId).orElseThrow(() -> new RuntimeException(boardId + "not found"))); } + @Transactional(readOnly = true) public List getMyBoards(Long memberId) { // TODO 페이지네이션 - 스와이프 방식? return boardRepositoryImpl.findBoardsByMemberId(memberId) @@ -50,7 +57,56 @@ public List getMyBoards(Long memberId) { .toList(); } + @Transactional(readOnly = true) public List getRandomBoards(Long memberId) { - return null; + return getTodayBoardByMemberId(memberId); + } + + + public void batchReadBoard(Long memberId) { + // 이미 읽은 일지 + List aleadyReadBoardIds = readBoardRepositoryImpl.findBoardsByMemberId(memberId); + + // 안 읽은 일지 + List notReadBoards = boardRepositoryImpl.findNotReadBoardsByMemberId(memberId, aleadyReadBoardIds); + + // 적재할 일지 고유 id + List randomIdxs = selectRandomNumbers(notReadBoards.size() - 1); + + // 적재 + Member member = memberRepository.findById(memberId).orElseThrow(() -> new RuntimeException(memberId + "not found")); + readBoardRepository.save(ReadBoard.createReadBoard(member, notReadBoards.get(Math.toIntExact(randomIdxs.get(0))))); + readBoardRepository.save(ReadBoard.createReadBoard(member, notReadBoards.get(Math.toIntExact(randomIdxs.get(1))))); + readBoardRepository.save(ReadBoard.createReadBoard(member, notReadBoards.get(Math.toIntExact(randomIdxs.get(2))))); + + } + + public List getTodayBoardByMemberId(Long memberId) { + List todayBoards = readBoardRepositoryImpl.findTodayBoardsByMemberId(memberId); + List result = boardRepositoryImpl.findBoardInBoardIds(todayBoards); + return result.stream() + .map(AddBoardResponseDto::from) + .toList(); + } + + public static List selectRandomNumbers(long n) { + // 1부터 n 까지의 수를 리스트에 추가 + List numbers = new ArrayList<>(); + for (long i = 1; i <= n; i++) { + numbers.add(i); + } + + // 랜덤으로 3개를 선택하기 위해 새 리스트를 생성 + List randomNumbers = new ArrayList<>(); + Random random = new Random(); + for (int i = 0; i < 3; i++) { + // 리스트에서 랜덤하게 하나의 수를 선택하여 결과 리스트에 추가 + int randomIndex = random.nextInt(numbers.size()); + randomNumbers.add(numbers.get(randomIndex)); + // 선택된 수는 다시 리스트에서 제거하여 중복 선택을 방지 + numbers.remove(randomIndex); + } + + return randomNumbers; } } From ec436381f83fcf5446c63e02795b9175449b3c3e Mon Sep 17 00:00:00 2001 From: Beakjiyeon Date: Sat, 6 Apr 2024 12:36:03 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20=EC=8A=A4=EC=BC=80=EC=A4=84?= =?UTF-8?q?=EB=9F=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/unit/daybook/DaybookApplication.java | 2 ++ .../board/controller/BoardController.java | 19 +++++------- .../daybook/domain/board/entity/Board.java | 2 +- .../board/repository/BoardRepositoryImpl.java | 2 +- .../domain/board/service/BoardService.java | 30 +++++++++++-------- .../scheduler/todayBoardsScheduler.java | 27 +++++++++++++++++ 6 files changed, 56 insertions(+), 26 deletions(-) create mode 100644 src/main/java/com/unit/daybook/scheduler/todayBoardsScheduler.java diff --git a/src/main/java/com/unit/daybook/DaybookApplication.java b/src/main/java/com/unit/daybook/DaybookApplication.java index 1542a71..6eb1cfc 100644 --- a/src/main/java/com/unit/daybook/DaybookApplication.java +++ b/src/main/java/com/unit/daybook/DaybookApplication.java @@ -3,7 +3,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +import org.springframework.scheduling.annotation.EnableScheduling; +@EnableScheduling @EnableJpaAuditing @SpringBootApplication public class DaybookApplication { diff --git a/src/main/java/com/unit/daybook/domain/board/controller/BoardController.java b/src/main/java/com/unit/daybook/domain/board/controller/BoardController.java index 39b3a1b..987e702 100644 --- a/src/main/java/com/unit/daybook/domain/board/controller/BoardController.java +++ b/src/main/java/com/unit/daybook/domain/board/controller/BoardController.java @@ -20,19 +20,21 @@ public AddBoardResponseDto getBoard(@PathVariable("boardId") Long boardId) { return boardService.getBoard(boardId); } + /** + * 사용자가 작성한 일지 목록 조회 + */ @GetMapping("/boards") public List getMyBoards() { - // 사용자가 작성한 일지 목록 조회 Long memberId = 1L; // TODO 인증 return boardService.getMyBoards(memberId); } - // TODO 사용자가 보지 않은 글 중에서 랜덤 3개 골라 주기 + /** + * 사용자가 보지 않은 글 중에서 랜덤 3개 골라 주기 + * 밤 12시에 적재된 사용자가 읽지 않은 글을 조회 + */ @GetMapping("/random") public List getRandomBoards() { - // 사용자가 작성한 일지 목록 조회 - // api 호출 시점마다 랜덤x. 12시에 사용자가 읽지 않은 글을 today_user_table에 저장한다. 전날 건 삭제 - // todo api는 today_user_table 에 있는 정보를 read. 계산 x Long memberId = 1L; // TODO 인증 return boardService.getRandomBoards(memberId); } @@ -41,7 +43,6 @@ public List getRandomBoards() { public AddBoardResponseDto addBoard(@RequestBody AddBoardRequestDto addBoardRequestDto) { Long memberId = 1L; // TODO 인증 return boardService.addBoard(addBoardRequestDto, memberId); - } @PostMapping("/{boardId}") @@ -54,10 +55,4 @@ public String deleteBoard(@PathVariable("boardId") Long boardId) { return "삭제 성공"; } - - @GetMapping("/test") - public String test() { - boardService.batchReadBoard(1L); - return "test"; - } } diff --git a/src/main/java/com/unit/daybook/domain/board/entity/Board.java b/src/main/java/com/unit/daybook/domain/board/entity/Board.java index fe4a3b0..0079bc5 100644 --- a/src/main/java/com/unit/daybook/domain/board/entity/Board.java +++ b/src/main/java/com/unit/daybook/domain/board/entity/Board.java @@ -50,7 +50,7 @@ public static Board createBoard(AddBoardRequestDto addBoardRequestDto, Member me .respectBoardId(addBoardRequestDto.respectBoardId()) .member(member) .category(addBoardRequestDto.category()) - .hearts(0L) // todo + .hearts(0L) .build(); } diff --git a/src/main/java/com/unit/daybook/domain/board/repository/BoardRepositoryImpl.java b/src/main/java/com/unit/daybook/domain/board/repository/BoardRepositoryImpl.java index bbcecc2..18d1eb4 100644 --- a/src/main/java/com/unit/daybook/domain/board/repository/BoardRepositoryImpl.java +++ b/src/main/java/com/unit/daybook/domain/board/repository/BoardRepositoryImpl.java @@ -46,7 +46,7 @@ public List findBoardInBoardIds(List todayBoards) { .select(board) .from(board) .where( - (board.boardId.in(todayBoards)) + board.boardId.in(todayBoards) ) .fetch(); diff --git a/src/main/java/com/unit/daybook/domain/board/service/BoardService.java b/src/main/java/com/unit/daybook/domain/board/service/BoardService.java index b782b56..89d42e3 100644 --- a/src/main/java/com/unit/daybook/domain/board/service/BoardService.java +++ b/src/main/java/com/unit/daybook/domain/board/service/BoardService.java @@ -63,22 +63,28 @@ public List getRandomBoards(Long memberId) { } - public void batchReadBoard(Long memberId) { - // 이미 읽은 일지 - List aleadyReadBoardIds = readBoardRepositoryImpl.findBoardsByMemberId(memberId); + public void batchReadBoard() { - // 안 읽은 일지 - List notReadBoards = boardRepositoryImpl.findNotReadBoardsByMemberId(memberId, aleadyReadBoardIds); + List memberIds = memberRepository.findAll().stream().map(Member::getId).toList(); - // 적재할 일지 고유 id - List randomIdxs = selectRandomNumbers(notReadBoards.size() - 1); + for (int i = 0; i < memberIds.size(); i++) { + Long memberId = memberIds.get(i); + // 이미 읽은 일지 + List aleadyReadBoardIds = readBoardRepositoryImpl.findBoardsByMemberId(memberId); - // 적재 - Member member = memberRepository.findById(memberId).orElseThrow(() -> new RuntimeException(memberId + "not found")); - readBoardRepository.save(ReadBoard.createReadBoard(member, notReadBoards.get(Math.toIntExact(randomIdxs.get(0))))); - readBoardRepository.save(ReadBoard.createReadBoard(member, notReadBoards.get(Math.toIntExact(randomIdxs.get(1))))); - readBoardRepository.save(ReadBoard.createReadBoard(member, notReadBoards.get(Math.toIntExact(randomIdxs.get(2))))); + // 안 읽은 일지 + List notReadBoards = boardRepositoryImpl.findNotReadBoardsByMemberId(memberId, aleadyReadBoardIds); + + // 적재할 일지 고유 id + List randomIdxs = selectRandomNumbers(notReadBoards.size() - 1); + // 적재 + Member member = memberRepository.findById(memberId).orElseThrow(() -> new RuntimeException(memberId + "not found")); + readBoardRepository.save(ReadBoard.createReadBoard(member, notReadBoards.get(Math.toIntExact(randomIdxs.get(0))))); + readBoardRepository.save(ReadBoard.createReadBoard(member, notReadBoards.get(Math.toIntExact(randomIdxs.get(1))))); + readBoardRepository.save(ReadBoard.createReadBoard(member, notReadBoards.get(Math.toIntExact(randomIdxs.get(2))))); + + } } public List getTodayBoardByMemberId(Long memberId) { diff --git a/src/main/java/com/unit/daybook/scheduler/todayBoardsScheduler.java b/src/main/java/com/unit/daybook/scheduler/todayBoardsScheduler.java new file mode 100644 index 0000000..c474c62 --- /dev/null +++ b/src/main/java/com/unit/daybook/scheduler/todayBoardsScheduler.java @@ -0,0 +1,27 @@ +package com.unit.daybook.scheduler; + +import com.unit.daybook.domain.board.service.BoardService; +import com.unit.daybook.domain.member.domain.Member; +import com.unit.daybook.domain.member.repository.MemberRepository; +import com.unit.daybook.domain.member.repository.MemberRepositoryImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.util.List; + +@RequiredArgsConstructor +@Component +public class todayBoardsScheduler { + private final BoardService boardService; + private final MemberRepository memberRepository; + @Scheduled(cron = "0 0 0 * * *", zone = "Asia/Seoul") + public void run() { + System.out.println(LocalDateTime.now().toString()); + List memberIds = memberRepository.findAll().stream().map(Member::getId).toList(); + + boardService.batchReadBoard(memberId); + + } +} From 0b839bf1465ffa5e16fa4ed352559f008a683009 Mon Sep 17 00:00:00 2001 From: Beakjiyeon Date: Sat, 6 Apr 2024 12:40:24 +0900 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20=EC=98=A4=ED=83=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/unit/daybook/scheduler/todayBoardsScheduler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/unit/daybook/scheduler/todayBoardsScheduler.java b/src/main/java/com/unit/daybook/scheduler/todayBoardsScheduler.java index c474c62..8a3d21a 100644 --- a/src/main/java/com/unit/daybook/scheduler/todayBoardsScheduler.java +++ b/src/main/java/com/unit/daybook/scheduler/todayBoardsScheduler.java @@ -21,7 +21,7 @@ public void run() { System.out.println(LocalDateTime.now().toString()); List memberIds = memberRepository.findAll().stream().map(Member::getId).toList(); - boardService.batchReadBoard(memberId); + boardService.batchReadBoard(); } }