Skip to content

Commit 8659135

Browse files
authored
[DPMBE-101] 알림이 필요한 이벤트일때 FCM 메세지를 발송한다 (#185)
* move: event와 handler 패키지 이동 * style: handler 메서드명 통일 * feat: 이미지를 올렸을때 FCM 발송 * feat: 위치 공유 시작할때 FCM 발송 * feat: TIMEOVER 되었을때 FCM 발송 * feat: Interaction 전송 시 FCM 발송 * feat: fcm message send할 때 data값 추가 * feat: 위치 공유 종료 시 FCM 발송 * feat: Interaction 100개 달성 시 FCM 발송 * Interaction 100개 달성 시 이벤트 발행 로직 handler로 이동
1 parent a07eb92 commit 8659135

30 files changed

+509
-84
lines changed

Whatnow-Common/src/main/kotlin/com/depromeet/whatnow/consts/WhatNowStatic.kt

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const val DEV = "dev"
2626
const val LOCAL = "local"
2727
const val WITHDRAW_PREFIX = "withdraw"
2828
const val RADIUS_WAIT_CONFIRM = 200
29+
const val INTERACTION_FIXED_COUNT = 200L
2930
const val SLACK_MAX_LENGTH = 1000
3031

3132
const val NCP_LOCAL_SEARCH_DISPLAY_COUNT = 10

Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/domains/interaction/domain/Interaction.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ class Interaction(
2727
@Column(name = "interaction_id")
2828
val id: Long? = null,
2929
) : BaseTimeEntity() {
30-
fun increment() {
30+
fun increment(): Long {
3131
count += 1
32+
return count
3233
}
3334

3435
companion object {

Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/domains/interaction/handler/InteractionHistoryRegisterHandler.kt

-24
This file was deleted.

Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/domains/interaction/service/InteractionDomainService.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ class InteractionDomainService(
2424
identifier = "userId",
2525
)
2626
@CheckUserParticipation
27-
fun increment(promiseId: Long, userId: Long, interactionType: InteractionType) {
27+
fun increment(promiseId: Long, userId: Long, interactionType: InteractionType): Long {
2828
val interaction = interactionAdapter.queryInteraction(promiseId, userId, interactionType)
29-
interaction.increment()
29+
return interaction.increment()
3030
}
3131

3232
@CheckUserParticipation

Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/domains/interactionhistory/domain/InteractionHistory.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package com.depromeet.whatnow.domains.interactionhistory.domain
33
import com.depromeet.whatnow.common.BaseTimeEntity
44
import com.depromeet.whatnow.common.aop.event.Events
55
import com.depromeet.whatnow.domains.interaction.domain.InteractionType
6-
import com.depromeet.whatnow.domains.interactionhistory.event.InteractionHistoryRegisterEvent
6+
import com.depromeet.whatnow.events.domainEvent.InteractionHistoryRegisterEvent
77
import javax.persistence.Column
88
import javax.persistence.Entity
99
import javax.persistence.GeneratedValue
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.depromeet.whatnow.domains.notification.adapter
2+
3+
import com.depromeet.whatnow.annotation.Adapter
4+
import com.depromeet.whatnow.domains.notification.domain.Notification
5+
import com.depromeet.whatnow.domains.notification.repository.NotificationRepository
6+
7+
@Adapter
8+
class NotificationAdapter(
9+
val notificationRepository: NotificationRepository,
10+
) {
11+
fun save(notification: Notification): Notification {
12+
return notificationRepository.save(notification)
13+
}
14+
}

Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/domains/notification/domain/Notification.kt

+81-5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.depromeet.whatnow.domains.notification.domain
22

33
import com.depromeet.whatnow.common.BaseTimeEntity
44
import com.depromeet.whatnow.domains.interaction.domain.InteractionType
5+
import com.depromeet.whatnow.domains.promiseuser.domain.PromiseUserType
56
import javax.persistence.Column
67
import javax.persistence.ElementCollection
78
import javax.persistence.Entity
@@ -19,17 +20,92 @@ class Notification(
1920
var notificationType: NotificationType,
2021

2122
@Enumerated(EnumType.STRING)
22-
var interactionType: InteractionType,
23+
var interactionType: InteractionType?,
2324

24-
var userId: Long,
25+
@Enumerated(EnumType.STRING)
26+
var promiseUserType: PromiseUserType?,
27+
28+
var userId: Long?,
2529

2630
@ElementCollection
27-
var targetUserId: Set<Long>,
31+
var targetUserIds: Set<Long>,
2832

29-
var targetId: Long,
33+
var targetId: Long?,
3034

3135
@Id
3236
@GeneratedValue(strategy = GenerationType.IDENTITY)
3337
@Column(name = "notification_id")
3438
val id: Long? = null,
35-
) : BaseTimeEntity()
39+
) : BaseTimeEntity() {
40+
companion object {
41+
fun createForImage(userId: Long, targetUserId: Set<Long>, promiseImageId: Long): Notification {
42+
return Notification(
43+
notificationType = NotificationType.IMAGE,
44+
userId = userId,
45+
targetUserIds = targetUserId,
46+
targetId = promiseImageId,
47+
interactionType = null,
48+
promiseUserType = null,
49+
)
50+
}
51+
52+
fun createForStartSharing(targetUserIds: Set<Long>, promiseId: Long): Notification {
53+
return Notification(
54+
notificationType = NotificationType.START_SHARING,
55+
userId = null,
56+
targetUserIds = targetUserIds,
57+
targetId = promiseId,
58+
interactionType = null,
59+
promiseUserType = null,
60+
)
61+
}
62+
63+
fun createForEndSharing(targetUserIds: Set<Long>, promiseId: Long): Notification {
64+
return Notification(
65+
notificationType = NotificationType.END_SHARING,
66+
userId = null,
67+
targetUserIds = targetUserIds,
68+
targetId = promiseId,
69+
interactionType = null,
70+
promiseUserType = null,
71+
)
72+
}
73+
74+
fun createForTimeOver(targetUserIds: Set<Long>, promiseId: Long): Notification {
75+
return Notification(
76+
notificationType = NotificationType.TIMEOVER,
77+
userId = null,
78+
targetUserIds = targetUserIds,
79+
targetId = promiseId,
80+
interactionType = null,
81+
promiseUserType = null,
82+
)
83+
}
84+
85+
fun createForInteraction(userId: Long, targetUserId: Long, interactionType: InteractionType): Notification {
86+
return Notification(
87+
notificationType = NotificationType.INTERACTION,
88+
userId = userId,
89+
targetUserIds = mutableSetOf(targetUserId),
90+
targetId = null,
91+
interactionType = interactionType,
92+
promiseUserType = null,
93+
)
94+
}
95+
96+
fun createForInteractionAttainment(
97+
userId: Long,
98+
senderUserIds: Set<Long>,
99+
interactionType: InteractionType,
100+
): Notification {
101+
return Notification(
102+
notificationType = NotificationType.INTERACTION_ATTAINMENT,
103+
userId = userId,
104+
targetUserIds = senderUserIds,
105+
targetId = null,
106+
interactionType = interactionType,
107+
promiseUserType = null,
108+
)
109+
}
110+
}
111+
}

Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/domains/notification/domain/NotificationType.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ enum class NotificationType(val kr: String) {
55
ARRIVAL("도착"),
66
TIMEOVER("시간 초과"),
77
INTERACTION("인터렉션"),
8-
PICTURE("사진"),
8+
INTERACTION_ATTAINMENT("인터렉션 달성"),
9+
IMAGE("이미지"),
910
START_SHARING("공유 시작"),
1011
END_SHARING("공유 종료"),
1112
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.depromeet.whatnow.domains.notification.repository
2+
3+
import com.depromeet.whatnow.domains.notification.domain.Notification
4+
import org.springframework.data.jpa.repository.JpaRepository
5+
6+
interface NotificationRepository : JpaRepository<Notification, Long>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.depromeet.whatnow.domains.notification.service
2+
3+
import com.depromeet.whatnow.domains.interaction.domain.InteractionType
4+
import com.depromeet.whatnow.domains.notification.adapter.NotificationAdapter
5+
import com.depromeet.whatnow.domains.notification.domain.Notification
6+
import org.springframework.stereotype.Service
7+
import org.springframework.transaction.annotation.Transactional
8+
9+
@Service
10+
class NotificationDomainService(
11+
val notificationAdapter: NotificationAdapter,
12+
) {
13+
@Transactional
14+
fun saveForImage(userId: Long, targetUserId: Set<Long>, promiseImageId: Long) {
15+
notificationAdapter.save(Notification.createForImage(userId, targetUserId, promiseImageId))
16+
}
17+
18+
@Transactional
19+
fun saveForStartSharing(targetUserIds: Set<Long>, promiseId: Long) {
20+
notificationAdapter.save(Notification.createForStartSharing(targetUserIds, promiseId))
21+
}
22+
23+
@Transactional
24+
fun saveForEndSharing(targetUserIds: Set<Long>, promiseId: Long) {
25+
notificationAdapter.save(Notification.createForEndSharing(targetUserIds, promiseId))
26+
}
27+
28+
@Transactional
29+
fun saveForTimeOver(targetUserIds: Set<Long>, promiseId: Long) {
30+
notificationAdapter.save(Notification.createForTimeOver(targetUserIds, promiseId))
31+
}
32+
33+
@Transactional
34+
fun saveForInteraction(userId: Long, targetUserId: Long, interactionType: InteractionType) {
35+
notificationAdapter.save(Notification.createForInteraction(userId, targetUserId, interactionType))
36+
}
37+
38+
@Transactional
39+
fun saveForInteractionAttainment(userId: Long, senderUserIds: Set<Long>, interactionType: InteractionType) {
40+
notificationAdapter.save(Notification.createForInteractionAttainment(userId, senderUserIds, interactionType))
41+
}
42+
}

Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/domains/user/domain/User.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import com.depromeet.whatnow.consts.USER_DEFAULT_PROFILE_IMAGE
88
import com.depromeet.whatnow.domains.user.exception.AlreadyDeletedUserException
99
import com.depromeet.whatnow.domains.user.exception.ForbiddenUserException
1010
import com.depromeet.whatnow.events.domainEvent.UserProfileImageUpdatedEvent
11-
import com.depromeet.whatnow.events.domainEvent.UserSignUpEvent
11+
import com.depromeet.whatnow.events.domainEvent.UserRegisterEvent
1212
import java.time.LocalDateTime
1313
import javax.persistence.Column
1414
import javax.persistence.Embedded
@@ -52,7 +52,7 @@ class User(
5252

5353
@PostPersist
5454
fun registerEvent() {
55-
Events.raise(UserSignUpEvent(this.id!!))
55+
Events.raise(UserRegisterEvent(this.id!!))
5656
}
5757

5858
fun login(fcmToken: String) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.depromeet.whatnow.events.domainEvent
2+
3+
import com.depromeet.whatnow.common.aop.event.DomainEvent
4+
import com.depromeet.whatnow.domains.interaction.domain.InteractionType
5+
6+
class InteractionFixedEvent(
7+
val promiseId: Long,
8+
val userId: Long,
9+
val interactionType: InteractionType,
10+
) : DomainEvent()
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.depromeet.whatnow.domains.interactionhistory.event
1+
package com.depromeet.whatnow.events.domainEvent
22

33
import com.depromeet.whatnow.common.aop.event.DomainEvent
44
import com.depromeet.whatnow.domains.interaction.domain.InteractionType

Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/events/domainEvent/UserSignUpEvent.kt renamed to Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/events/domainEvent/UserRegisterEvent.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ package com.depromeet.whatnow.events.domainEvent
22

33
import com.depromeet.whatnow.common.aop.event.DomainEvent
44

5-
data class UserSignUpEvent(
5+
data class UserRegisterEvent(
66
val userId: Long,
77
) : DomainEvent()
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,70 @@
11
package com.depromeet.whatnow.events.handler
22

33
import com.depromeet.whatnow.annotation.Handler
4+
import com.depromeet.whatnow.config.fcm.FcmService
5+
import com.depromeet.whatnow.domains.notification.domain.NotificationType
6+
import com.depromeet.whatnow.domains.notification.service.NotificationDomainService
7+
import com.depromeet.whatnow.domains.promiseuser.adaptor.PromiseUserAdaptor
8+
import com.depromeet.whatnow.domains.promiseuser.domain.PromiseUserType.LATE
9+
import com.depromeet.whatnow.domains.promiseuser.domain.PromiseUserType.WAIT
10+
import com.depromeet.whatnow.domains.user.adapter.UserAdapter
411
import com.depromeet.whatnow.events.domainEvent.PromiseImageRegisterEvent
512
import org.springframework.scheduling.annotation.Async
613
import org.springframework.transaction.event.TransactionPhase
714
import org.springframework.transaction.event.TransactionalEventListener
815

916
@Handler
10-
class ImageRegisterEventHandler {
17+
class ImageRegisterEventHandler(
18+
val promiseUserAdapter: PromiseUserAdaptor,
19+
val userAdapter: UserAdapter,
20+
val fcmService: FcmService,
21+
val notificationDomainService: NotificationDomainService,
22+
) {
1123
@Async
1224
@TransactionalEventListener(classes = [PromiseImageRegisterEvent::class], phase = TransactionPhase.AFTER_COMMIT)
13-
fun handleRegisterPictureEvent(promiseImageRegisterEvent: PromiseImageRegisterEvent) {
25+
fun handlePromiseImageRegisterEvent(promiseImageRegisterEvent: PromiseImageRegisterEvent) {
1426
val userId: Long = promiseImageRegisterEvent.userId
1527
val promiseId: Long = promiseImageRegisterEvent.promiseId
16-
// TODO: FCM 토큰 발급 후 약속ID 기준 참여자들에게 푸시 알림 보내기
28+
29+
// 사진을 보낸 유저가 Late인지 Wait인지 확인하기 위한 PromiseUser 조회
30+
val promiseUser = promiseUserAdapter.findByPromiseIdAndUserId(promiseId, userId)
31+
32+
// 약속에 참여한 유저들 조회 (사진 보낸 유저 제외)
33+
val usersExcludingSelf = promiseUserAdapter.findByPromiseId(promiseId)
34+
.filter { promiseUser -> promiseUser.userId != userId }
35+
.map { promiseUser -> userAdapter.queryUser(promiseUser.userId) }
36+
37+
// 앱 알람 허용한 유저
38+
val appAlarmPermitUsers = usersExcludingSelf.filter { user ->
39+
user.fcmNotification.fcmToken != null && user.fcmNotification.appAlarm
40+
}
41+
42+
val data: MutableMap<String, String> = mutableMapOf()
43+
data["notificationType"] = NotificationType.IMAGE.name
44+
data["promiseId"] = promiseId.toString()
45+
46+
// 앱 알람 허용한 유저에게 알람 보내기
47+
when (promiseUser.promiseUserType) {
48+
LATE -> {
49+
fcmService.sendGroupMessageAsync(
50+
appAlarmPermitUsers.map { user -> user.fcmNotification.fcmToken!! },
51+
"지각한 친구의 사진 도착",
52+
"지각한 친구가 보낸 사진을 확인해봐!",
53+
data,
54+
)
55+
}
56+
WAIT -> {
57+
fcmService.sendGroupMessageAsync(
58+
appAlarmPermitUsers.map { user -> user.fcmNotification.fcmToken!! },
59+
"도착한 친구들의 사진 도착",
60+
"도착한 친구들이 보낸 사진을 확인해봐!",
61+
data,
62+
)
63+
}
64+
}
65+
66+
// notification 저장
67+
val targetUserIds = usersExcludingSelf.map { user -> user.id!! }.toSet()
68+
notificationDomainService.saveForImage(userId, targetUserIds, promiseId)
1769
}
1870
}

0 commit comments

Comments
 (0)