Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/#664 행사 dto의 image url을 s3의 데이터로 대체 #676

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
4e2d027
Merge branch 'backend-main' of https://github.com/woowacourse-teams/2…
Sep 25, 2023
61ad1bf
Merge branches 'backend-main' and 'backend-main' of https://github.co…
Sep 27, 2023
6920902
feat: 행사 단건 조회 시 섬네일 이미지와 나머지 이미지들을 반환하도록 DTO 수정
Sep 27, 2023
67616b9
Merge branch 'Feature/#650-행사_상태_리팩터링' of https://github.com/woowacou…
Sep 27, 2023
3980a47
test: 이미지 Fixture 추가
Sep 28, 2023
7a8452e
feat: 행사 섬네일 이미지만 가져오는 쿼리문 추가
Sep 28, 2023
488018e
test: 행사 섬네일 이미지만 가져오는 쿼리문 테스트 작성
Sep 28, 2023
9a9e042
feat: 불필요한 DTO 제거
Sep 28, 2023
e1f465d
feat: 행사 다건 조회 시 S3에 저장된 이미지를 조회하도록 수정 및 행사 도메인의 imageUrl 컬럼 제거
Sep 28, 2023
7f8f6b1
feat: 행사 생성/수정 시 imageUrl을 받지 않도록 수정
Sep 28, 2023
b6f4208
feat: event table의 image_url 컬럼 제거
Sep 29, 2023
2dc704c
Merge branch 'backend-main' into Feature/#664-행사_DTO의_imageUrl을_S3의_데…
amaran-th Oct 3, 2023
568b8e4
fix: data.sql에서 event의 image 컬럼 값 제거
Oct 4, 2023
dbfcf72
Merge branch 'Feature/#664-행사_DTO의_imageUrl을_S3의_데이터로_대체' of https://…
Oct 4, 2023
5e2152c
fix: schema.sql ddl 끝에 세미콜론 추가
Oct 4, 2023
6294a3b
Merge branch 'backend-main' into Feature/#664-행사_DTO의_imageUrl을_S3의_데…
amaran-th Oct 5, 2023
88360e9
fix: DetailResponse에서 imageUrl 제거
Oct 6, 2023
9696715
Merge branch 'backend-main' into Feature/#664-행사_DTO의_imageUrl을_S3의_데…
amaran-th Oct 6, 2023
53749ce
refactor: 행사 상세 정보 조회 dto에서 필요없는 요소 제거
Oct 7, 2023
7cca4ea
refactor: 행사 상세 정보 조회 dto에서 필요없는 요소 제거
Oct 7, 2023
965b078
refactor: 행사 상세 조회/목록 조회 DTO의 imageUrl을 thumbnailUrl로 이름 변경
Oct 9, 2023
1978ad0
Merge branch 'backend-main' of https://github.com/woowacourse-teams/2…
Oct 9, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(),
Expand All @@ -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()))
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,15 @@ public ResponseEntity<List<EventResponse>> findEvents(
@ResponseStatus(HttpStatus.CREATED)
public EventDetailResponse addEvent(@RequestPart @Valid final EventDetailRequest request,
@RequestPart final List<MultipartFile> images) {
return eventService.addEvent(request, images, LocalDate.now());
return eventService.addEvent(request, images);
}

@PutMapping(path = "/{eventId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ResponseStatus(HttpStatus.OK)
public EventDetailResponse updateEvent(@PathVariable final Long eventId,
@RequestPart @Valid final EventDetailRequest request,
@RequestPart final List<MultipartFile> images) {
return eventService.updateEvent(eventId, request, images, LocalDate.now());
return eventService.updateEvent(eventId, request, images);
}

@DeleteMapping("/{eventId}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<String> informationImageUrls = extractInformationImages(imageUrls);
return EventDetailResponse.from(event, thumbnailImageUrl, informationImageUrls);
}

private String extractThumbnailImage(final List<String> imageUrls) {
if (imageUrls.isEmpty()) {
return null;
}
return imageUrls.get(0);
}

return EventDetailResponse.from(event, today, imageUrls);
private List<String> extractInformationImages(final List<String> imageUrls) {
if (imageUrls.size() <= 1) {
return Collections.emptyList();
}
return imageUrls.subList(1, imageUrls.size());
}

@Transactional(readOnly = true)
Expand All @@ -88,10 +107,11 @@ public List<EventResponse> findEvents(final EventType category,
spec = spec.and(EventSpecification.filterByPeriod(startDateTime, endDateTime));
}
final List<Event> events = eventRepository.findAll(spec);

final EnumMap<EventStatus, List<Event>> eventsForEventStatus
= groupByEventStatus(nowDate, events);

return filterByStatuses(statuses, eventsForEventStatus);
return filterByStatuses(statuses, eventsForEventStatus, makeImageUrlPerEventId(events));
}

private boolean isExistTagNames(final List<String> tagNames) {
Expand Down Expand Up @@ -138,6 +158,19 @@ private void validateEndDateAfterDateStart(final LocalDateTime startDate,
}
}

// TODO: 2023/09/27 코드 중복 제거(ScrapService)
private Map<Long, String> makeImageUrlPerEventId(final List<Event> events) {
final List<Long> scrappedEventIds = events.stream()
.map(Event::getId)
.collect(Collectors.toList());
final List<Image> images = imageRepository.findAllThumbnailByEventIdIn(scrappedEventIds);
Map<Long, String> imageUrlPerEventId = new HashMap<>();
for (Image image : images) {
imageUrlPerEventId.put(image.getContentId(), image.getName());
}
return imageUrlPerEventId;
}

private EnumMap<EventStatus, List<Event>> groupByEventStatus(final LocalDate nowDate,
final List<Event> events) {
return events.stream()
Expand All @@ -150,12 +183,13 @@ private EnumMap<EventStatus, List<Event>> groupByEventStatus(final LocalDate now

private List<EventResponse> filterByStatuses(
final List<EventStatus> statuses,
final EnumMap<EventStatus, List<Event>> eventsForEventStatus
final EnumMap<EventStatus, List<Event>> eventsForEventStatus,
final Map<Long, String> 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<EventStatus> statuses) {
Expand All @@ -164,20 +198,22 @@ private boolean isExistStatusName(final List<EventStatus> statuses) {

private List<EventResponse> filterEventResponseByStatuses(
final List<EventStatus> statuses,
final EnumMap<EventStatus, List<Event>> eventsForEventStatus
final EnumMap<EventStatus, List<Event>> eventsForEventStatus,
final Map<Long, String> 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;
});
}

public EventDetailResponse addEvent(final EventDetailRequest request,
final List<MultipartFile> images, final LocalDate today) {
final List<MultipartFile> images) {
final Event event = eventRepository.save(request.toEvent());
final List<Tag> tags = findAllPersistTagsOrElseThrow(request.getTags());
event.addAllEventTags(tags);
Expand All @@ -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<String> informationImageUrls = extractInformationImages(imageUrls);
return EventDetailResponse.from(event, thumbnailImageUrl, informationImageUrls);
}

public EventDetailResponse updateEvent(final Long eventId, final EventDetailRequest request,
final List<MultipartFile> images, final LocalDate today) {
final List<MultipartFile> images) {
final Event event = eventRepository.findById(eventId)
.orElseThrow(() -> new EventException(NOT_FOUND_EVENT));

Expand All @@ -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<String> informationImageUrls = extractInformationImages(imageUrls);
return EventDetailResponse.from(updatedEvent, thumbnailImageUrl, informationImageUrls);
}

public void deleteEvent(final Long eventId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ public class EventDetailRequest {
private final LocalDateTime applyEndDateTime;

private final List<TagRequest> tags;

private final String imageUrl;
private final EventType type;

private final EventMode eventMode;
Expand All @@ -62,7 +60,6 @@ public Event toEvent() {
applyEndDateTime,
informationUrl,
type,
imageUrl,
paymentType,
eventMode,
organization
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -30,20 +29,16 @@ 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<String> tags;
private final String imageUrl;
private final Integer remainingDays;
private final Integer applyRemainingDays;
private final String thumbnailUrl;
private final String type;
private final List<String> imageUrls;
private final String organization;
private final String paymentType;

public static EventDetailResponse from(
final Event event,
final LocalDate today,
final String thumbnailUrl,
final List<String> imageUrls
) {
final List<String> tagNames = event.getTags().stream()
Expand All @@ -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(),
Expand Down
Loading
Loading