Skip to content

Commit

Permalink
Merge pull request #138 from onetime-with-members/feature/#126/mod-event
Browse files Browse the repository at this point in the history
[feat] : 유저는 자신이 생성한 이벤트를 수정할 수 있다
  • Loading branch information
bbbang105 authored Dec 10, 2024
2 parents 000dc2a + 726a906 commit dd02779
Show file tree
Hide file tree
Showing 10 changed files with 252 additions and 47 deletions.
26 changes: 16 additions & 10 deletions src/main/java/side/onetime/controller/EventController.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import side.onetime.dto.event.request.CreateEventRequest;
import side.onetime.dto.event.request.ModifyUserCreatedEventTitleRequest;
import side.onetime.dto.event.request.ModifyUserCreatedEventRequest;
import side.onetime.dto.event.response.*;
import side.onetime.global.common.ApiResponse;
import side.onetime.global.common.status.SuccessStatus;
Expand Down Expand Up @@ -131,23 +131,29 @@ public ResponseEntity<ApiResponse<SuccessStatus>> removeUserCreatedEvent(
}

/**
* 유저가 생성한 이벤트 제목 수정 API.
* 유저가 생성한 이벤트 수정 API.
*
* 이 API는 인증된 유저가 생성한 특정 이벤트의 제목을 수정합니다.
* 이 API는 인증된 유저가 생성한 특정 이벤트의 제목, 시간, 설문 범위를 수정합니다.
* 수정 가능한 항목은 다음과 같습니다:
* - 이벤트 제목
* - 시작 시간 및 종료 시간
* - 날짜 또는 요일 범위
*
* 요청 데이터에 따라 변경 사항을 반영하며, 필요에 따라 기존 스케줄 데이터를 삭제하거나 새로운 스케줄을 생성합니다.
*
* @param authorizationHeader 인증된 유저의 토큰
* @param eventId 제목을 수정할 이벤트의 ID
* @param modifyUserCreatedEventTitleRequest 새로운 제목 정보가 담긴 요청 데이터
* @param eventId 수정할 이벤트의 ID
* @param modifyUserCreatedEventRequest 새로운 이벤트 정보가 담긴 요청 데이터 (제목, 시간, 범위 등)
* @return 수정 성공 여부
*/
@PutMapping("/{event_id}")
public ResponseEntity<ApiResponse<SuccessStatus>> modifyUserCreatedEventTitle(
@PatchMapping("/{event_id}")
public ResponseEntity<ApiResponse<SuccessStatus>> modifyUserCreatedEvent(
@RequestHeader("Authorization") String authorizationHeader,
@PathVariable("event_id") String eventId,
@Valid @RequestBody ModifyUserCreatedEventTitleRequest modifyUserCreatedEventTitleRequest) {
@Valid @RequestBody ModifyUserCreatedEventRequest modifyUserCreatedEventRequest) {

eventService.modifyUserCreatedEventTitle(authorizationHeader, eventId, modifyUserCreatedEventTitleRequest);
return ApiResponse.onSuccess(SuccessStatus._MODIFY_USER_CREATED_EVENT_TITLE);
eventService.modifyUserCreatedEvent(authorizationHeader, eventId, modifyUserCreatedEventRequest);
return ApiResponse.onSuccess(SuccessStatus._MODIFY_USER_CREATED_EVENT);
}

/**
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/side/onetime/domain/Event.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ public void updateTitle(String title) {
this.title = title;
}

public void updateStartTime(String startTime) {
this.startTime = startTime;
}

public void updateEndTime(String endTime) {
this.endTime = endTime;
}

public void addQrFileName(String qrFileName) {
this.qrFileName = qrFileName;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package side.onetime.dto.event.request;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

import java.util.List;

@JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class)
@JsonInclude(JsonInclude.Include.NON_NULL)
public record ModifyUserCreatedEventRequest(
@NotBlank(message = "제목은 필수 값입니다.") String title,
@NotBlank(message = "시작 시간은 필수 값입니다.") String startTime,
@NotBlank(message = "종료 시간은 필수 값입니다.") String endTime,
@NotNull(message = "설문 범위는 필수 값입니다.") List<String> ranges
) {
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public enum SuccessStatus implements BaseCode {
_GET_MOST_POSSIBLE_TIME(HttpStatus.OK, "200", "가장 많이 되는 시간 조회에 성공했습니다."),
_GET_USER_PARTICIPATED_EVENTS(HttpStatus.OK, "200", "유저 참여 이벤트 목록 조회에 성공했습니다."),
_REMOVE_USER_CREATED_EVENT(HttpStatus.OK, "200", "유저가 생성한 이벤트 삭제에 성공했습니다."),
_MODIFY_USER_CREATED_EVENT_TITLE(HttpStatus.OK, "200", "유저가 생성한 이벤트 제목 수정에 성공했습니다."),
_MODIFY_USER_CREATED_EVENT(HttpStatus.OK, "200", "유저가 생성한 이벤트 수정에 성공했습니다."),
_GET_EVENT_QR_CODE(HttpStatus.OK, "200", "이벤트 QR 코드 조회에 성공했습니다."),
// Member
_REGISTER_MEMBER(HttpStatus.CREATED, "201", "멤버 등록에 성공했습니다."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@

public interface EventRepositoryCustom {
void deleteEvent(Event event);
void deleteSchedulesByRange(Event event, String range);
void deleteSchedulesByTime(Event event, String time);
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,51 @@ public void deleteEvent(Event e) {
.where(event.eq(e))
.execute();
}

/**
* 특정 범위에 해당하는 스케줄 삭제 메서드.
*
* 이벤트와 연결된 특정 범위(DATE 또는 DAY)에 해당하는 모든 스케줄 및 관련 데이터를 삭제합니다.
* 삭제 순서는 외래 키 제약 조건을 고려하여,
* Selection → Schedule 순으로 진행됩니다.
*
* @param event 이벤트 객체
* @param range 삭제할 범위 (DATE 또는 DAY)
*/
@Override
public void deleteSchedulesByRange(Event event, String range) {
queryFactory.delete(selection)
.where(selection.schedule.event.eq(event)
.and(selection.schedule.date.eq(range)
.or(selection.schedule.day.eq(range))))
.execute();

queryFactory.delete(schedule)
.where(schedule.event.eq(event)
.and(schedule.date.eq(range).or(schedule.day.eq(range))))
.execute();
}

/**
* 특정 시간에 해당하는 스케줄 삭제 메서드.
*
* 이벤트와 연결된 특정 시간(HH:mm 형식)에 해당하는 모든 스케줄 및 관련 데이터를 삭제합니다.
* 삭제 순서는 외래 키 제약 조건을 고려하여,
* Selection → Schedule 순으로 진행됩니다.
*
* @param event 이벤트 객체
* @param time 삭제할 시간 (HH:mm 형식)
*/
@Override
public void deleteSchedulesByTime(Event event, String time) {
queryFactory.delete(selection)
.where(selection.schedule.event.eq(event)
.and(selection.schedule.time.eq(time)))
.execute();

queryFactory.delete(schedule)
.where(schedule.event.eq(event)
.and(schedule.time.eq(time)))
.execute();
}
}
149 changes: 138 additions & 11 deletions src/main/java/side/onetime/service/EventService.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import side.onetime.domain.enums.Category;
import side.onetime.domain.enums.EventStatus;
import side.onetime.dto.event.request.CreateEventRequest;
import side.onetime.dto.event.request.ModifyUserCreatedEventTitleRequest;
import side.onetime.dto.event.request.ModifyUserCreatedEventRequest;
import side.onetime.dto.event.response.*;
import side.onetime.exception.CustomException;
import side.onetime.exception.status.EventErrorStatus;
Expand All @@ -23,7 +23,6 @@
import side.onetime.util.QrUtil;
import side.onetime.util.S3Util;

import java.time.LocalTime;
import java.util.*;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -142,13 +141,13 @@ private String generateAndUploadQrCode(UUID eventId) {
*/
@Transactional
protected void createAndSaveDateSchedules(Event event, List<String> ranges, String startTime, String endTime) {
List<LocalTime> timeSets = DateUtil.createTimeSets(startTime, endTime);
List<String> timeSets = DateUtil.createTimeSets(startTime, endTime);
List<Schedule> schedules = ranges.stream()
.flatMap(range -> timeSets.stream()
.map(time -> Schedule.builder()
.event(event)
.date(range)
.time(String.valueOf(time))
.time(time)
.build()))
.collect(Collectors.toList());
scheduleRepository.saveAll(schedules);
Expand All @@ -165,13 +164,13 @@ protected void createAndSaveDateSchedules(Event event, List<String> ranges, Stri
*/
@Transactional
protected void createAndSaveDaySchedules(Event event, List<String> ranges, String startTime, String endTime) {
List<LocalTime> timeSets = DateUtil.createTimeSets(startTime, endTime);
List<String> timeSets = DateUtil.createTimeSets(startTime, endTime);
List<Schedule> schedules = ranges.stream()
.flatMap(range -> timeSets.stream()
.map(time -> Schedule.builder()
.event(event)
.day(range)
.time(String.valueOf(time))
.time(time)
.build()))
.collect(Collectors.toList());
scheduleRepository.saveAll(schedules);
Expand Down Expand Up @@ -451,17 +450,145 @@ public void removeUserCreatedEvent(String authorizationHeader, String eventId) {
}

/**
* 유저가 생성한 이벤트 제목 수정 메서드.
* 인증된 유저가 생성한 특정 이벤트의 제목을 수정합니다.
* 유저가 생성한 이벤트 수정 메서드.
* 인증된 유저가 생성한 특정 이벤트를 수정합니다.
*
* @param authorizationHeader 인증된 유저의 토큰
* @param eventId 수정할 이벤트의 ID
* @param modifyUserCreatedEventTitleRequest 새로운 제목 데이터
* @param modifyUserCreatedEventRequest 새로운 이벤트 데이터
*/
@Transactional
public void modifyUserCreatedEventTitle(String authorizationHeader, String eventId, ModifyUserCreatedEventTitleRequest modifyUserCreatedEventTitleRequest) {
public void modifyUserCreatedEvent(String authorizationHeader, String eventId, ModifyUserCreatedEventRequest modifyUserCreatedEventRequest) {
EventParticipation eventParticipation = verifyUserIsEventCreator(authorizationHeader, eventId);
eventParticipation.getEvent().updateTitle(modifyUserCreatedEventTitleRequest.title());
Event event = eventParticipation.getEvent();

updateEventTitle(event, modifyUserCreatedEventRequest.title());
updateEventRanges(event, event.getSchedules(), modifyUserCreatedEventRequest.ranges(), modifyUserCreatedEventRequest.startTime(), modifyUserCreatedEventRequest.endTime());
updateEventTimes(event, event.getSchedules(), modifyUserCreatedEventRequest.startTime(), modifyUserCreatedEventRequest.endTime());
}

/**
* 이벤트 제목 업데이트 메서드.
* 이벤트의 제목을 새로운 제목으로 업데이트합니다.
*
* @param event 이벤트 객체
* @param newTitle 새로운 제목
*/
protected void updateEventTitle(Event event, String newTitle) {
if (newTitle != null) {
event.updateTitle(newTitle);
}
}

/**
* 이벤트 범위 업데이트 메서드.
* 기존의 범위를 새로운 범위로 업데이트하며, 삭제 및 생성 대상 범위를 처리합니다.
*
* @param event 이벤트 객체
* @param schedules 기존 스케줄 목록
* @param newRanges 새로운 범위 리스트
* @param newStartTime 새로 설정할 시작 시간
* @param newEndTime 새로 설정할 종료 시간
*/
protected void updateEventRanges(Event event, List<Schedule> schedules, List<String> newRanges, String newStartTime, String newEndTime) {
Set<String> existRanges = event.getCategory() == Category.DATE
? schedules.stream().map(Schedule::getDate).filter(Objects::nonNull).collect(Collectors.toSet())
: schedules.stream().map(Schedule::getDay).filter(Objects::nonNull).collect(Collectors.toSet());

// 삭제 대상 처리
existRanges.stream()
.filter(range -> !newRanges.contains(range))
.forEach(range -> eventRepository.deleteSchedulesByRange(event, range));

// 생성 대상 처리
List<String> rangesToCreate = newRanges.stream()
.filter(range -> !existRanges.contains(range))
.collect(Collectors.toList());

if (!rangesToCreate.isEmpty()) {
if (event.getCategory() == Category.DATE) {
createAndSaveDateSchedules(event, rangesToCreate, newStartTime, newEndTime);
} else if (event.getCategory() == Category.DAY) {
createAndSaveDaySchedules(event, rangesToCreate, newStartTime, newEndTime);
}
}
}

/**
* 이벤트 시간 업데이트 메서드.
* 기존의 시간대를 새로운 시간대로 업데이트하며, 삭제 및 생성 대상 시간대를 처리합니다.
*
* @param event 이벤트 객체
* @param schedules 기존 스케줄 목록
* @param newStartTime 새로 설정할 시작 시간
* @param newEndTime 새로 설정할 종료 시간
*/
protected void updateEventTimes(Event event, List<Schedule> schedules, String newStartTime, String newEndTime) {
if (!event.getStartTime().equals(newStartTime) || !event.getEndTime().equals(newEndTime)) {
List<String> newTimeSets = DateUtil.createTimeSets(newStartTime, newEndTime);

Set<String> existTimes = schedules.stream()
.map(Schedule::getTime)
.filter(Objects::nonNull)
.collect(Collectors.toSet());

// 삭제 대상 시간 처리
existTimes.stream()
.filter(time -> !newTimeSets.contains(time))
.forEach(time -> eventRepository.deleteSchedulesByTime(event, time));

// 생성 대상 시간 처리
List<String> timesToCreate = newTimeSets.stream()
.filter(newTime -> !existTimes.contains(newTime))
.toList();

if (!timesToCreate.isEmpty()) {
if (event.getCategory() == Category.DATE) {
createSchedulesForTime(event, extractExistingRanges(schedules, event.getCategory()), timesToCreate, true);
} else if (event.getCategory() == Category.DAY) {
createSchedulesForTime(event, extractExistingRanges(schedules, event.getCategory()), timesToCreate, false);
}
}

event.updateStartTime(newStartTime);
event.updateEndTime(newEndTime);
}
}

/**
* 기존 범위 추출 메서드.
* 스케줄 목록에서 현재 존재하는 날짜 또는 요일 범위를 추출합니다.
*
* @param schedules 스케줄 목록
* @param category 이벤트의 카테고리 (날짜 또는 요일)
* @return 현재 존재하는 범위 집합
*/
private Set<String> extractExistingRanges(List<Schedule> schedules, Category category) {
return category == Category.DATE
? schedules.stream().map(Schedule::getDate).filter(Objects::nonNull).collect(Collectors.toSet())
: schedules.stream().map(Schedule::getDay).filter(Objects::nonNull).collect(Collectors.toSet());
}

/**
* 시간 기반 스케줄 생성 메서드.
* 시간대를 기반으로 새로운 스케줄을 생성하고 저장합니다.
*
* @param event 이벤트 객체
* @param ranges 범위 리스트 (날짜 또는 요일)
* @param timesToCreate 새로 생성할 시간 리스트
* @param isDateBased 날짜 기반 여부
*/
private void createSchedulesForTime(Event event, Set<String> ranges, List<String> timesToCreate, boolean isDateBased) {
List<Schedule> newSchedules = ranges.stream()
.flatMap(range -> timesToCreate.stream()
.map(time -> Schedule.builder()
.event(event)
.date(isDateBased ? range : null)
.day(!isDateBased ? range : null)
.time(time)
.build()))
.collect(Collectors.toList());
scheduleRepository.saveAll(newSchedules);
}

/**
Expand Down
8 changes: 4 additions & 4 deletions src/main/java/side/onetime/util/DateUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ public class DateUtil {
* @param end 종료 시간 (HH:mm 형식)
* @return 30분 간격의 시간 리스트
*/
public static List<LocalTime> createTimeSets(String start, String end) {
List<LocalTime> timeSets = new ArrayList<>();
public static List<String> createTimeSets(String start, String end) {
List<String> timeSets = new ArrayList<>();

boolean isEndTimeMidnight = end.equals("24:00");
if (isEndTimeMidnight) {
Expand All @@ -41,12 +41,12 @@ public static List<LocalTime> createTimeSets(String start, String end) {
LocalTime currentTime = startTime;

while (!currentTime.isAfter(endTime.minusMinutes(30))) {
timeSets.add(currentTime);
timeSets.add(String.valueOf(currentTime));
currentTime = currentTime.plusMinutes(30);
}

if (isEndTimeMidnight) {
timeSets.add(LocalTime.of(23, 30));
timeSets.add("23:30");
}

return timeSets;
Expand Down
Loading

0 comments on commit dd02779

Please sign in to comment.