Skip to content

Commit 7a646df

Browse files
authored
Merge pull request #217 from prgrms-aibe-devcourse/refector/v2-calendar-endpoints
refector: 캘린더 서비스/컨트롤러 V2 명명 규칙 적용
2 parents 82007f2 + 64d46b1 commit 7a646df

4 files changed

Lines changed: 771 additions & 0 deletions

File tree

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
package store.lastdance.controller.calendar;
2+
3+
import io.swagger.v3.oas.annotations.Operation;
4+
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
5+
import io.swagger.v3.oas.annotations.tags.Tag;
6+
import jakarta.validation.Valid;
7+
import lombok.RequiredArgsConstructor;
8+
import lombok.extern.slf4j.Slf4j;
9+
import org.springframework.data.domain.Pageable;
10+
import org.springframework.format.annotation.DateTimeFormat;
11+
import org.springframework.http.HttpStatus;
12+
import org.springframework.http.ResponseEntity;
13+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
14+
import org.springframework.web.bind.annotation.*;
15+
import store.lastdance.dto.calender.request.CreateCalendarRequestDTO;
16+
import store.lastdance.dto.calender.request.UpdateCalendarRequestDTO;
17+
import store.lastdance.dto.calender.response.CalendarResponseDTO;
18+
import store.lastdance.dto.response.ApiResponse;
19+
import store.lastdance.exception.CustomException;
20+
import store.lastdance.exception.ErrorCode;
21+
import store.lastdance.security.oauth.CustomOAuth2User;
22+
import store.lastdance.service.calendar.CalendarV2Service;
23+
24+
import java.time.LocalDateTime;
25+
import java.util.List;
26+
import java.util.UUID;
27+
28+
@Tag(name = "Calender", description = "캘린더 관리 API")
29+
@RestController
30+
@RequestMapping("/api/v2/calendars")
31+
@RequiredArgsConstructor
32+
@Slf4j
33+
public class CalendarV2Controller {
34+
35+
private final CalendarV2Service calendarService;
36+
37+
@Operation(
38+
summary = "캘린더 생성",
39+
description = "새로운 일정을 생성합니다.",
40+
security = @SecurityRequirement(name = "bearerAuth")
41+
)
42+
@PostMapping
43+
public ResponseEntity<ApiResponse<CalendarResponseDTO>> createCalendar(
44+
@Valid @RequestBody CreateCalendarRequestDTO request,
45+
@RequestParam(required = false) UUID groupId,
46+
@AuthenticationPrincipal CustomOAuth2User user) {
47+
48+
try {
49+
CalendarResponseDTO calendar = calendarService.createCalendar(request, user.getUserId(), groupId);
50+
51+
return ResponseEntity.status(HttpStatus.CREATED)
52+
.body(ApiResponse.success(calendar));
53+
54+
} catch (Exception e) {
55+
log.error("일정 생성 실패 - 사용자: {}, 에러: {}", user.getUserId(), e.getMessage(), e);
56+
throw new CustomException(ErrorCode.CALENDAR_CREATE_FAILED);
57+
}
58+
}
59+
60+
@Operation(
61+
summary = "내 일정 목록 조회",
62+
description = "사용자의 모든 일정을 조회합니다. (필터링 가능)",
63+
security = @SecurityRequirement(name = "bearerAuth")
64+
)
65+
@GetMapping("/me")
66+
public ResponseEntity<ApiResponse<List<CalendarResponseDTO>>> getMyCalendars(
67+
@RequestParam(required = false, defaultValue = "MONTHLY") String viewType,
68+
@RequestParam(required = false)
69+
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
70+
LocalDateTime dateTime,
71+
@RequestParam(required = false) String type,
72+
@RequestParam(required = false) String category,
73+
@RequestParam(required = false) UUID groupId,
74+
@AuthenticationPrincipal CustomOAuth2User user,
75+
Pageable pageable) {
76+
77+
try {
78+
List<CalendarResponseDTO> responses = calendarService.getCalendarsByUser(
79+
user.getUserId(), viewType, dateTime, type, category, groupId, pageable);
80+
81+
return ResponseEntity.ok(ApiResponse.success(responses));
82+
} catch (Exception e) {
83+
log.error("일정 조회 실패 - 사용자: {}, 에러: {}", user.getUserId(), e.getMessage());
84+
throw new CustomException(ErrorCode.CALENDAR_FOUND_FAILED);
85+
}
86+
}
87+
88+
@Operation(
89+
summary = "특정 일정 상세 조회",
90+
description = "특정 일정의 상세 정보를 조회합니다.",
91+
security = @SecurityRequirement(name = "bearerAuth")
92+
)
93+
@GetMapping("/{calendarId}")
94+
public ResponseEntity<ApiResponse<CalendarResponseDTO>> getCalendar(
95+
@PathVariable Long calendarId,
96+
@AuthenticationPrincipal CustomOAuth2User user) {
97+
98+
try {
99+
CalendarResponseDTO calendar = calendarService.getCalendarById(calendarId, user.getUserId());
100+
101+
return ResponseEntity.ok(ApiResponse.success(calendar));
102+
103+
} catch (CustomException e) {
104+
log.warn("일정 조회 실패 - 권한 없음: 사용자 {}, 일정 ID: {}", user.getUserId(), calendarId);
105+
throw new CustomException(ErrorCode.CALENDAR_ACCESS_DENIED);
106+
} catch (Exception e) {
107+
log.error("일정 조회 실패 - 일정 ID: {}, 에러: {}", calendarId, e.getMessage());
108+
throw new CustomException(ErrorCode.CALENDAR_FOUND_FAILED);
109+
}
110+
}
111+
112+
@Operation(
113+
summary = "일정 수정",
114+
description = "기존 일정의 정보를 수정합니다.",
115+
security = @SecurityRequirement(name = "bearerAuth")
116+
)
117+
@PatchMapping("/{calendarId}")
118+
public ResponseEntity<ApiResponse<CalendarResponseDTO>> updateCalendar(
119+
@PathVariable Long calendarId,
120+
@Valid @RequestBody UpdateCalendarRequestDTO request,
121+
@AuthenticationPrincipal CustomOAuth2User user) {
122+
123+
try {
124+
CalendarResponseDTO calendar = calendarService.updateCalendar(calendarId, request, user.getUserId());
125+
126+
return ResponseEntity.ok(ApiResponse.success(calendar));
127+
128+
} catch (CustomException e) {
129+
log.warn("일정 수정 실패 - 권한 없음: 사용자 {}, 일정 ID: {}", user.getUserId(), calendarId);
130+
throw new CustomException(ErrorCode.CALENDAR_ACCESS_DENIED);
131+
132+
} catch (Exception e) {
133+
log.error("일정 수정 실패 - 일정 ID: {}, 에러: {}", calendarId, e.getMessage());
134+
throw new CustomException(ErrorCode.CALENDAR_UPDATE_FAILED);
135+
}
136+
}
137+
138+
@Operation(
139+
summary = "일정 삭제",
140+
description = "기존 일정을 삭제합니다.",
141+
security = @SecurityRequirement(name = "bearerAuth")
142+
)
143+
@DeleteMapping("/{calendarId}")
144+
public ResponseEntity<ApiResponse<Void>> deleteCalendar(
145+
@PathVariable Long calendarId,
146+
@AuthenticationPrincipal CustomOAuth2User user) {
147+
148+
try {
149+
calendarService.deleteCalendar(calendarId, user.getUserId());
150+
151+
return ResponseEntity.ok(ApiResponse.success());
152+
153+
} catch (CustomException e) {
154+
log.warn("일정 삭제 실패 - 권한 없음: 사용자 {}, 일정 ID: {}",
155+
user.getUserId(), calendarId);
156+
throw new CustomException(ErrorCode.CALENDAR_ACCESS_DENIED);
157+
158+
} catch (Exception e) {
159+
log.error("일정 삭제 실패 - 일정 ID: {}, 에러: {}", calendarId, e.getMessage());
160+
throw new CustomException(ErrorCode.CALENDAR_DELETE_FAILED);
161+
}
162+
}
163+
164+
@Operation(
165+
summary = "그룹 일정 목록 조회",
166+
description = "특정 그룹의 모든 일정을 조회합니다. (필터링 가능)",
167+
security = @SecurityRequirement(name = "bearerAuth")
168+
)
169+
@GetMapping("/groups/{groupId}")
170+
public ResponseEntity<ApiResponse<List<CalendarResponseDTO>>> getGroupCalendars(
171+
@PathVariable UUID groupId,
172+
@RequestParam(required = false, defaultValue = "MONTHLY") String viewType,
173+
@RequestParam(required = false)
174+
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
175+
LocalDateTime dateTime,
176+
@RequestParam(required = false) String type,
177+
@RequestParam(required = false) String category,
178+
@AuthenticationPrincipal CustomOAuth2User user,
179+
Pageable pageable) {
180+
181+
try {
182+
if (!calendarService.isGroupMember(groupId, user.getUserId())) {
183+
throw new CustomException(ErrorCode.CALENDAR_ACCESS_DENIED);
184+
}
185+
186+
List<CalendarResponseDTO> responses = calendarService.getCalendarsByUser(
187+
user.getUserId(), viewType, dateTime, type, category, groupId, pageable);
188+
189+
return ResponseEntity.ok(ApiResponse.success(responses));
190+
191+
} catch (Exception e) {
192+
log.error("그룹 일정 조회 실패 - 그룹 ID: {}, 에러: {}", groupId, e.getMessage());
193+
throw new CustomException(ErrorCode.CALENDAR_FOUND_FAILED);
194+
}
195+
}
196+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package store.lastdance.converter.calendar;
2+
3+
import org.springframework.stereotype.Component;
4+
import store.lastdance.domain.calendar.Calendar;
5+
import store.lastdance.domain.calendar.CalendarType;
6+
import store.lastdance.domain.group.Group;
7+
import store.lastdance.domain.user.User;
8+
import store.lastdance.dto.calender.request.CreateCalendarRequestDTO;
9+
import store.lastdance.dto.calender.response.CalendarResponseDTO;
10+
11+
import java.time.Duration;
12+
import java.time.LocalDateTime;
13+
14+
@Component
15+
public class CalendarConverter {
16+
public Calendar toEntity(CreateCalendarRequestDTO request, User user, Group group, CalendarType type) {
17+
return Calendar.builder()
18+
.title(request.getTitle())
19+
.description(request.getDescription())
20+
.startDate(request.getStartDate())
21+
.endDate(request.getEndDate())
22+
.isAllDay(request.getIsAllDay())
23+
.type(type)
24+
.category(request.getCategory())
25+
.group(group)
26+
.user(user)
27+
.repeatType(request.getRepeatType())
28+
.repeatEndDate(request.getRepeatEndDate())
29+
.build();
30+
}
31+
32+
public Calendar toEntity(Calendar base, LocalDateTime newStartDate, Duration duration) {
33+
return Calendar.builder()
34+
.title(base.getTitle())
35+
.description(base.getDescription())
36+
.startDate(newStartDate)
37+
.endDate(newStartDate.plus(duration))
38+
.isAllDay(base.getIsAllDay())
39+
.type(base.getType())
40+
.category(base.getCategory())
41+
.group(base.getGroup())
42+
.user(base.getUser())
43+
.repeatType(base.getRepeatType())
44+
.repeatEndDate(base.getRepeatEndDate())
45+
.build();
46+
}
47+
48+
public CalendarResponseDTO toDto(Calendar calendar, User user, Group group, String groupName) {
49+
return CalendarResponseDTO.builder()
50+
.calendarId(calendar.getCalendarId())
51+
.title(calendar.getTitle())
52+
.description(calendar.getDescription())
53+
.startDate(calendar.getStartDate())
54+
.endDate(calendar.getEndDate())
55+
.isAllDay(calendar.getIsAllDay())
56+
.type(calendar.getType())
57+
.category(calendar.getCategory())
58+
.groupId(calendar.getGroup() != null ? calendar.getGroup().getGroupId() : null)
59+
.groupName(groupName)
60+
.userId(calendar.getUser() != null ? calendar.getUser().getUserId() : null)
61+
.repeatType(calendar.getRepeatType())
62+
.repeatEndDate(calendar.getRepeatEndDate())
63+
.createdAt(calendar.getCreatedAt())
64+
.build();
65+
}
66+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package store.lastdance.service.calendar;
2+
3+
import org.springframework.data.domain.Pageable;
4+
import store.lastdance.dto.calender.request.CreateCalendarRequestDTO;
5+
import store.lastdance.dto.calender.request.UpdateCalendarRequestDTO;
6+
import store.lastdance.dto.calender.response.CalendarResponseDTO;
7+
8+
import java.time.LocalDateTime;
9+
import java.util.List;
10+
import java.util.UUID;
11+
12+
public interface CalendarV2Service {
13+
14+
CalendarResponseDTO createCalendar(CreateCalendarRequestDTO request, UUID userId, UUID groupId);
15+
16+
List<CalendarResponseDTO> getCalendarsByUser(UUID userId, String viewType, LocalDateTime dateTime, String type, String category, UUID groupId, Pageable pageable);
17+
18+
CalendarResponseDTO getCalendarById(Long calendarId, UUID userId);
19+
20+
CalendarResponseDTO updateCalendar(Long calendarId, UpdateCalendarRequestDTO request, UUID userId);
21+
22+
void deleteCalendar(Long calendarId, UUID userId);
23+
24+
boolean isGroupMember(UUID groupId, UUID userId);
25+
}

0 commit comments

Comments
 (0)