From 2bdede3918de02949bd8de923e2c6f9e8ca7e0a5 Mon Sep 17 00:00:00 2001 From: amaran-th Date: Mon, 30 Oct 2023 19:28:01 +0900 Subject: [PATCH 01/41] =?UTF-8?q?refactor:=20Event=20=EB=8B=A4=EA=B1=B4=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EB=AA=85=EC=84=B8=EB=A5=BC=20=EB=8B=A8?= =?UTF-8?q?=EA=B1=B4=20=EC=A1=B0=ED=9A=8C=20=EB=AA=85=EC=84=B8=EC=99=80=20?= =?UTF-8?q?=EB=8F=99=EC=9D=BC=ED=95=98=EA=B2=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #803 --- .../java/com/emmsale/EventApiTest.java | 76 +++++---- .../java/com/emmsale/ScrapApiTest.java | 104 +++++++----- .../java/com/emmsale/event/api/EventApi.java | 3 +- .../event/application/EventService.java | 64 +++++-- .../event/application/dto/EventResponse.java | 70 -------- .../java/com/emmsale/scrap/api/ScrapApi.java | 4 +- .../scrap/application/ScrapQueryService.java | 71 ++++++-- .../event/application/EventServiceTest.java | 158 +++++++++++------- .../application/ScrapQueryServiceTest.java | 49 ++++-- 9 files changed, 342 insertions(+), 257 deletions(-) delete mode 100644 backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventResponse.java diff --git a/backend/emm-sale/src/documentTest/java/com/emmsale/EventApiTest.java b/backend/emm-sale/src/documentTest/java/com/emmsale/EventApiTest.java index b9ea40906..ac02b8497 100644 --- a/backend/emm-sale/src/documentTest/java/com/emmsale/EventApiTest.java +++ b/backend/emm-sale/src/documentTest/java/com/emmsale/EventApiTest.java @@ -15,7 +15,6 @@ import com.emmsale.event.api.EventApi; import com.emmsale.event.application.dto.EventDetailRequest; import com.emmsale.event.application.dto.EventDetailResponse; -import com.emmsale.event.application.dto.EventResponse; import com.emmsale.event.domain.Event; import com.emmsale.event.domain.EventMode; import com.emmsale.event.domain.EventType; @@ -81,6 +80,31 @@ class EventApiTest extends MockMvcTestHelper { fieldWithPath("eventMode").description("온/오프라인 여부(온라인,오프라인,온오프라인)") ); + private static final ResponseFieldsSnippet EVENT_RESPONSE_FILED = PayloadDocumentation.responseFields( + fieldWithPath("[].id").type(JsonFieldType.NUMBER).description("행사 식별자"), + fieldWithPath("[].name").type(JsonFieldType.STRING) + .description("행사 이름"), + fieldWithPath("[].informationUrl").type(JsonFieldType.STRING) + .description("행사 상세정보 url"), + fieldWithPath("[].startDate").type(JsonFieldType.STRING) + .description("행사 시작 일자"), + fieldWithPath("[].endDate").type(JsonFieldType.STRING).description("행사 종료 일자"), + fieldWithPath("[].applyStartDate").type(JsonFieldType.STRING) + .description("행사 신청 시작 일자(nullable)"), + fieldWithPath("[].applyEndDate").type(JsonFieldType.STRING) + .description("행사 신청 종료 일자(nullable)"), + fieldWithPath("[].location").type(JsonFieldType.STRING).description("행사 장소"), + fieldWithPath("[].tags[]").type(JsonFieldType.ARRAY).description("행사 태그들"), + fieldWithPath("[].thumbnailUrl").type(JsonFieldType.STRING) + .description("행사 섬네일 이미지 Url(포스터)"), + fieldWithPath("[].type").type(JsonFieldType.STRING) + .description("행사의 분류"), + fieldWithPath("[].imageUrls[]").description("행사의 상세 정보 이미지 URL들").optional(), + fieldWithPath("[].organization").description("행사 주최기관"), + fieldWithPath("[].paymentType").description("행사의 유무료 여부(유료,무료,유무료)"), + fieldWithPath("[].eventMode").description("행사의 온/오프라인 여부(온라인,오프라인,온오프라인)") + ); + @Test @DisplayName("컨퍼런스의 상세정보를 조회할 수 있다.") void findEvent() throws Exception { @@ -124,49 +148,32 @@ void findEvents() throws Exception { .optional() ); - final ResponseFieldsSnippet responseFields = PayloadDocumentation.responseFields( - PayloadDocumentation.fieldWithPath("[].id").type(JsonFieldType.NUMBER).description("행사 id"), - PayloadDocumentation.fieldWithPath("[].name").type(JsonFieldType.STRING).description("행사명"), - PayloadDocumentation.fieldWithPath("[].eventStartDate").type(JsonFieldType.STRING) - .description("행사 시작일(yyyy:MM:dd:HH:mm:ss)"), - PayloadDocumentation.fieldWithPath("[].eventEndDate").type(JsonFieldType.STRING) - .description("행사 마감일(yyyy:MM:dd:HH:mm:ss)"), - PayloadDocumentation.fieldWithPath("[].applyStartDate").type(JsonFieldType.STRING) - .description("행사 시작일(yyyy:MM:dd:HH:mm:ss)"), - PayloadDocumentation.fieldWithPath("[].applyEndDate").type(JsonFieldType.STRING) - .description("행사 마감일(yyyy:MM:dd:HH:mm:ss)"), - PayloadDocumentation.fieldWithPath("[].tags[]").type(JsonFieldType.ARRAY) - .description("행사 태그 목록"), - PayloadDocumentation.fieldWithPath("[].thumbnailUrl").type(JsonFieldType.STRING) - .description("행사 섬네일 이미지 URL"), - PayloadDocumentation.fieldWithPath("[].eventMode").type(JsonFieldType.STRING) - .description("행사 온라인 여부(온라인, 오프라인, 온오프라인)"), - PayloadDocumentation.fieldWithPath("[].paymentType").type(JsonFieldType.STRING) - .description("행사 유료 여부(유료, 무료, 유무료)") - ); - - final List eventResponses = List.of( - new EventResponse( + final List eventResponses = List.of( + new EventDetailResponse( 5L, "웹 컨퍼런스", + "https://biz.pusan.ac.kr/dext5editordata/2022/08/20220810_160546511_10103.jpg", LocalDateTime.parse("2023-07-03T12:00:00"), LocalDateTime.parse("2023-08-03T12:00:00"), LocalDateTime.parse("2023-06-23T10:00:00"), LocalDateTime.parse("2023-07-03T12:00:00"), - List.of("백엔드", "프론트엔드"), - "https://biz.pusan.ac.kr/dext5editordata/2022/08/20220810_160546511_10103.jpg", - EventMode.ONLINE.getValue(), - PaymentType.PAID.getValue()), - new EventResponse(2L, + "코엑스", List.of("백엔드", "프론트엔드"), + "imageUrl0", EventType.CONFERENCE.name(), + List.of("imageUrl1", "imageUrl2"), "인프런", + PaymentType.PAID.getValue(), + EventMode.ONLINE.getValue()), + new EventDetailResponse(2L, "AI 컨퍼런스", + "https://biz.pusan.ac.kr/dext5editordata/2022/08/20220810_160546511_10103.jpg", LocalDateTime.parse("2023-07-22T12:00:00"), LocalDateTime.parse("2023-07-30T12:00:00"), LocalDateTime.parse("2023-07-01T00:00:00"), LocalDateTime.parse("2023-07-21T23:59:59"), - List.of("AI"), - "https://biz.pusan.ac.kr/dext5editordata/2022/08/20220810_160546511_10103.jpg", - EventMode.ONLINE.getValue(), - PaymentType.PAID.getValue()) + "코엑스", List.of("AI"), + "imageUrl0", EventType.CONFERENCE.name(), + List.of("imageUrl1", "imageUrl2"), "인프런", + PaymentType.PAID.getValue(), + EventMode.ONLINE.getValue()) ); Mockito.when(eventService.findEvents(any(EventType.class), @@ -183,7 +190,8 @@ void findEvents() throws Exception { .param("keyword", "컨퍼") ) .andExpect(status().isOk()) - .andDo(MockMvcRestDocumentation.document("find-events", requestParameters, responseFields)); + .andDo(MockMvcRestDocumentation.document("find-events", requestParameters, + EVENT_RESPONSE_FILED)); } @Test diff --git a/backend/emm-sale/src/documentTest/java/com/emmsale/ScrapApiTest.java b/backend/emm-sale/src/documentTest/java/com/emmsale/ScrapApiTest.java index 6287a84cd..c23bdf3d1 100644 --- a/backend/emm-sale/src/documentTest/java/com/emmsale/ScrapApiTest.java +++ b/backend/emm-sale/src/documentTest/java/com/emmsale/ScrapApiTest.java @@ -11,8 +11,9 @@ import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import com.emmsale.event.application.dto.EventResponse; +import com.emmsale.event.application.dto.EventDetailResponse; import com.emmsale.event.domain.EventMode; +import com.emmsale.event.domain.EventType; import com.emmsale.event.domain.PaymentType; import com.emmsale.scrap.api.ScrapApi; import com.emmsale.scrap.application.dto.ScrapRequest; @@ -35,61 +36,80 @@ class ScrapApiTest extends MockMvcTestHelper { @DisplayName("스크랩 목록을 성공적으로 조회하면 200 OK를 반환한다.") void findAllScraps() throws Exception { //given - final List expectedScrapResponse = List.of( - new EventResponse( + final List expectedScrapResponse = List.of( + new EventDetailResponse( 1L, "인프콘 2023", + "https://aaa", LocalDateTime.parse("2023-06-03T12:00:00"), LocalDateTime.parse("2023-09-03T12:00:00"), LocalDateTime.parse("2023-09-01T00:00:00"), LocalDateTime.parse("2023-09-02T23:59:59"), + "코엑스", List.of("백엔드", "프론트엔드", "안드로이드", "IOS", "AI"), - "https://biz.pusan.ac.kr/dext5editordata/2022/08/20220810_160546511_10103.jpg", - EventMode.ONLINE.getValue(), - PaymentType.PAID.getValue() + "image0.jpg", + EventType.CONFERENCE.name(), + List.of("image1.jpg", "image2.jpg", "image3.jpg"), + "인프런", + PaymentType.PAID.getValue(), + EventMode.ONLINE.getValue() ), - new EventResponse( + new EventDetailResponse( 5L, "웹 컨퍼런스", - LocalDateTime.parse("2023-07-03T12:00:00"), - LocalDateTime.parse("2023-08-03T12:00:00"), - LocalDateTime.parse("2023-06-23T10:00:00"), - LocalDateTime.parse("2023-07-03T12:00:00"), - List.of("백엔드", "프론트엔드"), - "https://biz.pusan.ac.kr/dext5editordata/2022/08/20220810_160546511_10103.jpg", - EventMode.ONLINE.getValue(), - PaymentType.PAID.getValue()), - new EventResponse(2L, + "https://aaa", + LocalDateTime.parse("2023-06-03T12:00:00"), + LocalDateTime.parse("2023-09-03T12:00:00"), + LocalDateTime.parse("2023-09-01T00:00:00"), + LocalDateTime.parse("2023-09-02T23:59:59"), + "코엑스", + List.of("백엔드", "프론트엔드", "안드로이드", "IOS", "AI"), + "image0.jpg", + EventType.CONFERENCE.name(), + List.of("image1.jpg", "image2.jpg", "image3.jpg"), + "인프런", + PaymentType.PAID.getValue(), + EventMode.ONLINE.getValue()), + new EventDetailResponse(2L, "AI 컨퍼런스", - LocalDateTime.parse("2023-07-22T12:00:00"), - LocalDateTime.parse("2023-07-30T12:00:00"), - LocalDateTime.parse("2023-07-01T00:00:00"), - LocalDateTime.parse("2023-07-21T23:59:59"), - List.of("AI"), - "https://biz.pusan.ac.kr/dext5editordata/2022/08/20220810_160546511_10103.jpg", - EventMode.ONLINE.getValue(), - PaymentType.PAID.getValue()) + "https://aaa", + LocalDateTime.parse("2023-06-03T12:00:00"), + LocalDateTime.parse("2023-09-03T12:00:00"), + LocalDateTime.parse("2023-09-01T00:00:00"), + LocalDateTime.parse("2023-09-02T23:59:59"), + "코엑스", + List.of("백엔드", "프론트엔드", "안드로이드", "IOS", "AI"), + "image0.jpg", + EventType.CONFERENCE.name(), + List.of("image1.jpg", "image2.jpg", "image3.jpg"), + "인프런", + PaymentType.PAID.getValue(), + EventMode.ONLINE.getValue()) ); final ResponseFieldsSnippet responseFields = PayloadDocumentation.responseFields( - PayloadDocumentation.fieldWithPath("[].id").type(JsonFieldType.NUMBER).description("행사 id"), - PayloadDocumentation.fieldWithPath("[].name").type(JsonFieldType.STRING).description("행사명"), - PayloadDocumentation.fieldWithPath("[].eventStartDate").type(JsonFieldType.STRING) - .description("행사 시작일(yyyy:MM:dd:HH:mm:ss)"), - PayloadDocumentation.fieldWithPath("[].eventEndDate").type(JsonFieldType.STRING) - .description("행사 마감일(yyyy:MM:dd:HH:mm:ss)"), - PayloadDocumentation.fieldWithPath("[].applyStartDate").type(JsonFieldType.STRING) - .description("행사 시작일(yyyy:MM:dd:HH:mm:ss)"), - PayloadDocumentation.fieldWithPath("[].applyEndDate").type(JsonFieldType.STRING) - .description("행사 마감일(yyyy:MM:dd:HH:mm:ss)"), - PayloadDocumentation.fieldWithPath("[].tags[]").type(JsonFieldType.ARRAY) - .description("행사 태그 목록"), - PayloadDocumentation.fieldWithPath("[].thumbnailUrl").type(JsonFieldType.STRING) - .description("행사 섬네일 이미지 URL"), - PayloadDocumentation.fieldWithPath("[].eventMode").type(JsonFieldType.STRING) - .description("행사 온라인 여부(온라인, 오프라인, 온오프라인)"), - PayloadDocumentation.fieldWithPath("[].paymentType").type(JsonFieldType.STRING) - .description("행사 유료 여부(유료, 무료, 유무료)") + fieldWithPath("[].id").type(JsonFieldType.NUMBER).description("행사 식별자"), + fieldWithPath("[].name").type(JsonFieldType.STRING) + .description("행사 이름"), + fieldWithPath("[].informationUrl").type(JsonFieldType.STRING) + .description("행사 상세정보 url"), + fieldWithPath("[].startDate").type(JsonFieldType.STRING) + .description("행사 시작 일자"), + fieldWithPath("[].endDate").type(JsonFieldType.STRING).description("행사 종료 일자"), + fieldWithPath("[].applyStartDate").type(JsonFieldType.STRING) + .description("행사 신청 시작 일자(nullable)"), + fieldWithPath("[].applyEndDate").type(JsonFieldType.STRING) + .description("행사 신청 종료 일자(nullable)"), + fieldWithPath("[].location").type(JsonFieldType.STRING).description("행사 장소"), + fieldWithPath("[].tags[]").type(JsonFieldType.ARRAY).description("행사 태그들"), + fieldWithPath("[].thumbnailUrl").type(JsonFieldType.STRING) + .description("행사 섬네일 이미지 Url(포스터)"), + fieldWithPath("[].type").type(JsonFieldType.STRING) + .description("행사의 분류"), + fieldWithPath("[].imageUrls[]").description("행사의 상세 정보 이미지 URL들").optional(), + fieldWithPath("[].organization").description("행사 주최기관"), + fieldWithPath("[].paymentType").description("행사의 유무료 여부(유료,무료,유무료)"), + fieldWithPath("[].eventMode").description("행사의 온/오프라인 여부(온라인,오프라인,온오프라인)") ); //when diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java b/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java index 5ecd44ee0..44f2d29a0 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java @@ -3,7 +3,6 @@ import com.emmsale.event.application.EventService; import com.emmsale.event.application.dto.EventDetailRequest; import com.emmsale.event.application.dto.EventDetailResponse; -import com.emmsale.event.application.dto.EventResponse; import com.emmsale.event.domain.EventStatus; import com.emmsale.event.domain.EventType; import java.time.LocalDate; @@ -39,7 +38,7 @@ public ResponseEntity findEventById(@PathVariable final Lon } @GetMapping - public ResponseEntity> findEvents( + public ResponseEntity> findEvents( @RequestParam(required = false) final EventType category, @RequestParam(name = "start_date", required = false) final String startDate, @RequestParam(name = "end_date", required = false) final String endDate, diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java index 8c67d38e1..4b2bf6136 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java @@ -12,7 +12,6 @@ import com.emmsale.event.application.dto.EventDetailRequest; import com.emmsale.event.application.dto.EventDetailResponse; -import com.emmsale.event.application.dto.EventResponse; import com.emmsale.event.domain.Event; import com.emmsale.event.domain.EventStatus; import com.emmsale.event.domain.EventType; @@ -91,7 +90,7 @@ private List extractInformationImages(final List imageUrls) { } @Transactional(readOnly = true) - public List findEvents(final EventType category, + public List findEvents(final EventType category, final LocalDateTime nowDateTime, final String startDate, final String endDate, final List tagNames, final List statuses, final String keyword) { Specification spec = (root, query, criteriaBuilder) -> null; @@ -105,7 +104,7 @@ public List findEvents(final EventType category, final EnumMap> eventsForEventStatus = groupByEventStatus(nowDateTime, events); - return filterByStatuses(statuses, eventsForEventStatus, makeImageUrlPerEventId(events)); + return filterByStatuses(statuses, eventsForEventStatus, makeImageUrlsPerEventId(events)); } private Specification filterByCategoryIfExist(final EventType category, @@ -185,16 +184,22 @@ private void validateEndDateAfterDateStart(final LocalDateTime startDate, } // TODO: 2023/09/27 코드 중복 제거(ScrapService) - private Map makeImageUrlPerEventId(final List events) { - final List scrappedEventIds = events.stream() + private Map> makeImageUrlsPerEventId(final List events) { + final List eventIds = events.stream() .map(Event::getId) .collect(Collectors.toList()); - final List images = imageRepository.findAllThumbnailByEventIdIn(scrappedEventIds); - Map imageUrlPerEventId = new HashMap<>(); - for (Image image : images) { - imageUrlPerEventId.put(image.getContentId(), image.getName()); + + Map> imageUrlsPerEventId = new HashMap<>(); + for (Long eventId : eventIds) { + final List images = imageRepository.findAllByTypeAndContentId(ImageType.EVENT, + eventId) + .stream() + .sorted(comparing(Image::getOrder)) + .map(Image::getName) + .collect(toList()); + imageUrlsPerEventId.put(eventId, images); } - return imageUrlPerEventId; + return imageUrlsPerEventId; } private Specification filterByKeywordIfExist(final String keyword, @@ -220,31 +225,56 @@ private EnumMap> groupByEventStatus(final LocalDateTime ); } - private List filterByStatuses( + private List filterByStatuses( final List statuses, final EnumMap> eventsForEventStatus, - final Map imageUrlPerEventId + final Map> imageUrlsPerEventId ) { if (isExistStatusName(statuses)) { - return filterEventResponseByStatuses(statuses, eventsForEventStatus, imageUrlPerEventId); + return filterEventResponseByStatuses(statuses, eventsForEventStatus, imageUrlsPerEventId); } - return EventResponse.mergeEventResponses(eventsForEventStatus, imageUrlPerEventId); + return mergeEventResponses(eventsForEventStatus, imageUrlsPerEventId); } private boolean isExistStatusName(final List statuses) { return statuses != null; } - private List filterEventResponseByStatuses( + private List filterEventResponseByStatuses( final List statuses, final EnumMap> eventsForEventStatus, - final Map imageUrlPerEventId + final Map> imageUrlsPerEventId ) { return eventsForEventStatus.entrySet() .stream() .filter(entry -> statuses.contains(entry.getKey())) .map( - entry -> EventResponse.makeEventResponsesByStatus(entry.getValue(), imageUrlPerEventId)) + entry -> makeEventResponsesByStatus(entry.getValue(), + imageUrlsPerEventId)) + .reduce(new ArrayList<>(), (combinedEvents, eventsToAdd) -> { + combinedEvents.addAll(eventsToAdd); + return combinedEvents; + }); + } + + private List makeEventResponsesByStatus(final List events, + final Map> imageUrlsPerEventId) { + return events.stream() + .map(event -> { + final List allImageUrls = imageUrlsPerEventId.get(event.getId()); + final String thumbnailImageUrl = extractThumbnailImage(allImageUrls); + final List informationImageUrls = extractInformationImages(allImageUrls); + return EventDetailResponse.from(event, thumbnailImageUrl, informationImageUrls); + }) + .collect(Collectors.toList()); + } + + private List mergeEventResponses( + final Map> groupByEventStatus, + final Map> imageUrlsPerEventId + ) { + return groupByEventStatus.values().stream() + .map(events -> makeEventResponsesByStatus(events, imageUrlsPerEventId)) .reduce(new ArrayList<>(), (combinedEvents, eventsToAdd) -> { combinedEvents.addAll(eventsToAdd); return combinedEvents; diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventResponse.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventResponse.java deleted file mode 100644 index 241f3c4ee..000000000 --- a/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventResponse.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.emmsale.event.application.dto; - -import com.emmsale.event.domain.Event; -import com.emmsale.event.domain.EventStatus; -import com.fasterxml.jackson.annotation.JsonFormat; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.ToString; - -@Getter -@ToString -@RequiredArgsConstructor -public class EventResponse { - - public static final String DATE_TIME_FORMAT = "yyyy:MM:dd:HH:mm:ss"; - - private final Long id; - private final String name; - @JsonFormat(pattern = DATE_TIME_FORMAT) - private final LocalDateTime eventStartDate; - @JsonFormat(pattern = DATE_TIME_FORMAT) - private final LocalDateTime eventEndDate; - @JsonFormat(pattern = DATE_TIME_FORMAT) - private final LocalDateTime applyStartDate; - @JsonFormat(pattern = DATE_TIME_FORMAT) - private final LocalDateTime applyEndDate; - private final List tags; - private final String thumbnailUrl; - private final String eventMode; - private final String paymentType; - - public static List makeEventResponsesByStatus(final List events, - final Map imageUrlPerEventId) { - return events.stream() - .map(event -> EventResponse.from(event, imageUrlPerEventId.get(event.getId()))) - .collect(Collectors.toList()); - } - - public static List mergeEventResponses( - final Map> groupByEventStatus, - final Map imageUrlPerEventId - ) { - return groupByEventStatus.values().stream() - .map(events -> makeEventResponsesByStatus(events, imageUrlPerEventId)) - .reduce(new ArrayList<>(), (combinedEvents, eventsToAdd) -> { - combinedEvents.addAll(eventsToAdd); - return combinedEvents; - }); - } - - private static EventResponse from(final Event event, final String thumbnailUrl) { - return new EventResponse( - event.getId(), - event.getName(), - event.getEventPeriod().getStartDate(), - event.getEventPeriod().getEndDate(), - event.getEventPeriod().getApplyStartDate(), - event.getEventPeriod().getApplyEndDate(), - event.extractTags(), - thumbnailUrl, - event.getEventMode().getValue(), - event.getPaymentType().getValue() - ); - } -} diff --git a/backend/emm-sale/src/main/java/com/emmsale/scrap/api/ScrapApi.java b/backend/emm-sale/src/main/java/com/emmsale/scrap/api/ScrapApi.java index ce89d8fe2..5d5f28270 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/scrap/api/ScrapApi.java +++ b/backend/emm-sale/src/main/java/com/emmsale/scrap/api/ScrapApi.java @@ -1,6 +1,6 @@ package com.emmsale.scrap.api; -import com.emmsale.event.application.dto.EventResponse; +import com.emmsale.event.application.dto.EventDetailResponse; import com.emmsale.member.domain.Member; import com.emmsale.scrap.application.ScrapCommandService; import com.emmsale.scrap.application.ScrapQueryService; @@ -27,7 +27,7 @@ public class ScrapApi { @GetMapping @ResponseStatus(HttpStatus.OK) - public List findAllScraps(final Member member) { + public List findAllScraps(final Member member) { return scrapQueryService.findAllScraps(member); } diff --git a/backend/emm-sale/src/main/java/com/emmsale/scrap/application/ScrapQueryService.java b/backend/emm-sale/src/main/java/com/emmsale/scrap/application/ScrapQueryService.java index 9aa878962..8e5455d9d 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/scrap/application/ScrapQueryService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/scrap/application/ScrapQueryService.java @@ -1,16 +1,21 @@ package com.emmsale.scrap.application; +import static java.util.Comparator.comparing; import static java.util.stream.Collectors.groupingBy; +import static java.util.stream.Collectors.toList; -import com.emmsale.event.application.dto.EventResponse; +import com.emmsale.event.application.dto.EventDetailResponse; import com.emmsale.event.domain.Event; import com.emmsale.event.domain.EventStatus; import com.emmsale.image.domain.Image; +import com.emmsale.image.domain.ImageType; import com.emmsale.image.domain.repository.ImageRepository; import com.emmsale.member.domain.Member; import com.emmsale.scrap.domain.Scrap; import com.emmsale.scrap.domain.ScrapRepository; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -27,7 +32,7 @@ public class ScrapQueryService { private final ScrapRepository scrapRepository; private final ImageRepository imageRepository; - public List findAllScraps(final Member member) { + public List findAllScraps(final Member member) { //TODO : Scrap에서 event 사용해서 N+1 발생 final List scrappedEvents = scrapRepository.findAllByMemberId(member.getId()) .stream() @@ -38,19 +43,63 @@ public List findAllScraps(final Member member) { .collect( groupingBy(event -> event.getEventPeriod().calculateEventStatus(LocalDateTime.now()))); - return EventResponse.mergeEventResponses(eventGroupByStatus, - makeImageUrlPerEventId(scrappedEvents)); + return mergeEventResponses(eventGroupByStatus, makeImageUrlsPerEventId(scrappedEvents)); } - private Map makeImageUrlPerEventId(final List events) { - final List scrappedEventIds = events.stream() + private Map> makeImageUrlsPerEventId(final List events) { + final List eventIds = events.stream() .map(Event::getId) .collect(Collectors.toList()); - final List images = imageRepository.findAllThumbnailByEventIdIn(scrappedEventIds); - Map imageUrlPerEventId = new HashMap<>(); - for (Image image : images) { - imageUrlPerEventId.put(image.getContentId(), image.getName()); + + Map> imageUrlsPerEventId = new HashMap<>(); + for (Long eventId : eventIds) { + final List images = imageRepository.findAllByTypeAndContentId(ImageType.EVENT, + eventId) + .stream() + .sorted(comparing(Image::getOrder)) + .map(Image::getName) + .collect(toList()); + imageUrlsPerEventId.put(eventId, images); } - return imageUrlPerEventId; + return imageUrlsPerEventId; + } + + private List makeEventResponsesByStatus(final List events, + final Map> imageUrlsPerEventId) { + return events.stream() + .map(event -> { + final List allImageUrls = imageUrlsPerEventId.get(event.getId()); + final String thumbnailImageUrl = extractThumbnailImage(allImageUrls); + final List informationImageUrls = extractInformationImages(allImageUrls); + return EventDetailResponse.from(event, thumbnailImageUrl, informationImageUrls); + }) + .collect(Collectors.toList()); } + + private List mergeEventResponses( + final Map> groupByEventStatus, + final Map> imageUrlsPerEventId + ) { + return groupByEventStatus.values().stream() + .map(events -> makeEventResponsesByStatus(events, imageUrlsPerEventId)) + .reduce(new ArrayList<>(), (combinedEvents, eventsToAdd) -> { + combinedEvents.addAll(eventsToAdd); + return combinedEvents; + }); + } + + private String extractThumbnailImage(final List imageUrls) { + if (imageUrls.isEmpty()) { + return null; + } + return imageUrls.get(0); + } + + private List extractInformationImages(final List imageUrls) { + if (imageUrls.size() <= 1) { + return Collections.emptyList(); + } + return imageUrls.subList(1, imageUrls.size()); + } + } diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java index 5ba3f7257..7499cfd69 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java @@ -35,7 +35,6 @@ import com.emmsale.event.application.dto.EventDetailRequest; import com.emmsale.event.application.dto.EventDetailResponse; -import com.emmsale.event.application.dto.EventResponse; import com.emmsale.event.domain.Event; import com.emmsale.event.domain.EventMode; import com.emmsale.event.domain.EventStatus; @@ -74,21 +73,34 @@ class EventServiceTest extends ServiceIntegrationTestHelper { - private static final EventResponse 인프콘_2023 = new EventResponse(null, "인프콘 2023", null, null, - null, null, List.of(), "이미지1", EventMode.OFFLINE.getValue(), - PaymentType.PAID.getValue()); - private static final EventResponse 웹_컨퍼런스 = new EventResponse(null, "웹 컨퍼런스", null, null, null, - null, List.of(), "이미지1", EventMode.ONLINE.getValue(), PaymentType.PAID.getValue()); - private static final EventResponse 안드로이드_컨퍼런스 = new EventResponse(null, "안드로이드 컨퍼런스", null, null, - null, null, List.of(), "이미지1", EventMode.ONLINE.getValue(), PaymentType.PAID.getValue()); - private static final EventResponse AI_컨퍼런스 = new EventResponse(null, "AI 컨퍼런스", null, null, null, - null, List.of(), null, EventMode.ONLINE.getValue(), PaymentType.PAID.getValue()); - private static final EventResponse 모바일_컨퍼런스 = new EventResponse(null, "모바일 컨퍼런스", null, null, - null, null, List.of(), null, EventMode.ONLINE.getValue(), PaymentType.PAID.getValue()); - private static final EventResponse AI_아이디어_공모전 = new EventResponse(null, "AI 아이디어 공모전", null, - null, null, null, List.of(), null, EventMode.ONLINE.getValue(), PaymentType.PAID.getValue()); - private static final EventResponse 구름톤 = new EventResponse(null, "구름톤", null, null, null, null, - List.of(), null, EventMode.ONLINE.getValue(), PaymentType.PAID.getValue()); + private static final EventDetailResponse 인프콘_2023 = new EventDetailResponse(null, "인프콘 2023", + null, null, null, null, null, "코엑스", List.of("백엔드"), + "이미지1", EventType.CONFERENCE.name(), List.of(), "인프런", PaymentType.PAID.getValue(), + EventMode.OFFLINE.getValue()); + private static final EventDetailResponse 웹_컨퍼런스 = new EventDetailResponse(null, "웹 컨퍼런스", null, + null, null, + null, null, "코엑스", List.of("백엔드"), "이미지1", EventType.CONFERENCE.name(), + List.of(), "주최기관", PaymentType.PAID.getValue(), EventMode.ONLINE.getValue()); + private static final EventDetailResponse 안드로이드_컨퍼런스 = new EventDetailResponse(null, "안드로이드 컨퍼런스", + null, null, null, null, null, "코엑스", List.of("백엔드"), + "이미지1", EventType.CONFERENCE.name(), List.of(), "주최기관", PaymentType.PAID.getValue(), + EventMode.ONLINE.getValue()); + private static final EventDetailResponse AI_컨퍼런스 = new EventDetailResponse(null, "AI 컨퍼런스", + null, null, null, null, null, "코엑스", List.of("백엔드"), + "이미지1", EventType.CONFERENCE.name(), List.of(), "주최기관", PaymentType.PAID.getValue(), + EventMode.ONLINE.getValue()); + private static final EventDetailResponse 모바일_컨퍼런스 = new EventDetailResponse(null, "모바일 컨퍼런스", + null, null, null, null, null, "코엑스", List.of("백엔드"), + "이미지1", EventType.CONFERENCE.name(), List.of(), "주최기관", PaymentType.PAID.getValue(), + EventMode.ONLINE.getValue()); + private static final EventDetailResponse AI_아이디어_공모전 = new EventDetailResponse(null, + "AI 아이디어 공모전", null, null, null, null, null, "코엑스", + List.of("백엔드"), "이미지1", EventType.CONFERENCE.name(), List.of(), "주최기관", + PaymentType.PAID.getValue(), EventMode.ONLINE.getValue()); + private static final EventDetailResponse 구름톤 = new EventDetailResponse(null, "구름톤", null, + null, null, null, null, "코엑스", List.of("백엔드"), + "이미지1", EventType.COMPETITION.name(), List.of(), "주최기관", PaymentType.PAID.getValue(), + EventMode.ONLINE.getValue()); private static final LocalDateTime TODAY = LocalDateTime.of(2023, 7, 21, 0, 0); @Autowired @@ -245,17 +257,18 @@ class findEvents { @DisplayName("2023년 7월 21일에 행사를 조회하면, 모든 행사 목록을 조회할 수 있다.") void findEvents_all() { // given - final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, 구름톤, AI_컨퍼런스, 모바일_컨퍼런스, + final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, 구름톤, AI_컨퍼런스, + 모바일_컨퍼런스, 안드로이드_컨퍼런스, AI_아이디어_공모전); // when - final List actualEvents = eventService.findEvents(null, TODAY, + final List actualEvents = eventService.findEvents(null, TODAY, null, null, null, null, null); // then assertThat(actualEvents) .usingRecursiveComparison() - .comparingOnlyFields("name", "status", "applyStatus", "imageUrl") + .comparingOnlyFields("name", "status", "applyStatus") .isEqualTo(expectedEvents); } @@ -263,17 +276,18 @@ void findEvents_all() { @DisplayName("2023년 7월 21일에 컨퍼런스 행사를 조회하면, 해당 카테고리에 해당하는 모든 행사 목록을 조회할 수 있다.") void findEvents_CONFERENCE() { // given - final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, AI_컨퍼런스, 모바일_컨퍼런스, + final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, AI_컨퍼런스, 모바일_컨퍼런스, 안드로이드_컨퍼런스); // when - final List actualEvents = eventService.findEvents(EventType.CONFERENCE, TODAY, + final List actualEvents = eventService.findEvents(EventType.CONFERENCE, + TODAY, null, null, null, null, null); // then assertThat(actualEvents) .usingRecursiveComparison() - .comparingOnlyFields("name", "status", "applyStatus", "imageUrl") + .comparingOnlyFields("name", "status", "applyStatus") .isEqualTo(expectedEvents); } @@ -281,16 +295,17 @@ void findEvents_CONFERENCE() { @DisplayName("2023년 7월 21일에 대회 행사를 조회하면, 해당 카테고리에 해당하는 모든 행사 목록을 조회할 수 있다.") void findEvents_COMPETITION() { // given - final List expectedEvents = List.of(구름톤, AI_아이디어_공모전); + final List expectedEvents = List.of(구름톤, AI_아이디어_공모전); // when - final List actualEvents = eventService.findEvents(EventType.COMPETITION, TODAY, + final List actualEvents = eventService.findEvents(EventType.COMPETITION, + TODAY, null, null, null, null, null); // then assertThat(actualEvents) .usingRecursiveComparison() - .comparingOnlyFields("name", "status", "applyStatus", "imageUrl") + .comparingOnlyFields("name", "status", "applyStatus") .isEqualTo(expectedEvents); } @@ -298,16 +313,18 @@ void findEvents_COMPETITION() { @DisplayName("2023년 7월 21일에 2023년 7월 행사를 조회하면, 해당 기간에 걸쳐있는 모든 행사 목록을 조회할 수 있다.") void findEvents_2023_7() { // given - final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, AI_컨퍼런스, 안드로이드_컨퍼런스); + final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, AI_컨퍼런스, + 안드로이드_컨퍼런스); // when - final List actualEvents = eventService.findEvents(EventType.CONFERENCE, TODAY, + final List actualEvents = eventService.findEvents(EventType.CONFERENCE, + TODAY, "2023-07-01", "2023-07-31", null, null, null); // then assertThat(actualEvents) .usingRecursiveComparison() - .comparingOnlyFields("name", "status", "applyStatus", "imageUrl") + .comparingOnlyFields("name", "status", "applyStatus") .isEqualTo(expectedEvents); } @@ -315,16 +332,17 @@ void findEvents_2023_7() { @DisplayName("2023년 7월 21일에 2023년 8월 행사를 조회하면, 해당 기간에 걸쳐있는 모든 행사 목록을 조회할 수 있다.") void findEvents_2023_8() { // given - final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, 모바일_컨퍼런스); + final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, 모바일_컨퍼런스); // when - final List actualEvents = eventService.findEvents(EventType.CONFERENCE, TODAY, + final List actualEvents = eventService.findEvents(EventType.CONFERENCE, + TODAY, "2023-08-01", "2023-08-31", null, null, null); // then assertThat(actualEvents) .usingRecursiveComparison() - .comparingOnlyFields("name", "status", "applyStatus", "imageUrl") + .comparingOnlyFields("name", "status", "applyStatus") .isEqualTo(expectedEvents); } @@ -332,16 +350,17 @@ void findEvents_2023_8() { @DisplayName("2023년 7월 21일에 2023년 6월 행사를 조회하면, 해당 기간에 걸쳐있는 모든 행사 목록을 조회할 수 있다.") void findEvents_2023_6() { // given - final List expectedEvents = List.of(인프콘_2023, 안드로이드_컨퍼런스); + final List expectedEvents = List.of(인프콘_2023, 안드로이드_컨퍼런스); // when - final List actualEvents = eventService.findEvents(EventType.CONFERENCE, TODAY, + final List actualEvents = eventService.findEvents(EventType.CONFERENCE, + TODAY, "2023-06-01", "2023-06-30", null, null, null); // then assertThat(actualEvents) .usingRecursiveComparison() - .comparingOnlyFields("name", "status", "applyStatus", "imageUrl") + .comparingOnlyFields("name", "status", "applyStatus") .isEqualTo(expectedEvents); } @@ -349,16 +368,17 @@ void findEvents_2023_6() { @DisplayName("2023년 7월 21일에 2023년 7월 17일 이후에 있는 행사를 조회하면, 해당 기간에 걸쳐있는 모든 행사 목록을 조회할 수 있다.") void findEvents_after_2023_7_17() { // given - final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, AI_컨퍼런스, 모바일_컨퍼런스); + final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, AI_컨퍼런스, 모바일_컨퍼런스); // when - final List actualEvents = eventService.findEvents(EventType.CONFERENCE, TODAY, + final List actualEvents = eventService.findEvents(EventType.CONFERENCE, + TODAY, "2023-07-17", null, null, null, null); // then assertThat(actualEvents) .usingRecursiveComparison() - .comparingOnlyFields("name", "status", "applyStatus", "imageUrl") + .comparingOnlyFields("name", "status", "applyStatus") .isEqualTo(expectedEvents); } @@ -366,16 +386,18 @@ void findEvents_after_2023_7_17() { @DisplayName("2023년 7월 21일에 2023년 7월 31일 이전에 있는 행사를 조회하면, 해당 기간에 걸쳐있는 모든 행사 목록을 조회할 수 있다.") void findEvents_before_2023_7_31() { // given - final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, AI_컨퍼런스, 안드로이드_컨퍼런스); + final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, AI_컨퍼런스, + 안드로이드_컨퍼런스); // when - final List actualEvents = eventService.findEvents(EventType.CONFERENCE, TODAY, + final List actualEvents = eventService.findEvents(EventType.CONFERENCE, + TODAY, null, "2023-07-31", null, null, null); // then assertThat(actualEvents) .usingRecursiveComparison() - .comparingOnlyFields("name", "status", "applyStatus", "imageUrl") + .comparingOnlyFields("name", "status", "applyStatus") .isEqualTo(expectedEvents); } @@ -383,7 +405,8 @@ void findEvents_before_2023_7_31() { @DisplayName("아무 행사도 없는 2023년 12월 행사를 조회하면, 빈 목록을 반환한다.") void findEvents_2023_12() { // given, when - final List actualEvents = eventService.findEvents(EventType.CONFERENCE, TODAY, + final List actualEvents = eventService.findEvents(EventType.CONFERENCE, + TODAY, "2023-12-01", "2023-12-31", null, null, null); // then @@ -435,16 +458,17 @@ void findEvents_start_after_end_fail() { @DisplayName("'안드로이드' 태그를 포함하는 행사 목록을 조회할 수 있다.") void findEvents_tag_filter() { // given - final List expectedEvents = List.of(인프콘_2023, 모바일_컨퍼런스, 안드로이드_컨퍼런스); + final List expectedEvents = List.of(인프콘_2023, 모바일_컨퍼런스, 안드로이드_컨퍼런스); // when - final List actualEvents = eventService.findEvents(EventType.CONFERENCE, TODAY, + final List actualEvents = eventService.findEvents(EventType.CONFERENCE, + TODAY, null, null, List.of("안드로이드"), null, null); // then assertThat(actualEvents) .usingRecursiveComparison() - .comparingOnlyFields("name", "status", "applyStatus", "imageUrl") + .comparingOnlyFields("name", "status", "applyStatus") .isEqualTo(expectedEvents); } @@ -452,16 +476,18 @@ void findEvents_tag_filter() { @DisplayName("'안드로이드', '백엔드' 태그를 포함하는 행사 목록을 조회할 수 있다.") void findEvents_tags_filter() { // given - final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, 모바일_컨퍼런스, 안드로이드_컨퍼런스); + final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, 모바일_컨퍼런스, + 안드로이드_컨퍼런스); // when - final List actualEvents = eventService.findEvents(EventType.CONFERENCE, TODAY, + final List actualEvents = eventService.findEvents(EventType.CONFERENCE, + TODAY, null, null, List.of("안드로이드", "백엔드"), null, null); // then assertThat(actualEvents) .usingRecursiveComparison() - .comparingOnlyFields("name", "status", "applyStatus", "imageUrl") + .comparingOnlyFields("name", "status", "applyStatus") .isEqualTo(expectedEvents); } @@ -482,16 +508,17 @@ void findEvents_tag_filter_fail() { @DisplayName("'진행 중' 상태의 행사 목록을 조회할 수 있다.") void findEvents_status_filter() { // given - final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스); + final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스); // when - final List actualEvents = eventService.findEvents(EventType.CONFERENCE, TODAY, + final List actualEvents = eventService.findEvents(EventType.CONFERENCE, + TODAY, null, null, null, List.of(IN_PROGRESS), null); // then assertThat(actualEvents) .usingRecursiveComparison() - .comparingOnlyFields("name", "status", "applyStatus", "imageUrl") + .comparingOnlyFields("name", "status", "applyStatus") .isEqualTo(expectedEvents); } @@ -499,16 +526,17 @@ void findEvents_status_filter() { @DisplayName("'진행 중' 및 '진행 예정' 상태의 행사 목록을 조회할 수 있다.") void findEvents_statuses_filter() { // given - final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, AI_컨퍼런스, 모바일_컨퍼런스); + final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, AI_컨퍼런스, 모바일_컨퍼런스); // when - final List actualEvents = eventService.findEvents(EventType.CONFERENCE, TODAY, + final List actualEvents = eventService.findEvents(EventType.CONFERENCE, + TODAY, null, null, null, List.of(EventStatus.UPCOMING, IN_PROGRESS), null); // then assertThat(actualEvents) .usingRecursiveComparison() - .comparingOnlyFields("name", "status", "applyStatus", "imageUrl") + .comparingOnlyFields("name", "status", "applyStatus") .isEqualTo(expectedEvents); } @@ -516,16 +544,17 @@ void findEvents_statuses_filter() { @DisplayName("9월에 존재하는 진행 예정인 '안드로이드', '백엔드' 태그를 포함하는 행사 목록을 조회할 수 있다.") void findEvents_period_tags_filter() { // given - final List expectedEvents = List.of(모바일_컨퍼런스); + final List expectedEvents = List.of(모바일_컨퍼런스); // when - final List actualEvents = eventService.findEvents(EventType.CONFERENCE, TODAY, + final List actualEvents = eventService.findEvents(EventType.CONFERENCE, + TODAY, "2023-09-01", "2023-09-30", List.of("안드로이드", "백엔드"), List.of(EventStatus.UPCOMING), null); // then assertThat(actualEvents) .usingRecursiveComparison() - .comparingOnlyFields("name", "status", "applyStatus", "imageUrl") + .comparingOnlyFields("name", "status", "applyStatus") .isEqualTo(expectedEvents); } @@ -538,11 +567,12 @@ class SearchEvent { @DisplayName("2023년 7월 21일에 컨퍼런스 행사를 조회할 때, 검색어가 공백인 경우 해당 카테고리에 해당하는 모든 행사 목록을 조회할 수 있다.") void findEvents_blank_search(final String keyword) { // given - final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, 구름톤, AI_컨퍼런스, 모바일_컨퍼런스, + final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, 구름톤, AI_컨퍼런스, + 모바일_컨퍼런스, 안드로이드_컨퍼런스, AI_아이디어_공모전); // when - final List actualEvents = eventService.findEvents(null, + final List actualEvents = eventService.findEvents(null, TODAY, null, null, null, null, keyword); @@ -558,11 +588,11 @@ void findEvents_blank_search(final String keyword) { void findEvents_search() { // given final String keyword = " 컨퍼런스"; - final List expectedEvents = List.of(웹_컨퍼런스, AI_컨퍼런스, 모바일_컨퍼런스, + final List expectedEvents = List.of(웹_컨퍼런스, AI_컨퍼런스, 모바일_컨퍼런스, 안드로이드_컨퍼런스); // when - final List actualEvents = eventService.findEvents(null, + final List actualEvents = eventService.findEvents(null, TODAY, null, null, null, null, keyword); // then @@ -577,10 +607,10 @@ void findEvents_search() { void findEvents_multiple_tokens_search() { // given final String keyword = " 모 컨퍼"; - final List expectedEvents = List.of(모바일_컨퍼런스); + final List expectedEvents = List.of(모바일_컨퍼런스); // when - final List actualEvents = eventService.findEvents(null, + final List actualEvents = eventService.findEvents(null, TODAY, null, null, null, null, keyword); // then @@ -595,10 +625,10 @@ void findEvents_multiple_tokens_search() { void findEvents_status_filter_and_search() { // given final String keyword = "프콘 "; - final List expectedEvents = List.of(인프콘_2023); + final List expectedEvents = List.of(인프콘_2023); // when - final List actualEvents = eventService.findEvents(null, + final List actualEvents = eventService.findEvents(null, TODAY, null, null, null, List.of(IN_PROGRESS), keyword); diff --git a/backend/emm-sale/src/test/java/com/emmsale/scrap/application/ScrapQueryServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/scrap/application/ScrapQueryServiceTest.java index b522baca9..2eb7dc65d 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/scrap/application/ScrapQueryServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/scrap/application/ScrapQueryServiceTest.java @@ -7,8 +7,9 @@ import static org.assertj.core.api.Assertions.assertThat; import com.emmsale.event.EventFixture; -import com.emmsale.event.application.dto.EventResponse; +import com.emmsale.event.application.dto.EventDetailResponse; import com.emmsale.event.domain.Event; +import com.emmsale.event.domain.EventTag; import com.emmsale.event.domain.repository.EventRepository; import com.emmsale.helper.ServiceIntegrationTestHelper; import com.emmsale.image.domain.repository.ImageRepository; @@ -16,8 +17,9 @@ import com.emmsale.member.domain.MemberRepository; import com.emmsale.scrap.domain.Scrap; import com.emmsale.scrap.domain.ScrapRepository; -import java.util.Collections; +import com.emmsale.tag.domain.Tag; import java.util.List; +import java.util.stream.Collectors; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -50,20 +52,37 @@ void findAllScrapsTest() { scrapRepository.save(new Scrap(member.getId(), event2)); //when - final List actual = scrapQueryService.findAllScraps(member); + final List actual = scrapQueryService.findAllScraps(member); - final List expected = List.of( - new EventResponse(event1.getId(), event1.getName(), event1.getEventPeriod().getStartDate(), - event1.getEventPeriod().getEndDate(), event1.getEventPeriod().getApplyStartDate(), - event1.getEventPeriod().getApplyEndDate(), - Collections.emptyList(), 행사_이미지1(event1.getId()).getName(), - event1.getEventMode().getValue(), - event1.getPaymentType().getValue()), - new EventResponse(event2.getId(), event2.getName(), event2.getEventPeriod().getStartDate(), - event2.getEventPeriod().getEndDate(), event2.getEventPeriod().getApplyStartDate(), - event2.getEventPeriod().getApplyEndDate(), - Collections.emptyList(), null, event2.getEventMode().getValue(), - event2.getPaymentType().getValue()) + final List expected = List.of( + new EventDetailResponse( + event1.getId(), event1.getName(), event1.getInformationUrl(), + event1.getEventPeriod().getStartDate(), event1.getEventPeriod().getEndDate(), + event1.getEventPeriod().getApplyStartDate(), event1.getEventPeriod().getApplyEndDate(), + event1.getLocation(), + event1.getTags() + .stream() + .map(EventTag::getTag) + .map(Tag::getName) + .collect(Collectors.toList()), + 행사_이미지1(event1.getId()).getName(), event1.getType().name(), + List.of(행사_이미지2(event1.getId()).getName(), 행사_이미지3(event1.getId()).getName(), + 행사_이미지4(event1.getId()).getName()), + event1.getOrganization(), event1.getPaymentType().getValue(), + event1.getEventMode().getValue()), + new EventDetailResponse(event2.getId(), event2.getName(), event2.getInformationUrl(), + event2.getEventPeriod().getStartDate(), event2.getEventPeriod().getEndDate(), + event2.getEventPeriod().getApplyStartDate(), event2.getEventPeriod().getApplyEndDate(), + event2.getLocation(), + event2.getTags() + .stream() + .map(EventTag::getTag) + .map(Tag::getName) + .collect(Collectors.toList()), + null, event2.getType().name(), + List.of(), + event2.getOrganization(), event2.getPaymentType().getValue(), + event2.getEventMode().getValue()) ); //then From cc120f639e081f4ef746131a41ba0b9071570353 Mon Sep 17 00:00:00 2001 From: amaran-th Date: Mon, 30 Oct 2023 19:38:57 +0900 Subject: [PATCH 02/41] =?UTF-8?q?refactor:=20API=20=EB=AC=B8=EC=84=9C?= =?UTF-8?q?=EC=9D=98=20API=20=EC=88=9C=EC=84=9C=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #803 --- .../src/documentTest/asciidoc/index.adoc | 165 +++++++++--------- 1 file changed, 83 insertions(+), 82 deletions(-) diff --git a/backend/emm-sale/src/documentTest/asciidoc/index.adoc b/backend/emm-sale/src/documentTest/asciidoc/index.adoc index b28a6da0f..701dc6e70 100644 --- a/backend/emm-sale/src/documentTest/asciidoc/index.adoc +++ b/backend/emm-sale/src/documentTest/asciidoc/index.adoc @@ -19,20 +19,6 @@ include::{snippets}/find-all-activities/http-response.adoc[] .HTTP response 설명 include::{snippets}/find-all-activities/response-fields.adoc[] -=== `POST`: 새로운 Activity 추가 - -.HTTP request -include::{snippets}/add-activity/http-request.adoc[] - -.HTTP request 설명 -include::{snippets}/add-activity/request-fields.adoc[] - -.HTTP response -include::{snippets}/add-activity/http-response.adoc[] - -.HTTP response 설명 -include::{snippets}/add-activity/response-fields.adoc[] - == Member === `POST`: 초기 사용자 등록 @@ -192,6 +178,31 @@ include::{snippets}/delete-interest-tag/response-fields.adoc[] == Event +=== `GET`: 행사 목록 조회 + +.HTTP request +include::{snippets}/find-events/http-request.adoc[] + +.HTTP request 설명 +include::{snippets}/find-events/request-parameters.adoc[] + +.HTTP response +include::{snippets}/find-events/http-response.adoc[] + +.HTTP response 설명 +include::{snippets}/find-events/response-fields.adoc[] + +=== `GET` : 행사 상세정보 조회 + +.HTTP request +include::{snippets}/find-event/http-request.adoc[] + +.HTTP response +include::{snippets}/find-event/http-response.adoc[] + +.HTTP response 설명 +include::{snippets}/find-event/response-fields.adoc[] + === `POST` : 행사의 함께 해요 목록에 게시글 추가 .HTTP request 설명 @@ -219,71 +230,46 @@ include::{snippets}/delete-recruitment-post/http-request.adoc[] .HTTP response include::{snippets}/delete-recruitment-post/http-response.adoc[] -=== `GET`: 사용자가 해당 행사에 이미 함께 해요 게시글을 작성했는지 확인 - -.HTTP request -include::{snippets}/check-already-recruitment-post/http-request.adoc[] - -.HTTP response -include::{snippets}/check-already-recruitment-post/http-response.adoc[] - -=== `GET`: 사용자의 id를 기반으로 사용자가 작성한 모든 함께해요 요청 조회 - -.HTTP request -include::{snippets}/find-all-by-member-id-recruitment-post/http-request.adoc[] - -.HTTP response -include::{snippets}/find-all-by-member-id-recruitment-post/http-response.adoc[] - -.HTTP response 설명 -include::{snippets}/find-all-by-member-id-recruitment-post/response-fields.adoc[] - -=== `GET` : 행사 상세정보 조회 +=== `GET` : 함께 해요 게시글 목록 조회 .HTTP request -include::{snippets}/find-event/http-request.adoc[] +include::{snippets}/find-recruitment-posts/http-request.adoc[] .HTTP response -include::{snippets}/find-event/http-response.adoc[] +include::{snippets}/find-recruitment-posts/http-response.adoc[] .HTTP response 설명 -include::{snippets}/find-event/response-fields.adoc[] +include::{snippets}/find-recruitment-posts/response-fields.adoc[] -=== `GET`: 행사 목록 조회 +=== `GET` : 함께 해요 게시글 단건 조회 .HTTP request -include::{snippets}/find-events/http-request.adoc[] - -.HTTP request 설명 -include::{snippets}/find-events/request-parameters.adoc[] +include::{snippets}/find-recruitment-post/http-request.adoc[] .HTTP response -include::{snippets}/find-events/http-response.adoc[] +include::{snippets}/find-recruitment-post/http-response.adoc[] .HTTP response 설명 -include::{snippets}/find-events/response-fields.adoc[] +include::{snippets}/find-recruitment-post/response-fields.adoc[] -=== `GET` : 함께 해요 게시글 목록 조회 +=== `GET`: 사용자의 id를 기반으로 사용자가 작성한 모든 함께해요 요청 조회 .HTTP request -include::{snippets}/find-recruitment-posts/http-request.adoc[] +include::{snippets}/find-all-by-member-id-recruitment-post/http-request.adoc[] .HTTP response -include::{snippets}/find-recruitment-posts/http-response.adoc[] +include::{snippets}/find-all-by-member-id-recruitment-post/http-response.adoc[] .HTTP response 설명 -include::{snippets}/find-recruitment-posts/response-fields.adoc[] +include::{snippets}/find-all-by-member-id-recruitment-post/response-fields.adoc[] -=== `GET` : 함께 해요 게시글 단건 조회 +=== `GET`: 사용자가 해당 행사에 이미 함께 해요 게시글을 작성했는지 확인 .HTTP request -include::{snippets}/find-recruitment-post/http-request.adoc[] +include::{snippets}/check-already-recruitment-post/http-request.adoc[] .HTTP response -include::{snippets}/find-recruitment-post/http-response.adoc[] - -.HTTP response 설명 -include::{snippets}/find-recruitment-post/response-fields.adoc[] +include::{snippets}/check-already-recruitment-post/http-response.adoc[] == Comment @@ -401,32 +387,29 @@ include::{snippets}/unregister-block/http-request.adoc[] .HTTP response include::{snippets}/unregister-block/http-response.adoc[] -== TAG - -=== `GET` : 존재하는 모든 태그 조회 +=== `GET` : 차단한 사용자 목록 조회 .HTTP request -include::{snippets}/find-tags/http-request.adoc[] +include::{snippets}/find-blocks/http-request.adoc[] .HTTP response -include::{snippets}/find-tags/http-response.adoc[] +include::{snippets}/find-blocks/http-response.adoc[] .HTTP response 설명 -include::{snippets}/find-tags/response-fields.adoc[] +include::{snippets}/find-blocks/response-fields.adoc[] -=== `POST` : 새로운 태그 추가 +== TAG -.HTTP request -include::{snippets}/add-tag/http-request.adoc[] +=== `GET` : 존재하는 모든 태그 조회 -.HTTP request 설명 -include::{snippets}/add-tag/request-fields.adoc[] +.HTTP request +include::{snippets}/find-tags/http-request.adoc[] .HTTP response -include::{snippets}/add-tag/http-response.adoc[] +include::{snippets}/find-tags/http-response.adoc[] .HTTP response 설명 -include::{snippets}/add-tag/response-fields.adoc[] +include::{snippets}/find-tags/response-fields.adoc[] == Report @@ -444,17 +427,6 @@ include::{snippets}/add-report/http-response.adoc[] .HTTP response 설명 include::{snippets}/add-report/response-fields.adoc[] -=== `GET` : 신고 목록 전체 조회 - -.HTTP request -include::{snippets}/find-reports/http-request.adoc[] - -.HTTP response -include::{snippets}/find-reports/http-response.adoc[] - -.HTTP response 설명 -include::{snippets}/find-reports/response-fields.adoc[] - == 스크랩 === `GET` : 전체 스크랩 조회 @@ -696,14 +668,43 @@ include::{snippets}/delete-event/http-request.adoc[] .HTTP response include::{snippets}/delete-event/http-response.adoc[] -=== `GET` : 차단한 사용자 목록 조회 +=== `POST`: 새로운 Activity 추가 .HTTP request -include::{snippets}/find-blocks/http-request.adoc[] +include::{snippets}/add-activity/http-request.adoc[] + +.HTTP request 설명 +include::{snippets}/add-activity/request-fields.adoc[] .HTTP response -include::{snippets}/find-blocks/http-response.adoc[] +include::{snippets}/add-activity/http-response.adoc[] .HTTP response 설명 -include::{snippets}/find-blocks/response-fields.adoc[] +include::{snippets}/add-activity/response-fields.adoc[] + +=== `POST` : 새로운 태그 추가 + +.HTTP request +include::{snippets}/add-tag/http-request.adoc[] + +.HTTP request 설명 +include::{snippets}/add-tag/request-fields.adoc[] + +.HTTP response +include::{snippets}/add-tag/http-response.adoc[] + +.HTTP response 설명 +include::{snippets}/add-tag/response-fields.adoc[] + +=== `GET` : 신고 목록 전체 조회 + +.HTTP request +include::{snippets}/find-reports/http-request.adoc[] + +.HTTP response +include::{snippets}/find-reports/http-response.adoc[] + +.HTTP response 설명 +include::{snippets}/find-reports/response-fields.adoc[] + From 0fdaea53b9c479bded05e5b65626521d710376ae Mon Sep 17 00:00:00 2001 From: amaran-th Date: Mon, 30 Oct 2023 21:00:06 +0900 Subject: [PATCH 03/41] =?UTF-8?q?refactor:=20EventService=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - EventService를 Command와 Query로 분리 - AllImagesOfContent 일급컬렉션 구현 #803 --- .../java/com/emmsale/EventApiTest.java | 10 +- .../java/com/emmsale/MockMvcTestHelper.java | 7 +- .../java/com/emmsale/event/api/EventApi.java | 17 +- .../application/EventCommandService.java | 102 ++++ ...entService.java => EventQueryService.java} | 176 +------ .../application/dto/EventDetailResponse.java | 29 ++ .../com/emmsale/event/domain/EventStatus.java | 1 + .../image/domain/AllImagesOfContent.java | 34 ++ .../java/com/emmsale/image/domain/Image.java | 22 +- .../scrap/application/ScrapQueryService.java | 59 +-- .../application/EventCommandServiceTest.java | 467 ++++++++++++++++++ ...ceTest.java => EventQueryServiceTest.java} | 419 ++-------------- ...ventServiceEventIntegrationTestQuery.java} | 10 +- 13 files changed, 750 insertions(+), 603 deletions(-) create mode 100644 backend/emm-sale/src/main/java/com/emmsale/event/application/EventCommandService.java rename backend/emm-sale/src/main/java/com/emmsale/event/application/{EventService.java => EventQueryService.java} (55%) create mode 100644 backend/emm-sale/src/main/java/com/emmsale/image/domain/AllImagesOfContent.java create mode 100644 backend/emm-sale/src/test/java/com/emmsale/event/application/EventCommandServiceTest.java rename backend/emm-sale/src/test/java/com/emmsale/event/application/{EventServiceTest.java => EventQueryServiceTest.java} (62%) rename backend/emm-sale/src/test/java/com/emmsale/event/application/{EventServiceEventIntegrationTest.java => EventServiceEventIntegrationTestQuery.java} (95%) diff --git a/backend/emm-sale/src/documentTest/java/com/emmsale/EventApiTest.java b/backend/emm-sale/src/documentTest/java/com/emmsale/EventApiTest.java index ac02b8497..40207691b 100644 --- a/backend/emm-sale/src/documentTest/java/com/emmsale/EventApiTest.java +++ b/backend/emm-sale/src/documentTest/java/com/emmsale/EventApiTest.java @@ -118,7 +118,7 @@ void findEvent() throws Exception { "https://www.image.com", EventType.COMPETITION.toString(), List.of("imageUrl1", "imageUrl2"), "인프런", "유료", "온라인"); - Mockito.when(eventService.findEvent(ArgumentMatchers.anyLong(), any())) + Mockito.when(eventQueryService.findEvent(ArgumentMatchers.anyLong(), any())) .thenReturn(eventDetailResponse); //when @@ -176,7 +176,7 @@ void findEvents() throws Exception { EventMode.ONLINE.getValue()) ); - Mockito.when(eventService.findEvents(any(EventType.class), + Mockito.when(eventQueryService.findEvents(any(EventType.class), any(LocalDateTime.class), eq("2023-07-01"), eq("2023-07-31"), eq(null), any(), eq("컨퍼"))).thenReturn(eventResponses); @@ -232,7 +232,7 @@ void updateEventTest() throws Exception { "image1.jpg", request.getType().toString(), List.of("imageUrl1", "imageUrl2"), "행사기관", "유료", "온라인"); - Mockito.when(eventService.updateEvent(eq(eventId), any(EventDetailRequest.class), any())) + Mockito.when(eventCommandService.updateEvent(eq(eventId), any(EventDetailRequest.class), any())) .thenReturn(response); final String contents = objectMapper.writeValueAsString(request); @@ -282,7 +282,7 @@ void deleteEventTest() throws Exception { //given final long eventId = 1L; - Mockito.doNothing().when(eventService).deleteEvent(eventId); + Mockito.doNothing().when(eventCommandService).deleteEvent(eventId); //when final ResultActions result = mockMvc.perform( delete("/events/" + eventId)); @@ -334,7 +334,7 @@ void addEventTest() throws Exception { "image1.jpg", request.getType().toString(), List.of("imageUrl1", "imageUrl2"), "행사기관", "무료", "오프라인"); - Mockito.when(eventService.addEvent(any(EventDetailRequest.class), any())) + Mockito.when(eventCommandService.addEvent(any(EventDetailRequest.class), any())) .thenReturn(response); final String contents = objectMapper.writeValueAsString(request); diff --git a/backend/emm-sale/src/documentTest/java/com/emmsale/MockMvcTestHelper.java b/backend/emm-sale/src/documentTest/java/com/emmsale/MockMvcTestHelper.java index 003ba7104..dc74849c6 100644 --- a/backend/emm-sale/src/documentTest/java/com/emmsale/MockMvcTestHelper.java +++ b/backend/emm-sale/src/documentTest/java/com/emmsale/MockMvcTestHelper.java @@ -9,7 +9,8 @@ import com.emmsale.block.application.BlockQueryService; import com.emmsale.comment.application.CommentCommandService; import com.emmsale.comment.application.CommentQueryService; -import com.emmsale.event.application.EventService; +import com.emmsale.event.application.EventCommandService; +import com.emmsale.event.application.EventQueryService; import com.emmsale.event.application.RecruitmentPostCommandService; import com.emmsale.event.application.RecruitmentPostQueryService; import com.emmsale.feed.application.FeedCommandService; @@ -77,7 +78,9 @@ abstract class MockMvcTestHelper { @MockBean protected LoginService loginService; @MockBean - protected EventService eventService; + protected EventQueryService eventQueryService; + @MockBean + protected EventCommandService eventCommandService; @MockBean protected CommentCommandService commentCommandService; @MockBean diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java b/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java index 44f2d29a0..aadbaaee4 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java @@ -1,6 +1,7 @@ package com.emmsale.event.api; -import com.emmsale.event.application.EventService; +import com.emmsale.event.application.EventCommandService; +import com.emmsale.event.application.EventQueryService; import com.emmsale.event.application.dto.EventDetailRequest; import com.emmsale.event.application.dto.EventDetailResponse; import com.emmsale.event.domain.EventStatus; @@ -30,11 +31,12 @@ @RequiredArgsConstructor public class EventApi { - private final EventService eventService; + private final EventQueryService eventQueryService; + private final EventCommandService eventCommandService; @GetMapping("/{id}") public ResponseEntity findEventById(@PathVariable final Long id) { - return ResponseEntity.ok(eventService.findEvent(id, LocalDate.now())); + return ResponseEntity.ok(eventQueryService.findEvent(id, LocalDate.now())); } @GetMapping @@ -46,7 +48,8 @@ public ResponseEntity> findEvents( @RequestParam(required = false) final List statuses, @RequestParam(required = false) final String keyword) { return ResponseEntity.ok( - eventService.findEvents(category, LocalDateTime.now(), startDate, endDate, tags, statuses, + eventQueryService.findEvents(category, LocalDateTime.now(), startDate, endDate, tags, + statuses, keyword)); } @@ -54,7 +57,7 @@ public ResponseEntity> findEvents( @ResponseStatus(HttpStatus.CREATED) public EventDetailResponse addEvent(@RequestPart @Valid final EventDetailRequest request, @RequestPart final List images) { - return eventService.addEvent(request, images); + return eventCommandService.addEvent(request, images); } @PutMapping(path = "/{eventId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @@ -62,12 +65,12 @@ public EventDetailResponse addEvent(@RequestPart @Valid final EventDetailRequest public EventDetailResponse updateEvent(@PathVariable final Long eventId, @RequestPart @Valid final EventDetailRequest request, @RequestPart final List images) { - return eventService.updateEvent(eventId, request, images); + return eventCommandService.updateEvent(eventId, request, images); } @DeleteMapping("/{eventId}") @ResponseStatus(HttpStatus.NO_CONTENT) public void deleteEvent(@PathVariable final Long eventId) { - eventService.deleteEvent(eventId); + eventCommandService.deleteEvent(eventId); } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventCommandService.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventCommandService.java new file mode 100644 index 000000000..6bc0b23df --- /dev/null +++ b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventCommandService.java @@ -0,0 +1,102 @@ +package com.emmsale.event.application; + +import static com.emmsale.event.exception.EventExceptionType.NOT_FOUND_EVENT; +import static java.util.stream.Collectors.toList; + +import com.emmsale.event.application.dto.EventDetailRequest; +import com.emmsale.event.application.dto.EventDetailResponse; +import com.emmsale.event.domain.Event; +import com.emmsale.event.domain.repository.EventRepository; +import com.emmsale.event.domain.repository.EventTagRepository; +import com.emmsale.event.exception.EventException; +import com.emmsale.event.exception.EventExceptionType; +import com.emmsale.event_publisher.EventPublisher; +import com.emmsale.image.application.ImageCommandService; +import com.emmsale.image.domain.AllImagesOfContent; +import com.emmsale.image.domain.ImageType; +import com.emmsale.tag.application.dto.TagRequest; +import com.emmsale.tag.domain.Tag; +import com.emmsale.tag.domain.TagRepository; +import java.util.ArrayList; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +@Service +@Transactional +@RequiredArgsConstructor +public class EventCommandService { + + private final EventRepository eventRepository; + private final EventTagRepository eventTagRepository; + private final TagRepository tagRepository; + private final EventPublisher eventPublisher; + private final ImageCommandService imageCommandService; + + public EventDetailResponse addEvent(final EventDetailRequest request, + final List images) { + final Event event = eventRepository.save(request.toEvent()); + final List tags = findAllPersistTagsOrElseThrow(request.getTags()); + event.addAllEventTags(tags); + + final AllImagesOfContent savedImages = new AllImagesOfContent(imageCommandService + .saveImages(ImageType.EVENT, event.getId(), images)); + + eventPublisher.publish(event); + final String thumbnailImageUrl = savedImages.extractThumbnailImage(); + final List informationImageUrls = savedImages.extractInformationImages(); + return EventDetailResponse.from(event, thumbnailImageUrl, informationImageUrls); + } + + public EventDetailResponse updateEvent(final Long eventId, final EventDetailRequest request, + final List images) { + final Event event = eventRepository.findById(eventId) + .orElseThrow(() -> new EventException(NOT_FOUND_EVENT)); + + final List tags = findAllPersistTagsOrElseThrow(request.getTags()); + + // TODO: 2023/09/25 더 좋은 방법을 고민해보기 + eventTagRepository.deleteAllByEventId(eventId); + final Event updatedEvent = event.updateEventContent( + request.getName(), + request.getLocation(), + request.getStartDateTime(), + request.getEndDateTime(), + request.getApplyStartDateTime(), + request.getApplyEndDateTime(), + request.getInformationUrl(), + tags, + request.getType(), + request.getEventMode(), + request.getPaymentType(), + request.getOrganization() + ); + imageCommandService.deleteImages(ImageType.EVENT, eventId); + final AllImagesOfContent savedImages = new AllImagesOfContent(imageCommandService + .saveImages(ImageType.EVENT, event.getId(), images)); + final String thumbnailImageUrl = savedImages.extractThumbnailImage(); + final List informationImageUrls = savedImages.extractInformationImages(); + return EventDetailResponse.from(updatedEvent, thumbnailImageUrl, informationImageUrls); + } + + public void deleteEvent(final Long eventId) { + if (!eventRepository.existsById(eventId)) { + throw new EventException(NOT_FOUND_EVENT); + } + imageCommandService.deleteImages(ImageType.EVENT, eventId); + eventRepository.deleteById(eventId); + } + + private List findAllPersistTagsOrElseThrow(final List tags) { + if (tags == null || tags.isEmpty()) { + return new ArrayList<>(); + } + + return tags.stream() + .map(tag -> tagRepository.findByName(tag.getName()) + .orElseThrow(() -> new EventException(EventExceptionType.NOT_FOUND_TAG))) + .collect(toList()); + } +} diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventQueryService.java similarity index 55% rename from backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java rename to backend/emm-sale/src/main/java/com/emmsale/event/application/EventQueryService.java index 4b2bf6136..7f0b0790b 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventQueryService.java @@ -10,21 +10,17 @@ import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.toList; -import com.emmsale.event.application.dto.EventDetailRequest; import com.emmsale.event.application.dto.EventDetailResponse; import com.emmsale.event.domain.Event; import com.emmsale.event.domain.EventStatus; import com.emmsale.event.domain.EventType; import com.emmsale.event.domain.repository.EventRepository; -import com.emmsale.event.domain.repository.EventTagRepository; import com.emmsale.event.exception.EventException; import com.emmsale.event.exception.EventExceptionType; -import com.emmsale.event_publisher.EventPublisher; -import com.emmsale.image.application.ImageCommandService; +import com.emmsale.image.domain.AllImagesOfContent; import com.emmsale.image.domain.Image; import com.emmsale.image.domain.ImageType; import com.emmsale.image.domain.repository.ImageRepository; -import com.emmsale.tag.application.dto.TagRequest; import com.emmsale.tag.domain.Tag; import com.emmsale.tag.domain.TagRepository; import com.emmsale.tag.exception.TagException; @@ -32,7 +28,6 @@ import java.time.LocalDateTime; import java.time.format.DateTimeParseException; import java.util.ArrayList; -import java.util.Collections; import java.util.EnumMap; import java.util.HashMap; import java.util.List; @@ -42,54 +37,33 @@ import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.multipart.MultipartFile; @Service -@Transactional +@Transactional(readOnly = true) @RequiredArgsConstructor -public class EventService { +public class EventQueryService { private static final String MIN_DATE = "2000-01-01"; private static final String MAX_DATE = "2999-12-31"; private final EventRepository eventRepository; - private final EventTagRepository eventTagRepository; private final TagRepository tagRepository; - private final EventPublisher eventPublisher; - private final ImageCommandService imageCommandService; private final ImageRepository imageRepository; - @Transactional(readOnly = true) public EventDetailResponse findEvent(final Long id, final LocalDate today) { final Event event = eventRepository.findById(id) .orElseThrow(() -> new EventException(NOT_FOUND_EVENT)); - final List imageUrls = imageRepository + final AllImagesOfContent images = new AllImagesOfContent(imageRepository .findAllByTypeAndContentId(ImageType.EVENT, event.getId()) .stream() .sorted(comparing(Image::getOrder)) - .map(Image::getName) - .collect(toList()); - final String thumbnailImageUrl = extractThumbnailImage(imageUrls); - final List informationImageUrls = extractInformationImages(imageUrls); + .collect(toList())); + final String thumbnailImageUrl = images.extractThumbnailImage(); + final List informationImageUrls = images.extractInformationImages(); return EventDetailResponse.from(event, thumbnailImageUrl, informationImageUrls); } - private String extractThumbnailImage(final List imageUrls) { - if (imageUrls.isEmpty()) { - return null; - } - return imageUrls.get(0); - } - - private List extractInformationImages(final List imageUrls) { - if (imageUrls.size() <= 1) { - return Collections.emptyList(); - } - return imageUrls.subList(1, imageUrls.size()); - } - - @Transactional(readOnly = true) public List findEvents(final EventType category, final LocalDateTime nowDateTime, final String startDate, final String endDate, final List tagNames, final List statuses, final String keyword) { @@ -104,7 +78,7 @@ public List findEvents(final EventType category, final EnumMap> eventsForEventStatus = groupByEventStatus(nowDateTime, events); - return filterByStatuses(statuses, eventsForEventStatus, makeImageUrlsPerEventId(events)); + return filterByStatuses(statuses, eventsForEventStatus, makeImagesPerEventId(events)); } private Specification filterByCategoryIfExist(final EventType category, @@ -183,25 +157,6 @@ private void validateEndDateAfterDateStart(final LocalDateTime startDate, } } - // TODO: 2023/09/27 코드 중복 제거(ScrapService) - private Map> makeImageUrlsPerEventId(final List events) { - final List eventIds = events.stream() - .map(Event::getId) - .collect(Collectors.toList()); - - Map> imageUrlsPerEventId = new HashMap<>(); - for (Long eventId : eventIds) { - final List images = imageRepository.findAllByTypeAndContentId(ImageType.EVENT, - eventId) - .stream() - .sorted(comparing(Image::getOrder)) - .map(Image::getName) - .collect(toList()); - imageUrlsPerEventId.put(eventId, images); - } - return imageUrlsPerEventId; - } - private Specification filterByKeywordIfExist(final String keyword, Specification spec) { if (isExistKeyword(keyword)) { @@ -228,12 +183,12 @@ private EnumMap> groupByEventStatus(final LocalDateTime private List filterByStatuses( final List statuses, final EnumMap> eventsForEventStatus, - final Map> imageUrlsPerEventId + final Map imagesPerEventId ) { if (isExistStatusName(statuses)) { - return filterEventResponseByStatuses(statuses, eventsForEventStatus, imageUrlsPerEventId); + return filterEventResponseByStatuses(statuses, eventsForEventStatus, imagesPerEventId); } - return mergeEventResponses(eventsForEventStatus, imageUrlsPerEventId); + return EventDetailResponse.mergeEventResponses(eventsForEventStatus, imagesPerEventId); } private boolean isExistStatusName(final List statuses) { @@ -243,114 +198,31 @@ private boolean isExistStatusName(final List statuses) { private List filterEventResponseByStatuses( final List statuses, final EnumMap> eventsForEventStatus, - final Map> imageUrlsPerEventId + final Map imagesPerEventId ) { return eventsForEventStatus.entrySet() .stream() .filter(entry -> statuses.contains(entry.getKey())) - .map( - entry -> makeEventResponsesByStatus(entry.getValue(), - imageUrlsPerEventId)) + .map(entry -> EventDetailResponse.makeEventResponsesByStatus(entry.getValue(), + imagesPerEventId)) .reduce(new ArrayList<>(), (combinedEvents, eventsToAdd) -> { combinedEvents.addAll(eventsToAdd); return combinedEvents; }); } - private List makeEventResponsesByStatus(final List events, - final Map> imageUrlsPerEventId) { - return events.stream() - .map(event -> { - final List allImageUrls = imageUrlsPerEventId.get(event.getId()); - final String thumbnailImageUrl = extractThumbnailImage(allImageUrls); - final List informationImageUrls = extractInformationImages(allImageUrls); - return EventDetailResponse.from(event, thumbnailImageUrl, informationImageUrls); - }) + // TODO: 2023/09/27 코드 중복 제거(ScrapService) + private Map makeImagesPerEventId(final List events) { + final List eventIds = events.stream() + .map(Event::getId) .collect(Collectors.toList()); - } - - private List mergeEventResponses( - final Map> groupByEventStatus, - final Map> imageUrlsPerEventId - ) { - return groupByEventStatus.values().stream() - .map(events -> makeEventResponsesByStatus(events, imageUrlsPerEventId)) - .reduce(new ArrayList<>(), (combinedEvents, eventsToAdd) -> { - combinedEvents.addAll(eventsToAdd); - return combinedEvents; - }); - } - - public EventDetailResponse addEvent(final EventDetailRequest request, - final List images) { - final Event event = eventRepository.save(request.toEvent()); - final List tags = findAllPersistTagsOrElseThrow(request.getTags()); - event.addAllEventTags(tags); - - final List imageUrls = imageCommandService - .saveImages(ImageType.EVENT, event.getId(), images) - .stream() - .sorted(comparing(Image::getOrder)) - .map(Image::getName) - .collect(toList()); - - eventPublisher.publish(event); - final String thumbnailImageUrl = extractThumbnailImage(imageUrls); - final List informationImageUrls = extractInformationImages(imageUrls); - return EventDetailResponse.from(event, thumbnailImageUrl, informationImageUrls); - } - public EventDetailResponse updateEvent(final Long eventId, final EventDetailRequest request, - final List images) { - final Event event = eventRepository.findById(eventId) - .orElseThrow(() -> new EventException(NOT_FOUND_EVENT)); - - final List tags = findAllPersistTagsOrElseThrow(request.getTags()); - - // TODO: 2023/09/25 더 좋은 방법을 고민해보기 - eventTagRepository.deleteAllByEventId(eventId); - final Event updatedEvent = event.updateEventContent( - request.getName(), - request.getLocation(), - request.getStartDateTime(), - request.getEndDateTime(), - request.getApplyStartDateTime(), - request.getApplyEndDateTime(), - request.getInformationUrl(), - tags, - request.getType(), - request.getEventMode(), - request.getPaymentType(), - request.getOrganization() - ); - imageCommandService.deleteImages(ImageType.EVENT, eventId); - final List imageUrls = imageCommandService - .saveImages(ImageType.EVENT, event.getId(), images) - .stream() - .sorted(comparing(Image::getOrder)) - .map(Image::getName) - .collect(toList()); - final String thumbnailImageUrl = extractThumbnailImage(imageUrls); - final List informationImageUrls = extractInformationImages(imageUrls); - return EventDetailResponse.from(updatedEvent, thumbnailImageUrl, informationImageUrls); - } - - public void deleteEvent(final Long eventId) { - if (!eventRepository.existsById(eventId)) { - throw new EventException(NOT_FOUND_EVENT); - } - imageCommandService.deleteImages(ImageType.EVENT, eventId); - eventRepository.deleteById(eventId); - } - - private List findAllPersistTagsOrElseThrow(final List tags) { - if (tags == null || tags.isEmpty()) { - return new ArrayList<>(); + Map imagesPerEventId = new HashMap<>(); + for (Long eventId : eventIds) { + final AllImagesOfContent images = new AllImagesOfContent( + imageRepository.findAllByTypeAndContentId(ImageType.EVENT, eventId)); + imagesPerEventId.put(eventId, images); } - - return tags.stream() - .map(tag -> tagRepository.findByName(tag.getName()) - .orElseThrow(() -> new EventException(EventExceptionType.NOT_FOUND_TAG))) - .collect(toList()); + return imagesPerEventId; } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventDetailResponse.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventDetailResponse.java index 00dee8583..18a339f56 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventDetailResponse.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventDetailResponse.java @@ -3,11 +3,16 @@ import static java.util.stream.Collectors.toUnmodifiableList; import com.emmsale.event.domain.Event; +import com.emmsale.event.domain.EventStatus; import com.emmsale.event.domain.EventTag; +import com.emmsale.image.domain.AllImagesOfContent; import com.emmsale.tag.domain.Tag; import com.fasterxml.jackson.annotation.JsonFormat; import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -65,4 +70,28 @@ public static EventDetailResponse from( event.getEventMode().getValue() ); } + + public static List makeEventResponsesByStatus(final List events, + final Map imagesPerEventId) { + return events.stream() + .map(event -> { + final AllImagesOfContent allImageUrls = imagesPerEventId.get(event.getId()); + final String thumbnailImageUrl = allImageUrls.extractThumbnailImage(); + final List informationImageUrls = allImageUrls.extractInformationImages(); + return EventDetailResponse.from(event, thumbnailImageUrl, informationImageUrls); + }) + .collect(Collectors.toList()); + } + + public static List mergeEventResponses( + final Map> groupByEventStatus, + final Map imagesPerEventId + ) { + return groupByEventStatus.values().stream() + .map(events -> makeEventResponsesByStatus(events, imagesPerEventId)) + .reduce(new ArrayList<>(), (combinedEvents, eventsToAdd) -> { + combinedEvents.addAll(eventsToAdd); + return combinedEvents; + }); + } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/domain/EventStatus.java b/backend/emm-sale/src/main/java/com/emmsale/event/domain/EventStatus.java index aa5539cab..8f7fdba88 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/domain/EventStatus.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/domain/EventStatus.java @@ -8,4 +8,5 @@ public enum EventStatus { IN_PROGRESS, UPCOMING, ENDED; + } diff --git a/backend/emm-sale/src/main/java/com/emmsale/image/domain/AllImagesOfContent.java b/backend/emm-sale/src/main/java/com/emmsale/image/domain/AllImagesOfContent.java new file mode 100644 index 000000000..8cfe13728 --- /dev/null +++ b/backend/emm-sale/src/main/java/com/emmsale/image/domain/AllImagesOfContent.java @@ -0,0 +1,34 @@ +package com.emmsale.image.domain; + +import static java.util.Comparator.comparing; + +import java.util.List; +import java.util.stream.Collectors; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class AllImagesOfContent { + + private final List images; + + public String extractThumbnailImage() { + if (images.isEmpty()) { + return null; + } + return images.stream() + .filter(Image::isThumbnail) + .findFirst() + .orElseThrow() + .getName(); + } + + public List extractInformationImages() { + return images.stream() + .sorted(comparing(Image::getOrder)) + .filter(Image::isNotThumbnail) + .map(Image::getName) + .collect(Collectors.toList()); + } +} diff --git a/backend/emm-sale/src/main/java/com/emmsale/image/domain/Image.java b/backend/emm-sale/src/main/java/com/emmsale/image/domain/Image.java index aa59874b4..b760efe65 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/image/domain/Image.java +++ b/backend/emm-sale/src/main/java/com/emmsale/image/domain/Image.java @@ -16,26 +16,26 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter public class Image { - + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - + @Column(nullable = false) private String name; - + @Enumerated(EnumType.STRING) @Column(nullable = false) private ImageType type; - + @Column(nullable = false) private Long contentId; - + @Column(name = "order_number", nullable = false) private int order; - + private LocalDateTime createdAt; - + public Image(final String name, final ImageType type, final Long contentId, final int order, final LocalDateTime createdAt) { this.name = name; @@ -44,4 +44,12 @@ public Image(final String name, final ImageType type, final Long contentId, fina this.order = order; this.createdAt = createdAt; } + + public boolean isThumbnail() { + return order == 0; + } + + public boolean isNotThumbnail() { + return order != 0; + } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/scrap/application/ScrapQueryService.java b/backend/emm-sale/src/main/java/com/emmsale/scrap/application/ScrapQueryService.java index 8e5455d9d..41a38b6d9 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/scrap/application/ScrapQueryService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/scrap/application/ScrapQueryService.java @@ -1,21 +1,17 @@ package com.emmsale.scrap.application; -import static java.util.Comparator.comparing; import static java.util.stream.Collectors.groupingBy; -import static java.util.stream.Collectors.toList; import com.emmsale.event.application.dto.EventDetailResponse; import com.emmsale.event.domain.Event; import com.emmsale.event.domain.EventStatus; -import com.emmsale.image.domain.Image; +import com.emmsale.image.domain.AllImagesOfContent; import com.emmsale.image.domain.ImageType; import com.emmsale.image.domain.repository.ImageRepository; import com.emmsale.member.domain.Member; import com.emmsale.scrap.domain.Scrap; import com.emmsale.scrap.domain.ScrapRepository; import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -43,63 +39,22 @@ public List findAllScraps(final Member member) { .collect( groupingBy(event -> event.getEventPeriod().calculateEventStatus(LocalDateTime.now()))); - return mergeEventResponses(eventGroupByStatus, makeImageUrlsPerEventId(scrappedEvents)); + return EventDetailResponse.mergeEventResponses(eventGroupByStatus, + makeImageUrlsPerEventId(scrappedEvents)); } - private Map> makeImageUrlsPerEventId(final List events) { + private Map makeImageUrlsPerEventId(final List events) { final List eventIds = events.stream() .map(Event::getId) .collect(Collectors.toList()); - Map> imageUrlsPerEventId = new HashMap<>(); + Map imageUrlsPerEventId = new HashMap<>(); for (Long eventId : eventIds) { - final List images = imageRepository.findAllByTypeAndContentId(ImageType.EVENT, - eventId) - .stream() - .sorted(comparing(Image::getOrder)) - .map(Image::getName) - .collect(toList()); + final AllImagesOfContent images = new AllImagesOfContent( + imageRepository.findAllByTypeAndContentId(ImageType.EVENT, eventId)); imageUrlsPerEventId.put(eventId, images); } return imageUrlsPerEventId; } - private List makeEventResponsesByStatus(final List events, - final Map> imageUrlsPerEventId) { - return events.stream() - .map(event -> { - final List allImageUrls = imageUrlsPerEventId.get(event.getId()); - final String thumbnailImageUrl = extractThumbnailImage(allImageUrls); - final List informationImageUrls = extractInformationImages(allImageUrls); - return EventDetailResponse.from(event, thumbnailImageUrl, informationImageUrls); - }) - .collect(Collectors.toList()); - } - - private List mergeEventResponses( - final Map> groupByEventStatus, - final Map> imageUrlsPerEventId - ) { - return groupByEventStatus.values().stream() - .map(events -> makeEventResponsesByStatus(events, imageUrlsPerEventId)) - .reduce(new ArrayList<>(), (combinedEvents, eventsToAdd) -> { - combinedEvents.addAll(eventsToAdd); - return combinedEvents; - }); - } - - private String extractThumbnailImage(final List imageUrls) { - if (imageUrls.isEmpty()) { - return null; - } - return imageUrls.get(0); - } - - private List extractInformationImages(final List imageUrls) { - if (imageUrls.size() <= 1) { - return Collections.emptyList(); - } - return imageUrls.subList(1, imageUrls.size()); - } - } diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventCommandServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventCommandServiceTest.java new file mode 100644 index 000000000..a790354d0 --- /dev/null +++ b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventCommandServiceTest.java @@ -0,0 +1,467 @@ +package com.emmsale.event.application; + +import static com.emmsale.event.EventFixture.AI_아이디어_공모전; +import static com.emmsale.event.EventFixture.AI_컨퍼런스; +import static com.emmsale.event.EventFixture.구름톤; +import static com.emmsale.event.EventFixture.모바일_컨퍼런스; +import static com.emmsale.event.EventFixture.안드로이드_컨퍼런스; +import static com.emmsale.event.EventFixture.웹_컨퍼런스; +import static com.emmsale.event.EventFixture.인프콘_2023; +import static com.emmsale.event.exception.EventExceptionType.NOT_FOUND_EVENT; +import static com.emmsale.event.exception.EventExceptionType.NOT_FOUND_TAG; +import static com.emmsale.event.exception.EventExceptionType.START_DATE_TIME_AFTER_END_DATE_TIME; +import static com.emmsale.image.ImageFixture.행사_이미지1; +import static com.emmsale.image.ImageFixture.행사_이미지2; +import static com.emmsale.image.ImageFixture.행사_이미지3; +import static com.emmsale.tag.TagFixture.AI; +import static com.emmsale.tag.TagFixture.IOS; +import static com.emmsale.tag.TagFixture.백엔드; +import static com.emmsale.tag.TagFixture.안드로이드; +import static com.emmsale.tag.TagFixture.프론트엔드; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.doNothing; + +import com.emmsale.event.application.dto.EventDetailRequest; +import com.emmsale.event.application.dto.EventDetailResponse; +import com.emmsale.event.domain.Event; +import com.emmsale.event.domain.EventMode; +import com.emmsale.event.domain.EventTag; +import com.emmsale.event.domain.EventType; +import com.emmsale.event.domain.PaymentType; +import com.emmsale.event.domain.repository.EventRepository; +import com.emmsale.event.domain.repository.EventTagRepository; +import com.emmsale.event.exception.EventException; +import com.emmsale.helper.ServiceIntegrationTestHelper; +import com.emmsale.image.domain.repository.ImageRepository; +import com.emmsale.notification.domain.Notification; +import com.emmsale.tag.application.dto.TagRequest; +import com.emmsale.tag.domain.Tag; +import com.emmsale.tag.domain.TagRepository; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.web.multipart.MultipartFile; + +class EventCommandServiceTest extends ServiceIntegrationTestHelper { + + private static final EventDetailResponse 인프콘_2023 = new EventDetailResponse(null, "인프콘 2023", + null, null, null, null, null, "코엑스", List.of("백엔드"), + "이미지1", EventType.CONFERENCE.name(), List.of(), "인프런", PaymentType.PAID.getValue(), + EventMode.OFFLINE.getValue()); + private static final EventDetailResponse 웹_컨퍼런스 = new EventDetailResponse(null, "웹 컨퍼런스", null, + null, null, + null, null, "코엑스", List.of("백엔드"), "이미지1", EventType.CONFERENCE.name(), + List.of(), "주최기관", PaymentType.PAID.getValue(), EventMode.ONLINE.getValue()); + private static final EventDetailResponse 안드로이드_컨퍼런스 = new EventDetailResponse(null, "안드로이드 컨퍼런스", + null, null, null, null, null, "코엑스", List.of("백엔드"), + "이미지1", EventType.CONFERENCE.name(), List.of(), "주최기관", PaymentType.PAID.getValue(), + EventMode.ONLINE.getValue()); + private static final EventDetailResponse AI_컨퍼런스 = new EventDetailResponse(null, "AI 컨퍼런스", + null, null, null, null, null, "코엑스", List.of("백엔드"), + "이미지1", EventType.CONFERENCE.name(), List.of(), "주최기관", PaymentType.PAID.getValue(), + EventMode.ONLINE.getValue()); + private static final EventDetailResponse 모바일_컨퍼런스 = new EventDetailResponse(null, "모바일 컨퍼런스", + null, null, null, null, null, "코엑스", List.of("백엔드"), + "이미지1", EventType.CONFERENCE.name(), List.of(), "주최기관", PaymentType.PAID.getValue(), + EventMode.ONLINE.getValue()); + private static final EventDetailResponse AI_아이디어_공모전 = new EventDetailResponse(null, + "AI 아이디어 공모전", null, null, null, null, null, "코엑스", + List.of("백엔드"), "이미지1", EventType.CONFERENCE.name(), List.of(), "주최기관", + PaymentType.PAID.getValue(), EventMode.ONLINE.getValue()); + private static final EventDetailResponse 구름톤 = new EventDetailResponse(null, "구름톤", null, + null, null, null, null, "코엑스", List.of("백엔드"), + "이미지1", EventType.COMPETITION.name(), List.of(), "주최기관", PaymentType.PAID.getValue(), + EventMode.ONLINE.getValue()); + + @Autowired + private EventCommandService eventCommandService; + @Autowired + private EventRepository eventRepository; + @Autowired + private EventTagRepository eventTagRepository; + @Autowired + private TagRepository tagRepository; + @Autowired + private ImageRepository imageRepository; + + private List mockMultipartFiles; + + @BeforeEach + void init() { + final Tag 백엔드 = tagRepository.save(백엔드()); + final Tag 프론트엔드 = tagRepository.save(프론트엔드()); + final Tag 안드로이드 = tagRepository.save(안드로이드()); + final Tag IOS = tagRepository.save(IOS()); + final Tag AI = tagRepository.save(AI()); + + final Event 인프콘_2023 = eventRepository.save(인프콘_2023()); + final Event AI_컨퍼런스 = eventRepository.save(AI_컨퍼런스()); + final Event 모바일_컨퍼런스 = eventRepository.save(모바일_컨퍼런스()); + final Event 안드로이드_컨퍼런스 = eventRepository.save(안드로이드_컨퍼런스()); + final Event 웹_컨퍼런스 = eventRepository.save(웹_컨퍼런스()); + final Event AI_아이디어_공모전 = eventRepository.save(AI_아이디어_공모전()); + final Event 구름톤 = eventRepository.save(구름톤()); + + eventTagRepository.saveAll(List.of( + new EventTag(인프콘_2023, 백엔드), new EventTag(인프콘_2023, 프론트엔드), new EventTag(인프콘_2023, 안드로이드), + new EventTag(인프콘_2023, IOS), new EventTag(인프콘_2023, AI), new EventTag(AI_컨퍼런스, AI), + new EventTag(모바일_컨퍼런스, 안드로이드), new EventTag(모바일_컨퍼런스, IOS), new EventTag(안드로이드_컨퍼런스, 안드로이드), + new EventTag(웹_컨퍼런스, 백엔드), new EventTag(웹_컨퍼런스, 프론트엔드)) + ); + + mockMultipartFiles = List.of( + new MockMultipartFile( + "picture", + "picture.jpg", + MediaType.TEXT_PLAIN_VALUE, + "test data".getBytes() + ) + ); + imageRepository.saveAll(List.of( + 행사_이미지1(인프콘_2023.getId()), + 행사_이미지2(인프콘_2023.getId()), + 행사_이미지3(인프콘_2023.getId()) + )); + imageRepository.saveAll(List.of( + 행사_이미지1(웹_컨퍼런스.getId()), + 행사_이미지2(웹_컨퍼런스.getId()) + )); + imageRepository.saveAll(List.of( + 행사_이미지1(안드로이드_컨퍼런스.getId()) + )); + } + + @Nested + class AddEvent { + + final List tagRequests = List.of( + new TagRequest(IOS().getName()), + new TagRequest(AI().getName()) + ); + private final LocalDateTime beforeDateTime = LocalDateTime.now(); + private final LocalDateTime afterDateTime = beforeDateTime.plusDays(1); + private final String eventName = "새로운 이름"; + private final String eventLocation = "새로운 장소"; + private final String eventInformationUrl = "https://새로운-상세-URL.com"; + private final String imageUrl = "https://image.com"; + private final PaymentType paymentType = PaymentType.FREE_PAID; + private final EventMode eventMode = EventMode.ON_OFFLINE; + private final EventType type = EventType.CONFERENCE; + private final LocalDate now = LocalDate.now(); + private final String organization = "행사기관"; + + @Test + @DisplayName("이벤트를 성공적으로 저장한다.") + void addEventTest() { + //given + final EventDetailRequest request = new EventDetailRequest( + eventName, + eventLocation, + eventInformationUrl, + beforeDateTime, + afterDateTime, + beforeDateTime, + afterDateTime, + tagRequests, + type, + eventMode, + paymentType, + organization + ); + + doNothing().when(firebaseCloudMessageClient) + .sendMessageTo(any(Notification.class), anyLong()); + + //when + final EventDetailResponse response = eventCommandService.addEvent(request, + mockMultipartFiles); + final Event savedEvent = eventRepository.findById(response.getId()).get(); + + //then + assertAll( + () -> assertEquals(eventName, savedEvent.getName()), + () -> assertEquals(eventLocation, savedEvent.getLocation()), + () -> assertEquals(eventInformationUrl, savedEvent.getInformationUrl()), + () -> assertEquals(beforeDateTime, savedEvent.getEventPeriod().getStartDate()), + () -> assertEquals(afterDateTime, savedEvent.getEventPeriod().getEndDate()) + ); + assertThat(response.getTags()) + .containsAll( + tagRequests.stream() + .map(TagRequest::getName) + .collect(Collectors.toList()) + ); + } + + @Test + @DisplayName("행사 시작 일시가 행사 종료 일시 이후일 경우 EventException이 발생한다.") + void addEventWithStartDateTimeAfterBeforeDateTimeTest() { + //given + final LocalDateTime startDateTime = afterDateTime; + final LocalDateTime endDatetime = beforeDateTime; + + final EventDetailRequest request = new EventDetailRequest( + eventName, + eventLocation, + eventInformationUrl, + startDateTime, + endDatetime, + beforeDateTime, + afterDateTime, + tagRequests, + type, + eventMode, + paymentType, + organization + ); + + doNothing().when(firebaseCloudMessageClient) + .sendMessageTo(any(Notification.class), anyLong()); + + //when & then + final EventException exception = assertThrowsExactly(EventException.class, + () -> eventCommandService.addEvent(request, mockMultipartFiles)); + + assertEquals(START_DATE_TIME_AFTER_END_DATE_TIME, exception.exceptionType()); + } + + @Test + @DisplayName("Tag가 존재하지 않을 경우 EventException이 발생한다.") + void addEventWithNotExistTagTest() { + //given + final List tagRequests = List.of( + new TagRequest(백엔드().getName()), + new TagRequest(안드로이드().getName()), + new TagRequest("존재하지 않는 태그") + ); + + final EventDetailRequest request = new EventDetailRequest( + eventName, + eventLocation, + eventInformationUrl, + beforeDateTime, + afterDateTime, + beforeDateTime, + afterDateTime, + tagRequests, + type, + eventMode, + paymentType, + organization + ); + + doNothing().when(firebaseCloudMessageClient) + .sendMessageTo(any(Notification.class), anyLong()); + + //when & then + final EventException exception = assertThrowsExactly(EventException.class, + () -> eventCommandService.addEvent(request, mockMultipartFiles)); + + assertEquals(NOT_FOUND_TAG, exception.exceptionType()); + } + } + + @Nested + class UpdateEvent { + + final List newTagRequests = List.of( + new TagRequest(IOS().getName()), + new TagRequest(AI().getName()) + ); + private final LocalDateTime beforeDateTime = LocalDateTime.now(); + private final LocalDateTime afterDateTime = beforeDateTime.plusDays(1); + private final String newName = "새로운 이름"; + private final String newLocation = "새로운 장소"; + private final String newInformationUrl = "https://새로운-상세-URL.com"; + private final LocalDate now = LocalDate.now(); + private final PaymentType paymentType = PaymentType.FREE_PAID; + private final EventMode eventMode = EventMode.ON_OFFLINE; + private final String organization = "행사기관"; + + @Test + @DisplayName("이벤트를 성공적으로 업데이트한다.") + void updateEventTest() { + //given + final LocalDateTime newStartDateTime = beforeDateTime; + final LocalDateTime newEndDateTime = afterDateTime; + + final EventDetailRequest updateRequest = new EventDetailRequest( + newName, + newLocation, + newInformationUrl, + beforeDateTime, + afterDateTime, + beforeDateTime, + afterDateTime, + newTagRequests, + EventType.CONFERENCE, + eventMode, + paymentType, + organization + ); + + final Event event = eventRepository.save(인프콘_2023()); + final Long eventId = event.getId(); + + //when + final EventDetailResponse response = eventCommandService.updateEvent(eventId, updateRequest, + mockMultipartFiles); + final Event updatedEvent = eventRepository.findById(eventId).get(); + + //then + assertAll( + () -> assertEquals(newName, updatedEvent.getName()), + () -> assertEquals(newLocation, updatedEvent.getLocation()), + () -> assertEquals(newStartDateTime, updatedEvent.getEventPeriod().getStartDate()), + () -> assertEquals(newEndDateTime, updatedEvent.getEventPeriod().getEndDate()), + () -> assertEquals(newInformationUrl, updatedEvent.getInformationUrl()) + ); + assertThat(response.getTags()) + .containsAll( + newTagRequests.stream() + .map(TagRequest::getName) + .collect(Collectors.toList()) + ); + } + + @Test + @DisplayName("업데이트할 이벤트가 존재하지 않을 경우 EventException이 발생한다.") + void updateEventWithNotExistsEventTest() { + //given + final long notExistsEventId = 0L; + + final EventDetailRequest updateRequest = new EventDetailRequest( + newName, + newLocation, + newInformationUrl, + beforeDateTime, + afterDateTime, + beforeDateTime, + afterDateTime, + newTagRequests, + EventType.CONFERENCE, + eventMode, + paymentType, + organization + ); + + //when & then + final EventException exception = assertThrowsExactly(EventException.class, + () -> eventCommandService.updateEvent(notExistsEventId, updateRequest, + mockMultipartFiles)); + + assertEquals(NOT_FOUND_EVENT, exception.exceptionType()); + } + + @Test + @DisplayName("행사 시작 일시가 행사 종료 일시 이후일 경우 EventException이 발생한다.") + void updateEventWithStartDateTimeAfterBeforeDateTimeTest() { + //given + final LocalDateTime newStartDateTime = afterDateTime; + final LocalDateTime newEndDateTime = beforeDateTime; + + final EventDetailRequest updateRequest = new EventDetailRequest( + newName, + newLocation, + newInformationUrl, + newStartDateTime, + newEndDateTime, + beforeDateTime, + afterDateTime, + newTagRequests, + EventType.CONFERENCE, + eventMode, + paymentType, + organization + ); + + final Event event = eventRepository.save(인프콘_2023()); + final Long eventId = event.getId(); + + //when & then + final EventException exception = assertThrowsExactly(EventException.class, + () -> eventCommandService.updateEvent(eventId, updateRequest, mockMultipartFiles)); + + assertEquals(START_DATE_TIME_AFTER_END_DATE_TIME, exception.exceptionType()); + } + + @Test + @DisplayName("Tag가 존재하지 않을 경우 EventException이 발생한다.") + void updateEventWithNotExistTagTest() { + //given + final List newTagRequests = List.of( + new TagRequest("존재하지 않는 태그") + ); + + final EventDetailRequest updateRequest = new EventDetailRequest( + newName, + newLocation, + newInformationUrl, + beforeDateTime, + afterDateTime, + beforeDateTime, + afterDateTime, + newTagRequests, + EventType.CONFERENCE, + eventMode, + paymentType, + organization + ); + + final Event event = eventRepository.save(인프콘_2023()); + final Long eventId = event.getId(); + + //when & then + final EventException exception = assertThrowsExactly(EventException.class, + () -> eventCommandService.updateEvent(eventId, updateRequest, mockMultipartFiles)); + + assertEquals(NOT_FOUND_TAG, exception.exceptionType()); + } + } + + @Nested + class DeleteEvent { + + @Test + @DisplayName("이벤트를 성공적으로 삭제한다.") + void deleteEventTest() { + //given + final Event event = eventRepository.save(인프콘_2023()); + final Long eventId = event.getId(); + + //when + eventCommandService.deleteEvent(eventId); + + //then + assertFalse(eventRepository.findById(eventId).isPresent()); + } + + @Test + @DisplayName("삭제할 이벤트가 존재하지 않을 경우 EventException이 발생한다.") + void deleteEventWithNotExistsEventTest() { + //given + final long notExistsEventId = 0L; + + //when & then + final EventException exception = assertThrowsExactly(EventException.class, + () -> eventCommandService.deleteEvent(notExistsEventId)); + + assertEquals(NOT_FOUND_EVENT, exception.exceptionType()); + } + } + +} \ No newline at end of file diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventQueryServiceTest.java similarity index 62% rename from backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java rename to backend/emm-sale/src/test/java/com/emmsale/event/application/EventQueryServiceTest.java index 7499cfd69..1289354f7 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventQueryServiceTest.java @@ -12,9 +12,7 @@ import static com.emmsale.event.domain.EventStatus.IN_PROGRESS; import static com.emmsale.event.exception.EventExceptionType.INVALID_DATE_FORMAT; import static com.emmsale.event.exception.EventExceptionType.NOT_FOUND_EVENT; -import static com.emmsale.event.exception.EventExceptionType.NOT_FOUND_TAG; import static com.emmsale.event.exception.EventExceptionType.START_DATE_AFTER_END_DATE; -import static com.emmsale.event.exception.EventExceptionType.START_DATE_TIME_AFTER_END_DATE_TIME; import static com.emmsale.image.ImageFixture.행사_이미지1; import static com.emmsale.image.ImageFixture.행사_이미지2; import static com.emmsale.image.ImageFixture.행사_이미지3; @@ -25,15 +23,7 @@ import static com.emmsale.tag.TagFixture.프론트엔드; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrowsExactly; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.Mockito.doNothing; - -import com.emmsale.event.application.dto.EventDetailRequest; + import com.emmsale.event.application.dto.EventDetailResponse; import com.emmsale.event.domain.Event; import com.emmsale.event.domain.EventMode; @@ -48,8 +38,6 @@ import com.emmsale.image.domain.Image; import com.emmsale.image.domain.ImageType; import com.emmsale.image.domain.repository.ImageRepository; -import com.emmsale.notification.domain.Notification; -import com.emmsale.tag.application.dto.TagRequest; import com.emmsale.tag.domain.Tag; import com.emmsale.tag.domain.TagRepository; import com.emmsale.tag.exception.TagException; @@ -58,7 +46,6 @@ import java.time.LocalDateTime; import java.util.Collections; import java.util.List; -import java.util.stream.Collectors; import org.assertj.core.api.ThrowableAssert.ThrowingCallable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -67,11 +54,8 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.MediaType; -import org.springframework.mock.web.MockMultipartFile; -import org.springframework.web.multipart.MultipartFile; -class EventServiceTest extends ServiceIntegrationTestHelper { +class EventQueryServiceTest extends ServiceIntegrationTestHelper { private static final EventDetailResponse 인프콘_2023 = new EventDetailResponse(null, "인프콘 2023", null, null, null, null, null, "코엑스", List.of("백엔드"), @@ -104,7 +88,7 @@ class EventServiceTest extends ServiceIntegrationTestHelper { private static final LocalDateTime TODAY = LocalDateTime.of(2023, 7, 21, 0, 0); @Autowired - private EventService eventService; + private EventQueryService eventQueryService; @Autowired private EventRepository eventRepository; @Autowired @@ -114,8 +98,6 @@ class EventServiceTest extends ServiceIntegrationTestHelper { @Autowired private ImageRepository imageRepository; - private List mockMultipartFiles; - @BeforeEach void init() { final Tag 백엔드 = tagRepository.save(백엔드()); @@ -138,15 +120,6 @@ void init() { new EventTag(모바일_컨퍼런스, 안드로이드), new EventTag(모바일_컨퍼런스, IOS), new EventTag(안드로이드_컨퍼런스, 안드로이드), new EventTag(웹_컨퍼런스, 백엔드), new EventTag(웹_컨퍼런스, 프론트엔드)) ); - - mockMultipartFiles = List.of( - new MockMultipartFile( - "picture", - "picture.jpg", - MediaType.TEXT_PLAIN_VALUE, - "test data".getBytes() - ) - ); imageRepository.saveAll(List.of( 행사_이미지1(인프콘_2023.getId()), 행사_이미지2(인프콘_2023.getId()), @@ -187,7 +160,7 @@ void success() { imageUrls); //when - final EventDetailResponse actual = eventService.findEvent(event.getId(), 날짜_8월_10일()); + final EventDetailResponse actual = eventQueryService.findEvent(event.getId(), 날짜_8월_10일()); //then assertThat(actual) @@ -205,7 +178,7 @@ void success_imageUrl_null_imageUrls_empty() { Collections.emptyList()); //when - final EventDetailResponse actual = eventService.findEvent(event.getId(), 날짜_8월_10일()); + final EventDetailResponse actual = eventQueryService.findEvent(event.getId(), 날짜_8월_10일()); //then assertThat(actual) @@ -227,7 +200,7 @@ void success_imageUrls_empty() { Collections.emptyList()); //when - final EventDetailResponse actual = eventService.findEvent(event.getId(), 날짜_8월_10일()); + final EventDetailResponse actual = eventQueryService.findEvent(event.getId(), 날짜_8월_10일()); //then assertThat(actual) @@ -243,7 +216,7 @@ void fail_EventNotFoundException() { final LocalDate today = 날짜_8월_10일(); //when, then assertThatThrownBy(() -> - eventService.findEvent(notFoundEventId, today)) + eventQueryService.findEvent(notFoundEventId, today)) .isInstanceOf(EventException.class) .hasMessage(NOT_FOUND_EVENT.errorMessage()); } @@ -262,7 +235,7 @@ void findEvents_all() { 안드로이드_컨퍼런스, AI_아이디어_공모전); // when - final List actualEvents = eventService.findEvents(null, TODAY, + final List actualEvents = eventQueryService.findEvents(null, TODAY, null, null, null, null, null); // then @@ -280,7 +253,8 @@ void findEvents_CONFERENCE() { 안드로이드_컨퍼런스); // when - final List actualEvents = eventService.findEvents(EventType.CONFERENCE, + final List actualEvents = eventQueryService.findEvents( + EventType.CONFERENCE, TODAY, null, null, null, null, null); @@ -298,7 +272,8 @@ void findEvents_COMPETITION() { final List expectedEvents = List.of(구름톤, AI_아이디어_공모전); // when - final List actualEvents = eventService.findEvents(EventType.COMPETITION, + final List actualEvents = eventQueryService.findEvents( + EventType.COMPETITION, TODAY, null, null, null, null, null); @@ -317,7 +292,8 @@ void findEvents_2023_7() { 안드로이드_컨퍼런스); // when - final List actualEvents = eventService.findEvents(EventType.CONFERENCE, + final List actualEvents = eventQueryService.findEvents( + EventType.CONFERENCE, TODAY, "2023-07-01", "2023-07-31", null, null, null); @@ -335,7 +311,8 @@ void findEvents_2023_8() { final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, 모바일_컨퍼런스); // when - final List actualEvents = eventService.findEvents(EventType.CONFERENCE, + final List actualEvents = eventQueryService.findEvents( + EventType.CONFERENCE, TODAY, "2023-08-01", "2023-08-31", null, null, null); @@ -353,7 +330,8 @@ void findEvents_2023_6() { final List expectedEvents = List.of(인프콘_2023, 안드로이드_컨퍼런스); // when - final List actualEvents = eventService.findEvents(EventType.CONFERENCE, + final List actualEvents = eventQueryService.findEvents( + EventType.CONFERENCE, TODAY, "2023-06-01", "2023-06-30", null, null, null); @@ -371,7 +349,8 @@ void findEvents_after_2023_7_17() { final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, AI_컨퍼런스, 모바일_컨퍼런스); // when - final List actualEvents = eventService.findEvents(EventType.CONFERENCE, + final List actualEvents = eventQueryService.findEvents( + EventType.CONFERENCE, TODAY, "2023-07-17", null, null, null, null); @@ -390,7 +369,8 @@ void findEvents_before_2023_7_31() { 안드로이드_컨퍼런스); // when - final List actualEvents = eventService.findEvents(EventType.CONFERENCE, + final List actualEvents = eventQueryService.findEvents( + EventType.CONFERENCE, TODAY, null, "2023-07-31", null, null, null); @@ -405,7 +385,8 @@ void findEvents_before_2023_7_31() { @DisplayName("아무 행사도 없는 2023년 12월 행사를 조회하면, 빈 목록을 반환한다.") void findEvents_2023_12() { // given, when - final List actualEvents = eventService.findEvents(EventType.CONFERENCE, + final List actualEvents = eventQueryService.findEvents( + EventType.CONFERENCE, TODAY, "2023-12-01", "2023-12-31", null, null, null); @@ -418,7 +399,8 @@ void findEvents_2023_12() { @DisplayName("유효하지 않은 값이 시작일 정보로 들어오면 예외를 반환한다.") void findEvents_start_date_fail(final String startDate) { // given, when - final ThrowingCallable actual = () -> eventService.findEvents(EventType.CONFERENCE, TODAY, + final ThrowingCallable actual = () -> eventQueryService.findEvents(EventType.CONFERENCE, + TODAY, startDate, "2023-07-31", null, null, null); // then @@ -432,7 +414,8 @@ void findEvents_start_date_fail(final String startDate) { @DisplayName("유효하지 않은 값이 종료일 값으로 들어오면 예외를 반환한다.") void findEvents_end_date_fail(final String endDate) { // given, when - final ThrowingCallable actual = () -> eventService.findEvents(EventType.CONFERENCE, TODAY, + final ThrowingCallable actual = () -> eventQueryService.findEvents(EventType.CONFERENCE, + TODAY, "2023-07-01", endDate, null, null, null); // then @@ -445,7 +428,8 @@ void findEvents_end_date_fail(final String endDate) { @DisplayName("시작일이 종료일보다 뒤에 있으면 예외를 반환한다.") void findEvents_start_after_end_fail() { // given, when - final ThrowingCallable actual = () -> eventService.findEvents(EventType.CONFERENCE, TODAY, + final ThrowingCallable actual = () -> eventQueryService.findEvents(EventType.CONFERENCE, + TODAY, "2023-07-16", "2023-07-15", null, null, null); // then @@ -461,7 +445,8 @@ void findEvents_tag_filter() { final List expectedEvents = List.of(인프콘_2023, 모바일_컨퍼런스, 안드로이드_컨퍼런스); // when - final List actualEvents = eventService.findEvents(EventType.CONFERENCE, + final List actualEvents = eventQueryService.findEvents( + EventType.CONFERENCE, TODAY, null, null, List.of("안드로이드"), null, null); @@ -480,7 +465,8 @@ void findEvents_tags_filter() { 안드로이드_컨퍼런스); // when - final List actualEvents = eventService.findEvents(EventType.CONFERENCE, + final List actualEvents = eventQueryService.findEvents( + EventType.CONFERENCE, TODAY, null, null, List.of("안드로이드", "백엔드"), null, null); @@ -495,7 +481,8 @@ void findEvents_tags_filter() { @DisplayName("존재하지 않는 태그가 입력으로 들어오면 예외를 반환한다.") void findEvents_tag_filter_fail() { // given, when - final ThrowingCallable actual = () -> eventService.findEvents(EventType.CONFERENCE, TODAY, + final ThrowingCallable actual = () -> eventQueryService.findEvents(EventType.CONFERENCE, + TODAY, null, null, List.of("개발"), null, null); // then @@ -511,7 +498,8 @@ void findEvents_status_filter() { final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스); // when - final List actualEvents = eventService.findEvents(EventType.CONFERENCE, + final List actualEvents = eventQueryService.findEvents( + EventType.CONFERENCE, TODAY, null, null, null, List.of(IN_PROGRESS), null); @@ -529,7 +517,8 @@ void findEvents_statuses_filter() { final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, AI_컨퍼런스, 모바일_컨퍼런스); // when - final List actualEvents = eventService.findEvents(EventType.CONFERENCE, + final List actualEvents = eventQueryService.findEvents( + EventType.CONFERENCE, TODAY, null, null, null, List.of(EventStatus.UPCOMING, IN_PROGRESS), null); @@ -547,7 +536,8 @@ void findEvents_period_tags_filter() { final List expectedEvents = List.of(모바일_컨퍼런스); // when - final List actualEvents = eventService.findEvents(EventType.CONFERENCE, + final List actualEvents = eventQueryService.findEvents( + EventType.CONFERENCE, TODAY, "2023-09-01", "2023-09-30", List.of("안드로이드", "백엔드"), List.of(EventStatus.UPCOMING), null); @@ -572,7 +562,7 @@ void findEvents_blank_search(final String keyword) { 안드로이드_컨퍼런스, AI_아이디어_공모전); // when - final List actualEvents = eventService.findEvents(null, + final List actualEvents = eventQueryService.findEvents(null, TODAY, null, null, null, null, keyword); @@ -592,7 +582,7 @@ void findEvents_search() { 안드로이드_컨퍼런스); // when - final List actualEvents = eventService.findEvents(null, + final List actualEvents = eventQueryService.findEvents(null, TODAY, null, null, null, null, keyword); // then @@ -610,7 +600,7 @@ void findEvents_multiple_tokens_search() { final List expectedEvents = List.of(모바일_컨퍼런스); // when - final List actualEvents = eventService.findEvents(null, + final List actualEvents = eventQueryService.findEvents(null, TODAY, null, null, null, null, keyword); // then @@ -628,7 +618,7 @@ void findEvents_status_filter_and_search() { final List expectedEvents = List.of(인프콘_2023); // when - final List actualEvents = eventService.findEvents(null, + final List actualEvents = eventQueryService.findEvents(null, TODAY, null, null, null, List.of(IN_PROGRESS), keyword); @@ -640,321 +630,4 @@ void findEvents_status_filter_and_search() { } } } - - @Nested - class AddEvent { - - final List tagRequests = List.of( - new TagRequest(IOS().getName()), - new TagRequest(AI().getName()) - ); - private final LocalDateTime beforeDateTime = LocalDateTime.now(); - private final LocalDateTime afterDateTime = beforeDateTime.plusDays(1); - private final String eventName = "새로운 이름"; - private final String eventLocation = "새로운 장소"; - private final String eventInformationUrl = "https://새로운-상세-URL.com"; - private final String imageUrl = "https://image.com"; - private final PaymentType paymentType = PaymentType.FREE_PAID; - private final EventMode eventMode = EventMode.ON_OFFLINE; - private final EventType type = EventType.CONFERENCE; - private final LocalDate now = LocalDate.now(); - private final String organization = "행사기관"; - - @Test - @DisplayName("이벤트를 성공적으로 저장한다.") - void addEventTest() { - //given - final EventDetailRequest request = new EventDetailRequest( - eventName, - eventLocation, - eventInformationUrl, - beforeDateTime, - afterDateTime, - beforeDateTime, - afterDateTime, - tagRequests, - type, - eventMode, - paymentType, - organization - ); - - doNothing().when(firebaseCloudMessageClient) - .sendMessageTo(any(Notification.class), anyLong()); - - //when - final EventDetailResponse response = eventService.addEvent(request, mockMultipartFiles); - final Event savedEvent = eventRepository.findById(response.getId()).get(); - - //then - assertAll( - () -> assertEquals(eventName, savedEvent.getName()), - () -> assertEquals(eventLocation, savedEvent.getLocation()), - () -> assertEquals(eventInformationUrl, savedEvent.getInformationUrl()), - () -> assertEquals(beforeDateTime, savedEvent.getEventPeriod().getStartDate()), - () -> assertEquals(afterDateTime, savedEvent.getEventPeriod().getEndDate()) - ); - assertThat(response.getTags()) - .containsAll( - tagRequests.stream() - .map(TagRequest::getName) - .collect(Collectors.toList()) - ); - } - - @Test - @DisplayName("행사 시작 일시가 행사 종료 일시 이후일 경우 EventException이 발생한다.") - void addEventWithStartDateTimeAfterBeforeDateTimeTest() { - //given - final LocalDateTime startDateTime = afterDateTime; - final LocalDateTime endDatetime = beforeDateTime; - - final EventDetailRequest request = new EventDetailRequest( - eventName, - eventLocation, - eventInformationUrl, - startDateTime, - endDatetime, - beforeDateTime, - afterDateTime, - tagRequests, - type, - eventMode, - paymentType, - organization - ); - - doNothing().when(firebaseCloudMessageClient) - .sendMessageTo(any(Notification.class), anyLong()); - - //when & then - final EventException exception = assertThrowsExactly(EventException.class, - () -> eventService.addEvent(request, mockMultipartFiles)); - - assertEquals(START_DATE_TIME_AFTER_END_DATE_TIME, exception.exceptionType()); - } - - @Test - @DisplayName("Tag가 존재하지 않을 경우 EventException이 발생한다.") - void addEventWithNotExistTagTest() { - //given - final List tagRequests = List.of( - new TagRequest(백엔드().getName()), - new TagRequest(안드로이드().getName()), - new TagRequest("존재하지 않는 태그") - ); - - final EventDetailRequest request = new EventDetailRequest( - eventName, - eventLocation, - eventInformationUrl, - beforeDateTime, - afterDateTime, - beforeDateTime, - afterDateTime, - tagRequests, - type, - eventMode, - paymentType, - organization - ); - - doNothing().when(firebaseCloudMessageClient) - .sendMessageTo(any(Notification.class), anyLong()); - - //when & then - final EventException exception = assertThrowsExactly(EventException.class, - () -> eventService.addEvent(request, mockMultipartFiles)); - - assertEquals(NOT_FOUND_TAG, exception.exceptionType()); - } - } - - @Nested - class UpdateEvent { - - final List newTagRequests = List.of( - new TagRequest(IOS().getName()), - new TagRequest(AI().getName()) - ); - private final LocalDateTime beforeDateTime = LocalDateTime.now(); - private final LocalDateTime afterDateTime = beforeDateTime.plusDays(1); - private final String newName = "새로운 이름"; - private final String newLocation = "새로운 장소"; - private final String newInformationUrl = "https://새로운-상세-URL.com"; - private final LocalDate now = LocalDate.now(); - private final PaymentType paymentType = PaymentType.FREE_PAID; - private final EventMode eventMode = EventMode.ON_OFFLINE; - private final String organization = "행사기관"; - - @Test - @DisplayName("이벤트를 성공적으로 업데이트한다.") - void updateEventTest() { - //given - final LocalDateTime newStartDateTime = beforeDateTime; - final LocalDateTime newEndDateTime = afterDateTime; - - final EventDetailRequest updateRequest = new EventDetailRequest( - newName, - newLocation, - newInformationUrl, - beforeDateTime, - afterDateTime, - beforeDateTime, - afterDateTime, - newTagRequests, - EventType.CONFERENCE, - eventMode, - paymentType, - organization - ); - - final Event event = eventRepository.save(인프콘_2023()); - final Long eventId = event.getId(); - - //when - final EventDetailResponse response = eventService.updateEvent(eventId, updateRequest, - mockMultipartFiles); - final Event updatedEvent = eventRepository.findById(eventId).get(); - - //then - assertAll( - () -> assertEquals(newName, updatedEvent.getName()), - () -> assertEquals(newLocation, updatedEvent.getLocation()), - () -> assertEquals(newStartDateTime, updatedEvent.getEventPeriod().getStartDate()), - () -> assertEquals(newEndDateTime, updatedEvent.getEventPeriod().getEndDate()), - () -> assertEquals(newInformationUrl, updatedEvent.getInformationUrl()) - ); - assertThat(response.getTags()) - .containsAll( - newTagRequests.stream() - .map(TagRequest::getName) - .collect(Collectors.toList()) - ); - } - - @Test - @DisplayName("업데이트할 이벤트가 존재하지 않을 경우 EventException이 발생한다.") - void updateEventWithNotExistsEventTest() { - //given - final long notExistsEventId = 0L; - - final EventDetailRequest updateRequest = new EventDetailRequest( - newName, - newLocation, - newInformationUrl, - beforeDateTime, - afterDateTime, - beforeDateTime, - afterDateTime, - newTagRequests, - EventType.CONFERENCE, - eventMode, - paymentType, - organization - ); - - //when & then - final EventException exception = assertThrowsExactly(EventException.class, - () -> eventService.updateEvent(notExistsEventId, updateRequest, mockMultipartFiles)); - - assertEquals(NOT_FOUND_EVENT, exception.exceptionType()); - } - - @Test - @DisplayName("행사 시작 일시가 행사 종료 일시 이후일 경우 EventException이 발생한다.") - void updateEventWithStartDateTimeAfterBeforeDateTimeTest() { - //given - final LocalDateTime newStartDateTime = afterDateTime; - final LocalDateTime newEndDateTime = beforeDateTime; - - final EventDetailRequest updateRequest = new EventDetailRequest( - newName, - newLocation, - newInformationUrl, - newStartDateTime, - newEndDateTime, - beforeDateTime, - afterDateTime, - newTagRequests, - EventType.CONFERENCE, - eventMode, - paymentType, - organization - ); - - final Event event = eventRepository.save(인프콘_2023()); - final Long eventId = event.getId(); - - //when & then - final EventException exception = assertThrowsExactly(EventException.class, - () -> eventService.updateEvent(eventId, updateRequest, mockMultipartFiles)); - - assertEquals(START_DATE_TIME_AFTER_END_DATE_TIME, exception.exceptionType()); - } - - @Test - @DisplayName("Tag가 존재하지 않을 경우 EventException이 발생한다.") - void updateEventWithNotExistTagTest() { - //given - final List newTagRequests = List.of( - new TagRequest("존재하지 않는 태그") - ); - - final EventDetailRequest updateRequest = new EventDetailRequest( - newName, - newLocation, - newInformationUrl, - beforeDateTime, - afterDateTime, - beforeDateTime, - afterDateTime, - newTagRequests, - EventType.CONFERENCE, - eventMode, - paymentType, - organization - ); - - final Event event = eventRepository.save(인프콘_2023()); - final Long eventId = event.getId(); - - //when & then - final EventException exception = assertThrowsExactly(EventException.class, - () -> eventService.updateEvent(eventId, updateRequest, mockMultipartFiles)); - - assertEquals(NOT_FOUND_TAG, exception.exceptionType()); - } - } - - @Nested - class DeleteEvent { - - @Test - @DisplayName("이벤트를 성공적으로 삭제한다.") - void deleteEventTest() { - //given - final Event event = eventRepository.save(인프콘_2023()); - final Long eventId = event.getId(); - - //when - eventService.deleteEvent(eventId); - - //then - assertFalse(eventRepository.findById(eventId).isPresent()); - } - - @Test - @DisplayName("삭제할 이벤트가 존재하지 않을 경우 EventException이 발생한다.") - void deleteEventWithNotExistsEventTest() { - //given - final long notExistsEventId = 0L; - - //when & then - final EventException exception = assertThrowsExactly(EventException.class, - () -> eventService.deleteEvent(notExistsEventId)); - - assertEquals(NOT_FOUND_EVENT, exception.exceptionType()); - } - } } diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceEventIntegrationTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceEventIntegrationTestQuery.java similarity index 95% rename from backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceEventIntegrationTest.java rename to backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceEventIntegrationTestQuery.java index 4c3a1844a..be4533b48 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceEventIntegrationTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceEventIntegrationTestQuery.java @@ -28,10 +28,10 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -class EventServiceEventIntegrationTest extends ServiceIntegrationTestHelper { +class EventServiceEventIntegrationTestQuery extends ServiceIntegrationTestHelper { @Autowired - private EventService eventService; + private EventCommandService eventCommandService; @Autowired private InterestTagRepository interestTagRepository; @Autowired @@ -77,7 +77,7 @@ void test_publish_event() throws Exception { ); //when - eventService.addEvent(eventDetailRequest, null); + eventCommandService.addEvent(eventDetailRequest, null); //then verify(firebaseCloudMessageClient, times(2)) @@ -121,7 +121,7 @@ void test_publish_event_no_notification_event_has_no_interest_tag() throws Excep ); //when - eventService.addEvent(eventDetailRequest, null); + eventCommandService.addEvent(eventDetailRequest, null); //then verify(firebaseCloudMessageClient, times(0)) @@ -155,7 +155,7 @@ void test_publish_event_no_notification_member_has_no_interest_tag() throws Exce ); //when - eventService.addEvent(eventDetailRequest, null); + eventCommandService.addEvent(eventDetailRequest, null); //then verify(firebaseCloudMessageClient, times(0)) From c33ed381308e8ea35c17371b464bc36a9d39df95 Mon Sep 17 00:00:00 2001 From: amaran-th Date: Mon, 30 Oct 2023 21:02:09 +0900 Subject: [PATCH 04/41] =?UTF-8?q?refactor:=20=ED=95=A8=EA=BB=98=ED=95=B4?= =?UTF-8?q?=EC=9A=94=20=EC=9A=94=EC=B2=AD=20=EC=A1=B0=ED=9A=8C=20API=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - MemberReferenceResponse dto를 포함하도록 구현 #803 --- .../com/emmsale/RecruitmentPostApiTest.java | 24 +++++++++++----- .../dto/RecruitmentPostQueryResponse.java | 13 ++++----- .../dto/MemberReferenceResponse.java | 28 +++++++++++++++++++ 3 files changed, 51 insertions(+), 14 deletions(-) create mode 100644 backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberReferenceResponse.java diff --git a/backend/emm-sale/src/documentTest/java/com/emmsale/RecruitmentPostApiTest.java b/backend/emm-sale/src/documentTest/java/com/emmsale/RecruitmentPostApiTest.java index 276f0035d..d248e3ce3 100644 --- a/backend/emm-sale/src/documentTest/java/com/emmsale/RecruitmentPostApiTest.java +++ b/backend/emm-sale/src/documentTest/java/com/emmsale/RecruitmentPostApiTest.java @@ -20,6 +20,8 @@ import com.emmsale.event.application.dto.RecruitmentPostRequest; import com.emmsale.event.application.dto.RecruitmentPostResponse; import com.emmsale.event.application.dto.RecruitmentPostUpdateRequest; +import com.emmsale.member.application.dto.MemberReferenceResponse; +import com.emmsale.member.domain.Member; import java.time.LocalDate; import java.util.List; import org.junit.jupiter.api.DisplayName; @@ -187,20 +189,28 @@ void isAlreadyRecruitTest() throws Exception { void findRecruitmentPostsByMemberIdTest() throws Exception { //given final Long memberId = 1L; + final Member member = new Member(1L, 3L, "https://github.image", "아마란스", "amaran-th"); final LocalDate postedAt = LocalDate.of(2023, 7, 15); - final List response = List.of( - new RecruitmentPostQueryResponse(1L, memberId, 21L, "인프콘 2023", "함께해요~", postedAt), - new RecruitmentPostQueryResponse(2L, memberId, 43L, "구름톤", "같이 가요~", postedAt) + new RecruitmentPostQueryResponse(1L, "함께해요~", postedAt, + MemberReferenceResponse.from(member), 21L), + new RecruitmentPostQueryResponse(2L, "같이 가요~", postedAt, + MemberReferenceResponse.from(member), 43L) ); final ResponseFieldsSnippet responseFields = responseFields( fieldWithPath("[].postId").type(JsonFieldType.NUMBER).description("함께해요 게시글 식별자"), - fieldWithPath("[].memberId").type(JsonFieldType.NUMBER).description("member의 식별자"), - fieldWithPath("[].eventId").type(JsonFieldType.NUMBER).description("행사의 식별자"), - fieldWithPath("[].eventName").type(JsonFieldType.STRING).description("행사 제목"), fieldWithPath("[].content").type(JsonFieldType.STRING).description("함께해요 게시글 내용"), - fieldWithPath("[].updatedAt").type(JsonFieldType.STRING).description("함께해요 게시글 수정 날짜") + fieldWithPath("[].updatedAt").type(JsonFieldType.STRING).description("함께해요 게시글 수정 날짜"), + fieldWithPath("[].member.id").type(JsonFieldType.NUMBER).description("member의 식별자"), + fieldWithPath("[].member.name").type(JsonFieldType.STRING).description("member의 이름"), + fieldWithPath("[].member.description").type(JsonFieldType.STRING) + .description("member의 한줄 자기소개"), + fieldWithPath("[].member.imageUrl").type(JsonFieldType.STRING) + .description("member의 이미지 url"), + fieldWithPath("[].member.githubUrl").type(JsonFieldType.STRING) + .description("member의 github Url"), + fieldWithPath("[].eventId").type(JsonFieldType.NUMBER).description("행사의 식별자") ); //when diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/RecruitmentPostQueryResponse.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/RecruitmentPostQueryResponse.java index cd174618e..0740f7826 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/RecruitmentPostQueryResponse.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/RecruitmentPostQueryResponse.java @@ -1,6 +1,7 @@ package com.emmsale.event.application.dto; import com.emmsale.event.domain.RecruitmentPost; +import com.emmsale.member.application.dto.MemberReferenceResponse; import com.emmsale.member.domain.Member; import java.time.LocalDate; import lombok.Getter; @@ -14,22 +15,20 @@ public class RecruitmentPostQueryResponse { private static final String DATE_TIME_FORMAT = "yyyy:MM:dd:HH:mm:ss"; private final Long postId; - private final Long memberId; - private final Long eventId; - private final String eventName; private final String content; @DateTimeFormat(pattern = DATE_TIME_FORMAT) private final LocalDate updatedAt; + private final MemberReferenceResponse member; + private final Long eventId; public static RecruitmentPostQueryResponse from(final RecruitmentPost recruitmentPost) { final Member member = recruitmentPost.getMember(); return new RecruitmentPostQueryResponse( recruitmentPost.getId(), - member.getId(), - recruitmentPost.getEvent().getId(), - recruitmentPost.getEvent().getName(), recruitmentPost.getContent(), - recruitmentPost.getUpdatedAt().toLocalDate() + recruitmentPost.getUpdatedAt().toLocalDate(), + MemberReferenceResponse.from(member), + recruitmentPost.getEvent().getId() ); } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberReferenceResponse.java b/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberReferenceResponse.java new file mode 100644 index 000000000..7cc60e958 --- /dev/null +++ b/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberReferenceResponse.java @@ -0,0 +1,28 @@ +package com.emmsale.member.application.dto; + +import com.emmsale.member.domain.Member; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class MemberReferenceResponse { + + private static final String GITHUB_URL_PREFIX = "https://github.com/"; + + private final Long id; + private final String name; + private final String description; + private final String imageUrl; + private final String githubUrl; + + public static MemberReferenceResponse from(Member member) { + return new MemberReferenceResponse( + member.getId(), + member.getName(), + member.getDescription(), + member.getImageUrl(), + GITHUB_URL_PREFIX + member.getGithubUsername() + ); + } +} From 819ed48d3e462078de7a1c8d542823694a314f81 Mon Sep 17 00:00:00 2001 From: hong-sile Date: Tue, 31 Oct 2023 14:11:41 +0900 Subject: [PATCH 05/41] =?UTF-8?q?feat:=20MemberRepository=EC=97=90=20Excep?= =?UTF-8?q?tion=EC=9D=84=20=EB=B0=98=ED=99=98=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=EC=9D=84=20=EC=95=88=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #811 --- .../java/com/emmsale/member/domain/MemberRepository.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/domain/MemberRepository.java b/backend/emm-sale/src/main/java/com/emmsale/member/domain/MemberRepository.java index 48124dc54..31ce32c2f 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/member/domain/MemberRepository.java +++ b/backend/emm-sale/src/main/java/com/emmsale/member/domain/MemberRepository.java @@ -1,5 +1,8 @@ package com.emmsale.member.domain; +import static com.emmsale.member.exception.MemberExceptionType.NOT_FOUND_MEMBER; + +import com.emmsale.member.exception.MemberException; import java.util.List; import java.util.Optional; import java.util.Set; @@ -19,4 +22,8 @@ public interface MemberRepository extends JpaRepository { @Query("select m from Member m where m.id in :memberIds") List findAllByIdIn(@Param("memberIds") final Set memberIds); + + default Member getByIdOrElseThrow(final Long id) { + return findById(id).orElseThrow(() -> new MemberException(NOT_FOUND_MEMBER)); + } } From 64b4e62e5c2664ef6e2cc5fd10bb28491d709afc Mon Sep 17 00:00:00 2001 From: amaran-th Date: Tue, 31 Oct 2023 16:48:41 +0900 Subject: [PATCH 06/41] =?UTF-8?q?refactor:=20EventDetailResponse=EB=A5=BC?= =?UTF-8?q?=20EventResponse=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #803 --- .../java/com/emmsale/EventApiTest.java | 16 +-- .../java/com/emmsale/ScrapApiTest.java | 10 +- .../java/com/emmsale/event/api/EventApi.java | 10 +- .../application/EventCommandService.java | 10 +- .../event/application/EventQueryService.java | 16 +-- ...DetailResponse.java => EventResponse.java} | 12 +-- .../java/com/emmsale/scrap/api/ScrapApi.java | 4 +- .../scrap/application/ScrapQueryService.java | 6 +- .../application/EventCommandServiceTest.java | 20 ++-- .../application/EventQueryServiceTest.java | 98 +++++++++---------- ...sponseTest.java => EventResponseTest.java} | 7 +- .../application/ScrapQueryServiceTest.java | 10 +- 12 files changed, 109 insertions(+), 110 deletions(-) rename backend/emm-sale/src/main/java/com/emmsale/event/application/dto/{EventDetailResponse.java => EventResponse.java} (89%) rename backend/emm-sale/src/test/java/com/emmsale/event/application/dto/{EventDetailResponseTest.java => EventResponseTest.java} (85%) diff --git a/backend/emm-sale/src/documentTest/java/com/emmsale/EventApiTest.java b/backend/emm-sale/src/documentTest/java/com/emmsale/EventApiTest.java index 40207691b..ef69d49b0 100644 --- a/backend/emm-sale/src/documentTest/java/com/emmsale/EventApiTest.java +++ b/backend/emm-sale/src/documentTest/java/com/emmsale/EventApiTest.java @@ -14,7 +14,7 @@ import com.emmsale.event.EventFixture; import com.emmsale.event.api.EventApi; import com.emmsale.event.application.dto.EventDetailRequest; -import com.emmsale.event.application.dto.EventDetailResponse; +import com.emmsale.event.application.dto.EventResponse; import com.emmsale.event.domain.Event; import com.emmsale.event.domain.EventMode; import com.emmsale.event.domain.EventType; @@ -110,7 +110,7 @@ class EventApiTest extends MockMvcTestHelper { void findEvent() throws Exception { //given final Long eventId = 1L; - final EventDetailResponse eventDetailResponse = new EventDetailResponse(eventId, "인프콘 2023", + final EventResponse eventResponse = new EventResponse(eventId, "인프콘 2023", "http://infcon.com", LocalDateTime.of(2023, 8, 15, 12, 0), LocalDateTime.of(2023, 8, 15, 12, 0), LocalDateTime.of(2023, 8, 1, 12, 0), LocalDateTime.of(2023, 8, 15, 12, 0), "코엑스", @@ -119,7 +119,7 @@ void findEvent() throws Exception { List.of("imageUrl1", "imageUrl2"), "인프런", "유료", "온라인"); Mockito.when(eventQueryService.findEvent(ArgumentMatchers.anyLong(), any())) - .thenReturn(eventDetailResponse); + .thenReturn(eventResponse); //when mockMvc.perform(get("/events/" + eventId)).andExpect( @@ -148,8 +148,8 @@ void findEvents() throws Exception { .optional() ); - final List eventResponses = List.of( - new EventDetailResponse( + final List eventResponses = List.of( + new EventResponse( 5L, "웹 컨퍼런스", "https://biz.pusan.ac.kr/dext5editordata/2022/08/20220810_160546511_10103.jpg", @@ -162,7 +162,7 @@ void findEvents() throws Exception { List.of("imageUrl1", "imageUrl2"), "인프런", PaymentType.PAID.getValue(), EventMode.ONLINE.getValue()), - new EventDetailResponse(2L, + new EventResponse(2L, "AI 컨퍼런스", "https://biz.pusan.ac.kr/dext5editordata/2022/08/20220810_160546511_10103.jpg", LocalDateTime.parse("2023-07-22T12:00:00"), @@ -224,7 +224,7 @@ void updateEventTest() throws Exception { tags, event.getType(), EventMode.ON_OFFLINE, PaymentType.FREE, "행사기관"); - final EventDetailResponse response = new EventDetailResponse(1L, request.getName(), + final EventResponse response = new EventResponse(1L, request.getName(), request.getInformationUrl(), request.getStartDateTime(), request.getEndDateTime(), request.getApplyStartDateTime(), request.getApplyEndDateTime(), request.getLocation(), @@ -326,7 +326,7 @@ void addEventTest() throws Exception { tags, event.getType(), EventMode.ON_OFFLINE, PaymentType.FREE, "행사기관"); - final EventDetailResponse response = new EventDetailResponse(1L, request.getName(), + final EventResponse response = new EventResponse(1L, request.getName(), request.getInformationUrl(), request.getStartDateTime(), request.getEndDateTime(), request.getApplyStartDateTime(), request.getApplyEndDateTime(), request.getLocation(), diff --git a/backend/emm-sale/src/documentTest/java/com/emmsale/ScrapApiTest.java b/backend/emm-sale/src/documentTest/java/com/emmsale/ScrapApiTest.java index c23bdf3d1..af0f65739 100644 --- a/backend/emm-sale/src/documentTest/java/com/emmsale/ScrapApiTest.java +++ b/backend/emm-sale/src/documentTest/java/com/emmsale/ScrapApiTest.java @@ -11,7 +11,7 @@ import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import com.emmsale.event.application.dto.EventDetailResponse; +import com.emmsale.event.application.dto.EventResponse; import com.emmsale.event.domain.EventMode; import com.emmsale.event.domain.EventType; import com.emmsale.event.domain.PaymentType; @@ -36,8 +36,8 @@ class ScrapApiTest extends MockMvcTestHelper { @DisplayName("스크랩 목록을 성공적으로 조회하면 200 OK를 반환한다.") void findAllScraps() throws Exception { //given - final List expectedScrapResponse = List.of( - new EventDetailResponse( + final List expectedScrapResponse = List.of( + new EventResponse( 1L, "인프콘 2023", "https://aaa", @@ -54,7 +54,7 @@ void findAllScraps() throws Exception { PaymentType.PAID.getValue(), EventMode.ONLINE.getValue() ), - new EventDetailResponse( + new EventResponse( 5L, "웹 컨퍼런스", "https://aaa", @@ -70,7 +70,7 @@ void findAllScraps() throws Exception { "인프런", PaymentType.PAID.getValue(), EventMode.ONLINE.getValue()), - new EventDetailResponse(2L, + new EventResponse(2L, "AI 컨퍼런스", "https://aaa", LocalDateTime.parse("2023-06-03T12:00:00"), diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java b/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java index aadbaaee4..7aebba943 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java @@ -3,7 +3,7 @@ import com.emmsale.event.application.EventCommandService; import com.emmsale.event.application.EventQueryService; import com.emmsale.event.application.dto.EventDetailRequest; -import com.emmsale.event.application.dto.EventDetailResponse; +import com.emmsale.event.application.dto.EventResponse; import com.emmsale.event.domain.EventStatus; import com.emmsale.event.domain.EventType; import java.time.LocalDate; @@ -35,12 +35,12 @@ public class EventApi { private final EventCommandService eventCommandService; @GetMapping("/{id}") - public ResponseEntity findEventById(@PathVariable final Long id) { + public ResponseEntity findEventById(@PathVariable final Long id) { return ResponseEntity.ok(eventQueryService.findEvent(id, LocalDate.now())); } @GetMapping - public ResponseEntity> findEvents( + public ResponseEntity> findEvents( @RequestParam(required = false) final EventType category, @RequestParam(name = "start_date", required = false) final String startDate, @RequestParam(name = "end_date", required = false) final String endDate, @@ -55,14 +55,14 @@ public ResponseEntity> findEvents( @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @ResponseStatus(HttpStatus.CREATED) - public EventDetailResponse addEvent(@RequestPart @Valid final EventDetailRequest request, + public EventResponse addEvent(@RequestPart @Valid final EventDetailRequest request, @RequestPart final List images) { return eventCommandService.addEvent(request, images); } @PutMapping(path = "/{eventId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @ResponseStatus(HttpStatus.OK) - public EventDetailResponse updateEvent(@PathVariable final Long eventId, + public EventResponse updateEvent(@PathVariable final Long eventId, @RequestPart @Valid final EventDetailRequest request, @RequestPart final List images) { return eventCommandService.updateEvent(eventId, request, images); diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventCommandService.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventCommandService.java index 6bc0b23df..bb1fce090 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventCommandService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventCommandService.java @@ -4,7 +4,7 @@ import static java.util.stream.Collectors.toList; import com.emmsale.event.application.dto.EventDetailRequest; -import com.emmsale.event.application.dto.EventDetailResponse; +import com.emmsale.event.application.dto.EventResponse; import com.emmsale.event.domain.Event; import com.emmsale.event.domain.repository.EventRepository; import com.emmsale.event.domain.repository.EventTagRepository; @@ -35,7 +35,7 @@ public class EventCommandService { private final EventPublisher eventPublisher; private final ImageCommandService imageCommandService; - public EventDetailResponse addEvent(final EventDetailRequest request, + public EventResponse addEvent(final EventDetailRequest request, final List images) { final Event event = eventRepository.save(request.toEvent()); final List tags = findAllPersistTagsOrElseThrow(request.getTags()); @@ -47,10 +47,10 @@ public EventDetailResponse addEvent(final EventDetailRequest request, eventPublisher.publish(event); final String thumbnailImageUrl = savedImages.extractThumbnailImage(); final List informationImageUrls = savedImages.extractInformationImages(); - return EventDetailResponse.from(event, thumbnailImageUrl, informationImageUrls); + return EventResponse.from(event, thumbnailImageUrl, informationImageUrls); } - public EventDetailResponse updateEvent(final Long eventId, final EventDetailRequest request, + public EventResponse updateEvent(final Long eventId, final EventDetailRequest request, final List images) { final Event event = eventRepository.findById(eventId) .orElseThrow(() -> new EventException(NOT_FOUND_EVENT)); @@ -78,7 +78,7 @@ public EventDetailResponse updateEvent(final Long eventId, final EventDetailRequ .saveImages(ImageType.EVENT, event.getId(), images)); final String thumbnailImageUrl = savedImages.extractThumbnailImage(); final List informationImageUrls = savedImages.extractInformationImages(); - return EventDetailResponse.from(updatedEvent, thumbnailImageUrl, informationImageUrls); + return EventResponse.from(updatedEvent, thumbnailImageUrl, informationImageUrls); } public void deleteEvent(final Long eventId) { diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventQueryService.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventQueryService.java index 7f0b0790b..76383c07a 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventQueryService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventQueryService.java @@ -10,7 +10,7 @@ import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.toList; -import com.emmsale.event.application.dto.EventDetailResponse; +import com.emmsale.event.application.dto.EventResponse; import com.emmsale.event.domain.Event; import com.emmsale.event.domain.EventStatus; import com.emmsale.event.domain.EventType; @@ -50,7 +50,7 @@ public class EventQueryService { private final TagRepository tagRepository; private final ImageRepository imageRepository; - public EventDetailResponse findEvent(final Long id, final LocalDate today) { + public EventResponse findEvent(final Long id, final LocalDate today) { final Event event = eventRepository.findById(id) .orElseThrow(() -> new EventException(NOT_FOUND_EVENT)); @@ -61,10 +61,10 @@ public EventDetailResponse findEvent(final Long id, final LocalDate today) { .collect(toList())); final String thumbnailImageUrl = images.extractThumbnailImage(); final List informationImageUrls = images.extractInformationImages(); - return EventDetailResponse.from(event, thumbnailImageUrl, informationImageUrls); + return EventResponse.from(event, thumbnailImageUrl, informationImageUrls); } - public List findEvents(final EventType category, + public List findEvents(final EventType category, final LocalDateTime nowDateTime, final String startDate, final String endDate, final List tagNames, final List statuses, final String keyword) { Specification spec = (root, query, criteriaBuilder) -> null; @@ -180,7 +180,7 @@ private EnumMap> groupByEventStatus(final LocalDateTime ); } - private List filterByStatuses( + private List filterByStatuses( final List statuses, final EnumMap> eventsForEventStatus, final Map imagesPerEventId @@ -188,14 +188,14 @@ private List filterByStatuses( if (isExistStatusName(statuses)) { return filterEventResponseByStatuses(statuses, eventsForEventStatus, imagesPerEventId); } - return EventDetailResponse.mergeEventResponses(eventsForEventStatus, imagesPerEventId); + return EventResponse.mergeEventResponses(eventsForEventStatus, imagesPerEventId); } private boolean isExistStatusName(final List statuses) { return statuses != null; } - private List filterEventResponseByStatuses( + private List filterEventResponseByStatuses( final List statuses, final EnumMap> eventsForEventStatus, final Map imagesPerEventId @@ -203,7 +203,7 @@ private List filterEventResponseByStatuses( return eventsForEventStatus.entrySet() .stream() .filter(entry -> statuses.contains(entry.getKey())) - .map(entry -> EventDetailResponse.makeEventResponsesByStatus(entry.getValue(), + .map(entry -> EventResponse.makeEventResponsesByStatus(entry.getValue(), imagesPerEventId)) .reduce(new ArrayList<>(), (combinedEvents, eventsToAdd) -> { combinedEvents.addAll(eventsToAdd); diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventDetailResponse.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventResponse.java similarity index 89% rename from backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventDetailResponse.java rename to backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventResponse.java index 18a339f56..c0dcefd83 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventDetailResponse.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventResponse.java @@ -18,7 +18,7 @@ @RequiredArgsConstructor @Getter -public class EventDetailResponse { +public class EventResponse { public static final String DATE_TIME_FORMAT = "yyyy:MM:dd:HH:mm:ss"; @@ -42,7 +42,7 @@ public class EventDetailResponse { private final String paymentType; private final String eventMode; - public static EventDetailResponse from( + public static EventResponse from( final Event event, final String thumbnailUrl, final List imageUrls @@ -52,7 +52,7 @@ public static EventDetailResponse from( .map(Tag::getName) .collect(toUnmodifiableList()); - return new EventDetailResponse( + return new EventResponse( event.getId(), event.getName(), event.getInformationUrl(), @@ -71,19 +71,19 @@ public static EventDetailResponse from( ); } - public static List makeEventResponsesByStatus(final List events, + public static List makeEventResponsesByStatus(final List events, final Map imagesPerEventId) { return events.stream() .map(event -> { final AllImagesOfContent allImageUrls = imagesPerEventId.get(event.getId()); final String thumbnailImageUrl = allImageUrls.extractThumbnailImage(); final List informationImageUrls = allImageUrls.extractInformationImages(); - return EventDetailResponse.from(event, thumbnailImageUrl, informationImageUrls); + return EventResponse.from(event, thumbnailImageUrl, informationImageUrls); }) .collect(Collectors.toList()); } - public static List mergeEventResponses( + public static List mergeEventResponses( final Map> groupByEventStatus, final Map imagesPerEventId ) { diff --git a/backend/emm-sale/src/main/java/com/emmsale/scrap/api/ScrapApi.java b/backend/emm-sale/src/main/java/com/emmsale/scrap/api/ScrapApi.java index 5d5f28270..ce89d8fe2 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/scrap/api/ScrapApi.java +++ b/backend/emm-sale/src/main/java/com/emmsale/scrap/api/ScrapApi.java @@ -1,6 +1,6 @@ package com.emmsale.scrap.api; -import com.emmsale.event.application.dto.EventDetailResponse; +import com.emmsale.event.application.dto.EventResponse; import com.emmsale.member.domain.Member; import com.emmsale.scrap.application.ScrapCommandService; import com.emmsale.scrap.application.ScrapQueryService; @@ -27,7 +27,7 @@ public class ScrapApi { @GetMapping @ResponseStatus(HttpStatus.OK) - public List findAllScraps(final Member member) { + public List findAllScraps(final Member member) { return scrapQueryService.findAllScraps(member); } diff --git a/backend/emm-sale/src/main/java/com/emmsale/scrap/application/ScrapQueryService.java b/backend/emm-sale/src/main/java/com/emmsale/scrap/application/ScrapQueryService.java index 41a38b6d9..930daed40 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/scrap/application/ScrapQueryService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/scrap/application/ScrapQueryService.java @@ -2,7 +2,7 @@ import static java.util.stream.Collectors.groupingBy; -import com.emmsale.event.application.dto.EventDetailResponse; +import com.emmsale.event.application.dto.EventResponse; import com.emmsale.event.domain.Event; import com.emmsale.event.domain.EventStatus; import com.emmsale.image.domain.AllImagesOfContent; @@ -28,7 +28,7 @@ public class ScrapQueryService { private final ScrapRepository scrapRepository; private final ImageRepository imageRepository; - public List findAllScraps(final Member member) { + public List findAllScraps(final Member member) { //TODO : Scrap에서 event 사용해서 N+1 발생 final List scrappedEvents = scrapRepository.findAllByMemberId(member.getId()) .stream() @@ -39,7 +39,7 @@ public List findAllScraps(final Member member) { .collect( groupingBy(event -> event.getEventPeriod().calculateEventStatus(LocalDateTime.now()))); - return EventDetailResponse.mergeEventResponses(eventGroupByStatus, + return EventResponse.mergeEventResponses(eventGroupByStatus, makeImageUrlsPerEventId(scrappedEvents)); } diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventCommandServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventCommandServiceTest.java index a790354d0..d16ada5a2 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventCommandServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventCommandServiceTest.java @@ -28,7 +28,7 @@ import static org.mockito.Mockito.doNothing; import com.emmsale.event.application.dto.EventDetailRequest; -import com.emmsale.event.application.dto.EventDetailResponse; +import com.emmsale.event.application.dto.EventResponse; import com.emmsale.event.domain.Event; import com.emmsale.event.domain.EventMode; import com.emmsale.event.domain.EventTag; @@ -58,31 +58,31 @@ class EventCommandServiceTest extends ServiceIntegrationTestHelper { - private static final EventDetailResponse 인프콘_2023 = new EventDetailResponse(null, "인프콘 2023", + private static final EventResponse 인프콘_2023 = new EventResponse(null, "인프콘 2023", null, null, null, null, null, "코엑스", List.of("백엔드"), "이미지1", EventType.CONFERENCE.name(), List.of(), "인프런", PaymentType.PAID.getValue(), EventMode.OFFLINE.getValue()); - private static final EventDetailResponse 웹_컨퍼런스 = new EventDetailResponse(null, "웹 컨퍼런스", null, + private static final EventResponse 웹_컨퍼런스 = new EventResponse(null, "웹 컨퍼런스", null, null, null, null, null, "코엑스", List.of("백엔드"), "이미지1", EventType.CONFERENCE.name(), List.of(), "주최기관", PaymentType.PAID.getValue(), EventMode.ONLINE.getValue()); - private static final EventDetailResponse 안드로이드_컨퍼런스 = new EventDetailResponse(null, "안드로이드 컨퍼런스", + private static final EventResponse 안드로이드_컨퍼런스 = new EventResponse(null, "안드로이드 컨퍼런스", null, null, null, null, null, "코엑스", List.of("백엔드"), "이미지1", EventType.CONFERENCE.name(), List.of(), "주최기관", PaymentType.PAID.getValue(), EventMode.ONLINE.getValue()); - private static final EventDetailResponse AI_컨퍼런스 = new EventDetailResponse(null, "AI 컨퍼런스", + private static final EventResponse AI_컨퍼런스 = new EventResponse(null, "AI 컨퍼런스", null, null, null, null, null, "코엑스", List.of("백엔드"), "이미지1", EventType.CONFERENCE.name(), List.of(), "주최기관", PaymentType.PAID.getValue(), EventMode.ONLINE.getValue()); - private static final EventDetailResponse 모바일_컨퍼런스 = new EventDetailResponse(null, "모바일 컨퍼런스", + private static final EventResponse 모바일_컨퍼런스 = new EventResponse(null, "모바일 컨퍼런스", null, null, null, null, null, "코엑스", List.of("백엔드"), "이미지1", EventType.CONFERENCE.name(), List.of(), "주최기관", PaymentType.PAID.getValue(), EventMode.ONLINE.getValue()); - private static final EventDetailResponse AI_아이디어_공모전 = new EventDetailResponse(null, + private static final EventResponse AI_아이디어_공모전 = new EventResponse(null, "AI 아이디어 공모전", null, null, null, null, null, "코엑스", List.of("백엔드"), "이미지1", EventType.CONFERENCE.name(), List.of(), "주최기관", PaymentType.PAID.getValue(), EventMode.ONLINE.getValue()); - private static final EventDetailResponse 구름톤 = new EventDetailResponse(null, "구름톤", null, + private static final EventResponse 구름톤 = new EventResponse(null, "구름톤", null, null, null, null, null, "코엑스", List.of("백엔드"), "이미지1", EventType.COMPETITION.name(), List.of(), "주최기관", PaymentType.PAID.getValue(), EventMode.ONLINE.getValue()); @@ -187,7 +187,7 @@ void addEventTest() { .sendMessageTo(any(Notification.class), anyLong()); //when - final EventDetailResponse response = eventCommandService.addEvent(request, + final EventResponse response = eventCommandService.addEvent(request, mockMultipartFiles); final Event savedEvent = eventRepository.findById(response.getId()).get(); @@ -318,7 +318,7 @@ void updateEventTest() { final Long eventId = event.getId(); //when - final EventDetailResponse response = eventCommandService.updateEvent(eventId, updateRequest, + final EventResponse response = eventCommandService.updateEvent(eventId, updateRequest, mockMultipartFiles); final Event updatedEvent = eventRepository.findById(eventId).get(); diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventQueryServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventQueryServiceTest.java index 1289354f7..b2790b303 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventQueryServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventQueryServiceTest.java @@ -24,7 +24,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import com.emmsale.event.application.dto.EventDetailResponse; +import com.emmsale.event.application.dto.EventResponse; import com.emmsale.event.domain.Event; import com.emmsale.event.domain.EventMode; import com.emmsale.event.domain.EventStatus; @@ -57,31 +57,31 @@ class EventQueryServiceTest extends ServiceIntegrationTestHelper { - private static final EventDetailResponse 인프콘_2023 = new EventDetailResponse(null, "인프콘 2023", + private static final EventResponse 인프콘_2023 = new EventResponse(null, "인프콘 2023", null, null, null, null, null, "코엑스", List.of("백엔드"), "이미지1", EventType.CONFERENCE.name(), List.of(), "인프런", PaymentType.PAID.getValue(), EventMode.OFFLINE.getValue()); - private static final EventDetailResponse 웹_컨퍼런스 = new EventDetailResponse(null, "웹 컨퍼런스", null, + private static final EventResponse 웹_컨퍼런스 = new EventResponse(null, "웹 컨퍼런스", null, null, null, null, null, "코엑스", List.of("백엔드"), "이미지1", EventType.CONFERENCE.name(), List.of(), "주최기관", PaymentType.PAID.getValue(), EventMode.ONLINE.getValue()); - private static final EventDetailResponse 안드로이드_컨퍼런스 = new EventDetailResponse(null, "안드로이드 컨퍼런스", + private static final EventResponse 안드로이드_컨퍼런스 = new EventResponse(null, "안드로이드 컨퍼런스", null, null, null, null, null, "코엑스", List.of("백엔드"), "이미지1", EventType.CONFERENCE.name(), List.of(), "주최기관", PaymentType.PAID.getValue(), EventMode.ONLINE.getValue()); - private static final EventDetailResponse AI_컨퍼런스 = new EventDetailResponse(null, "AI 컨퍼런스", + private static final EventResponse AI_컨퍼런스 = new EventResponse(null, "AI 컨퍼런스", null, null, null, null, null, "코엑스", List.of("백엔드"), "이미지1", EventType.CONFERENCE.name(), List.of(), "주최기관", PaymentType.PAID.getValue(), EventMode.ONLINE.getValue()); - private static final EventDetailResponse 모바일_컨퍼런스 = new EventDetailResponse(null, "모바일 컨퍼런스", + private static final EventResponse 모바일_컨퍼런스 = new EventResponse(null, "모바일 컨퍼런스", null, null, null, null, null, "코엑스", List.of("백엔드"), "이미지1", EventType.CONFERENCE.name(), List.of(), "주최기관", PaymentType.PAID.getValue(), EventMode.ONLINE.getValue()); - private static final EventDetailResponse AI_아이디어_공모전 = new EventDetailResponse(null, + private static final EventResponse AI_아이디어_공모전 = new EventResponse(null, "AI 아이디어 공모전", null, null, null, null, null, "코엑스", List.of("백엔드"), "이미지1", EventType.CONFERENCE.name(), List.of(), "주최기관", PaymentType.PAID.getValue(), EventMode.ONLINE.getValue()); - private static final EventDetailResponse 구름톤 = new EventDetailResponse(null, "구름톤", null, + private static final EventResponse 구름톤 = new EventResponse(null, "구름톤", null, null, null, null, null, "코엑스", List.of("백엔드"), "이미지1", EventType.COMPETITION.name(), List.of(), "주최기관", PaymentType.PAID.getValue(), EventMode.ONLINE.getValue()); @@ -156,11 +156,11 @@ void success() { final String thumbnailUrl = "imageUrl2"; final List imageUrls = List.of("imageUrl1", "imageUrl3"); - final EventDetailResponse expected = EventDetailResponse.from(event, thumbnailUrl, + final EventResponse expected = EventResponse.from(event, thumbnailUrl, imageUrls); //when - final EventDetailResponse actual = eventQueryService.findEvent(event.getId(), 날짜_8월_10일()); + final EventResponse actual = eventQueryService.findEvent(event.getId(), 날짜_8월_10일()); //then assertThat(actual) @@ -174,11 +174,11 @@ void success_imageUrl_null_imageUrls_empty() { //given final Event event = eventRepository.save(eventFixture()); - final EventDetailResponse expected = EventDetailResponse.from(event, null, + final EventResponse expected = EventResponse.from(event, null, Collections.emptyList()); //when - final EventDetailResponse actual = eventQueryService.findEvent(event.getId(), 날짜_8월_10일()); + final EventResponse actual = eventQueryService.findEvent(event.getId(), 날짜_8월_10일()); //then assertThat(actual) @@ -196,11 +196,11 @@ void success_imageUrls_empty() { ); final String thumbnailUrl = "imageUrl2"; - final EventDetailResponse expected = EventDetailResponse.from(event, thumbnailUrl, + final EventResponse expected = EventResponse.from(event, thumbnailUrl, Collections.emptyList()); //when - final EventDetailResponse actual = eventQueryService.findEvent(event.getId(), 날짜_8월_10일()); + final EventResponse actual = eventQueryService.findEvent(event.getId(), 날짜_8월_10일()); //then assertThat(actual) @@ -230,12 +230,12 @@ class findEvents { @DisplayName("2023년 7월 21일에 행사를 조회하면, 모든 행사 목록을 조회할 수 있다.") void findEvents_all() { // given - final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, 구름톤, AI_컨퍼런스, + final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, 구름톤, AI_컨퍼런스, 모바일_컨퍼런스, 안드로이드_컨퍼런스, AI_아이디어_공모전); // when - final List actualEvents = eventQueryService.findEvents(null, TODAY, + final List actualEvents = eventQueryService.findEvents(null, TODAY, null, null, null, null, null); // then @@ -249,11 +249,11 @@ void findEvents_all() { @DisplayName("2023년 7월 21일에 컨퍼런스 행사를 조회하면, 해당 카테고리에 해당하는 모든 행사 목록을 조회할 수 있다.") void findEvents_CONFERENCE() { // given - final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, AI_컨퍼런스, 모바일_컨퍼런스, + final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, AI_컨퍼런스, 모바일_컨퍼런스, 안드로이드_컨퍼런스); // when - final List actualEvents = eventQueryService.findEvents( + final List actualEvents = eventQueryService.findEvents( EventType.CONFERENCE, TODAY, null, null, null, null, null); @@ -269,10 +269,10 @@ void findEvents_CONFERENCE() { @DisplayName("2023년 7월 21일에 대회 행사를 조회하면, 해당 카테고리에 해당하는 모든 행사 목록을 조회할 수 있다.") void findEvents_COMPETITION() { // given - final List expectedEvents = List.of(구름톤, AI_아이디어_공모전); + final List expectedEvents = List.of(구름톤, AI_아이디어_공모전); // when - final List actualEvents = eventQueryService.findEvents( + final List actualEvents = eventQueryService.findEvents( EventType.COMPETITION, TODAY, null, null, null, null, null); @@ -288,11 +288,11 @@ void findEvents_COMPETITION() { @DisplayName("2023년 7월 21일에 2023년 7월 행사를 조회하면, 해당 기간에 걸쳐있는 모든 행사 목록을 조회할 수 있다.") void findEvents_2023_7() { // given - final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, AI_컨퍼런스, + final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, AI_컨퍼런스, 안드로이드_컨퍼런스); // when - final List actualEvents = eventQueryService.findEvents( + final List actualEvents = eventQueryService.findEvents( EventType.CONFERENCE, TODAY, "2023-07-01", "2023-07-31", null, null, null); @@ -308,10 +308,10 @@ void findEvents_2023_7() { @DisplayName("2023년 7월 21일에 2023년 8월 행사를 조회하면, 해당 기간에 걸쳐있는 모든 행사 목록을 조회할 수 있다.") void findEvents_2023_8() { // given - final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, 모바일_컨퍼런스); + final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, 모바일_컨퍼런스); // when - final List actualEvents = eventQueryService.findEvents( + final List actualEvents = eventQueryService.findEvents( EventType.CONFERENCE, TODAY, "2023-08-01", "2023-08-31", null, null, null); @@ -327,10 +327,10 @@ void findEvents_2023_8() { @DisplayName("2023년 7월 21일에 2023년 6월 행사를 조회하면, 해당 기간에 걸쳐있는 모든 행사 목록을 조회할 수 있다.") void findEvents_2023_6() { // given - final List expectedEvents = List.of(인프콘_2023, 안드로이드_컨퍼런스); + final List expectedEvents = List.of(인프콘_2023, 안드로이드_컨퍼런스); // when - final List actualEvents = eventQueryService.findEvents( + final List actualEvents = eventQueryService.findEvents( EventType.CONFERENCE, TODAY, "2023-06-01", "2023-06-30", null, null, null); @@ -346,10 +346,10 @@ void findEvents_2023_6() { @DisplayName("2023년 7월 21일에 2023년 7월 17일 이후에 있는 행사를 조회하면, 해당 기간에 걸쳐있는 모든 행사 목록을 조회할 수 있다.") void findEvents_after_2023_7_17() { // given - final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, AI_컨퍼런스, 모바일_컨퍼런스); + final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, AI_컨퍼런스, 모바일_컨퍼런스); // when - final List actualEvents = eventQueryService.findEvents( + final List actualEvents = eventQueryService.findEvents( EventType.CONFERENCE, TODAY, "2023-07-17", null, null, null, null); @@ -365,11 +365,11 @@ void findEvents_after_2023_7_17() { @DisplayName("2023년 7월 21일에 2023년 7월 31일 이전에 있는 행사를 조회하면, 해당 기간에 걸쳐있는 모든 행사 목록을 조회할 수 있다.") void findEvents_before_2023_7_31() { // given - final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, AI_컨퍼런스, + final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, AI_컨퍼런스, 안드로이드_컨퍼런스); // when - final List actualEvents = eventQueryService.findEvents( + final List actualEvents = eventQueryService.findEvents( EventType.CONFERENCE, TODAY, null, "2023-07-31", null, null, null); @@ -385,7 +385,7 @@ void findEvents_before_2023_7_31() { @DisplayName("아무 행사도 없는 2023년 12월 행사를 조회하면, 빈 목록을 반환한다.") void findEvents_2023_12() { // given, when - final List actualEvents = eventQueryService.findEvents( + final List actualEvents = eventQueryService.findEvents( EventType.CONFERENCE, TODAY, "2023-12-01", "2023-12-31", null, null, null); @@ -442,10 +442,10 @@ void findEvents_start_after_end_fail() { @DisplayName("'안드로이드' 태그를 포함하는 행사 목록을 조회할 수 있다.") void findEvents_tag_filter() { // given - final List expectedEvents = List.of(인프콘_2023, 모바일_컨퍼런스, 안드로이드_컨퍼런스); + final List expectedEvents = List.of(인프콘_2023, 모바일_컨퍼런스, 안드로이드_컨퍼런스); // when - final List actualEvents = eventQueryService.findEvents( + final List actualEvents = eventQueryService.findEvents( EventType.CONFERENCE, TODAY, null, null, List.of("안드로이드"), null, null); @@ -461,11 +461,11 @@ void findEvents_tag_filter() { @DisplayName("'안드로이드', '백엔드' 태그를 포함하는 행사 목록을 조회할 수 있다.") void findEvents_tags_filter() { // given - final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, 모바일_컨퍼런스, + final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, 모바일_컨퍼런스, 안드로이드_컨퍼런스); // when - final List actualEvents = eventQueryService.findEvents( + final List actualEvents = eventQueryService.findEvents( EventType.CONFERENCE, TODAY, null, null, List.of("안드로이드", "백엔드"), null, null); @@ -495,10 +495,10 @@ void findEvents_tag_filter_fail() { @DisplayName("'진행 중' 상태의 행사 목록을 조회할 수 있다.") void findEvents_status_filter() { // given - final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스); + final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스); // when - final List actualEvents = eventQueryService.findEvents( + final List actualEvents = eventQueryService.findEvents( EventType.CONFERENCE, TODAY, null, null, null, List.of(IN_PROGRESS), null); @@ -514,10 +514,10 @@ void findEvents_status_filter() { @DisplayName("'진행 중' 및 '진행 예정' 상태의 행사 목록을 조회할 수 있다.") void findEvents_statuses_filter() { // given - final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, AI_컨퍼런스, 모바일_컨퍼런스); + final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, AI_컨퍼런스, 모바일_컨퍼런스); // when - final List actualEvents = eventQueryService.findEvents( + final List actualEvents = eventQueryService.findEvents( EventType.CONFERENCE, TODAY, null, null, null, List.of(EventStatus.UPCOMING, IN_PROGRESS), null); @@ -533,10 +533,10 @@ void findEvents_statuses_filter() { @DisplayName("9월에 존재하는 진행 예정인 '안드로이드', '백엔드' 태그를 포함하는 행사 목록을 조회할 수 있다.") void findEvents_period_tags_filter() { // given - final List expectedEvents = List.of(모바일_컨퍼런스); + final List expectedEvents = List.of(모바일_컨퍼런스); // when - final List actualEvents = eventQueryService.findEvents( + final List actualEvents = eventQueryService.findEvents( EventType.CONFERENCE, TODAY, "2023-09-01", "2023-09-30", List.of("안드로이드", "백엔드"), List.of(EventStatus.UPCOMING), null); @@ -557,12 +557,12 @@ class SearchEvent { @DisplayName("2023년 7월 21일에 컨퍼런스 행사를 조회할 때, 검색어가 공백인 경우 해당 카테고리에 해당하는 모든 행사 목록을 조회할 수 있다.") void findEvents_blank_search(final String keyword) { // given - final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, 구름톤, AI_컨퍼런스, + final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, 구름톤, AI_컨퍼런스, 모바일_컨퍼런스, 안드로이드_컨퍼런스, AI_아이디어_공모전); // when - final List actualEvents = eventQueryService.findEvents(null, + final List actualEvents = eventQueryService.findEvents(null, TODAY, null, null, null, null, keyword); @@ -578,11 +578,11 @@ void findEvents_blank_search(final String keyword) { void findEvents_search() { // given final String keyword = " 컨퍼런스"; - final List expectedEvents = List.of(웹_컨퍼런스, AI_컨퍼런스, 모바일_컨퍼런스, + final List expectedEvents = List.of(웹_컨퍼런스, AI_컨퍼런스, 모바일_컨퍼런스, 안드로이드_컨퍼런스); // when - final List actualEvents = eventQueryService.findEvents(null, + final List actualEvents = eventQueryService.findEvents(null, TODAY, null, null, null, null, keyword); // then @@ -597,10 +597,10 @@ void findEvents_search() { void findEvents_multiple_tokens_search() { // given final String keyword = " 모 컨퍼"; - final List expectedEvents = List.of(모바일_컨퍼런스); + final List expectedEvents = List.of(모바일_컨퍼런스); // when - final List actualEvents = eventQueryService.findEvents(null, + final List actualEvents = eventQueryService.findEvents(null, TODAY, null, null, null, null, keyword); // then @@ -615,10 +615,10 @@ void findEvents_multiple_tokens_search() { void findEvents_status_filter_and_search() { // given final String keyword = "프콘 "; - final List expectedEvents = List.of(인프콘_2023); + final List expectedEvents = List.of(인프콘_2023); // when - final List actualEvents = eventQueryService.findEvents(null, + final List actualEvents = eventQueryService.findEvents(null, TODAY, null, null, null, List.of(IN_PROGRESS), keyword); diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/application/dto/EventDetailResponseTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/application/dto/EventResponseTest.java similarity index 85% rename from backend/emm-sale/src/test/java/com/emmsale/event/application/dto/EventDetailResponseTest.java rename to backend/emm-sale/src/test/java/com/emmsale/event/application/dto/EventResponseTest.java index 1a05ece4d..398f8877b 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/event/application/dto/EventDetailResponseTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/event/application/dto/EventResponseTest.java @@ -5,14 +5,13 @@ import com.emmsale.event.EventFixture; import com.emmsale.event.domain.Event; import com.emmsale.event.domain.EventMode; -import com.emmsale.event.domain.EventStatus; import com.emmsale.event.domain.PaymentType; import java.util.Collections; import java.util.List; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -class EventDetailResponseTest { +class EventResponseTest { @Test @DisplayName("이벤트를 상세 조회할 값을 변환할 수 있다.") @@ -22,7 +21,7 @@ void createEventDetailResponseTest() { final String thumbnailUrl = "thumbnail"; final List imageUrls = List.of("imageUrl1", "imageUrl2"); - final EventDetailResponse expected = new EventDetailResponse( + final EventResponse expected = new EventResponse( 구름톤.getId(), 구름톤.getName(), 구름톤.getInformationUrl(), @@ -41,7 +40,7 @@ void createEventDetailResponseTest() { ); //when - final EventDetailResponse actual = EventDetailResponse.from(구름톤, thumbnailUrl, imageUrls); + final EventResponse actual = EventResponse.from(구름톤, thumbnailUrl, imageUrls); //then assertThat(actual) diff --git a/backend/emm-sale/src/test/java/com/emmsale/scrap/application/ScrapQueryServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/scrap/application/ScrapQueryServiceTest.java index 2eb7dc65d..0112c4709 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/scrap/application/ScrapQueryServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/scrap/application/ScrapQueryServiceTest.java @@ -7,7 +7,7 @@ import static org.assertj.core.api.Assertions.assertThat; import com.emmsale.event.EventFixture; -import com.emmsale.event.application.dto.EventDetailResponse; +import com.emmsale.event.application.dto.EventResponse; import com.emmsale.event.domain.Event; import com.emmsale.event.domain.EventTag; import com.emmsale.event.domain.repository.EventRepository; @@ -52,10 +52,10 @@ void findAllScrapsTest() { scrapRepository.save(new Scrap(member.getId(), event2)); //when - final List actual = scrapQueryService.findAllScraps(member); + final List actual = scrapQueryService.findAllScraps(member); - final List expected = List.of( - new EventDetailResponse( + final List expected = List.of( + new EventResponse( event1.getId(), event1.getName(), event1.getInformationUrl(), event1.getEventPeriod().getStartDate(), event1.getEventPeriod().getEndDate(), event1.getEventPeriod().getApplyStartDate(), event1.getEventPeriod().getApplyEndDate(), @@ -70,7 +70,7 @@ void findAllScrapsTest() { 행사_이미지4(event1.getId()).getName()), event1.getOrganization(), event1.getPaymentType().getValue(), event1.getEventMode().getValue()), - new EventDetailResponse(event2.getId(), event2.getName(), event2.getInformationUrl(), + new EventResponse(event2.getId(), event2.getName(), event2.getInformationUrl(), event2.getEventPeriod().getStartDate(), event2.getEventPeriod().getEndDate(), event2.getEventPeriod().getApplyStartDate(), event2.getEventPeriod().getApplyEndDate(), event2.getLocation(), From 80a3b75ecbd06256a4fa4c1d0d0aa4829681f265 Mon Sep 17 00:00:00 2001 From: amaran-th Date: Tue, 31 Oct 2023 17:36:01 +0900 Subject: [PATCH 07/41] =?UTF-8?q?refactor:=20=EC=BD=94=EB=93=9C=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #803 --- .../application/EventCommandService.java | 8 ++--- .../event/application/EventQueryService.java | 6 ++-- .../RecruitmentPostQueryService.java | 2 +- .../event/application/dto/EventResponse.java | 22 ++++++------ .../java/com/emmsale/event/domain/Event.java | 4 +-- .../image/domain/AllImagesOfContent.java | 6 ++-- .../image/exception/ImageExceptionType.java | 14 +++++--- .../scrap/application/ScrapQueryService.java | 6 ++-- .../application/EventQueryServiceTest.java | 34 +++++++++---------- .../application/dto/EventResponseTest.java | 16 +++++++-- 10 files changed, 65 insertions(+), 53 deletions(-) diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventCommandService.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventCommandService.java index bb1fce090..a9146d1d5 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventCommandService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventCommandService.java @@ -45,9 +45,7 @@ public EventResponse addEvent(final EventDetailRequest request, .saveImages(ImageType.EVENT, event.getId(), images)); eventPublisher.publish(event); - final String thumbnailImageUrl = savedImages.extractThumbnailImage(); - final List informationImageUrls = savedImages.extractInformationImages(); - return EventResponse.from(event, thumbnailImageUrl, informationImageUrls); + return EventResponse.from(event, savedImages); } public EventResponse updateEvent(final Long eventId, final EventDetailRequest request, @@ -76,9 +74,7 @@ public EventResponse updateEvent(final Long eventId, final EventDetailRequest re imageCommandService.deleteImages(ImageType.EVENT, eventId); final AllImagesOfContent savedImages = new AllImagesOfContent(imageCommandService .saveImages(ImageType.EVENT, event.getId(), images)); - final String thumbnailImageUrl = savedImages.extractThumbnailImage(); - final List informationImageUrls = savedImages.extractInformationImages(); - return EventResponse.from(updatedEvent, thumbnailImageUrl, informationImageUrls); + return EventResponse.from(updatedEvent, savedImages); } public void deleteEvent(final Long eventId) { diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventQueryService.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventQueryService.java index 76383c07a..1a57a0719 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventQueryService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventQueryService.java @@ -59,9 +59,7 @@ public EventResponse findEvent(final Long id, final LocalDate today) { .stream() .sorted(comparing(Image::getOrder)) .collect(toList())); - final String thumbnailImageUrl = images.extractThumbnailImage(); - final List informationImageUrls = images.extractInformationImages(); - return EventResponse.from(event, thumbnailImageUrl, informationImageUrls); + return EventResponse.from(event, images); } public List findEvents(final EventType category, @@ -215,7 +213,7 @@ private List filterEventResponseByStatuses( private Map makeImagesPerEventId(final List events) { final List eventIds = events.stream() .map(Event::getId) - .collect(Collectors.toList()); + .collect(Collectors.toUnmodifiableList()); Map imagesPerEventId = new HashMap<>(); for (Long eventId : eventIds) { diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/RecruitmentPostQueryService.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/RecruitmentPostQueryService.java index 61bf91ab7..cab441964 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/application/RecruitmentPostQueryService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/application/RecruitmentPostQueryService.java @@ -59,7 +59,7 @@ public List findRecruitmentPostsByMemberId( return posts.stream() .map(RecruitmentPostQueryResponse::from) - .collect(Collectors.toList()); + .collect(Collectors.toUnmodifiableList()); } private void validateOwner(final Member member, final Long memberId) { diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventResponse.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventResponse.java index c0dcefd83..de14d8ab3 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventResponse.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventResponse.java @@ -44,8 +44,7 @@ public class EventResponse { public static EventResponse from( final Event event, - final String thumbnailUrl, - final List imageUrls + final AllImagesOfContent images ) { final List tagNames = event.getTags().stream() .map(EventTag::getTag) @@ -62,9 +61,9 @@ public static EventResponse from( event.getEventPeriod().getApplyEndDate(), event.getLocation(), tagNames, - thumbnailUrl, + images.extractThumbnailImage(), event.getType().toString(), - imageUrls, + images.extractInformationImages(), event.getOrganization(), event.getPaymentType().getValue(), event.getEventMode().getValue() @@ -74,13 +73,14 @@ public static EventResponse from( public static List makeEventResponsesByStatus(final List events, final Map imagesPerEventId) { return events.stream() - .map(event -> { - final AllImagesOfContent allImageUrls = imagesPerEventId.get(event.getId()); - final String thumbnailImageUrl = allImageUrls.extractThumbnailImage(); - final List informationImageUrls = allImageUrls.extractInformationImages(); - return EventResponse.from(event, thumbnailImageUrl, informationImageUrls); - }) - .collect(Collectors.toList()); + .map(event -> toEventResponse(imagesPerEventId, event)) + .collect(Collectors.toUnmodifiableList()); + } + + private static EventResponse toEventResponse( + final Map imagesPerEventId, final Event event) { + final AllImagesOfContent allImageUrls = imagesPerEventId.get(event.getId()); + return EventResponse.from(event, allImageUrls); } public static List mergeEventResponses( diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/domain/Event.java b/backend/emm-sale/src/main/java/com/emmsale/event/domain/Event.java index 37f7833f8..49e6a593e 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/domain/Event.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/domain/Event.java @@ -99,7 +99,7 @@ public RecruitmentPost createRecruitmentPost(final Member member, final String c public void addAllEventTags(final List tags) { final List eventTags = tags.stream() .map(tag -> new EventTag(this, tag)) - .collect(Collectors.toList()); + .collect(Collectors.toUnmodifiableList()); this.tags.addAll(eventTags); } @@ -150,6 +150,6 @@ public boolean isDiffer(final Long eventId) { public List extractTags() { return tags.stream() .map(tag -> tag.getTag().getName()) - .collect(Collectors.toList()); + .collect(Collectors.toUnmodifiableList()); } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/image/domain/AllImagesOfContent.java b/backend/emm-sale/src/main/java/com/emmsale/image/domain/AllImagesOfContent.java index 8cfe13728..6ba4a521a 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/image/domain/AllImagesOfContent.java +++ b/backend/emm-sale/src/main/java/com/emmsale/image/domain/AllImagesOfContent.java @@ -1,7 +1,9 @@ package com.emmsale.image.domain; +import static com.emmsale.image.exception.ImageExceptionType.NOT_FOUND_THUMBNAIL; import static java.util.Comparator.comparing; +import com.emmsale.image.exception.ImageException; import java.util.List; import java.util.stream.Collectors; import lombok.AllArgsConstructor; @@ -20,7 +22,7 @@ public String extractThumbnailImage() { return images.stream() .filter(Image::isThumbnail) .findFirst() - .orElseThrow() + .orElseThrow(() -> new ImageException(NOT_FOUND_THUMBNAIL)) .getName(); } @@ -29,6 +31,6 @@ public List extractInformationImages() { .sorted(comparing(Image::getOrder)) .filter(Image::isNotThumbnail) .map(Image::getName) - .collect(Collectors.toList()); + .collect(Collectors.toUnmodifiableList()); } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/image/exception/ImageExceptionType.java b/backend/emm-sale/src/main/java/com/emmsale/image/exception/ImageExceptionType.java index c0228e6d9..c7ae02090 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/image/exception/ImageExceptionType.java +++ b/backend/emm-sale/src/main/java/com/emmsale/image/exception/ImageExceptionType.java @@ -4,7 +4,11 @@ import org.springframework.http.HttpStatus; public enum ImageExceptionType implements BaseExceptionType { - + + NOT_FOUND_THUMBNAIL( + HttpStatus.NOT_FOUND, + "주어진 이미지들 중 섬네일 이미지가 존재하지 않습니다." + ), INVALID_FILE_FORMAT( HttpStatus.BAD_REQUEST, "잘못된 형식의 파일입니다." @@ -29,20 +33,20 @@ public enum ImageExceptionType implements BaseExceptionType { HttpStatus.INTERNAL_SERVER_ERROR, "이미지를 DB서 삭제하지 못했습니다." ); - + private final HttpStatus httpStatus; private final String errorMessage; - + ImageExceptionType(final HttpStatus httpStatus, final String errorMessage) { this.httpStatus = httpStatus; this.errorMessage = errorMessage; } - + @Override public HttpStatus httpStatus() { return httpStatus; } - + @Override public String errorMessage() { return errorMessage; diff --git a/backend/emm-sale/src/main/java/com/emmsale/scrap/application/ScrapQueryService.java b/backend/emm-sale/src/main/java/com/emmsale/scrap/application/ScrapQueryService.java index 930daed40..f53f05466 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/scrap/application/ScrapQueryService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/scrap/application/ScrapQueryService.java @@ -33,7 +33,7 @@ public List findAllScraps(final Member member) { final List scrappedEvents = scrapRepository.findAllByMemberId(member.getId()) .stream() .map(Scrap::getEvent) - .collect(Collectors.toList()); + .collect(Collectors.toUnmodifiableList()); final Map> eventGroupByStatus = scrappedEvents.stream() .collect( @@ -46,9 +46,9 @@ public List findAllScraps(final Member member) { private Map makeImageUrlsPerEventId(final List events) { final List eventIds = events.stream() .map(Event::getId) - .collect(Collectors.toList()); + .collect(Collectors.toUnmodifiableList()); - Map imageUrlsPerEventId = new HashMap<>(); + final Map imageUrlsPerEventId = new HashMap<>(); for (Long eventId : eventIds) { final AllImagesOfContent images = new AllImagesOfContent( imageRepository.findAllByTypeAndContentId(ImageType.EVENT, eventId)); diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventQueryServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventQueryServiceTest.java index b2790b303..121cf9ffe 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventQueryServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventQueryServiceTest.java @@ -35,6 +35,7 @@ import com.emmsale.event.domain.repository.EventTagRepository; import com.emmsale.event.exception.EventException; import com.emmsale.helper.ServiceIntegrationTestHelper; +import com.emmsale.image.domain.AllImagesOfContent; import com.emmsale.image.domain.Image; import com.emmsale.image.domain.ImageType; import com.emmsale.image.domain.repository.ImageRepository; @@ -143,21 +144,21 @@ class findEventTest { void success() { //given final Event event = eventRepository.save(eventFixture()); - imageRepository.save( - new Image("imageUrl1", ImageType.EVENT, event.getId(), 1, LocalDateTime.now()) + final String thumbnailUrl = "imageUrl2"; + final String imageUrl1 = "imageUrl1"; + final String imageUrl2 = "imageUrl3"; + final Image image1 = imageRepository.save( + new Image(imageUrl1, ImageType.EVENT, event.getId(), 1, LocalDateTime.now()) ); - imageRepository.save( - new Image("imageUrl2", ImageType.EVENT, event.getId(), 0, LocalDateTime.now()) + final Image image2 = imageRepository.save( + new Image(thumbnailUrl, ImageType.EVENT, event.getId(), 0, LocalDateTime.now()) ); - imageRepository.save( - new Image("imageUrl3", ImageType.EVENT, event.getId(), 2, LocalDateTime.now()) + final Image image3 = imageRepository.save( + new Image(imageUrl2, ImageType.EVENT, event.getId(), 2, LocalDateTime.now()) ); - final String thumbnailUrl = "imageUrl2"; - final List imageUrls = List.of("imageUrl1", "imageUrl3"); - - final EventResponse expected = EventResponse.from(event, thumbnailUrl, - imageUrls); + final EventResponse expected = EventResponse.from(event, + new AllImagesOfContent(List.of(image2, image1, image3))); //when final EventResponse actual = eventQueryService.findEvent(event.getId(), 날짜_8월_10일()); @@ -174,8 +175,8 @@ void success_imageUrl_null_imageUrls_empty() { //given final Event event = eventRepository.save(eventFixture()); - final EventResponse expected = EventResponse.from(event, null, - Collections.emptyList()); + final EventResponse expected = EventResponse.from(event, + new AllImagesOfContent(Collections.emptyList())); //when final EventResponse actual = eventQueryService.findEvent(event.getId(), 날짜_8월_10일()); @@ -191,13 +192,12 @@ void success_imageUrl_null_imageUrls_empty() { void success_imageUrls_empty() { //given final Event event = eventRepository.save(eventFixture()); - imageRepository.save( + final Image image = imageRepository.save( new Image("imageUrl2", ImageType.EVENT, event.getId(), 0, LocalDateTime.now()) ); - final String thumbnailUrl = "imageUrl2"; - final EventResponse expected = EventResponse.from(event, thumbnailUrl, - Collections.emptyList()); + final EventResponse expected = EventResponse.from(event, + new AllImagesOfContent(List.of(image))); //when final EventResponse actual = eventQueryService.findEvent(event.getId(), 날짜_8월_10일()); diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/application/dto/EventResponseTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/application/dto/EventResponseTest.java index 398f8877b..41b9b675e 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/event/application/dto/EventResponseTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/event/application/dto/EventResponseTest.java @@ -6,6 +6,9 @@ import com.emmsale.event.domain.Event; import com.emmsale.event.domain.EventMode; import com.emmsale.event.domain.PaymentType; +import com.emmsale.image.domain.AllImagesOfContent; +import com.emmsale.image.domain.Image; +import com.emmsale.image.domain.ImageType; import java.util.Collections; import java.util.List; import org.junit.jupiter.api.DisplayName; @@ -19,7 +22,16 @@ void createEventDetailResponseTest() { //given final Event 구름톤 = EventFixture.구름톤(); final String thumbnailUrl = "thumbnail"; - final List imageUrls = List.of("imageUrl1", "imageUrl2"); + final String imageUrl1 = "imageUrl1"; + final String imageUrl2 = "imageUrl2"; + final List imageUrls = List.of(imageUrl1, imageUrl2); + final AllImagesOfContent images = new AllImagesOfContent( + List.of( + new Image(thumbnailUrl, ImageType.EVENT, 구름톤.getId(), 0, null), + new Image(imageUrl1, ImageType.EVENT, 구름톤.getId(), 1, null), + new Image(imageUrl2, ImageType.EVENT, 구름톤.getId(), 2, null) + ) + ); final EventResponse expected = new EventResponse( 구름톤.getId(), @@ -40,7 +52,7 @@ void createEventDetailResponseTest() { ); //when - final EventResponse actual = EventResponse.from(구름톤, thumbnailUrl, imageUrls); + final EventResponse actual = EventResponse.from(구름톤, images); //then assertThat(actual) From 2b50448b0bb1a7cd31f7b3a4e333838c34b25e75 Mon Sep 17 00:00:00 2001 From: hong-sile Date: Wed, 1 Nov 2023 10:51:54 +0900 Subject: [PATCH 08/41] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20Member=20updateProfileUrl=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #811 --- .../java/com/emmsale/MemberApiTest.java | 4 +- .../java/com/emmsale/MockMvcTestHelper.java | 4 +- .../com/emmsale/member/api/MemberApi.java | 35 +++++++---------- ...Service.java => MemberCommandService.java} | 15 +------ .../com/emmsale/member/domain/Member.java | 12 ------ ...est.java => MemberCommandServiceTest.java} | 39 +++++-------------- .../com/emmsale/member/domain/MemberTest.java | 1 - 7 files changed, 28 insertions(+), 82 deletions(-) rename backend/emm-sale/src/main/java/com/emmsale/member/application/{MemberUpdateService.java => MemberCommandService.java} (77%) rename backend/emm-sale/src/test/java/com/emmsale/member/application/{MemberUpdateServiceTest.java => MemberCommandServiceTest.java} (83%) diff --git a/backend/emm-sale/src/documentTest/java/com/emmsale/MemberApiTest.java b/backend/emm-sale/src/documentTest/java/com/emmsale/MemberApiTest.java index ea596c11d..4c8a172ea 100644 --- a/backend/emm-sale/src/documentTest/java/com/emmsale/MemberApiTest.java +++ b/backend/emm-sale/src/documentTest/java/com/emmsale/MemberApiTest.java @@ -252,7 +252,7 @@ void test_findProfile() throws Exception { void deleteMemberTest() throws Exception { //given final long memberId = 1L; - doNothing().when(memberUpdateService).deleteMember(any(), anyLong()); + doNothing().when(memberCommandService).deleteMember(any(), anyLong()); final String accessToken = "access_token"; //when @@ -272,7 +272,7 @@ void updateProfile() throws Exception { final String accessToken = "access_token"; final MockMultipartHttpServletRequestBuilder builder = createUpdateProfileBuilder(memberId); - when(memberUpdateService.updateMemberProfile + when(memberCommandService.updateMemberProfile (any(MultipartFile.class), anyLong(), any(Member.class))) .thenReturn(memberImageResponse); diff --git a/backend/emm-sale/src/documentTest/java/com/emmsale/MockMvcTestHelper.java b/backend/emm-sale/src/documentTest/java/com/emmsale/MockMvcTestHelper.java index 003ba7104..58ff5923b 100644 --- a/backend/emm-sale/src/documentTest/java/com/emmsale/MockMvcTestHelper.java +++ b/backend/emm-sale/src/documentTest/java/com/emmsale/MockMvcTestHelper.java @@ -18,7 +18,7 @@ import com.emmsale.member.application.InterestTagService; import com.emmsale.member.application.MemberActivityService; import com.emmsale.member.application.MemberQueryService; -import com.emmsale.member.application.MemberUpdateService; +import com.emmsale.member.application.MemberCommandService; import com.emmsale.message_room.application.MessageCommandService; import com.emmsale.message_room.application.RoomQueryService; import com.emmsale.notification.application.FcmTokenRegisterService; @@ -69,7 +69,7 @@ abstract class MockMvcTestHelper { @MockBean protected MemberActivityService memberActivityService; @MockBean - protected MemberUpdateService memberUpdateService; + protected MemberCommandService memberCommandService; @MockBean protected MemberQueryService memberQueryService; @MockBean diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/api/MemberApi.java b/backend/emm-sale/src/main/java/com/emmsale/member/api/MemberApi.java index 15b465d2d..f39df0aea 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/member/api/MemberApi.java +++ b/backend/emm-sale/src/main/java/com/emmsale/member/api/MemberApi.java @@ -1,18 +1,17 @@ package com.emmsale.member.api; -import com.emmsale.member.application.MemberActivityService; +import com.emmsale.member.application.MemberActivityCommandService; +import com.emmsale.member.application.MemberActivityQueryService; +import com.emmsale.member.application.MemberCommandService; import com.emmsale.member.application.MemberQueryService; -import com.emmsale.member.application.MemberUpdateService; import com.emmsale.member.application.dto.DescriptionRequest; import com.emmsale.member.application.dto.MemberActivityAddRequest; import com.emmsale.member.application.dto.MemberActivityInitialRequest; import com.emmsale.member.application.dto.MemberActivityResponses; import com.emmsale.member.application.dto.MemberImageResponse; import com.emmsale.member.application.dto.MemberProfileResponse; -import com.emmsale.member.application.dto.OpenProfileUrlRequest; import com.emmsale.member.domain.Member; import java.util.List; -import javax.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -32,8 +31,9 @@ @RequiredArgsConstructor public class MemberApi { - private final MemberActivityService memberActivityService; - private final MemberUpdateService memberUpdateService; + private final MemberActivityQueryService memberActivityQueryService; + private final MemberActivityCommandService memberActivityCommandService; + private final MemberCommandService memberCommandService; private final MemberQueryService memberQueryService; @PostMapping("/members") @@ -41,7 +41,7 @@ public ResponseEntity register( final Member member, @RequestBody final MemberActivityInitialRequest memberActivityInitialRequest ) { - memberActivityService.registerActivities(member, memberActivityInitialRequest); + memberActivityCommandService.registerActivities(member, memberActivityInitialRequest); return ResponseEntity.noContent().build(); } @@ -51,29 +51,20 @@ public ResponseEntity> addActivity( @RequestBody final MemberActivityAddRequest memberActivityAddRequest ) { return ResponseEntity.status(HttpStatus.CREATED) - .body(memberActivityService.addActivity(member, memberActivityAddRequest)); + .body(memberActivityCommandService.addActivity(member, memberActivityAddRequest)); } @DeleteMapping("/members/activities") public ResponseEntity> deleteActivity(final Member member, @RequestParam("activity-ids") final List deleteActivityIds) { return ResponseEntity.ok( - memberActivityService.deleteActivity(member, deleteActivityIds)); + memberActivityCommandService.deleteActivity(member, deleteActivityIds)); } @GetMapping("/members/{member-id}/activities") public ResponseEntity> findActivity( @PathVariable("member-id") final Long memberId) { - return ResponseEntity.ok(memberActivityService.findActivities(memberId)); - } - - @PutMapping("/members/open-profile-url") - public ResponseEntity updateOpenProfileUrl( - final Member member, - @RequestBody @Valid final OpenProfileUrlRequest openProfileUrlRequest - ) { - memberUpdateService.updateOpenProfileUrl(member, openProfileUrlRequest); - return ResponseEntity.noContent().build(); + return ResponseEntity.ok(memberActivityQueryService.findActivities(memberId)); } @PutMapping("/members/description") @@ -81,7 +72,7 @@ public ResponseEntity updateDescription( final Member member, @RequestBody final DescriptionRequest descriptionRequest ) { - memberUpdateService.updateDescription(member, descriptionRequest); + memberCommandService.updateDescription(member, descriptionRequest); return ResponseEntity.noContent().build(); } @@ -96,7 +87,7 @@ public ResponseEntity deleteMember( @PathVariable final Long memberId, final Member member ) { - memberUpdateService.deleteMember(member, memberId); + memberCommandService.deleteMember(member, memberId); return ResponseEntity.noContent().build(); } @@ -107,7 +98,7 @@ public ResponseEntity updateProfile( final Member member ) { final MemberImageResponse memberImageResponse - = memberUpdateService.updateMemberProfile(image, memberId, member); + = memberCommandService.updateMemberProfile(image, memberId, member); return ResponseEntity.ok(memberImageResponse); } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberUpdateService.java b/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberCommandService.java similarity index 77% rename from backend/emm-sale/src/main/java/com/emmsale/member/application/MemberUpdateService.java rename to backend/emm-sale/src/main/java/com/emmsale/member/application/MemberCommandService.java index 5668771c6..0c1efbc51 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberUpdateService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberCommandService.java @@ -3,8 +3,6 @@ import com.emmsale.image.application.S3Client; import com.emmsale.member.application.dto.DescriptionRequest; import com.emmsale.member.application.dto.MemberImageResponse; -import com.emmsale.member.application.dto.MemberProfileResponse; -import com.emmsale.member.application.dto.OpenProfileUrlRequest; import com.emmsale.member.domain.Member; import com.emmsale.member.domain.MemberRepository; import com.emmsale.member.exception.MemberException; @@ -18,22 +16,11 @@ @Service @Transactional @RequiredArgsConstructor -public class MemberUpdateService { +public class MemberCommandService { private final MemberRepository memberRepository; private final S3Client s3Client; - public void updateOpenProfileUrl( - final Member member, - final OpenProfileUrlRequest openProfileUrlRequest - ) { - final Member persistMember = memberRepository.findById(member.getId()) - .orElseThrow(() -> new MemberException((MemberExceptionType.NOT_FOUND_MEMBER))); - final String openProfileUrl = openProfileUrlRequest.getOpenProfileUrl(); - - persistMember.updateOpenProfileUrl(openProfileUrl); - } - public void updateDescription(final Member member, final DescriptionRequest descriptionRequest) { final String description = descriptionRequest.getDescription(); diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/domain/Member.java b/backend/emm-sale/src/main/java/com/emmsale/member/domain/Member.java index 314ccb927..51003a9d1 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/member/domain/Member.java +++ b/backend/emm-sale/src/main/java/com/emmsale/member/domain/Member.java @@ -3,7 +3,6 @@ import com.emmsale.base.BaseEntity; import com.emmsale.member.exception.MemberException; import com.emmsale.member.exception.MemberExceptionType; -import java.util.Optional; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; @@ -30,9 +29,6 @@ public class Member extends BaseEntity { private String name; @Column(nullable = false) private String description; - @Column - @Getter(value = AccessLevel.PRIVATE) - private String openProfileUrl; @Column(nullable = false) private String imageUrl; @Column(nullable = false) @@ -59,10 +55,6 @@ public void updateName(final String name) { this.name = name; } - public void updateOpenProfileUrl(final String openProfileUrl) { - this.openProfileUrl = openProfileUrl; - } - public void updateDescription(final String description) { validateDescriptionNull(description); validateDescriptionLength(description); @@ -112,8 +104,4 @@ public boolean isOnboarded() { public boolean isNotGithubProfile() { return !imageUrl.startsWith(GITHUB_PROFILE_DOMAIN); } - - public Optional getOptionalOpenProfileUrl() { - return Optional.ofNullable(openProfileUrl); - } } diff --git a/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberUpdateServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberCommandServiceTest.java similarity index 83% rename from backend/emm-sale/src/test/java/com/emmsale/member/application/MemberUpdateServiceTest.java rename to backend/emm-sale/src/test/java/com/emmsale/member/application/MemberCommandServiceTest.java index a854c0ee2..d9a8495f4 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberUpdateServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberCommandServiceTest.java @@ -13,7 +13,6 @@ import com.emmsale.helper.ServiceIntegrationTestHelper; import com.emmsale.member.application.dto.DescriptionRequest; -import com.emmsale.member.application.dto.OpenProfileUrlRequest; import com.emmsale.member.domain.Member; import com.emmsale.member.domain.MemberRepository; import com.emmsale.member.exception.MemberException; @@ -32,31 +31,13 @@ import org.springframework.test.util.ReflectionTestUtils; import org.springframework.web.multipart.MultipartFile; -class MemberUpdateServiceTest extends ServiceIntegrationTestHelper { +class MemberCommandServiceTest extends ServiceIntegrationTestHelper { @Autowired - private MemberUpdateService memberUpdateService; + private MemberCommandService memberCommandService; @Autowired private MemberRepository memberRepository; - @Test - @DisplayName("오픈 프로필 URL을 업데이트한다.") - void updateOpenProfileUrlTest() { - // given - final Member member = memberRepository.save(memberFixture()); - - final String expectOpenProfileUrl = "https://open.kakao.com/new/profile/url"; - final OpenProfileUrlRequest request = new OpenProfileUrlRequest(expectOpenProfileUrl); - - // when - memberUpdateService.updateOpenProfileUrl(member, request); - - final Member actualMember = memberRepository.findById(member.getId()).get(); - - // then - assertThat(actualMember.getOptionalOpenProfileUrl().get()).isEqualTo(expectOpenProfileUrl); - } - @Nested @DisplayName("한줄 자기소개를 업데이트한다.") class UpdateDescription { @@ -72,7 +53,7 @@ void updateDescription_success(final String inputDescription) { final DescriptionRequest request = new DescriptionRequest(expectDescription); // when - memberUpdateService.updateDescription(member, request); + memberCommandService.updateDescription(member, request); final Member actualMember = memberRepository.findById(member.getId()).get(); @@ -90,7 +71,7 @@ void updateDescription_fail() { final DescriptionRequest request = new DescriptionRequest(invalidDescription); // when - final ThrowingCallable actual = () -> memberUpdateService.updateDescription(member, request); + final ThrowingCallable actual = () -> memberCommandService.updateDescription(member, request); // then assertThatThrownBy(actual) @@ -110,7 +91,7 @@ void updateDescription_trim(final String inputDescription) { final DescriptionRequest request = new DescriptionRequest(inputDescription); // when - memberUpdateService.updateDescription(member, request); + memberCommandService.updateDescription(member, request); final Member actualMember = memberRepository.findById(member.getId()).get(); @@ -131,7 +112,7 @@ void success() { final Member member = memberRepository.findById(memberId).get(); //when - memberUpdateService.deleteMember(member, memberId); + memberCommandService.deleteMember(member, memberId); //then assertThat(memberRepository.findById(memberId)) @@ -146,7 +127,7 @@ void forbbidenDeleteMemberException() { final Member member = memberRepository.findById(memberId).get(); //when && then - assertThatThrownBy(() -> memberUpdateService.deleteMember(member, otherMemberId)) + assertThatThrownBy(() -> memberCommandService.deleteMember(member, otherMemberId)) .isInstanceOf(MemberException.class) .hasMessage(MemberExceptionType.FORBIDDEN_DELETE_MEMBER.errorMessage()); } @@ -178,7 +159,7 @@ void pastGithubUrl() { final Member member = memberRepository.save(fixture); //when - memberUpdateService.updateMemberProfile(image, member.getId(), member); + memberCommandService.updateMemberProfile(image, member.getId(), member); //then assertAll( @@ -198,7 +179,7 @@ void pastCustomImage() { final Member member = memberRepository.save(fixture); //when - memberUpdateService.updateMemberProfile(image, member.getId(), member); + memberCommandService.updateMemberProfile(image, member.getId(), member); //then assertAll( @@ -219,7 +200,7 @@ void validateMemberIsNotMe() { //when && then final ThrowingCallable testTarget = () -> - memberUpdateService.updateMemberProfile(image, member.getId() + 1, member); + memberCommandService.updateMemberProfile(image, member.getId() + 1, member); assertThatThrownBy(testTarget) .isInstanceOf(MemberException.class) diff --git a/backend/emm-sale/src/test/java/com/emmsale/member/domain/MemberTest.java b/backend/emm-sale/src/test/java/com/emmsale/member/domain/MemberTest.java index 06b7532c5..f3b26d819 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/member/domain/MemberTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/member/domain/MemberTest.java @@ -90,5 +90,4 @@ void trueCase() { assertTrue(member.isNotGithubProfile()); } } - } From e7caef1ef844eb619f3a9a3e4befb491f595f2eb Mon Sep 17 00:00:00 2001 From: amaran-th Date: Wed, 1 Nov 2023 11:03:18 +0900 Subject: [PATCH 09/41] =?UTF-8?q?refactor:=20=ED=95=A8=EA=BB=98=ED=95=B4?= =?UTF-8?q?=EC=9A=94=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20=EB=B0=98=ED=99=98=20?= =?UTF-8?q?=ED=98=95=EC=8B=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 단건 조회, 다건 조회 API에 적용 #803 --- .../com/emmsale/RecruitmentPostApiTest.java | 94 ++++++++++--------- .../emmsale/event/api/RecruitmentPostApi.java | 9 +- .../event/application/EventQueryService.java | 9 +- .../RecruitmentPostQueryService.java | 9 +- .../RecruitmentPostQueryServiceTest.java | 23 ++--- 5 files changed, 72 insertions(+), 72 deletions(-) diff --git a/backend/emm-sale/src/documentTest/java/com/emmsale/RecruitmentPostApiTest.java b/backend/emm-sale/src/documentTest/java/com/emmsale/RecruitmentPostApiTest.java index d248e3ce3..17c61de19 100644 --- a/backend/emm-sale/src/documentTest/java/com/emmsale/RecruitmentPostApiTest.java +++ b/backend/emm-sale/src/documentTest/java/com/emmsale/RecruitmentPostApiTest.java @@ -18,7 +18,6 @@ import com.emmsale.event.api.RecruitmentPostApi; import com.emmsale.event.application.dto.RecruitmentPostQueryResponse; import com.emmsale.event.application.dto.RecruitmentPostRequest; -import com.emmsale.event.application.dto.RecruitmentPostResponse; import com.emmsale.event.application.dto.RecruitmentPostUpdateRequest; import com.emmsale.member.application.dto.MemberReferenceResponse; import com.emmsale.member.domain.Member; @@ -36,6 +35,36 @@ @WebMvcTest(RecruitmentPostApi.class) class RecruitmentPostApiTest extends MockMvcTestHelper { + private static final ResponseFieldsSnippet RECRUITMENT_POSTS_RESPONSE_FIELDS = responseFields( + fieldWithPath("[].postId").type(JsonFieldType.NUMBER).description("함께해요 게시글 식별자"), + fieldWithPath("[].content").type(JsonFieldType.STRING).description("함께해요 게시글 내용"), + fieldWithPath("[].updatedAt").type(JsonFieldType.STRING).description("함께해요 게시글 수정 날짜"), + fieldWithPath("[].member.id").type(JsonFieldType.NUMBER).description("member의 식별자"), + fieldWithPath("[].member.name").type(JsonFieldType.STRING).description("member의 이름"), + fieldWithPath("[].member.description").type(JsonFieldType.STRING) + .description("member의 한줄 자기소개"), + fieldWithPath("[].member.imageUrl").type(JsonFieldType.STRING) + .description("member의 이미지 url"), + fieldWithPath("[].member.githubUrl").type(JsonFieldType.STRING) + .description("member의 github Url"), + fieldWithPath("[].eventId").type(JsonFieldType.NUMBER).description("행사의 식별자") + ); + + private static final ResponseFieldsSnippet RECRUITMENT_POST_RESPONSE_FIELDS = responseFields( + fieldWithPath("postId").type(JsonFieldType.NUMBER).description("함께해요 게시글 식별자"), + fieldWithPath("content").type(JsonFieldType.STRING).description("함께해요 게시글 내용"), + fieldWithPath("updatedAt").type(JsonFieldType.STRING).description("함께해요 게시글 수정 날짜"), + fieldWithPath("member.id").type(JsonFieldType.NUMBER).description("member의 식별자"), + fieldWithPath("member.name").type(JsonFieldType.STRING).description("member의 이름"), + fieldWithPath("member.description").type(JsonFieldType.STRING) + .description("member의 한줄 자기소개"), + fieldWithPath("member.imageUrl").type(JsonFieldType.STRING) + .description("member의 이미지 url"), + fieldWithPath("member.githubUrl").type(JsonFieldType.STRING) + .description("member의 github Url"), + fieldWithPath("eventId").type(JsonFieldType.NUMBER).description("행사의 식별자") + ); + @Test @DisplayName("Event에 참여게시글을 추가할 수 있다.") void createRecruitmentPost() throws Exception { @@ -91,27 +120,22 @@ void deleteRecruitmentPost() throws Exception { void findRecruitmentPosts() throws Exception { //given final Long eventId = 1L; - final ResponseFieldsSnippet responseFields = responseFields( - fieldWithPath("[].id").type(JsonFieldType.NUMBER).description("함께해요 게시글 식별자"), - fieldWithPath("[].memberId").type(JsonFieldType.NUMBER).description("member의 식별자"), - fieldWithPath("[].name").type(JsonFieldType.STRING).description("member 이름"), - fieldWithPath("[].imageUrl").type(JsonFieldType.STRING).description("프로필 이미지 url"), - fieldWithPath("[].content").type(JsonFieldType.STRING).description("함께해요 게시글 내용"), - fieldWithPath("[].updatedAt").type(JsonFieldType.STRING).description("함께해요 게시글 수정 날짜") - ); - final List responses = List.of( - new RecruitmentPostResponse(1L, 1L, "스캇", "imageUrl", "저랑 같이 컨퍼런스 갈 사람", - LocalDate.of(2023, 7, 15)), - new RecruitmentPostResponse(2L, 2L, "홍실", "imageUrl", "스캇 말고 저랑 갈 사람", - LocalDate.of(2023, 7, 22)) + final Member member1 = new Member(1L, 3L, "https://github.image", "아마란스", "amaran-th"); + final Member member2 = new Member(2L, 4L, "https://github.image", "아마아마", "아마아마"); + final LocalDate postedAt = LocalDate.of(2023, 7, 15); + final List response = List.of( + new RecruitmentPostQueryResponse(1L, "함께해요~", postedAt, + MemberReferenceResponse.from(member1), 21L), + new RecruitmentPostQueryResponse(2L, "같이 가요~", postedAt, + MemberReferenceResponse.from(member2), 43L) ); - when(postQueryService.findRecruitmentPosts(eventId)).thenReturn(responses); + when(postQueryService.findRecruitmentPosts(eventId)).thenReturn(response); //when && then mockMvc.perform(get(String.format("/events/%s/recruitment-posts", eventId))) .andExpect(status().isOk()) - .andDo(document("find-recruitment-posts", responseFields)); + .andDo(document("find-recruitment-posts", RECRUITMENT_POSTS_RESPONSE_FIELDS)); } @Test @@ -120,24 +144,19 @@ void findRecruitmentPost() throws Exception { //given final Long eventId = 1L; final Long postId = 2L; - final ResponseFieldsSnippet responseFields = responseFields( - fieldWithPath("id").type(JsonFieldType.NUMBER).description("함께해요 게시글 식별자"), - fieldWithPath("memberId").type(JsonFieldType.NUMBER).description("member의 식별자"), - fieldWithPath("name").type(JsonFieldType.STRING).description("member 이름"), - fieldWithPath("imageUrl").type(JsonFieldType.STRING).description("프로필 이미지 url"), - fieldWithPath("content").type(JsonFieldType.STRING).description("함께해요 게시글 내용"), - fieldWithPath("updatedAt").type(JsonFieldType.STRING).description("함께해요 게시글 수정 날짜") - ); - final RecruitmentPostResponse response = new RecruitmentPostResponse(2L, 1L, - "스캇", "imageUrl", "저랑 같이 컨퍼런스 갈 사람", - LocalDate.of(2023, 7, 15)); + final Long memberId = 1L; + final Member member = new Member(memberId, 3L, "https://github.image", "아마란스", "amaran-th"); + final LocalDate postedAt = LocalDate.of(2023, 7, 15); + final RecruitmentPostQueryResponse response = new RecruitmentPostQueryResponse(1L, "함께해요~", + postedAt, + MemberReferenceResponse.from(member), 21L); when(postQueryService.findRecruitmentPost(eventId, postId)).thenReturn(response); //when && then mockMvc.perform(get(String.format("/events/%s/recruitment-posts/%s", eventId, postId))) .andExpect(status().isOk()) - .andDo(document("find-recruitment-post", responseFields)); + .andDo(document("find-recruitment-post", RECRUITMENT_POST_RESPONSE_FIELDS)); } @Test @@ -189,7 +208,7 @@ void isAlreadyRecruitTest() throws Exception { void findRecruitmentPostsByMemberIdTest() throws Exception { //given final Long memberId = 1L; - final Member member = new Member(1L, 3L, "https://github.image", "아마란스", "amaran-th"); + final Member member = new Member(memberId, 3L, "https://github.image", "아마란스", "amaran-th"); final LocalDate postedAt = LocalDate.of(2023, 7, 15); final List response = List.of( new RecruitmentPostQueryResponse(1L, "함께해요~", postedAt, @@ -198,21 +217,6 @@ void findRecruitmentPostsByMemberIdTest() throws Exception { MemberReferenceResponse.from(member), 43L) ); - final ResponseFieldsSnippet responseFields = responseFields( - fieldWithPath("[].postId").type(JsonFieldType.NUMBER).description("함께해요 게시글 식별자"), - fieldWithPath("[].content").type(JsonFieldType.STRING).description("함께해요 게시글 내용"), - fieldWithPath("[].updatedAt").type(JsonFieldType.STRING).description("함께해요 게시글 수정 날짜"), - fieldWithPath("[].member.id").type(JsonFieldType.NUMBER).description("member의 식별자"), - fieldWithPath("[].member.name").type(JsonFieldType.STRING).description("member의 이름"), - fieldWithPath("[].member.description").type(JsonFieldType.STRING) - .description("member의 한줄 자기소개"), - fieldWithPath("[].member.imageUrl").type(JsonFieldType.STRING) - .description("member의 이미지 url"), - fieldWithPath("[].member.githubUrl").type(JsonFieldType.STRING) - .description("member의 github Url"), - fieldWithPath("[].eventId").type(JsonFieldType.NUMBER).description("행사의 식별자") - ); - //when given(postQueryService.findRecruitmentPostsByMemberId(any(), any())).willReturn(response); @@ -223,6 +227,6 @@ void findRecruitmentPostsByMemberIdTest() throws Exception { .header(HttpHeaders.AUTHORIZATION, "Bearer AccessToken")) .andExpect(status().isOk()) .andDo(document("find-all-by-member-id-recruitment-post", - responseFields)); + RECRUITMENT_POSTS_RESPONSE_FIELDS)); } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/api/RecruitmentPostApi.java b/backend/emm-sale/src/main/java/com/emmsale/event/api/RecruitmentPostApi.java index fe2ae1847..96d4f7b83 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/api/RecruitmentPostApi.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/api/RecruitmentPostApi.java @@ -7,7 +7,6 @@ import com.emmsale.event.application.RecruitmentPostQueryService; import com.emmsale.event.application.dto.RecruitmentPostQueryResponse; import com.emmsale.event.application.dto.RecruitmentPostRequest; -import com.emmsale.event.application.dto.RecruitmentPostResponse; import com.emmsale.event.application.dto.RecruitmentPostUpdateRequest; import com.emmsale.member.domain.Member; import java.util.List; @@ -71,18 +70,18 @@ public ResponseEntity deleteRecruitmentPost( } @GetMapping("/{id}/recruitment-posts") - public ResponseEntity> findRecruitmentPosts( + public ResponseEntity> findRecruitmentPosts( @PathVariable final Long id) { - final List responses = postQueryService.findRecruitmentPosts(id); + final List responses = postQueryService.findRecruitmentPosts(id); return ResponseEntity.ok(responses); } @GetMapping("/{event-id}/recruitment-posts/{recruitment-post-id}") - public ResponseEntity findRecruitmentPost( + public ResponseEntity findRecruitmentPost( @PathVariable("event-id") final Long eventId, @PathVariable("recruitment-post-id") final Long postId ) { - final RecruitmentPostResponse response + final RecruitmentPostQueryResponse response = postQueryService.findRecruitmentPost(eventId, postId); return ResponseEntity.ok(response); } diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventQueryService.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventQueryService.java index 1a57a0719..e8f444e34 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventQueryService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventQueryService.java @@ -18,7 +18,6 @@ import com.emmsale.event.exception.EventException; import com.emmsale.event.exception.EventExceptionType; import com.emmsale.image.domain.AllImagesOfContent; -import com.emmsale.image.domain.Image; import com.emmsale.image.domain.ImageType; import com.emmsale.image.domain.repository.ImageRepository; import com.emmsale.tag.domain.Tag; @@ -54,11 +53,9 @@ public EventResponse findEvent(final Long id, final LocalDate today) { final Event event = eventRepository.findById(id) .orElseThrow(() -> new EventException(NOT_FOUND_EVENT)); - final AllImagesOfContent images = new AllImagesOfContent(imageRepository - .findAllByTypeAndContentId(ImageType.EVENT, event.getId()) - .stream() - .sorted(comparing(Image::getOrder)) - .collect(toList())); + final AllImagesOfContent images = new AllImagesOfContent( + imageRepository.findAllByTypeAndContentId(ImageType.EVENT, event.getId()) + ); return EventResponse.from(event, images); } diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/RecruitmentPostQueryService.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/RecruitmentPostQueryService.java index cab441964..325c5c5d0 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/application/RecruitmentPostQueryService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/application/RecruitmentPostQueryService.java @@ -6,7 +6,6 @@ import static java.util.stream.Collectors.toUnmodifiableList; import com.emmsale.event.application.dto.RecruitmentPostQueryResponse; -import com.emmsale.event.application.dto.RecruitmentPostResponse; import com.emmsale.event.domain.Event; import com.emmsale.event.domain.RecruitmentPost; import com.emmsale.event.domain.repository.EventRepository; @@ -29,12 +28,12 @@ public class RecruitmentPostQueryService { private final RecruitmentPostRepository recruitmentPostRepository; private final EventRepository eventRepository; - public List findRecruitmentPosts(final Long eventId) { + public List findRecruitmentPosts(final Long eventId) { final Event event = eventRepository.findById(eventId) .orElseThrow(() -> new EventException(NOT_FOUND_EVENT)); return event.getRecruitmentPosts().stream() .sorted(comparing(RecruitmentPost::getId)) - .map(RecruitmentPostResponse::from) + .map(RecruitmentPostQueryResponse::from) .collect(toUnmodifiableList()); } @@ -42,11 +41,11 @@ public Boolean isAlreadyRecruit(final Long eventId, final Long memberId) { return recruitmentPostRepository.existsByEventIdAndMemberId(eventId, memberId); } - public RecruitmentPostResponse findRecruitmentPost(final Long eventId, final Long postId) { + public RecruitmentPostQueryResponse findRecruitmentPost(final Long eventId, final Long postId) { final RecruitmentPost recruitmentPost = recruitmentPostRepository.findById(postId) .orElseThrow(() -> new EventException(NOT_FOUND_RECRUITMENT_POST)); recruitmentPost.validateEvent(eventId); - return RecruitmentPostResponse.from(recruitmentPost); + return RecruitmentPostQueryResponse.from(recruitmentPost); } public List findRecruitmentPostsByMemberId( diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/application/RecruitmentPostQueryServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/application/RecruitmentPostQueryServiceTest.java index b8135e6d4..82cd71375 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/event/application/RecruitmentPostQueryServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/event/application/RecruitmentPostQueryServiceTest.java @@ -8,12 +8,12 @@ import com.emmsale.event.EventFixture; import com.emmsale.event.application.dto.RecruitmentPostQueryResponse; import com.emmsale.event.application.dto.RecruitmentPostRequest; -import com.emmsale.event.application.dto.RecruitmentPostResponse; import com.emmsale.event.domain.Event; import com.emmsale.event.domain.RecruitmentPost; import com.emmsale.event.domain.repository.EventRepository; import com.emmsale.event.domain.repository.RecruitmentPostRepository; import com.emmsale.helper.ServiceIntegrationTestHelper; +import com.emmsale.member.application.dto.MemberReferenceResponse; import com.emmsale.member.domain.Member; import com.emmsale.member.domain.MemberRepository; import com.emmsale.member.exception.MemberException; @@ -65,15 +65,16 @@ void findRecruitmentPosts() { final Long 멤버2_참가글_ID = postCommandService .createRecruitmentPost(인프콘.getId(), requestMember2, 사용자2); - final List expected = List.of( - new RecruitmentPostResponse(멤버1_참가글_ID, 사용자1.getId(), 사용자1.getName(), 사용자1.getImageUrl(), - requestMember1.getContent(), LocalDate.now()), - new RecruitmentPostResponse(멤버2_참가글_ID, 사용자2.getId(), 사용자2.getName(), 사용자2.getImageUrl(), - requestMember2.getContent(), LocalDate.now()) + final List expected = List.of( + new RecruitmentPostQueryResponse(멤버1_참가글_ID, requestMember1.getContent(), LocalDate.now(), + MemberReferenceResponse.from(사용자1), 인프콘.getId()), + new RecruitmentPostQueryResponse(멤버2_참가글_ID, requestMember2.getContent(), LocalDate.now(), + MemberReferenceResponse.from(사용자2), 인프콘.getId()) ); //when - final List actual = postQueryService.findRecruitmentPosts(인프콘.getId()); + final List actual = postQueryService.findRecruitmentPosts( + 인프콘.getId()); //then assertThat(actual) @@ -90,12 +91,12 @@ void findRecruitmentPost() { final Long 멤버1_참가글_ID = postCommandService .createRecruitmentPost(인프콘.getId(), requestMember1, 사용자1); - final RecruitmentPostResponse expected = - new RecruitmentPostResponse(멤버1_참가글_ID, 사용자1.getId(), 사용자1.getName(), 사용자1.getImageUrl(), - requestMember1.getContent(), LocalDate.now()); + final RecruitmentPostQueryResponse expected = new RecruitmentPostQueryResponse(멤버1_참가글_ID, + requestMember1.getContent(), LocalDate.now(), MemberReferenceResponse.from(사용자1), + 인프콘.getId()); //when - final RecruitmentPostResponse actual = postQueryService.findRecruitmentPost( + final RecruitmentPostQueryResponse actual = postQueryService.findRecruitmentPost( 인프콘.getId(), 멤버1_참가글_ID); //then From 734844c5b4d2e207c93d0f727ba160d01cea8acd Mon Sep 17 00:00:00 2001 From: hong-sile Date: Thu, 2 Nov 2023 13:54:46 +0900 Subject: [PATCH 10/41] =?UTF-8?q?refactor:=20MemberActivityService=20Query?= =?UTF-8?q?=EC=99=80=20Command=20=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #811 --- .../MemberActivityCommandService.java | 105 ++++++++++++++++++ .../MemberActivityQueryService.java | 24 ++++ ... => MemberActivityCommandServiceTest.java} | 2 +- 3 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 backend/emm-sale/src/main/java/com/emmsale/member/application/MemberActivityCommandService.java create mode 100644 backend/emm-sale/src/main/java/com/emmsale/member/application/MemberActivityQueryService.java rename backend/emm-sale/src/test/java/com/emmsale/member/application/{MemberActivityQueryServiceTest.java => MemberActivityCommandServiceTest.java} (98%) diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberActivityCommandService.java b/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberActivityCommandService.java new file mode 100644 index 000000000..58816309b --- /dev/null +++ b/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberActivityCommandService.java @@ -0,0 +1,105 @@ +package com.emmsale.member.application; + +import static java.util.stream.Collectors.toUnmodifiableList; + +import com.emmsale.activity.domain.ActivityRepository; +import com.emmsale.member.application.dto.MemberActivityAddRequest; +import com.emmsale.member.application.dto.MemberActivityInitialRequest; +import com.emmsale.member.application.dto.MemberActivityResponses; +import com.emmsale.member.domain.Member; +import com.emmsale.member.domain.MemberActivity; +import com.emmsale.member.domain.MemberActivityRepository; +import com.emmsale.member.exception.MemberException; +import com.emmsale.member.exception.MemberExceptionType; +import java.util.HashSet; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional +@RequiredArgsConstructor +public class MemberActivityCommandService { + + private final MemberActivityRepository memberActivityRepository; + private final ActivityRepository activityRepository; + + public void registerActivities( + final Member member, + final MemberActivityInitialRequest memberActivityInitialRequest + ) { + if (member.isOnboarded()) { + throw new MemberException(MemberExceptionType.ALREADY_ONBOARDING); + } + final List activityIds = memberActivityInitialRequest.getActivityIds(); + saveMemberActivities(member, activityIds); + + member.updateName(memberActivityInitialRequest.getName()); + } + + private void saveMemberActivities(final Member member, final List activityIds) { + final List memberActivities = activityRepository.findAllById(activityIds) + .stream() + .map(it -> new MemberActivity(it, member)) + .collect(toUnmodifiableList()); + + validateAllActivityIdsExist(activityIds, memberActivities); + + memberActivityRepository.saveAll(memberActivities); + } + + private void validateAllActivityIdsExist( + final List activityIds, + final List memberActivities + ) { + if (memberActivities.size() != activityIds.size()) { + throw new MemberException(MemberExceptionType.INVALID_ACTIVITY_IDS); + } + } + + public List addActivity( + final Member member, + final MemberActivityAddRequest memberActivityAddRequest + ) { + final List activityIds = memberActivityAddRequest.getActivityIds(); + final List memberActivities = memberActivityRepository.findAllByMember(member); + if (hasDuplicateId(activityIds)) { + throw new MemberException(MemberExceptionType.DUPLICATE_ACTIVITY); + } + if (isAlreadyExistActivity(memberActivities, activityIds)) { + throw new MemberException(MemberExceptionType.ALREADY_EXIST_ACTIVITY); + } + saveMemberActivities(member, activityIds); + + return MemberActivityResponses.from(memberActivityRepository.findAllByMember(member)); + } + + private boolean isAlreadyExistActivity(final List memberActivities, + final List activityIds) { + return memberActivities + .stream() + .anyMatch(memberActivity -> + activityIds.contains(memberActivity.getActivity().getId()) + ); + } + + private boolean hasDuplicateId(final List activityIds) { + return new HashSet<>(activityIds).size() != activityIds.size(); + } + + public List deleteActivity( + final Member member, + final List deleteActivityIds + ) { + final List savedMemberActivityIds = + memberActivityRepository.findAllByMemberAndActivityIds(member, deleteActivityIds) + .stream() + .map(MemberActivity::getId) + .collect(toUnmodifiableList()); + + memberActivityRepository.deleteAllByIdInBatch(savedMemberActivityIds); + + return MemberActivityResponses.from(memberActivityRepository.findAllByMember(member)); + } +} diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberActivityQueryService.java b/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberActivityQueryService.java new file mode 100644 index 000000000..f70313592 --- /dev/null +++ b/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberActivityQueryService.java @@ -0,0 +1,24 @@ +package com.emmsale.member.application; + +import com.emmsale.member.application.dto.MemberActivityResponses; +import com.emmsale.member.domain.Member; +import com.emmsale.member.domain.MemberActivityRepository; +import com.emmsale.member.domain.MemberRepository; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional(readOnly = true) +@RequiredArgsConstructor +public class MemberActivityQueryService { + + private final MemberRepository memberRepository; + private final MemberActivityRepository memberActivityRepository; + + public List findActivities(final Long memberId) { + final Member member = memberRepository.getByIdOrElseThrow(memberId); + return MemberActivityResponses.from(memberActivityRepository.findAllByMember(member)); + } +} diff --git a/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberActivityQueryServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberActivityCommandServiceTest.java similarity index 98% rename from backend/emm-sale/src/test/java/com/emmsale/member/application/MemberActivityQueryServiceTest.java rename to backend/emm-sale/src/test/java/com/emmsale/member/application/MemberActivityCommandServiceTest.java index 99db4b09c..4730a665d 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberActivityQueryServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberActivityCommandServiceTest.java @@ -21,7 +21,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -class MemberActivityQueryServiceTest extends ServiceIntegrationTestHelper { +class MemberActivityCommandServiceTest extends ServiceIntegrationTestHelper { @Autowired private MemberActivityService memberActivityService; From 0b054aec5e1635b1868c44d065d9848f7c97e585 Mon Sep 17 00:00:00 2001 From: hong-sile Date: Thu, 2 Nov 2023 14:16:26 +0900 Subject: [PATCH 11/41] =?UTF-8?q?style:=20=EC=BD=94=EB=93=9C=20=EC=A0=95?= =?UTF-8?q?=EB=A0=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #811 --- .../src/main/java/com/emmsale/event/domain/Event.java | 3 +-- .../src/main/java/com/emmsale/feed/api/FeedApi.java | 6 ++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/domain/Event.java b/backend/emm-sale/src/main/java/com/emmsale/event/domain/Event.java index 37f7833f8..6a778250a 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/domain/Event.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/domain/Event.java @@ -17,7 +17,6 @@ import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; -import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @@ -61,7 +60,7 @@ public class Event extends BaseEntity { @Column(nullable = false) private String organization; - @OneToMany(mappedBy = "event", fetch = FetchType.LAZY, cascade = CascadeType.PERSIST) + @OneToMany(mappedBy = "event", cascade = CascadeType.PERSIST) private List tags = new ArrayList<>(); @OneToMany(mappedBy = "event", cascade = CascadeType.PERSIST) diff --git a/backend/emm-sale/src/main/java/com/emmsale/feed/api/FeedApi.java b/backend/emm-sale/src/main/java/com/emmsale/feed/api/FeedApi.java index 94a2918f4..c51638caf 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/feed/api/FeedApi.java +++ b/backend/emm-sale/src/main/java/com/emmsale/feed/api/FeedApi.java @@ -34,8 +34,10 @@ public class FeedApi { private final FeedCommandService feedCommandService; @GetMapping - public FeedListResponse findAllFeeds(final Member member, - @RequestParam("event-id") final Long eventId) { + public FeedListResponse findAllFeeds( + final Member member, + @RequestParam("event-id") final Long eventId + ) { return feedQueryService.findAllFeeds(member, eventId); } From bc0015f3162b69030f27f8ee3cbbd23fac2734f6 Mon Sep 17 00:00:00 2001 From: hong-sile Date: Thu, 2 Nov 2023 14:17:19 +0900 Subject: [PATCH 12/41] =?UTF-8?q?refactor:=20MemberActivity=20=EB=B0=98?= =?UTF-8?q?=ED=99=98=EA=B0=92=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #811 --- .../java/com/emmsale/MockMvcTestHelper.java | 9 +- .../com/emmsale/member/api/MemberApi.java | 8 +- .../MemberActivityCommandService.java | 16 ++- .../MemberActivityQueryService.java | 12 +- .../application/MemberActivityService.java | 115 ------------------ .../dto/MemberActivityResponse.java | 11 ++ .../dto/MemberActivityResponses.java | 60 --------- .../MemberActivityCommandServiceTest.java | 54 +++----- .../application/MemberQueryServiceTest.java | 1 - 9 files changed, 61 insertions(+), 225 deletions(-) delete mode 100644 backend/emm-sale/src/main/java/com/emmsale/member/application/MemberActivityService.java delete mode 100644 backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberActivityResponses.java diff --git a/backend/emm-sale/src/documentTest/java/com/emmsale/MockMvcTestHelper.java b/backend/emm-sale/src/documentTest/java/com/emmsale/MockMvcTestHelper.java index 58ff5923b..e1d8e6723 100644 --- a/backend/emm-sale/src/documentTest/java/com/emmsale/MockMvcTestHelper.java +++ b/backend/emm-sale/src/documentTest/java/com/emmsale/MockMvcTestHelper.java @@ -16,9 +16,10 @@ import com.emmsale.feed.application.FeedQueryService; import com.emmsale.login.application.LoginService; import com.emmsale.member.application.InterestTagService; -import com.emmsale.member.application.MemberActivityService; -import com.emmsale.member.application.MemberQueryService; +import com.emmsale.member.application.MemberActivityCommandService; +import com.emmsale.member.application.MemberActivityQueryService; import com.emmsale.member.application.MemberCommandService; +import com.emmsale.member.application.MemberQueryService; import com.emmsale.message_room.application.MessageCommandService; import com.emmsale.message_room.application.RoomQueryService; import com.emmsale.notification.application.FcmTokenRegisterService; @@ -67,7 +68,9 @@ abstract class MockMvcTestHelper { @MockBean protected FcmTokenRegisterService fcmTokenRegisterService; @MockBean - protected MemberActivityService memberActivityService; + protected MemberActivityQueryService memberActivityQueryService; + @MockBean + protected MemberActivityCommandService memberActivityCommandService; @MockBean protected MemberCommandService memberCommandService; @MockBean diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/api/MemberApi.java b/backend/emm-sale/src/main/java/com/emmsale/member/api/MemberApi.java index f39df0aea..f3d72ec15 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/member/api/MemberApi.java +++ b/backend/emm-sale/src/main/java/com/emmsale/member/api/MemberApi.java @@ -7,7 +7,7 @@ import com.emmsale.member.application.dto.DescriptionRequest; import com.emmsale.member.application.dto.MemberActivityAddRequest; import com.emmsale.member.application.dto.MemberActivityInitialRequest; -import com.emmsale.member.application.dto.MemberActivityResponses; +import com.emmsale.member.application.dto.MemberActivityResponse; import com.emmsale.member.application.dto.MemberImageResponse; import com.emmsale.member.application.dto.MemberProfileResponse; import com.emmsale.member.domain.Member; @@ -46,7 +46,7 @@ public ResponseEntity register( } @PostMapping("/members/activities") - public ResponseEntity> addActivity( + public ResponseEntity> addActivity( final Member member, @RequestBody final MemberActivityAddRequest memberActivityAddRequest ) { @@ -55,14 +55,14 @@ public ResponseEntity> addActivity( } @DeleteMapping("/members/activities") - public ResponseEntity> deleteActivity(final Member member, + public ResponseEntity> deleteActivity(final Member member, @RequestParam("activity-ids") final List deleteActivityIds) { return ResponseEntity.ok( memberActivityCommandService.deleteActivity(member, deleteActivityIds)); } @GetMapping("/members/{member-id}/activities") - public ResponseEntity> findActivity( + public ResponseEntity> findActivity( @PathVariable("member-id") final Long memberId) { return ResponseEntity.ok(memberActivityQueryService.findActivities(memberId)); } diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberActivityCommandService.java b/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberActivityCommandService.java index 58816309b..10d662412 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberActivityCommandService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberActivityCommandService.java @@ -5,7 +5,7 @@ import com.emmsale.activity.domain.ActivityRepository; import com.emmsale.member.application.dto.MemberActivityAddRequest; import com.emmsale.member.application.dto.MemberActivityInitialRequest; -import com.emmsale.member.application.dto.MemberActivityResponses; +import com.emmsale.member.application.dto.MemberActivityResponse; import com.emmsale.member.domain.Member; import com.emmsale.member.domain.MemberActivity; import com.emmsale.member.domain.MemberActivityRepository; @@ -58,7 +58,7 @@ private void validateAllActivityIdsExist( } } - public List addActivity( + public List addActivity( final Member member, final MemberActivityAddRequest memberActivityAddRequest ) { @@ -72,7 +72,10 @@ public List addActivity( } saveMemberActivities(member, activityIds); - return MemberActivityResponses.from(memberActivityRepository.findAllByMember(member)); + return memberActivityRepository.findAllByMember(member) + .stream() + .map(MemberActivityResponse::from) + .collect(toUnmodifiableList()); } private boolean isAlreadyExistActivity(final List memberActivities, @@ -88,7 +91,7 @@ private boolean hasDuplicateId(final List activityIds) { return new HashSet<>(activityIds).size() != activityIds.size(); } - public List deleteActivity( + public List deleteActivity( final Member member, final List deleteActivityIds ) { @@ -100,6 +103,9 @@ public List deleteActivity( memberActivityRepository.deleteAllByIdInBatch(savedMemberActivityIds); - return MemberActivityResponses.from(memberActivityRepository.findAllByMember(member)); + return memberActivityRepository.findAllByMember(member) + .stream() + .map(MemberActivityResponse::from) + .collect(toUnmodifiableList()); } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberActivityQueryService.java b/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberActivityQueryService.java index f70313592..b7ae5fe67 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberActivityQueryService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberActivityQueryService.java @@ -1,6 +1,8 @@ package com.emmsale.member.application; -import com.emmsale.member.application.dto.MemberActivityResponses; +import static java.util.stream.Collectors.toUnmodifiableList; + +import com.emmsale.member.application.dto.MemberActivityResponse; import com.emmsale.member.domain.Member; import com.emmsale.member.domain.MemberActivityRepository; import com.emmsale.member.domain.MemberRepository; @@ -17,8 +19,12 @@ public class MemberActivityQueryService { private final MemberRepository memberRepository; private final MemberActivityRepository memberActivityRepository; - public List findActivities(final Long memberId) { + public List findActivities(final Long memberId) { final Member member = memberRepository.getByIdOrElseThrow(memberId); - return MemberActivityResponses.from(memberActivityRepository.findAllByMember(member)); + + return memberActivityRepository.findAllByMember(member) + .stream() + .map(MemberActivityResponse::from) + .collect(toUnmodifiableList()); } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberActivityService.java b/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberActivityService.java deleted file mode 100644 index 692ba147a..000000000 --- a/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberActivityService.java +++ /dev/null @@ -1,115 +0,0 @@ -package com.emmsale.member.application; - -import static java.util.stream.Collectors.toList; - -import com.emmsale.activity.domain.ActivityRepository; -import com.emmsale.member.application.dto.MemberActivityAddRequest; -import com.emmsale.member.application.dto.MemberActivityInitialRequest; -import com.emmsale.member.application.dto.MemberActivityResponses; -import com.emmsale.member.domain.Member; -import com.emmsale.member.domain.MemberActivity; -import com.emmsale.member.domain.MemberActivityRepository; -import com.emmsale.member.domain.MemberRepository; -import com.emmsale.member.exception.MemberException; -import com.emmsale.member.exception.MemberExceptionType; -import java.util.HashSet; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Service -@Transactional -@RequiredArgsConstructor -public class MemberActivityService { - - private final MemberActivityRepository memberActivityRepository; - private final ActivityRepository activityRepository; - private final MemberRepository memberRepository; - - public void registerActivities( - final Member member, - final MemberActivityInitialRequest memberActivityInitialRequest - ) { - if (member.isOnboarded()) { - throw new MemberException(MemberExceptionType.ALREADY_ONBOARDING); - } - final List activityIds = memberActivityInitialRequest.getActivityIds(); - saveMemberActivities(member, activityIds); - - member.updateName(memberActivityInitialRequest.getName()); - } - - private void saveMemberActivities(final Member member, final List activityIds) { - final List memberActivities = activityRepository.findAllById(activityIds) - .stream() - .map(it -> new MemberActivity(it, member)) - .collect(toList()); - - validateAllActivityIdsExist(activityIds, memberActivities); - - memberActivityRepository.saveAll(memberActivities); - } - - private void validateAllActivityIdsExist( - final List activityIds, - final List memberActivities - ) { - if (memberActivities.size() != activityIds.size()) { - throw new MemberException(MemberExceptionType.INVALID_ACTIVITY_IDS); - } - } - - public List addActivity( - final Member member, - final MemberActivityAddRequest memberActivityAddRequest - ) { - final List activityIds = memberActivityAddRequest.getActivityIds(); - final List memberActivities = memberActivityRepository.findAllByMember(member); - if (hasDuplicateId(activityIds)) { - throw new MemberException(MemberExceptionType.DUPLICATE_ACTIVITY); - } - if (isAlreadyExistActivity(memberActivities, activityIds)) { - throw new MemberException(MemberExceptionType.ALREADY_EXIST_ACTIVITY); - } - saveMemberActivities(member, activityIds); - - return MemberActivityResponses.from(memberActivityRepository.findAllByMember(member)); - } - - private boolean isAlreadyExistActivity(final List memberActivities, - final List activityIds) { - return memberActivities - .stream() - .anyMatch(memberActivity -> - activityIds.contains(memberActivity.getActivity().getId()) - ); - } - - private boolean hasDuplicateId(final List activityIds) { - return new HashSet<>(activityIds).size() != activityIds.size(); - } - - public List deleteActivity( - final Member member, - final List deleteActivityIds - ) { - final List savedMemberActivityIds = - memberActivityRepository.findAllByMemberAndActivityIds(member, deleteActivityIds) - .stream() - .map(MemberActivity::getId) - .collect(toList()); - - memberActivityRepository.deleteAllByIdInBatch(savedMemberActivityIds); - - return MemberActivityResponses.from(memberActivityRepository.findAllByMember(member)); - } - - @Transactional(readOnly = true) - public List findActivities(final Long memberId) { - final Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberException(MemberExceptionType.NOT_FOUND_MEMBER)); - return MemberActivityResponses.from(memberActivityRepository.findAllByMember(member)); - } -} - diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberActivityResponse.java b/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberActivityResponse.java index a5c59715b..a3e731412 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberActivityResponse.java +++ b/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberActivityResponse.java @@ -1,5 +1,7 @@ package com.emmsale.member.application.dto; +import com.emmsale.member.domain.MemberActivity; +import java.util.List; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -9,4 +11,13 @@ public class MemberActivityResponse { private final Long id; private final String name; + private final String activityType; + + public static MemberActivityResponse from(final MemberActivity memberActivity) { + return new MemberActivityResponse( + memberActivity.getId(), + memberActivity.getActivity().getName(), + memberActivity.getActivity().getActivityType().getValue() + ); + } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberActivityResponses.java b/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberActivityResponses.java deleted file mode 100644 index 299db43bd..000000000 --- a/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberActivityResponses.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.emmsale.member.application.dto; - -import static java.util.Comparator.comparing; -import static java.util.stream.Collectors.groupingBy; -import static java.util.stream.Collectors.toList; - -import com.emmsale.activity.domain.Activity; -import com.emmsale.activity.domain.ActivityType; -import com.emmsale.member.domain.MemberActivity; -import java.util.ArrayList; -import java.util.EnumMap; -import java.util.List; -import java.util.Map.Entry; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@Getter -@RequiredArgsConstructor -public class MemberActivityResponses { - - private final String activityType; - private final List memberActivityResponses; - - public static List from(final List memberActivities) { - final EnumMap> groupByActivityType = - groupingByActivityTypeAndSortedByActivityName(memberActivities); - - final List responses = new ArrayList<>(); - - for (final Entry> entry : groupByActivityType.entrySet()) { - final List activityResponse = - mapToMemberActivityResponses(entry); - - responses.add(new MemberActivityResponses(entry.getKey().getValue(), activityResponse)); - } - - return responses; - } - - private static List mapToMemberActivityResponses( - final Entry> entry - ) { - return entry.getValue() - .stream() - .map(it -> new MemberActivityResponse(it.getId(), it.getName())) - .collect(toList()); - } - - private static EnumMap> groupingByActivityTypeAndSortedByActivityName( - final List memberActivities - ) { - return memberActivities - .stream() - .map(MemberActivity::getActivity) - .sorted(comparing(activity -> activity.getName().toLowerCase())) - .collect( - groupingBy(Activity::getActivityType, () -> new EnumMap<>(ActivityType.class), toList()) - ); - } -} diff --git a/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberActivityCommandServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberActivityCommandServiceTest.java index 4730a665d..f98c10ee5 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberActivityCommandServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberActivityCommandServiceTest.java @@ -10,7 +10,6 @@ import com.emmsale.member.application.dto.MemberActivityAddRequest; import com.emmsale.member.application.dto.MemberActivityInitialRequest; import com.emmsale.member.application.dto.MemberActivityResponse; -import com.emmsale.member.application.dto.MemberActivityResponses; import com.emmsale.member.domain.Member; import com.emmsale.member.domain.MemberRepository; import com.emmsale.member.exception.MemberException; @@ -24,7 +23,7 @@ class MemberActivityCommandServiceTest extends ServiceIntegrationTestHelper { @Autowired - private MemberActivityService memberActivityService; + private MemberActivityCommandService memberActivityCommandService; @Autowired private MemberRepository memberRepository; @@ -44,7 +43,8 @@ void registerActivities() throws Exception { //when & then assertAll( - () -> assertDoesNotThrow(() -> memberActivityService.registerActivities(member, request)), + () -> assertDoesNotThrow( + () -> memberActivityCommandService.registerActivities(member, request)), () -> assertEquals(updateName, member.getName()) ); } @@ -63,8 +63,8 @@ void registerActivities_fail() throws Exception { activityIds); // when - memberActivityService.registerActivities(member, request); - final ThrowingCallable actual = () -> memberActivityService.registerActivities(member, + memberActivityCommandService.registerActivities(member, request); + final ThrowingCallable actual = () -> memberActivityCommandService.registerActivities(member, request); // then @@ -84,29 +84,18 @@ void addActivity() throws Exception { final Member member = memberRepository.findById(savedMemberId).get(); final MemberActivityAddRequest request = new MemberActivityAddRequest(activityIds); - final List expected = List.of( - new MemberActivityResponses("동아리", - List.of( - new MemberActivityResponse(1L, "YAPP"), - new MemberActivityResponse(2L, "DND"), - new MemberActivityResponse(3L, "nexters") - )), - new MemberActivityResponses("컨퍼런스", - List.of( - new MemberActivityResponse(4L, "인프콘") - )), - new MemberActivityResponses("교육", - List.of( - new MemberActivityResponse(5L, "우아한테크코스") - )), - new MemberActivityResponses("직무", - List.of( - new MemberActivityResponse(6L, "Backend") - )) + final List expected = List.of( + new MemberActivityResponse(1L, "YAPP", "동아리"), + new MemberActivityResponse(2L, "DND", "동아리"), + new MemberActivityResponse(3L, "nexters", "동아리"), + new MemberActivityResponse(4L, "인프콘", "컨퍼런스"), + new MemberActivityResponse(5L, "우아한테크코스", "교육"), + new MemberActivityResponse(6L, "Backend", "직무") ); //when - final List actual = memberActivityService.addActivity(member, request); + final List actual = memberActivityCommandService.addActivity(member, + request); //then assertThat(expected) .usingRecursiveComparison() @@ -123,7 +112,7 @@ void test_addActivity_invalid_activity_ids_Exception() throws Exception { final MemberActivityAddRequest request = new MemberActivityAddRequest(activityIds); //when & then - assertThatThrownBy(() -> memberActivityService.addActivity(savedMember, request)) + assertThatThrownBy(() -> memberActivityCommandService.addActivity(savedMember, request)) .isInstanceOf(MemberException.class) .hasMessage(MemberExceptionType.INVALID_ACTIVITY_IDS.errorMessage()); } @@ -137,7 +126,7 @@ void test_addActivity_ALREADY_EXIST_ACTIVITY_Exception_duplicate_try() throws Ex final MemberActivityAddRequest request = new MemberActivityAddRequest(activityIds); // when, then - assertThatThrownBy(() -> memberActivityService.addActivity(savedMember, request)) + assertThatThrownBy(() -> memberActivityCommandService.addActivity(savedMember, request)) .isInstanceOf(MemberException.class) .hasMessage(MemberExceptionType.ALREADY_EXIST_ACTIVITY.errorMessage()); } @@ -151,7 +140,7 @@ void test_addActivity_ALREADY_EXIST_ACTIVITY_Exception_duplicate_input() throws final MemberActivityAddRequest request = new MemberActivityAddRequest(activityIds); // when, then - assertThatThrownBy(() -> memberActivityService.addActivity(savedMember, request)) + assertThatThrownBy(() -> memberActivityCommandService.addActivity(savedMember, request)) .isInstanceOf(MemberException.class) .hasMessage(MemberExceptionType.DUPLICATE_ACTIVITY.errorMessage()); } @@ -165,15 +154,12 @@ void test_deleteActivity() throws Exception { final Member member = memberRepository.findById(savedMemberId).get(); - final List expected = List.of( - new MemberActivityResponses("동아리", - List.of( - new MemberActivityResponse(3L, "nexters") - )) + final List expected = List.of( + new MemberActivityResponse(3L, "nexters", "동아리") ); //when - final List actual = memberActivityService.deleteActivity(member, + final List actual = memberActivityCommandService.deleteActivity(member, deleteActivityIds); //then diff --git a/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberQueryServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberQueryServiceTest.java index e9e3eaf5d..b6a6e05db 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberQueryServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberQueryServiceTest.java @@ -72,7 +72,6 @@ void findProfile_success() { null, "", "https://imageurl.com", - "https://openprofileurl.com", "https://github.com/amaran-th" ); From d1bd971979a7519bc3a4f292ca77193ab7811b23 Mon Sep 17 00:00:00 2001 From: hong-sile Date: Thu, 2 Nov 2023 14:18:03 +0900 Subject: [PATCH 13/41] =?UTF-8?q?feat:=20Member=EC=97=90=EC=84=9C=20openPr?= =?UTF-8?q?ofileUrl=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #811 --- .../java/com/emmsale/MemberApiTest.java | 45 ++++++------------- .../dto/MemberProfileResponse.java | 4 +- 2 files changed, 15 insertions(+), 34 deletions(-) diff --git a/backend/emm-sale/src/documentTest/java/com/emmsale/MemberApiTest.java b/backend/emm-sale/src/documentTest/java/com/emmsale/MemberApiTest.java index 4c8a172ea..509310f6e 100644 --- a/backend/emm-sale/src/documentTest/java/com/emmsale/MemberApiTest.java +++ b/backend/emm-sale/src/documentTest/java/com/emmsale/MemberApiTest.java @@ -21,7 +21,6 @@ import com.emmsale.member.application.dto.MemberActivityAddRequest; import com.emmsale.member.application.dto.MemberActivityInitialRequest; import com.emmsale.member.application.dto.MemberActivityResponse; -import com.emmsale.member.application.dto.MemberActivityResponses; import com.emmsale.member.application.dto.MemberImageResponse; import com.emmsale.member.application.dto.MemberProfileResponse; import com.emmsale.member.application.dto.OpenProfileUrlRequest; @@ -95,9 +94,9 @@ void addActivity() throws Exception { final List activityIds = List.of(4L, 5L, 6L); final MemberActivityAddRequest request = new MemberActivityAddRequest(activityIds); - final List memberActivityResponses = createMemberActivityResponses(); + final List memberActivityResponses = createMemberActivityResponses(); - when(memberActivityService.addActivity(any(), any())) + when(memberActivityCommandService.addActivity(any(), any())) .thenReturn(memberActivityResponses); //when & then @@ -111,26 +110,14 @@ void addActivity() throws Exception { MEMBER_ACTIVITY_RESPONSE_FIELDS)); } - private List createMemberActivityResponses() { + private List createMemberActivityResponses() { return List.of( - new MemberActivityResponses("동아리", - List.of( - new MemberActivityResponse(1L, "YAPP"), - new MemberActivityResponse(2L, "DND"), - new MemberActivityResponse(3L, "nexters") - )), - new MemberActivityResponses("컨퍼런스", - List.of( - new MemberActivityResponse(4L, "인프콘") - )), - new MemberActivityResponses("교육", - List.of( - new MemberActivityResponse(5L, "우아한테크코스") - )), - new MemberActivityResponses("직무", - List.of( - new MemberActivityResponse(6L, "Backend") - )) + new MemberActivityResponse(1L, "YAPP", "동아리"), + new MemberActivityResponse(2L, "DND", "동아리"), + new MemberActivityResponse(3L, "nexters", "동아리"), + new MemberActivityResponse(4L, "인프콘", "컨퍼런스"), + new MemberActivityResponse(5L, "우아한테크코스", "교육"), + new MemberActivityResponse(6L, "Backend", "직무") ); } @@ -140,14 +127,11 @@ void test_deleteActivity() throws Exception { //given final String activityIds = "1,2"; - final List memberActivityResponses = List.of( - new MemberActivityResponses("동아리", - List.of( - new MemberActivityResponse(3L, "nexters") - )) + final List memberActivityResponses = List.of( + new MemberActivityResponse(3L, "nexters", "동아리") ); - when(memberActivityService.deleteActivity(any(), any())) + when(memberActivityCommandService.deleteActivity(any(), any())) .thenReturn(memberActivityResponses); //when & then @@ -164,10 +148,10 @@ void test_deleteActivity() throws Exception { @DisplayName("내 활동들을 조회할 수 있다.") void test_findActivity() throws Exception { //given - final List memberActivityResponse = createMemberActivityResponses(); + final List memberActivityResponse = createMemberActivityResponses(); //when - when(memberActivityService.findActivities(any())) + when(memberActivityQueryService.findActivities(any())) .thenReturn(memberActivityResponse); //then @@ -233,7 +217,6 @@ void test_findProfile() throws Exception { "김길동", "안녕하세요, 김길동입니다.", "https://image", - "https://open.profile.url", "https://github.com/amaran-th" ); when(memberQueryService.findProfile(any())) diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberProfileResponse.java b/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberProfileResponse.java index 298736a47..e802bffb9 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberProfileResponse.java +++ b/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberProfileResponse.java @@ -14,16 +14,14 @@ public class MemberProfileResponse { private final String name; private final String description; private final String imageUrl; - private final String openProfileUrl; private final String githubUrl; - public static MemberProfileResponse from(Member member) { + public static MemberProfileResponse from(final Member member) { return new MemberProfileResponse( member.getId(), member.getName(), member.getDescription(), member.getImageUrl(), - member.getOptionalOpenProfileUrl().orElse(""), GITHUB_URL_PREFIX + member.getGithubUsername() ); } From 16508effdd53e1f5f699dc5f99360507f3b571ea Mon Sep 17 00:00:00 2001 From: hong-sile Date: Thu, 2 Nov 2023 14:22:38 +0900 Subject: [PATCH 14/41] =?UTF-8?q?test:=20=EB=AC=B8=EC=84=9C=ED=99=94=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=97=90=EC=84=9C=20responsefileds?= =?UTF-8?q?=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #811 --- .../java/com/emmsale/MemberApiTest.java | 31 ++----------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/backend/emm-sale/src/documentTest/java/com/emmsale/MemberApiTest.java b/backend/emm-sale/src/documentTest/java/com/emmsale/MemberApiTest.java index 509310f6e..f2cbe32bd 100644 --- a/backend/emm-sale/src/documentTest/java/com/emmsale/MemberApiTest.java +++ b/backend/emm-sale/src/documentTest/java/com/emmsale/MemberApiTest.java @@ -23,7 +23,6 @@ import com.emmsale.member.application.dto.MemberActivityResponse; import com.emmsale.member.application.dto.MemberImageResponse; import com.emmsale.member.application.dto.MemberProfileResponse; -import com.emmsale.member.application.dto.OpenProfileUrlRequest; import com.emmsale.member.domain.Member; import java.io.IOException; import java.util.List; @@ -45,17 +44,14 @@ class MemberApiTest extends MockMvcTestHelper { private static final ResponseFieldsSnippet MEMBER_ACTIVITY_RESPONSE_FIELDS = responseFields( fieldWithPath("[].activityType").type(JsonFieldType.STRING).description("activity 분류"), - fieldWithPath("[].memberActivityResponses[].id").type(JsonFieldType.NUMBER) - .description("activity id"), - fieldWithPath("[].memberActivityResponses[].name").type(JsonFieldType.STRING) - .description("activity 이름") + fieldWithPath("[].id").type(JsonFieldType.NUMBER).description("activity id"), + fieldWithPath("[].name").type(JsonFieldType.STRING).description("activity 이름") ); private static final ResponseFieldsSnippet MEMBER_PROFILE_RESPONSE_FIELDS = responseFields( fieldWithPath("id").type(JsonFieldType.NUMBER).description("사용자 id"), fieldWithPath("name").type(JsonFieldType.STRING).description("사용자 이름"), fieldWithPath("description").type(JsonFieldType.STRING).description("사용자 한 줄 자기소개"), fieldWithPath("imageUrl").type(JsonFieldType.STRING).description("사용자 프로필 이미지 url"), - fieldWithPath("openProfileUrl").type(JsonFieldType.STRING).description("오픈 프로필 url"), fieldWithPath("githubUrl").type(JsonFieldType.STRING).description("깃허브 URL") ); private static final RequestFieldsSnippet MEMBER_ACTIVITY_REQUEST_FIELDS = requestFields( @@ -162,29 +158,6 @@ void test_findActivity() throws Exception { .andDo(document("find-activity", MEMBER_ACTIVITY_RESPONSE_FIELDS)); } - @Test - @DisplayName("사용자의 openProfileUrl을 성공적으로 업데이트하면, 200 OK가 반환된다.") - void test_updateOpenProfileUrl() throws Exception { - // given - final String openProfileUrl = "https://open.kakao.com/o/openprofileurl"; - final OpenProfileUrlRequest request = new OpenProfileUrlRequest(openProfileUrl); - - final RequestFieldsSnippet REQUEST_FIELDS = requestFields( - fieldWithPath("openProfileUrl").description("오픈 채팅 url") - ); - - // when - final ResultActions result = mockMvc.perform(put("/members/open-profile-url") - .header(HttpHeaders.AUTHORIZATION, FAKE_BEARER_ACCESS_TOKEN) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(request))); - - // then - result.andExpect(status().isNoContent()) - .andDo(print()) - .andDo(document("update-open-profile-url", REQUEST_FIELDS)); - } - @Test @DisplayName("사용자의 description을 성공적으로 업데이트하면, 200 OK가 반환된다.") void test_updateDescription() throws Exception { From e1e24e25d66966a49489da628ca8e2a63fe2f7b4 Mon Sep 17 00:00:00 2001 From: hong-sile Date: Thu, 2 Nov 2023 14:36:34 +0900 Subject: [PATCH 15/41] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #811 --- .../application/MemberActivityCommandServiceTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberActivityCommandServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberActivityCommandServiceTest.java index f98c10ee5..c78514045 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberActivityCommandServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberActivityCommandServiceTest.java @@ -76,7 +76,7 @@ void registerActivities_fail() throws Exception { @Test @DisplayName("Activity의 id를 통해서, 사용자의 Activity에 추가할 수 있다.") - void addActivity() throws Exception { + void addActivity() { //given final List activityIds = List.of(4L, 5L, 6L); final long savedMemberId = 1L; @@ -88,9 +88,9 @@ void addActivity() throws Exception { new MemberActivityResponse(1L, "YAPP", "동아리"), new MemberActivityResponse(2L, "DND", "동아리"), new MemberActivityResponse(3L, "nexters", "동아리"), - new MemberActivityResponse(4L, "인프콘", "컨퍼런스"), - new MemberActivityResponse(5L, "우아한테크코스", "교육"), - new MemberActivityResponse(6L, "Backend", "직무") + new MemberActivityResponse(5L, "인프콘", "컨퍼런스"), + new MemberActivityResponse(6L, "우아한테크코스", "교육"), + new MemberActivityResponse(7L, "Backend", "직무") ); //when From cd7920e5305795a3958ce947df532e9edbbf8f81 Mon Sep 17 00:00:00 2001 From: amaran-th Date: Thu, 2 Nov 2023 15:10:05 +0900 Subject: [PATCH 16/41] =?UTF-8?q?refactor:=20=ED=96=89=EC=82=AC=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=EC=9D=84=20=EB=B6=88=EB=9F=AC=EC=98=AC=20?= =?UTF-8?q?=EB=96=84=20=EC=9D=B4=EB=AF=B8=EC=A7=80=EB=A5=BC=20=EB=B6=88?= =?UTF-8?q?=EB=9F=AC=EC=98=A4=EB=A9=B4=EC=84=9C=20=EB=B0=9C=EC=83=9D?= =?UTF-8?q?=ED=95=98=EB=8A=94=20N+1=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 스크랩 행사 목록 조회 시 행사마다 image 조회 쿼리가 나가는 대신 행사들의 이미지를 모두 불러온 후 그룹핑되도록 수정 - 코드 중복을 없애기 위한 ImageQueryService 추가 #803 --- .../event/application/EventQueryService.java | 32 +++++--------- .../image/application/ImageQueryService.java | 42 +++++++++++++++++++ .../domain/repository/ImageRepository.java | 2 + .../scrap/application/ScrapQueryService.java | 24 +++-------- 4 files changed, 59 insertions(+), 41 deletions(-) create mode 100644 backend/emm-sale/src/main/java/com/emmsale/image/application/ImageQueryService.java diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventQueryService.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventQueryService.java index e8f444e34..58b34d0fc 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventQueryService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventQueryService.java @@ -17,9 +17,9 @@ import com.emmsale.event.domain.repository.EventRepository; import com.emmsale.event.exception.EventException; import com.emmsale.event.exception.EventExceptionType; +import com.emmsale.image.application.ImageQueryService; import com.emmsale.image.domain.AllImagesOfContent; import com.emmsale.image.domain.ImageType; -import com.emmsale.image.domain.repository.ImageRepository; import com.emmsale.tag.domain.Tag; import com.emmsale.tag.domain.TagRepository; import com.emmsale.tag.exception.TagException; @@ -28,7 +28,6 @@ import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.EnumMap; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -47,15 +46,14 @@ public class EventQueryService { private final EventRepository eventRepository; private final TagRepository tagRepository; - private final ImageRepository imageRepository; + private final ImageQueryService imageQueryService; public EventResponse findEvent(final Long id, final LocalDate today) { final Event event = eventRepository.findById(id) .orElseThrow(() -> new EventException(NOT_FOUND_EVENT)); - final AllImagesOfContent images = new AllImagesOfContent( - imageRepository.findAllByTypeAndContentId(ImageType.EVENT, event.getId()) - ); + final AllImagesOfContent images = imageQueryService.findImagesOfContent(ImageType.EVENT, + event.getId()); return EventResponse.from(event, images); } @@ -73,7 +71,12 @@ public List findEvents(final EventType category, final EnumMap> eventsForEventStatus = groupByEventStatus(nowDateTime, events); - return filterByStatuses(statuses, eventsForEventStatus, makeImagesPerEventId(events)); + final List eventIds = events.stream() + .map(Event::getId) + .collect(Collectors.toUnmodifiableList()); + + return filterByStatuses(statuses, eventsForEventStatus, + imageQueryService.findImagesPerContentId(ImageType.EVENT, eventIds)); } private Specification filterByCategoryIfExist(final EventType category, @@ -205,19 +208,4 @@ private List filterEventResponseByStatuses( return combinedEvents; }); } - - // TODO: 2023/09/27 코드 중복 제거(ScrapService) - private Map makeImagesPerEventId(final List events) { - final List eventIds = events.stream() - .map(Event::getId) - .collect(Collectors.toUnmodifiableList()); - - Map imagesPerEventId = new HashMap<>(); - for (Long eventId : eventIds) { - final AllImagesOfContent images = new AllImagesOfContent( - imageRepository.findAllByTypeAndContentId(ImageType.EVENT, eventId)); - imagesPerEventId.put(eventId, images); - } - return imagesPerEventId; - } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/image/application/ImageQueryService.java b/backend/emm-sale/src/main/java/com/emmsale/image/application/ImageQueryService.java new file mode 100644 index 000000000..d19e1c783 --- /dev/null +++ b/backend/emm-sale/src/main/java/com/emmsale/image/application/ImageQueryService.java @@ -0,0 +1,42 @@ +package com.emmsale.image.application; + +import static java.util.stream.Collectors.groupingBy; + +import com.emmsale.image.domain.AllImagesOfContent; +import com.emmsale.image.domain.Image; +import com.emmsale.image.domain.ImageType; +import com.emmsale.image.domain.repository.ImageRepository; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +@Transactional +public class ImageQueryService { + + private final ImageRepository imageRepository; + + public Map findImagesPerContentId(final ImageType imageType, + final List eventIds) { + final Map> imagesPerEventId = imageRepository.findAllByTypeAndContentIdIn( + imageType, eventIds) + .stream() + .collect(groupingBy(Image::getContentId)); + Map result = new HashMap<>(); + eventIds.forEach(key -> + result.put(key, + new AllImagesOfContent(imagesPerEventId.getOrDefault(key, new ArrayList<>()))) + ); + return result; + } + + public AllImagesOfContent findImagesOfContent(final ImageType imageType, final Long eventId) { + return new AllImagesOfContent(imageRepository.findAllByTypeAndContentId(imageType, eventId)); + } + +} diff --git a/backend/emm-sale/src/main/java/com/emmsale/image/domain/repository/ImageRepository.java b/backend/emm-sale/src/main/java/com/emmsale/image/domain/repository/ImageRepository.java index 09e6721d9..acc4d380c 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/image/domain/repository/ImageRepository.java +++ b/backend/emm-sale/src/main/java/com/emmsale/image/domain/repository/ImageRepository.java @@ -10,6 +10,8 @@ public interface ImageRepository extends JpaRepository { List findAllByTypeAndContentId(final ImageType type, final Long contentId); + List findAllByTypeAndContentIdIn(final ImageType type, final List contentIds); + @Query("select i from Image i where i.type='EVENT' and i.order=0 and i.contentId in :eventIds") List findAllThumbnailByEventIdIn(final List eventIds); diff --git a/backend/emm-sale/src/main/java/com/emmsale/scrap/application/ScrapQueryService.java b/backend/emm-sale/src/main/java/com/emmsale/scrap/application/ScrapQueryService.java index f53f05466..375f8d2fc 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/scrap/application/ScrapQueryService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/scrap/application/ScrapQueryService.java @@ -5,14 +5,12 @@ import com.emmsale.event.application.dto.EventResponse; import com.emmsale.event.domain.Event; import com.emmsale.event.domain.EventStatus; -import com.emmsale.image.domain.AllImagesOfContent; +import com.emmsale.image.application.ImageQueryService; import com.emmsale.image.domain.ImageType; -import com.emmsale.image.domain.repository.ImageRepository; import com.emmsale.member.domain.Member; import com.emmsale.scrap.domain.Scrap; import com.emmsale.scrap.domain.ScrapRepository; import java.time.LocalDateTime; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -26,7 +24,7 @@ public class ScrapQueryService { private final ScrapRepository scrapRepository; - private final ImageRepository imageRepository; + private final ImageQueryService imageQueryService; public List findAllScraps(final Member member) { //TODO : Scrap에서 event 사용해서 N+1 발생 @@ -38,23 +36,11 @@ public List findAllScraps(final Member member) { final Map> eventGroupByStatus = scrappedEvents.stream() .collect( groupingBy(event -> event.getEventPeriod().calculateEventStatus(LocalDateTime.now()))); - - return EventResponse.mergeEventResponses(eventGroupByStatus, - makeImageUrlsPerEventId(scrappedEvents)); - } - - private Map makeImageUrlsPerEventId(final List events) { - final List eventIds = events.stream() + final List eventIds = scrappedEvents.stream() .map(Event::getId) .collect(Collectors.toUnmodifiableList()); - - final Map imageUrlsPerEventId = new HashMap<>(); - for (Long eventId : eventIds) { - final AllImagesOfContent images = new AllImagesOfContent( - imageRepository.findAllByTypeAndContentId(ImageType.EVENT, eventId)); - imageUrlsPerEventId.put(eventId, images); - } - return imageUrlsPerEventId; + return EventResponse.mergeEventResponses(eventGroupByStatus, + imageQueryService.findImagesPerContentId(ImageType.EVENT, eventIds)); } } From bdee3ce9405170c39100e16ecd2ded48a910e551 Mon Sep 17 00:00:00 2001 From: amaran-th Date: Thu, 2 Nov 2023 15:25:29 +0900 Subject: [PATCH 17/41] =?UTF-8?q?refactor:=20=ED=96=89=EC=82=AC=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20=EC=8B=9C=20tag=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=EB=A1=9C=20=EC=9D=B8=ED=95=B4=20=EB=B0=9C?= =?UTF-8?q?=EC=83=9D=ED=95=98=EB=8A=94=20N+1=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #803 --- .../event/application/EventCommandService.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventCommandService.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventCommandService.java index a9146d1d5..c958f360a 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventCommandService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventCommandService.java @@ -89,10 +89,17 @@ private List findAllPersistTagsOrElseThrow(final List tags) { if (tags == null || tags.isEmpty()) { return new ArrayList<>(); } - - return tags.stream() - .map(tag -> tagRepository.findByName(tag.getName()) - .orElseThrow(() -> new EventException(EventExceptionType.NOT_FOUND_TAG))) + final List tagNames = tags.stream() + .map(TagRequest::getName) .collect(toList()); + final List result = tagRepository.findByNameIn(tagNames); + validateAllTagsExist(tags, result); + return result; + } + + private void validateAllTagsExist(final List tags, final List result) { + if (tags.size() != result.size()) { + throw new EventException(EventExceptionType.NOT_FOUND_TAG); + } } } From b9ca8c90016927eea32c574ef327db02fd13f5f5 Mon Sep 17 00:00:00 2001 From: amaran-th Date: Thu, 2 Nov 2023 15:27:52 +0900 Subject: [PATCH 18/41] =?UTF-8?q?refactor:=20=EB=88=84=EB=9D=BD=EB=90=9C?= =?UTF-8?q?=20final=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #803 --- .../java/com/emmsale/image/application/ImageQueryService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/emm-sale/src/main/java/com/emmsale/image/application/ImageQueryService.java b/backend/emm-sale/src/main/java/com/emmsale/image/application/ImageQueryService.java index d19e1c783..fb6801007 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/image/application/ImageQueryService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/image/application/ImageQueryService.java @@ -27,7 +27,7 @@ public Map findImagesPerContentId(final ImageType imag imageType, eventIds) .stream() .collect(groupingBy(Image::getContentId)); - Map result = new HashMap<>(); + final Map result = new HashMap<>(); eventIds.forEach(key -> result.put(key, new AllImagesOfContent(imagesPerEventId.getOrDefault(key, new ArrayList<>()))) From 6602615e2bc98f33f59b220afc24a85085c693b7 Mon Sep 17 00:00:00 2001 From: amaran-th Date: Thu, 2 Nov 2023 15:32:06 +0900 Subject: [PATCH 19/41] =?UTF-8?q?refactor:=20QueryService=EC=9D=98=20@Tran?= =?UTF-8?q?sactional=EC=97=90=20readOnly=20=EC=86=8D=EC=84=B1=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #803 --- .../application/ImageCommandService.java | 22 +++++++++---------- .../image/application/ImageQueryService.java | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/backend/emm-sale/src/main/java/com/emmsale/image/application/ImageCommandService.java b/backend/emm-sale/src/main/java/com/emmsale/image/application/ImageCommandService.java index 50912d857..1adcbb1c9 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/image/application/ImageCommandService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/image/application/ImageCommandService.java @@ -15,9 +15,9 @@ import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; -import javax.transaction.Transactional; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; @Service @@ -25,19 +25,19 @@ @Transactional public class ImageCommandService { // TODO: 2023/09/14 put 메서드 구현 - + private final S3Client s3Client; private final ImageRepository imageRepository; private final EventRepository eventRepository; private final FeedRepository feedRepository; - + public List saveImages(final ImageType imageType, final Long contentId, final List multipartFiles) { validateContentExist(imageType, contentId); validateImageCount(imageType, multipartFiles); - + final List imageNames = s3Client.uploadImages(multipartFiles); - + try { return saveImagesToDb(imageType, contentId, imageNames); } catch (Exception exception) { @@ -46,7 +46,7 @@ public List saveImages(final ImageType imageType, final Long contentId, throw new ImageException(ImageExceptionType.FAIL_DB_UPLOAD_IMAGE); } } - + private void validateContentExist(final ImageType imageType, final Long contentId) { if (imageType == ImageType.EVENT) { validateEventExist(contentId); @@ -55,26 +55,26 @@ private void validateContentExist(final ImageType imageType, final Long contentI validateFeedExist(contentId); } } - + private void validateEventExist(final Long contentId) { if (!eventRepository.existsById(contentId)) { throw new EventException(EventExceptionType.NOT_FOUND_EVENT); } } - + private void validateFeedExist(final Long contentId) { if (!feedRepository.existsById(contentId)) { throw new FeedException(FeedExceptionType.NOT_FOUND_FEED); } } - + private void validateImageCount(final ImageType imageType, final List multipartFiles) { if (imageType.isOverMaxImageCount(multipartFiles.size())) { throw new ImageException(ImageExceptionType.OVER_MAX_IMAGE_COUNT); } } - + private List saveImagesToDb(final ImageType imageType, final Long contentId, final List imageNames) { final List images = new ArrayList<>(); @@ -85,7 +85,7 @@ private List saveImagesToDb(final ImageType imageType, final Long content } return images; } - + public void deleteImages(final ImageType imageType, final Long contentId) { final List images = imageRepository.findAllByTypeAndContentId(imageType, contentId); final List imageIds = images.stream() diff --git a/backend/emm-sale/src/main/java/com/emmsale/image/application/ImageQueryService.java b/backend/emm-sale/src/main/java/com/emmsale/image/application/ImageQueryService.java index fb6801007..a035615aa 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/image/application/ImageQueryService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/image/application/ImageQueryService.java @@ -10,13 +10,13 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.transaction.Transactional; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service @RequiredArgsConstructor -@Transactional +@Transactional(readOnly = true) public class ImageQueryService { private final ImageRepository imageRepository; From 946308d1dc92a8a45233e7ca467165e758b1358d Mon Sep 17 00:00:00 2001 From: amaran-th Date: Sun, 5 Nov 2023 05:14:31 +0900 Subject: [PATCH 20/41] =?UTF-8?q?refactor:=20=EC=8A=A4=ED=81=AC=EB=9E=A9?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80/=EC=82=AD=EC=A0=9C=20=EC=8B=9C=20?= =?UTF-8?q?=EC=8A=A4=ED=81=AC=EB=9E=A9=ED=95=9C=20=ED=96=89=EC=82=AC=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=EB=A5=BC=20=EB=B0=98=ED=99=98?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #823 --- .../java/com/emmsale/ScrapApiTest.java | 69 +++++++++++++++++-- .../java/com/emmsale/scrap/api/ScrapApi.java | 8 +-- .../application/ScrapCommandService.java | 16 ++++- 3 files changed, 81 insertions(+), 12 deletions(-) diff --git a/backend/emm-sale/src/documentTest/java/com/emmsale/ScrapApiTest.java b/backend/emm-sale/src/documentTest/java/com/emmsale/ScrapApiTest.java index af0f65739..c8ada91e4 100644 --- a/backend/emm-sale/src/documentTest/java/com/emmsale/ScrapApiTest.java +++ b/backend/emm-sale/src/documentTest/java/com/emmsale/ScrapApiTest.java @@ -1,7 +1,6 @@ package com.emmsale; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.when; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.delete; @@ -32,6 +31,31 @@ @WebMvcTest(ScrapApi.class) class ScrapApiTest extends MockMvcTestHelper { + final ResponseFieldsSnippet SCRAPPED_EVENT_RESPONSE_FIELDS = PayloadDocumentation.responseFields( + fieldWithPath("id").type(JsonFieldType.NUMBER).description("행사 식별자"), + fieldWithPath("name").type(JsonFieldType.STRING) + .description("행사 이름"), + fieldWithPath("informationUrl").type(JsonFieldType.STRING) + .description("행사 상세정보 url"), + fieldWithPath("startDate").type(JsonFieldType.STRING) + .description("행사 시작 일자"), + fieldWithPath("endDate").type(JsonFieldType.STRING).description("행사 종료 일자"), + fieldWithPath("applyStartDate").type(JsonFieldType.STRING) + .description("행사 신청 시작 일자(nullable)"), + fieldWithPath("applyEndDate").type(JsonFieldType.STRING) + .description("행사 신청 종료 일자(nullable)"), + fieldWithPath("location").type(JsonFieldType.STRING).description("행사 장소"), + fieldWithPath("tags[]").type(JsonFieldType.ARRAY).description("행사 태그들"), + fieldWithPath("thumbnailUrl").type(JsonFieldType.STRING) + .description("행사 섬네일 이미지 Url(포스터)"), + fieldWithPath("type").type(JsonFieldType.STRING) + .description("행사의 분류"), + fieldWithPath("imageUrls[]").description("행사의 상세 정보 이미지 URL들").optional(), + fieldWithPath("organization").description("행사 주최기관"), + fieldWithPath("paymentType").description("행사의 유무료 여부(유료,무료,유무료)"), + fieldWithPath("eventMode").description("행사의 온/오프라인 여부(온라인,오프라인,온오프라인)") + ); + @Test @DisplayName("스크랩 목록을 성공적으로 조회하면 200 OK를 반환한다.") void findAllScraps() throws Exception { @@ -129,13 +153,29 @@ void append() throws Exception { final long eventId = 1L; final ScrapRequest request = new ScrapRequest(eventId); - + final EventResponse expectedScrapResponse = new EventResponse( + 1L, + "인프콘 2023", + "https://aaa", + LocalDateTime.parse("2023-06-03T12:00:00"), + LocalDateTime.parse("2023-09-03T12:00:00"), + LocalDateTime.parse("2023-09-01T00:00:00"), + LocalDateTime.parse("2023-09-02T23:59:59"), + "코엑스", + List.of("백엔드", "프론트엔드", "안드로이드", "IOS", "AI"), + "image0.jpg", + EventType.CONFERENCE.name(), + List.of("image1.jpg", "image2.jpg", "image3.jpg"), + "인프런", + PaymentType.PAID.getValue(), + EventMode.ONLINE.getValue() + ); final RequestFieldsSnippet requestFields = requestFields( fieldWithPath("eventId").description("스크랩할 이벤트 id") ); //when - doNothing().when(scrapCommandService).append(any(), any()); + when(scrapCommandService.append(any(), any())).thenReturn(expectedScrapResponse); //then mockMvc.perform(post("/scraps") @@ -143,7 +183,7 @@ void append() throws Exception { .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request))) .andExpect(status().isCreated()) - .andDo(document("append-scrap", requestFields)); + .andDo(document("append-scrap", requestFields, SCRAPPED_EVENT_RESPONSE_FIELDS)); } @Test @@ -151,14 +191,31 @@ void append() throws Exception { void deleteScrap() throws Exception { //given final long eventId = 1L; + final EventResponse expectedScrapResponse = new EventResponse( + 1L, + "인프콘 2023", + "https://aaa", + LocalDateTime.parse("2023-06-03T12:00:00"), + LocalDateTime.parse("2023-09-03T12:00:00"), + LocalDateTime.parse("2023-09-01T00:00:00"), + LocalDateTime.parse("2023-09-02T23:59:59"), + "코엑스", + List.of("백엔드", "프론트엔드", "안드로이드", "IOS", "AI"), + "image0.jpg", + EventType.CONFERENCE.name(), + List.of("image1.jpg", "image2.jpg", "image3.jpg"), + "인프런", + PaymentType.PAID.getValue(), + EventMode.ONLINE.getValue() + ); //when - doNothing().when(scrapCommandService).deleteScrap(any(), any()); + when(scrapCommandService.deleteScrap(any(), any())).thenReturn(expectedScrapResponse); //then mockMvc.perform(delete("/scraps?event-id={eventId}", eventId) .header(HttpHeaders.AUTHORIZATION, "Bearer AccessToken")) .andExpect(status().isNoContent()) - .andDo(document("delete-scrap")); + .andDo(document("delete-scrap", SCRAPPED_EVENT_RESPONSE_FIELDS)); } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/scrap/api/ScrapApi.java b/backend/emm-sale/src/main/java/com/emmsale/scrap/api/ScrapApi.java index ce89d8fe2..51c697f3e 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/scrap/api/ScrapApi.java +++ b/backend/emm-sale/src/main/java/com/emmsale/scrap/api/ScrapApi.java @@ -33,14 +33,14 @@ public List findAllScraps(final Member member) { @PostMapping @ResponseStatus(HttpStatus.CREATED) - public void append(final Member member, @RequestBody final ScrapRequest scrapRequest) { - scrapCommandService.append(member, scrapRequest); + public EventResponse append(final Member member, @RequestBody final ScrapRequest scrapRequest) { + return scrapCommandService.append(member, scrapRequest); } @DeleteMapping @ResponseStatus(HttpStatus.NO_CONTENT) - public void delete(final Member member, @RequestParam("event-id") final Long eventId) { - scrapCommandService.deleteScrap(member, eventId); + public EventResponse delete(final Member member, @RequestParam("event-id") final Long eventId) { + return scrapCommandService.deleteScrap(member, eventId); } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/scrap/application/ScrapCommandService.java b/backend/emm-sale/src/main/java/com/emmsale/scrap/application/ScrapCommandService.java index 5e2bcccb6..5756e8ddb 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/scrap/application/ScrapCommandService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/scrap/application/ScrapCommandService.java @@ -1,9 +1,12 @@ package com.emmsale.scrap.application; +import com.emmsale.event.application.dto.EventResponse; import com.emmsale.event.domain.Event; import com.emmsale.event.domain.repository.EventRepository; import com.emmsale.event.exception.EventException; import com.emmsale.event.exception.EventExceptionType; +import com.emmsale.image.application.ImageQueryService; +import com.emmsale.image.domain.ImageType; import com.emmsale.member.domain.Member; import com.emmsale.scrap.application.dto.ScrapRequest; import com.emmsale.scrap.domain.Scrap; @@ -21,8 +24,10 @@ public class ScrapCommandService { private final ScrapRepository scrapRepository; private final EventRepository eventRepository; + private final ImageQueryService imageQueryService; - public void append(final Member member, final ScrapRequest scrapRequest) { + + public EventResponse append(final Member member, final ScrapRequest scrapRequest) { final Long memberId = member.getId(); final Long eventId = scrapRequest.getEventId(); @@ -32,10 +37,17 @@ public void append(final Member member, final ScrapRequest scrapRequest) { .orElseThrow(() -> new EventException(EventExceptionType.NOT_FOUND_EVENT)); scrapRepository.save(new Scrap(memberId, event)); + return EventResponse.from(event, + imageQueryService.findImagesOfContent(ImageType.EVENT, event.getId())); } - public void deleteScrap(final Member member, final Long eventId) { + public EventResponse deleteScrap(final Member member, final Long eventId) { + final Event event = eventRepository.findById(eventId) + .orElseThrow(() -> new EventException(EventExceptionType.NOT_FOUND_EVENT)); scrapRepository.deleteByMemberIdAndEventId(member.getId(), eventId); + return EventResponse.from(event, + imageQueryService.findImagesOfContent(ImageType.EVENT, eventId)); + } private void validateAlreadyScraped(final Long memberId, final Long eventId) { From 6ecb9f92dd1e472e2a1ce8edf59f62a9b8d3ec3f Mon Sep 17 00:00:00 2001 From: amaran-th Date: Sun, 5 Nov 2023 07:11:27 +0900 Subject: [PATCH 21/41] =?UTF-8?q?refactor:=20=EC=8A=A4=ED=81=AC=EB=9E=A9?= =?UTF-8?q?=20=EC=82=AD=EC=A0=9C=20=EC=8B=9C=20=EC=8A=A4=ED=81=AC=EB=9E=A9?= =?UTF-8?q?=ED=95=9C=20=ED=96=89=EC=82=AC=20=EB=8D=B0=EC=9D=B4=ED=84=B0?= =?UTF-8?q?=EB=A5=BC=20=EB=B0=98=ED=99=98=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #823 --- .../src/documentTest/java/com/emmsale/ScrapApiTest.java | 1 - .../emm-sale/src/main/java/com/emmsale/scrap/api/ScrapApi.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/emm-sale/src/documentTest/java/com/emmsale/ScrapApiTest.java b/backend/emm-sale/src/documentTest/java/com/emmsale/ScrapApiTest.java index c8ada91e4..e784bb06e 100644 --- a/backend/emm-sale/src/documentTest/java/com/emmsale/ScrapApiTest.java +++ b/backend/emm-sale/src/documentTest/java/com/emmsale/ScrapApiTest.java @@ -151,7 +151,6 @@ void findAllScraps() throws Exception { void append() throws Exception { //given final long eventId = 1L; - final ScrapRequest request = new ScrapRequest(eventId); final EventResponse expectedScrapResponse = new EventResponse( 1L, diff --git a/backend/emm-sale/src/main/java/com/emmsale/scrap/api/ScrapApi.java b/backend/emm-sale/src/main/java/com/emmsale/scrap/api/ScrapApi.java index 51c697f3e..7cc928110 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/scrap/api/ScrapApi.java +++ b/backend/emm-sale/src/main/java/com/emmsale/scrap/api/ScrapApi.java @@ -38,7 +38,7 @@ public EventResponse append(final Member member, @RequestBody final ScrapRequest } @DeleteMapping - @ResponseStatus(HttpStatus.NO_CONTENT) + @ResponseStatus(HttpStatus.OK) public EventResponse delete(final Member member, @RequestParam("event-id") final Long eventId) { return scrapCommandService.deleteScrap(member, eventId); } From cdf1f0f6bce9027ea7e9586c2b4b80f2e7358fb8 Mon Sep 17 00:00:00 2001 From: amaran-th Date: Sun, 5 Nov 2023 07:12:09 +0900 Subject: [PATCH 22/41] =?UTF-8?q?docs:=20scrap=20=EA=B4=80=EB=A0=A8=20api?= =?UTF-8?q?=20=EC=9A=94=EC=B2=AD=20http=20=ED=8C=8C=EC=9D=BC=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #823 --- .../src/main/resources/http/scrap.http | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 backend/emm-sale/src/main/resources/http/scrap.http diff --git a/backend/emm-sale/src/main/resources/http/scrap.http b/backend/emm-sale/src/main/resources/http/scrap.http new file mode 100644 index 000000000..d509767b3 --- /dev/null +++ b/backend/emm-sale/src/main/resources/http/scrap.http @@ -0,0 +1,21 @@ +### 스크랩 조회 + +GET http://localhost:8080/scraps +Content-Type: application/json +Authorization: bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwiaWF0IjoxNjk5MTI5NDAwLCJleHAiOjE3MDI3Mjk0MDB9.Gokmyr--11BIQCXHYv8_M156ngoZ6cIs9Exk7EEbwd0 + +### 스크랩 추가 + +POST http://localhost:8080/scraps +Content-Type: application/json +Authorization: bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwiaWF0IjoxNjk5MTI5NDAwLCJleHAiOjE3MDI3Mjk0MDB9.Gokmyr--11BIQCXHYv8_M156ngoZ6cIs9Exk7EEbwd0 + +{ + "eventId": 1 +} + +### 스크랩 삭제 + +DELETE http://localhost:8080/scraps?event-id=1 +Content-Type: application/json +Authorization: bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwiaWF0IjoxNjk5MTI5NDAwLCJleHAiOjE3MDI3Mjk0MDB9.Gokmyr--11BIQCXHYv8_M156ngoZ6cIs9Exk7EEbwd0 From b448b1950f59f984072563d791a375d0094c9039 Mon Sep 17 00:00:00 2001 From: hong-sile Date: Sun, 5 Nov 2023 16:05:57 +0900 Subject: [PATCH 23/41] =?UTF-8?q?refactor:=20MemberActivityRepository=20N+?= =?UTF-8?q?1=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #819 --- .../emmsale/member/domain/MemberActivityRepository.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/domain/MemberActivityRepository.java b/backend/emm-sale/src/main/java/com/emmsale/member/domain/MemberActivityRepository.java index c0c855fd0..50e141218 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/member/domain/MemberActivityRepository.java +++ b/backend/emm-sale/src/main/java/com/emmsale/member/domain/MemberActivityRepository.java @@ -7,8 +7,13 @@ public interface MemberActivityRepository extends JpaRepository { - @Query("select mc from MemberActivity mc where mc.member = :member") - List findAllByMember(@Param("member") final Member member); + @Query("select mc from MemberActivity mc " + + "join fetch Activity a " + + "on a = mc.activity " + + "where mc.member = :member ") + List findAllByMember( + @Param("member") final Member member + ); @Query("select mc from MemberActivity mc " + "where mc.member = :member " From 75cd54270b10da4b8e9b84a30f6f6826d7ea235f Mon Sep 17 00:00:00 2001 From: hong-sile Date: Sun, 5 Nov 2023 16:06:30 +0900 Subject: [PATCH 24/41] =?UTF-8?q?feat:=20MemberDetailResponse=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #819 --- .../application/MemberQueryService.java | 7 ++-- .../application/dto/MemberDetailResponse.java | 41 +++++++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberDetailResponse.java diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberQueryService.java b/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberQueryService.java index 37fae32fa..5fc1a93a7 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberQueryService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberQueryService.java @@ -6,6 +6,7 @@ import com.emmsale.login.application.dto.MemberQueryResponse; import com.emmsale.member.application.dto.MemberProfileResponse; import com.emmsale.member.domain.Member; +import com.emmsale.member.domain.MemberActivityRepository; import com.emmsale.member.domain.MemberRepository; import com.emmsale.member.exception.MemberException; import lombok.RequiredArgsConstructor; @@ -18,6 +19,7 @@ public class MemberQueryService { private final MemberRepository memberRepository; + private final MemberActivityRepository memberActivityRepository; public Member findById(final Long memberId) { return memberRepository.findById(memberId) @@ -34,9 +36,8 @@ public MemberQueryResponse findOrCreateMember( return new MemberQueryResponse(member.getId(), member.isOnboarded()); } - public MemberProfileResponse findProfile(Long memberId) { - final Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberException(NOT_FOUND_MEMBER)); + public MemberProfileResponse findProfile(final Long memberId) { + final Member member = memberRepository.getByIdOrElseThrow(memberId); return MemberProfileResponse.from(member); } diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberDetailResponse.java b/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberDetailResponse.java new file mode 100644 index 000000000..f5c9e82ae --- /dev/null +++ b/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberDetailResponse.java @@ -0,0 +1,41 @@ +package com.emmsale.member.application.dto; + +import static java.util.stream.Collectors.toUnmodifiableList; + +import com.emmsale.member.domain.Member; +import com.emmsale.member.domain.MemberActivity; +import java.util.List; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public class MemberDetailResponse { + + private static final String GITHUB_URL_PREFIX = "https://github.com/"; + + private final Long id; + private final String name; + private final String description; + private final String imageUrl; + private final String githubUrl; + private final List activities; + + public static MemberDetailResponse from( + final Member member, + final List activities + ) { + final List memberActivityResponses = activities.stream() + .map(MemberActivityResponse::from) + .collect(toUnmodifiableList()); + + return new MemberDetailResponse( + member.getId(), + member.getName(), + member.getDescription(), + member.getImageUrl(), + GITHUB_URL_PREFIX + member.getGithubUsername(), + memberActivityResponses + ); + } +} From 59198e68a6fd3a257113978292cacd3a5851a9d3 Mon Sep 17 00:00:00 2001 From: hong-sile Date: Sun, 5 Nov 2023 16:20:01 +0900 Subject: [PATCH 25/41] =?UTF-8?q?refactor:=20MemberDetailResponse=EB=A5=BC?= =?UTF-8?q?=20=EB=B0=98=ED=99=98=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #819 --- .../java/com/emmsale/MemberApiTest.java | 16 ++++++++++++---- .../com/emmsale/member/api/MemberApi.java | 3 ++- .../application/MemberQueryService.java | 9 ++++++--- .../dto/MemberActivityResponse.java | 9 +++++++++ .../application/dto/MemberDetailResponse.java | 4 +++- .../application/MemberQueryServiceTest.java | 19 +++++++++++++------ 6 files changed, 45 insertions(+), 15 deletions(-) diff --git a/backend/emm-sale/src/documentTest/java/com/emmsale/MemberApiTest.java b/backend/emm-sale/src/documentTest/java/com/emmsale/MemberApiTest.java index f2cbe32bd..74ee8554c 100644 --- a/backend/emm-sale/src/documentTest/java/com/emmsale/MemberApiTest.java +++ b/backend/emm-sale/src/documentTest/java/com/emmsale/MemberApiTest.java @@ -21,8 +21,8 @@ import com.emmsale.member.application.dto.MemberActivityAddRequest; import com.emmsale.member.application.dto.MemberActivityInitialRequest; import com.emmsale.member.application.dto.MemberActivityResponse; +import com.emmsale.member.application.dto.MemberDetailResponse; import com.emmsale.member.application.dto.MemberImageResponse; -import com.emmsale.member.application.dto.MemberProfileResponse; import com.emmsale.member.domain.Member; import java.io.IOException; import java.util.List; @@ -52,7 +52,12 @@ class MemberApiTest extends MockMvcTestHelper { fieldWithPath("name").type(JsonFieldType.STRING).description("사용자 이름"), fieldWithPath("description").type(JsonFieldType.STRING).description("사용자 한 줄 자기소개"), fieldWithPath("imageUrl").type(JsonFieldType.STRING).description("사용자 프로필 이미지 url"), - fieldWithPath("githubUrl").type(JsonFieldType.STRING).description("깃허브 URL") + fieldWithPath("githubUrl").type(JsonFieldType.STRING).description("깃허브 URL"), + fieldWithPath("activities[].id").type(JsonFieldType.NUMBER).description("MemberActivity Id"), + fieldWithPath("activities[].name").type(JsonFieldType.STRING) + .description("MemberActivity 이름"), + fieldWithPath("activities[].activityType").type(JsonFieldType.STRING) + .description("MemberActivity Type") ); private static final RequestFieldsSnippet MEMBER_ACTIVITY_REQUEST_FIELDS = requestFields( fieldWithPath("activityIds").description("활동 id들")); @@ -185,12 +190,15 @@ void test_updateDescription() throws Exception { @DisplayName("특정 사용자의 프로필 정보를 조회할 수 있다.") void test_findProfile() throws Exception { //given - final MemberProfileResponse memberProfileResponse = new MemberProfileResponse( + final MemberDetailResponse memberProfileResponse = new MemberDetailResponse( 1L, "김길동", "안녕하세요, 김길동입니다.", "https://image", - "https://github.com/amaran-th" + "https://github.com/amaran-th", + List.of( + new MemberActivityResponse(1L, "YAPP", "동아리") + ) ); when(memberQueryService.findProfile(any())) .thenReturn(memberProfileResponse); diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/api/MemberApi.java b/backend/emm-sale/src/main/java/com/emmsale/member/api/MemberApi.java index f3d72ec15..169c83404 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/member/api/MemberApi.java +++ b/backend/emm-sale/src/main/java/com/emmsale/member/api/MemberApi.java @@ -8,6 +8,7 @@ import com.emmsale.member.application.dto.MemberActivityAddRequest; import com.emmsale.member.application.dto.MemberActivityInitialRequest; import com.emmsale.member.application.dto.MemberActivityResponse; +import com.emmsale.member.application.dto.MemberDetailResponse; import com.emmsale.member.application.dto.MemberImageResponse; import com.emmsale.member.application.dto.MemberProfileResponse; import com.emmsale.member.domain.Member; @@ -77,7 +78,7 @@ public ResponseEntity updateDescription( } @GetMapping("/members/{member-id}") - public ResponseEntity findProfile( + public ResponseEntity findProfile( @PathVariable("member-id") final Long memberId) { return ResponseEntity.ok(memberQueryService.findProfile(memberId)); } diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberQueryService.java b/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberQueryService.java index 5fc1a93a7..03f09a408 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberQueryService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberQueryService.java @@ -4,11 +4,13 @@ import com.emmsale.login.application.dto.GithubProfileResponse; import com.emmsale.login.application.dto.MemberQueryResponse; -import com.emmsale.member.application.dto.MemberProfileResponse; +import com.emmsale.member.application.dto.MemberDetailResponse; import com.emmsale.member.domain.Member; +import com.emmsale.member.domain.MemberActivity; import com.emmsale.member.domain.MemberActivityRepository; import com.emmsale.member.domain.MemberRepository; import com.emmsale.member.exception.MemberException; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -36,9 +38,10 @@ public MemberQueryResponse findOrCreateMember( return new MemberQueryResponse(member.getId(), member.isOnboarded()); } - public MemberProfileResponse findProfile(final Long memberId) { + public MemberDetailResponse findProfile(final Long memberId) { final Member member = memberRepository.getByIdOrElseThrow(memberId); + final List memberActivities = memberActivityRepository.findAllByMember(member); - return MemberProfileResponse.from(member); + return MemberDetailResponse.of(member,memberActivities); } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberActivityResponse.java b/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberActivityResponse.java index a3e731412..531b1c54f 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberActivityResponse.java +++ b/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberActivityResponse.java @@ -20,4 +20,13 @@ public static MemberActivityResponse from(final MemberActivity memberActivity) { memberActivity.getActivity().getActivityType().getValue() ); } + + @Override + public String toString() { + return "MemberActivityResponse{" + + "id=" + id + + ", name='" + name + '\'' + + ", activityType='" + activityType + '\'' + + '}'; + } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberDetailResponse.java b/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberDetailResponse.java index f5c9e82ae..3c45006fe 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberDetailResponse.java +++ b/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberDetailResponse.java @@ -7,9 +7,11 @@ import java.util.List; import lombok.Getter; import lombok.RequiredArgsConstructor; +import lombok.ToString; @RequiredArgsConstructor @Getter +@ToString public class MemberDetailResponse { private static final String GITHUB_URL_PREFIX = "https://github.com/"; @@ -21,7 +23,7 @@ public class MemberDetailResponse { private final String githubUrl; private final List activities; - public static MemberDetailResponse from( + public static MemberDetailResponse of( final Member member, final List activities ) { diff --git a/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberQueryServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberQueryServiceTest.java index b6a6e05db..de995f6c0 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberQueryServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberQueryServiceTest.java @@ -6,9 +6,11 @@ import com.emmsale.helper.ServiceIntegrationTestHelper; import com.emmsale.login.application.dto.GithubProfileResponse; import com.emmsale.login.application.dto.MemberQueryResponse; -import com.emmsale.member.application.dto.MemberProfileResponse; +import com.emmsale.member.application.dto.MemberActivityResponse; +import com.emmsale.member.application.dto.MemberDetailResponse; import com.emmsale.member.exception.MemberException; import com.emmsale.member.exception.MemberExceptionType; +import java.util.List; import org.assertj.core.api.ThrowableAssert.ThrowingCallable; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -67,22 +69,27 @@ class FindProfile { void findProfile_success() { //given final Long memberId = 1L; - final MemberProfileResponse expectResponse = new MemberProfileResponse( + final MemberDetailResponse expect = new MemberDetailResponse( memberId, null, "", "https://imageurl.com", - "https://github.com/amaran-th" + "https://github.com/amaran-th", + List.of( + new MemberActivityResponse(1L, "YAPP", "동아리"), + new MemberActivityResponse(2L, "DND", "동아리"), + new MemberActivityResponse(3L, "nexters", "동아리") + ) ); //when - final MemberProfileResponse actualResponse = memberQueryService.findProfile(memberId); + final MemberDetailResponse actual = memberQueryService.findProfile(memberId); //then - assertThat(actualResponse) + assertThat(actual) .usingRecursiveComparison() .ignoringFields("description") - .isEqualTo(expectResponse); + .isEqualTo(expect); } @Test From ed0e7a41266bbabec92a2b59989390fae04c5fc8 Mon Sep 17 00:00:00 2001 From: hong-sile Date: Sun, 5 Nov 2023 16:21:03 +0900 Subject: [PATCH 26/41] =?UTF-8?q?refactor:=20=ED=95=84=EC=9A=94=20?= =?UTF-8?q?=EC=97=86=EB=8A=94=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #819 --- .../com/emmsale/member/api/MemberApi.java | 1 - .../dto/MemberActivityResponse.java | 9 ------ .../application/dto/MemberDetailResponse.java | 2 -- .../dto/MemberProfileResponse.java | 28 ------------------- 4 files changed, 40 deletions(-) delete mode 100644 backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberProfileResponse.java diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/api/MemberApi.java b/backend/emm-sale/src/main/java/com/emmsale/member/api/MemberApi.java index 169c83404..6bf9b1958 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/member/api/MemberApi.java +++ b/backend/emm-sale/src/main/java/com/emmsale/member/api/MemberApi.java @@ -10,7 +10,6 @@ import com.emmsale.member.application.dto.MemberActivityResponse; import com.emmsale.member.application.dto.MemberDetailResponse; import com.emmsale.member.application.dto.MemberImageResponse; -import com.emmsale.member.application.dto.MemberProfileResponse; import com.emmsale.member.domain.Member; import java.util.List; import lombok.RequiredArgsConstructor; diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberActivityResponse.java b/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberActivityResponse.java index 531b1c54f..a3e731412 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberActivityResponse.java +++ b/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberActivityResponse.java @@ -20,13 +20,4 @@ public static MemberActivityResponse from(final MemberActivity memberActivity) { memberActivity.getActivity().getActivityType().getValue() ); } - - @Override - public String toString() { - return "MemberActivityResponse{" + - "id=" + id + - ", name='" + name + '\'' + - ", activityType='" + activityType + '\'' + - '}'; - } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberDetailResponse.java b/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberDetailResponse.java index 3c45006fe..ba3d54e13 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberDetailResponse.java +++ b/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberDetailResponse.java @@ -7,11 +7,9 @@ import java.util.List; import lombok.Getter; import lombok.RequiredArgsConstructor; -import lombok.ToString; @RequiredArgsConstructor @Getter -@ToString public class MemberDetailResponse { private static final String GITHUB_URL_PREFIX = "https://github.com/"; diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberProfileResponse.java b/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberProfileResponse.java deleted file mode 100644 index e802bffb9..000000000 --- a/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberProfileResponse.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.emmsale.member.application.dto; - -import com.emmsale.member.domain.Member; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@Getter -@RequiredArgsConstructor -public class MemberProfileResponse { - - private static final String GITHUB_URL_PREFIX = "https://github.com/"; - - private final Long id; - private final String name; - private final String description; - private final String imageUrl; - private final String githubUrl; - - public static MemberProfileResponse from(final Member member) { - return new MemberProfileResponse( - member.getId(), - member.getName(), - member.getDescription(), - member.getImageUrl(), - GITHUB_URL_PREFIX + member.getGithubUsername() - ); - } -} From 009adecaf1b1ef43fb1db5962997741b61e671ea Mon Sep 17 00:00:00 2001 From: amaran-th Date: Sun, 5 Nov 2023 17:42:53 +0900 Subject: [PATCH 27/41] =?UTF-8?q?refactor:=20Message=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=20api=20=EB=B0=98=ED=99=98=20=EB=AA=85=EC=84=B8=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 - Message가 id와 sender(MemberReferenceResponse)를 추가로 반환하도록 수정 - Room이 Message와 interlocutor(MemberReferenceResponse)를 추가로 반환하도록 수정 #821 --- .../java/com/emmsale/RoomApiTest.java | 97 +++++++++++++------ .../MessageNotificationEvent.java | 2 +- .../application/MessageCommandService.java | 2 +- .../application/RoomQueryService.java | 6 +- .../application/dto/MessageResponse.java | 7 +- .../application/dto/RoomResponse.java | 25 +++-- .../emmsale/message_room/domain/Message.java | 23 ++++- .../persistence/MessageDao.java | 3 +- .../persistence/dto/MessageOverview.java | 1 + .../event_publisher/EventPublisherTest.java | 5 +- .../MessageCommandServiceTest.java | 9 +- .../application/RoomQueryServiceTest.java | 45 +++++---- .../domain/MessageRepositoryTest.java | 8 +- .../persistence/MessageDaoTest.java | 13 +-- 14 files changed, 156 insertions(+), 90 deletions(-) diff --git a/backend/emm-sale/src/documentTest/java/com/emmsale/RoomApiTest.java b/backend/emm-sale/src/documentTest/java/com/emmsale/RoomApiTest.java index 5ddc36255..3036b2a58 100644 --- a/backend/emm-sale/src/documentTest/java/com/emmsale/RoomApiTest.java +++ b/backend/emm-sale/src/documentTest/java/com/emmsale/RoomApiTest.java @@ -13,9 +13,12 @@ import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import com.emmsale.member.application.dto.MemberReferenceResponse; +import com.emmsale.member.domain.Member; import com.emmsale.message_room.api.RoomApi; import com.emmsale.message_room.application.dto.MessageResponse; import com.emmsale.message_room.application.dto.RoomResponse; +import com.emmsale.message_room.domain.Message; import java.time.LocalDateTime; import java.util.List; import java.util.UUID; @@ -23,6 +26,7 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.http.HttpHeaders; +import org.springframework.restdocs.payload.JsonFieldType; import org.springframework.restdocs.payload.ResponseFieldsSnippet; import org.springframework.restdocs.request.PathParametersSnippet; import org.springframework.restdocs.request.RequestParametersSnippet; @@ -30,7 +34,17 @@ @WebMvcTest(RoomApi.class) class RoomApiTest extends MockMvcTestHelper { - private String accessToken = "Bearer AccessToken"; + private static final ResponseFieldsSnippet MESSAGES_RESPONSE_FIELDS = responseFields( + fieldWithPath("[].id").description("메시지의 ID"), + fieldWithPath("[].sender.id").description("메시지를 보낸 사람 ID"), + fieldWithPath("[].sender.name").description("메시지를 보낸 사람의 이름"), + fieldWithPath("[].sender.description").description("메시지를 보낸 사람의 한 줄 자기소개"), + fieldWithPath("[].sender.imageUrl").description("메시지를 보낸 사람의 프로필 이미지 URL"), + fieldWithPath("[].sender.githubUrl").description("메시지를 보낸 사람의 Github ID"), + fieldWithPath("[].content").description("메시지 내용"), + fieldWithPath("[].createdAt").description("메시지 보낸 시간") + ); + private static final String accessToken = "Bearer AccessToken"; @Test @DisplayName("findAllRoom() : 사용자의 쪽지함을 성공적으로 조회하면 200 OK를 반환할 수 있다.") @@ -42,20 +56,47 @@ void test_findAllRoom() throws Exception { final ResponseFieldsSnippet responseFields = responseFields( fieldWithPath("[].roomId").description("Room Id(String 타입의 UUID입니다)"), - fieldWithPath("[].interlocutorId").description("쪽지를 주고받는 상대방 ID"), - fieldWithPath("[].interlocutorName").description("쪽지를 주고받는 상대방의 이름"), - fieldWithPath("[].interlocutorProfile").description("쪽지를 주고받는 상대방 이미지 URL"), - fieldWithPath("[].recentlyMessage").description("최근 메시지 내용"), - fieldWithPath("[].recentlyMessageTime").description("최근 메시지 시간") + fieldWithPath("[].interlocutor.id").type(JsonFieldType.NUMBER) + .description("메세지를 주고받는 member의 식별자"), + fieldWithPath("[].interlocutor.name").type(JsonFieldType.STRING) + .description("메세지를 주고받는 member의 이름"), + fieldWithPath("[].interlocutor.description").type(JsonFieldType.STRING) + .description("메세지를 주고받는 member의 한줄 자기소개"), + fieldWithPath("[].interlocutor.imageUrl").type(JsonFieldType.STRING) + .description("메세지를 주고받는 member의 이미지 url"), + fieldWithPath("[].interlocutor.githubUrl").type(JsonFieldType.STRING) + .description("메세지를 주고받는 member의 github Url"), + fieldWithPath("[].recentlyMessage.id").description("최근 메시지 내용"), + fieldWithPath("[].recentlyMessage.content").description("최근 메시지 내용"), + fieldWithPath("[].recentlyMessage.sender.id").type(JsonFieldType.NUMBER) + .description("최근 메세지를 전송한 member의 식별자"), + fieldWithPath("[].recentlyMessage.sender.name").type(JsonFieldType.STRING) + .description("최근 메세지를 전송한 member의 이름"), + fieldWithPath("[].recentlyMessage.sender.description").type(JsonFieldType.STRING) + .description("최근 메세지를 전송한 member의 한줄 자기소개"), + fieldWithPath("[].recentlyMessage.sender.imageUrl").type(JsonFieldType.STRING) + .description("최근 메세지를 전송한 member의 이미지 url"), + fieldWithPath("[].recentlyMessage.sender.githubUrl").type(JsonFieldType.STRING) + .description("최근 메세지를 전송한 member의 github Url"), + fieldWithPath("[].recentlyMessage.createdAt").description("최근 메시지 시간") ); + final Member member1 = new Member(1L, 3L, "https://github.image", "receiver1", "amaran-th"); + final Member member2 = new Member(2L, 4L, "https://github.image2", "receiver2", "amaran-th2"); + final Member member3 = new Member(3L, 8L, "https://github.image2", "receiver3", "amaran-th3"); + final Message message1 = new Message(1L, "최근 메시지1", member1, "ROOMID1111", LocalDateTime.now()); + final Message message2 = new Message(2L, "최근 메시지2", member2, "ROOMID2222", + LocalDateTime.now().minusDays(2)); + final Message message3 = new Message(3L, "최근 메시지3", member3, "ROOMID3333", + LocalDateTime.now().minusDays(3)); + final List roomResponses = List.of( - new RoomResponse(UUID.randomUUID().toString(), 1L, "receiver1", "imageUrl1", "최근 메시지1", - LocalDateTime.now()), - new RoomResponse(UUID.randomUUID().toString(), 2L, "receiver2", "imageUrl2", "최근 메시지2", - LocalDateTime.now().minusDays(2)), - new RoomResponse(UUID.randomUUID().toString(), 1L, "receiver3", "imageUrl3", "최근 메시지3", - LocalDateTime.now().minusDays(3)) + new RoomResponse(UUID.randomUUID().toString(), MemberReferenceResponse.from(member1), + MessageResponse.from(message1)), + new RoomResponse(UUID.randomUUID().toString(), MemberReferenceResponse.from(member2), + MessageResponse.from(message2)), + new RoomResponse(UUID.randomUUID().toString(), MemberReferenceResponse.from(member3), + MessageResponse.from(message3)) ); when(roomQueryService.findAll(any(), anyLong())) @@ -78,20 +119,15 @@ void test_findByRoomId() throws Exception { parameterWithName("member-id").description("로그인 한 사용자 ID") ); - final ResponseFieldsSnippet responseFields = responseFields( - fieldWithPath("[].senderId").description("메시지를 보낸 사람 ID"), - fieldWithPath("[].content").description("메시지 내용"), - fieldWithPath("[].createdAt").description("메시지 보낸 시간") - ); - final PathParametersSnippet pathParams = pathParameters( parameterWithName("room-id").description("조회할 Room UUID") ); - + final Member member1 = new Member(1L, 3L, "https://github.image", "sender", "amaran-th"); + final Member member2 = new Member(2L, 4L, "https://github.image", "receiver", "amaran-th"); final List messageResponses = List.of( - new MessageResponse(1L, "내용1", LocalDateTime.now()), - new MessageResponse(2L, "내용2", LocalDateTime.now()), - new MessageResponse(1L, "내용3", LocalDateTime.now()) + new MessageResponse(1L, MemberReferenceResponse.from(member1), "내용1", LocalDateTime.now()), + new MessageResponse(2L, MemberReferenceResponse.from(member2), "내용2", LocalDateTime.now()), + new MessageResponse(3L, MemberReferenceResponse.from(member1), "내용3", LocalDateTime.now()) ); when(roomQueryService.findByRoomId(any(), any(), anyLong())) @@ -103,7 +139,7 @@ void test_findByRoomId() throws Exception { .header(HttpHeaders.AUTHORIZATION, accessToken)) .andExpect(status().isOk()) .andDo(print()) - .andDo(document("get-rooms-roomId", requestParam, responseFields, pathParams)); + .andDo(document("get-rooms-roomId", requestParam, MESSAGES_RESPONSE_FIELDS, pathParams)); } @Test @@ -115,16 +151,13 @@ void test_findByInterlocutorIds() throws Exception { parameterWithName("receiver-id").description("쪽지방 참여 상대방 ID") ); - final ResponseFieldsSnippet responseFields = responseFields( - fieldWithPath("[].senderId").description("메시지를 보낸 사람 ID"), - fieldWithPath("[].content").description("메시지 내용"), - fieldWithPath("[].createdAt").description("메시지 보낸 시간") - ); + final Member member1 = new Member(1L, 3L, "https://github.image", "sender", "amaran-th"); + final Member member2 = new Member(2L, 4L, "https://github.image", "receiver", "amaran-th"); final List messageResponses = List.of( - new MessageResponse(1L, "내용1", LocalDateTime.now()), - new MessageResponse(2L, "내용2", LocalDateTime.now()), - new MessageResponse(1L, "내용3", LocalDateTime.now()) + new MessageResponse(1L, MemberReferenceResponse.from(member1), "내용1", LocalDateTime.now()), + new MessageResponse(2L, MemberReferenceResponse.from(member2), "내용2", LocalDateTime.now()), + new MessageResponse(3L, MemberReferenceResponse.from(member1), "내용3", LocalDateTime.now()) ); when(roomQueryService.findByInterlocutorIds(anyLong(), anyLong(), any())) @@ -137,6 +170,6 @@ void test_findByInterlocutorIds() throws Exception { .header(HttpHeaders.AUTHORIZATION, accessToken)) .andExpect(status().isOk()) .andDo(print()) - .andDo(document("get-rooms-interlocutorId", requestParam, responseFields)); + .andDo(document("get-rooms-interlocutorId", requestParam, MESSAGES_RESPONSE_FIELDS)); } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/event_publisher/MessageNotificationEvent.java b/backend/emm-sale/src/main/java/com/emmsale/event_publisher/MessageNotificationEvent.java index 47b16ead6..235b6de6b 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event_publisher/MessageNotificationEvent.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event_publisher/MessageNotificationEvent.java @@ -19,7 +19,7 @@ public static MessageNotificationEvent of(final Message message, final Long rece return new MessageNotificationEvent( message.getRoomId(), message.getContent(), - message.getSenderId(), + message.getSender().getId(), receiverId, message.getCreatedAt() ); diff --git a/backend/emm-sale/src/main/java/com/emmsale/message_room/application/MessageCommandService.java b/backend/emm-sale/src/main/java/com/emmsale/message_room/application/MessageCommandService.java index e379d989c..bb5a92323 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/message_room/application/MessageCommandService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/message_room/application/MessageCommandService.java @@ -39,7 +39,7 @@ public MessageSendResponse sendMessage(final MessageSendRequest request, final M .map(room -> room.getRoomId().getUuid()) .orElseGet(() -> saveRooms(request)); - final Message message = new Message(request.getContent(), request.getSenderId(), + final Message message = new Message(request.getContent(), member, roomId, LocalDateTime.now()); messageRepository.save(message); diff --git a/backend/emm-sale/src/main/java/com/emmsale/message_room/application/RoomQueryService.java b/backend/emm-sale/src/main/java/com/emmsale/message_room/application/RoomQueryService.java index e415ee31f..04be29708 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/message_room/application/RoomQueryService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/message_room/application/RoomQueryService.java @@ -38,7 +38,8 @@ public List findAll(final Member loginMember, final Long memberId) final Long loginMemberId = loginMember.getId(); - final List messageOverviews = messageDao.findRecentlyMessages(loginMemberId); + final List messageOverviews = messageDao.findRecentlyMessages( + loginMemberId); final Map interlocutorIdPerRoomExceptMe = groupingInterlocutorIdByRoom( messageOverviews, @@ -49,6 +50,9 @@ public List findAll(final Member loginMember, final Long memberId) .map(messageOverview -> RoomResponse.from( messageOverview, + memberRepository.findById(messageOverview.getSenderId()) + .orElseThrow(() -> new MemberException(NOT_FOUND_MEMBER)), + // TODO: 2023/11/05 N+1 문제 findInterlocutor(interlocutorIdPerRoomExceptMe.get(messageOverview.getRoomUUID())) )) .collect(Collectors.toList()); diff --git a/backend/emm-sale/src/main/java/com/emmsale/message_room/application/dto/MessageResponse.java b/backend/emm-sale/src/main/java/com/emmsale/message_room/application/dto/MessageResponse.java index 8faac4fbf..f3b44e4b0 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/message_room/application/dto/MessageResponse.java +++ b/backend/emm-sale/src/main/java/com/emmsale/message_room/application/dto/MessageResponse.java @@ -1,5 +1,6 @@ package com.emmsale.message_room.application.dto; +import com.emmsale.member.application.dto.MemberReferenceResponse; import com.emmsale.message_room.domain.Message; import com.fasterxml.jackson.annotation.JsonFormat; import java.time.LocalDateTime; @@ -10,14 +11,16 @@ @Getter public class MessageResponse { - private final Long senderId; + private final Long id; + private final MemberReferenceResponse sender; private final String content; @JsonFormat(pattern = "yyyy:MM:dd:HH:mm:ss") private final LocalDateTime createdAt; public static MessageResponse from(final Message message) { return new MessageResponse( - message.getSenderId(), + message.getId(), + MemberReferenceResponse.from(message.getSender()), message.getContent(), message.getCreatedAt() ); diff --git a/backend/emm-sale/src/main/java/com/emmsale/message_room/application/dto/RoomResponse.java b/backend/emm-sale/src/main/java/com/emmsale/message_room/application/dto/RoomResponse.java index 34f70a1a2..22cd71023 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/message_room/application/dto/RoomResponse.java +++ b/backend/emm-sale/src/main/java/com/emmsale/message_room/application/dto/RoomResponse.java @@ -1,9 +1,9 @@ package com.emmsale.message_room.application.dto; +import com.emmsale.member.application.dto.MemberReferenceResponse; import com.emmsale.member.domain.Member; +import com.emmsale.message_room.domain.Message; import com.emmsale.message_room.infrastructure.persistence.dto.MessageOverview; -import com.fasterxml.jackson.annotation.JsonFormat; -import java.time.LocalDateTime; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -12,24 +12,23 @@ public class RoomResponse { private final String roomId; - private final Long interlocutorId; - private final String interlocutorName; - private final String interlocutorProfile; - private final String recentlyMessage; - @JsonFormat(pattern = "yyyy:MM:dd:HH:mm:ss") - private final LocalDateTime recentlyMessageTime; + private final MemberReferenceResponse interlocutor; + private final MessageResponse recentlyMessage; public static RoomResponse from( final MessageOverview messageOverview, + final Member recentMessageSender, final Member interlocutor ) { return new RoomResponse( messageOverview.getRoomUUID(), - interlocutor.getId(), - interlocutor.getName(), - interlocutor.getImageUrl(), - messageOverview.getContent(), - messageOverview.getCreatedAt() + MemberReferenceResponse.from(interlocutor), + MessageResponse.from(new Message( + messageOverview.getId(), + messageOverview.getContent(), + recentMessageSender, + messageOverview.getRoomUUID(), + messageOverview.getCreatedAt())) ); } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/message_room/domain/Message.java b/backend/emm-sale/src/main/java/com/emmsale/message_room/domain/Message.java index 2c72f890a..329809bf1 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/message_room/domain/Message.java +++ b/backend/emm-sale/src/main/java/com/emmsale/message_room/domain/Message.java @@ -1,11 +1,14 @@ package com.emmsale.message_room.domain; +import com.emmsale.member.domain.Member; import java.time.LocalDateTime; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -22,8 +25,9 @@ public class Message { @Column(nullable = false) private String content; - @Column(nullable = false) - private Long senderId; + @ManyToOne + @JoinColumn(nullable = false) + private Member sender; @Column(nullable = false) private String roomId; @@ -31,14 +35,25 @@ public class Message { private LocalDateTime createdAt; public Message( + final Long id, final String content, - final Long senderId, + final Member sender, final String roomId, final LocalDateTime createdAt ) { + this.id = id; this.content = content; - this.senderId = senderId; + this.sender = sender; this.roomId = roomId; this.createdAt = createdAt; } + + public Message( + final String content, + final Member sender, + final String roomId, + final LocalDateTime createdAt + ) { + this(null, content, sender, roomId, createdAt); + } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/message_room/infrastructure/persistence/MessageDao.java b/backend/emm-sale/src/main/java/com/emmsale/message_room/infrastructure/persistence/MessageDao.java index 521157ef2..7627785a5 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/message_room/infrastructure/persistence/MessageDao.java +++ b/backend/emm-sale/src/main/java/com/emmsale/message_room/infrastructure/persistence/MessageDao.java @@ -16,6 +16,7 @@ public class MessageDao { new MessageOverview( rs.getLong("id"), rs.getString("content"), + rs.getLong("sender_id"), rs.getTimestamp("created_at").toLocalDateTime(), rs.getString("room_id") ); @@ -26,7 +27,7 @@ public List findRecentlyMessages(final Long memberId) { StringBuilder sqlBuilder = new StringBuilder(); sqlBuilder - .append("SELECT m2.id, m2.content, m2.created_at, m2.room_id ") + .append("SELECT m2.id, m2.content, m2.sender_id, m2.created_at, m2.room_id ") .append("FROM room r ") .append("JOIN (SELECT room_id, MAX(created_at) as max_created_at ") .append(" FROM message ") diff --git a/backend/emm-sale/src/main/java/com/emmsale/message_room/infrastructure/persistence/dto/MessageOverview.java b/backend/emm-sale/src/main/java/com/emmsale/message_room/infrastructure/persistence/dto/MessageOverview.java index a4e86966a..f31bc0fd8 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/message_room/infrastructure/persistence/dto/MessageOverview.java +++ b/backend/emm-sale/src/main/java/com/emmsale/message_room/infrastructure/persistence/dto/MessageOverview.java @@ -10,6 +10,7 @@ public class MessageOverview { private final Long id; private final String content; + private final Long senderId; private final LocalDateTime createdAt; private final String roomUUID; } diff --git a/backend/emm-sale/src/test/java/com/emmsale/event_publisher/EventPublisherTest.java b/backend/emm-sale/src/test/java/com/emmsale/event_publisher/EventPublisherTest.java index 5cb32927c..f622e7158 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/event_publisher/EventPublisherTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/event_publisher/EventPublisherTest.java @@ -312,7 +312,8 @@ void test_publish_comment_not_notification_deletedComment2() throws Exception { @DisplayName("publish() : message를 받아서, Event로 변환 후 publish한다.") void publishMessageEvent() { //given - final Message message = new Message("content", 1L, "roomId", LocalDateTime.now()); + final Member sender = memberRepository.findById(1L).get(); + final Message message = new Message("content", sender, "roomId", LocalDateTime.now()); final Long receiverId = 2L; //when @@ -328,7 +329,7 @@ void publishMessageEvent() { final MessageNotificationEvent expectedEvent = new MessageNotificationEvent( message.getRoomId(), message.getContent(), - message.getSenderId(), + message.getSender().getId(), receiverId, message.getCreatedAt() ); diff --git a/backend/emm-sale/src/test/java/com/emmsale/message_room/application/MessageCommandServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/message_room/application/MessageCommandServiceTest.java index fac690350..9a89d68d3 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/message_room/application/MessageCommandServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/message_room/application/MessageCommandServiceTest.java @@ -60,14 +60,15 @@ void alreadyRoomExist() { )); final MessageSendRequest request = new MessageSendRequest(requesterId, receiverId, content); - final Message expected = new Message(content, requesterId, roomUUID, LocalDateTime.now()); + final Message expected = new Message(content, member, roomUUID, LocalDateTime.now()); //when messageCommandService.sendMessage(request, member); //then assertThat(messageRepository.findAll()) - .usingRecursiveFieldByFieldElementComparatorIgnoringFields("id", "createdAt") + .usingRecursiveFieldByFieldElementComparatorIgnoringFields("id", "createdAt", + "sender.createdAt", "sender.updatedAt") .containsExactlyInAnyOrder(expected); } @@ -81,7 +82,7 @@ void notExistRoom() { final String content = "메시지 내용"; final MessageSendRequest request = new MessageSendRequest(requesterId, receiverId, content); - final Message expected = new Message(content, requesterId, null, LocalDateTime.now()); + final Message expected = new Message(content, member, null, LocalDateTime.now()); //when messageCommandService.sendMessage(request, member); @@ -90,7 +91,7 @@ void notExistRoom() { assertAll( () -> assertThat(messageRepository.findAll()) .usingRecursiveFieldByFieldElementComparatorIgnoringFields("id", "createdAt", - "roomId") + "roomId", "sender.createdAt", "sender.updatedAt") .containsExactly(expected), () -> verify(firebaseCloudMessageClient, times(1)) .sendMessageTo(any(MessageNotificationEvent.class)) diff --git a/backend/emm-sale/src/test/java/com/emmsale/message_room/application/RoomQueryServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/message_room/application/RoomQueryServiceTest.java index 3de1b97be..aaa5c6719 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/message_room/application/RoomQueryServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/message_room/application/RoomQueryServiceTest.java @@ -4,6 +4,7 @@ import com.emmsale.helper.ServiceIntegrationTestHelper; import com.emmsale.member.MemberFixture; +import com.emmsale.member.application.dto.MemberReferenceResponse; import com.emmsale.member.domain.Member; import com.emmsale.member.domain.MemberRepository; import com.emmsale.message_room.application.dto.MessageResponse; @@ -79,26 +80,26 @@ void test_findAll() throws Exception { //given final Message resultMessage1 = new Message( "방1메시지3", - loginMember.getId(), + loginMember, room1UUID, LocalDateTime.parse("2023-10-07T16:45:39") ); final Message resultMessage2 = new Message( "방2메시지4", - room2Interlocutor.getId(), + room2Interlocutor, room2UUID, LocalDateTime.parse("2023-10-07T16:45:39") ); messageRepository.saveAll( List.of( - new Message("방1메시지1", loginMember.getId(), room1UUID, + new Message("방1메시지1", loginMember, room1UUID, LocalDateTime.parse("2023-05-07T16:45:39")), - new Message("방1메시지2", loginMember.getId(), room1UUID, + new Message("방1메시지2", loginMember, room1UUID, LocalDateTime.parse("2023-06-07T16:45:39")), resultMessage1, resultMessage2, - new Message("방3메시지5", loginMember.getId(), room3UUID, + new Message("방3메시지5", loginMember, room3UUID, LocalDateTime.parse("2023-08-07T16:45:39")) ) ); @@ -106,6 +107,7 @@ void test_findAll() throws Exception { final MessageOverview messageOverview1 = new MessageOverview( resultMessage1.getId(), resultMessage1.getContent(), + resultMessage1.getSender().getId(), resultMessage1.getCreatedAt(), resultMessage1.getRoomId() ); @@ -113,14 +115,15 @@ void test_findAll() throws Exception { final MessageOverview messageOverview2 = new MessageOverview( resultMessage2.getId(), resultMessage2.getContent(), + resultMessage2.getSender().getId(), resultMessage2.getCreatedAt(), resultMessage2.getRoomId() ); final List expect = List.of( - RoomResponse.from(messageOverview1, + RoomResponse.from(messageOverview1, resultMessage1.getSender(), memberRepository.findById(room1Interlocutor.getId()).get()), - RoomResponse.from(messageOverview2, + RoomResponse.from(messageOverview2, resultMessage2.getSender(), memberRepository.findById(room2Interlocutor.getId()).get()) ); @@ -137,26 +140,28 @@ void test_findAll() throws Exception { @DisplayName("findByRoomId() : Room UUID를 통해 Room에 있는 메시지들을 조회할 수 있다.") void test_findByRoomId() throws Exception { //given - final Message room1Message1 = new Message("방1메시지1", loginMember.getId(), room1UUID, + final Message room1Message1 = new Message("방1메시지1", loginMember, room1UUID, LocalDateTime.parse("2023-05-07T16:45:39")); - final Message room1Message2 = new Message("방1메시지2", loginMember.getId(), room1UUID, + final Message room1Message2 = new Message("방1메시지2", loginMember, room1UUID, LocalDateTime.parse("2023-06-07T16:45:38")); messageRepository.saveAll( List.of( room1Message1, room1Message2, - new Message("방2메시지3", loginMember.getId(), room2UUID, + new Message("방2메시지3", loginMember, room2UUID, LocalDateTime.parse("2023-10-07T16:45:39")), - new Message("방2메시지4", room1Interlocutor.getId(), room2UUID, + new Message("방2메시지4", room1Interlocutor, room2UUID, LocalDateTime.parse("2023-10-07T16:45:39")) ) ); final List expect = List.of( - new MessageResponse(room1Message1.getSenderId(), room1Message1.getContent(), + new MessageResponse(room1Message1.getId(), + MemberReferenceResponse.from(room1Message1.getSender()), room1Message1.getContent(), room1Message1.getCreatedAt()), - new MessageResponse(room1Message2.getSenderId(), room1Message2.getContent(), + new MessageResponse(room1Message2.getId(), + MemberReferenceResponse.from(room1Message2.getSender()), room1Message2.getContent(), room1Message2.getCreatedAt()) ); @@ -174,26 +179,28 @@ void test_findByRoomId() throws Exception { @DisplayName("findByInterlocutorIds() : Room 에 참여한 사용자의 ID를 통해 쪽지방을 조회할 수 있다.") void test_findByInterlocutorIds() throws Exception { //given - final Message room1Message1 = new Message("방1메시지1", loginMember.getId(), room1UUID, + final Message room1Message1 = new Message("방1메시지1", loginMember, room1UUID, LocalDateTime.parse("2023-05-07T16:45:39")); - final Message room1Message2 = new Message("방1메시지2", loginMember.getId(), room1UUID, + final Message room1Message2 = new Message("방1메시지2", loginMember, room1UUID, LocalDateTime.parse("2023-06-07T16:45:38")); messageRepository.saveAll( List.of( room1Message1, room1Message2, - new Message("방2메시지3", loginMember.getId(), room2UUID, + new Message("방2메시지3", loginMember, room2UUID, LocalDateTime.parse("2023-10-07T16:45:39")), - new Message("방2메시지4", room1Interlocutor.getId(), room2UUID, + new Message("방2메시지4", room1Interlocutor, room2UUID, LocalDateTime.parse("2023-10-07T16:45:39")) ) ); final List expect = List.of( - new MessageResponse(room1Message1.getSenderId(), room1Message1.getContent(), + new MessageResponse(room1Message1.getId(), + MemberReferenceResponse.from(room1Message1.getSender()), room1Message1.getContent(), room1Message1.getCreatedAt()), - new MessageResponse(room1Message2.getSenderId(), room1Message2.getContent(), + new MessageResponse(room1Message2.getId(), + MemberReferenceResponse.from(room1Message2.getSender()), room1Message2.getContent(), room1Message2.getCreatedAt()) ); diff --git a/backend/emm-sale/src/test/java/com/emmsale/message_room/domain/MessageRepositoryTest.java b/backend/emm-sale/src/test/java/com/emmsale/message_room/domain/MessageRepositoryTest.java index b5ef58669..cfcf34ac8 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/message_room/domain/MessageRepositoryTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/message_room/domain/MessageRepositoryTest.java @@ -38,18 +38,18 @@ void test_findByRoomId() throws Exception { final Room member3Room2 = new Room(new RoomId(room2UUID, room2Interlocutor.getId()), LocalDateTime.parse("2023-09-07T16:48:24")); - final Message room1Message1 = new Message("방1메시지1", loginMember.getId(), room1UUID, + final Message room1Message1 = new Message("방1메시지1", loginMember, room1UUID, LocalDateTime.parse("2023-05-07T16:45:39")); - final Message room1Message2 = new Message("방1메시지2", loginMember.getId(), room1UUID, + final Message room1Message2 = new Message("방1메시지2", loginMember, room1UUID, LocalDateTime.parse("2023-06-07T16:45:38")); messageRepository.saveAll( List.of( room1Message1, room1Message2, - new Message("방2메시지3", loginMember.getId(), room2UUID, + new Message("방2메시지3", loginMember, room2UUID, LocalDateTime.parse("2023-10-07T16:45:39")), - new Message("방2메시지4", room1Interlocutor.getId(), room2UUID, + new Message("방2메시지4", room1Interlocutor, room2UUID, LocalDateTime.parse("2023-10-07T16:45:39")) ) ); diff --git a/backend/emm-sale/src/test/java/com/emmsale/message_room/infrastructure/persistence/MessageDaoTest.java b/backend/emm-sale/src/test/java/com/emmsale/message_room/infrastructure/persistence/MessageDaoTest.java index d418c309e..3aa1f63ff 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/message_room/infrastructure/persistence/MessageDaoTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/message_room/infrastructure/persistence/MessageDaoTest.java @@ -18,7 +18,6 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.test.annotation.Rollback; class MessageDaoTest extends JpaRepositorySliceTestHelper { @@ -82,26 +81,26 @@ void test_findRecentlyMessages() throws Exception { final Message resultMessage1 = new Message( "방1메시지3", - loginMember.getId(), + loginMember, room1UUID, LocalDateTime.parse("2023-10-07T16:45:39") ); final Message resultMessage2 = new Message( "방2메시지4", - room2Interlocutor.getId(), + room2Interlocutor, room2UUID, LocalDateTime.parse("2023-10-07T16:45:39") ); messageRepository.saveAll( List.of( - new Message("방1메시지1", loginMember.getId(), room1UUID, + new Message("방1메시지1", loginMember, room1UUID, LocalDateTime.parse("2023-05-07T16:45:39")), - new Message("방1메시지2", loginMember.getId(), room1UUID, + new Message("방1메시지2", loginMember, room1UUID, LocalDateTime.parse("2023-06-07T16:45:39")), resultMessage1, resultMessage2, - new Message("방3메시지5", loginMember.getId(), room3UUID, + new Message("방3메시지5", loginMember, room3UUID, LocalDateTime.parse("2023-08-07T16:45:39")) ) ); @@ -110,12 +109,14 @@ void test_findRecentlyMessages() throws Exception { new MessageOverview( resultMessage1.getId(), resultMessage1.getContent(), + resultMessage1.getSender().getId(), resultMessage1.getCreatedAt(), resultMessage1.getRoomId() ), new MessageOverview( resultMessage2.getId(), resultMessage2.getContent(), + resultMessage2.getSender().getId(), resultMessage2.getCreatedAt(), resultMessage2.getRoomId() ) From 18e88667ba1c6936389abe0e704fd904e83314f0 Mon Sep 17 00:00:00 2001 From: amaran-th Date: Sun, 5 Nov 2023 18:02:23 +0900 Subject: [PATCH 28/41] =?UTF-8?q?refactor:=20Message=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=20api=20=EB=B0=98=ED=99=98=20=EB=AA=85=EC=84=B8=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 - 쪽지방 조회 시 조회하려는 회원의 id를 입력받지 않도록 수정 #821 --- .../java/com/emmsale/RoomApiTest.java | 12 +++--------- .../com/emmsale/message_room/api/RoomApi.java | 8 +++----- .../application/RoomQueryService.java | 17 ++++++++++------- .../message_room/domain/MessageRepository.java | 1 + .../message_room/domain/RoomRepository.java | 2 ++ .../application/RoomQueryServiceTest.java | 4 +--- 6 files changed, 20 insertions(+), 24 deletions(-) diff --git a/backend/emm-sale/src/documentTest/java/com/emmsale/RoomApiTest.java b/backend/emm-sale/src/documentTest/java/com/emmsale/RoomApiTest.java index 3036b2a58..c99dfe7c1 100644 --- a/backend/emm-sale/src/documentTest/java/com/emmsale/RoomApiTest.java +++ b/backend/emm-sale/src/documentTest/java/com/emmsale/RoomApiTest.java @@ -115,9 +115,6 @@ void test_findAllRoom() throws Exception { @DisplayName("findByRoomId() : Room Id로 쪽지방을 성공적으로 조회하면 200 OK를 반환할 수 있다.") void test_findByRoomId() throws Exception { //given - final RequestParametersSnippet requestParam = requestParameters( - parameterWithName("member-id").description("로그인 한 사용자 ID") - ); final PathParametersSnippet pathParams = pathParameters( parameterWithName("room-id").description("조회할 Room UUID") @@ -130,16 +127,15 @@ void test_findByRoomId() throws Exception { new MessageResponse(3L, MemberReferenceResponse.from(member1), "내용3", LocalDateTime.now()) ); - when(roomQueryService.findByRoomId(any(), any(), anyLong())) + when(roomQueryService.findByRoomId(any(), any())) .thenReturn(messageResponses); //when & then mockMvc.perform(get("/rooms/{room-id}", 1L) - .queryParam("member-id", "1") .header(HttpHeaders.AUTHORIZATION, accessToken)) .andExpect(status().isOk()) .andDo(print()) - .andDo(document("get-rooms-roomId", requestParam, MESSAGES_RESPONSE_FIELDS, pathParams)); + .andDo(document("get-rooms-roomId", MESSAGES_RESPONSE_FIELDS, pathParams)); } @Test @@ -147,7 +143,6 @@ void test_findByRoomId() throws Exception { void test_findByInterlocutorIds() throws Exception { //given final RequestParametersSnippet requestParam = requestParameters( - parameterWithName("member-id").description("로그인 한 사용자 ID"), parameterWithName("receiver-id").description("쪽지방 참여 상대방 ID") ); @@ -160,12 +155,11 @@ void test_findByInterlocutorIds() throws Exception { new MessageResponse(3L, MemberReferenceResponse.from(member1), "내용3", LocalDateTime.now()) ); - when(roomQueryService.findByInterlocutorIds(anyLong(), anyLong(), any())) + when(roomQueryService.findByInterlocutorIds(anyLong(), any())) .thenReturn(messageResponses); //when & then mockMvc.perform(get("/rooms") - .queryParam("member-id", "1") .queryParam("receiver-id", "1") .header(HttpHeaders.AUTHORIZATION, accessToken)) .andExpect(status().isOk()) diff --git a/backend/emm-sale/src/main/java/com/emmsale/message_room/api/RoomApi.java b/backend/emm-sale/src/main/java/com/emmsale/message_room/api/RoomApi.java index 3e55d95bd..1695436f2 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/message_room/api/RoomApi.java +++ b/backend/emm-sale/src/main/java/com/emmsale/message_room/api/RoomApi.java @@ -28,18 +28,16 @@ public List findAllRoom( @GetMapping("/rooms/{room-id}") public List findByRoomId( final Member member, - @PathVariable("room-id") final String roomId, - @RequestParam("member-id") final Long memberId + @PathVariable("room-id") final String roomId ) { - return roomQueryService.findByRoomId(member, roomId, memberId); + return roomQueryService.findByRoomId(member, roomId); } @GetMapping("/rooms") public List findByInterlocutorIds( @RequestParam("receiver-id") final Long receiverId, - @RequestParam("member-id") final Long memberId, final Member loginMember ) { - return roomQueryService.findByInterlocutorIds(receiverId, memberId, loginMember); + return roomQueryService.findByInterlocutorIds(receiverId, loginMember); } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/message_room/application/RoomQueryService.java b/backend/emm-sale/src/main/java/com/emmsale/message_room/application/RoomQueryService.java index 04be29708..f690c55b3 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/message_room/application/RoomQueryService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/message_room/application/RoomQueryService.java @@ -11,6 +11,7 @@ import com.emmsale.message_room.application.dto.RoomResponse; import com.emmsale.message_room.domain.MessageRepository; import com.emmsale.message_room.domain.Room; +import com.emmsale.message_room.domain.RoomId; import com.emmsale.message_room.domain.RoomRepository; import com.emmsale.message_room.exception.MessageRoomException; import com.emmsale.message_room.infrastructure.persistence.MessageDao; @@ -88,14 +89,19 @@ private Member findInterlocutor(final Long interlocutorId) { public List findByRoomId( final Member loginMember, - final String roomId, - final Long memberId + final String roomId ) { - validateSameMember(loginMember, memberId); + validateAuthorizedMember(loginMember, roomId); return findMessageByRoomUUID(roomId); } + private void validateAuthorizedMember(final Member loginMember, final String roomId) { + if (!roomRepository.existsByRoomId(new RoomId(roomId, loginMember.getId()))) { + throw new MemberException(NOT_MATCHING_TOKEN_AND_LOGIN_MEMBER); + } + } + private List findMessageByRoomUUID(final String roomId) { return messageRepository.findByRoomUUID(roomId) .stream() @@ -106,12 +112,9 @@ private List findMessageByRoomUUID(final String roomId) { public List findByInterlocutorIds( final Long receiverId, - final Long senderId, final Member loginMember ) { - validateSameMember(loginMember, senderId); - - final Room room = roomRepository.findByInterlocutorIds(senderId, receiverId) + final Room room = roomRepository.findByInterlocutorIds(loginMember.getId(), receiverId) .orElseThrow(() -> new MessageRoomException(NOT_FOUND_MESSAGE_ROOM)); return findMessageByRoomUUID(room.getRoomId().getUuid()); diff --git a/backend/emm-sale/src/main/java/com/emmsale/message_room/domain/MessageRepository.java b/backend/emm-sale/src/main/java/com/emmsale/message_room/domain/MessageRepository.java index bb05762d7..c795433f3 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/message_room/domain/MessageRepository.java +++ b/backend/emm-sale/src/main/java/com/emmsale/message_room/domain/MessageRepository.java @@ -8,4 +8,5 @@ public interface MessageRepository extends JpaRepository { @Query("select m from Message m where m.roomId = :roomUUID") List findByRoomUUID(String roomUUID); + } diff --git a/backend/emm-sale/src/main/java/com/emmsale/message_room/domain/RoomRepository.java b/backend/emm-sale/src/main/java/com/emmsale/message_room/domain/RoomRepository.java index 667a6ac80..30f7cab02 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/message_room/domain/RoomRepository.java +++ b/backend/emm-sale/src/main/java/com/emmsale/message_room/domain/RoomRepository.java @@ -19,4 +19,6 @@ Optional findByInterlocutorIds( @Param("member1") final Long interlocutorId1, @Param("member2") final Long interlocutorId2 ); + + boolean existsByRoomId(RoomId roomId); } diff --git a/backend/emm-sale/src/test/java/com/emmsale/message_room/application/RoomQueryServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/message_room/application/RoomQueryServiceTest.java index aaa5c6719..cd1ccb5af 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/message_room/application/RoomQueryServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/message_room/application/RoomQueryServiceTest.java @@ -166,8 +166,7 @@ void test_findByRoomId() throws Exception { ); //when - final List actual = roomQueryService.findByRoomId(loginMember, room1UUID, - loginMember.getId()); + final List actual = roomQueryService.findByRoomId(loginMember, room1UUID); //then Assertions.assertThat(actual) @@ -207,7 +206,6 @@ void test_findByInterlocutorIds() throws Exception { //when final List actual = roomQueryService.findByInterlocutorIds( room1Interlocutor.getId(), - loginMember.getId(), loginMember ); From 9998b29fa7f0d798240862a9be7fb1b5eba62593 Mon Sep 17 00:00:00 2001 From: amaran-th Date: Sun, 5 Nov 2023 18:04:02 +0900 Subject: [PATCH 29/41] =?UTF-8?q?fix:=20=EC=8A=A4=ED=81=AC=EB=9E=A9=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EC=8B=9C=20=EA=B2=80=EC=A6=9D=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EC=83=81=ED=83=9C=EC=BD=94=EB=93=9C=EB=A5=BC=20noC?= =?UTF-8?q?ontent=20=EB=8C=80=EC=8B=A0=20ok=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #821 --- .../src/documentTest/java/com/emmsale/ScrapApiTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/emm-sale/src/documentTest/java/com/emmsale/ScrapApiTest.java b/backend/emm-sale/src/documentTest/java/com/emmsale/ScrapApiTest.java index e784bb06e..9b7d2d5bb 100644 --- a/backend/emm-sale/src/documentTest/java/com/emmsale/ScrapApiTest.java +++ b/backend/emm-sale/src/documentTest/java/com/emmsale/ScrapApiTest.java @@ -214,7 +214,7 @@ void deleteScrap() throws Exception { //then mockMvc.perform(delete("/scraps?event-id={eventId}", eventId) .header(HttpHeaders.AUTHORIZATION, "Bearer AccessToken")) - .andExpect(status().isNoContent()) + .andExpect(status().isOk()) .andDo(document("delete-scrap", SCRAPPED_EVENT_RESPONSE_FIELDS)); } } From fb3a7bb7d280a9e889742a69e21519066f1cfe76 Mon Sep 17 00:00:00 2001 From: hong-sile Date: Mon, 6 Nov 2023 10:02:48 +0900 Subject: [PATCH 30/41] =?UTF-8?q?refactor:=20=ED=94=BC=EB=93=9C=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=EC=A1=B0=ED=9A=8C=20=EB=B0=98=ED=99=98?= =?UTF-8?q?=EA=B0=92=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #822 --- .../java/com/emmsale/FeedApiTest.java | 60 ++++++++++++------- .../java/com/emmsale/feed/api/FeedApi.java | 4 +- .../feed/application/FeedQueryService.java | 48 +++++++++++---- .../application/dto/FeedResponseRefactor.java | 53 ++++++++++++++++ 4 files changed, 131 insertions(+), 34 deletions(-) create mode 100644 backend/emm-sale/src/main/java/com/emmsale/feed/application/dto/FeedResponseRefactor.java diff --git a/backend/emm-sale/src/documentTest/java/com/emmsale/FeedApiTest.java b/backend/emm-sale/src/documentTest/java/com/emmsale/FeedApiTest.java index cdabf473a..f5b08a642 100644 --- a/backend/emm-sale/src/documentTest/java/com/emmsale/FeedApiTest.java +++ b/backend/emm-sale/src/documentTest/java/com/emmsale/FeedApiTest.java @@ -15,10 +15,11 @@ import com.emmsale.feed.application.dto.FeedDetailResponse; import com.emmsale.feed.application.dto.FeedDetailResponse.WriterProfileResponse; -import com.emmsale.feed.application.dto.FeedListResponse; +import com.emmsale.feed.application.dto.FeedResponseRefactor; import com.emmsale.feed.application.dto.FeedSimpleResponse; import com.emmsale.feed.application.dto.FeedUpdateRequest; import com.emmsale.feed.application.dto.FeedUpdateResponse; +import com.emmsale.member.application.dto.MemberReferenceResponse; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; @@ -41,29 +42,48 @@ class FeedApiTest extends MockMvcTestHelper { void findAllFeedsTest() throws Exception { //given final ResponseFieldsSnippet responseFields = responseFields( - fieldWithPath("eventId").type(JsonFieldType.NUMBER).description("피드가 작성된 부모 이벤트 id"), - fieldWithPath("feeds").type(JsonFieldType.ARRAY).description("피드 리스트"), - fieldWithPath("feeds[].id").type(JsonFieldType.NUMBER).description("피드 id"), - fieldWithPath("feeds[].title").type(JsonFieldType.STRING).description("피드 제목"), - fieldWithPath("feeds[].content").type(JsonFieldType.STRING).description("피드 내용"), - fieldWithPath("feeds[].images").type(JsonFieldType.ARRAY).description("피드 이미지 url 리스트"), - fieldWithPath("feeds[].writerId").type(JsonFieldType.NUMBER).description("피드 작성자 id"), - fieldWithPath("feeds[].commentCount").type(JsonFieldType.NUMBER).description("피드의 댓글 개수"), - fieldWithPath("feeds[].createdAt").type(JsonFieldType.STRING).description("피드 생성 일시"), - fieldWithPath("feeds[].updatedAt").type(JsonFieldType.STRING).description("피드 업데이트 일시") + fieldWithPath("[].id").type(JsonFieldType.NUMBER).description("피드 id"), + fieldWithPath("[].eventId").type(JsonFieldType.NUMBER).description("피드가 작성된 부모 이벤트 id"), + fieldWithPath("[].title").type(JsonFieldType.STRING).description("피드 제목"), + fieldWithPath("[].content").type(JsonFieldType.STRING).description("피드 내용"), + fieldWithPath("[].images").type(JsonFieldType.ARRAY).description("피드 이미지 url 리스트"), + fieldWithPath("[].writer.id").type(JsonFieldType.NUMBER).description("writer의 식별자"), + fieldWithPath("[].writer.name").type(JsonFieldType.STRING).description("writer의 이름"), + fieldWithPath("[].writer.description").type(JsonFieldType.STRING) + .description("writer의 한줄 자기소개"), + fieldWithPath("[].writer.imageUrl").type(JsonFieldType.STRING) + .description("writer의 이미지 url"), + fieldWithPath("[].writer.githubUrl").type(JsonFieldType.STRING) + .description("writer의 github Url"), + fieldWithPath("[].commentCount").type(JsonFieldType.NUMBER).description("피드의 댓글 개수"), + fieldWithPath("[].createdAt").type(JsonFieldType.STRING).description("피드 생성 일시"), + fieldWithPath("[].updatedAt").type(JsonFieldType.STRING).description("피드 업데이트 일시") ); final long eventId = 11L; - final List feeds = List.of( - new FeedSimpleResponse(34L, "피드1 제목", "피드 내용", 23L, - List.of("https://image1.url", "https://image2.url"), 0L, - LocalDateTime.of(LocalDate.of(2023, 7, 13), LocalTime.of(11, 43, 11)), - LocalDateTime.of(LocalDate.of(2023, 7, 13), LocalTime.of(11, 43, 11))), - new FeedSimpleResponse(35L, "피드2 제목", "피드 내용", 43L, Collections.emptyList(), 3L, - LocalDateTime.of(LocalDate.of(2023, 7, 22), LocalTime.of(23, 54, 49)), - LocalDateTime.of(LocalDate.of(2023, 7, 22), LocalTime.of(23, 54, 49))) + final MemberReferenceResponse memberReferenceResponse = new MemberReferenceResponse( + 2L, + "멤버", + "멤버 설명", + "멤버 이미지url", + "멤버 깃허브 url" + ); + final List response = List.of( + new FeedResponseRefactor( + 34L, eventId, "피드 1 제목", "피드 1 내용", + memberReferenceResponse, + Collections.emptyList(), + 2L, + LocalDateTime.of(2023, 7, 13, 0, 0), LocalDateTime.of(2023, 7, 13, 0, 0) + ), + new FeedResponseRefactor( + 35L, eventId, "피드 2 제목", "피드 2 내용", + memberReferenceResponse, + Collections.emptyList(), + 2L, + LocalDateTime.of(2023, 7, 13, 0, 0), LocalDateTime.of(2023, 7, 13, 0, 0) + ) ); - final FeedListResponse response = new FeedListResponse(eventId, feeds); when(feedQueryService.findAllFeeds(any(), any())).thenReturn(response); diff --git a/backend/emm-sale/src/main/java/com/emmsale/feed/api/FeedApi.java b/backend/emm-sale/src/main/java/com/emmsale/feed/api/FeedApi.java index c51638caf..9ac76a879 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/feed/api/FeedApi.java +++ b/backend/emm-sale/src/main/java/com/emmsale/feed/api/FeedApi.java @@ -3,8 +3,8 @@ import com.emmsale.feed.application.FeedCommandService; import com.emmsale.feed.application.FeedQueryService; import com.emmsale.feed.application.dto.FeedDetailResponse; -import com.emmsale.feed.application.dto.FeedListResponse; import com.emmsale.feed.application.dto.FeedPostRequest; +import com.emmsale.feed.application.dto.FeedResponseRefactor; import com.emmsale.feed.application.dto.FeedSimpleResponse; import com.emmsale.feed.application.dto.FeedUpdateRequest; import com.emmsale.feed.application.dto.FeedUpdateResponse; @@ -34,7 +34,7 @@ public class FeedApi { private final FeedCommandService feedCommandService; @GetMapping - public FeedListResponse findAllFeeds( + public List findAllFeeds( final Member member, @RequestParam("event-id") final Long eventId ) { diff --git a/backend/emm-sale/src/main/java/com/emmsale/feed/application/FeedQueryService.java b/backend/emm-sale/src/main/java/com/emmsale/feed/application/FeedQueryService.java index d04e05699..462fd775e 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/feed/application/FeedQueryService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/feed/application/FeedQueryService.java @@ -1,5 +1,7 @@ package com.emmsale.feed.application; +import static java.util.stream.Collectors.toUnmodifiableList; + import com.emmsale.block.domain.Block; import com.emmsale.block.domain.BlockRepository; import com.emmsale.comment.infrastructure.persistence.CommentDao; @@ -8,7 +10,7 @@ import com.emmsale.event.exception.EventException; import com.emmsale.event.exception.EventExceptionType; import com.emmsale.feed.application.dto.FeedDetailResponse; -import com.emmsale.feed.application.dto.FeedListResponse; +import com.emmsale.feed.application.dto.FeedResponseRefactor; import com.emmsale.feed.application.dto.FeedSimpleResponse; import com.emmsale.feed.domain.Feed; import com.emmsale.feed.domain.repository.FeedRepository; @@ -40,28 +42,50 @@ public class FeedQueryService { private final ImageRepository imageRepository; private final CommentDao commentDao; - public FeedListResponse findAllFeeds(final Member member, final Long eventId) { +// public FeedListResponse findAllFeeds(final Member member, final Long eventId) { +// validateEvent(eventId); +// +// final List feeds = feedRepository.findAllByEventIdAndNotDeleted(eventId); +// final List filteredFeeds = excludeBlockedMembersFeed(member, feeds); +// final List feedIds = filteredFeeds.stream() +// .map(Feed::getId) +// .collect(Collectors.toList()); +// +// final Map feedCommentCounts = getFeedIdCommentCountMap(feedIds); +// final Map> feedImages = getFeedImagesMap(feedIds); +// +// final List feedSimpleResponses = filteredFeeds.stream() +// .map(feed -> { +// final List images = feedImages.getOrDefault(feed.getId(), Collections.emptyList()); +// final Long commentCount = feedCommentCounts.getOrDefault(feed.getId(), +// DEFAULT_COMMENT_COUNT); +// return FeedSimpleResponse.from(feed, images, commentCount); +// }) +// .collect(Collectors.toList()); +// +// return new FeedListResponse(eventId, feedSimpleResponses); +// } + + public List findAllFeeds(final Member member, final Long eventId) { validateEvent(eventId); - final List feeds = excludeBlockedMembersFeed(member, - feedRepository.findAllByEventIdAndNotDeleted(eventId)); - final List feedIds = feeds.stream() + final List feeds = feedRepository.findAllByEventIdAndNotDeleted(eventId); + final List filteredFeeds = excludeBlockedMembersFeed(member, feeds); + final List feedIds = filteredFeeds.stream() .map(Feed::getId) - .collect(Collectors.toList()); + .collect(toUnmodifiableList()); final Map feedCommentCounts = getFeedIdCommentCountMap(feedIds); final Map> feedImages = getFeedImagesMap(feedIds); - final List feedSimpleResponses = feeds.stream() + return filteredFeeds.stream() .map(feed -> { final List images = feedImages.getOrDefault(feed.getId(), Collections.emptyList()); final Long commentCount = feedCommentCounts.getOrDefault(feed.getId(), DEFAULT_COMMENT_COUNT); - return FeedSimpleResponse.from(feed, images, commentCount); + return FeedResponseRefactor.of(feed, images, commentCount); }) - .collect(Collectors.toList()); - - return new FeedListResponse(eventId, feedSimpleResponses); + .collect(toUnmodifiableList()); } private Map> getFeedImagesMap(final List feedIds) { @@ -70,7 +94,7 @@ private Map> getFeedImagesMap(final List feedIds) { Image::getContentId, Collectors.mapping( Function.identity(), - Collectors.toList() + Collectors.toUnmodifiableList() ) )); diff --git a/backend/emm-sale/src/main/java/com/emmsale/feed/application/dto/FeedResponseRefactor.java b/backend/emm-sale/src/main/java/com/emmsale/feed/application/dto/FeedResponseRefactor.java new file mode 100644 index 000000000..42274eedd --- /dev/null +++ b/backend/emm-sale/src/main/java/com/emmsale/feed/application/dto/FeedResponseRefactor.java @@ -0,0 +1,53 @@ +package com.emmsale.feed.application.dto; + +import static java.util.stream.Collectors.toUnmodifiableList; + +import com.emmsale.feed.domain.Feed; +import com.emmsale.image.domain.Image; +import com.emmsale.member.application.dto.MemberReferenceResponse; +import com.fasterxml.jackson.annotation.JsonFormat; +import java.time.LocalDateTime; +import java.util.List; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public class FeedResponseRefactor { + + private static final String DATE_TIME_FORMAT = "yyyy:MM:dd:HH:mm:ss"; + + private final Long id; + private final Long eventId; + private final String title; + private final String content; + private final MemberReferenceResponse writer; + private final List images; + private final Long commentCount; + @JsonFormat(pattern = DATE_TIME_FORMAT) + private final LocalDateTime createdAt; + @JsonFormat(pattern = DATE_TIME_FORMAT) + private final LocalDateTime updatedAt; + + public static FeedResponseRefactor of( + final Feed feed, + final List images, + final Long commentCount + ) { + final List imageUrls = images.stream() + .map(Image::getName) + .collect(toUnmodifiableList()); + + return new FeedResponseRefactor( + feed.getId(), + feed.getEvent().getId(), + feed.getTitle(), + feed.getContent(), + MemberReferenceResponse.from(feed.getWriter()), + imageUrls, + commentCount, + feed.getCreatedAt(), + feed.getUpdatedAt() + ); + } +} From f1a7d6ead762daff84344bd3aba3c125d15cd650 Mon Sep 17 00:00:00 2001 From: hong-sile Date: Mon, 6 Nov 2023 11:09:56 +0900 Subject: [PATCH 31/41] =?UTF-8?q?refactor:=20Feed=20=EB=8B=A8=EA=B1=B4?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EB=B0=98=ED=99=98=EA=B0=92=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 이 부분 안드로이드랑 논의가 필요함 #822 --- .../java/com/emmsale/FeedApiTest.java | 52 +++++++++++-------- .../java/com/emmsale/feed/api/FeedApi.java | 2 +- .../feed/application/FeedQueryService.java | 38 +++----------- .../application/dto/FeedListResponse.java | 13 ----- .../java/com/emmsale/feed/domain/Feed.java | 2 + .../domain/repository/FeedRepository.java | 15 +++++- .../application/FeedQueryServiceTest.java | 50 ++++++++---------- 7 files changed, 74 insertions(+), 98 deletions(-) delete mode 100644 backend/emm-sale/src/main/java/com/emmsale/feed/application/dto/FeedListResponse.java diff --git a/backend/emm-sale/src/documentTest/java/com/emmsale/FeedApiTest.java b/backend/emm-sale/src/documentTest/java/com/emmsale/FeedApiTest.java index f5b08a642..d73d7a847 100644 --- a/backend/emm-sale/src/documentTest/java/com/emmsale/FeedApiTest.java +++ b/backend/emm-sale/src/documentTest/java/com/emmsale/FeedApiTest.java @@ -13,8 +13,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import com.emmsale.feed.application.dto.FeedDetailResponse; -import com.emmsale.feed.application.dto.FeedDetailResponse.WriterProfileResponse; import com.emmsale.feed.application.dto.FeedResponseRefactor; import com.emmsale.feed.application.dto.FeedSimpleResponse; import com.emmsale.feed.application.dto.FeedUpdateRequest; @@ -37,6 +35,14 @@ class FeedApiTest extends MockMvcTestHelper { + private static final MemberReferenceResponse MEMBER_REFERENCE_RESPONSE = new MemberReferenceResponse( + 2L, + "멤버", + "멤버 설명", + "멤버 이미지url", + "멤버 깃허브 url" + ); + @Test @DisplayName("이벤트의 피드 목록을 성공적으로 반환하면 200 OK를 반환한다.") void findAllFeedsTest() throws Exception { @@ -61,24 +67,17 @@ void findAllFeedsTest() throws Exception { ); final long eventId = 11L; - final MemberReferenceResponse memberReferenceResponse = new MemberReferenceResponse( - 2L, - "멤버", - "멤버 설명", - "멤버 이미지url", - "멤버 깃허브 url" - ); final List response = List.of( new FeedResponseRefactor( 34L, eventId, "피드 1 제목", "피드 1 내용", - memberReferenceResponse, + MEMBER_REFERENCE_RESPONSE, Collections.emptyList(), 2L, LocalDateTime.of(2023, 7, 13, 0, 0), LocalDateTime.of(2023, 7, 13, 0, 0) ), new FeedResponseRefactor( 35L, eventId, "피드 2 제목", "피드 2 내용", - memberReferenceResponse, + MEMBER_REFERENCE_RESPONSE, Collections.emptyList(), 2L, LocalDateTime.of(2023, 7, 13, 0, 0), LocalDateTime.of(2023, 7, 13, 0, 0) @@ -101,26 +100,33 @@ void findDetailFeedTest() throws Exception { //given final ResponseFieldsSnippet responseFields = responseFields( fieldWithPath("id").type(JsonFieldType.NUMBER).description("피드 id"), - fieldWithPath("eventId").type(JsonFieldType.NUMBER).description("이벤트 id"), - fieldWithPath("writer").type(JsonFieldType.OBJECT).description("작성자"), - fieldWithPath("writer.memberId").type(JsonFieldType.NUMBER).description("작성자 id"), - fieldWithPath("writer.name").type(JsonFieldType.STRING).description("작성자명"), - fieldWithPath("writer.imageUrl").type(JsonFieldType.STRING).description("작성자 이미지 url"), + fieldWithPath("eventId").type(JsonFieldType.NUMBER).description("피드가 작성된 부모 이벤트 id"), fieldWithPath("title").type(JsonFieldType.STRING).description("피드 제목"), fieldWithPath("content").type(JsonFieldType.STRING).description("피드 내용"), fieldWithPath("images").type(JsonFieldType.ARRAY).description("피드 이미지 url 리스트"), + fieldWithPath("writer.id").type(JsonFieldType.NUMBER).description("writer의 식별자"), + fieldWithPath("writer.name").type(JsonFieldType.STRING).description("writer의 이름"), + fieldWithPath("writer.description").type(JsonFieldType.STRING) + .description("writer의 한줄 자기소개"), + fieldWithPath("writer.imageUrl").type(JsonFieldType.STRING) + .description("writer의 이미지 url"), + fieldWithPath("writer.githubUrl").type(JsonFieldType.STRING) + .description("writer의 github Url"), + fieldWithPath("commentCount").type(JsonFieldType.NUMBER).description("피드의 댓글 개수"), fieldWithPath("createdAt").type(JsonFieldType.STRING).description("피드 생성 일시"), fieldWithPath("updatedAt").type(JsonFieldType.STRING).description("피드 업데이트 일시") ); - final long eventId = 11L; final long feedId = 34L; - final WriterProfileResponse writer = new WriterProfileResponse(8L, "작성자명", - "https://member-image.com"); - final FeedDetailResponse response = new FeedDetailResponse(feedId, eventId, writer, "피드 제목", - "피드 상세 내용", List.of("https://image1.url", "https://image2.url"), - LocalDateTime.of(LocalDate.of(2023, 7, 22), LocalTime.of(23, 54, 49)), - LocalDateTime.of(LocalDate.of(2023, 7, 22), LocalTime.of(23, 54, 49))); + + final FeedResponseRefactor response = + new FeedResponseRefactor( + 34L, eventId, "피드 1 제목", "피드 1 내용", + MEMBER_REFERENCE_RESPONSE, + Collections.emptyList(), + 2L, + LocalDateTime.of(2023, 7, 13, 0, 0), LocalDateTime.of(2023, 7, 13, 0, 0) + ); when(feedQueryService.findFeed(any(), any())).thenReturn(response); diff --git a/backend/emm-sale/src/main/java/com/emmsale/feed/api/FeedApi.java b/backend/emm-sale/src/main/java/com/emmsale/feed/api/FeedApi.java index 9ac76a879..bce4c6d74 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/feed/api/FeedApi.java +++ b/backend/emm-sale/src/main/java/com/emmsale/feed/api/FeedApi.java @@ -42,7 +42,7 @@ public List findAllFeeds( } @GetMapping("/{id}") - public FeedDetailResponse findFeed(final Member member, @PathVariable final Long id) { + public FeedResponseRefactor findFeed(final Member member, @PathVariable final Long id) { return feedQueryService.findFeed(member, id); } diff --git a/backend/emm-sale/src/main/java/com/emmsale/feed/application/FeedQueryService.java b/backend/emm-sale/src/main/java/com/emmsale/feed/application/FeedQueryService.java index 462fd775e..cd2dfe599 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/feed/application/FeedQueryService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/feed/application/FeedQueryService.java @@ -9,7 +9,6 @@ import com.emmsale.event.domain.repository.EventRepository; import com.emmsale.event.exception.EventException; import com.emmsale.event.exception.EventExceptionType; -import com.emmsale.feed.application.dto.FeedDetailResponse; import com.emmsale.feed.application.dto.FeedResponseRefactor; import com.emmsale.feed.application.dto.FeedSimpleResponse; import com.emmsale.feed.domain.Feed; @@ -42,30 +41,6 @@ public class FeedQueryService { private final ImageRepository imageRepository; private final CommentDao commentDao; -// public FeedListResponse findAllFeeds(final Member member, final Long eventId) { -// validateEvent(eventId); -// -// final List feeds = feedRepository.findAllByEventIdAndNotDeleted(eventId); -// final List filteredFeeds = excludeBlockedMembersFeed(member, feeds); -// final List feedIds = filteredFeeds.stream() -// .map(Feed::getId) -// .collect(Collectors.toList()); -// -// final Map feedCommentCounts = getFeedIdCommentCountMap(feedIds); -// final Map> feedImages = getFeedImagesMap(feedIds); -// -// final List feedSimpleResponses = filteredFeeds.stream() -// .map(feed -> { -// final List images = feedImages.getOrDefault(feed.getId(), Collections.emptyList()); -// final Long commentCount = feedCommentCounts.getOrDefault(feed.getId(), -// DEFAULT_COMMENT_COUNT); -// return FeedSimpleResponse.from(feed, images, commentCount); -// }) -// .collect(Collectors.toList()); -// -// return new FeedListResponse(eventId, feedSimpleResponses); -// } - public List findAllFeeds(final Member member, final Long eventId) { validateEvent(eventId); @@ -89,12 +64,13 @@ public List findAllFeeds(final Member member, final Long e } private Map> getFeedImagesMap(final List feedIds) { - final Map> feedImagesMap = imageRepository.findAllByFeedIdIn(feedIds).stream() + final Map> feedImagesMap = imageRepository.findAllByFeedIdIn(feedIds) + .stream() .collect(Collectors.groupingBy( Image::getContentId, Collectors.mapping( Function.identity(), - Collectors.toUnmodifiableList() + Collectors.toList() ) )); @@ -129,16 +105,16 @@ private List getBlockedMemberIds(final Member member) { .collect(Collectors.toList()); } - public FeedDetailResponse findFeed(final Member member, final Long id) { - final Feed feed = feedRepository.findById(id) - .orElseThrow(() -> new FeedException(FeedExceptionType.NOT_FOUND_FEED)); + public FeedResponseRefactor findFeed(final Member member, final Long id) { + final Feed feed = feedRepository.getByIdOrThrow(id); final List images = imageRepository.findAllByFeedId(feed.getId()); images.sort(Comparator.comparing(Image::getOrder)); validateBlockedMemberFeed(member, feed); validateDeletedFeed(feed); - return FeedDetailResponse.from(feed, images); + //이 부분은 안드분들과 이야기를 해봐야할 듯 실질적으로 쓰지 않는 값 + return FeedResponseRefactor.of(feed, images, 0L); } private void validateBlockedMemberFeed(final Member member, final Feed feed) { diff --git a/backend/emm-sale/src/main/java/com/emmsale/feed/application/dto/FeedListResponse.java b/backend/emm-sale/src/main/java/com/emmsale/feed/application/dto/FeedListResponse.java deleted file mode 100644 index 6322d243d..000000000 --- a/backend/emm-sale/src/main/java/com/emmsale/feed/application/dto/FeedListResponse.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.emmsale.feed.application.dto; - -import java.util.List; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@Getter -@RequiredArgsConstructor -public class FeedListResponse { - - private final Long eventId; - private final List feeds; -} diff --git a/backend/emm-sale/src/main/java/com/emmsale/feed/domain/Feed.java b/backend/emm-sale/src/main/java/com/emmsale/feed/domain/Feed.java index 5942488ce..4b9a18cfb 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/feed/domain/Feed.java +++ b/backend/emm-sale/src/main/java/com/emmsale/feed/domain/Feed.java @@ -16,10 +16,12 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.ToString; @Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) +@ToString public class Feed extends BaseEntity { @Id diff --git a/backend/emm-sale/src/main/java/com/emmsale/feed/domain/repository/FeedRepository.java b/backend/emm-sale/src/main/java/com/emmsale/feed/domain/repository/FeedRepository.java index 0dc8b0bb5..c60236c2f 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/feed/domain/repository/FeedRepository.java +++ b/backend/emm-sale/src/main/java/com/emmsale/feed/domain/repository/FeedRepository.java @@ -1,6 +1,9 @@ package com.emmsale.feed.domain.repository; +import static com.emmsale.feed.exception.FeedExceptionType.NOT_FOUND_FEED; + import com.emmsale.feed.domain.Feed; +import com.emmsale.feed.exception.FeedException; import com.emmsale.member.domain.Member; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; @@ -10,9 +13,17 @@ @Repository public interface FeedRepository extends JpaRepository { - @Query("select f from Feed f where f.event.id = :eventId and f.isDeleted = false") - List findAllByEventIdAndNotDeleted(Long eventId); + @Query("select f " + + "from Feed f " + + "join fetch f.writer w " + + "where f.event.id = :eventId " + + "and f.isDeleted = false") + List findAllByEventIdAndNotDeleted(final Long eventId); @Query("select f from Feed f where f.writer = :member and f.isDeleted = false") List findByMember(Member member); + + default Feed getByIdOrThrow(final Long id) { + return findById(id).orElseThrow(() -> new FeedException(NOT_FOUND_FEED)); + } } diff --git a/backend/emm-sale/src/test/java/com/emmsale/feed/application/FeedQueryServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/feed/application/FeedQueryServiceTest.java index b257d446d..006e4131f 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/feed/application/FeedQueryServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/feed/application/FeedQueryServiceTest.java @@ -11,8 +11,7 @@ import com.emmsale.event.domain.repository.EventRepository; import com.emmsale.event.exception.EventException; import com.emmsale.event.exception.EventExceptionType; -import com.emmsale.feed.application.dto.FeedDetailResponse; -import com.emmsale.feed.application.dto.FeedListResponse; +import com.emmsale.feed.application.dto.FeedResponseRefactor; import com.emmsale.feed.application.dto.FeedSimpleResponse; import com.emmsale.feed.domain.Feed; import com.emmsale.feed.domain.repository.FeedRepository; @@ -73,15 +72,13 @@ void findAllFeedsTest() { //given final Long eventId = event.getId(); - final List feedSimpleResponses = List.of( - FeedSimpleResponse.from(feed1, Collections.emptyList(), 0L), - FeedSimpleResponse.from(feed2, Collections.emptyList(), 0L) + final List expect = List.of( + FeedResponseRefactor.of(feed1, Collections.emptyList(), 0L), + FeedResponseRefactor.of(feed2, Collections.emptyList(), 0L) ); - final FeedListResponse expect = new FeedListResponse(eventId, feedSimpleResponses); - //when - final FeedListResponse actual = feedQueryService.findAllFeeds(writer, eventId); + final List actual = feedQueryService.findAllFeeds(writer, eventId); //then assertThat(actual) @@ -97,13 +94,12 @@ void findAllFeedsWithWithDeletedFeedTest() { feedRepository.save(feed1); final Long eventId = event.getId(); - final List feedSimpleResponses = List.of( - FeedSimpleResponse.from(feed2, Collections.emptyList(), 0L) + final List expect = List.of( + FeedResponseRefactor.of(feed2, Collections.emptyList(), 0L) ); - final FeedListResponse expect = new FeedListResponse(eventId, feedSimpleResponses); //when - final FeedListResponse actual = feedQueryService.findAllFeeds(writer, eventId); + final List actual = feedQueryService.findAllFeeds(writer, eventId); //then assertThat(actual) @@ -135,13 +131,12 @@ void findAllFeedsWithBlockedMember() { final Feed feed3 = feedRepository.save(new Feed(event, reader, "피드3 제목", "피드3 내용")); blockRepository.save(new Block(reader.getId(), writer.getId())); - final List feedSimpleResponses = List.of( - FeedSimpleResponse.from(feed3, Collections.emptyList(), 0L) + final List expect = List.of( + FeedResponseRefactor.of(feed3, Collections.emptyList(), 0L) ); - final FeedListResponse expect = new FeedListResponse(event.getId(), feedSimpleResponses); - //when - final FeedListResponse actual = feedQueryService.findAllFeeds(reader, event.getId()); + final List actual = feedQueryService + .findAllFeeds(reader, event.getId()); //then assertThat(actual) @@ -155,11 +150,11 @@ void findAllFeedsWithNotExistFeed() { //given final Event noFeedEvent = eventRepository.save(EventFixture.구름톤()); - final FeedListResponse expect = new FeedListResponse(noFeedEvent.getId(), - Collections.emptyList()); + final List expect = Collections.emptyList(); //when - final FeedListResponse actual = feedQueryService.findAllFeeds(writer, noFeedEvent.getId()); + final List actual + = feedQueryService.findAllFeeds(writer, noFeedEvent.getId()); //then assertThat(actual) @@ -179,10 +174,11 @@ void findFeedTest() { final Feed feed = feed1; final Long feedId = feed.getId(); - final FeedDetailResponse expect = FeedDetailResponse.from(feed, Collections.emptyList()); + final FeedResponseRefactor expect = FeedResponseRefactor.of(feed, Collections.emptyList(), + 0L); //when - final FeedDetailResponse actual = feedQueryService.findFeed(writer, feedId); + final FeedResponseRefactor actual = feedQueryService.findFeed(writer, feedId); //then assertThat(actual) @@ -298,15 +294,13 @@ void findAllFeedsWithImages() { //given final Long eventId = event.getId(); - final List feedSimpleResponses = List.of( + final List expect = List.of( FeedSimpleResponse.from(feed1, images, 0L), FeedSimpleResponse.from(feed2, Collections.emptyList(), 0L) ); - final FeedListResponse expect = new FeedListResponse(eventId, feedSimpleResponses); - //when - final FeedListResponse actual = feedQueryService.findAllFeeds(writer, eventId); + final List actual = feedQueryService.findAllMyFeeds(writer); //then assertThat(actual) @@ -318,10 +312,10 @@ void findAllFeedsWithImages() { @DisplayName("피드에 이미지가 있을 경우 피드 목록에서 이미지 리스트를 order순으로 정렬하여 함께 반환한다.") void findDetailFeedWithImages() { //given - final FeedDetailResponse expect = FeedDetailResponse.from(feed1, images); + final FeedResponseRefactor expect = FeedResponseRefactor.of(feed1, images, 0L); //when - final FeedDetailResponse actual = feedQueryService.findFeed(writer, feed1.getId()); + final FeedResponseRefactor actual = feedQueryService.findFeed(writer, feed1.getId()); //then assertThat(actual) From 25aa6e7ad68a1f8d1c6fc1d6dfd9976b7c2bd98a Mon Sep 17 00:00:00 2001 From: hong-sile Date: Mon, 6 Nov 2023 12:45:58 +0900 Subject: [PATCH 32/41] =?UTF-8?q?refactor:=20feed=20=EB=AA=A9=EB=A1=9D?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20APi=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #822 --- .../java/com/emmsale/FeedApiTest.java | 80 +++++++++---------- .../java/com/emmsale/feed/api/FeedApi.java | 2 +- .../feed/application/FeedQueryService.java | 27 ++----- .../application/FeedQueryServiceTest.java | 23 +++--- 4 files changed, 56 insertions(+), 76 deletions(-) diff --git a/backend/emm-sale/src/documentTest/java/com/emmsale/FeedApiTest.java b/backend/emm-sale/src/documentTest/java/com/emmsale/FeedApiTest.java index d73d7a847..a046b58c5 100644 --- a/backend/emm-sale/src/documentTest/java/com/emmsale/FeedApiTest.java +++ b/backend/emm-sale/src/documentTest/java/com/emmsale/FeedApiTest.java @@ -14,13 +14,10 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import com.emmsale.feed.application.dto.FeedResponseRefactor; -import com.emmsale.feed.application.dto.FeedSimpleResponse; import com.emmsale.feed.application.dto.FeedUpdateRequest; import com.emmsale.feed.application.dto.FeedUpdateResponse; import com.emmsale.member.application.dto.MemberReferenceResponse; -import java.time.LocalDate; import java.time.LocalDateTime; -import java.time.LocalTime; import java.util.Collections; import java.util.List; import org.junit.jupiter.api.DisplayName; @@ -42,30 +39,29 @@ class FeedApiTest extends MockMvcTestHelper { "멤버 이미지url", "멤버 깃허브 url" ); + private static final ResponseFieldsSnippet FEEDS_RESPONSE_FIELDS = responseFields( + fieldWithPath("[].id").type(JsonFieldType.NUMBER).description("피드 id"), + fieldWithPath("[].eventId").type(JsonFieldType.NUMBER).description("피드가 작성된 부모 이벤트 id"), + fieldWithPath("[].title").type(JsonFieldType.STRING).description("피드 제목"), + fieldWithPath("[].content").type(JsonFieldType.STRING).description("피드 내용"), + fieldWithPath("[].images").type(JsonFieldType.ARRAY).description("피드 이미지 url 리스트"), + fieldWithPath("[].writer.id").type(JsonFieldType.NUMBER).description("writer의 식별자"), + fieldWithPath("[].writer.name").type(JsonFieldType.STRING).description("writer의 이름"), + fieldWithPath("[].writer.description").type(JsonFieldType.STRING) + .description("writer의 한줄 자기소개"), + fieldWithPath("[].writer.imageUrl").type(JsonFieldType.STRING) + .description("writer의 이미지 url"), + fieldWithPath("[].writer.githubUrl").type(JsonFieldType.STRING) + .description("writer의 github Url"), + fieldWithPath("[].commentCount").type(JsonFieldType.NUMBER).description("피드의 댓글 개수"), + fieldWithPath("[].createdAt").type(JsonFieldType.STRING).description("피드 생성 일시"), + fieldWithPath("[].updatedAt").type(JsonFieldType.STRING).description("피드 업데이트 일시") + ); @Test @DisplayName("이벤트의 피드 목록을 성공적으로 반환하면 200 OK를 반환한다.") void findAllFeedsTest() throws Exception { //given - final ResponseFieldsSnippet responseFields = responseFields( - fieldWithPath("[].id").type(JsonFieldType.NUMBER).description("피드 id"), - fieldWithPath("[].eventId").type(JsonFieldType.NUMBER).description("피드가 작성된 부모 이벤트 id"), - fieldWithPath("[].title").type(JsonFieldType.STRING).description("피드 제목"), - fieldWithPath("[].content").type(JsonFieldType.STRING).description("피드 내용"), - fieldWithPath("[].images").type(JsonFieldType.ARRAY).description("피드 이미지 url 리스트"), - fieldWithPath("[].writer.id").type(JsonFieldType.NUMBER).description("writer의 식별자"), - fieldWithPath("[].writer.name").type(JsonFieldType.STRING).description("writer의 이름"), - fieldWithPath("[].writer.description").type(JsonFieldType.STRING) - .description("writer의 한줄 자기소개"), - fieldWithPath("[].writer.imageUrl").type(JsonFieldType.STRING) - .description("writer의 이미지 url"), - fieldWithPath("[].writer.githubUrl").type(JsonFieldType.STRING) - .description("writer의 github Url"), - fieldWithPath("[].commentCount").type(JsonFieldType.NUMBER).description("피드의 댓글 개수"), - fieldWithPath("[].createdAt").type(JsonFieldType.STRING).description("피드 생성 일시"), - fieldWithPath("[].updatedAt").type(JsonFieldType.STRING).description("피드 업데이트 일시") - ); - final long eventId = 11L; final List response = List.of( new FeedResponseRefactor( @@ -91,7 +87,7 @@ void findAllFeedsTest() throws Exception { .param("event-id", String.valueOf(eventId))) .andExpect(status().isOk()) .andDo(print()) - .andDo(document("find-all-feed", responseFields)); + .andDo(document("find-all-feed", FEEDS_RESPONSE_FIELDS)); } @Test @@ -141,35 +137,31 @@ void findDetailFeedTest() throws Exception { @DisplayName("자신의 피드 목록을 성공적으로 반환하면 200 OK를 반환한다.") void findAllMyFeedsTest() throws Exception { //given - final ResponseFieldsSnippet responseFields = responseFields( - fieldWithPath("[].id").type(JsonFieldType.NUMBER).description("피드 id"), - fieldWithPath("[].title").type(JsonFieldType.STRING).description("피드 제목"), - fieldWithPath("[].content").type(JsonFieldType.STRING).description("피드 내용"), - fieldWithPath("[].images").type(JsonFieldType.ARRAY).description("피드 이미지 url 리스트"), - fieldWithPath("[].writerId").type(JsonFieldType.NUMBER).description("피드 작성자 id"), - fieldWithPath("[].commentCount").type(JsonFieldType.NUMBER).description("피드의 댓글 개수"), - fieldWithPath("[].createdAt").type(JsonFieldType.STRING).description("피드 생성 일시"), - fieldWithPath("[].updatedAt").type(JsonFieldType.STRING).description("피드 업데이트 일시") - ); - - final List feeds = List.of( - new FeedSimpleResponse(34L, "피드1 제목", "피드 내용", 23L, - List.of("https://image1.url", "https://image2.url"), 0L, - LocalDateTime.of(LocalDate.of(2023, 7, 13), LocalTime.of(11, 43, 11)), - LocalDateTime.of(LocalDate.of(2023, 7, 13), LocalTime.of(11, 43, 11))), - new FeedSimpleResponse(35L, "피드2 제목", "피드 내용", 43L, Collections.emptyList(), 3L, - LocalDateTime.of(LocalDate.of(2023, 7, 22), LocalTime.of(23, 54, 49)), - LocalDateTime.of(LocalDate.of(2023, 7, 22), LocalTime.of(23, 54, 49))) + final List responses = List.of( + new FeedResponseRefactor( + 34L, 2L, "피드 1 제목", "피드 1 내용", + MEMBER_REFERENCE_RESPONSE, + Collections.emptyList(), + 2L, + LocalDateTime.of(2023, 7, 13, 0, 0), LocalDateTime.of(2023, 7, 13, 0, 0) + ), + new FeedResponseRefactor( + 35L, 2L, "피드 2 제목", "피드 2 내용", + MEMBER_REFERENCE_RESPONSE, + Collections.emptyList(), + 2L, + LocalDateTime.of(2023, 7, 13, 0, 0), LocalDateTime.of(2023, 7, 13, 0, 0) + ) ); - when(feedQueryService.findAllMyFeeds(any())).thenReturn(feeds); + when(feedQueryService.findAllMyFeeds(any())).thenReturn(responses); //when & then mockMvc.perform(get("/feeds/my") .header(HttpHeaders.AUTHORIZATION, "Bearer accessToken")) .andExpect(status().isOk()) .andDo(print()) - .andDo(document("find-all-my-feed", responseFields)); + .andDo(document("find-all-my-feed", FEEDS_RESPONSE_FIELDS)); } @Test diff --git a/backend/emm-sale/src/main/java/com/emmsale/feed/api/FeedApi.java b/backend/emm-sale/src/main/java/com/emmsale/feed/api/FeedApi.java index bce4c6d74..1fc1acaad 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/feed/api/FeedApi.java +++ b/backend/emm-sale/src/main/java/com/emmsale/feed/api/FeedApi.java @@ -47,7 +47,7 @@ public FeedResponseRefactor findFeed(final Member member, @PathVariable final Lo } @GetMapping("/my") - public List findAllMyFeeds(final Member member) { + public List findAllMyFeeds(final Member member) { return feedQueryService.findAllMyFeeds(member); } diff --git a/backend/emm-sale/src/main/java/com/emmsale/feed/application/FeedQueryService.java b/backend/emm-sale/src/main/java/com/emmsale/feed/application/FeedQueryService.java index cd2dfe599..201c18537 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/feed/application/FeedQueryService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/feed/application/FeedQueryService.java @@ -10,7 +10,6 @@ import com.emmsale.event.exception.EventException; import com.emmsale.event.exception.EventExceptionType; import com.emmsale.feed.application.dto.FeedResponseRefactor; -import com.emmsale.feed.application.dto.FeedSimpleResponse; import com.emmsale.feed.domain.Feed; import com.emmsale.feed.domain.repository.FeedRepository; import com.emmsale.feed.exception.FeedException; @@ -46,21 +45,7 @@ public List findAllFeeds(final Member member, final Long e final List feeds = feedRepository.findAllByEventIdAndNotDeleted(eventId); final List filteredFeeds = excludeBlockedMembersFeed(member, feeds); - final List feedIds = filteredFeeds.stream() - .map(Feed::getId) - .collect(toUnmodifiableList()); - - final Map feedCommentCounts = getFeedIdCommentCountMap(feedIds); - final Map> feedImages = getFeedImagesMap(feedIds); - - return filteredFeeds.stream() - .map(feed -> { - final List images = feedImages.getOrDefault(feed.getId(), Collections.emptyList()); - final Long commentCount = feedCommentCounts.getOrDefault(feed.getId(), - DEFAULT_COMMENT_COUNT); - return FeedResponseRefactor.of(feed, images, commentCount); - }) - .collect(toUnmodifiableList()); + return createFeedResponses(filteredFeeds); } private Map> getFeedImagesMap(final List feedIds) { @@ -136,9 +121,13 @@ private void validateDeletedFeed(final Feed feed) { } } - public List findAllMyFeeds(final Member member) { + public List findAllMyFeeds(final Member member) { final List feeds = feedRepository.findByMember(member); + return createFeedResponses(feeds); + } + + private List createFeedResponses(List feeds) { final List feedIds = feeds.stream() .map(Feed::getId) .collect(Collectors.toList()); @@ -151,8 +140,8 @@ public List findAllMyFeeds(final Member member) { final List images = feedImages.getOrDefault(feed.getId(), Collections.emptyList()); final Long commentCount = feedCommentCounts.getOrDefault(feed.getId(), DEFAULT_COMMENT_COUNT); - return FeedSimpleResponse.from(feed, images, commentCount); + return FeedResponseRefactor.of(feed, images, commentCount); }) - .collect(Collectors.toList()); + .collect(toUnmodifiableList()); } } diff --git a/backend/emm-sale/src/test/java/com/emmsale/feed/application/FeedQueryServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/feed/application/FeedQueryServiceTest.java index 006e4131f..8ebbaf051 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/feed/application/FeedQueryServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/feed/application/FeedQueryServiceTest.java @@ -12,7 +12,6 @@ import com.emmsale.event.exception.EventException; import com.emmsale.event.exception.EventExceptionType; import com.emmsale.feed.application.dto.FeedResponseRefactor; -import com.emmsale.feed.application.dto.FeedSimpleResponse; import com.emmsale.feed.domain.Feed; import com.emmsale.feed.domain.repository.FeedRepository; import com.emmsale.feed.exception.FeedException; @@ -234,13 +233,13 @@ class FindAllMy { @DisplayName("자신이 작성한 모든 피드를 조회한다.") void findAllFeedsTest() { //given - final List expect = List.of( - FeedSimpleResponse.from(feed1, Collections.emptyList(), 0L), - FeedSimpleResponse.from(feed2, Collections.emptyList(), 0L) + final List expect = List.of( + FeedResponseRefactor.of(feed1, Collections.emptyList(), 0L), + FeedResponseRefactor.of(feed2, Collections.emptyList(), 0L) ); //when - final List actual = feedQueryService.findAllMyFeeds(writer); + final List actual = feedQueryService.findAllMyFeeds(writer); //then assertThat(actual) @@ -255,12 +254,12 @@ void findAllFeedsWithWithDeletedFeedTest() { feed1.delete(); feedRepository.save(feed1); - final List expect = List.of( - FeedSimpleResponse.from(feed2, Collections.emptyList(), 0L) + final List expect = List.of( + FeedResponseRefactor.of(feed2, Collections.emptyList(), 0L) ); //when - final List actual = feedQueryService.findAllMyFeeds(writer); + final List actual = feedQueryService.findAllMyFeeds(writer); //then assertThat(actual) @@ -294,13 +293,13 @@ void findAllFeedsWithImages() { //given final Long eventId = event.getId(); - final List expect = List.of( - FeedSimpleResponse.from(feed1, images, 0L), - FeedSimpleResponse.from(feed2, Collections.emptyList(), 0L) + final List expect = List.of( + FeedResponseRefactor.of(feed1, images, 0L), + FeedResponseRefactor.of(feed2, Collections.emptyList(), 0L) ); //when - final List actual = feedQueryService.findAllMyFeeds(writer); + final List actual = feedQueryService.findAllMyFeeds(writer); //then assertThat(actual) From 3fd58bd30e865ea7ec668ce643a6337e3eb4f394 Mon Sep 17 00:00:00 2001 From: amaran-th Date: Wed, 8 Nov 2023 11:10:06 +0900 Subject: [PATCH 33/41] =?UTF-8?q?fix:=20=EC=8A=A4=ED=81=AC=EB=9E=A9=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EC=8B=9C=20=EA=B2=80=EC=A6=9D=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EC=83=81=ED=83=9C=EC=BD=94=EB=93=9C=EB=A5=BC=20noC?= =?UTF-8?q?ontent=20=EB=8C=80=EC=8B=A0=20ok=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #823 --- .../src/documentTest/java/com/emmsale/ScrapApiTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/emm-sale/src/documentTest/java/com/emmsale/ScrapApiTest.java b/backend/emm-sale/src/documentTest/java/com/emmsale/ScrapApiTest.java index e784bb06e..9b7d2d5bb 100644 --- a/backend/emm-sale/src/documentTest/java/com/emmsale/ScrapApiTest.java +++ b/backend/emm-sale/src/documentTest/java/com/emmsale/ScrapApiTest.java @@ -214,7 +214,7 @@ void deleteScrap() throws Exception { //then mockMvc.perform(delete("/scraps?event-id={eventId}", eventId) .header(HttpHeaders.AUTHORIZATION, "Bearer AccessToken")) - .andExpect(status().isNoContent()) + .andExpect(status().isOk()) .andDo(document("delete-scrap", SCRAPPED_EVENT_RESPONSE_FIELDS)); } } From 0d180a6470ab477a92ba2611544aa45924a6ff41 Mon Sep 17 00:00:00 2001 From: amaran-th Date: Wed, 8 Nov 2023 20:27:58 +0900 Subject: [PATCH 34/41] =?UTF-8?q?fix:=20N+1=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #821 --- .../java/com/emmsale/member/domain/Member.java | 13 ++++++++----- .../application/RoomQueryService.java | 3 --- .../application/dto/RoomResponse.java | 3 +-- .../infrastructure/persistence/MessageDao.java | 17 ++++++++++++++--- .../persistence/dto/MessageOverview.java | 3 ++- .../application/RoomQueryServiceTest.java | 8 ++++---- .../persistence/MessageDaoTest.java | 5 +++-- 7 files changed, 32 insertions(+), 20 deletions(-) diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/domain/Member.java b/backend/emm-sale/src/main/java/com/emmsale/member/domain/Member.java index 51003a9d1..4f916de76 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/member/domain/Member.java +++ b/backend/emm-sale/src/main/java/com/emmsale/member/domain/Member.java @@ -35,20 +35,23 @@ public class Member extends BaseEntity { private String githubUsername; public Member(final Long id, final Long githubId, final String imageUrl, final String name, + final String description, final String githubUsername) { this.id = id; this.githubId = githubId; this.imageUrl = imageUrl; this.name = name; - this.description = DEFAULT_DESCRIPTION; + this.description = description; this.githubUsername = githubUsername; } + public Member(final Long id, final Long githubId, final String imageUrl, final String name, + final String githubUsername) { + this(id, githubId, imageUrl, name, DEFAULT_DESCRIPTION, githubUsername); + } + public Member(final Long githubId, final String imageUrl, final String githubUsername) { - this.githubId = githubId; - this.imageUrl = imageUrl; - this.description = DEFAULT_DESCRIPTION; - this.githubUsername = githubUsername; + this(null, githubId, imageUrl, null, DEFAULT_DESCRIPTION, githubUsername); } public void updateName(final String name) { diff --git a/backend/emm-sale/src/main/java/com/emmsale/message_room/application/RoomQueryService.java b/backend/emm-sale/src/main/java/com/emmsale/message_room/application/RoomQueryService.java index f690c55b3..93da4734d 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/message_room/application/RoomQueryService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/message_room/application/RoomQueryService.java @@ -51,9 +51,6 @@ public List findAll(final Member loginMember, final Long memberId) .map(messageOverview -> RoomResponse.from( messageOverview, - memberRepository.findById(messageOverview.getSenderId()) - .orElseThrow(() -> new MemberException(NOT_FOUND_MEMBER)), - // TODO: 2023/11/05 N+1 문제 findInterlocutor(interlocutorIdPerRoomExceptMe.get(messageOverview.getRoomUUID())) )) .collect(Collectors.toList()); diff --git a/backend/emm-sale/src/main/java/com/emmsale/message_room/application/dto/RoomResponse.java b/backend/emm-sale/src/main/java/com/emmsale/message_room/application/dto/RoomResponse.java index 22cd71023..222fcd79b 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/message_room/application/dto/RoomResponse.java +++ b/backend/emm-sale/src/main/java/com/emmsale/message_room/application/dto/RoomResponse.java @@ -17,7 +17,6 @@ public class RoomResponse { public static RoomResponse from( final MessageOverview messageOverview, - final Member recentMessageSender, final Member interlocutor ) { return new RoomResponse( @@ -26,7 +25,7 @@ public static RoomResponse from( MessageResponse.from(new Message( messageOverview.getId(), messageOverview.getContent(), - recentMessageSender, + messageOverview.getSender(), messageOverview.getRoomUUID(), messageOverview.getCreatedAt())) ); diff --git a/backend/emm-sale/src/main/java/com/emmsale/message_room/infrastructure/persistence/MessageDao.java b/backend/emm-sale/src/main/java/com/emmsale/message_room/infrastructure/persistence/MessageDao.java index 7627785a5..21f991a99 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/message_room/infrastructure/persistence/MessageDao.java +++ b/backend/emm-sale/src/main/java/com/emmsale/message_room/infrastructure/persistence/MessageDao.java @@ -1,6 +1,7 @@ package com.emmsale.message_room.infrastructure.persistence; +import com.emmsale.member.domain.Member; import com.emmsale.message_room.infrastructure.persistence.dto.MessageOverview; import java.util.List; import lombok.RequiredArgsConstructor; @@ -14,9 +15,16 @@ public class MessageDao { private static final RowMapper ROW_MAPPER = (rs, rowNum) -> new MessageOverview( - rs.getLong("id"), + rs.getLong("m2.id"), rs.getString("content"), - rs.getLong("sender_id"), + new Member( + rs.getLong("sender.id"), + rs.getLong("github_id"), + rs.getString("image_url"), + rs.getString("name"), + rs.getString("description"), + rs.getString("github_username") + ), rs.getTimestamp("created_at").toLocalDateTime(), rs.getString("room_id") ); @@ -27,12 +35,15 @@ public List findRecentlyMessages(final Long memberId) { StringBuilder sqlBuilder = new StringBuilder(); sqlBuilder - .append("SELECT m2.id, m2.content, m2.sender_id, m2.created_at, m2.room_id ") + .append("SELECT m2.id, m2.content, m2.sender_id, m2.created_at, m2.room_id, ") + .append( + "sender.id, sender.github_id, sender.image_url, sender.name, sender.description, sender.github_username ") .append("FROM room r ") .append("JOIN (SELECT room_id, MAX(created_at) as max_created_at ") .append(" FROM message ") .append(" GROUP BY room_id) m1 ON r.uuid = m1.room_id ") .append("JOIN message m2 ON m1.room_id = m2.room_id AND m1.max_created_at = m2.created_at ") + .append("JOIN member sender ON m2.sender_id = sender.id ") .append("WHERE r.member_id = ? ") .append("AND m2.created_at > r.last_exited_time ") .append("ORDER BY m2.created_at DESC"); diff --git a/backend/emm-sale/src/main/java/com/emmsale/message_room/infrastructure/persistence/dto/MessageOverview.java b/backend/emm-sale/src/main/java/com/emmsale/message_room/infrastructure/persistence/dto/MessageOverview.java index f31bc0fd8..3bd09f8db 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/message_room/infrastructure/persistence/dto/MessageOverview.java +++ b/backend/emm-sale/src/main/java/com/emmsale/message_room/infrastructure/persistence/dto/MessageOverview.java @@ -1,5 +1,6 @@ package com.emmsale.message_room.infrastructure.persistence.dto; +import com.emmsale.member.domain.Member; import java.time.LocalDateTime; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -10,7 +11,7 @@ public class MessageOverview { private final Long id; private final String content; - private final Long senderId; + private final Member sender; private final LocalDateTime createdAt; private final String roomUUID; } diff --git a/backend/emm-sale/src/test/java/com/emmsale/message_room/application/RoomQueryServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/message_room/application/RoomQueryServiceTest.java index cd1ccb5af..7ecdbfeda 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/message_room/application/RoomQueryServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/message_room/application/RoomQueryServiceTest.java @@ -107,7 +107,7 @@ void test_findAll() throws Exception { final MessageOverview messageOverview1 = new MessageOverview( resultMessage1.getId(), resultMessage1.getContent(), - resultMessage1.getSender().getId(), + resultMessage1.getSender(), resultMessage1.getCreatedAt(), resultMessage1.getRoomId() ); @@ -115,15 +115,15 @@ void test_findAll() throws Exception { final MessageOverview messageOverview2 = new MessageOverview( resultMessage2.getId(), resultMessage2.getContent(), - resultMessage2.getSender().getId(), + resultMessage2.getSender(), resultMessage2.getCreatedAt(), resultMessage2.getRoomId() ); final List expect = List.of( - RoomResponse.from(messageOverview1, resultMessage1.getSender(), + RoomResponse.from(messageOverview1, memberRepository.findById(room1Interlocutor.getId()).get()), - RoomResponse.from(messageOverview2, resultMessage2.getSender(), + RoomResponse.from(messageOverview2, memberRepository.findById(room2Interlocutor.getId()).get()) ); diff --git a/backend/emm-sale/src/test/java/com/emmsale/message_room/infrastructure/persistence/MessageDaoTest.java b/backend/emm-sale/src/test/java/com/emmsale/message_room/infrastructure/persistence/MessageDaoTest.java index 3aa1f63ff..c8ac8696d 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/message_room/infrastructure/persistence/MessageDaoTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/message_room/infrastructure/persistence/MessageDaoTest.java @@ -109,14 +109,14 @@ void test_findRecentlyMessages() throws Exception { new MessageOverview( resultMessage1.getId(), resultMessage1.getContent(), - resultMessage1.getSender().getId(), + resultMessage1.getSender(), resultMessage1.getCreatedAt(), resultMessage1.getRoomId() ), new MessageOverview( resultMessage2.getId(), resultMessage2.getContent(), - resultMessage2.getSender().getId(), + resultMessage2.getSender(), resultMessage2.getCreatedAt(), resultMessage2.getRoomId() ) @@ -128,6 +128,7 @@ void test_findRecentlyMessages() throws Exception { //then Assertions.assertThat(actual) .usingRecursiveComparison() + .ignoringFields("sender.createdAt", "sender.updatedAt") .isEqualTo(expect); } } From 4471047b7b557fba908fc06b14cc06a9a3423994 Mon Sep 17 00:00:00 2001 From: hong-sile Date: Tue, 21 Nov 2023 15:50:58 +0900 Subject: [PATCH 35/41] =?UTF-8?q?refactor:=20=EB=B0=98=ED=99=98=EA=B0=92?= =?UTF-8?q?=EC=9D=84=20MemberACtivityResponse=20->=20ActivityResponse?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #820 --- .../MemberActivityCommandService.java | 11 +++++---- .../MemberActivityCommandServiceTest.java | 24 +++++++++---------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberActivityCommandService.java b/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberActivityCommandService.java index 10d662412..58192eae9 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberActivityCommandService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberActivityCommandService.java @@ -2,6 +2,7 @@ import static java.util.stream.Collectors.toUnmodifiableList; +import com.emmsale.activity.application.dto.ActivityResponse; import com.emmsale.activity.domain.ActivityRepository; import com.emmsale.member.application.dto.MemberActivityAddRequest; import com.emmsale.member.application.dto.MemberActivityInitialRequest; @@ -58,7 +59,7 @@ private void validateAllActivityIdsExist( } } - public List addActivity( + public List addActivity( final Member member, final MemberActivityAddRequest memberActivityAddRequest ) { @@ -74,7 +75,8 @@ public List addActivity( return memberActivityRepository.findAllByMember(member) .stream() - .map(MemberActivityResponse::from) + .map(MemberActivity::getActivity) + .map(ActivityResponse::from) .collect(toUnmodifiableList()); } @@ -91,7 +93,7 @@ private boolean hasDuplicateId(final List activityIds) { return new HashSet<>(activityIds).size() != activityIds.size(); } - public List deleteActivity( + public List deleteActivity( final Member member, final List deleteActivityIds ) { @@ -105,7 +107,8 @@ public List deleteActivity( return memberActivityRepository.findAllByMember(member) .stream() - .map(MemberActivityResponse::from) + .map(MemberActivity::getActivity) + .map(ActivityResponse::from) .collect(toUnmodifiableList()); } } diff --git a/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberActivityCommandServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberActivityCommandServiceTest.java index c78514045..625d2311b 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberActivityCommandServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberActivityCommandServiceTest.java @@ -6,10 +6,10 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; +import com.emmsale.activity.application.dto.ActivityResponse; import com.emmsale.helper.ServiceIntegrationTestHelper; import com.emmsale.member.application.dto.MemberActivityAddRequest; import com.emmsale.member.application.dto.MemberActivityInitialRequest; -import com.emmsale.member.application.dto.MemberActivityResponse; import com.emmsale.member.domain.Member; import com.emmsale.member.domain.MemberRepository; import com.emmsale.member.exception.MemberException; @@ -84,17 +84,17 @@ void addActivity() { final Member member = memberRepository.findById(savedMemberId).get(); final MemberActivityAddRequest request = new MemberActivityAddRequest(activityIds); - final List expected = List.of( - new MemberActivityResponse(1L, "YAPP", "동아리"), - new MemberActivityResponse(2L, "DND", "동아리"), - new MemberActivityResponse(3L, "nexters", "동아리"), - new MemberActivityResponse(5L, "인프콘", "컨퍼런스"), - new MemberActivityResponse(6L, "우아한테크코스", "교육"), - new MemberActivityResponse(7L, "Backend", "직무") + final List expected = List.of( + new ActivityResponse(1L, "동아리", "YAPP"), + new ActivityResponse(2L, "동아리", "DND"), + new ActivityResponse(3L, "동아리", "nexters"), + new ActivityResponse(4L, "컨퍼런스", "인프콘"), + new ActivityResponse(5L, "교육", "우아한테크코스"), + new ActivityResponse(6L, "직무", "Backend") ); //when - final List actual = memberActivityCommandService.addActivity(member, + final List actual = memberActivityCommandService.addActivity(member, request); //then assertThat(expected) @@ -154,12 +154,12 @@ void test_deleteActivity() throws Exception { final Member member = memberRepository.findById(savedMemberId).get(); - final List expected = List.of( - new MemberActivityResponse(3L, "nexters", "동아리") + final List expected = List.of( + new ActivityResponse(3L, "동아리", "nexters") ); //when - final List actual = memberActivityCommandService.deleteActivity(member, + final List actual = memberActivityCommandService.deleteActivity(member, deleteActivityIds); //then From 90b51389fd85aa38c0825924100126ed20c2a118 Mon Sep 17 00:00:00 2001 From: hong-sile Date: Tue, 21 Nov 2023 17:19:02 +0900 Subject: [PATCH 36/41] =?UTF-8?q?refactor:=20=EB=B0=98=ED=99=98=EA=B0=92?= =?UTF-8?q?=EC=9D=84=20MemberACtivityResponse=20->=20ActivityResponse?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #840 --- .../MemberActivityQueryService.java | 8 ++-- .../MemberActivityQueryServiceTest.java | 40 +++++++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 backend/emm-sale/src/test/java/com/emmsale/member/application/MemberActivityQueryServiceTest.java diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberActivityQueryService.java b/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberActivityQueryService.java index b7ae5fe67..906983dd6 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberActivityQueryService.java +++ b/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberActivityQueryService.java @@ -2,8 +2,9 @@ import static java.util.stream.Collectors.toUnmodifiableList; -import com.emmsale.member.application.dto.MemberActivityResponse; +import com.emmsale.activity.application.dto.ActivityResponse; import com.emmsale.member.domain.Member; +import com.emmsale.member.domain.MemberActivity; import com.emmsale.member.domain.MemberActivityRepository; import com.emmsale.member.domain.MemberRepository; import java.util.List; @@ -19,12 +20,13 @@ public class MemberActivityQueryService { private final MemberRepository memberRepository; private final MemberActivityRepository memberActivityRepository; - public List findActivities(final Long memberId) { + public List findActivities(final Long memberId) { final Member member = memberRepository.getByIdOrElseThrow(memberId); return memberActivityRepository.findAllByMember(member) .stream() - .map(MemberActivityResponse::from) + .map(MemberActivity::getActivity) + .map(ActivityResponse::from) .collect(toUnmodifiableList()); } } diff --git a/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberActivityQueryServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberActivityQueryServiceTest.java new file mode 100644 index 000000000..41d09729c --- /dev/null +++ b/backend/emm-sale/src/test/java/com/emmsale/member/application/MemberActivityQueryServiceTest.java @@ -0,0 +1,40 @@ +package com.emmsale.member.application; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.emmsale.activity.application.dto.ActivityResponse; +import com.emmsale.helper.ServiceIntegrationTestHelper; +import com.emmsale.member.domain.MemberRepository; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class MemberActivityQueryServiceTest extends ServiceIntegrationTestHelper { + + @Autowired + private MemberRepository memberRepository; + + @Autowired + private MemberActivityQueryService memberActivityQueryService; + + @Test + @DisplayName("findActivities(): 유저의 Activity들을 조회한다.") + void findActivities() { + final Long memberId = 1L; + + final List actual + = memberActivityQueryService.findActivities(memberId); + + final List expected = List.of( + new ActivityResponse(1L, "동아리", "YAPP"), + new ActivityResponse(2L, "동아리", "DND"), + new ActivityResponse(3L, "동아리", "nexters") + ); + + assertThat(actual) + .usingRecursiveComparison() + .ignoringCollectionOrder() + .isEqualTo(expected); + } +} From e288371991100a332367f948f1168002b5ecf474 Mon Sep 17 00:00:00 2001 From: hong-sile Date: Tue, 21 Nov 2023 17:45:19 +0900 Subject: [PATCH 37/41] =?UTF-8?q?refactor:=20API=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EB=A6=AC=ED=8C=A9=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #840 --- .../java/com/emmsale/MemberApiTest.java | 30 +++++++++---------- .../application/dto/ActivityResponse.java | 22 +++++--------- .../com/emmsale/member/api/MemberApi.java | 7 +++-- 3 files changed, 26 insertions(+), 33 deletions(-) diff --git a/backend/emm-sale/src/documentTest/java/com/emmsale/MemberApiTest.java b/backend/emm-sale/src/documentTest/java/com/emmsale/MemberApiTest.java index 74ee8554c..2e14caf91 100644 --- a/backend/emm-sale/src/documentTest/java/com/emmsale/MemberApiTest.java +++ b/backend/emm-sale/src/documentTest/java/com/emmsale/MemberApiTest.java @@ -16,6 +16,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import com.emmsale.activity.application.dto.ActivityResponse; import com.emmsale.member.api.MemberApi; import com.emmsale.member.application.dto.DescriptionRequest; import com.emmsale.member.application.dto.MemberActivityAddRequest; @@ -95,10 +96,10 @@ void addActivity() throws Exception { final List activityIds = List.of(4L, 5L, 6L); final MemberActivityAddRequest request = new MemberActivityAddRequest(activityIds); - final List memberActivityResponses = createMemberActivityResponses(); + final List activityResponses = createActivityResponses(); when(memberActivityCommandService.addActivity(any(), any())) - .thenReturn(memberActivityResponses); + .thenReturn(activityResponses); //when & then mockMvc.perform(post("/members/activities") @@ -111,14 +112,14 @@ void addActivity() throws Exception { MEMBER_ACTIVITY_RESPONSE_FIELDS)); } - private List createMemberActivityResponses() { + private List createActivityResponses() { return List.of( - new MemberActivityResponse(1L, "YAPP", "동아리"), - new MemberActivityResponse(2L, "DND", "동아리"), - new MemberActivityResponse(3L, "nexters", "동아리"), - new MemberActivityResponse(4L, "인프콘", "컨퍼런스"), - new MemberActivityResponse(5L, "우아한테크코스", "교육"), - new MemberActivityResponse(6L, "Backend", "직무") + new ActivityResponse(1L, "YAPP", "동아리"), + new ActivityResponse(2L, "DND", "동아리"), + new ActivityResponse(3L, "nexters", "동아리"), + new ActivityResponse(4L, "인프콘", "컨퍼런스"), + new ActivityResponse(5L, "우아한테크코스", "교육"), + new ActivityResponse(6L, "Backend", "직무") ); } @@ -128,12 +129,12 @@ void test_deleteActivity() throws Exception { //given final String activityIds = "1,2"; - final List memberActivityResponses = List.of( - new MemberActivityResponse(3L, "nexters", "동아리") + final List activityResponses = List.of( + new ActivityResponse(3L, "nexters", "동아리") ); when(memberActivityCommandService.deleteActivity(any(), any())) - .thenReturn(memberActivityResponses); + .thenReturn(activityResponses); //when & then mockMvc.perform( @@ -149,11 +150,10 @@ void test_deleteActivity() throws Exception { @DisplayName("내 활동들을 조회할 수 있다.") void test_findActivity() throws Exception { //given - final List memberActivityResponse = createMemberActivityResponses(); + final List memberActivityResponse = createActivityResponses(); //when - when(memberActivityQueryService.findActivities(any())) - .thenReturn(memberActivityResponse); + when(memberActivityQueryService.findActivities(any())).thenReturn(memberActivityResponse); //then mockMvc.perform(get("/members/1/activities") diff --git a/backend/emm-sale/src/main/java/com/emmsale/activity/application/dto/ActivityResponse.java b/backend/emm-sale/src/main/java/com/emmsale/activity/application/dto/ActivityResponse.java index e101a9421..e7c6488dc 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/activity/application/dto/ActivityResponse.java +++ b/backend/emm-sale/src/main/java/com/emmsale/activity/application/dto/ActivityResponse.java @@ -1,9 +1,13 @@ package com.emmsale.activity.application.dto; import com.emmsale.activity.domain.Activity; +import lombok.Getter; import lombok.RequiredArgsConstructor; +import lombok.ToString; @RequiredArgsConstructor +@ToString +@Getter public class ActivityResponse { private final Long id; @@ -12,21 +16,9 @@ public class ActivityResponse { public static ActivityResponse from(final Activity activity) { return new ActivityResponse( - activity.getId(), - activity.getActivityType().getValue(), - activity.getName() + activity.getId(), + activity.getActivityType().getValue(), + activity.getName() ); } - - public Long getId() { - return id; - } - - public String getActivityType() { - return activityType; - } - - public String getName() { - return name; - } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/api/MemberApi.java b/backend/emm-sale/src/main/java/com/emmsale/member/api/MemberApi.java index 6bf9b1958..406216ce4 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/member/api/MemberApi.java +++ b/backend/emm-sale/src/main/java/com/emmsale/member/api/MemberApi.java @@ -1,5 +1,6 @@ package com.emmsale.member.api; +import com.emmsale.activity.application.dto.ActivityResponse; import com.emmsale.member.application.MemberActivityCommandService; import com.emmsale.member.application.MemberActivityQueryService; import com.emmsale.member.application.MemberCommandService; @@ -46,7 +47,7 @@ public ResponseEntity register( } @PostMapping("/members/activities") - public ResponseEntity> addActivity( + public ResponseEntity> addActivity( final Member member, @RequestBody final MemberActivityAddRequest memberActivityAddRequest ) { @@ -55,14 +56,14 @@ public ResponseEntity> addActivity( } @DeleteMapping("/members/activities") - public ResponseEntity> deleteActivity(final Member member, + public ResponseEntity> deleteActivity(final Member member, @RequestParam("activity-ids") final List deleteActivityIds) { return ResponseEntity.ok( memberActivityCommandService.deleteActivity(member, deleteActivityIds)); } @GetMapping("/members/{member-id}/activities") - public ResponseEntity> findActivity( + public ResponseEntity> findActivity( @PathVariable("member-id") final Long memberId) { return ResponseEntity.ok(memberActivityQueryService.findActivities(memberId)); } From 22a2f49025ec4b6873282b3673579f7d471c432c Mon Sep 17 00:00:00 2001 From: amaran-th Date: Wed, 22 Nov 2023 17:44:14 +0900 Subject: [PATCH 38/41] =?UTF-8?q?feat:=20dev=20=EC=84=9C=EB=B2=84=20?= =?UTF-8?q?=EC=9D=B4=EA=B4=80=EC=97=90=20=EB=94=B0=EB=A5=B8=20=ED=99=98?= =?UTF-8?q?=EA=B2=BD=20=EC=A0=95=EB=B3=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #844 --- backend/emm-sale/src/main/resources/kerdy-submodule | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/emm-sale/src/main/resources/kerdy-submodule b/backend/emm-sale/src/main/resources/kerdy-submodule index 201498016..73be18d55 160000 --- a/backend/emm-sale/src/main/resources/kerdy-submodule +++ b/backend/emm-sale/src/main/resources/kerdy-submodule @@ -1 +1 @@ -Subproject commit 201498016c6f27f34972c92cf1699252e294033b +Subproject commit 73be18d55240fd16a8f077a57e1aee049fe5e7ae From 5bfd564857e3286550b26bfc00431a595871fee5 Mon Sep 17 00:00:00 2001 From: amaran-th Date: Wed, 22 Nov 2023 18:43:56 +0900 Subject: [PATCH 39/41] =?UTF-8?q?feat:=20dev=20=EC=84=9C=EB=B2=84=20?= =?UTF-8?q?=EC=9D=B4=EA=B4=80=EC=97=90=20=EB=94=B0=EB=A5=B8=20=ED=99=98?= =?UTF-8?q?=EA=B2=BD=20=EC=A0=95=EB=B3=B4=20=EC=88=98=EC=A0=95=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #844 --- backend/emm-sale/src/main/resources/kerdy-submodule | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/emm-sale/src/main/resources/kerdy-submodule b/backend/emm-sale/src/main/resources/kerdy-submodule index 73be18d55..bf84ca0f4 160000 --- a/backend/emm-sale/src/main/resources/kerdy-submodule +++ b/backend/emm-sale/src/main/resources/kerdy-submodule @@ -1 +1 @@ -Subproject commit 73be18d55240fd16a8f077a57e1aee049fe5e7ae +Subproject commit bf84ca0f4e052294abde21c0f3923aee69fd8c1a From 0b1eb03ae9a370af7597179498e0c9e7035a68de Mon Sep 17 00:00:00 2001 From: HyeonjaeKwon Date: Wed, 22 Nov 2023 20:16:12 +0900 Subject: [PATCH 40/41] =?UTF-8?q?refactor:=20=EC=84=9C=EB=B8=8C=EB=AA=A8?= =?UTF-8?q?=EB=93=88=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/emm-sale/src/main/resources/kerdy-submodule | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/emm-sale/src/main/resources/kerdy-submodule b/backend/emm-sale/src/main/resources/kerdy-submodule index bf84ca0f4..0de927e9c 160000 --- a/backend/emm-sale/src/main/resources/kerdy-submodule +++ b/backend/emm-sale/src/main/resources/kerdy-submodule @@ -1 +1 @@ -Subproject commit bf84ca0f4e052294abde21c0f3923aee69fd8c1a +Subproject commit 0de927e9c8f427883b94f9d72a51f7879e0fca10 From 54ae78983d03b2c575b549a4187c72b27cfc214b Mon Sep 17 00:00:00 2001 From: hong-sile Date: Mon, 27 Nov 2023 15:21:24 +0900 Subject: [PATCH 41/41] =?UTF-8?q?docs:=20privacy.html=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #852 --- .../src/main/resources/static/privacy.html | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 backend/emm-sale/src/main/resources/static/privacy.html diff --git a/backend/emm-sale/src/main/resources/static/privacy.html b/backend/emm-sale/src/main/resources/static/privacy.html new file mode 100644 index 000000000..4cb669818 --- /dev/null +++ b/backend/emm-sale/src/main/resources/static/privacy.html @@ -0,0 +1,47 @@ + + + + + + 개인정보처리방침 + + + + + + + + + + + + + + + + + + + +

< Kerdy >('https://kerdy.kro.kr/'이하 'Kerdy')은(는) 「개인정보 보호법」 제30조에 따라 정보주체의 개인정보를 보호하고 이와 관련한 고충을 신속하고 원활하게 처리할 수 있도록 하기 위하여 다음과 같이 개인정보 처리방침을 수립·공개합니다.

○ 이 개인정보처리방침은 2023728부터 적용됩니다.


제1조(개인정보의 처리 목적)

< Kerdy >('https://kerdy.kro.kr/'이하 'Kerdy')은(는) 다음의 목적을 위하여 개인정보를 처리합니다. 처리하고 있는 개인정보는 다음의 목적 이외의 용도로는 이용되지 않으며 이용 목적이 변경되는 경우에는 「개인정보 보호법」 제18조에 따라 별도의 동의를 받는 등 필요한 조치를 이행할 예정입니다.

    1. 홈페이지 회원가입 및 관리

    회원 가입의사 확인, 회원자격 유지·관리 목적으로 개인정보를 처리합니다.




제2조(개인정보의 처리 및 보유 기간)

< Kerdy >은(는) 법령에 따른 개인정보 보유·이용기간 또는 정보주체로부터 개인정보를 수집 시에 동의받은 개인정보 보유·이용기간 내에서 개인정보를 처리·보유합니다.

② 각각의 개인정보 처리 및 보유 기간은 다음과 같습니다.

  • 1.<홈페이지 회원가입 및 관리>
  • <홈페이지 회원가입 및 관리>와 관련한 개인정보는 수집.이용에 관한 동의일로부터<지체없이 파기>까지 위 이용목적을 위하여 보유.이용됩니다.
  • 보유근거 : 사용자 로그인 정보를 바탕으로 푸시 알림을 제공합니다.
  • 관련법령 :
  • 예외사유 :


제3조(처리하는 개인정보의 항목)

< Kerdy >은(는) 다음의 개인정보 항목을 처리하고 있습니다.

  • 1< 홈페이지 회원가입 및 관리 >
  • 필수항목 : 이름
  • 선택항목 :


제4조(개인정보의 파기절차 및 파기방법)


① < Kerdy > 은(는) 개인정보 보유기간의 경과, 처리목적 달성 등 개인정보가 불필요하게 되었을 때에는 지체없이 해당 개인정보를 파기합니다.

② 정보주체로부터 동의받은 개인정보 보유기간이 경과하거나 처리목적이 달성되었음에도 불구하고 다른 법령에 따라 개인정보를 계속 보존하여야 하는 경우에는, 해당 개인정보를 별도의 데이터베이스(DB)로 옮기거나 보관장소를 달리하여 보존합니다.
1. 법령 근거 :
2. 보존하는 개인정보 항목 : 계좌정보, 거래날짜

③ 개인정보 파기의 절차 및 방법은 다음과 같습니다.
1. 파기절차
< Kerdy > 은(는) 파기 사유가 발생한 개인정보를 선정하고, < Kerdy > 의 개인정보 보호책임자의 승인을 받아 개인정보를 파기합니다.

2. 파기방법

전자적 파일 형태의 정보는 기록을 재생할 수 없는 기술적 방법을 사용합니다



제5조(정보주체와 법정대리인의 권리·의무 및 그 행사방법에 관한 사항)



① 정보주체는 Kerdy에 대해 언제든지 개인정보 열람·정정·삭제·처리정지 요구 등의 권리를 행사할 수 있습니다.

② 제1항에 따른 권리 행사는Kerdy에 대해 「개인정보 보호법」 시행령 제41조제1항에 따라 서면, 전자우편, 모사전송(FAX) 등을 통하여 하실 수 있으며 Kerdy은(는) 이에 대해 지체 없이 조치하겠습니다.

③ 제1항에 따른 권리 행사는 정보주체의 법정대리인이나 위임을 받은 자 등 대리인을 통하여 하실 수 있습니다.이 경우 “개인정보 처리 방법에 관한 고시(제2020-7호)” 별지 제11호 서식에 따른 위임장을 제출하셔야 합니다.

④ 개인정보 열람 및 처리정지 요구는 「개인정보 보호법」 제35조 제4항, 제37조 제2항에 의하여 정보주체의 권리가 제한 될 수 있습니다.

⑤ 개인정보의 정정 및 삭제 요구는 다른 법령에서 그 개인정보가 수집 대상으로 명시되어 있는 경우에는 그 삭제를 요구할 수 없습니다.

⑥ Kerdy은(는) 정보주체 권리에 따른 열람의 요구, 정정·삭제의 요구, 처리정지의 요구 시 열람 등 요구를 한 자가 본인이거나 정당한 대리인인지를 확인합니다.



제6조(개인정보의 안전성 확보조치에 관한 사항)

< Kerdy >
은(는) 개인정보의 안전성 확보를 위해 다음과 같은 조치를 취하고 있습니다.

1. 개인정보에 대한 접근 제한
개인정보를 처리하는 데이터베이스시스템에 대한 접근권한의 부여,변경,말소를 통하여 개인정보에 대한 접근통제를 위하여 필요한 조치를 하고 있으며 침입차단시스템을 이용하여 외부로부터의 무단 접근을 통제하고 있습니다.



제7조(개인정보를 자동으로 수집하는 장치의 설치·운영 및 그 거부에 관한 사항)



Kerdy 은(는) 정보주체의 이용정보를 저장하고 수시로 불러오는 ‘쿠키(cookie)’를 사용하지 않습니다.

제8조 (개인정보 보호책임자에 관한 사항)

Kerdy 은(는) 개인정보 처리에 관한 업무를 총괄해서 책임지고, 개인정보 처리와 관련한 정보주체의 불만처리 및 피해구제 등을 위하여 아래와 같이 개인정보 보호책임자를 지정하고 있습니다.

  • ▶ 개인정보 보호책임자
  • 성명 :Kerdy
  • 직책 :Developer
  • 직급 :Developer
  • 연락처 :01099846497, bbuna1592@gmail.com,

※ 개인정보 보호 담당부서로 연결됩니다.

  • ▶ 개인정보 보호 담당부서
  • 부서명 :
  • 담당자 :
  • 연락처 :, ,

② 정보주체께서는 Kerdy 의 서비스(또는 사업)을 이용하시면서 발생한 모든 개인정보 보호 관련 문의, 불만처리, 피해구제 등에 관한 사항을 개인정보 보호책임자 및 담당부서로 문의하실 수 있습니다. Kerdy 은(는) 정보주체의 문의에 대해 지체 없이 답변 및 처리해드릴 것입니다.

제9조(개인정보의 열람청구를 접수·처리하는 부서)
정보주체는 「개인정보 보호법」 제35조에 따른 개인정보의 열람 청구를 아래의 부서에 할 수 있습니다.
< Kerdy >은(는) 정보주체의 개인정보 열람청구가 신속하게 처리되도록 노력하겠습니다.

  • ▶ 개인정보 열람청구 접수·처리 부서
  • 부서명 :
  • 담당자 :
  • 연락처 : , ,


제10조(정보주체의 권익침해에 대한 구제방법)



정보주체는 개인정보침해로 인한 구제를 받기 위하여 개인정보분쟁조정위원회, 한국인터넷진흥원 개인정보침해신고센터 등에 분쟁해결이나 상담 등을 신청할 수 있습니다. 이 밖에 기타 개인정보침해의 신고, 상담에 대하여는 아래의 기관에 문의하시기 바랍니다.

+ + + + 1. 개인정보분쟁조정위원회 : (국번없이) 1833-6972 (www.kopico.go.kr)
+ + 2. 개인정보침해신고센터 : (국번없이) 118 (privacy.kisa.or.kr)
+ + 3. 대검찰청 : (국번없이) 1301 (www.spo.go.kr)
+ + 4. 경찰청 : (국번없이) 182 (ecrm.cyber.go.kr)

+ + + +「개인정보보호법」제35조(개인정보의 열람), 제36조(개인정보의 정정·삭제), 제37조(개인정보의 처리정지 등)의 규정에 의한 요구에 대 하여 공공기관의 장이 행한 처분 또는 부작위로 인하여 권리 또는 이익의 침해를 받은 자는 행정심판법이 정하는 바에 따라 행정심판을 청구할 수 있습니다.

+ + + +※ 행정심판에 대해 자세한 사항은 중앙행정심판위원회(www.simpan.go.kr) 홈페이지를 참고하시기 바랍니다.

제11조(개인정보 처리방침 변경)


① 이 개인정보처리방침은 2023년 7월 28부터 적용됩니다.

② 이전의 개인정보 처리방침은 아래에서 확인하실 수 있습니다.

예시 ) - 20XX. X. X ~ 20XX. X. X 적용 (클릭)

예시 ) - 20XX. X. X ~ 20XX. X. X 적용 (클릭)

예시 ) - 20XX. X. X ~ 20XX. X. X 적용 (클릭)

+ + +
\ No newline at end of file