From 24fbcc119fa86e91faea9a0e0ace2f87889515af Mon Sep 17 00:00:00 2001 From: jiwonniddaaa Date: Mon, 25 May 2026 21:01:16 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat/#20:=20=EA=B3=B5=EC=9C=A0=20=EC=BA=98?= =?UTF-8?q?=EB=A6=B0=EB=8D=94=20=EB=B2=94=EC=9C=84=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit + cal CRUD 테스트 완료 --- .../cal/controller/ScheduleController.kt | 14 ++++++++ .../cal/dto/CalendarSchedulesResponse.kt | 18 ++++++++++ .../cal/repository/ScheduleRepository.kt | 15 +++++++++ .../beat_it/cal/service/ScheduleService.kt | 33 +++++++++++++++++++ 4 files changed, 80 insertions(+) create mode 100644 src/main/kotlin/com/beat_it/cal/dto/CalendarSchedulesResponse.kt diff --git a/src/main/kotlin/com/beat_it/cal/controller/ScheduleController.kt b/src/main/kotlin/com/beat_it/cal/controller/ScheduleController.kt index e3c9812..036c411 100644 --- a/src/main/kotlin/com/beat_it/cal/controller/ScheduleController.kt +++ b/src/main/kotlin/com/beat_it/cal/controller/ScheduleController.kt @@ -1,5 +1,6 @@ package com.beat_it.cal.controller +import com.beat_it.cal.dto.CalendarSchedulesResponse import com.beat_it.cal.dto.ScheduleCreateRequest import com.beat_it.cal.dto.ScheduleCreateResponse import com.beat_it.cal.dto.ScheduleDetailResponse @@ -63,4 +64,17 @@ class ScheduleController( .body(BasicResponse.success(responseData, HttpStatus.OK, "일정 상세 조회에 성공했습니다.") ) } + + @GetMapping + fun getCalendarSchedules( + @RequestHeader("X-USER-ID") userId: Long, + @RequestParam year: Int, + @RequestParam month: Int + ): ResponseEntity> { + val responseData = scheduleService.getCalendarSchedules(userId, year, month) + return ResponseEntity + .status(HttpStatus.OK) + .body(BasicResponse.success(responseData, HttpStatus.OK, "공유 캘린더 범위 조회에 성공했습니다.")) + } + } \ No newline at end of file diff --git a/src/main/kotlin/com/beat_it/cal/dto/CalendarSchedulesResponse.kt b/src/main/kotlin/com/beat_it/cal/dto/CalendarSchedulesResponse.kt new file mode 100644 index 0000000..f82d4b4 --- /dev/null +++ b/src/main/kotlin/com/beat_it/cal/dto/CalendarSchedulesResponse.kt @@ -0,0 +1,18 @@ +package com.beat_it.cal.dto + +import com.fasterxml.jackson.annotation.JsonProperty +import java.time.OffsetDateTime + +data class CalendarSchedulesResponse( + val items: List +) + +data class CalendarSchedule( + @JsonProperty("schedule_id") + val scheduleId: Long, + val title: String, + @JsonProperty("starts_at") + val startsAt: OffsetDateTime, + @JsonProperty("ends_at") + val endsAt: OffsetDateTime +) \ No newline at end of file diff --git a/src/main/kotlin/com/beat_it/cal/repository/ScheduleRepository.kt b/src/main/kotlin/com/beat_it/cal/repository/ScheduleRepository.kt index 4d853c0..80b87f5 100644 --- a/src/main/kotlin/com/beat_it/cal/repository/ScheduleRepository.kt +++ b/src/main/kotlin/com/beat_it/cal/repository/ScheduleRepository.kt @@ -2,8 +2,23 @@ package com.beat_it.cal.repository import com.beat_it.cal.entity.Schedule import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Query +import org.springframework.data.repository.query.Param import org.springframework.stereotype.Repository +import java.time.OffsetDateTime @Repository interface ScheduleRepository : JpaRepository { + @Query(""" + SELECT s FROM Schedule s + WHERE s.userId = :userId + AND s.startsAt <= :endDateTime + AND s.endsAt >= :startDateTime + ORDER BY s.startsAt ASC + """) + fun findByUserIdAndMonthRange( + @Param("userId") userId: Long, + @Param("startDateTime") startDateTime: OffsetDateTime, + @Param("endDateTime") endDateTime: OffsetDateTime + ): List } \ No newline at end of file diff --git a/src/main/kotlin/com/beat_it/cal/service/ScheduleService.kt b/src/main/kotlin/com/beat_it/cal/service/ScheduleService.kt index 3370855..4d28a7b 100644 --- a/src/main/kotlin/com/beat_it/cal/service/ScheduleService.kt +++ b/src/main/kotlin/com/beat_it/cal/service/ScheduleService.kt @@ -1,5 +1,7 @@ package com.beat_it.cal.service +import com.beat_it.cal.dto.CalendarSchedule +import com.beat_it.cal.dto.CalendarSchedulesResponse import com.beat_it.cal.dto.ParticipantResponse import com.beat_it.cal.dto.ScheduleCreateRequest import com.beat_it.cal.dto.ScheduleCreateResponse @@ -11,7 +13,10 @@ import com.beat_it.global.error.BusinessException import com.beat_it.global.error.ErrorCode import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional +import java.time.LocalDate +import java.time.LocalTime import java.time.OffsetDateTime +import java.time.ZoneOffset @Service class ScheduleService( @@ -136,6 +141,34 @@ class ScheduleService( ) } + @Transactional(readOnly = true) + fun getCalendarSchedules(userId: Long, year: Int, month: Int): CalendarSchedulesResponse { + // 1. 해당 월의 시작일(1일)과 종료일(말일) 계산 + val startLocalDate = LocalDate.of(year, month, 1) + val endLocalDate = startLocalDate.withDayOfMonth(startLocalDate.lengthOfMonth()) + + // 2. 쿼리에 사용할 범위 지정 (그 달의 1일 00:00:00 ~ 말일 23:59:59.999) + // 프로젝트 타임존 컨벤션에 맞게 ZoneOffset을 조정하세요. (여기서는 KST인 +09:00 가정) + val zoneOffset = ZoneOffset.ofHours(9) + val startDateTime = OffsetDateTime.of(startLocalDate, LocalTime.MIN, zoneOffset) + val endDateTime = OffsetDateTime.of(endLocalDate, LocalTime.MAX, zoneOffset) + + // 3. Repository를 통해 조건에 맞는 일정 조회 (기간 겹침 조건 적용) + val schedules = scheduleRepository.findByUserIdAndMonthRange(userId, startDateTime, endDateTime) + + // 4. Entity 리스트를 DTO 리스트로 매핑하여 반환 + val calendarSchedules = schedules.map { schedule -> + CalendarSchedule( + scheduleId = schedule.scheduleId ?: throw IllegalStateException("일정 ID가 존재하지 않습니다."), + title = schedule.title, + startsAt = schedule.startsAt, + endsAt = schedule.endsAt + ) + } + + return CalendarSchedulesResponse(items = calendarSchedules) + } + private fun validateScheduleCommon(title: String?, startsAt: OffsetDateTime?, endsAt: OffsetDateTime?) { if (title.isNullOrBlank()) { throw BusinessException(ErrorCode.CALENDAR_TITLE_REQUIRED) From c312ec696a4bb232f100ab5fc895223ee2444007 Mon Sep 17 00:00:00 2001 From: jiwonniddaaa Date: Tue, 26 May 2026 00:18:39 +0900 Subject: [PATCH 2/4] =?UTF-8?q?feat/#20:=20=EC=84=A0=ED=83=9D=20=EB=82=A0?= =?UTF-8?q?=EC=A7=9C=20=EC=9D=BC=EC=A0=95=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cal/controller/ScheduleController.kt | 16 +++++++++- .../beat_it/cal/dto/DateScheduleResponse.kt | 26 ++++++++++++++++ .../cal/repository/ScheduleRepository.kt | 13 ++++++++ .../beat_it/cal/service/ScheduleService.kt | 31 ++++++++++++++++--- 4 files changed, 80 insertions(+), 6 deletions(-) create mode 100644 src/main/kotlin/com/beat_it/cal/dto/DateScheduleResponse.kt diff --git a/src/main/kotlin/com/beat_it/cal/controller/ScheduleController.kt b/src/main/kotlin/com/beat_it/cal/controller/ScheduleController.kt index 036c411..70b8ac5 100644 --- a/src/main/kotlin/com/beat_it/cal/controller/ScheduleController.kt +++ b/src/main/kotlin/com/beat_it/cal/controller/ScheduleController.kt @@ -1,6 +1,7 @@ package com.beat_it.cal.controller import com.beat_it.cal.dto.CalendarSchedulesResponse +import com.beat_it.cal.dto.DateSchedulesResponse import com.beat_it.cal.dto.ScheduleCreateRequest import com.beat_it.cal.dto.ScheduleCreateResponse import com.beat_it.cal.dto.ScheduleDetailResponse @@ -65,7 +66,7 @@ class ScheduleController( ) } - @GetMapping + @GetMapping("/month") fun getCalendarSchedules( @RequestHeader("X-USER-ID") userId: Long, @RequestParam year: Int, @@ -77,4 +78,17 @@ class ScheduleController( .body(BasicResponse.success(responseData, HttpStatus.OK, "공유 캘린더 범위 조회에 성공했습니다.")) } + @GetMapping("/date") + fun getDateSchedules( + @RequestHeader("X-USER-ID") userId: Long, + @RequestParam year: Int, + @RequestParam month: Int, + @RequestParam date: Int + ): ResponseEntity> { // 반환 DTO 타입을 명확히 컴파일 시점에 지정 가능! + val responseData = scheduleService.getDateSchedules(userId, year, month, date) + return ResponseEntity + .status(HttpStatus.OK) + .body(BasicResponse.success(responseData, HttpStatus.OK, "선택 날짜 일정 조회에 성공했습니다.")) + } + } \ No newline at end of file diff --git a/src/main/kotlin/com/beat_it/cal/dto/DateScheduleResponse.kt b/src/main/kotlin/com/beat_it/cal/dto/DateScheduleResponse.kt new file mode 100644 index 0000000..51c0d1d --- /dev/null +++ b/src/main/kotlin/com/beat_it/cal/dto/DateScheduleResponse.kt @@ -0,0 +1,26 @@ +package com.beat_it.cal.dto + +import com.fasterxml.jackson.annotation.JsonProperty +import java.time.OffsetDateTime + +data class DateSchedulesResponse( + val items: List +) + +data class DateSchedule( + @JsonProperty("schedule_id") + val scheduleId: Long, + + val title: String, + + val content: String, + + @JsonProperty("starts_at") + val startsAt: OffsetDateTime, + + @JsonProperty("ends_at") + val endsAt: OffsetDateTime, + + @JsonProperty("location_id") + val locationId: Long? +) \ No newline at end of file diff --git a/src/main/kotlin/com/beat_it/cal/repository/ScheduleRepository.kt b/src/main/kotlin/com/beat_it/cal/repository/ScheduleRepository.kt index 80b87f5..c3cf398 100644 --- a/src/main/kotlin/com/beat_it/cal/repository/ScheduleRepository.kt +++ b/src/main/kotlin/com/beat_it/cal/repository/ScheduleRepository.kt @@ -21,4 +21,17 @@ interface ScheduleRepository : JpaRepository { @Param("startDateTime") startDateTime: OffsetDateTime, @Param("endDateTime") endDateTime: OffsetDateTime ): List + + @Query(""" + SELECT s FROM Schedule s + WHERE s.userId = :userId + AND s.startsAt <= :endDateTime + AND s.endsAt >= :startDateTime + ORDER BY s.startsAt ASC + """) + fun findByUserIdAndDailyRange( + @Param("userId") userId: Long, + @Param("startDateTime") startDateTime: OffsetDateTime, + @Param("endDateTime") endDateTime: OffsetDateTime + ): List } \ No newline at end of file diff --git a/src/main/kotlin/com/beat_it/cal/service/ScheduleService.kt b/src/main/kotlin/com/beat_it/cal/service/ScheduleService.kt index 4d28a7b..5706fc2 100644 --- a/src/main/kotlin/com/beat_it/cal/service/ScheduleService.kt +++ b/src/main/kotlin/com/beat_it/cal/service/ScheduleService.kt @@ -2,6 +2,8 @@ package com.beat_it.cal.service import com.beat_it.cal.dto.CalendarSchedule import com.beat_it.cal.dto.CalendarSchedulesResponse +import com.beat_it.cal.dto.DateSchedule +import com.beat_it.cal.dto.DateSchedulesResponse import com.beat_it.cal.dto.ParticipantResponse import com.beat_it.cal.dto.ScheduleCreateRequest import com.beat_it.cal.dto.ScheduleCreateResponse @@ -143,20 +145,15 @@ class ScheduleService( @Transactional(readOnly = true) fun getCalendarSchedules(userId: Long, year: Int, month: Int): CalendarSchedulesResponse { - // 1. 해당 월의 시작일(1일)과 종료일(말일) 계산 val startLocalDate = LocalDate.of(year, month, 1) val endLocalDate = startLocalDate.withDayOfMonth(startLocalDate.lengthOfMonth()) - // 2. 쿼리에 사용할 범위 지정 (그 달의 1일 00:00:00 ~ 말일 23:59:59.999) - // 프로젝트 타임존 컨벤션에 맞게 ZoneOffset을 조정하세요. (여기서는 KST인 +09:00 가정) val zoneOffset = ZoneOffset.ofHours(9) val startDateTime = OffsetDateTime.of(startLocalDate, LocalTime.MIN, zoneOffset) val endDateTime = OffsetDateTime.of(endLocalDate, LocalTime.MAX, zoneOffset) - // 3. Repository를 통해 조건에 맞는 일정 조회 (기간 겹침 조건 적용) val schedules = scheduleRepository.findByUserIdAndMonthRange(userId, startDateTime, endDateTime) - // 4. Entity 리스트를 DTO 리스트로 매핑하여 반환 val calendarSchedules = schedules.map { schedule -> CalendarSchedule( scheduleId = schedule.scheduleId ?: throw IllegalStateException("일정 ID가 존재하지 않습니다."), @@ -169,6 +166,30 @@ class ScheduleService( return CalendarSchedulesResponse(items = calendarSchedules) } + @Transactional(readOnly = true) + fun getDateSchedules(userId: Long, year: Int, month: Int, date: Int): DateSchedulesResponse { + val targetLocalDate = LocalDate.of(year, month, date) + + val zoneOffset = ZoneOffset.ofHours(9) + val startDateTime = OffsetDateTime.of(targetLocalDate, LocalTime.MIN, zoneOffset) + val endDateTime = OffsetDateTime.of(targetLocalDate, LocalTime.MAX, zoneOffset) + + val schedules = scheduleRepository.findByUserIdAndDailyRange(userId, startDateTime, endDateTime) + + val dateSchedules = schedules.map { schedule -> + DateSchedule( + scheduleId = schedule.scheduleId ?: throw IllegalStateException("일정 ID가 존재하지 않습니다."), + title = schedule.title, + content = schedule.content ?: "", + startsAt = schedule.startsAt, + endsAt = schedule.endsAt, + locationId = schedule.locationId + ) + } + + return DateSchedulesResponse(items = dateSchedules) + } + private fun validateScheduleCommon(title: String?, startsAt: OffsetDateTime?, endsAt: OffsetDateTime?) { if (title.isNullOrBlank()) { throw BusinessException(ErrorCode.CALENDAR_TITLE_REQUIRED) From e84cf8ed070aa9383b478c7eedea3331cb5f8784 Mon Sep 17 00:00:00 2001 From: jiwonniddaaa Date: Tue, 26 May 2026 00:42:31 +0900 Subject: [PATCH 3/4] =?UTF-8?q?feat/#20:=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=97=90=EB=9F=AC=20=ED=95=B8=EB=93=A4=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../beat_it/cal/service/ScheduleService.kt | 33 ++++++++++++++++++- .../com/beat_it/global/error/ErrorCode.kt | 4 +++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/beat_it/cal/service/ScheduleService.kt b/src/main/kotlin/com/beat_it/cal/service/ScheduleService.kt index 5706fc2..20283ab 100644 --- a/src/main/kotlin/com/beat_it/cal/service/ScheduleService.kt +++ b/src/main/kotlin/com/beat_it/cal/service/ScheduleService.kt @@ -145,6 +145,7 @@ class ScheduleService( @Transactional(readOnly = true) fun getCalendarSchedules(userId: Long, year: Int, month: Int): CalendarSchedulesResponse { + validateYearAndMonth(year, month) val startLocalDate = LocalDate.of(year, month, 1) val endLocalDate = startLocalDate.withDayOfMonth(startLocalDate.lengthOfMonth()) @@ -168,6 +169,7 @@ class ScheduleService( @Transactional(readOnly = true) fun getDateSchedules(userId: Long, year: Int, month: Int, date: Int): DateSchedulesResponse { + validateYearMonthAndDate(year, month, date) val targetLocalDate = LocalDate.of(year, month, date) val zoneOffset = ZoneOffset.ofHours(9) @@ -175,7 +177,7 @@ class ScheduleService( val endDateTime = OffsetDateTime.of(targetLocalDate, LocalTime.MAX, zoneOffset) val schedules = scheduleRepository.findByUserIdAndDailyRange(userId, startDateTime, endDateTime) - + val dateSchedules = schedules.map { schedule -> DateSchedule( scheduleId = schedule.scheduleId ?: throw IllegalStateException("일정 ID가 존재하지 않습니다."), @@ -237,4 +239,33 @@ class ScheduleService( throw BusinessException(ErrorCode.CALENDAR_TEAM_MISMATCH) } } + + private fun validateYearAndMonth(year: Int, month: Int) { + if (year !in 1000..9999) { + throw BusinessException(ErrorCode.CALENDAR_INVALID_YEAR) + } + if (month !in 1..12) { + throw BusinessException(ErrorCode.CALENDAR_INVALID_MONTH) + } + } + + private fun validateYearMonthAndDate(year: Int, month: Int, date: Int) { + if (year !in 1000..9999) { + throw BusinessException(ErrorCode.CALENDAR_INVALID_YEAR) + } + + if (month !in 1..12) { + throw BusinessException(ErrorCode.CALENDAR_INVALID_MONTH) + } + + if (date !in 1..31) { + throw BusinessException(ErrorCode.CALENDAR_INVALID_DATE) + } + + try { + java.time.YearMonth.of(year, month).atDay(date) + } catch (e: java.time.DateTimeException) { + throw BusinessException(ErrorCode.CALENDAR_NON_EXISTENT_DATE) + } + } } \ No newline at end of file diff --git a/src/main/kotlin/com/beat_it/global/error/ErrorCode.kt b/src/main/kotlin/com/beat_it/global/error/ErrorCode.kt index 7ed8ae4..35e341d 100644 --- a/src/main/kotlin/com/beat_it/global/error/ErrorCode.kt +++ b/src/main/kotlin/com/beat_it/global/error/ErrorCode.kt @@ -35,6 +35,10 @@ enum class ErrorCode( CALENDAR_TITLE_REQUIRED(HttpStatus.BAD_REQUEST, "CALENDAR-007", "일정명은 필수입니다."), CALENDAR_START_TIME_REQUIRED(HttpStatus.BAD_REQUEST, "CALENDAR-008", "일정 시작 시각은 필수입니다."), CALENDAR_END_TIME_REQUIRED(HttpStatus.BAD_REQUEST, "CALENDAR-009", "일정 종료 시각은 필수입니다."), + CALENDAR_INVALID_YEAR(HttpStatus.BAD_REQUEST, "CALENDAR-010", "연도 값이 올바르지 않습니다."), + CALENDAR_INVALID_MONTH(HttpStatus.BAD_REQUEST, "CALENDAR-011", "월 값은 1~12 사이여야 합니다."), + CALENDAR_INVALID_DATE(HttpStatus.BAD_REQUEST, "CALENDAR-012", "일 값은 1~31 사이여야 합니다."), + CALENDAR_NON_EXISTENT_DATE(HttpStatus.BAD_REQUEST, "CALENDAR-013", "해당 연월에 존재하지 않는 날짜입니다."), // --- 장소 관련 에러 --- LOCATION_NOT_FOUND(HttpStatus.NOT_FOUND, "LOCATION-001", "장소를 찾을 수 없습니다."), From 951d897787e60f714ff3d8ec43e34eb9d00b51a6 Mon Sep 17 00:00:00 2001 From: jiwonniddaaa Date: Tue, 2 Jun 2026 16:09:46 +0900 Subject: [PATCH 4/4] =?UTF-8?q?fix:=20=EC=9D=BC=EC=A0=95=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EB=A6=AC=EB=B7=B0=20=EC=9A=94=EC=B2=AD=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - jwt 토큰 어노테이션 - 카멜 케이스 반환 - 커스텀 에러코드 반환 - 스웨거 순서 지정 --- .../cal/controller/ScheduleController.kt | 42 +++++++++++++------ .../cal/dto/CalendarSchedulesResponse.kt | 3 -- .../beat_it/cal/dto/DateScheduleResponse.kt | 9 ---- .../beat_it/cal/dto/ScheduleCreateRequest.kt | 9 ---- .../beat_it/cal/dto/ScheduleCreateResponse.kt | 8 ---- .../beat_it/cal/dto/ScheduleDetailResponse.kt | 20 ++++----- .../beat_it/cal/dto/ScheduledUpdateRequest.kt | 8 ---- .../beat_it/cal/service/ScheduleService.kt | 6 +-- 8 files changed, 43 insertions(+), 62 deletions(-) diff --git a/src/main/kotlin/com/beat_it/cal/controller/ScheduleController.kt b/src/main/kotlin/com/beat_it/cal/controller/ScheduleController.kt index 70b8ac5..3950687 100644 --- a/src/main/kotlin/com/beat_it/cal/controller/ScheduleController.kt +++ b/src/main/kotlin/com/beat_it/cal/controller/ScheduleController.kt @@ -7,11 +7,17 @@ import com.beat_it.cal.dto.ScheduleCreateResponse import com.beat_it.cal.dto.ScheduleDetailResponse import com.beat_it.cal.dto.ScheduleUpdateRequest import com.beat_it.cal.service.ScheduleService +import com.beat_it.global.error.BusinessException +import com.beat_it.global.error.ErrorCode import com.beat_it.global.response.BasicResponse +import io.swagger.v3.oas.annotations.tags.Tag import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity +import org.springframework.security.core.annotation.AuthenticationPrincipal +import org.springframework.security.core.userdetails.UserDetails import org.springframework.web.bind.annotation.* +@Tag(name = "4. CALENDAR API", description = "일정 관련 로직") @RestController @RequestMapping("/calendar") class ScheduleController( @@ -20,11 +26,12 @@ class ScheduleController( @PostMapping fun createSchedule( - @RequestHeader("X-USER-ID") userId: Long, + @AuthenticationPrincipal userDetails: UserDetails?, @RequestBody request: ScheduleCreateRequest ): ResponseEntity> { - val responseData = scheduleService.createSchedule(userId, request) + val currentUserId = userDetails?.username?.toLong()?: throw BusinessException(ErrorCode.UNAUTHORIZED) + val responseData = scheduleService.createSchedule(currentUserId, request) return ResponseEntity .status(HttpStatus.CREATED) @@ -33,11 +40,13 @@ class ScheduleController( @PatchMapping("/{scheduleId}") fun updateSchedule( - @RequestHeader("X-USER-ID") userId: Long, + @AuthenticationPrincipal userDetails: UserDetails?, @PathVariable scheduleId: Long, @RequestBody request: ScheduleUpdateRequest ): ResponseEntity> { - val responseData = scheduleService.updateSchedule(scheduleId, userId, request) + val currentUserId = userDetails?.username?.toLong() + ?: throw BusinessException(ErrorCode.UNAUTHORIZED) + val responseData = scheduleService.updateSchedule(scheduleId, currentUserId, request) return ResponseEntity .status(HttpStatus.OK) .body(BasicResponse.success(responseData, HttpStatus.OK, "일정이 수정되었습니다.")) @@ -45,10 +54,12 @@ class ScheduleController( @DeleteMapping("/{scheduleId}") fun deleteSchedule( - @RequestHeader("X-USER-ID") userId: Long, + @AuthenticationPrincipal userDetails: UserDetails?, @PathVariable scheduleId: Long ): ResponseEntity> { - scheduleService.deleteSchedule(scheduleId, userId) + val currentUserId = userDetails?.username?.toLong() + ?: throw BusinessException(ErrorCode.UNAUTHORIZED) + scheduleService.deleteSchedule(scheduleId, currentUserId) return ResponseEntity .status(HttpStatus.OK) .body(BasicResponse.success(HttpStatus.OK, "일정이 성공적으로 삭제되었습니다.") @@ -57,9 +68,12 @@ class ScheduleController( @GetMapping("/{scheduleId}") fun getScheduleDetail( + @AuthenticationPrincipal userDetails: UserDetails?, @PathVariable scheduleId: Long ): ResponseEntity> { - val responseData = scheduleService.getScheduleDetail(scheduleId) + val currentUserId = userDetails?.username?.toLong() + ?: throw BusinessException(ErrorCode.UNAUTHORIZED) + val responseData = scheduleService.getScheduleDetail(scheduleId, currentUserId) return ResponseEntity .status(HttpStatus.OK) .body(BasicResponse.success(responseData, HttpStatus.OK, "일정 상세 조회에 성공했습니다.") @@ -68,11 +82,13 @@ class ScheduleController( @GetMapping("/month") fun getCalendarSchedules( - @RequestHeader("X-USER-ID") userId: Long, + @AuthenticationPrincipal userDetails: UserDetails?, @RequestParam year: Int, @RequestParam month: Int ): ResponseEntity> { - val responseData = scheduleService.getCalendarSchedules(userId, year, month) + val currentUserId = userDetails?.username?.toLong() + ?: throw BusinessException(ErrorCode.UNAUTHORIZED) + val responseData = scheduleService.getCalendarSchedules(currentUserId, year, month) return ResponseEntity .status(HttpStatus.OK) .body(BasicResponse.success(responseData, HttpStatus.OK, "공유 캘린더 범위 조회에 성공했습니다.")) @@ -80,12 +96,14 @@ class ScheduleController( @GetMapping("/date") fun getDateSchedules( - @RequestHeader("X-USER-ID") userId: Long, + @AuthenticationPrincipal userDetails: UserDetails?, @RequestParam year: Int, @RequestParam month: Int, @RequestParam date: Int - ): ResponseEntity> { // 반환 DTO 타입을 명확히 컴파일 시점에 지정 가능! - val responseData = scheduleService.getDateSchedules(userId, year, month, date) + ): ResponseEntity> { + val currentUserId = userDetails?.username?.toLong() + ?: throw BusinessException(ErrorCode.UNAUTHORIZED) + val responseData = scheduleService.getDateSchedules(currentUserId, year, month, date) return ResponseEntity .status(HttpStatus.OK) .body(BasicResponse.success(responseData, HttpStatus.OK, "선택 날짜 일정 조회에 성공했습니다.")) diff --git a/src/main/kotlin/com/beat_it/cal/dto/CalendarSchedulesResponse.kt b/src/main/kotlin/com/beat_it/cal/dto/CalendarSchedulesResponse.kt index f82d4b4..b1da322 100644 --- a/src/main/kotlin/com/beat_it/cal/dto/CalendarSchedulesResponse.kt +++ b/src/main/kotlin/com/beat_it/cal/dto/CalendarSchedulesResponse.kt @@ -8,11 +8,8 @@ data class CalendarSchedulesResponse( ) data class CalendarSchedule( - @JsonProperty("schedule_id") val scheduleId: Long, val title: String, - @JsonProperty("starts_at") val startsAt: OffsetDateTime, - @JsonProperty("ends_at") val endsAt: OffsetDateTime ) \ No newline at end of file diff --git a/src/main/kotlin/com/beat_it/cal/dto/DateScheduleResponse.kt b/src/main/kotlin/com/beat_it/cal/dto/DateScheduleResponse.kt index 51c0d1d..743a2b4 100644 --- a/src/main/kotlin/com/beat_it/cal/dto/DateScheduleResponse.kt +++ b/src/main/kotlin/com/beat_it/cal/dto/DateScheduleResponse.kt @@ -8,19 +8,10 @@ data class DateSchedulesResponse( ) data class DateSchedule( - @JsonProperty("schedule_id") val scheduleId: Long, - val title: String, - val content: String, - - @JsonProperty("starts_at") val startsAt: OffsetDateTime, - - @JsonProperty("ends_at") val endsAt: OffsetDateTime, - - @JsonProperty("location_id") val locationId: Long? ) \ No newline at end of file diff --git a/src/main/kotlin/com/beat_it/cal/dto/ScheduleCreateRequest.kt b/src/main/kotlin/com/beat_it/cal/dto/ScheduleCreateRequest.kt index 421eff8..3edc1d5 100644 --- a/src/main/kotlin/com/beat_it/cal/dto/ScheduleCreateRequest.kt +++ b/src/main/kotlin/com/beat_it/cal/dto/ScheduleCreateRequest.kt @@ -4,19 +4,10 @@ import com.fasterxml.jackson.annotation.JsonProperty import java.time.OffsetDateTime data class ScheduleCreateRequest( - @JsonProperty("location_id") val locationId: Long?, - val title: String?, - val content: String?, - - @JsonProperty("starts_at") val startsAt: OffsetDateTime?, - - @JsonProperty("ends_at") val endsAt: OffsetDateTime?, - - @JsonProperty("participant_user_ids") val participantUserIds: List = emptyList() ) \ No newline at end of file diff --git a/src/main/kotlin/com/beat_it/cal/dto/ScheduleCreateResponse.kt b/src/main/kotlin/com/beat_it/cal/dto/ScheduleCreateResponse.kt index fff72e7..6ed23ed 100644 --- a/src/main/kotlin/com/beat_it/cal/dto/ScheduleCreateResponse.kt +++ b/src/main/kotlin/com/beat_it/cal/dto/ScheduleCreateResponse.kt @@ -4,17 +4,9 @@ import com.fasterxml.jackson.annotation.JsonProperty import java.time.OffsetDateTime data class ScheduleCreateResponse( - @JsonProperty("schedule_id") val scheduleId: Long, - val title: String, - - @JsonProperty("starts_at") val startsAt: OffsetDateTime, - - @JsonProperty("ends_at") val endsAt: OffsetDateTime, - - @JsonProperty("created_at") val createdAt: OffsetDateTime ) \ No newline at end of file diff --git a/src/main/kotlin/com/beat_it/cal/dto/ScheduleDetailResponse.kt b/src/main/kotlin/com/beat_it/cal/dto/ScheduleDetailResponse.kt index f7d8f99..0d80890 100644 --- a/src/main/kotlin/com/beat_it/cal/dto/ScheduleDetailResponse.kt +++ b/src/main/kotlin/com/beat_it/cal/dto/ScheduleDetailResponse.kt @@ -4,20 +4,20 @@ import com.fasterxml.jackson.annotation.JsonProperty import java.time.OffsetDateTime data class ScheduleDetailResponse( - @JsonProperty("schedule_id") val scheduleId: Long, - @JsonProperty("team_id") val teamId: Long, - @JsonProperty("user_id") val userId: Long, - @JsonProperty("location_id") val locationId: Long?, + val scheduleId: Long, + val teamId: Long, + val userId: Long, + val locationId: Long?, val title: String, val content: String?, - @JsonProperty("starts_at") val startsAt: OffsetDateTime, - @JsonProperty("ends_at") val endsAt: OffsetDateTime, - @JsonProperty("created_at") val createdAt: OffsetDateTime, - @JsonProperty("updated_at") val updatedAt: OffsetDateTime, + val startsAt: OffsetDateTime, + val endsAt: OffsetDateTime, + val createdAt: OffsetDateTime, + val updatedAt: OffsetDateTime, val participants: List ) data class ParticipantResponse( - @JsonProperty("schedule_participant_id") val scheduleParticipantId: Long, - @JsonProperty("user_id") val userId: Long + val scheduleParticipantId: Long, + val userId: Long ) \ No newline at end of file diff --git a/src/main/kotlin/com/beat_it/cal/dto/ScheduledUpdateRequest.kt b/src/main/kotlin/com/beat_it/cal/dto/ScheduledUpdateRequest.kt index 3b95832..ce1bab0 100644 --- a/src/main/kotlin/com/beat_it/cal/dto/ScheduledUpdateRequest.kt +++ b/src/main/kotlin/com/beat_it/cal/dto/ScheduledUpdateRequest.kt @@ -4,18 +4,10 @@ import com.fasterxml.jackson.annotation.JsonProperty import java.time.OffsetDateTime data class ScheduleUpdateRequest( - @JsonProperty("location_id") val locationId: Long?, - val title: String?, val content: String?, - - @JsonProperty("starts_at") val startsAt: OffsetDateTime?, - - @JsonProperty("ends_at") val endsAt: OffsetDateTime?, - - @JsonProperty("participant_user_ids") val participantUserIds: List? = null ) \ No newline at end of file diff --git a/src/main/kotlin/com/beat_it/cal/service/ScheduleService.kt b/src/main/kotlin/com/beat_it/cal/service/ScheduleService.kt index 20283ab..35b5dde 100644 --- a/src/main/kotlin/com/beat_it/cal/service/ScheduleService.kt +++ b/src/main/kotlin/com/beat_it/cal/service/ScheduleService.kt @@ -117,7 +117,7 @@ class ScheduleService( } @Transactional(readOnly = true) - fun getScheduleDetail(scheduleId: Long): ScheduleDetailResponse { + fun getScheduleDetail(scheduleId: Long, userId: Long): ScheduleDetailResponse { val schedule = findScheduleOrThrow(scheduleId) //TODO: 현재 내가 있는 팀 소속이 무엇인지 어떻게 넘겨받을 것인지 @@ -157,7 +157,7 @@ class ScheduleService( val calendarSchedules = schedules.map { schedule -> CalendarSchedule( - scheduleId = schedule.scheduleId ?: throw IllegalStateException("일정 ID가 존재하지 않습니다."), + scheduleId = schedule.scheduleId ?: throw BusinessException(ErrorCode.CALENDAR_NOT_FOUND), title = schedule.title, startsAt = schedule.startsAt, endsAt = schedule.endsAt @@ -180,7 +180,7 @@ class ScheduleService( val dateSchedules = schedules.map { schedule -> DateSchedule( - scheduleId = schedule.scheduleId ?: throw IllegalStateException("일정 ID가 존재하지 않습니다."), + scheduleId = schedule.scheduleId ?: throw BusinessException(ErrorCode.CALENDAR_NOT_FOUND), title = schedule.title, content = schedule.content ?: "", startsAt = schedule.startsAt,