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 78c5073da..7631f55f2 100644 --- a/backend/emm-sale/src/documentTest/java/com/emmsale/EventApiTest.java +++ b/backend/emm-sale/src/documentTest/java/com/emmsale/EventApiTest.java @@ -18,7 +18,6 @@ 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; import com.emmsale.event.domain.EventType; import com.emmsale.event.domain.PaymentType; import com.emmsale.tag.TagFixture; @@ -72,16 +71,9 @@ class EventApiTest extends MockMvcTestHelper { fieldWithPath("applyEndDate").type(JsonFieldType.STRING) .description("신청 종료일자(nullable)"), fieldWithPath("location").type(JsonFieldType.STRING).description("장소"), - fieldWithPath("status").type(JsonFieldType.STRING).description("진행상태"), - fieldWithPath("applyStatus").type(JsonFieldType.STRING) - .description("행사 신청 기간의 진행 상황"), fieldWithPath("tags[]").type(JsonFieldType.ARRAY).description("태그들"), - fieldWithPath("imageUrl").type(JsonFieldType.STRING) - .description("이미지 Url(포스터)"), - fieldWithPath("remainingDays").type(JsonFieldType.NUMBER) - .description("시작일로 부터 D-day"), - fieldWithPath("applyRemainingDays").type(JsonFieldType.NUMBER) - .description("행사 신청 시작일까지 남은 일 수"), + fieldWithPath("thumbnailUrl").type(JsonFieldType.STRING) + .description("섬네일 이미지 Url(포스터)"), fieldWithPath("type").type(JsonFieldType.STRING) .description("event의 타입"), fieldWithPath("imageUrls[]").description("이미지 URL들").optional(), @@ -98,9 +90,8 @@ void findEvent() throws Exception { "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), "코엑스", - "UPCOMING", - "ENDED", List.of("코틀린", "백엔드", "안드로이드"), - "https://www.image.com", 2, -12, EventType.COMPETITION.toString(), + List.of("코틀린", "백엔드", "안드로이드"), + "https://www.image.com", EventType.COMPETITION.toString(), List.of("imageUrl1", "imageUrl2"), "인프런", "유료"); Mockito.when(eventService.findEvent(ArgumentMatchers.anyLong(), any())) @@ -143,8 +134,8 @@ void findEvents() throws Exception { .description("행사 마감일(yyyy:MM:dd:HH:mm:ss)"), PayloadDocumentation.fieldWithPath("[].tags[]").type(JsonFieldType.ARRAY) .description("행사 태그 목록"), - PayloadDocumentation.fieldWithPath("[].imageUrl").type(JsonFieldType.STRING) - .description("행사 이미지 URL"), + PayloadDocumentation.fieldWithPath("[].thumbnailUrl").type(JsonFieldType.STRING) + .description("행사 섬네일 이미지 URL"), PayloadDocumentation.fieldWithPath("[].eventMode").type(JsonFieldType.STRING) .description("행사 온라인 여부(온라인, 오프라인, 온오프라인)"), PayloadDocumentation.fieldWithPath("[].paymentType").type(JsonFieldType.STRING) @@ -230,18 +221,18 @@ void updateEventTest() throws Exception { event.getLocation(), event.getInformationUrl(), event.getEventPeriod().getStartDate(), event.getEventPeriod().getEndDate(), event.getEventPeriod().getApplyStartDate(), event.getEventPeriod().getApplyEndDate(), - tags, event.getImageUrl(), event.getType(), EventMode.ON_OFFLINE, PaymentType.FREE, + tags, event.getType(), EventMode.ON_OFFLINE, PaymentType.FREE, "행사기관"); final EventDetailResponse response = new EventDetailResponse(1L, request.getName(), request.getInformationUrl(), request.getStartDateTime(), request.getEndDateTime(), request.getApplyStartDateTime(), request.getApplyEndDateTime(), - request.getLocation(), EventStatus.IN_PROGRESS.name(), EventStatus.ENDED.name(), + request.getLocation(), tags.stream().map(TagRequest::getName).collect(Collectors.toList()), - request.getImageUrl(), 10, 10, request.getType().toString(), + "image1.jpg", request.getType().toString(), List.of("imageUrl1", "imageUrl2"), "행사기관", "유료"); - Mockito.when(eventService.updateEvent(eq(eventId), any(EventDetailRequest.class), any(), any())) + Mockito.when(eventService.updateEvent(eq(eventId), any(EventDetailRequest.class), any())) .thenReturn(response); final String contents = objectMapper.writeValueAsString(request); @@ -332,18 +323,18 @@ void addEventTest() throws Exception { event.getLocation(), event.getInformationUrl(), event.getEventPeriod().getStartDate(), event.getEventPeriod().getEndDate(), event.getEventPeriod().getApplyStartDate(), event.getEventPeriod().getApplyEndDate(), - tags, event.getImageUrl(), event.getType(), EventMode.ON_OFFLINE, PaymentType.FREE, + tags, event.getType(), EventMode.ON_OFFLINE, PaymentType.FREE, "행사기관"); final EventDetailResponse response = new EventDetailResponse(1L, request.getName(), request.getInformationUrl(), request.getStartDateTime(), request.getEndDateTime(), request.getApplyStartDateTime(), request.getApplyEndDateTime(), - request.getLocation(), EventStatus.IN_PROGRESS.name(), EventStatus.ENDED.name(), + request.getLocation(), tags.stream().map(TagRequest::getName).collect(Collectors.toList()), - request.getImageUrl(), 10, 10, request.getType().toString(), + "image1.jpg", request.getType().toString(), List.of("imageUrl1", "imageUrl2"), "행사기관", "무료"); - Mockito.when(eventService.addEvent(any(EventDetailRequest.class), any(), any())) + Mockito.when(eventService.addEvent(any(EventDetailRequest.class), any())) .thenReturn(response); final String contents = objectMapper.writeValueAsString(request); @@ -412,7 +403,7 @@ void addEventWithEmptyNameTest(final String eventName) throws Exception { eventName, event.getLocation(), event.getInformationUrl(), event.getEventPeriod() .getStartDate(), event.getEventPeriod().getEndDate(), event.getEventPeriod().getApplyStartDate(), event.getEventPeriod().getApplyEndDate(), - tags, event.getImageUrl(), event.getType(), event.getEventMode(), + tags, event.getType(), event.getEventMode(), event.getPaymentType(), event.getOrganization()); final String contents = objectMapper.writeValueAsString(request); //when & then @@ -453,7 +444,7 @@ void addEventWithEmptyLocationTest(final String eventLocation) throws Exception event.getName(), eventLocation, event.getInformationUrl(), event.getEventPeriod() .getStartDate(), event.getEventPeriod().getEndDate(), event.getEventPeriod().getApplyStartDate(), event.getEventPeriod().getApplyEndDate(), - tags, event.getImageUrl(), event.getType(), event.getEventMode(), + tags, event.getType(), event.getEventMode(), event.getPaymentType(), event.getOrganization()); final String contents = objectMapper.writeValueAsString(request); //when & then @@ -495,7 +486,7 @@ void addEventWithInvalidInformationUrlTest(final String informationUrl) throws E event.getName(), event.getLocation(), informationUrl, event.getEventPeriod() .getStartDate(), event.getEventPeriod().getEndDate(), event.getEventPeriod().getApplyStartDate(), event.getEventPeriod().getApplyEndDate(), - tags, event.getImageUrl(), event.getType(), event.getEventMode(), + tags, event.getType(), event.getEventMode(), event.getPaymentType(), event.getOrganization()); final String contents = objectMapper.writeValueAsString(request); //when & then @@ -540,7 +531,6 @@ void addEventWithUnformattedStartDateTimeTest(final String startDateTime) request.put("endDateTime", event.getEventPeriod().getEndDate().toString()); request.put("applyStartDateTime", event.getEventPeriod().getApplyStartDate().toString()); request.put("applyEndDateTime", event.getEventPeriod().getApplyEndDate().toString()); - request.put("imageUrl", event.getImageUrl()); request.put("type", event.getType().name()); request.put("eventMode", event.getEventMode().name()); request.put("paymentType", event.getPaymentType().name()); @@ -588,7 +578,6 @@ void addEventWithUnformattedEndDateTimeTest(final String endDateTime) throws Exc request.put("endDateTime", endDateTime); request.put("applyStartDateTime", event.getEventPeriod().getApplyStartDate().toString()); request.put("applyEndDateTime", event.getEventPeriod().getApplyEndDate().toString()); - request.put("imageUrl", event.getImageUrl()); request.put("type", event.getType().name()); request.put("eventMode", event.getEventMode().name()); request.put("paymentType", event.getPaymentType().name()); 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 6ab7aec47..6287a84cd 100644 --- a/backend/emm-sale/src/documentTest/java/com/emmsale/ScrapApiTest.java +++ b/backend/emm-sale/src/documentTest/java/com/emmsale/ScrapApiTest.java @@ -84,8 +84,8 @@ void findAllScraps() throws Exception { .description("행사 마감일(yyyy:MM:dd:HH:mm:ss)"), PayloadDocumentation.fieldWithPath("[].tags[]").type(JsonFieldType.ARRAY) .description("행사 태그 목록"), - PayloadDocumentation.fieldWithPath("[].imageUrl").type(JsonFieldType.STRING) - .description("행사 이미지 URL"), + PayloadDocumentation.fieldWithPath("[].thumbnailUrl").type(JsonFieldType.STRING) + .description("행사 섬네일 이미지 URL"), PayloadDocumentation.fieldWithPath("[].eventMode").type(JsonFieldType.STRING) .description("행사 온라인 여부(온라인, 오프라인, 온오프라인)"), PayloadDocumentation.fieldWithPath("[].paymentType").type(JsonFieldType.STRING) 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 c95630d3e..e53a603fc 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 @@ -52,7 +52,7 @@ public ResponseEntity> findEvents( @ResponseStatus(HttpStatus.CREATED) public EventDetailResponse addEvent(@RequestPart @Valid final EventDetailRequest request, @RequestPart final List images) { - return eventService.addEvent(request, images, LocalDate.now()); + return eventService.addEvent(request, images); } @PutMapping(path = "/{eventId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @@ -60,7 +60,7 @@ 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, LocalDate.now()); + return eventService.updateEvent(eventId, request, images); } @DeleteMapping("/{eventId}") 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 465cecaba..2af2aa20e 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 @@ -32,8 +32,12 @@ 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; +import java.util.Map; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; @@ -66,8 +70,23 @@ public EventDetailResponse findEvent(final Long id, final LocalDate today) { .sorted(comparing(Image::getOrder)) .map(Image::getName) .collect(toList()); + final String thumbnailImageUrl = extractThumbnailImage(imageUrls); + final List informationImageUrls = extractInformationImages(imageUrls); + return EventDetailResponse.from(event, thumbnailImageUrl, informationImageUrls); + } + + private String extractThumbnailImage(final List imageUrls) { + if (imageUrls.isEmpty()) { + return null; + } + return imageUrls.get(0); + } - return EventDetailResponse.from(event, today, imageUrls); + private List extractInformationImages(final List imageUrls) { + if (imageUrls.size() <= 1) { + return Collections.emptyList(); + } + return imageUrls.subList(1, imageUrls.size()); } @Transactional(readOnly = true) @@ -88,10 +107,11 @@ public List findEvents(final EventType category, spec = spec.and(EventSpecification.filterByPeriod(startDateTime, endDateTime)); } final List events = eventRepository.findAll(spec); + final EnumMap> eventsForEventStatus = groupByEventStatus(nowDate, events); - return filterByStatuses(statuses, eventsForEventStatus); + return filterByStatuses(statuses, eventsForEventStatus, makeImageUrlPerEventId(events)); } private boolean isExistTagNames(final List tagNames) { @@ -138,6 +158,19 @@ private void validateEndDateAfterDateStart(final LocalDateTime startDate, } } + // TODO: 2023/09/27 코드 중복 제거(ScrapService) + private Map makeImageUrlPerEventId(final List events) { + final List scrappedEventIds = 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()); + } + return imageUrlPerEventId; + } + private EnumMap> groupByEventStatus(final LocalDate nowDate, final List events) { return events.stream() @@ -150,12 +183,13 @@ private EnumMap> groupByEventStatus(final LocalDate now private List filterByStatuses( final List statuses, - final EnumMap> eventsForEventStatus + final EnumMap> eventsForEventStatus, + final Map imageUrlPerEventId ) { if (isExistStatusName(statuses)) { - return filterEventResponseByStatuses(statuses, eventsForEventStatus); + return filterEventResponseByStatuses(statuses, eventsForEventStatus, imageUrlPerEventId); } - return EventResponse.mergeEventResponses(eventsForEventStatus); + return EventResponse.mergeEventResponses(eventsForEventStatus, imageUrlPerEventId); } private boolean isExistStatusName(final List statuses) { @@ -164,12 +198,14 @@ private boolean isExistStatusName(final List statuses) { private List filterEventResponseByStatuses( final List statuses, - final EnumMap> eventsForEventStatus + final EnumMap> eventsForEventStatus, + final Map imageUrlPerEventId ) { return eventsForEventStatus.entrySet() .stream() .filter(entry -> statuses.contains(entry.getKey())) - .map(entry -> EventResponse.makeEventResponsesByStatus(entry.getValue())) + .map( + entry -> EventResponse.makeEventResponsesByStatus(entry.getValue(), imageUrlPerEventId)) .reduce(new ArrayList<>(), (combinedEvents, eventsToAdd) -> { combinedEvents.addAll(eventsToAdd); return combinedEvents; @@ -177,7 +213,7 @@ private List filterEventResponseByStatuses( } public EventDetailResponse addEvent(final EventDetailRequest request, - final List images, final LocalDate today) { + final List images) { final Event event = eventRepository.save(request.toEvent()); final List tags = findAllPersistTagsOrElseThrow(request.getTags()); event.addAllEventTags(tags); @@ -190,12 +226,13 @@ public EventDetailResponse addEvent(final EventDetailRequest request, .collect(toList()); eventPublisher.publish(event); - - return EventDetailResponse.from(event, today, imageUrls); + 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 LocalDate today) { + final List images) { final Event event = eventRepository.findById(eventId) .orElseThrow(() -> new EventException(NOT_FOUND_EVENT)); @@ -220,8 +257,9 @@ public EventDetailResponse updateEvent(final Long eventId, final EventDetailRequ .sorted(comparing(Image::getOrder)) .map(Image::getName) .collect(toList()); - - return EventDetailResponse.from(updatedEvent, today, imageUrls); + final String thumbnailImageUrl = extractThumbnailImage(imageUrls); + final List informationImageUrls = extractInformationImages(imageUrls); + return EventDetailResponse.from(updatedEvent, thumbnailImageUrl, informationImageUrls); } public void deleteEvent(final Long eventId) { diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventDetailRequest.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventDetailRequest.java index e82c8fcfe..9e801243e 100644 --- a/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventDetailRequest.java +++ b/backend/emm-sale/src/main/java/com/emmsale/event/application/dto/EventDetailRequest.java @@ -43,8 +43,6 @@ public class EventDetailRequest { private final LocalDateTime applyEndDateTime; private final List tags; - - private final String imageUrl; private final EventType type; private final EventMode eventMode; @@ -62,7 +60,6 @@ public Event toEvent() { applyEndDateTime, informationUrl, type, - imageUrl, paymentType, eventMode, organization 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 10bcb7728..fd1278b3e 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 @@ -6,7 +6,6 @@ import com.emmsale.event.domain.EventTag; import com.emmsale.tag.domain.Tag; import com.fasterxml.jackson.annotation.JsonFormat; -import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; import lombok.Getter; @@ -30,12 +29,8 @@ public class EventDetailResponse { @JsonFormat(pattern = DATE_TIME_FORMAT) private final LocalDateTime applyEndDate; private final String location; - private final String status; - private final String applyStatus; private final List tags; - private final String imageUrl; - private final Integer remainingDays; - private final Integer applyRemainingDays; + private final String thumbnailUrl; private final String type; private final List imageUrls; private final String organization; @@ -43,7 +38,7 @@ public class EventDetailResponse { public static EventDetailResponse from( final Event event, - final LocalDate today, + final String thumbnailUrl, final List imageUrls ) { final List tagNames = event.getTags().stream() @@ -60,12 +55,8 @@ public static EventDetailResponse from( event.getEventPeriod().getApplyStartDate(), event.getEventPeriod().getApplyEndDate(), event.getLocation(), - event.getEventPeriod().calculateEventStatus(today).name(), - event.getEventPeriod().calculateEventApplyStatus(today).name(), tagNames, - event.getImageUrl(), - event.getEventPeriod().calculateRemainingDays(today), - event.getEventPeriod().calculateApplyRemainingDays(today), + thumbnailUrl, event.getType().toString(), imageUrls, event.getOrganization(), 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 9fe4763d2..241f3c4ee 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 @@ -1,7 +1,5 @@ package com.emmsale.event.application.dto; -import static java.util.stream.Collectors.toList; - import com.emmsale.event.domain.Event; import com.emmsale.event.domain.EventStatus; import com.fasterxml.jackson.annotation.JsonFormat; @@ -9,6 +7,7 @@ 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; @@ -31,28 +30,30 @@ public class EventResponse { @JsonFormat(pattern = DATE_TIME_FORMAT) private final LocalDateTime applyEndDate; private final List tags; - private final String imageUrl; + private final String thumbnailUrl; private final String eventMode; private final String paymentType; - public static List makeEventResponsesByStatus(final List events) { + public static List makeEventResponsesByStatus(final List events, + final Map imageUrlPerEventId) { return events.stream() - .map(EventResponse::from) - .collect(toList()); + .map(event -> EventResponse.from(event, imageUrlPerEventId.get(event.getId()))) + .collect(Collectors.toList()); } public static List mergeEventResponses( - final Map> groupByEventStatus + final Map> groupByEventStatus, + final Map imageUrlPerEventId ) { return groupByEventStatus.values().stream() - .map(EventResponse::makeEventResponsesByStatus) + .map(events -> makeEventResponsesByStatus(events, imageUrlPerEventId)) .reduce(new ArrayList<>(), (combinedEvents, eventsToAdd) -> { combinedEvents.addAll(eventsToAdd); return combinedEvents; }); } - private static EventResponse from(final Event event) { + private static EventResponse from(final Event event, final String thumbnailUrl) { return new EventResponse( event.getId(), event.getName(), @@ -61,7 +62,7 @@ private static EventResponse from(final Event event) { event.getEventPeriod().getApplyStartDate(), event.getEventPeriod().getApplyEndDate(), event.extractTags(), - event.getImageUrl(), + thumbnailUrl, event.getEventMode().getValue(), event.getPaymentType().getValue() ); 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 6aa2939e7..22e455dfa 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 @@ -58,8 +58,6 @@ public class Event extends BaseEntity { @Column(nullable = false) private EventMode eventMode; - private String imageUrl; - @Column(nullable = false) private String organization; @@ -78,7 +76,6 @@ public Event( final LocalDateTime applyEndDate, final String informationUrl, final EventType eventType, - final String imageUrl, final PaymentType paymentType, final EventMode eventMode, final String organization @@ -88,7 +85,6 @@ public Event( this.eventPeriod = new EventPeriod(startDate, endDate, applyStartDate, applyEndDate); this.informationUrl = informationUrl; this.type = eventType; - this.imageUrl = imageUrl; this.paymentType = paymentType; this.eventMode = eventMode; this.organization = organization; 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 18baed0a2..09e6721d9 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,9 @@ public interface ImageRepository extends JpaRepository { List findAllByTypeAndContentId(final ImageType type, final Long contentId); + @Query("select i from Image i where i.type='EVENT' and i.order=0 and i.contentId in :eventIds") + List findAllThumbnailByEventIdIn(final List eventIds); + @Query("select i from Image i where i.type = 'FEED' and i.contentId in :feedIds") List findAllByFeedIdIn(List feedIds); 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 1605ca738..0cf5a3f78 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,12 +5,16 @@ import com.emmsale.event.application.dto.EventResponse; import com.emmsale.event.domain.Event; import com.emmsale.event.domain.EventStatus; +import com.emmsale.image.domain.Image; +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.LocalDate; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -21,14 +25,31 @@ public class ScrapQueryService { private final ScrapRepository scrapRepository; + private final ImageRepository imageRepository; public List findAllScraps(final Member member) { //TODO : Scrap에서 event 사용해서 N+1 발생 - final Map> eventGroupByStatus - = scrapRepository.findAllByMemberId(member.getId()) + final List scrappedEvents = scrapRepository.findAllByMemberId(member.getId()) .stream() .map(Scrap::getEvent) + .collect(Collectors.toList()); + + final Map> eventGroupByStatus = scrappedEvents.stream() .collect(groupingBy(event -> event.getEventPeriod().calculateEventStatus(LocalDate.now()))); - return EventResponse.mergeEventResponses(eventGroupByStatus); + + return EventResponse.mergeEventResponses(eventGroupByStatus, + makeImageUrlPerEventId(scrappedEvents)); + } + + private Map makeImageUrlPerEventId(final List events) { + final List scrappedEventIds = 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()); + } + return imageUrlPerEventId; } } diff --git a/backend/emm-sale/src/main/java/com/emmsale/scrap/application/dto/ScrapResponse.java b/backend/emm-sale/src/main/java/com/emmsale/scrap/application/dto/ScrapResponse.java deleted file mode 100644 index 52208ed08..000000000 --- a/backend/emm-sale/src/main/java/com/emmsale/scrap/application/dto/ScrapResponse.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.emmsale.scrap.application.dto; - -import com.emmsale.event.domain.Event; -import com.emmsale.scrap.domain.Scrap; -import java.time.LocalDate; -import java.util.List; -import java.util.stream.Collectors; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.ToString; - -@Getter -@RequiredArgsConstructor -@ToString -public class ScrapResponse { - - private final Long scrapId; - private final Long eventId; - private final String name; - private final String status; - private final String imageUrl; - private final List tags; - - public static ScrapResponse from(final Scrap scrap) { - final Event event = scrap.getEvent(); - final String eventStatus = event.getEventPeriod().calculateEventStatus(LocalDate.now()) - .toString(); - final List eventTags = event.getTags().stream() - .map(eventTag -> eventTag.getTag().getName()) - .collect(Collectors.toList()); - - return new ScrapResponse( - scrap.getId(), - event.getId(), - event.getName(), - eventStatus, - event.getImageUrl(), - eventTags - ); - } - -} diff --git a/backend/emm-sale/src/main/resources/data.sql b/backend/emm-sale/src/main/resources/data.sql index 16b6f9a49..364184b07 100644 --- a/backend/emm-sale/src/main/resources/data.sql +++ b/backend/emm-sale/src/main/resources/data.sql @@ -75,96 +75,86 @@ values (7, '정보보안'); insert into event(id, name, start_date, end_date, apply_start_date, apply_end_date, location, information_url, created_at, updated_at, - image_url, type, event_mode, payment_type, organization) + type, event_mode, payment_type, organization) values (1, '인프콘 2023', '2023-06-01T12:00:00', '2023-09-01T12:00:00', '2023-05-01T12:00:00', '2023-06-01T12:00:00', '코엑스', 'https://www.naver.com', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), - 'https://biz.pusan.ac.kr/dext5editordata/2022/08/20220810_160546511_10103.jpg', 'CONFERENCE', 'OFFLINE', 'PAID', '행사기관'); insert into event(id, name, start_date, end_date, apply_start_date, apply_end_date, - location, information_url, created_at, updated_at, - image_url, type, event_mode, payment_type, organization) + location, information_url, created_at, updated_at, type, event_mode, payment_type, + organization) values (2, 'AI 컨퍼런스', '2023-07-22T12:00:00', '2023-07-30T12:00:00', '2023-07-01T12:00:00', '2023-07-22T12:00:00', '코엑스', 'https://www.naver.com', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), - 'https://biz.pusan.ac.kr/dext5editordata/2022/08/20220810_160546511_10103.jpg', 'CONFERENCE', 'ONLINE', 'PAID', '행사기관'); insert into event(id, name, start_date, end_date, apply_start_date, apply_end_date, location, information_url, created_at, updated_at, - image_url, type, event_mode, payment_type, organization) + type, event_mode, payment_type, organization) values (3, '모바일 컨퍼런스', '2023-08-03T12:00:00', '2023-09-03T12:00:00', '2023-08-01T12:00:00', '2023-08-02T12:00:00', '코엑스', 'https://www.naver.com', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), - 'https://biz.pusan.ac.kr/dext5editordata/2022/08/20220810_160546511_10103.jpg', 'CONFERENCE', 'ONLINE', 'PAID', '행사기관'); insert into event(id, name, start_date, end_date, apply_start_date, apply_end_date, location, information_url, created_at, updated_at, - image_url, type, event_mode, payment_type, organization) + type, event_mode, payment_type, organization) values (4, '안드로이드 컨퍼런스', '2023-06-29T12:00:00', '2023-07-16T12:00:00', '2023-06-01T12:00:00', '2023-06-20T12:00:00', '코엑스', 'https://www.naver.com', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), - 'https://biz.pusan.ac.kr/dext5editordata/2022/08/20220810_160546511_10103.jpg', 'CONFERENCE', 'ONLINE', 'PAID', '행사기관'); insert into event(id, name, start_date, end_date, apply_start_date, apply_end_date, location, information_url, created_at, updated_at, - image_url, type, event_mode, payment_type, organization) + type, event_mode, payment_type, organization) values (5, '웹 컨퍼런스', '2023-07-03T12:00:00', '2023-08-03T12:00:00', '2023-07-03T12:00:00', '2023-08-03T12:00:00', '코엑스', 'https://www.naver.com', - CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), null, 'CONFERENCE', 'ONLINE', 'PAID', '행사기관'); + CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), 'CONFERENCE', 'ONLINE', 'PAID', '행사기관'); insert into event(id, name, start_date, end_date, apply_start_date, apply_end_date, location, information_url, created_at, updated_at, - image_url, type, event_mode, payment_type, organization) + type, event_mode, payment_type, organization) values (6, '옛날 웹 컨퍼런스', '2022-07-03T12:00:00', '2022-08-03T12:00:00', '2022-07-03T12:00:00', '2022-08-03T12:00:00', '코엑스', 'https://www.naver.com', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), - 'https://biz.pusan.ac.kr/dext5editordata/2022/08/20220810_160546511_10103.jpg', 'CONFERENCE', 'ONLINE', 'PAID', '행사기관'); insert into event(id, name, start_date, end_date, apply_start_date, apply_end_date, location, information_url, created_at, updated_at, - image_url, type, event_mode, payment_type, organization) + type, event_mode, payment_type, organization) values (7, '알고리즘 경진대회', '2023-06-01T12:00:00', '2023-09-01T12:00:00', '2023-06-01T12:00:00', '2023-09-01T12:00:00', '온라인', 'https://www.naver.com', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), - 'https://biz.pusan.ac.kr/dext5editordata/2022/08/20220810_160546511_10103.jpg', 'COMPETITION', 'ONLINE', 'PAID', '행사기관'); insert into event(id, name, start_date, end_date, apply_start_date, apply_end_date, location, information_url, created_at, updated_at, - image_url, type, event_mode, payment_type, organization) + type, event_mode, payment_type, organization) values (8, '첨단 해커톤', '2023-07-22T12:00:00', '2023-07-30T12:00:00', '2023-07-22T12:00:00', '2023-07-30T12:00:00', '코엑스', 'https://www.naver.com', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), - 'https://biz.pusan.ac.kr/dext5editordata/2022/08/20220810_160546511_10103.jpg', 'COMPETITION', 'ONLINE', 'PAID', '행사기관'); insert into event(id, name, start_date, end_date, apply_start_date, apply_end_date, location, information_url, created_at, updated_at, - image_url, type, event_mode, payment_type, organization) + type, event_mode, payment_type, organization) values (9, 'AI 아이디어 공모전', '2023-08-03T12:00:00', '2023-09-03T12:00:00', '2023-08-03T12:00:00', '2023-09-03T12:00:00', '코엑스', 'https://www.naver.com', - CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), null, 'COMPETITION', 'ONLINE', 'PAID', '행사기관'); + CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), 'COMPETITION', 'ONLINE', 'PAID', '행사기관'); insert into event(id, name, start_date, end_date, apply_start_date, apply_end_date, location, information_url, created_at, updated_at, - image_url, type, event_mode, payment_type, organization) + type, event_mode, payment_type, organization) values (10, '구름톤', '2023-06-29T12:00:00', '2023-07-16T12:00:00', '2023-06-29T12:00:00', '2023-07-16T12:00:00', '코엑스', 'https://www.naver.com', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), - 'https://biz.pusan.ac.kr/dext5editordata/2022/08/20220810_160546511_10103.jpg', 'COMPETITION', 'ONLINE', 'PAID', '행사기관'); insert into event(id, name, start_date, end_date, apply_start_date, apply_end_date, location, information_url, created_at, updated_at, - image_url, type, event_mode, payment_type, organization) + type, event_mode, payment_type, organization) values (11, 'CTF', '2023-07-03T12:00:00', '2023-08-03T12:00:00', '2023-07-03T12:00:00', '2023-08-03T12:00:00', '코엑스', 'https://www.naver.com', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), - 'https://biz.pusan.ac.kr/dext5editordata/2022/08/20220810_160546511_10103.jpg', 'COMPETITION', 'ONLINE', 'PAID', '행사기관'); insert into event(id, name, start_date, end_date, apply_start_date, apply_end_date, location, information_url, created_at, updated_at, - image_url, type, event_mode, payment_type, organization) + type, event_mode, payment_type, organization) values (12, '보안 경진대회', '2022-07-03T12:00:00', '2022-08-03T12:00:00', '2022-07-03T12:00:00', '2022-08-03T12:00:00', '코엑스', 'https://www.naver.com', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), - 'https://biz.pusan.ac.kr/dext5editordata/2022/08/20220810_160546511_10103.jpg', 'COMPETITION', 'ONLINE', 'PAID', '행사기관'); diff --git a/backend/emm-sale/src/main/resources/schema.sql b/backend/emm-sale/src/main/resources/schema.sql index 40a8e6200..4711fb4e7 100644 --- a/backend/emm-sale/src/main/resources/schema.sql +++ b/backend/emm-sale/src/main/resources/schema.sql @@ -258,6 +258,10 @@ create table notification is_read bit not null ); + +-- 2023-09-29 18:33 +alter table event drop image_url; + -- 2023-09-30 01:24 alter table notification add column receiver_id bigint default 0; diff --git a/backend/emm-sale/src/test/java/com/emmsale/comment/application/CommentCommandServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/comment/application/CommentCommandServiceTest.java index 134942cfa..6d2e2344e 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/comment/application/CommentCommandServiceTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/comment/application/CommentCommandServiceTest.java @@ -64,7 +64,6 @@ void init() { afterDateTime, "url", EventType.CONFERENCE, - "https://image.com", PaymentType.FREE_PAID, EventMode.ON_OFFLINE, "행사기간" @@ -195,11 +194,11 @@ void test_modify_canNotModifyDeletedComment() throws Exception { final CommentModifyRequest request = new CommentModifyRequest("변경된 내용"); comment.delete(); - final Comment deletedComment = commentRepository.save(comment); + final Long deletedCommentId = commentRepository.save(comment).getId(); //when & then Assertions.assertThatThrownBy( - () -> commentCommandService.modify(deletedComment.getId(), 댓글_작성자, request)) + () -> commentCommandService.modify(deletedCommentId, 댓글_작성자, request)) .isInstanceOf(CommentException.class) .hasMessage(CommentExceptionType.FORBIDDEN_MODIFY_DELETED_COMMENT.errorMessage()); } diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/EventFixture.java b/backend/emm-sale/src/test/java/com/emmsale/event/EventFixture.java index a260ce8c1..d90412544 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/event/EventFixture.java +++ b/backend/emm-sale/src/test/java/com/emmsale/event/EventFixture.java @@ -21,7 +21,6 @@ public static Event eventFixture() { LocalDateTime.of(2023, 8, 15, 15, 0), "http://infcon.com", EventType.CONFERENCE, - "https://image.url", PaymentType.PAID, EventMode.OFFLINE, "인프런" @@ -32,10 +31,7 @@ public static Event eventFixture() { return new Event("인프콘 2023", "코엑스", LocalDateTime.parse("2023-06-01T12:00:00"), LocalDateTime.parse("2023-09-01T12:00:00"), LocalDateTime.parse("2023-05-01T12:00:00"), LocalDateTime.parse("2023-06-01T12:00:00"), "https://~~~", EventType.CONFERENCE, - "https://image.url", - PaymentType.FREE_PAID, - EventMode.ON_OFFLINE, - "인프런" + PaymentType.FREE_PAID, EventMode.ON_OFFLINE, "인프런" ); } @@ -43,10 +39,7 @@ public static Event eventFixture() { return new Event("AI 컨퍼런스", "코엑스", LocalDateTime.parse("2023-07-22T12:00:00"), LocalDateTime.parse("2023-07-30T12:00:00"), LocalDateTime.parse("2023-07-01T12:00:00"), LocalDateTime.parse("2023-07-22T12:00:00"), "https://~~~", EventType.CONFERENCE, - "https://image.url", - PaymentType.FREE_PAID, - EventMode.ON_OFFLINE, - "행사기관" + PaymentType.FREE_PAID, EventMode.ON_OFFLINE, "행사기관" ); } @@ -54,10 +47,7 @@ public static Event eventFixture() { return new Event("모바일 컨퍼런스", "코엑스", LocalDateTime.parse("2023-08-03T12:00:00"), LocalDateTime.parse("2023-09-03T12:00:00"), LocalDateTime.parse("2023-08-01T12:00:00"), LocalDateTime.parse("2023-08-02T12:00:00"), "https://~~~", EventType.CONFERENCE, - "https://image.url", - PaymentType.FREE_PAID, - EventMode.ON_OFFLINE, - "행사기관" + PaymentType.FREE_PAID, EventMode.ON_OFFLINE, "행사기관" ); } @@ -65,10 +55,7 @@ public static Event eventFixture() { return new Event("안드로이드 컨퍼런스", "코엑스", LocalDateTime.parse("2023-06-29T12:00:00"), LocalDateTime.parse("2023-07-16T12:00:00"), LocalDateTime.parse("2023-06-01T12:00:00"), LocalDateTime.parse("2023-06-20T12:00:00"), "https://~~~", EventType.CONFERENCE, - "https://image.url", - PaymentType.FREE_PAID, - EventMode.ON_OFFLINE, - "행사기관" + PaymentType.FREE_PAID, EventMode.ON_OFFLINE, "행사기관" ); } @@ -76,10 +63,7 @@ public static Event eventFixture() { return new Event("웹 컨퍼런스", "코엑스", LocalDateTime.parse("2023-07-03T12:00:00"), LocalDateTime.parse("2023-08-03T12:00:00"), LocalDateTime.parse("2023-07-03T12:00:00"), LocalDateTime.parse("2023-08-03T12:00:00"), "https://~~~", EventType.CONFERENCE, - "https://image.url", - PaymentType.FREE_PAID, - EventMode.ON_OFFLINE, - "행사기관" + PaymentType.FREE_PAID, EventMode.ON_OFFLINE, "행사기관" ); } @@ -87,11 +71,7 @@ public static Event eventFixture() { return new Event("AI 아이디어 공모전", "코엑스", LocalDateTime.parse("2023-06-29T12:00:00"), LocalDateTime.parse("2023-07-16T12:00:00"), LocalDateTime.parse("2023-06-01T12:00:00"), LocalDateTime.parse("2023-07-16T12:00:00"), "https://~~~", - EventType.COMPETITION, - "https://image.url", - PaymentType.FREE_PAID, - EventMode.ON_OFFLINE, - "행사기관" + EventType.COMPETITION, PaymentType.FREE_PAID, EventMode.ON_OFFLINE, "행사기관" ); } @@ -99,11 +79,7 @@ public static Event eventFixture() { return new Event("구름톤", "코엑스", LocalDateTime.parse("2023-07-03T12:00:00"), LocalDateTime.parse("2023-08-03T12:00:00"), LocalDateTime.parse("2023-07-03T12:00:00"), LocalDateTime.parse("2023-08-03T12:00:00"), "https://~~~", - EventType.COMPETITION, - "https://image.url", - PaymentType.FREE_PAID, - EventMode.ON_OFFLINE, - "행사기관" + EventType.COMPETITION, PaymentType.FREE_PAID, EventMode.ON_OFFLINE, "행사기관" ); } 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/EventServiceEventIntegrationTest.java index b2d192789..4c3a1844a 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/EventServiceEventIntegrationTest.java @@ -22,7 +22,6 @@ 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 org.junit.jupiter.api.DisplayName; @@ -71,7 +70,6 @@ void test_publish_event() throws Exception { new TagRequest(안드로이드().getName()), new TagRequest(백엔드().getName()) ), // Assuming you don't have a direct TagRequest list from Event. - "https://image.url", EventType.CONFERENCE, EventMode.ON_OFFLINE, PaymentType.FREE_PAID, @@ -79,7 +77,7 @@ void test_publish_event() throws Exception { ); //when - eventService.addEvent(eventDetailRequest, null, LocalDate.now()); + eventService.addEvent(eventDetailRequest, null); //then verify(firebaseCloudMessageClient, times(2)) @@ -116,7 +114,6 @@ void test_publish_event_no_notification_event_has_no_interest_tag() throws Excep List.of( new TagRequest(IOS().getName()) ), // Assuming you don't have a direct TagRequest list from Event. - "https://image.url", EventType.CONFERENCE, EventMode.ON_OFFLINE, PaymentType.FREE_PAID, @@ -124,7 +121,7 @@ void test_publish_event_no_notification_event_has_no_interest_tag() throws Excep ); //when - eventService.addEvent(eventDetailRequest, null, LocalDate.now()); + eventService.addEvent(eventDetailRequest, null); //then verify(firebaseCloudMessageClient, times(0)) @@ -151,7 +148,6 @@ void test_publish_event_no_notification_member_has_no_interest_tag() throws Exce List.of( new TagRequest(IOS().getName()) ), // Assuming you don't have a direct TagRequest list from Event. - "https://image.url", EventType.CONFERENCE, EventMode.ON_OFFLINE, PaymentType.FREE_PAID, @@ -159,7 +155,7 @@ void test_publish_event_no_notification_member_has_no_interest_tag() throws Exce ); //when - eventService.addEvent(eventDetailRequest, null, LocalDate.now()); + eventService.addEvent(eventDetailRequest, null); //then verify(firebaseCloudMessageClient, times(0)) 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 8a77a0fd2..ff4c4b6ec 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 @@ -15,6 +15,9 @@ 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; import static com.emmsale.tag.TagFixture.AI; import static com.emmsale.tag.TagFixture.IOS; import static com.emmsale.tag.TagFixture.백엔드; @@ -54,6 +57,7 @@ import com.emmsale.tag.exception.TagExceptionType; import java.time.LocalDate; import java.time.LocalDateTime; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import org.assertj.core.api.ThrowableAssert.ThrowingCallable; @@ -72,12 +76,12 @@ class EventServiceTest extends ServiceIntegrationTestHelper { private static final EventResponse 인프콘_2023 = new EventResponse(null, "인프콘 2023", null, null, - null, null, List.of(), null, EventMode.OFFLINE.getValue(), + null, null, List.of(), "이미지1", EventMode.OFFLINE.getValue(), PaymentType.PAID.getValue()); private static final EventResponse 웹_컨퍼런스 = new EventResponse(null, "웹 컨퍼런스", null, null, null, - null, List.of(), null, EventMode.ONLINE.getValue(), PaymentType.PAID.getValue()); + null, List.of(), "이미지1", EventMode.ONLINE.getValue(), PaymentType.PAID.getValue()); private static final EventResponse 안드로이드_컨퍼런스 = new EventResponse(null, "안드로이드 컨퍼런스", null, null, - null, null, List.of(), EventMode.ONLINE.getValue(), null, PaymentType.PAID.getValue()); + 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, @@ -133,6 +137,18 @@ void init() { "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 @@ -150,10 +166,55 @@ void success() { imageRepository.save( new Image("imageUrl2", ImageType.EVENT, event.getId(), 0, LocalDateTime.now()) ); + imageRepository.save( + new Image("imageUrl3", ImageType.EVENT, event.getId(), 2, LocalDateTime.now()) + ); - final List imageUrls = List.of("imageUrl2", "imageUrl1"); + final String thumbnailUrl = "imageUrl2"; + final List imageUrls = List.of("imageUrl1", "imageUrl3"); - final EventDetailResponse expected = EventDetailResponse.from(event, 날짜_8월_10일(), imageUrls); + final EventDetailResponse expected = EventDetailResponse.from(event, thumbnailUrl, + imageUrls); + + //when + final EventDetailResponse actual = eventService.findEvent(event.getId(), 날짜_8월_10일()); + + //then + assertThat(actual) + .usingRecursiveComparison() + .isEqualTo(expected); + } + + @Test + @DisplayName("이미지가 등록되지 않는 event의 imageUrl은 null, imageUrls는 빈 리스트로 반환된다.") + void success_imageUrl_null_imageUrls_empty() { + //given + final Event event = eventRepository.save(eventFixture()); + + final EventDetailResponse expected = EventDetailResponse.from(event, null, + Collections.emptyList()); + + //when + final EventDetailResponse actual = eventService.findEvent(event.getId(), 날짜_8월_10일()); + + //then + assertThat(actual) + .usingRecursiveComparison() + .isEqualTo(expected); + } + + @Test + @DisplayName("이미지가 하나만 등록된 event의 imageUrls은 빈 리스트로 반환된다.") + void success_imageUrls_empty() { + //given + final Event event = eventRepository.save(eventFixture()); + imageRepository.save( + new Image("imageUrl2", ImageType.EVENT, event.getId(), 0, LocalDateTime.now()) + ); + + final String thumbnailUrl = "imageUrl2"; + final EventDetailResponse expected = EventDetailResponse.from(event, thumbnailUrl, + Collections.emptyList()); //when final EventDetailResponse actual = eventService.findEvent(event.getId(), 날짜_8월_10일()); @@ -169,9 +230,10 @@ void success() { void fail_EventNotFoundException() { //given final Long notFoundEventId = Long.MAX_VALUE; - + final LocalDate today = 날짜_8월_10일(); //when, then - assertThatThrownBy(() -> eventService.findEvent(notFoundEventId, 날짜_8월_10일())) + assertThatThrownBy(() -> + eventService.findEvent(notFoundEventId, today)) .isInstanceOf(EventException.class) .hasMessage(NOT_FOUND_EVENT.errorMessage()); } @@ -195,7 +257,7 @@ void findEvents_CONFERENCE() { // then assertThat(actualEvents) .usingRecursiveComparison() - .comparingOnlyFields("name", "status", "applyStatus") + .comparingOnlyFields("name", "status", "applyStatus", "imageUrl") .isEqualTo(expectedEvents); } @@ -212,7 +274,7 @@ void findEvents_COMPETITION() { // then assertThat(actualEvents) .usingRecursiveComparison() - .comparingOnlyFields("name", "status", "applyStatus") + .comparingOnlyFields("name", "status", "applyStatus", "imageUrl") .isEqualTo(expectedEvents); } @@ -229,7 +291,7 @@ void findEvents_2023_7() { // then assertThat(actualEvents) .usingRecursiveComparison() - .comparingOnlyFields("name", "status", "applyStatus") + .comparingOnlyFields("name", "status", "applyStatus", "imageUrl") .isEqualTo(expectedEvents); } @@ -246,7 +308,7 @@ void findEvents_2023_8() { // then assertThat(actualEvents) .usingRecursiveComparison() - .comparingOnlyFields("name", "status", "applyStatus") + .comparingOnlyFields("name", "status", "applyStatus", "imageUrl") .isEqualTo(expectedEvents); } @@ -263,7 +325,7 @@ void findEvents_2023_6() { // then assertThat(actualEvents) .usingRecursiveComparison() - .comparingOnlyFields("name", "status", "applyStatus") + .comparingOnlyFields("name", "status", "applyStatus", "imageUrl") .isEqualTo(expectedEvents); } @@ -280,7 +342,7 @@ void findEvents_after_2023_7_17() { // then assertThat(actualEvents) .usingRecursiveComparison() - .comparingOnlyFields("name", "status", "applyStatus") + .comparingOnlyFields("name", "status", "applyStatus", "imageUrl") .isEqualTo(expectedEvents); } @@ -297,7 +359,7 @@ void findEvents_before_2023_7_31() { // then assertThat(actualEvents) .usingRecursiveComparison() - .comparingOnlyFields("name", "status", "applyStatus") + .comparingOnlyFields("name", "status", "applyStatus", "imageUrl") .isEqualTo(expectedEvents); } @@ -408,7 +470,7 @@ void findEvents_tag_filter() { // then assertThat(actualEvents) .usingRecursiveComparison() - .comparingOnlyFields("name", "status", "applyStatus") + .comparingOnlyFields("name", "status", "applyStatus", "imageUrl") .isEqualTo(expectedEvents); } @@ -426,7 +488,7 @@ void findEvents_tags_filter() { // then assertThat(actualEvents) .usingRecursiveComparison() - .comparingOnlyFields("name", "status", "applyStatus") + .comparingOnlyFields("name", "status", "applyStatus", "imageUrl") .isEqualTo(expectedEvents); } @@ -457,7 +519,7 @@ void findEvents_status_filter() { // then assertThat(actualEvents) .usingRecursiveComparison() - .comparingOnlyFields("name", "status", "applyStatus") + .comparingOnlyFields("name", "status", "applyStatus", "imageUrl") .isEqualTo(expectedEvents); } @@ -475,7 +537,7 @@ void findEvents_statuses_filter() { // then assertThat(actualEvents) .usingRecursiveComparison() - .comparingOnlyFields("name", "status", "applyStatus") + .comparingOnlyFields("name", "status", "applyStatus", "imageUrl") .isEqualTo(expectedEvents); } @@ -493,7 +555,7 @@ void findEvents_period_tags_filter() { // then assertThat(actualEvents) .usingRecursiveComparison() - .comparingOnlyFields("name", "status", "applyStatus") + .comparingOnlyFields("name", "status", "applyStatus", "imageUrl") .isEqualTo(expectedEvents); } } @@ -521,9 +583,6 @@ class AddEvent { @DisplayName("이벤트를 성공적으로 저장한다.") void addEventTest() { //given - final Image image1 = new Image("image", ImageType.EVENT, 1L, 0, LocalDateTime.now()); - final Image image2 = new Image("image", ImageType.EVENT, 1L, 0, LocalDateTime.now()); - final EventDetailRequest request = new EventDetailRequest( eventName, eventLocation, @@ -533,17 +592,17 @@ void addEventTest() { beforeDateTime, afterDateTime, tagRequests, - imageUrl, type, eventMode, paymentType, organization ); - doNothing().when(firebaseCloudMessageClient).sendMessageTo(any(Notification.class), anyLong()); + doNothing().when(firebaseCloudMessageClient) + .sendMessageTo(any(Notification.class), anyLong()); //when - final EventDetailResponse response = eventService.addEvent(request, mockMultipartFiles, now); + final EventDetailResponse response = eventService.addEvent(request, mockMultipartFiles); final Event savedEvent = eventRepository.findById(response.getId()).get(); //then @@ -578,20 +637,20 @@ void addEventWithStartDateTimeAfterBeforeDateTimeTest() { beforeDateTime, afterDateTime, tagRequests, - imageUrl, type, eventMode, paymentType, organization ); - doNothing().when(firebaseCloudMessageClient).sendMessageTo(any(Notification.class), anyLong()); + doNothing().when(firebaseCloudMessageClient) + .sendMessageTo(any(Notification.class), anyLong()); //when & then final EventException exception = assertThrowsExactly(EventException.class, - () -> eventService.addEvent(request, mockMultipartFiles, now)); + () -> eventService.addEvent(request, mockMultipartFiles)); - assertEquals(exception.exceptionType(), START_DATE_TIME_AFTER_END_DATE_TIME); + assertEquals(START_DATE_TIME_AFTER_END_DATE_TIME, exception.exceptionType()); } @Test @@ -613,20 +672,20 @@ void addEventWithNotExistTagTest() { beforeDateTime, afterDateTime, tagRequests, - imageUrl, type, eventMode, paymentType, organization ); - doNothing().when(firebaseCloudMessageClient).sendMessageTo(any(Notification.class), anyLong()); + doNothing().when(firebaseCloudMessageClient) + .sendMessageTo(any(Notification.class), anyLong()); //when & then final EventException exception = assertThrowsExactly(EventException.class, - () -> eventService.addEvent(request, mockMultipartFiles, now)); + () -> eventService.addEvent(request, mockMultipartFiles)); - assertEquals(exception.exceptionType(), NOT_FOUND_TAG); + assertEquals(NOT_FOUND_TAG, exception.exceptionType()); } } @@ -642,7 +701,6 @@ class UpdateEvent { private final String newName = "새로운 이름"; private final String newLocation = "새로운 장소"; private final String newInformationUrl = "https://새로운-상세-URL.com"; - private final String imageUrl = "https://image.com"; private final LocalDate now = LocalDate.now(); private final PaymentType paymentType = PaymentType.FREE_PAID; private final EventMode eventMode = EventMode.ON_OFFLINE; @@ -664,7 +722,6 @@ void updateEventTest() { beforeDateTime, afterDateTime, newTagRequests, - imageUrl, EventType.CONFERENCE, eventMode, paymentType, @@ -676,7 +733,7 @@ void updateEventTest() { //when final EventDetailResponse response = eventService.updateEvent(eventId, updateRequest, - mockMultipartFiles, now); + mockMultipartFiles); final Event updatedEvent = eventRepository.findById(eventId).get(); //then @@ -710,7 +767,6 @@ void updateEventWithNotExistsEventTest() { beforeDateTime, afterDateTime, newTagRequests, - imageUrl, EventType.CONFERENCE, eventMode, paymentType, @@ -719,9 +775,9 @@ void updateEventWithNotExistsEventTest() { //when & then final EventException exception = assertThrowsExactly(EventException.class, - () -> eventService.updateEvent(notExistsEventId, updateRequest, mockMultipartFiles, now)); + () -> eventService.updateEvent(notExistsEventId, updateRequest, mockMultipartFiles)); - assertEquals(exception.exceptionType(), NOT_FOUND_EVENT); + assertEquals(NOT_FOUND_EVENT, exception.exceptionType()); } @Test @@ -740,7 +796,6 @@ void updateEventWithStartDateTimeAfterBeforeDateTimeTest() { beforeDateTime, afterDateTime, newTagRequests, - imageUrl, EventType.CONFERENCE, eventMode, paymentType, @@ -752,9 +807,9 @@ void updateEventWithStartDateTimeAfterBeforeDateTimeTest() { //when & then final EventException exception = assertThrowsExactly(EventException.class, - () -> eventService.updateEvent(eventId, updateRequest, mockMultipartFiles, now)); + () -> eventService.updateEvent(eventId, updateRequest, mockMultipartFiles)); - assertEquals(exception.exceptionType(), START_DATE_TIME_AFTER_END_DATE_TIME); + assertEquals(START_DATE_TIME_AFTER_END_DATE_TIME, exception.exceptionType()); } @Test @@ -774,7 +829,6 @@ void updateEventWithNotExistTagTest() { beforeDateTime, afterDateTime, newTagRequests, - imageUrl, EventType.CONFERENCE, eventMode, paymentType, @@ -786,9 +840,9 @@ void updateEventWithNotExistTagTest() { //when & then final EventException exception = assertThrowsExactly(EventException.class, - () -> eventService.updateEvent(eventId, updateRequest, mockMultipartFiles, now)); + () -> eventService.updateEvent(eventId, updateRequest, mockMultipartFiles)); - assertEquals(exception.exceptionType(), NOT_FOUND_TAG); + assertEquals(NOT_FOUND_TAG, exception.exceptionType()); } } @@ -819,7 +873,7 @@ void deleteEventWithNotExistsEventTest() { final EventException exception = assertThrowsExactly(EventException.class, () -> eventService.deleteEvent(notExistsEventId)); - assertEquals(exception.exceptionType(), NOT_FOUND_EVENT); + assertEquals(NOT_FOUND_EVENT, exception.exceptionType()); } } } 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/EventDetailResponseTest.java index 132b50fd3..885fe1c77 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/EventDetailResponseTest.java @@ -4,9 +4,7 @@ import com.emmsale.event.EventFixture; import com.emmsale.event.domain.Event; -import com.emmsale.event.domain.EventStatus; import com.emmsale.event.domain.PaymentType; -import java.time.LocalDate; import java.util.Collections; import java.util.List; import org.junit.jupiter.api.DisplayName; @@ -19,7 +17,7 @@ class EventDetailResponseTest { void createEventDetailResponseTest() { //given final Event 구름톤 = EventFixture.구름톤(); - final LocalDate 날짜 = LocalDate.of(2023, 7, 1); + final String thumbnailUrl = "thumbnail"; final List imageUrls = List.of("imageUrl1", "imageUrl2"); final EventDetailResponse expected = new EventDetailResponse( @@ -31,11 +29,8 @@ void createEventDetailResponseTest() { 구름톤.getEventPeriod().getApplyStartDate(), 구름톤.getEventPeriod().getApplyEndDate(), 구름톤.getLocation(), - EventStatus.UPCOMING.name(), - EventStatus.UPCOMING.name(), Collections.emptyList(), - 구름톤.getImageUrl(), - 2, 2, + thumbnailUrl, 구름톤.getType().toString(), imageUrls, 구름톤.getOrganization(), @@ -43,7 +38,7 @@ void createEventDetailResponseTest() { ); //when - final EventDetailResponse actual = EventDetailResponse.from(구름톤, 날짜, imageUrls); + final EventDetailResponse actual = EventDetailResponse.from(구름톤, thumbnailUrl, imageUrls); //then assertThat(actual) diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/domain/EventTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/domain/EventTest.java index 1a1470ca6..7c4009948 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/event/domain/EventTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/event/domain/EventTest.java @@ -51,13 +51,12 @@ void newEventWithStartDateAfterEndDateTest() { final String url = "https://information-url.com"; final LocalDateTime beforeDateTime = LocalDateTime.now(); final LocalDateTime afterDateTime = beforeDateTime.plusDays(1); - final String imageUrl = "https://image.com"; final String organization = "행사기관"; //when & then final EventException exception = assertThrowsExactly(EventException.class, () -> new Event(name, location, afterDateTime, beforeDateTime, beforeDateTime, - beforeDateTime, url, EventType.CONFERENCE, imageUrl, PaymentType.FREE, + beforeDateTime, url, EventType.CONFERENCE, PaymentType.FREE, EventMode.ON_OFFLINE, organization)); assertEquals(EventExceptionType.START_DATE_TIME_AFTER_END_DATE_TIME, exception.exceptionType()); @@ -72,13 +71,12 @@ void newEvent_fail_SUBSCRIPTION_START_AFTER_SUBSCRIPTION_END() { final String url = "https://information-url.com"; final LocalDateTime beforeDateTime = LocalDateTime.now(); final LocalDateTime afterDateTime = beforeDateTime.plusDays(1); - final String imageUrl = "https://image.com"; final String organization = "행사기관"; //when & then final EventException exception = assertThrowsExactly(EventException.class, () -> new Event(name, location, beforeDateTime, afterDateTime, - afterDateTime, beforeDateTime, url, EventType.CONFERENCE, imageUrl, PaymentType.FREE, + afterDateTime, beforeDateTime, url, EventType.CONFERENCE, PaymentType.FREE, EventMode.ON_OFFLINE, organization)); assertEquals(EventExceptionType.SUBSCRIPTION_START_AFTER_SUBSCRIPTION_END, @@ -94,13 +92,12 @@ void newEvent_fail_SUBSCRIPTION_END_AFTER_EVENT_END() { final String url = "https://information-url.com"; final LocalDateTime beforeDateTime = LocalDateTime.now(); final LocalDateTime afterDateTime = beforeDateTime.plusDays(1); - final String imageUrl = "https://image.com"; final String organization = "행사기관"; //when & then final EventException exception = assertThrowsExactly(EventException.class, () -> new Event(name, location, beforeDateTime, beforeDateTime, - beforeDateTime, afterDateTime, url, EventType.CONFERENCE, imageUrl, PaymentType.FREE, + beforeDateTime, afterDateTime, url, EventType.CONFERENCE, PaymentType.FREE, EventMode.ON_OFFLINE, organization)); assertEquals(EventExceptionType.SUBSCRIPTION_END_AFTER_EVENT_END, exception.exceptionType()); @@ -115,13 +112,12 @@ void newEvent_fail_SUBSCRIPTION_START_AFTER_EVENT_START() { final String url = "https://information-url.com"; final LocalDateTime beforeDateTime = LocalDateTime.now(); final LocalDateTime afterDateTime = beforeDateTime.plusDays(1); - final String imageUrl = "https://image.com"; final String organization = "행사기관"; //when & then final EventException exception = assertThrowsExactly(EventException.class, () -> new Event(name, location, beforeDateTime, afterDateTime, afterDateTime, afterDateTime, - url, EventType.CONFERENCE, imageUrl, PaymentType.FREE, EventMode.ON_OFFLINE, organization)); + url, EventType.CONFERENCE, PaymentType.FREE, EventMode.ON_OFFLINE, organization)); assertEquals(EventExceptionType.SUBSCRIPTION_START_AFTER_EVENT_START, exception.exceptionType()); diff --git a/backend/emm-sale/src/test/java/com/emmsale/image/ImageFixture.java b/backend/emm-sale/src/test/java/com/emmsale/image/ImageFixture.java new file mode 100644 index 000000000..60af6fe91 --- /dev/null +++ b/backend/emm-sale/src/test/java/com/emmsale/image/ImageFixture.java @@ -0,0 +1,28 @@ +package com.emmsale.image; + +import com.emmsale.image.domain.Image; +import com.emmsale.image.domain.ImageType; +import java.time.LocalDateTime; + +public class ImageFixture { + + public static Image 행사_이미지1(final Long eventId) { + return new Image( + "이미지1", ImageType.EVENT, eventId, 0, LocalDateTime.of(2023, 8, 15, 15, 0)); + } + + public static Image 행사_이미지2(final Long eventId) { + return new Image( + "이미지2", ImageType.EVENT, eventId, 1, LocalDateTime.of(2023, 8, 15, 15, 0)); + } + + public static Image 행사_이미지3(final Long eventId) { + return new Image( + "이미지3", ImageType.EVENT, eventId, 2, LocalDateTime.of(2023, 8, 15, 15, 0)); + } + + public static Image 행사_이미지4(final Long eventId) { + return new Image( + "이미지4", ImageType.EVENT, eventId, 3, LocalDateTime.of(2023, 8, 15, 15, 0)); + } +} diff --git a/backend/emm-sale/src/test/java/com/emmsale/image/domain/repository/ImageRepositoryTest.java b/backend/emm-sale/src/test/java/com/emmsale/image/domain/repository/ImageRepositoryTest.java index 73e879ffc..2fe56f72f 100644 --- a/backend/emm-sale/src/test/java/com/emmsale/image/domain/repository/ImageRepositoryTest.java +++ b/backend/emm-sale/src/test/java/com/emmsale/image/domain/repository/ImageRepositoryTest.java @@ -20,15 +20,35 @@ class ImageRepositoryTest extends JpaRepositorySliceTestHelper { private Image image2; private Image image3; private Image image4; + private Image image5; + private Image image6; @BeforeEach void setUp() { image1 = new Image("name1", ImageType.FEED, 1L, 1, LocalDateTime.now()); image2 = new Image("name2", ImageType.FEED, 1L, 2, LocalDateTime.now()); image3 = new Image("name3", ImageType.FEED, 2L, 1, LocalDateTime.now()); - image4 = new Image("name4", ImageType.EVENT, 1L, 1, LocalDateTime.now()); + image4 = new Image("name4", ImageType.EVENT, 1L, 0, LocalDateTime.now()); + image5 = new Image("name5", ImageType.EVENT, 1L, 1, LocalDateTime.now()); + image6 = new Image("name6", ImageType.EVENT, 2L, 0, LocalDateTime.now()); + + imageRepository.saveAll(List.of(image1, image2, image3, image4, image5, image6)); + } + + @Test + @DisplayName("복수 개의 행사 id들에 해당하는 섬네일 이미지들을 조회할 수 있다.") + void findAllThumbnailByEventIdIn() { + //given + final List expect = List.of(image4, image6); + + //when + final List actual = imageRepository.findAllThumbnailByEventIdIn(List.of(1L, 2L)); + + //then + assertThat(actual) + .usingRecursiveComparison() + .isEqualTo(expect); - imageRepository.saveAll(List.of(image1, image2, image3, image4)); } @Test 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 033c89b8b..b522baca9 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 @@ -1,5 +1,9 @@ package com.emmsale.scrap.application; +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.image.ImageFixture.행사_이미지4; import static org.assertj.core.api.Assertions.assertThat; import com.emmsale.event.EventFixture; @@ -7,6 +11,7 @@ import com.emmsale.event.domain.Event; import com.emmsale.event.domain.repository.EventRepository; import com.emmsale.helper.ServiceIntegrationTestHelper; +import com.emmsale.image.domain.repository.ImageRepository; import com.emmsale.member.domain.Member; import com.emmsale.member.domain.MemberRepository; import com.emmsale.scrap.domain.Scrap; @@ -27,6 +32,8 @@ class ScrapQueryServiceTest extends ServiceIntegrationTestHelper { private MemberRepository memberRepository; @Autowired private EventRepository eventRepository; + @Autowired + private ImageRepository imageRepository; @Test @DisplayName("전체 스크랩 목록을 조회한다.") @@ -36,7 +43,9 @@ void findAllScrapsTest() { final Event event1 = eventRepository.save(EventFixture.인프콘_2023()); final Event event2 = eventRepository.save(EventFixture.구름톤()); - + imageRepository.saveAll( + List.of(행사_이미지1(event1.getId()), 행사_이미지2(event1.getId()), 행사_이미지3(event1.getId()), + 행사_이미지4(event1.getId()))); scrapRepository.save(new Scrap(member.getId(), event1)); scrapRepository.save(new Scrap(member.getId(), event2)); @@ -47,12 +56,13 @@ void findAllScrapsTest() { new EventResponse(event1.getId(), event1.getName(), event1.getEventPeriod().getStartDate(), event1.getEventPeriod().getEndDate(), event1.getEventPeriod().getApplyStartDate(), event1.getEventPeriod().getApplyEndDate(), - Collections.emptyList(), event1.getImageUrl(), event1.getEventMode().getValue(), + 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(), event2.getImageUrl(), event2.getEventMode().getValue(), + Collections.emptyList(), null, event2.getEventMode().getValue(), event2.getPaymentType().getValue()) );