Skip to content

Commit 4472f35

Browse files
authored
Merge pull request #75 from soma-baekgu/feature/BG-342-application-domain
[BG-342]: notification 도메인 세팅 (4h / 3h)
2 parents 82f1bd6 + e30bc50 commit 4472f35

File tree

53 files changed

+539
-271
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+539
-271
lines changed

api/src/main/kotlin/com/backgu/amaker/api/workspace/event/WorkspaceInvitedEvent.kt

-16
This file was deleted.

api/src/main/kotlin/com/backgu/amaker/api/workspace/event/WorkspaceJoinedEvent.kt

-16
This file was deleted.

api/src/main/kotlin/com/backgu/amaker/api/workspace/service/WorkspaceFacadeService.kt

+30-9
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ import com.backgu.amaker.api.workspace.dto.WorkspaceCreateDto
77
import com.backgu.amaker.api.workspace.dto.WorkspaceDto
88
import com.backgu.amaker.api.workspace.dto.WorkspaceUserDto
99
import com.backgu.amaker.api.workspace.dto.WorkspacesDto
10-
import com.backgu.amaker.api.workspace.event.WorkspaceInvitedEvent
11-
import com.backgu.amaker.api.workspace.event.WorkspaceJoinedEvent
1210
import com.backgu.amaker.application.chat.service.ChatRoomService
1311
import com.backgu.amaker.application.chat.service.ChatRoomUserService
1412
import com.backgu.amaker.application.notification.service.NotificationEventService
@@ -18,6 +16,8 @@ import com.backgu.amaker.application.workspace.WorkspaceUserService
1816
import com.backgu.amaker.common.exception.BusinessException
1917
import com.backgu.amaker.common.status.StatusCode
2018
import com.backgu.amaker.domain.chat.ChatRoom
19+
import com.backgu.amaker.domain.notifiacation.workspace.WorkspaceInvited
20+
import com.backgu.amaker.domain.notifiacation.workspace.WorkspaceJoined
2121
import com.backgu.amaker.domain.user.User
2222
import com.backgu.amaker.domain.workspace.Workspace
2323
import org.springframework.stereotype.Service
@@ -41,18 +41,17 @@ class WorkspaceFacadeService(
4141
val leader: User = userService.getById(userId)
4242
val workspace: Workspace = workspaceService.save(leader.createWorkspace(workspaceCreateDto.name))
4343
workspaceUserService.save(workspace.assignLeader(leader))
44-
notificationEventService.publishNotificationEvent(WorkspaceJoinedEvent(leader, workspace))
44+
notificationEventService.publishNotificationEvent(WorkspaceJoined.of(workspace, leader))
4545

4646
val invitees = workspaceCreateDto.inviteesEmails.map { userService.getByEmail(it) }
4747
invitees.forEach {
4848
if (!leader.isNonInvitee(it)) throw BusinessException(StatusCode.INVALID_WORKSPACE_CREATE)
4949
workspaceUserService.save(workspace.inviteWorkspace(it))
50-
notificationEventService.publishNotificationEvent(WorkspaceInvitedEvent(it, workspace))
50+
notificationEventService.publishNotificationEvent(WorkspaceInvited.of(workspace, it))
5151
}
5252

5353
val chatRoom: ChatRoom = chatRoomService.save(workspace.createDefaultChatRoom())
5454
chatRoomUserService.save(chatRoom.addUser(leader))
55-
workspaceService.save(workspace)
5655

5756
return WorkspaceDto.of(workspace)
5857
}
@@ -98,11 +97,33 @@ class WorkspaceFacadeService(
9897
if (!workspace.isAvailToJoin()) throw BusinessException(StatusCode.INVALID_WORKSPACE_JOIN)
9998

10099
val workspaceUser = workspaceUserService.getWorkspaceUser(workspace, user)
101-
notificationEventService.publishNotificationEvent(WorkspaceJoinedEvent(user, workspace))
102100
if (workspaceUser.isActivated()) throw BusinessException(StatusCode.ALREADY_JOINED_WORKSPACE)
103101

104102
// TODO 트랜잭션 종료시점에 이벤트 publish
105-
notificationEventService.publishNotificationEvent(WorkspaceJoinedEvent(user, workspace))
103+
notificationEventService.publishNotificationEvent(WorkspaceJoined.of(workspace, user))
104+
workspaceUserService.save(workspaceUser.activate())
105+
106+
chatRoomUserService.save(chatRoomService.getDefaultChatRoomByWorkspaceId(workspaceId).addUser(user))
107+
workspaceService.updateBelonging(workspace.increaseMember())
108+
109+
return WorkspaceUserDto.of(user.email, workspaceUser)
110+
}
111+
112+
@Transactional
113+
fun activateWorkspaceUserWithPessimisticLock(
114+
userId: String,
115+
workspaceId: Long,
116+
): WorkspaceUserDto {
117+
val user = userService.getById(userId)
118+
119+
val workspace = workspaceService.getByIdWithPessimisticLock(workspaceId)
120+
if (!workspace.isAvailToJoin()) throw BusinessException(StatusCode.INVALID_WORKSPACE_JOIN)
121+
122+
val workspaceUser = workspaceUserService.getWorkspaceUser(workspace, user)
123+
if (workspaceUser.isActivated()) throw BusinessException(StatusCode.ALREADY_JOINED_WORKSPACE)
124+
125+
// TODO 트랜잭션 종료시점에 이벤트 publish
126+
notificationEventService.publishNotificationEvent(WorkspaceJoined.of(workspace, user))
106127
workspaceUserService.save(workspaceUser.activate())
107128

108129
chatRoomUserService.save(chatRoomService.getDefaultChatRoomByWorkspaceId(workspaceId).addUser(user))
@@ -126,7 +147,7 @@ class WorkspaceFacadeService(
126147
if (workspaceUser.isActivated()) throw BusinessException(StatusCode.ALREADY_JOINED_WORKSPACE)
127148

128149
// TODO 트랜잭션 종료시점에 이벤트 publish
129-
notificationEventService.publishNotificationEvent(WorkspaceJoinedEvent(user, workspace))
150+
notificationEventService.publishNotificationEvent(WorkspaceJoined.of(workspace, user))
130151
workspaceUserService.save(workspaceUser.activate())
131152

132153
chatRoomUserService.save(chatRoomService.getDefaultChatRoomByWorkspaceId(workspaceId).addUser(user))
@@ -149,7 +170,7 @@ class WorkspaceFacadeService(
149170
workspaceUserService.validateUserNotRelatedInWorkspace(invitee, workspace)
150171

151172
val workspaceUser = workspaceUserService.save(workspace.inviteWorkspace(invitee))
152-
notificationEventService.publishNotificationEvent(WorkspaceInvitedEvent(invitee, workspace))
173+
notificationEventService.publishNotificationEvent(WorkspaceInvited.of(workspace, invitee))
153174

154175
return WorkspaceUserDto.of(invitee.email, workspaceUser)
155176
}

api/src/test/kotlin/com/backgu/amaker/api/workspace/service/WorkspaceFacadeServiceTest.kt

+1-3
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,10 @@ import org.junit.jupiter.api.BeforeEach
1818
import org.junit.jupiter.api.DisplayName
1919
import org.junit.jupiter.api.Test
2020
import org.springframework.beans.factory.annotation.Autowired
21-
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
2221
import org.springframework.transaction.annotation.Transactional
2322

2423
@DisplayName("WorkspaceFacadeService 테스트")
2524
@Transactional
26-
@AutoConfigureMockMvc
2725
class WorkspaceFacadeServiceTest : IntegrationTest() {
2826
@Autowired
2927
private lateinit var workspaceFixtureFacade: WorkspaceFixtureFacade
@@ -204,7 +202,6 @@ class WorkspaceFacadeServiceTest : IntegrationTest() {
204202
@DisplayName("워크스페이스 유저 활성화 실패")
205203
fun activateWorkspaceUserLimitedUser() {
206204
// given
207-
208205
val leaderId = "leader"
209206
fixtures.user.createPersistedUser(leaderId)
210207
val workspace = fixtures.workspace.createPersistedWorkspace(name = "워크스페이스")
@@ -234,6 +231,7 @@ class WorkspaceFacadeServiceTest : IntegrationTest() {
234231
.isEqualTo(StatusCode.INVALID_WORKSPACE_JOIN)
235232
}
236233

234+
@Test
237235
@DisplayName("워크스페이스의 기본 채팅방을 조회")
238236
fun getDefaultChatRoom() {
239237
// given

batch/src/main/kotlin/com/backgu/amaker/batch/component/EventNotificationComponent.kt

+15-40
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ package com.backgu.amaker.batch.component
22

33
import com.backgu.amaker.application.notification.service.NotificationEventService
44
import com.backgu.amaker.batch.dto.EventNotificationCreateDto
5-
import com.backgu.amaker.batch.dto.EventNotificationEvent
6-
import com.backgu.amaker.domain.notifiacation.EventNotificationType
5+
import com.backgu.amaker.domain.notifiacation.Notification
6+
import com.backgu.amaker.domain.notifiacation.event.EventNotCompleted
7+
import com.backgu.amaker.domain.notifiacation.event.EventOverdue
78
import com.backgu.amaker.infra.jpa.notification.entity.EventNotificationEntity
89
import jakarta.persistence.EntityManagerFactory
910
import org.springframework.batch.core.Job
@@ -128,34 +129,22 @@ class EventNotificationComponent(
128129
?.isBefore(now) ?: false
129130

130131
if (shouldSendNotification) {
131-
val eventNotificationType =
132-
if (item.deadline.isBefore(now)) EventNotificationType.OVERDUE else EventNotificationType.NOT_COMPLETED
133-
134-
notificationEventService.publishNotificationEvent(
135-
EventNotificationEvent(
136-
email = item.email,
137-
title = eventNotificationType.title,
138-
content =
139-
getFormattedMessage(
140-
eventType = eventNotificationType,
141-
eventTitle = item.eventTitle,
142-
duration = Duration.between(now, item.deadline),
143-
),
144-
notificationType = eventNotificationType,
145-
),
146-
)
132+
val notification: Notification =
133+
if (item.deadline.isBefore(now)) {
134+
val eventOverdue = EventOverdue.of(item.userId, Duration.between(item.deadline, now))
135+
notificationEventService.publishNotificationEvent(eventOverdue)
136+
eventOverdue
137+
} else {
138+
val eventNotCompleted = EventNotCompleted.of(item.userId, Duration.between(now, item.deadline))
139+
notificationEventService.publishNotificationEvent(eventNotCompleted)
140+
eventNotCompleted
141+
}
147142

148143
EventNotificationEntity(
149-
title = eventNotificationType.title,
150-
content =
151-
getFormattedMessage(
152-
eventType = eventNotificationType,
153-
eventTitle = item.eventTitle,
154-
duration = Duration.between(now, item.deadline),
155-
),
144+
title = notification.method.title,
145+
content = notification.method.message,
156146
userId = item.userId,
157147
eventId = item.eventId,
158-
eventNotificationType = eventNotificationType,
159148
)
160149
} else {
161150
null
@@ -170,18 +159,4 @@ class EventNotificationComponent(
170159
JpaItemWriterBuilder<EventNotificationEntity>()
171160
.entityManagerFactory(entityManagerFactory)
172161
.build()
173-
174-
private fun getFormattedMessage(
175-
eventType: EventNotificationType,
176-
eventTitle: String,
177-
duration: Duration,
178-
): String =
179-
if (eventType == EventNotificationType.NOT_COMPLETED) {
180-
val hours = duration.toHours()
181-
val minutes = duration.toMinutes() % 60
182-
val timeLeft = if (hours > 0) "${hours}시간 ${minutes}" else "${minutes}"
183-
String.format(eventType.message, eventTitle, timeLeft)
184-
} else {
185-
String.format(eventType.message, eventTitle)
186-
}
187162
}

batch/src/main/kotlin/com/backgu/amaker/batch/dto/EventNotificationEvent.kt

-24
This file was deleted.

batch/src/main/resources/application.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
spring:
22
application:
33
name: a-maker
4+
main:
5+
web-application-type: none
46

57
batch-datasource:
68
url: jdbc:mysql://localhost:3306/amaker_batch?serverTimezone=Asia/Seoul&useSSL=false&allowPublicKeyRetrieval=true

domain/src/main/kotlin/com/backgu/amaker/domain/notifiacation/EventNotificationType.kt

-9
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.backgu.amaker.domain.notifiacation
2+
3+
import com.backgu.amaker.domain.notifiacation.method.NotificationMethod
4+
import java.io.Serializable
5+
6+
interface Notification : Serializable {
7+
val method: NotificationMethod
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.backgu.amaker.domain.notifiacation
2+
3+
import com.backgu.amaker.domain.notifiacation.method.RealTimeNotificationMethod
4+
5+
abstract class RealTimeBasedNotification(
6+
override val method: RealTimeNotificationMethod,
7+
) : Notification
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.backgu.amaker.domain.notifiacation
2+
3+
import com.backgu.amaker.domain.notifiacation.method.RealTimeNotificationMethod
4+
import com.backgu.amaker.domain.user.User
5+
6+
class UserFulfilledNotification(
7+
val user: User,
8+
override val method: RealTimeNotificationMethod,
9+
) : RealTimeBasedNotification(method)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.backgu.amaker.domain.notifiacation
2+
3+
import com.backgu.amaker.domain.notifiacation.method.RealTimeNotificationMethod
4+
5+
open class UserNotification(
6+
val userId: String,
7+
override val method: RealTimeNotificationMethod,
8+
) : RealTimeBasedNotification(method)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.backgu.amaker.domain.notifiacation
2+
3+
import com.backgu.amaker.domain.notifiacation.method.RealTimeNotificationMethod
4+
5+
open class WorkspaceNotification(
6+
open val workspaceId: Long,
7+
override val method: RealTimeNotificationMethod,
8+
) : RealTimeBasedNotification(method)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.backgu.amaker.domain.notifiacation.event
2+
3+
import com.backgu.amaker.domain.notifiacation.UserNotification
4+
import com.backgu.amaker.domain.notifiacation.method.TemplateEmailNotificationMethod
5+
import com.backgu.amaker.domain.notifiacation.type.UserNotificationType.NOT_COMPLETED
6+
import java.time.Duration
7+
8+
class EventNotCompleted private constructor(
9+
userId: String,
10+
method: TemplateEmailNotificationMethod,
11+
) : UserNotification(
12+
userId,
13+
method,
14+
) {
15+
companion object {
16+
private fun buildDetailMessage(): String = "이벤트가 완료되지 않았습니다."
17+
18+
private fun getFormattedMessage(
19+
eventTitle: String,
20+
eventMessage: String,
21+
duration: Duration,
22+
): String {
23+
val hours = duration.toHours()
24+
val minutes = duration.toMinutes() % 60
25+
val timeLeft = if (hours > 0) "${hours}시간 ${minutes}" else "${minutes}"
26+
return String.format(eventMessage, eventTitle, timeLeft)
27+
}
28+
29+
fun of(
30+
userId: String,
31+
duration: Duration,
32+
): EventNotCompleted =
33+
EventNotCompleted(
34+
userId,
35+
TemplateEmailNotificationMethod(
36+
NOT_COMPLETED.title,
37+
getFormattedMessage(NOT_COMPLETED.title, NOT_COMPLETED.message, duration),
38+
buildDetailMessage(),
39+
"event-notification",
40+
),
41+
)
42+
}
43+
}

0 commit comments

Comments
 (0)