From e7278a6cf9f156c39b024c8d5e9527e15293250c Mon Sep 17 00:00:00 2001 From: song Date: Fri, 2 Aug 2024 19:52:09 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20#8=20=EB=B2=84=EC=8A=A4=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EB=A6=AC=EB=B7=B0=20CRUD=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 버스 리뷰 조회 (버스, 정류장, 시간대 옵션) - 버스 리뷰 생성 - 버스 리뷰 수정 (리뷰, 시간대, 별점 옵션) - 버스 리뷰 삭제 --- .../controller/BusReviewController.java | 63 +++++ .../review/dao/BusReviewRepository.java | 17 ++ .../server/review/dto/BusReviewReqDto.java | 41 ++++ .../server/review/dto/BusReviewRespDto.java | 39 ++++ .../review/service/BusReviewService.java | 80 +++++++ .../review/service/BusReviewServiceTest.java | 215 ++++++++++++++++++ 6 files changed, 455 insertions(+) create mode 100644 server/src/main/java/com/talkka/server/review/controller/BusReviewController.java create mode 100644 server/src/main/java/com/talkka/server/review/dao/BusReviewRepository.java create mode 100644 server/src/main/java/com/talkka/server/review/dto/BusReviewReqDto.java create mode 100644 server/src/main/java/com/talkka/server/review/dto/BusReviewRespDto.java create mode 100644 server/src/main/java/com/talkka/server/review/service/BusReviewService.java create mode 100644 server/src/test/java/com/talkka/server/review/service/BusReviewServiceTest.java diff --git a/server/src/main/java/com/talkka/server/review/controller/BusReviewController.java b/server/src/main/java/com/talkka/server/review/controller/BusReviewController.java new file mode 100644 index 00000000..ccacd696 --- /dev/null +++ b/server/src/main/java/com/talkka/server/review/controller/BusReviewController.java @@ -0,0 +1,63 @@ +package com.talkka.server.review.controller; + +import java.util.List; + +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.SessionAttribute; + +import com.talkka.server.review.dto.BusReviewReqDto; +import com.talkka.server.review.dto.BusReviewRespDto; +import com.talkka.server.review.service.BusReviewService; + +import lombok.RequiredArgsConstructor; + +@RestController +@RequestMapping("/api/bus-review") +@RequiredArgsConstructor +public class BusReviewController { + + private final BusReviewService busReviewService; + + @GetMapping("") + public List getBusReviewList( + @SessionAttribute(name = "userId") Long userId, + @RequestParam Long routeId, + @RequestParam Long stationId, + @RequestParam Integer timeSlot + ) { + return busReviewService.getBusReviewList(userId, routeId, stationId, timeSlot); + } + + @PostMapping("") + public void saveBusReview( + @SessionAttribute(name = "userId") Long userId, + @RequestBody BusReviewReqDto busReviewReqDto + ) { + busReviewService.createBusReview(userId, busReviewReqDto); + } + + @PutMapping("{bus_review_id}") + public BusReviewRespDto updateBusReview( + @SessionAttribute(name = "userId") Long userId, + @PathVariable(name = "bus_review_id") Long busReviewId, + @RequestBody BusReviewReqDto busReviewReqDto + ) { + return busReviewService.updateBusReview(busReviewId, busReviewReqDto); + } + + @DeleteMapping("{bus_review_id}") + public void deleteBusReview( + @SessionAttribute(name = "userId") Long userId, + @PathVariable(name = "bus_review_id") Long busReviewId + ) { + busReviewService.deleteBusReview(busReviewId); + } +} diff --git a/server/src/main/java/com/talkka/server/review/dao/BusReviewRepository.java b/server/src/main/java/com/talkka/server/review/dao/BusReviewRepository.java new file mode 100644 index 00000000..128fed84 --- /dev/null +++ b/server/src/main/java/com/talkka/server/review/dao/BusReviewRepository.java @@ -0,0 +1,17 @@ +package com.talkka.server.review.dao; + +import java.util.List; +import java.util.Optional; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface BusReviewRepository extends JpaRepository { + + public Optional> findAllByUserIdAndRouteIdAndStationIdAndTimeSlot( + Long userId, + Long routeId, + Long stationId, + Integer timeSlot); +} diff --git a/server/src/main/java/com/talkka/server/review/dto/BusReviewReqDto.java b/server/src/main/java/com/talkka/server/review/dto/BusReviewReqDto.java new file mode 100644 index 00000000..c0786f3f --- /dev/null +++ b/server/src/main/java/com/talkka/server/review/dto/BusReviewReqDto.java @@ -0,0 +1,41 @@ +package com.talkka.server.review.dto; + +import java.util.Optional; + +import com.talkka.server.bus.dao.BusRouteEntity; +import com.talkka.server.bus.dao.BusRouteStationEntity; +import com.talkka.server.review.dao.BusReviewEntity; +import com.talkka.server.user.dao.UserEntity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Getter +@Builder +@ToString +@EqualsAndHashCode +@NoArgsConstructor +@AllArgsConstructor +public class BusReviewReqDto { + + private Long routeId; + private Long busRouteStationId; + private Optional content; + private Integer timeSlot; + private Integer rating; + + public BusReviewEntity toEntity(UserEntity user, BusRouteStationEntity station, BusRouteEntity route) { + return BusReviewEntity.builder() + .content(content.orElse(null)) + .timeSlot(timeSlot) + .rating(rating) + .writer(user) + .station(station) + .route(route) + .build(); + } +} diff --git a/server/src/main/java/com/talkka/server/review/dto/BusReviewRespDto.java b/server/src/main/java/com/talkka/server/review/dto/BusReviewRespDto.java new file mode 100644 index 00000000..099dca63 --- /dev/null +++ b/server/src/main/java/com/talkka/server/review/dto/BusReviewRespDto.java @@ -0,0 +1,39 @@ +package com.talkka.server.review.dto; + +import java.util.Optional; + +import com.talkka.server.review.dao.BusReviewEntity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Getter +@Builder +@ToString +@EqualsAndHashCode +@NoArgsConstructor +@AllArgsConstructor +public class BusReviewRespDto { + + private Long userId; + private Long routeId; + private Long busRouteStationId; + private Optional content; + private Integer timeSlot; + private Integer rating; + + public static BusReviewRespDto of(BusReviewEntity busEntity) { + return BusReviewRespDto.builder() + .userId(busEntity.getWriter().getUserId()) + .routeId(busEntity.getRoute().getRouteId()) + .busRouteStationId(busEntity.getStation().getBusRouteStationId()) + .content(Optional.ofNullable(busEntity.getContent())) + .timeSlot(busEntity.getTimeSlot()) + .rating(busEntity.getRating()) + .build(); + } +} diff --git a/server/src/main/java/com/talkka/server/review/service/BusReviewService.java b/server/src/main/java/com/talkka/server/review/service/BusReviewService.java new file mode 100644 index 00000000..3b9b17be --- /dev/null +++ b/server/src/main/java/com/talkka/server/review/service/BusReviewService.java @@ -0,0 +1,80 @@ +package com.talkka.server.review.service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import org.springframework.stereotype.Service; + +import com.talkka.server.bus.dao.BusRouteEntity; +import com.talkka.server.bus.dao.BusRouteRepository; +import com.talkka.server.bus.dao.BusRouteStationEntity; +import com.talkka.server.bus.dao.BusRouteStationRepository; +import com.talkka.server.common.exception.http.NotFoundException; +import com.talkka.server.review.dao.BusReviewEntity; +import com.talkka.server.review.dao.BusReviewRepository; +import com.talkka.server.review.dto.BusReviewReqDto; +import com.talkka.server.review.dto.BusReviewRespDto; +import com.talkka.server.user.dao.UserEntity; +import com.talkka.server.user.dao.UserRepository; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class BusReviewService { + + private final BusReviewRepository busReviewRepository; + private final UserRepository userRepository; + private final BusRouteStationRepository busRouteStationRepository; + private final BusRouteRepository busRouteRepository; + + public List getBusReviewList( + Long userId, Long routeId, Long stationId, Integer timeSlot + ) { + Optional> optionalList = busReviewRepository.findAllByUserIdAndRouteIdAndStationIdAndTimeSlot( + userId, routeId, stationId, timeSlot); + + List list = optionalList.orElseGet(ArrayList::new); + + return list.stream() + .map(BusReviewRespDto::of) + .collect(Collectors.toList()); + } + + public BusReviewRespDto createBusReview(Long userId, BusReviewReqDto busReviewReqDto) { + final UserEntity user = userRepository.findById(userId) + .orElseThrow(() -> new NotFoundException("존재하지 않는 유저입니다.")); + + final BusRouteStationEntity station = busRouteStationRepository.findById(busReviewReqDto.getBusRouteStationId()) + .orElseThrow(() -> new NotFoundException("존재하지 않는 경유 정류장입니다.")); + + final BusRouteEntity route = busRouteRepository.findById(busReviewReqDto.getRouteId()) + .orElseThrow(() -> new NotFoundException("존재하지 않는 노선입니다.")); + + final BusReviewEntity entity = busReviewReqDto.toEntity(user, station, route); + + BusReviewEntity savedReview = busReviewRepository.save(entity); + + return BusReviewRespDto.of(savedReview); + } + + public BusReviewRespDto updateBusReview(Long busReviewId, BusReviewReqDto busReviewReqDto) { + final BusReviewEntity review = busReviewRepository.findById(busReviewId) + .orElseThrow(() -> new NotFoundException("존재하지 않는 리뷰입니다.")); + + review.setContent(busReviewReqDto.getContent() + .orElse(null)); + review.setTimeSlot(busReviewReqDto.getTimeSlot()); + review.setRating(busReviewReqDto.getRating()); + + BusReviewEntity updatedReview = busReviewRepository.save(review); + + return BusReviewRespDto.of(updatedReview); + } + + public void deleteBusReview(Long busReviewId) { + busReviewRepository.deleteById(busReviewId); + } +} diff --git a/server/src/test/java/com/talkka/server/review/service/BusReviewServiceTest.java b/server/src/test/java/com/talkka/server/review/service/BusReviewServiceTest.java new file mode 100644 index 00000000..45fd328c --- /dev/null +++ b/server/src/test/java/com/talkka/server/review/service/BusReviewServiceTest.java @@ -0,0 +1,215 @@ +package com.talkka.server.review.service; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.BDDMockito.*; + +import java.util.Optional; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.talkka.server.bus.dao.BusRouteEntity; +import com.talkka.server.bus.dao.BusRouteRepository; +import com.talkka.server.bus.dao.BusRouteStationEntity; +import com.talkka.server.bus.dao.BusRouteStationRepository; +import com.talkka.server.common.exception.http.NotFoundException; +import com.talkka.server.review.dao.BusReviewEntity; +import com.talkka.server.review.dao.BusReviewRepository; +import com.talkka.server.review.dto.BusReviewReqDto; +import com.talkka.server.review.dto.BusReviewRespDto; +import com.talkka.server.user.dao.UserEntity; +import com.talkka.server.user.dao.UserRepository; + +@ExtendWith(MockitoExtension.class) +public class BusReviewServiceTest { + + @InjectMocks + private BusReviewService busReviewService; + + @Mock + private BusReviewRepository busReviewRepository; + + @Mock + private UserRepository userRepository; + + @Mock + private BusRouteStationRepository busRouteStationRepository; + + @Mock + private BusRouteRepository busRouteRepository; + + private BusReviewRespDto busReviewRespDtoFixture(Long userId) { + return BusReviewRespDto.builder() + .userId(userId) + .routeId(236000050L) + .busRouteStationId(1L) + .content(Optional.of("리뷰 내용")) + .timeSlot(24) + .rating(4) + .build(); + } + + final UserEntity user = UserEntity.builder() + .userId(1L) + .build(); + final BusRouteStationEntity station = BusRouteStationEntity.builder() + .busRouteStationId(1L) + .build(); + final BusRouteEntity route = BusRouteEntity.builder() + .routeId(236000050L) + .build(); + + @Nested + @DisplayName("createBusReview 메서드 테스트") + public class createBusReviewTest { + + @Test + void 제한된_요청에_따라_버스리뷰를_생성한다() { + //given + final BusReviewReqDto busReviewReqDto = BusReviewReqDto.builder() + .busRouteStationId(1L) + .routeId(236000050L) + .content(Optional.of("리뷰 내용")) + .timeSlot(24) + .rating(4) + .build(); + + final BusReviewEntity busReviewEntity = BusReviewEntity.builder() + .content("리뷰 내용") + .timeSlot(24) + .rating(4) + .writer(user) + .station(station) + .route(route) + .build(); + + given(userRepository.findById(1L)).willReturn(Optional.of(user)); + given(busRouteStationRepository.findById(1L)).willReturn(Optional.of(station)); + given(busRouteRepository.findById(236000050L)).willReturn(Optional.of(route)); + given(busReviewRepository.save(any(BusReviewEntity.class))).willReturn(busReviewEntity); + + final BusReviewRespDto resultDto = busReviewRespDtoFixture(1L); + + //when + final BusReviewRespDto result = busReviewService.createBusReview(1L, busReviewReqDto); + + //then + assertThat(result).isEqualTo(resultDto); + } + + @Test + void 유저를_못찾을_경우_Exception을_throw_한다() { + //given + final Class exceptionClass = NotFoundException.class; // 추후 변경될 가능성이 있어, 변수로 따로 지정함 + BusReviewReqDto busReviewReqDto = BusReviewReqDto.builder() + .build(); + + //when + //then + assertThatThrownBy(() -> busReviewService.createBusReview(null, busReviewReqDto)) + .isInstanceOf(exceptionClass) + .hasMessage("존재하지 않는 유저입니다."); + } + + @Test + void 경유_정류장을_못찾을_경우_Exceptio을_throw_한다() { + //given + final Class exceptionClass = NotFoundException.class; // 추후 변경될 가능성이 있어, 변수로 따로 지정함 + final BusReviewReqDto busReviewReqDto = BusReviewReqDto.builder() + .busRouteStationId(null) + .routeId(236000050L) + .content(Optional.of("리뷰 내용")) + .timeSlot(24) + .rating(4) + .build(); + + given(userRepository.findById(1L)).willReturn(Optional.of(user)); + + //when + //then + assertThatThrownBy(() -> busReviewService.createBusReview(1L, busReviewReqDto)) + .isInstanceOf(exceptionClass) + .hasMessage("존재하지 않는 경유 정류장입니다."); + } + + @Test + void 노선을_못찾을_경우_Exceptio을_throw_한다() { + //given + final Class exceptionClass = NotFoundException.class; // 추후 변경될 가능성이 있어, 변수로 따로 지정함 + final BusReviewReqDto busReviewReqDto = BusReviewReqDto.builder() + .busRouteStationId(1L) + .routeId(null) + .content(Optional.of("리뷰 내용")) + .timeSlot(24) + .rating(4) + .build(); + + given(userRepository.findById(1L)).willReturn(Optional.of(user)); + given(busRouteStationRepository.findById(1L)).willReturn(Optional.of(station)); + + //when + //then + assertThatThrownBy(() -> busReviewService.createBusReview(1L, busReviewReqDto)) + .isInstanceOf(exceptionClass) + .hasMessage("존재하지 않는 노선입니다."); + } + } + + @Nested + @DisplayName("updateBusReviewList 메서드 테스트") + public class updateBusReviewTest { + + BusReviewReqDto busReviewReqDto = BusReviewReqDto.builder() + .routeId(236000050L) + .busRouteStationId(1L) + .content(Optional.of("변경된 리뷰 내용")) + .timeSlot(23) + .rating(5) + .build(); + + @Test + void 리뷰를_찾지_못하는_경우_Exception을_throw_한다() { + //given + final Class exceptionClass = NotFoundException.class; // 추후 변경될 가능성이 있어, 변수로 따로 지정함 + + //when + //then + assertThatThrownBy( + () -> busReviewService.updateBusReview(1L, busReviewReqDto)) + .isInstanceOf(exceptionClass) + .hasMessage("존재하지 않는 리뷰입니다."); + } + + @Test + void 리뷰를_업데이트_한_경우_업데이트_된_BusReviewEntity의_BusReviewRespDto를_반환한다() { + //given + BusReviewEntity originEntity = BusReviewEntity.builder() + .busReviewId(1L) + .content("리뷰 내용") + .timeSlot(24) + .rating(4) + .writer(user) + .station(station) + .route(route) + .build(); + + BusReviewEntity updatedEntity = busReviewReqDto.toEntity(user, station, route); + + given(busReviewRepository.findById(1L)).willReturn(Optional.of(originEntity)); + given(busReviewRepository.save(originEntity)).willReturn(updatedEntity); + + BusReviewRespDto resultDto = BusReviewRespDto.of(updatedEntity); + + //when + BusReviewRespDto updatedReview = busReviewService.updateBusReview(1L, busReviewReqDto); + + //then + assertThat(updatedReview).isEqualTo(resultDto); + } + } +}