Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
@@ -0,0 +1,40 @@
package noweekend.core.api.controller.v2

import noweekend.core.api.controller.v1.response.ScheduleResponse
import noweekend.core.api.controller.v2.docs.ScheduleControllerDocsV2
import noweekend.core.api.controller.v2.request.ScheduleCreateRequestV2
import noweekend.core.api.controller.v2.request.ScheduleUpdateRequestV2
import noweekend.core.api.security.annotations.CurrentUserId
import noweekend.core.api.service.schedule.ScheduleApplicationService
import noweekend.core.support.response.ApiResponse
import org.springframework.validation.annotation.Validated
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
@RequestMapping("/api/v2/schedule")
class ScheduleControllerV2(
private val scheduleApplicationService: ScheduleApplicationService,
) : ScheduleControllerDocsV2 {

@PostMapping
override fun createSchedule(
@CurrentUserId userId: String,
@Validated @RequestBody request: ScheduleCreateRequestV2,
): ApiResponse<ScheduleResponse> {
return ApiResponse.success(scheduleApplicationService.createScheduleV2(userId, request))
}

@PutMapping("/{id}")
override fun updateSchedule(
@CurrentUserId userId: String,
@PathVariable id: String,
@Validated @RequestBody request: ScheduleUpdateRequestV2,
): ApiResponse<ScheduleResponse> {
return ApiResponse.success(scheduleApplicationService.updateScheduleV2(userId, id, request))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package noweekend.core.api.controller.v2.docs

import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.media.Content
import io.swagger.v3.oas.annotations.media.ExampleObject
import io.swagger.v3.oas.annotations.media.Schema
import io.swagger.v3.oas.annotations.parameters.RequestBody
import noweekend.core.api.controller.v1.response.ScheduleResponse
import noweekend.core.api.controller.v2.request.ScheduleCreateRequestV2
import noweekend.core.api.controller.v2.request.ScheduleUpdateRequestV2
import noweekend.core.support.response.ApiResponse
import io.swagger.v3.oas.annotations.responses.ApiResponse as SwaggerApiResponse

interface ScheduleControllerDocsV2 {

@Operation(
summary = "캘린더: 일정 생성",
description = "일정을 생성합니다.",
requestBody = RequestBody(
required = true,
content = [
Content(
mediaType = "application/json",
schema = Schema(implementation = ScheduleCreateRequestV2::class),
examples = [
ExampleObject(
name = "예시 요청",
value = """
{
"title": "회의",
"startDateTime": "2024-05-27 10:00:00",
"endDateTime": "2024-05-27 11:00:00",
"category": "COMPANY",
"temperature": 3,
"alarmOption": "FIFTEEN_MINUTES_BEFORE"
}
""",
),
],
),
],
),
responses = [
SwaggerApiResponse(
responseCode = "200",
description = "일정 생성 성공",
content = [
Content(
mediaType = "application/json",
schema = Schema(implementation = ApiResponse::class),
examples = [
ExampleObject(
name = "예시 응답",
value = """
{
"result": "SUCCESS",
"data": {
"id": "abc123",
"title": "회의",
"startDateTime": "2025-05-01T10:00:00",
"endDateTime": "2025-05-01T11:00:00",
"category": "COMPANY",
"temperature": 3,
"alarmOption": "FIFTEEN_MINUTES_BEFORE",
"completed": false
},
"error": null
}
""",
),
],
),
],
),
SwaggerApiResponse(
responseCode = "400",
description = "잘못된 요청",
content = [
Content(
mediaType = "application/json",
schema = Schema(implementation = ApiResponse::class),
examples = [
ExampleObject(
name = "예시 응답",
value = """
{
"result": "ERROR",
"data": null,
"error": {
"code": "INVALID_PARAMETER",
"message": "올바르지 않은 요청입니다.",
"data": {}
}
}
""",
),
],
),
],
),
],
)
fun createSchedule(
@Schema(hidden = true) userId: String,
request: ScheduleCreateRequestV2,
): ApiResponse<ScheduleResponse>

@Operation(
summary = "캘린더: 일정 수정",
description = "일정의 시작/종료 시간, 카테고리, 온도, 알람 옵션을 수정합니다.",
requestBody = RequestBody(
required = true,
content = [
Content(
mediaType = "application/json",
schema = Schema(implementation = ScheduleUpdateRequestV2::class),
),
],
),
responses = [
SwaggerApiResponse(
responseCode = "200",
description = "일정 수정 성공",
content = [
Content(
mediaType = "application/json",
schema = Schema(implementation = ApiResponse::class),
examples = [
ExampleObject(
name = "예시 응답",
value = """
{
"result": "SUCCESS",
"data": {
"id": "abc123",
"title": "회의",
"startDateTime": "2025-05-01T10:00:00",
"endDateTime": "2025-05-01T11:00:00",
"category": "COMPANY",
"temperature": 3,
"alarmOption": "FIFTEEN_MINUTES_BEFORE",
"completed": false
},
"error": null
}
""",
),
],
),
],
),
SwaggerApiResponse(
responseCode = "400",
description = "잘못된 요청",
content = [
Content(
mediaType = "application/json",
schema = Schema(implementation = ApiResponse::class),
examples = [
ExampleObject(
name = "예시 응답",
value = """
{
"result": "ERROR",
"data": null,
"error": {
"code": "INVALID_PARAMETER",
"message": "올바르지 않은 요청입니다.",
"data": {}
}
}
""",
),
],
),
],
),
],
)
fun updateSchedule(
@Schema(hidden = true) userId: String,
id: String,
request: ScheduleUpdateRequestV2,
): ApiResponse<ScheduleResponse>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package noweekend.core.api.controller.v2.request

import io.swagger.v3.oas.annotations.media.Schema
import jakarta.validation.constraints.Max
import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.NotNull
import jakarta.validation.constraints.Positive
import noweekend.core.domain.enumerate.AlarmOption
import noweekend.core.domain.enumerate.ScheduleCategory
import java.time.LocalDateTime

@Schema(description = "일정 생성 요청")
data class ScheduleCreateRequestV2(
@field:NotBlank(message = "제목은 필수입니다.")
@Schema(description = "제목")
val title: String,

@field:NotNull(message = "시작 시간은 필수입니다.")
@Schema(description = "시작 시간")
val startDateTime: LocalDateTime,

@field:NotNull(message = "종료 시간은 필수입니다.")
@Schema(description = "종료 시간")
val endDateTime: LocalDateTime,

@field:NotNull(message = "카테고리는 필수입니다.")
@Schema(description = "카테고리")
val category: ScheduleCategory,

@field:NotNull(message = "온도는 필수입니다.")
@field:Positive(message = "온도는 양수여야 합니다.")
@field:Max(value = 100, message = "온도는 100 이하여야 합니다.")
@Schema(description = "온도 (감정 등 표현)")
val temperature: Int,

@field:NotNull(message = "알람 설정은 필수입니다.")
@Schema(description = "알람 설정")
val alarmOption: AlarmOption,
)

@Schema(description = "일정 수정 요청")
data class ScheduleUpdateRequestV2(
@field:NotBlank(message = "제목은 필수입니다.")
@Schema(description = "제목")
val title: String,

@field:NotNull(message = "시작 시간은 필수입니다.")
@Schema(description = "시작 시간")
val startDateTime: LocalDateTime,

@field:NotNull(message = "종료 시간은 필수입니다.")
@Schema(description = "종료 시간")
val endDateTime: LocalDateTime,

@field:NotNull(message = "카테고리는 필수입니다.")
@Schema(description = "카테고리")
val category: ScheduleCategory,

@field:NotNull(message = "온도는 필수입니다.")
@field:Positive(message = "온도는 양수여야 합니다.")
@field:Max(value = 100, message = "온도는 100 이하여야 합니다.")
@Schema(description = "온도 (감정 등 표현)")
val temperature: Int,

@field:NotNull(message = "알람 설정은 필수입니다.")
@Schema(description = "알람 설정")
val alarmOption: AlarmOption,
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import noweekend.core.api.controller.v1.request.ScheduleCreateRequest
import noweekend.core.api.controller.v1.request.ScheduleUpdateRequest
import noweekend.core.api.controller.v1.response.DailyScheduleResponse
import noweekend.core.api.controller.v1.response.ScheduleResponse
import noweekend.core.api.controller.v2.request.ScheduleCreateRequestV2
import noweekend.core.api.controller.v2.request.ScheduleUpdateRequestV2
import noweekend.core.domain.schedule.Schedule
import noweekend.core.domain.schedule.ScheduleReader
import noweekend.core.domain.schedule.ScheduleWriter
Expand All @@ -25,12 +27,23 @@ interface ScheduleApplicationService {
request: ScheduleCreateRequest,
): ScheduleResponse

fun createScheduleV2(
userId: String,
request: ScheduleCreateRequestV2,
): ScheduleResponse

fun updateSchedule(
userId: String,
scheduleId: String,
request: ScheduleUpdateRequest,
): ScheduleResponse

fun updateScheduleV2(
userId: String,
scheduleId: String,
request: ScheduleUpdateRequestV2,
): ScheduleResponse

fun updateScheduleState(
userId: String,
scheduleId: String,
Expand Down Expand Up @@ -108,6 +121,28 @@ class ScheduleApplicationServiceImpl(
return savedSchedule.toResponse()
}

override fun createScheduleV2(
userId: String,
request: ScheduleCreateRequestV2,
): ScheduleResponse {
if (request.startDateTime > request.endDateTime) {
throw CoreException(ErrorType.INVALID_PARAMETER, "startDateTime must greater than endDateTime")
}

val schedule = Schedule.newScheduleV2(
userId = userId,
title = request.title,
startTime = request.startDateTime,
endTime = request.startDateTime,
category = request.category,
temperature = request.temperature,
alarmOption = request.alarmOption,
)

val savedSchedule = scheduleWriter.save(schedule)
return savedSchedule.toResponse()
}

override fun updateSchedule(
userId: String,
scheduleId: String,
Expand Down Expand Up @@ -147,6 +182,35 @@ class ScheduleApplicationServiceImpl(
return savedSchedule.toResponse()
}

override fun updateScheduleV2(
userId: String,
scheduleId: String,
request: ScheduleUpdateRequestV2,
): ScheduleResponse {
val existingSchedule = scheduleReader.findScheduleById(scheduleId)
?: throw CoreException(ErrorType.NOT_FOUND_ERROR, "Schedule not found: $scheduleId")

if (existingSchedule.userId != userId) {
throw CoreException(ErrorType.FORBIDDEN_ERROR, "You don't have permission to update this schedule")
}

if (request.startDateTime > request.endDateTime) {
throw CoreException(ErrorType.INVALID_PARAMETER, "startDateTime must greater than endDateTime")
}

val updatedSchedule = existingSchedule.copy(
title = request.title,
startTime = request.startDateTime,
endTime = request.endDateTime,
category = request.category,
temperature = request.temperature,
alarmOption = request.alarmOption,
)

val savedSchedule = scheduleWriter.update(updatedSchedule)
return savedSchedule.toResponse()
}

override fun updateScheduleState(
userId: String,
scheduleId: String,
Expand Down
Loading