Skip to content

Commit

Permalink
Merge pull request #117 from soma-baekgu/feature/BG-240-create-task-e…
Browse files Browse the repository at this point in the history
…vent

[BG-240]: 태스크 이벤트 생성 (2h / 2h)
  • Loading branch information
GGHDMS authored Oct 29, 2024
2 parents c5270d2 + b586771 commit 318d8ac
Show file tree
Hide file tree
Showing 12 changed files with 281 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import com.backgu.amaker.application.event.service.ReactionEventService
import com.backgu.amaker.application.event.service.ReactionOptionService
import com.backgu.amaker.application.event.service.ReplyCommentService
import com.backgu.amaker.application.event.service.ReplyEventService
import com.backgu.amaker.application.event.service.TaskEventService
import com.backgu.amaker.infra.jpa.event.repository.EventAssignedUserRepository
import com.backgu.amaker.infra.jpa.event.repository.EventRepository
import com.backgu.amaker.infra.jpa.event.repository.ReactionCommentRepository
import com.backgu.amaker.infra.jpa.event.repository.ReactionEventRepository
import com.backgu.amaker.infra.jpa.event.repository.ReactionOptionRepository
import com.backgu.amaker.infra.jpa.event.repository.ReplyCommentRepository
import com.backgu.amaker.infra.jpa.event.repository.ReplyEventRepository
import com.backgu.amaker.infra.jpa.event.repository.TaskEventRepository
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

Expand Down Expand Up @@ -44,4 +46,7 @@ class EventServiceConfig {
@Bean
fun reactionCommentService(reactionCommentRepository: ReactionCommentRepository): ReactionCommentService =
ReactionCommentService(reactionCommentRepository)

@Bean
fun taskEventService(taskEventRepository: TaskEventRepository): TaskEventService = TaskEventService(taskEventRepository)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.backgu.amaker.api.event.controller

import com.backgu.amaker.api.event.dto.request.ReactionEventCreateRequest
import com.backgu.amaker.api.event.dto.request.ReplyEventCreateRequest
import com.backgu.amaker.api.event.dto.request.TaskEventCreateRequest
import com.backgu.amaker.api.event.dto.response.ReactionEventDetailResponse
import com.backgu.amaker.api.event.dto.response.ReplyEventDetailResponse
import com.backgu.amaker.api.event.service.EventFacadeService
Expand Down Expand Up @@ -106,4 +107,25 @@ class EventController(
).id,
).toUri(),
).build()

@PostMapping("/events/task")
override fun createTaskEvent(
@AuthenticationPrincipal token: JwtAuthentication,
@PathVariable("chat-room-id") chatRoomId: Long,
@RequestBody @Valid request: TaskEventCreateRequest,
): ResponseEntity<Unit> =
ResponseEntity
.created(
ServletUriComponentsBuilder
.fromCurrentContextPath()
.path("/api/v1/events/{id}/task")
.buildAndExpand(
eventFacadeService
.createTaskEvent(
token.id,
chatRoomId,
request.toDto(),
).id,
).toUri(),
).build()
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.backgu.amaker.api.event.controller

import com.backgu.amaker.api.event.dto.request.ReactionEventCreateRequest
import com.backgu.amaker.api.event.dto.request.ReplyEventCreateRequest
import com.backgu.amaker.api.event.dto.request.TaskEventCreateRequest
import com.backgu.amaker.api.event.dto.response.ReactionEventDetailResponse
import com.backgu.amaker.api.event.dto.response.ReplyEventDetailResponse
import com.backgu.amaker.common.http.response.ApiResult
Expand All @@ -14,7 +15,6 @@ import jakarta.validation.Valid
import org.springframework.http.ResponseEntity
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody

@Tag(name = "event", description = "이벤트 API")
Expand Down Expand Up @@ -73,10 +73,24 @@ interface EventSwagger {
),
],
)
@PostMapping("/events/reaction")
fun createReactionEvent(
token: JwtAuthentication,
chatRoomId: Long,
request: ReactionEventCreateRequest,
): ResponseEntity<Unit>

@Operation(summary = "task 이벤트 생성", description = "task 이벤트 생성합니다.")
@ApiResponses(
value = [
ApiResponse(
responseCode = "201",
description = "task 이벤트 생성 성공",
),
],
)
fun createTaskEvent(
token: JwtAuthentication,
chatRoomId: Long,
request: TaskEventCreateRequest,
): ResponseEntity<Unit>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.backgu.amaker.api.event.dto

import java.time.LocalDateTime

data class TaskEventCreateDto(
val eventTitle: String,
val eventDetails: String,
val assignees: List<String>,
val deadLine: LocalDateTime,
val notificationStartHour: Int,
val notificationStartMinute: Int,
val interval: Int,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.backgu.amaker.api.event.dto

import com.backgu.amaker.domain.event.TaskEvent
import java.time.LocalDateTime

data class TaskEventDto(
val id: Long,
val eventTitle: String,
val deadLine: LocalDateTime,
val notificationStartTime: LocalDateTime,
val notificationInterval: Int,
) {
companion object {
fun of(reactionEvent: TaskEvent) =
TaskEventDto(
id = reactionEvent.id,
eventTitle = reactionEvent.eventTitle,
deadLine = reactionEvent.deadLine,
notificationStartTime = reactionEvent.notificationStartTime,
notificationInterval = reactionEvent.notificationInterval,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.backgu.amaker.api.event.dto.request

import com.backgu.amaker.api.event.dto.TaskEventCreateDto
import io.swagger.v3.oas.annotations.media.Schema
import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.Size
import org.springframework.format.annotation.DateTimeFormat
import java.time.LocalDateTime

data class TaskEventCreateRequest(
@Schema(description = "제목", example = "제목 어때요?")
@field:NotBlank(message = "이벤트 제목을 입력해주세요.")
val eventTitle: String,
@Schema(description = "상세내용", example = "상세내용 어때요?")
@field:NotBlank(message = "이벤트 내용을 입력해주세요.")
val eventDetails: String,
@Schema(description = "답변을 요청할 인원", example = "[\"user1\", \"user2\"]")
@field:Size(min = 1, message = "최소 한 명 이상의 인원을 지정해야 합니다.")
val assignees: List<String>,
@Schema(description = "마감 기한", example = "2021-08-01T00:00:00")
@DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
val deadLine: LocalDateTime,
@Schema(description = "알림 시작 시간", example = "1")
val notificationStartHour: Int,
@Schema(description = "알림 시작 분", example = "30")
val notificationStartMinute: Int,
@Schema(description = "알림 주기", example = "15")
val interval: Int,
) {
fun toDto() =
TaskEventCreateDto(
eventTitle = eventTitle,
eventDetails = eventDetails,
assignees = assignees,
deadLine = deadLine,
notificationStartMinute = notificationStartMinute,
notificationStartHour = notificationStartHour,
interval = interval,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import com.backgu.amaker.api.event.dto.ReactionEventDto
import com.backgu.amaker.api.event.dto.ReplyEventCreateDto
import com.backgu.amaker.api.event.dto.ReplyEventDetailDto
import com.backgu.amaker.api.event.dto.ReplyEventDto
import com.backgu.amaker.api.event.dto.TaskEventCreateDto
import com.backgu.amaker.api.event.dto.TaskEventDto
import com.backgu.amaker.api.user.dto.UserDto
import com.backgu.amaker.application.chat.event.EventChatSaveEvent
import com.backgu.amaker.application.chat.service.ChatRoomService
Expand All @@ -17,6 +19,7 @@ import com.backgu.amaker.application.event.service.EventService
import com.backgu.amaker.application.event.service.ReactionEventService
import com.backgu.amaker.application.event.service.ReactionOptionService
import com.backgu.amaker.application.event.service.ReplyEventService
import com.backgu.amaker.application.event.service.TaskEventService
import com.backgu.amaker.application.user.service.UserService
import com.backgu.amaker.application.workspace.WorkspaceUserService
import com.backgu.amaker.common.exception.BusinessException
Expand All @@ -39,6 +42,7 @@ class EventFacadeService(
private val chatRoomUserService: ChatRoomUserService,
private val chatService: ChatService,
private val replyEventService: ReplyEventService,
private val taskEventService: TaskEventService,
private val reactionEventService: ReactionEventService,
private val reactionOptionService: ReactionOptionService,
private val eventAssignedUserService: EventAssignedUserService,
Expand Down Expand Up @@ -164,6 +168,45 @@ class EventFacadeService(
return ReplyEventDto.of(replyEvent)
}

@Transactional
fun createTaskEvent(
userId: String,
chatRoomId: Long,
taskEventCreateDto: TaskEventCreateDto,
): TaskEventDto {
val user = userService.getById(userId)
val chatRoom = chatRoomService.getById(chatRoomId)
chatRoomUserService.validateUserInChatRoom(user, chatRoom)

val chat: Chat = chatService.save(chatRoom.createChat(user, taskEventCreateDto.eventTitle, ChatType.TASK))
chatRoomService.save(chatRoom.updateLastChatId(chat))

val taskEvent =
taskEventService.save(
chat.createTaskEvent(
taskEventCreateDto.deadLine,
taskEventCreateDto.notificationStartHour,
taskEventCreateDto.notificationStartMinute,
taskEventCreateDto.interval,
taskEventCreateDto.eventDetails,
),
)

val users = userService.getAllByUserEmails(taskEventCreateDto.assignees)
chatRoomUserService.validateUsersInChatRoom(users, chatRoom)

eventAssignedUserService.saveAll(taskEvent.createAssignedUsers(users))

eventPublisher.publishEvent(
EventChatSaveEvent.of(
chatRoomId,
chat.createEventChatWithUser(taskEvent, user, users),
),
)

return TaskEventDto.of(taskEvent)
}

@Transactional
fun createReactionEvent(
userId: String,
Expand Down
19 changes: 19 additions & 0 deletions domain/src/main/kotlin/com/backgu/amaker/domain/chat/Chat.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.backgu.amaker.domain.chat
import com.backgu.amaker.domain.event.Event
import com.backgu.amaker.domain.event.ReactionEvent
import com.backgu.amaker.domain.event.ReplyEvent
import com.backgu.amaker.domain.event.TaskEvent
import com.backgu.amaker.domain.user.User
import java.time.LocalDateTime

Expand All @@ -15,6 +16,24 @@ class Chat(
val createdAt: LocalDateTime = LocalDateTime.now(),
val updatedAt: LocalDateTime = LocalDateTime.now(),
) {
fun createTaskEvent(
deadLine: LocalDateTime,
notificationStartHour: Int,
notificationStartMinute: Int,
notificationInterval: Int,
eventDetails: String,
) = TaskEvent(
id = id,
eventTitle = content,
deadLine = deadLine,
notificationStartTime =
deadLine
.minusHours(notificationStartHour.toLong())
.minusMinutes(notificationStartMinute.toLong()),
notificationInterval = notificationInterval,
eventDetails = eventDetails,
)

fun createReplyEvent(
deadLine: LocalDateTime,
notificationStartHour: Int,
Expand Down
20 changes: 20 additions & 0 deletions domain/src/main/kotlin/com/backgu/amaker/domain/event/TaskEvent.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.backgu.amaker.domain.event

import java.time.LocalDateTime

class TaskEvent(
id: Long,
eventTitle: String,
val eventDetails: String,
deadLine: LocalDateTime,
notificationStartTime: LocalDateTime,
notificationInterval: Int,
createdAt: LocalDateTime = LocalDateTime.now(),
updatedAt: LocalDateTime = LocalDateTime.now(),
) : Event(id, eventTitle, deadLine, notificationStartTime, notificationInterval, createdAt, updatedAt) {
companion object {
const val EVENT_TYPE = "TASK"
}

override fun getEventType(): String = EVENT_TYPE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.backgu.amaker.application.event.service

import com.backgu.amaker.common.exception.BusinessException
import com.backgu.amaker.common.status.StatusCode
import com.backgu.amaker.domain.event.TaskEvent
import com.backgu.amaker.infra.jpa.event.entity.TaskEventEntity
import com.backgu.amaker.infra.jpa.event.repository.TaskEventRepository
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
@Transactional(readOnly = true)
class TaskEventService(
private val taskEventRepository: TaskEventRepository,
) {
@Transactional
fun save(replyEvent: TaskEvent): TaskEvent = taskEventRepository.save(TaskEventEntity.of(replyEvent)).toDomain()

fun getById(id: Long): TaskEvent =
taskEventRepository.findByIdOrNull(id)?.toDomain()
?: throw BusinessException(StatusCode.EVENT_NOT_FOUND)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.backgu.amaker.infra.jpa.event.entity

import com.backgu.amaker.domain.event.TaskEvent
import jakarta.persistence.Column
import jakarta.persistence.DiscriminatorValue
import jakarta.persistence.Entity
import jakarta.persistence.Table
import java.time.LocalDateTime

@Entity(name = "TaskEvent")
@Table(name = "task_event")
@DiscriminatorValue(value = "TASK")
class TaskEventEntity(
@Column(nullable = false)
val eventDetails: String,
id: Long,
eventTitle: String,
deadLine: LocalDateTime,
notificationStartTime: LocalDateTime,
notificationInterval: Int,
) : EventEntity(
id = id,
eventTitle = eventTitle,
deadLine = deadLine,
notificationStartTime = notificationStartTime,
notificationInterval = notificationInterval,
) {
override fun toDomain(): TaskEvent =
TaskEvent(
id = id,
eventTitle = eventTitle,
eventDetails = eventDetails,
deadLine = deadLine,
notificationStartTime = notificationStartTime,
notificationInterval = notificationInterval,
createdAt = createdAt,
updatedAt = updatedAt,
)

companion object {
fun of(taskEvent: TaskEvent) =
TaskEventEntity(
id = taskEvent.id,
eventTitle = taskEvent.eventTitle,
eventDetails = taskEvent.eventDetails,
deadLine = taskEvent.deadLine,
notificationStartTime = taskEvent.notificationStartTime,
notificationInterval = taskEvent.notificationInterval,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.backgu.amaker.infra.jpa.event.repository

import com.backgu.amaker.infra.jpa.event.entity.TaskEventEntity
import org.springframework.data.jpa.repository.JpaRepository

interface TaskEventRepository : JpaRepository<TaskEventEntity, Long>

0 comments on commit 318d8ac

Please sign in to comment.