diff --git a/src/main/kotlin/com/example/attendanceapimono/adapter/infra/config/SecurityConfig.kt b/src/main/kotlin/com/example/attendanceapimono/adapter/infra/config/SecurityConfig.kt index 959abb0..2052acd 100644 --- a/src/main/kotlin/com/example/attendanceapimono/adapter/infra/config/SecurityConfig.kt +++ b/src/main/kotlin/com/example/attendanceapimono/adapter/infra/config/SecurityConfig.kt @@ -6,6 +6,7 @@ import com.example.attendanceapimono.adapter.infra.security.SecurityContextRepos import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.http.HttpMethod +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity import org.springframework.security.config.web.server.ServerHttpSecurity diff --git a/src/main/kotlin/com/example/attendanceapimono/adapter/infra/security/UserPrincipal.kt b/src/main/kotlin/com/example/attendanceapimono/adapter/infra/security/UserPrincipal.kt index 228e476..f88cbcb 100644 --- a/src/main/kotlin/com/example/attendanceapimono/adapter/infra/security/UserPrincipal.kt +++ b/src/main/kotlin/com/example/attendanceapimono/adapter/infra/security/UserPrincipal.kt @@ -19,7 +19,7 @@ data class RoleAdapter(val role: UserRole): GrantedAuthority { class UserPrincipal( val id: UUID, - val roles: List + private val roles: List ) : UserDetails { override fun getAuthorities(): MutableCollection { return roles.toMutableList() diff --git a/src/main/kotlin/com/example/attendanceapimono/adapter/present/EventController.kt b/src/main/kotlin/com/example/attendanceapimono/adapter/present/EventController.kt index 5447b35..d7c0339 100644 --- a/src/main/kotlin/com/example/attendanceapimono/adapter/present/EventController.kt +++ b/src/main/kotlin/com/example/attendanceapimono/adapter/present/EventController.kt @@ -1,8 +1,19 @@ package com.example.attendanceapimono.adapter.present import com.example.attendanceapimono.adapter.present.api.EventAPI +import com.example.attendanceapimono.application.EventService +import com.example.attendanceapimono.application.dto.event.CreateEventRequest +import com.example.attendanceapimono.application.dto.event.CreateEventResponse +import com.example.attendanceapimono.application.exception.handleValidationCatch +import kotlinx.coroutines.reactor.awaitSingle import org.springframework.web.bind.annotation.RestController +import reactor.core.publisher.Mono @RestController -class EventController: EventAPI { +class EventController (private val eventService: EventService): EventAPI { + override suspend fun createEvent(body: Mono): CreateEventResponse { + return body.handleValidationCatch() + .map(eventService::createEvent) + .awaitSingle() + } } \ No newline at end of file diff --git a/src/main/kotlin/com/example/attendanceapimono/adapter/present/api/EventAPI.kt b/src/main/kotlin/com/example/attendanceapimono/adapter/present/api/EventAPI.kt index e6ec6a2..2c9754d 100644 --- a/src/main/kotlin/com/example/attendanceapimono/adapter/present/api/EventAPI.kt +++ b/src/main/kotlin/com/example/attendanceapimono/adapter/present/api/EventAPI.kt @@ -1,7 +1,47 @@ package com.example.attendanceapimono.adapter.present.api +import com.example.attendanceapimono.application.dto.event.CreateEventRequest +import com.example.attendanceapimono.application.dto.event.CreateEventResponse +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.responses.ApiResponse import io.swagger.v3.oas.annotations.tags.Tag +import org.springframework.http.HttpStatus +import org.springframework.http.MediaType +import org.springframework.security.access.prepost.PreAuthorize +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.ResponseStatus +import reactor.core.publisher.Mono +import javax.validation.Valid @Tag(name = "Event 관련 API") interface EventAPI { + @Operation( + summary = "이벤트 생성", + requestBody = io.swagger.v3.oas.annotations.parameters.RequestBody( + content = [ + Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = Schema(implementation = CreateEventRequest::class), + ) + ] + ) + ) + @ApiResponse( + responseCode = "201", + description = "이벤트 생성", + content = [Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = Schema(implementation = CreateEventResponse::class), + examples = [ExampleObject(CreateEventResponse.Example)] + )], + ) + @ResponseStatus(HttpStatus.CREATED) + @JWTTokenV1 + @PreAuthorize("hasRole('MASTER') or hasRole('STAFF')") + @PostMapping("/event") + suspend fun createEvent(@Valid @RequestBody body: Mono): CreateEventResponse } \ No newline at end of file diff --git a/src/main/kotlin/com/example/attendanceapimono/application/EventService.kt b/src/main/kotlin/com/example/attendanceapimono/application/EventService.kt index b9235ae..816bb8e 100644 --- a/src/main/kotlin/com/example/attendanceapimono/application/EventService.kt +++ b/src/main/kotlin/com/example/attendanceapimono/application/EventService.kt @@ -1,10 +1,23 @@ package com.example.attendanceapimono.application +import com.example.attendanceapimono.application.dto.event.CreateEventRequest +import com.example.attendanceapimono.application.dto.event.CreateEventResponse +import com.example.attendanceapimono.domain.event.EventRepository +import kotlinx.coroutines.async +import kotlinx.coroutines.runBlocking import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional @Service -class EventService { - fun createEvent() { +class EventService ( + private val eventRepository: EventRepository +) { + @Transactional + fun createEvent(dto: CreateEventRequest): CreateEventResponse = runBlocking { + val event = dto.entity() + eventRepository.save(event) + + CreateEventResponse(eventId = event.id) } } \ No newline at end of file diff --git a/src/main/kotlin/com/example/attendanceapimono/application/dto/event/CreateEventRequest.kt b/src/main/kotlin/com/example/attendanceapimono/application/dto/event/CreateEventRequest.kt index 6e4f2ba..e3ef4f5 100644 --- a/src/main/kotlin/com/example/attendanceapimono/application/dto/event/CreateEventRequest.kt +++ b/src/main/kotlin/com/example/attendanceapimono/application/dto/event/CreateEventRequest.kt @@ -1,9 +1,12 @@ package com.example.attendanceapimono.application.dto.event +import com.example.attendanceapimono.domain.event.Event import io.swagger.v3.oas.annotations.media.Schema import org.hibernate.validator.constraints.Length +import org.springframework.beans.factory.annotation.Value import org.springframework.format.annotation.DateTimeFormat import java.time.LocalDateTime +import java.util.* @Schema( title = "이벤트 등록 요청", @@ -26,12 +29,27 @@ class CreateEventRequest( @field:DateTimeFormat val expectedAt: String, + @Value("\${event.late-diff-minute}") @Schema(description = "지각 기준 시간 (분)") val lateDiffMinute: Int, + @Value("\${event.absent-diff-minute}") @Schema(description = "결석 기준 시간 (분)") val absentDiffMinute: Int ) { + fun entity() = Event ( + id = UUID.randomUUID(), + generationID = this.generationID, + title = this.title, + description = this.description, + expectedAt = LocalDateTime.parse(this.expectedAt), + startAt = LocalDateTime.parse(this.expectedAt), + lateDiffMinutes = this.lateDiffMinute, + absentDiffMinutes = this.absentDiffMinute, + createdAt = LocalDateTime.now(), + updatedAt = LocalDateTime.now() + ) + companion object { const val Example = """ { diff --git a/src/main/kotlin/com/example/attendanceapimono/application/dto/event/CreateEventResponse.kt b/src/main/kotlin/com/example/attendanceapimono/application/dto/event/CreateEventResponse.kt index e97f2ef..a2d8a01 100644 --- a/src/main/kotlin/com/example/attendanceapimono/application/dto/event/CreateEventResponse.kt +++ b/src/main/kotlin/com/example/attendanceapimono/application/dto/event/CreateEventResponse.kt @@ -1,7 +1,7 @@ package com.example.attendanceapimono.application.dto.event -import com.example.attendanceapimono.domain.event.Event import io.swagger.v3.oas.annotations.media.Schema +import java.util.* @Schema( title = "이벤트 등록 후 반환", @@ -10,22 +10,12 @@ import io.swagger.v3.oas.annotations.media.Schema ) data class CreateEventResponse ( @Schema(description = "생성된 event") - val event: Event, + val eventId: UUID, ) { companion object { const val Example = """ { - "id": "123e4567-e89b-12d3-a456-426614174000", - "generationID": 6, - "title": "오리엔테이션", - "description": "함께 모여 앞으로의 방향에 대해 이야기 나눠 보아요.", - "isDone": false, - "expectedAt": "2021-08-21T14:00:00.000", - "lateDiffMinute": 10, - "absentDiffMinute": 60, - "startAt": null, - "createdAt": "2021-08-12T22:13:47.245", - "updatedAt": "2021-08-12T22:13:47.245" + "id": "123e4567-e89b-12d3-a456-426614174000" } """ } diff --git a/src/main/resources/application-local.yaml b/src/main/resources/application-local.yaml index 224e377..5fd6cb9 100644 --- a/src/main/resources/application-local.yaml +++ b/src/main/resources/application-local.yaml @@ -17,4 +17,7 @@ spring: format_sql: true jwt: secret: test_QiPScSmMJnGrGCwTm*Xi0m1yAHe3KfAkhH&VpsENjwqfGP%grBdmv4Ee%GYZhNj2S4y6@$8KORZE9dsiw%v*ugKcba9x8CN18!Pl - expires: 31536000000000 \ No newline at end of file + expires: 31536000000000 +event: + late-diff-minute: 10 + absent-diff-minute: 50 \ No newline at end of file diff --git a/src/main/resources/application-prod.yaml b/src/main/resources/application-prod.yaml index 85d0b28..edeedbc 100644 --- a/src/main/resources/application-prod.yaml +++ b/src/main/resources/application-prod.yaml @@ -18,4 +18,7 @@ spring: format_sql: true jwt: secret: ${JWT_SECRET} - expires: ${JWT_EXPIRES} \ No newline at end of file + expires: ${JWT_EXPIRES} +event: + late-diff-minute: ${DDD_LATE_DIFF_MINUTE} + absent-diff-minute: ${DDD_ABSENT_DIFF_MINUTE} \ No newline at end of file