Skip to content

Commit b586771

Browse files
committed
feat: 태스크 이벤트 생성
1 parent f96b6f9 commit b586771

File tree

12 files changed

+281
-2
lines changed

12 files changed

+281
-2
lines changed

api/src/main/kotlin/com/backgu/amaker/api/event/config/EventServiceConfig.kt

+5
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ import com.backgu.amaker.application.event.service.ReactionEventService
77
import com.backgu.amaker.application.event.service.ReactionOptionService
88
import com.backgu.amaker.application.event.service.ReplyCommentService
99
import com.backgu.amaker.application.event.service.ReplyEventService
10+
import com.backgu.amaker.application.event.service.TaskEventService
1011
import com.backgu.amaker.infra.jpa.event.repository.EventAssignedUserRepository
1112
import com.backgu.amaker.infra.jpa.event.repository.EventRepository
1213
import com.backgu.amaker.infra.jpa.event.repository.ReactionCommentRepository
1314
import com.backgu.amaker.infra.jpa.event.repository.ReactionEventRepository
1415
import com.backgu.amaker.infra.jpa.event.repository.ReactionOptionRepository
1516
import com.backgu.amaker.infra.jpa.event.repository.ReplyCommentRepository
1617
import com.backgu.amaker.infra.jpa.event.repository.ReplyEventRepository
18+
import com.backgu.amaker.infra.jpa.event.repository.TaskEventRepository
1719
import org.springframework.context.annotation.Bean
1820
import org.springframework.context.annotation.Configuration
1921

@@ -44,4 +46,7 @@ class EventServiceConfig {
4446
@Bean
4547
fun reactionCommentService(reactionCommentRepository: ReactionCommentRepository): ReactionCommentService =
4648
ReactionCommentService(reactionCommentRepository)
49+
50+
@Bean
51+
fun taskEventService(taskEventRepository: TaskEventRepository): TaskEventService = TaskEventService(taskEventRepository)
4752
}

api/src/main/kotlin/com/backgu/amaker/api/event/controller/EventController.kt

+22
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.backgu.amaker.api.event.controller
22

33
import com.backgu.amaker.api.event.dto.request.ReactionEventCreateRequest
44
import com.backgu.amaker.api.event.dto.request.ReplyEventCreateRequest
5+
import com.backgu.amaker.api.event.dto.request.TaskEventCreateRequest
56
import com.backgu.amaker.api.event.dto.response.ReactionEventDetailResponse
67
import com.backgu.amaker.api.event.dto.response.ReplyEventDetailResponse
78
import com.backgu.amaker.api.event.service.EventFacadeService
@@ -106,4 +107,25 @@ class EventController(
106107
).id,
107108
).toUri(),
108109
).build()
110+
111+
@PostMapping("/events/task")
112+
override fun createTaskEvent(
113+
@AuthenticationPrincipal token: JwtAuthentication,
114+
@PathVariable("chat-room-id") chatRoomId: Long,
115+
@RequestBody @Valid request: TaskEventCreateRequest,
116+
): ResponseEntity<Unit> =
117+
ResponseEntity
118+
.created(
119+
ServletUriComponentsBuilder
120+
.fromCurrentContextPath()
121+
.path("/api/v1/events/{id}/task")
122+
.buildAndExpand(
123+
eventFacadeService
124+
.createTaskEvent(
125+
token.id,
126+
chatRoomId,
127+
request.toDto(),
128+
).id,
129+
).toUri(),
130+
).build()
109131
}

api/src/main/kotlin/com/backgu/amaker/api/event/controller/EventSwagger.kt

+16-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.backgu.amaker.api.event.controller
22

33
import com.backgu.amaker.api.event.dto.request.ReactionEventCreateRequest
44
import com.backgu.amaker.api.event.dto.request.ReplyEventCreateRequest
5+
import com.backgu.amaker.api.event.dto.request.TaskEventCreateRequest
56
import com.backgu.amaker.api.event.dto.response.ReactionEventDetailResponse
67
import com.backgu.amaker.api.event.dto.response.ReplyEventDetailResponse
78
import com.backgu.amaker.common.http.response.ApiResult
@@ -14,7 +15,6 @@ import jakarta.validation.Valid
1415
import org.springframework.http.ResponseEntity
1516
import org.springframework.security.core.annotation.AuthenticationPrincipal
1617
import org.springframework.web.bind.annotation.PathVariable
17-
import org.springframework.web.bind.annotation.PostMapping
1818
import org.springframework.web.bind.annotation.RequestBody
1919

2020
@Tag(name = "event", description = "이벤트 API")
@@ -73,10 +73,24 @@ interface EventSwagger {
7373
),
7474
],
7575
)
76-
@PostMapping("/events/reaction")
7776
fun createReactionEvent(
7877
token: JwtAuthentication,
7978
chatRoomId: Long,
8079
request: ReactionEventCreateRequest,
8180
): ResponseEntity<Unit>
81+
82+
@Operation(summary = "task 이벤트 생성", description = "task 이벤트 생성합니다.")
83+
@ApiResponses(
84+
value = [
85+
ApiResponse(
86+
responseCode = "201",
87+
description = "task 이벤트 생성 성공",
88+
),
89+
],
90+
)
91+
fun createTaskEvent(
92+
token: JwtAuthentication,
93+
chatRoomId: Long,
94+
request: TaskEventCreateRequest,
95+
): ResponseEntity<Unit>
8296
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.backgu.amaker.api.event.dto
2+
3+
import java.time.LocalDateTime
4+
5+
data class TaskEventCreateDto(
6+
val eventTitle: String,
7+
val eventDetails: String,
8+
val assignees: List<String>,
9+
val deadLine: LocalDateTime,
10+
val notificationStartHour: Int,
11+
val notificationStartMinute: Int,
12+
val interval: Int,
13+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.backgu.amaker.api.event.dto
2+
3+
import com.backgu.amaker.domain.event.TaskEvent
4+
import java.time.LocalDateTime
5+
6+
data class TaskEventDto(
7+
val id: Long,
8+
val eventTitle: String,
9+
val deadLine: LocalDateTime,
10+
val notificationStartTime: LocalDateTime,
11+
val notificationInterval: Int,
12+
) {
13+
companion object {
14+
fun of(reactionEvent: TaskEvent) =
15+
TaskEventDto(
16+
id = reactionEvent.id,
17+
eventTitle = reactionEvent.eventTitle,
18+
deadLine = reactionEvent.deadLine,
19+
notificationStartTime = reactionEvent.notificationStartTime,
20+
notificationInterval = reactionEvent.notificationInterval,
21+
)
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.backgu.amaker.api.event.dto.request
2+
3+
import com.backgu.amaker.api.event.dto.TaskEventCreateDto
4+
import io.swagger.v3.oas.annotations.media.Schema
5+
import jakarta.validation.constraints.NotBlank
6+
import jakarta.validation.constraints.Size
7+
import org.springframework.format.annotation.DateTimeFormat
8+
import java.time.LocalDateTime
9+
10+
data class TaskEventCreateRequest(
11+
@Schema(description = "제목", example = "제목 어때요?")
12+
@field:NotBlank(message = "이벤트 제목을 입력해주세요.")
13+
val eventTitle: String,
14+
@Schema(description = "상세내용", example = "상세내용 어때요?")
15+
@field:NotBlank(message = "이벤트 내용을 입력해주세요.")
16+
val eventDetails: String,
17+
@Schema(description = "답변을 요청할 인원", example = "[\"user1\", \"user2\"]")
18+
@field:Size(min = 1, message = "최소 한 명 이상의 인원을 지정해야 합니다.")
19+
val assignees: List<String>,
20+
@Schema(description = "마감 기한", example = "2021-08-01T00:00:00")
21+
@DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
22+
val deadLine: LocalDateTime,
23+
@Schema(description = "알림 시작 시간", example = "1")
24+
val notificationStartHour: Int,
25+
@Schema(description = "알림 시작 분", example = "30")
26+
val notificationStartMinute: Int,
27+
@Schema(description = "알림 주기", example = "15")
28+
val interval: Int,
29+
) {
30+
fun toDto() =
31+
TaskEventCreateDto(
32+
eventTitle = eventTitle,
33+
eventDetails = eventDetails,
34+
assignees = assignees,
35+
deadLine = deadLine,
36+
notificationStartMinute = notificationStartMinute,
37+
notificationStartHour = notificationStartHour,
38+
interval = interval,
39+
)
40+
}

api/src/main/kotlin/com/backgu/amaker/api/event/service/EventFacadeService.kt

+43
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import com.backgu.amaker.api.event.dto.ReactionEventDto
77
import com.backgu.amaker.api.event.dto.ReplyEventCreateDto
88
import com.backgu.amaker.api.event.dto.ReplyEventDetailDto
99
import com.backgu.amaker.api.event.dto.ReplyEventDto
10+
import com.backgu.amaker.api.event.dto.TaskEventCreateDto
11+
import com.backgu.amaker.api.event.dto.TaskEventDto
1012
import com.backgu.amaker.api.user.dto.UserDto
1113
import com.backgu.amaker.application.chat.event.EventChatSaveEvent
1214
import com.backgu.amaker.application.chat.service.ChatRoomService
@@ -17,6 +19,7 @@ import com.backgu.amaker.application.event.service.EventService
1719
import com.backgu.amaker.application.event.service.ReactionEventService
1820
import com.backgu.amaker.application.event.service.ReactionOptionService
1921
import com.backgu.amaker.application.event.service.ReplyEventService
22+
import com.backgu.amaker.application.event.service.TaskEventService
2023
import com.backgu.amaker.application.user.service.UserService
2124
import com.backgu.amaker.application.workspace.WorkspaceUserService
2225
import com.backgu.amaker.common.exception.BusinessException
@@ -39,6 +42,7 @@ class EventFacadeService(
3942
private val chatRoomUserService: ChatRoomUserService,
4043
private val chatService: ChatService,
4144
private val replyEventService: ReplyEventService,
45+
private val taskEventService: TaskEventService,
4246
private val reactionEventService: ReactionEventService,
4347
private val reactionOptionService: ReactionOptionService,
4448
private val eventAssignedUserService: EventAssignedUserService,
@@ -164,6 +168,45 @@ class EventFacadeService(
164168
return ReplyEventDto.of(replyEvent)
165169
}
166170

171+
@Transactional
172+
fun createTaskEvent(
173+
userId: String,
174+
chatRoomId: Long,
175+
taskEventCreateDto: TaskEventCreateDto,
176+
): TaskEventDto {
177+
val user = userService.getById(userId)
178+
val chatRoom = chatRoomService.getById(chatRoomId)
179+
chatRoomUserService.validateUserInChatRoom(user, chatRoom)
180+
181+
val chat: Chat = chatService.save(chatRoom.createChat(user, taskEventCreateDto.eventTitle, ChatType.TASK))
182+
chatRoomService.save(chatRoom.updateLastChatId(chat))
183+
184+
val taskEvent =
185+
taskEventService.save(
186+
chat.createTaskEvent(
187+
taskEventCreateDto.deadLine,
188+
taskEventCreateDto.notificationStartHour,
189+
taskEventCreateDto.notificationStartMinute,
190+
taskEventCreateDto.interval,
191+
taskEventCreateDto.eventDetails,
192+
),
193+
)
194+
195+
val users = userService.getAllByUserEmails(taskEventCreateDto.assignees)
196+
chatRoomUserService.validateUsersInChatRoom(users, chatRoom)
197+
198+
eventAssignedUserService.saveAll(taskEvent.createAssignedUsers(users))
199+
200+
eventPublisher.publishEvent(
201+
EventChatSaveEvent.of(
202+
chatRoomId,
203+
chat.createEventChatWithUser(taskEvent, user, users),
204+
),
205+
)
206+
207+
return TaskEventDto.of(taskEvent)
208+
}
209+
167210
@Transactional
168211
fun createReactionEvent(
169212
userId: String,

domain/src/main/kotlin/com/backgu/amaker/domain/chat/Chat.kt

+19
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.backgu.amaker.domain.chat
33
import com.backgu.amaker.domain.event.Event
44
import com.backgu.amaker.domain.event.ReactionEvent
55
import com.backgu.amaker.domain.event.ReplyEvent
6+
import com.backgu.amaker.domain.event.TaskEvent
67
import com.backgu.amaker.domain.user.User
78
import java.time.LocalDateTime
89

@@ -15,6 +16,24 @@ class Chat(
1516
val createdAt: LocalDateTime = LocalDateTime.now(),
1617
val updatedAt: LocalDateTime = LocalDateTime.now(),
1718
) {
19+
fun createTaskEvent(
20+
deadLine: LocalDateTime,
21+
notificationStartHour: Int,
22+
notificationStartMinute: Int,
23+
notificationInterval: Int,
24+
eventDetails: String,
25+
) = TaskEvent(
26+
id = id,
27+
eventTitle = content,
28+
deadLine = deadLine,
29+
notificationStartTime =
30+
deadLine
31+
.minusHours(notificationStartHour.toLong())
32+
.minusMinutes(notificationStartMinute.toLong()),
33+
notificationInterval = notificationInterval,
34+
eventDetails = eventDetails,
35+
)
36+
1837
fun createReplyEvent(
1938
deadLine: LocalDateTime,
2039
notificationStartHour: Int,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.backgu.amaker.domain.event
2+
3+
import java.time.LocalDateTime
4+
5+
class TaskEvent(
6+
id: Long,
7+
eventTitle: String,
8+
val eventDetails: String,
9+
deadLine: LocalDateTime,
10+
notificationStartTime: LocalDateTime,
11+
notificationInterval: Int,
12+
createdAt: LocalDateTime = LocalDateTime.now(),
13+
updatedAt: LocalDateTime = LocalDateTime.now(),
14+
) : Event(id, eventTitle, deadLine, notificationStartTime, notificationInterval, createdAt, updatedAt) {
15+
companion object {
16+
const val EVENT_TYPE = "TASK"
17+
}
18+
19+
override fun getEventType(): String = EVENT_TYPE
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.backgu.amaker.application.event.service
2+
3+
import com.backgu.amaker.common.exception.BusinessException
4+
import com.backgu.amaker.common.status.StatusCode
5+
import com.backgu.amaker.domain.event.TaskEvent
6+
import com.backgu.amaker.infra.jpa.event.entity.TaskEventEntity
7+
import com.backgu.amaker.infra.jpa.event.repository.TaskEventRepository
8+
import org.springframework.data.repository.findByIdOrNull
9+
import org.springframework.stereotype.Service
10+
import org.springframework.transaction.annotation.Transactional
11+
12+
@Service
13+
@Transactional(readOnly = true)
14+
class TaskEventService(
15+
private val taskEventRepository: TaskEventRepository,
16+
) {
17+
@Transactional
18+
fun save(replyEvent: TaskEvent): TaskEvent = taskEventRepository.save(TaskEventEntity.of(replyEvent)).toDomain()
19+
20+
fun getById(id: Long): TaskEvent =
21+
taskEventRepository.findByIdOrNull(id)?.toDomain()
22+
?: throw BusinessException(StatusCode.EVENT_NOT_FOUND)
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.backgu.amaker.infra.jpa.event.entity
2+
3+
import com.backgu.amaker.domain.event.TaskEvent
4+
import jakarta.persistence.Column
5+
import jakarta.persistence.DiscriminatorValue
6+
import jakarta.persistence.Entity
7+
import jakarta.persistence.Table
8+
import java.time.LocalDateTime
9+
10+
@Entity(name = "TaskEvent")
11+
@Table(name = "task_event")
12+
@DiscriminatorValue(value = "TASK")
13+
class TaskEventEntity(
14+
@Column(nullable = false)
15+
val eventDetails: String,
16+
id: Long,
17+
eventTitle: String,
18+
deadLine: LocalDateTime,
19+
notificationStartTime: LocalDateTime,
20+
notificationInterval: Int,
21+
) : EventEntity(
22+
id = id,
23+
eventTitle = eventTitle,
24+
deadLine = deadLine,
25+
notificationStartTime = notificationStartTime,
26+
notificationInterval = notificationInterval,
27+
) {
28+
override fun toDomain(): TaskEvent =
29+
TaskEvent(
30+
id = id,
31+
eventTitle = eventTitle,
32+
eventDetails = eventDetails,
33+
deadLine = deadLine,
34+
notificationStartTime = notificationStartTime,
35+
notificationInterval = notificationInterval,
36+
createdAt = createdAt,
37+
updatedAt = updatedAt,
38+
)
39+
40+
companion object {
41+
fun of(taskEvent: TaskEvent) =
42+
TaskEventEntity(
43+
id = taskEvent.id,
44+
eventTitle = taskEvent.eventTitle,
45+
eventDetails = taskEvent.eventDetails,
46+
deadLine = taskEvent.deadLine,
47+
notificationStartTime = taskEvent.notificationStartTime,
48+
notificationInterval = taskEvent.notificationInterval,
49+
)
50+
}
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.backgu.amaker.infra.jpa.event.repository
2+
3+
import com.backgu.amaker.infra.jpa.event.entity.TaskEventEntity
4+
import org.springframework.data.jpa.repository.JpaRepository
5+
6+
interface TaskEventRepository : JpaRepository<TaskEventEntity, Long>

0 commit comments

Comments
 (0)