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 21 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
128 changes: 66 additions & 62 deletions backend/emm-sale/src/documentTest/java/com/emmsale/EventApiTest.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -36,40 +36,54 @@ class ScrapApiTest extends MockMvcTestHelper {
void findAllScraps() throws Exception {
//given
final List<EventResponse> expectedScrapResponse = List.of(
new EventResponse(1L, "인프콘 2023", LocalDateTime.parse("2023-06-03T12:00:00"),
new EventResponse(
1L,
"인프콘 2023",
LocalDateTime.parse("2023-06-03T12:00:00"),
LocalDateTime.parse("2023-09-03T12:00:00"),
List.of("백엔드", "프론트엔드", "안드로이드", "IOS", "AI"), "IN_PROGRESS", "ENDED",
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",
3, -30, EventMode.ONLINE.getValue(), PaymentType.PAID.getValue()),
new EventResponse(5L, "웹 컨퍼런스", LocalDateTime.parse("2023-07-03T12:00:00"),
LocalDateTime.parse("2023-08-03T12:00:00"), List.of("백엔드", "프론트엔드"),
"IN_PROGRESS", "IN_PROGRESS",
EventMode.ONLINE.getValue(),
PaymentType.PAID.getValue()
),
new EventResponse(
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",
3, 3, EventMode.ONLINE.getValue(), PaymentType.PAID.getValue()),
new EventResponse(2L, "AI 컨퍼런스", LocalDateTime.parse("2023-07-22T12:00:00"),
LocalDateTime.parse("2023-07-30T12:00:00"), List.of("AI"), "UPCOMING",
"IN_PROGRESS",
EventMode.ONLINE.getValue(),
PaymentType.PAID.getValue()),
new EventResponse(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",
3, -18, EventMode.ONLINE.getValue(), PaymentType.PAID.getValue())
EventMode.ONLINE.getValue(),
PaymentType.PAID.getValue())
);

final ResponseFieldsSnippet responseFields = PayloadDocumentation.responseFields(
PayloadDocumentation.fieldWithPath("[].id").type(JsonFieldType.NUMBER).description("행사 id"),
PayloadDocumentation.fieldWithPath("[].name").type(JsonFieldType.STRING).description("행사명"),
PayloadDocumentation.fieldWithPath("[].startDate").type(JsonFieldType.STRING)
PayloadDocumentation.fieldWithPath("[].eventStartDate").type(JsonFieldType.STRING)
.description("행사 시작일(yyyy:MM:dd:HH:mm:ss)"),
PayloadDocumentation.fieldWithPath("[].endDate").type(JsonFieldType.STRING)
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("[].status").type(JsonFieldType.STRING)
.description("행사 진행 상황(IN_PROGRESS, UPCOMING, ENDED)"),
PayloadDocumentation.fieldWithPath("[].applyStatus").type(JsonFieldType.STRING)
.description("행사 신청 기간의 진행 상황(IN_PROGRESS, UPCOMING, ENDED)"),
PayloadDocumentation.fieldWithPath("[].remainingDays").type(JsonFieldType.NUMBER)
.description("행사 시작일까지 남은 일 수"),
PayloadDocumentation.fieldWithPath("[].applyRemainingDays").type(JsonFieldType.NUMBER)
.description("행사 신청 시작일까지 남은 일 수"),
PayloadDocumentation.fieldWithPath("[].imageUrl").type(JsonFieldType.STRING)
.description("행사 이미지 URL"),
PayloadDocumentation.fieldWithPath("[].eventMode").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(nowDate, statuses, eventsForEventStatus);
return filterByStatuses(statuses, eventsForEventStatus, makeImageUrlPerEventId(events));
}

private boolean isExistTagNames(final List<String> tagNames) {
Expand All @@ -115,7 +135,7 @@ private LocalDateTime validateStartDate(final String date) {
return LocalDate.parse(MIN_DATE).atStartOfDay();
}
return LocalDate.parse(date).atStartOfDay();
} catch (DateTimeParseException exception) {
} catch (final DateTimeParseException exception) {
throw new EventException(EventExceptionType.INVALID_DATE_FORMAT);
}
}
Expand All @@ -126,7 +146,7 @@ private LocalDateTime validateEndDate(final String date) {
return LocalDate.parse(MAX_DATE).atTime(23, 59, 59);
}
return LocalDate.parse(date).atTime(23, 59, 59);
} catch (DateTimeParseException exception) {
} catch (final DateTimeParseException exception) {
throw new EventException(EventExceptionType.INVALID_DATE_FORMAT);
}
}
Expand All @@ -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 @@ -149,38 +182,38 @@ private EnumMap<EventStatus, List<Event>> groupByEventStatus(final LocalDate now
}

private List<EventResponse> filterByStatuses(
final LocalDate today,
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(today, statuses, eventsForEventStatus);
return filterEventResponseByStatuses(statuses, eventsForEventStatus, imageUrlPerEventId);
}
return EventResponse.mergeEventResponses(today, eventsForEventStatus);
return EventResponse.mergeEventResponses(eventsForEventStatus, imageUrlPerEventId);
}

private boolean isExistStatusName(final List<EventStatus> statuses) {
return statuses != null;
}

private List<EventResponse> filterEventResponseByStatuses(
final LocalDate today,
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(today, entry.getKey(),
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 @@ -193,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 @@ -223,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 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 imageUrl,
Copy link
Collaborator

@hyeonjerry hyeonjerry Oct 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

개인적으로 imageUrl이라고 하는 것 보다 thumbnailUrl이라고 하는게 더 명확하게 의미를 전달할 수 있을 것 같아요.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

의견 반영해서 EventDetailResponse, EventResponse의 imageUrl을 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),
imageUrl,
event.getType().toString(),
imageUrls,
event.getOrganization(),
Expand Down
Loading
Loading