diff --git a/src/main/java/dev/steady/application/controller/ApplicationController.java b/src/main/java/dev/steady/application/controller/ApplicationController.java index c130cf4..21cb725 100644 --- a/src/main/java/dev/steady/application/controller/ApplicationController.java +++ b/src/main/java/dev/steady/application/controller/ApplicationController.java @@ -56,8 +56,8 @@ public ResponseEntity getApplicationDetail(@PathVaria @PatchMapping("/applications/{applicationId}/status") public ResponseEntity updateApplicationStatus(@PathVariable Long applicationId, - @RequestBody ApplicationStatusUpdateRequest request, - @Auth UserInfo userInfo) { + @RequestBody ApplicationStatusUpdateRequest request, + @Auth UserInfo userInfo) { applicationService.updateStatusOfApplication(applicationId, request, userInfo); return ResponseEntity.noContent().build(); } diff --git a/src/main/java/dev/steady/global/config/QueryDslConfig.java b/src/main/java/dev/steady/global/config/QueryDslConfig.java index 2bfe6b8..2191e95 100644 --- a/src/main/java/dev/steady/global/config/QueryDslConfig.java +++ b/src/main/java/dev/steady/global/config/QueryDslConfig.java @@ -1,5 +1,6 @@ package dev.steady.global.config; +import com.querydsl.jpa.JPQLTemplates; import com.querydsl.jpa.impl.JPAQueryFactory; import jakarta.persistence.EntityManager; import org.springframework.beans.factory.annotation.Autowired; @@ -14,7 +15,7 @@ public class QueryDslConfig { @Bean public JPAQueryFactory jpaQueryFactory() { - return new JPAQueryFactory(entityManager); + return new JPAQueryFactory(JPQLTemplates.DEFAULT, entityManager); } } diff --git a/src/main/java/dev/steady/notification/domain/ApplicationResultNotificationStrategy.java b/src/main/java/dev/steady/notification/domain/ApplicationResultNotificationStrategy.java index d8655d8..b751f8d 100644 --- a/src/main/java/dev/steady/notification/domain/ApplicationResultNotificationStrategy.java +++ b/src/main/java/dev/steady/notification/domain/ApplicationResultNotificationStrategy.java @@ -3,7 +3,6 @@ import dev.steady.application.domain.Application; import dev.steady.application.domain.ApplicationStatus; import dev.steady.steady.domain.Steady; -import dev.steady.user.domain.User; public class ApplicationResultNotificationStrategy extends NotificationStrategy { diff --git a/src/main/java/dev/steady/notification/domain/FreshApplicationNotificationStrategy.java b/src/main/java/dev/steady/notification/domain/FreshApplicationNotificationStrategy.java index 49e0ed2..db237c4 100644 --- a/src/main/java/dev/steady/notification/domain/FreshApplicationNotificationStrategy.java +++ b/src/main/java/dev/steady/notification/domain/FreshApplicationNotificationStrategy.java @@ -1,7 +1,6 @@ package dev.steady.notification.domain; import dev.steady.steady.domain.Steady; -import dev.steady.user.domain.User; public class FreshApplicationNotificationStrategy extends NotificationStrategy { diff --git a/src/main/java/dev/steady/notification/domain/NotificationMessage.java b/src/main/java/dev/steady/notification/domain/NotificationMessage.java index c02ce80..9e4a7a5 100644 --- a/src/main/java/dev/steady/notification/domain/NotificationMessage.java +++ b/src/main/java/dev/steady/notification/domain/NotificationMessage.java @@ -8,7 +8,8 @@ public enum NotificationMessage { FRESH_APPLICATION("[%s] 스테디에 새로운 신청서가 도착했어요."), APPLICATION_ACCEPTED("[%s] 스터디에 제출한 신청서가 수락됐어요."), - APPLICATION_REJECTED("[%s] 스터디에 제출한 신청서가 거절됐어요."),; + APPLICATION_REJECTED("[%s] 스터디에 제출한 신청서가 거절됐어요."), + ; private final String message; diff --git a/src/main/java/dev/steady/review/controller/ReviewController.java b/src/main/java/dev/steady/review/controller/ReviewController.java index a819459..7984466 100644 --- a/src/main/java/dev/steady/review/controller/ReviewController.java +++ b/src/main/java/dev/steady/review/controller/ReviewController.java @@ -2,10 +2,14 @@ import dev.steady.global.auth.Auth; import dev.steady.global.auth.UserInfo; -import dev.steady.review.dto.ReviewCreateRequest; +import dev.steady.review.dto.request.ReviewCreateRequest; +import dev.steady.review.dto.response.ReviewMyResponse; +import dev.steady.review.dto.response.ReviewSwitchResponse; import dev.steady.review.service.ReviewService; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -29,8 +33,22 @@ public ResponseEntity createReview(@PathVariable Long steadyId, reviewService.createUserCards(request); return ResponseEntity.created( - URI.create(String.format("/api/v1/steadies/%d/review/%d", steadyId, reviewId))) + URI.create(String.format("/api/v1/reviews/%d", reviewId))) .build(); } + @PatchMapping("/reviews/{reviewId}") + public ResponseEntity updateReviewIsPublic(@PathVariable Long reviewId, + @Auth UserInfo userInfo) { + ReviewSwitchResponse response = reviewService.switchReviewIsPublic(reviewId, userInfo); + + return ResponseEntity.ok(response); + } + + @GetMapping("/reviews/my") + public ResponseEntity getMyCardsAndReviews(@Auth UserInfo userInfo) { + ReviewMyResponse response = reviewService.getMyCardsAndReviews(userInfo); + return ResponseEntity.ok(response); + } + } diff --git a/src/main/java/dev/steady/review/domain/Review.java b/src/main/java/dev/steady/review/domain/Review.java index 79f7d10..6496354 100644 --- a/src/main/java/dev/steady/review/domain/Review.java +++ b/src/main/java/dev/steady/review/domain/Review.java @@ -56,4 +56,8 @@ private Review(Participant reviewer, this.isPublic = true; } + public void switchIsPublic() { + this.isPublic = !isPublic; + } + } diff --git a/src/main/java/dev/steady/review/domain/repository/ReviewRepository.java b/src/main/java/dev/steady/review/domain/repository/ReviewRepository.java index e9b4afa..6a6138e 100644 --- a/src/main/java/dev/steady/review/domain/repository/ReviewRepository.java +++ b/src/main/java/dev/steady/review/domain/repository/ReviewRepository.java @@ -9,7 +9,7 @@ import static dev.steady.review.exception.ReviewErrorCode.REVIEW_NOT_FOUND; public interface ReviewRepository extends JpaRepository { - + default Review getById(Long reviewId) { return findById(reviewId) .orElseThrow(() -> new NotFoundException(REVIEW_NOT_FOUND)); diff --git a/src/main/java/dev/steady/review/dto/ReviewCreateRequest.java b/src/main/java/dev/steady/review/dto/request/ReviewCreateRequest.java similarity index 93% rename from src/main/java/dev/steady/review/dto/ReviewCreateRequest.java rename to src/main/java/dev/steady/review/dto/request/ReviewCreateRequest.java index 3e6c76d..12ed22f 100644 --- a/src/main/java/dev/steady/review/dto/ReviewCreateRequest.java +++ b/src/main/java/dev/steady/review/dto/request/ReviewCreateRequest.java @@ -1,4 +1,4 @@ -package dev.steady.review.dto; +package dev.steady.review.dto.request; import dev.steady.review.domain.Review; import dev.steady.steady.domain.Participant; diff --git a/src/main/java/dev/steady/review/dto/response/ReviewDetailResponse.java b/src/main/java/dev/steady/review/dto/response/ReviewDetailResponse.java new file mode 100644 index 0000000..63171c4 --- /dev/null +++ b/src/main/java/dev/steady/review/dto/response/ReviewDetailResponse.java @@ -0,0 +1,11 @@ +package dev.steady.review.dto.response; + +import java.time.LocalDateTime; + +public record ReviewDetailResponse( + Long reviewId, + String comment, + boolean isPublic, + LocalDateTime createdAt +) { +} diff --git a/src/main/java/dev/steady/review/dto/response/ReviewMyResponse.java b/src/main/java/dev/steady/review/dto/response/ReviewMyResponse.java new file mode 100644 index 0000000..ccfc0a1 --- /dev/null +++ b/src/main/java/dev/steady/review/dto/response/ReviewMyResponse.java @@ -0,0 +1,15 @@ +package dev.steady.review.dto.response; + +import java.util.List; + +public record ReviewMyResponse( + List userCards, + List reviews +) { + + public static ReviewMyResponse of(List userCards, + List reviews) { + return new ReviewMyResponse(userCards, reviews); + } + +} diff --git a/src/main/java/dev/steady/review/dto/response/ReviewSwitchResponse.java b/src/main/java/dev/steady/review/dto/response/ReviewSwitchResponse.java new file mode 100644 index 0000000..18109fb --- /dev/null +++ b/src/main/java/dev/steady/review/dto/response/ReviewSwitchResponse.java @@ -0,0 +1,12 @@ +package dev.steady.review.dto.response; + +import dev.steady.review.domain.Review; + +public record ReviewSwitchResponse( + boolean isPublic +) { + + public static ReviewSwitchResponse from(Review review) { + return new ReviewSwitchResponse(review.isPublic()); + } +} diff --git a/src/main/java/dev/steady/review/dto/response/ReviewsBySteadyResponse.java b/src/main/java/dev/steady/review/dto/response/ReviewsBySteadyResponse.java new file mode 100644 index 0000000..8b818d6 --- /dev/null +++ b/src/main/java/dev/steady/review/dto/response/ReviewsBySteadyResponse.java @@ -0,0 +1,10 @@ +package dev.steady.review.dto.response; + +import java.util.List; + +public record ReviewsBySteadyResponse( + Long steadyId, + String steadyName, + List reviews +) { +} diff --git a/src/main/java/dev/steady/review/exception/ReviewErrorCode.java b/src/main/java/dev/steady/review/exception/ReviewErrorCode.java index ade1b8e..2d2bf0f 100644 --- a/src/main/java/dev/steady/review/exception/ReviewErrorCode.java +++ b/src/main/java/dev/steady/review/exception/ReviewErrorCode.java @@ -8,7 +8,8 @@ public enum ReviewErrorCode implements ErrorCode { CARD_NOT_FOUND("R002", "카드를 찾을 수 없습니다."), REVIEW_NOT_ENABLED("R003", "리뷰할 수 있는 상태가 아닙니다"), REVIEWEE_EQUALS_REVIEWER("R004", "리뷰이와 리뷰어가 동일합니다."), - REVIEW_DUPLICATE("R005", "리뷰를 중복 제출할 수 없습니다."); + REVIEW_DUPLICATE("R005", "리뷰를 중복 제출할 수 없습니다."), + REVIEW_AUTH_FAILURE("R006", "리뷰에 접근 권한이 없습니다."); private final String code; private final String message; diff --git a/src/main/java/dev/steady/review/infrastructure/ReviewQueryRepository.java b/src/main/java/dev/steady/review/infrastructure/ReviewQueryRepository.java index 7073367..9e5b11f 100644 --- a/src/main/java/dev/steady/review/infrastructure/ReviewQueryRepository.java +++ b/src/main/java/dev/steady/review/infrastructure/ReviewQueryRepository.java @@ -1,11 +1,14 @@ package dev.steady.review.infrastructure; +import dev.steady.review.dto.response.ReviewsBySteadyResponse; import dev.steady.user.domain.User; import java.util.List; public interface ReviewQueryRepository { - List findPublicCommentsByRevieweeUser(User user); + List getPublicCommentsByRevieweeUser(User user); + + List getAllReviewsByRevieweeUser(User user); } diff --git a/src/main/java/dev/steady/review/infrastructure/ReviewQueryRepositoryImpl.java b/src/main/java/dev/steady/review/infrastructure/ReviewQueryRepositoryImpl.java index 008ded2..c8d7bf1 100644 --- a/src/main/java/dev/steady/review/infrastructure/ReviewQueryRepositoryImpl.java +++ b/src/main/java/dev/steady/review/infrastructure/ReviewQueryRepositoryImpl.java @@ -1,7 +1,10 @@ package dev.steady.review.infrastructure; +import com.querydsl.core.types.Projections; import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.jpa.impl.JPAQueryFactory; +import dev.steady.review.dto.response.ReviewDetailResponse; +import dev.steady.review.dto.response.ReviewsBySteadyResponse; import dev.steady.user.domain.User; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; @@ -9,7 +12,10 @@ import java.util.List; +import static com.querydsl.core.group.GroupBy.groupBy; +import static com.querydsl.core.group.GroupBy.list; import static dev.steady.review.domain.QReview.review; +import static dev.steady.steady.domain.QSteady.steady; @Repository @RequiredArgsConstructor @@ -19,7 +25,7 @@ public class ReviewQueryRepositoryImpl implements ReviewQueryRepository { private final JPAQueryFactory jpaQueryFactory; @Override - public List findPublicCommentsByRevieweeUser(User user) { + public List getPublicCommentsByRevieweeUser(User user) { return jpaQueryFactory.select(review.comment) .from(review) .where(revieweeEqualsUser(user), @@ -28,6 +34,28 @@ public List findPublicCommentsByRevieweeUser(User user) { .fetch(); } + @Override + public List getAllReviewsByRevieweeUser(User user) { + return jpaQueryFactory.selectFrom(steady) + .leftJoin(review) + .on(steady.id.eq(review.steady.id)) + .where(revieweeEqualsUser(user)) + .orderBy(steady.finishedAt.desc()) + .transform(groupBy(steady.id) + .list(Projections.constructor( + ReviewsBySteadyResponse.class, + steady.id, + steady.name, + list(Projections.constructor( + ReviewDetailResponse.class, + review.id, + review.comment, + review.isPublic, + review.createdAt + )) + ))); + } + private BooleanExpression revieweeEqualsUser(User user) { return review.reviewee.user.eq(user); } diff --git a/src/main/java/dev/steady/review/infrastructure/UserCardQueryRepository.java b/src/main/java/dev/steady/review/infrastructure/UserCardQueryRepository.java index 94d02b9..1db10d7 100644 --- a/src/main/java/dev/steady/review/infrastructure/UserCardQueryRepository.java +++ b/src/main/java/dev/steady/review/infrastructure/UserCardQueryRepository.java @@ -7,6 +7,6 @@ public interface UserCardQueryRepository { - List findCardCountByUser(User user); + List getCardCountByUser(User user); } diff --git a/src/main/java/dev/steady/review/infrastructure/UserCardQueryRepositoryImpl.java b/src/main/java/dev/steady/review/infrastructure/UserCardQueryRepositoryImpl.java index e8720d4..cb05847 100644 --- a/src/main/java/dev/steady/review/infrastructure/UserCardQueryRepositoryImpl.java +++ b/src/main/java/dev/steady/review/infrastructure/UserCardQueryRepositoryImpl.java @@ -21,7 +21,7 @@ public class UserCardQueryRepositoryImpl implements UserCardQueryRepository { private final JPAQueryFactory jpaQueryFactory; @Override - public List findCardCountByUser(User user) { + public List getCardCountByUser(User user) { return jpaQueryFactory.select(Projections.constructor(UserCardResponse.class, card.id, card.content, diff --git a/src/main/java/dev/steady/review/service/ReviewService.java b/src/main/java/dev/steady/review/service/ReviewService.java index 60f54a3..d946a95 100644 --- a/src/main/java/dev/steady/review/service/ReviewService.java +++ b/src/main/java/dev/steady/review/service/ReviewService.java @@ -1,6 +1,7 @@ package dev.steady.review.service; import dev.steady.global.auth.UserInfo; +import dev.steady.global.exception.ForbiddenException; import dev.steady.global.exception.InvalidStateException; import dev.steady.review.domain.Card; import dev.steady.review.domain.Review; @@ -8,7 +9,13 @@ import dev.steady.review.domain.repository.CardRepository; import dev.steady.review.domain.repository.ReviewRepository; import dev.steady.review.domain.repository.UserCardRepository; -import dev.steady.review.dto.ReviewCreateRequest; +import dev.steady.review.dto.request.ReviewCreateRequest; +import dev.steady.review.dto.response.ReviewMyResponse; +import dev.steady.review.dto.response.ReviewSwitchResponse; +import dev.steady.review.dto.response.ReviewsBySteadyResponse; +import dev.steady.review.dto.response.UserCardResponse; +import dev.steady.review.infrastructure.ReviewQueryRepository; +import dev.steady.review.infrastructure.UserCardQueryRepository; import dev.steady.steady.domain.Participant; import dev.steady.steady.domain.Participants; import dev.steady.steady.domain.Steady; @@ -23,6 +30,7 @@ import java.util.Objects; import static dev.steady.review.exception.ReviewErrorCode.REVIEWEE_EQUALS_REVIEWER; +import static dev.steady.review.exception.ReviewErrorCode.REVIEW_AUTH_FAILURE; import static dev.steady.review.exception.ReviewErrorCode.REVIEW_DUPLICATE; import static dev.steady.review.exception.ReviewErrorCode.REVIEW_NOT_ENABLED; @@ -35,6 +43,8 @@ public class ReviewService { private final UserRepository userRepository; private final CardRepository cardRepository; private final UserCardRepository userCardRepository; + private final UserCardQueryRepository userCardQueryRepository; + private final ReviewQueryRepository reviewQueryRepository; @Transactional public Long createReview(Long steadyId, ReviewCreateRequest request, UserInfo userInfo) { @@ -75,6 +85,32 @@ public void createUserCards(ReviewCreateRequest request) { userCardRepository.saveAll(userCards); } + /** + * 리뷰의 공개 여부(isPublic)을 변경하고 그 결과를 반환한다. + * + * @param reviewId 리뷰 식별자 + * @param userInfo 사용자 정보 + * @return 리뷰 공개 여부 변경된 결과 + */ + @Transactional + public ReviewSwitchResponse switchReviewIsPublic(Long reviewId, UserInfo userInfo) { + Review review = reviewRepository.getById(reviewId); + Participant reviewee = review.getReviewee(); + if (!Objects.equals(reviewee.getUserId(), userInfo.userId())) { + throw new ForbiddenException(REVIEW_AUTH_FAILURE); + } + review.switchIsPublic(); + return ReviewSwitchResponse.from(review); + } + + @Transactional(readOnly = true) + public ReviewMyResponse getMyCardsAndReviews(UserInfo userInfo) { + User user = userRepository.getUserBy(userInfo.userId()); + List userCards = userCardQueryRepository.getCardCountByUser(user); + List reviews = reviewQueryRepository.getAllReviewsByRevieweeUser(user); + return ReviewMyResponse.of(userCards, reviews); + } + private boolean isAlreadyReviewed(Participant reviewer, Participant reviewee, Steady steady) { return reviewRepository.existsByReviewerAndRevieweeAndSteady( reviewer, diff --git a/src/main/java/dev/steady/user/service/UserService.java b/src/main/java/dev/steady/user/service/UserService.java index d326bdd..1b4c86f 100644 --- a/src/main/java/dev/steady/user/service/UserService.java +++ b/src/main/java/dev/steady/user/service/UserService.java @@ -79,8 +79,8 @@ public UserOtherDetailResponse getOtherUserDetail(Long userId) { User user = userRepository.getUserBy(userId); List userStacks = userStackRepository.findAllByUser(user); UserDetailResponse userDetailResponse = UserDetailResponse.of(user, userStacks); - List userCardResponses = userCardQueryRepository.findCardCountByUser(user); - List reviews = reviewQueryRepository.findPublicCommentsByRevieweeUser(user); + List userCardResponses = userCardQueryRepository.getCardCountByUser(user); + List reviews = reviewQueryRepository.getPublicCommentsByRevieweeUser(user); return UserOtherDetailResponse.of( userDetailResponse, diff --git a/src/test/java/dev/steady/global/config/ControllerTestConfig.java b/src/test/java/dev/steady/global/config/ControllerTestConfig.java index 1e7ec29..7a44b99 100644 --- a/src/test/java/dev/steady/global/config/ControllerTestConfig.java +++ b/src/test/java/dev/steady/global/config/ControllerTestConfig.java @@ -11,6 +11,8 @@ import dev.steady.global.auth.AuthContext; import dev.steady.notification.controller.NotificationController; import dev.steady.notification.service.NotificationService; +import dev.steady.review.controller.ReviewController; +import dev.steady.review.service.ReviewService; import dev.steady.steady.controller.SteadyController; import dev.steady.steady.controller.SteadyLikeController; import dev.steady.steady.service.SteadyLikeService; @@ -48,6 +50,7 @@ StackController.class, PositionController.class, NotificationController.class, + ReviewController.class, AuthContext.class, JwtResolver.class, JwtProperties.class, @@ -83,6 +86,8 @@ public abstract class ControllerTestConfig { @MockBean protected NotificationService notificationService; @MockBean + protected ReviewService reviewService; + @MockBean protected JwtResolver jwtResolver; @BeforeEach diff --git a/src/test/java/dev/steady/review/controller/ReviewControllerTest.java b/src/test/java/dev/steady/review/controller/ReviewControllerTest.java new file mode 100644 index 0000000..3805773 --- /dev/null +++ b/src/test/java/dev/steady/review/controller/ReviewControllerTest.java @@ -0,0 +1,132 @@ +package dev.steady.review.controller; + +import com.epages.restdocs.apispec.Schema; +import dev.steady.global.auth.Authentication; +import dev.steady.global.config.ControllerTestConfig; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.resourceDetails; +import static dev.steady.global.auth.AuthFixture.createUserInfo; +import static dev.steady.review.fixture.ReviewFixture.createReviewCreateRequest; +import static dev.steady.review.fixture.ReviewFixture.createReviewMyResponse; +import static dev.steady.review.fixture.ReviewFixture.createReviewSwitchResponse; +import static org.mockito.BDDMockito.given; +import static org.springframework.http.HttpHeaders.AUTHORIZATION; +import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.patch; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.restdocs.payload.JsonFieldType.ARRAY; +import static org.springframework.restdocs.payload.JsonFieldType.BOOLEAN; +import static org.springframework.restdocs.payload.JsonFieldType.NUMBER; +import static org.springframework.restdocs.payload.JsonFieldType.STRING; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class ReviewControllerTest extends ControllerTestConfig { + + @Test + @DisplayName("리뷰이 ID와 스테디 ID를 통해 리뷰를 생성할 수 있다.") + void createReviewTest() throws Exception { + // given + var userId = 1L; + var userInfo = createUserInfo(userId); + var steadyId = 1L; + var request = createReviewCreateRequest(); + var auth = new Authentication(userId); + given(jwtResolver.getAuthentication(TOKEN)).willReturn(auth); + given(reviewService.createReview(steadyId, request, userInfo)).willReturn(1L); + + // when, then + mockMvc.perform(post("/api/v1/steadies/{steadyId}/review", steadyId) + .contentType(APPLICATION_JSON) + .header(AUTHORIZATION, TOKEN) + .content(objectMapper.writeValueAsString(request)) + ) + .andDo(document("review-v1-create", + resourceDetails().tag("리뷰").description("리뷰 생성") + .requestSchema(Schema.schema("ReviewCreateRequest")), + requestHeaders( + headerWithName(AUTHORIZATION).description("토큰") + ), + requestFields( + fieldWithPath("revieweeId").type(NUMBER).description("리뷰이 사용자 식별자"), + fieldWithPath("cardsId").type(ARRAY).description("카드 식별자 리스트"), + fieldWithPath("comment").type(STRING).description("리뷰 코멘트") + ) + )) + .andExpect(status().isCreated()) + .andExpect(redirectedUrl(String.format("/api/v1/reviews/%d", steadyId, 1L))); + } + + @Test + @DisplayName("리뷰이는 자신이 받은 리뷰의 공개 여부를 설정할 수 있다.") + void updateReviewIsPublicTest() throws Exception { + // given + var userId = 1L; + var userInfo = createUserInfo(userId); + var auth = new Authentication(userId); + given(jwtResolver.getAuthentication(TOKEN)).willReturn(auth); + + var reviewId = 1L; + var response = createReviewSwitchResponse(true); + given(reviewService.switchReviewIsPublic(reviewId, userInfo)).willReturn(response); + + // when, then + mockMvc.perform(patch("/api/v1/reviews/{reviewId}", reviewId) + .contentType(APPLICATION_JSON) + .header(AUTHORIZATION, TOKEN) + ) + .andDo(document("review-v1-update", + resourceDetails().tag("리뷰").description("리뷰 공개 여부 수정") + .responseSchema(Schema.schema("ReviewSwitchRepsonse")), + responseFields( + fieldWithPath("isPublic").type(BOOLEAN).description("수정된 리뷰 공개 여부 상태") + ) + )) + .andExpect(status().isOk()) + .andExpect(content().string(objectMapper.writeValueAsString(response))); + } + + @Test + @DisplayName("내가 받은 카드 및 리뷰 목록을 조회할 수 있다.") + void getOtherUserDetail() throws Exception { + // given + var userId = 1L; + var userInfo = createUserInfo(userId); + var auth = new Authentication(userId); + var response = createReviewMyResponse(); + given(jwtResolver.getAuthentication(TOKEN)).willReturn(auth); + given(reviewService.getMyCardsAndReviews(userInfo)).willReturn(response); + + // when, then + mockMvc.perform(get("/api/v1/reviews/my") + .header(AUTHORIZATION, TOKEN) + ) + .andDo(document("review-v1-get-myAll", + resourceDetails().tag("리뷰").description("내가 받은 카드와 리뷰 조회") + .responseSchema(Schema.schema("ReviewMyResponse")), + responseFields( + fieldWithPath("userCards[].cardId").type(NUMBER).description("카드 식별자"), + fieldWithPath("userCards[].content").type(STRING).description("카드 내용"), + fieldWithPath("userCards[].count").type(NUMBER).description("사용자가 받은 카드 개수"), + fieldWithPath("reviews[].steadyId").type(NUMBER).description("스테디 식별자"), + fieldWithPath("reviews[].steadyName").type(STRING).description("스테디 이름"), + fieldWithPath("reviews[].reviews[].reviewId").type(NUMBER).description("리뷰 식별자"), + fieldWithPath("reviews[].reviews[].comment").type(STRING).description("리뷰 코멘트"), + fieldWithPath("reviews[].reviews[].isPublic").type(BOOLEAN).description("리뷰 공개 여부"), + fieldWithPath("reviews[].reviews[].createdAt").type(STRING).description("리뷰 생성일") + ) + )).andExpect(status().isOk()) + .andExpect(content().string(objectMapper.writeValueAsString(response))); + } + +} diff --git a/src/test/java/dev/steady/review/fixture/ReviewFixture.java b/src/test/java/dev/steady/review/fixture/ReviewFixture.java index 02f727c..d44b50b 100644 --- a/src/test/java/dev/steady/review/fixture/ReviewFixture.java +++ b/src/test/java/dev/steady/review/fixture/ReviewFixture.java @@ -3,12 +3,17 @@ import dev.steady.review.domain.Card; import dev.steady.review.domain.Review; import dev.steady.review.domain.UserCard; -import dev.steady.review.dto.ReviewCreateRequest; +import dev.steady.review.dto.request.ReviewCreateRequest; +import dev.steady.review.dto.response.ReviewDetailResponse; +import dev.steady.review.dto.response.ReviewMyResponse; +import dev.steady.review.dto.response.ReviewSwitchResponse; +import dev.steady.review.dto.response.ReviewsBySteadyResponse; import dev.steady.review.dto.response.UserCardResponse; import dev.steady.steady.domain.Participant; import dev.steady.steady.domain.Steady; import dev.steady.user.domain.User; +import java.time.LocalDateTime; import java.util.List; public class ReviewFixture { @@ -50,4 +55,34 @@ public static List createUserCardResponses() { ); } + public static ReviewCreateRequest createReviewCreateRequest() { + return new ReviewCreateRequest( + 2L, + List.of(1L, 2L), + "열심히 하는 모습 보기 좋습니다." + ); + } + + public static ReviewSwitchResponse createReviewSwitchResponse(boolean isPublic) { + return new ReviewSwitchResponse(isPublic); + } + + public static ReviewMyResponse createReviewMyResponse() { + ReviewDetailResponse reviewDetailResponse = new ReviewDetailResponse( + 1L, + "열심히 하는 모습 보기 좋습니다.", + true, + LocalDateTime.now() + ); + ReviewsBySteadyResponse reviewsBySteadyResponse = new ReviewsBySteadyResponse( + 1L, + "블리츠", + List.of(reviewDetailResponse) + ); + return new ReviewMyResponse( + createUserCardResponses(), + List.of(reviewsBySteadyResponse) + ); + } + } diff --git a/src/test/java/dev/steady/review/service/ReviewServiceTest.java b/src/test/java/dev/steady/review/service/ReviewServiceTest.java index c44971b..e169f7c 100644 --- a/src/test/java/dev/steady/review/service/ReviewServiceTest.java +++ b/src/test/java/dev/steady/review/service/ReviewServiceTest.java @@ -7,6 +7,10 @@ import dev.steady.review.domain.repository.CardRepository; import dev.steady.review.domain.repository.ReviewRepository; import dev.steady.review.domain.repository.UserCardRepository; +import dev.steady.review.dto.response.ReviewMyResponse; +import dev.steady.review.dto.response.ReviewSwitchResponse; +import dev.steady.review.infrastructure.ReviewQueryRepository; +import dev.steady.review.infrastructure.UserCardQueryRepository; import dev.steady.steady.domain.repository.ParticipantRepository; import dev.steady.steady.domain.repository.SteadyRepository; import dev.steady.user.domain.Stack; @@ -28,7 +32,9 @@ import static dev.steady.global.auth.AuthFixture.createUserInfo; import static dev.steady.review.fixture.ReviewFixture.createCard; +import static dev.steady.review.fixture.ReviewFixture.createReview; import static dev.steady.review.fixture.ReviewFixture.createReviewCreateRequest; +import static dev.steady.review.fixture.ReviewFixture.createUserCard; import static dev.steady.steady.domain.Participant.createMember; import static dev.steady.steady.domain.SteadyStatus.FINISHED; import static dev.steady.steady.fixture.SteadyFixtures.createSteady; @@ -49,6 +55,8 @@ class ReviewServiceTest { @Autowired private ReviewRepository reviewRepository; @Autowired + private ReviewQueryRepository reviewQueryRepository; + @Autowired private SteadyRepository steadyRepository; @Autowired private ParticipantRepository participantRepository; @@ -59,6 +67,8 @@ class ReviewServiceTest { @Autowired private UserCardRepository userCardRepository; @Autowired + private UserCardQueryRepository userCardQueryRepository; + @Autowired private PositionRepository positionRepository; @Autowired private StackRepository stackRepository; @@ -95,7 +105,7 @@ void tearDown() { @DisplayName("종료된 스테디에 대하여 리뷰를 생성한다.") @Test - void createReview() { + void createReviewTest() { // given var userInfo = createUserInfo(reviewerUser.getId()); @@ -125,7 +135,7 @@ void createReview() { @Test @DisplayName("리뷰 가능 기간이 지나면 리뷰를 생성할 수 없다.") - void createReviewAfterReviewEnabledPeriod() { + void createReviewAfterReviewEnabledPeriodTest() { // given var userInfo = createUserInfo(reviewerUser.getId()); var steady = createSteady(leader, stacks, FINISHED); @@ -148,18 +158,18 @@ void createReviewAfterReviewEnabledPeriod() { @Test @DisplayName("사용자의 리뷰 카드를 생성할 수 있다.") - void createUserCards() { + void createUserCardsTest() { // given List cards = IntStream.range(0, 3) .mapToObj(i -> createCard()) .toList(); - cardRepository.saveAll(cards); + List savedCards = cardRepository.saveAll(cards); var steady = steadyRepository.save(createSteady(reviewerUser, stacks, FINISHED)); var reviewee = participantRepository.save(createMember(revieweeUser, steady)); // when - List cardsId = List.of(1L, 2L); + List cardsId = List.of(savedCards.get(0).getId(), savedCards.get(1).getId()); var request = createReviewCreateRequest( reviewee.getUserId(), cardsId @@ -174,4 +184,49 @@ void createUserCards() { ); } + + @Test + @DisplayName("리뷰이는 본인의 리뷰 코멘트를 비공개로 설정할 수 있다.") + void switchReviewIsPublicTest() { + // given + var userInfo = createUserInfo(revieweeUser.getId()); + var steady = steadyRepository.save(createSteady(leader, stacks, FINISHED)); + var reviewer = participantRepository.save(createMember(reviewerUser, steady)); + var reviewee = participantRepository.save(createMember(revieweeUser, steady)); + var review = reviewRepository.save(createReview(reviewer, reviewee, steady)); + + // when + ReviewSwitchResponse response = reviewService.switchReviewIsPublic(review.getId(), userInfo); + + // then + Review foundReview = reviewRepository.getById(review.getId()); + assertThat(foundReview.isPublic()).isEqualTo(response.isPublic()); + } + + @Test + @DisplayName("인증된 사용자는 본인이 받은 카드 수와 리뷰 코멘트를 조회할 수 있다.") + void getMyReviewsTest() { + // given + var userInfo = createUserInfo(revieweeUser.getId()); + var steady = steadyRepository.save(createSteady(leader, stacks, FINISHED)); + var reviewer = participantRepository.save(createMember(reviewerUser, steady)); + var reviewee = participantRepository.save(createMember(revieweeUser, steady)); + var review = reviewRepository.save(createReview(reviewer, reviewee, steady)); + var savedCard = cardRepository.save(createCard()); + reviewRepository.save(review); + userCardRepository.save(createUserCard(revieweeUser, savedCard)); + + var allReviews = reviewQueryRepository.getAllReviewsByRevieweeUser(revieweeUser); + var cardsCount = userCardQueryRepository.getCardCountByUser(revieweeUser); + // when + ReviewMyResponse response = reviewService.getMyCardsAndReviews(userInfo); + + // then + assertAll( + () -> assertThat(response.reviews()).hasSameSizeAs(allReviews), + () -> assertThat(response.userCards()).hasSameSizeAs(cardsCount) + ); + + } + } diff --git a/src/test/java/dev/steady/steady/fixture/SteadyFixtures.java b/src/test/java/dev/steady/steady/fixture/SteadyFixtures.java index 077663b..580ba25 100644 --- a/src/test/java/dev/steady/steady/fixture/SteadyFixtures.java +++ b/src/test/java/dev/steady/steady/fixture/SteadyFixtures.java @@ -165,9 +165,9 @@ public static SteadyQuestionsResponse createSteadyQuestionsResponse() { return new SteadyQuestionsResponse( "스터디 제목", List.of( - new SteadyQuestionResponse(1L, "누구세요?", 1), - new SteadyQuestionResponse(2L, "뭐세요?", 2) - )); + new SteadyQuestionResponse(1L, "누구세요?", 1), + new SteadyQuestionResponse(2L, "뭐세요?", 2) + )); } public static SliceResponse createMySteadyResponse() { diff --git a/src/test/java/dev/steady/steady/service/SteadyServiceTest.java b/src/test/java/dev/steady/steady/service/SteadyServiceTest.java index ada421d..d3881c5 100644 --- a/src/test/java/dev/steady/steady/service/SteadyServiceTest.java +++ b/src/test/java/dev/steady/steady/service/SteadyServiceTest.java @@ -16,7 +16,6 @@ import dev.steady.steady.domain.repository.SteadyQuestionRepository; import dev.steady.steady.domain.repository.SteadyRepository; import dev.steady.steady.domain.repository.SteadyStackRepository; -import dev.steady.steady.domain.repository.ViewCountLogRepository; import dev.steady.steady.dto.SearchConditionDto; import dev.steady.steady.dto.request.SteadyCreateRequest; import dev.steady.steady.dto.request.SteadyPageRequest; diff --git a/src/test/java/dev/steady/user/service/UserServiceTest.java b/src/test/java/dev/steady/user/service/UserServiceTest.java index 71e18ef..85c1183 100644 --- a/src/test/java/dev/steady/user/service/UserServiceTest.java +++ b/src/test/java/dev/steady/user/service/UserServiceTest.java @@ -208,8 +208,8 @@ void getOtherUserDetail() { userCardRepository.save(createUserCard(revieweeUser, savedCard)); var userStacks = userStackRepository.findAllByUser(revieweeUser); - var reviews = reviewQueryRepository.findPublicCommentsByRevieweeUser(revieweeUser); - var userCards = userCardQueryRepository.findCardCountByUser(revieweeUser); + var reviews = reviewQueryRepository.getPublicCommentsByRevieweeUser(revieweeUser); + var userCards = userCardQueryRepository.getCardCountByUser(revieweeUser); // when UserOtherDetailResponse response = userService.getOtherUserDetail(revieweeUser.getId());