Skip to content

Commit f4d1e07

Browse files
authored
[DPMBE-127] 알림 하이라이트를 조회한다 (#228)
* feat: 알림 하이라이트를 조회한다 * fix: 하이라이트조회 endpoint PathVariable 파라미터명 변경 promiseId -> promsie-id * fix: PageDefault에 size=10 명시적 표기 * style: spotless
1 parent 8cba323 commit f4d1e07

File tree

14 files changed

+148
-16
lines changed

14 files changed

+148
-16
lines changed

Whatnow-Api/src/main/kotlin/com/depromeet/whatnow/api/notification/controller/NotificationController.kt

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.depromeet.whatnow.api.notification.controller
22

3+
import com.depromeet.whatnow.api.notification.dto.HighlightsResponse
34
import com.depromeet.whatnow.api.notification.dto.NotificationResponse
5+
import com.depromeet.whatnow.api.notification.usecase.NotificationHighlightsReadUseCase
46
import com.depromeet.whatnow.api.notification.usecase.NotificationReadUseCase
57
import io.swagger.v3.oas.annotations.Operation
68
import io.swagger.v3.oas.annotations.security.SecurityRequirement
@@ -9,6 +11,7 @@ import org.springdoc.api.annotations.ParameterObject
911
import org.springframework.data.domain.Pageable
1012
import org.springframework.data.web.PageableDefault
1113
import org.springframework.web.bind.annotation.GetMapping
14+
import org.springframework.web.bind.annotation.PathVariable
1215
import org.springframework.web.bind.annotation.RequestMapping
1316
import org.springframework.web.bind.annotation.RestController
1417

@@ -18,13 +21,26 @@ import org.springframework.web.bind.annotation.RestController
1821
@SecurityRequirement(name = "access-token")
1922
class NotificationController(
2023
val notificationReadUseCase: NotificationReadUseCase,
24+
val notificationHighlightsReadUseCase: NotificationHighlightsReadUseCase,
2125
) {
2226
@Operation(summary = "자신의 알림 조회")
2327
@GetMapping
2428
fun getMyNotifications(
25-
@ParameterObject @PageableDefault
29+
@ParameterObject
30+
@PageableDefault(size = 10)
2631
pageable: Pageable,
2732
): NotificationResponse {
2833
return notificationReadUseCase.execute(pageable)
2934
}
35+
36+
@Operation(summary = "하이라이트 조회", description = "약속 ID로 하이라이트를 조회합니다")
37+
@GetMapping("/highlights/promises/{promise-id}")
38+
fun getHighlights(
39+
@PathVariable("promise-id") promiseId: Long,
40+
@ParameterObject
41+
@PageableDefault(size = 10)
42+
pageable: Pageable,
43+
): HighlightsResponse {
44+
return notificationHighlightsReadUseCase.execute(promiseId, pageable)
45+
}
3046
}

Whatnow-Api/src/main/kotlin/com/depromeet/whatnow/api/notification/dto/ArrivalNotificationResponse.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class ArrivalNotificationResponse(
88
val promiseId: Long,
99
val senderUserId: Long,
1010
override val createdAt: LocalDateTime,
11-
) : NotificationAbstract(NotificationType.START_SHARING, createdAt) {
11+
) : NotificationAbstract(NotificationType.ARRIVAL, createdAt) {
1212
companion object {
1313
fun from(notification: ArrivalNotification): ArrivalNotificationResponse {
1414
return ArrivalNotificationResponse(

Whatnow-Api/src/main/kotlin/com/depromeet/whatnow/api/notification/dto/EndSharingNotificationResponse.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import java.time.LocalDateTime
77
class EndSharingNotificationResponse(
88
val promiseId: Long,
99
override val createdAt: LocalDateTime,
10-
) : NotificationAbstract(NotificationType.START_SHARING, createdAt) {
10+
) : NotificationAbstract(NotificationType.END_SHARING, createdAt) {
1111
companion object {
1212
fun from(notification: EndSharingNotification): EndSharingNotificationResponse {
1313
return EndSharingNotificationResponse(
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.depromeet.whatnow.api.notification.dto
2+
3+
data class HighlightsResponse(
4+
val highlights: List<NotificationAbstract>,
5+
)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.depromeet.whatnow.api.notification.usecase
2+
3+
import com.depromeet.whatnow.annotation.UseCase
4+
import com.depromeet.whatnow.api.notification.dto.ArrivalNotificationResponse
5+
import com.depromeet.whatnow.api.notification.dto.HighlightsResponse
6+
import com.depromeet.whatnow.config.security.SecurityUtils
7+
import com.depromeet.whatnow.domains.notification.domain.ArrivalNotification
8+
import com.depromeet.whatnow.domains.notification.exception.NotHighlightsTypeException
9+
import com.depromeet.whatnow.domains.notification.service.NotificationDomainService
10+
import org.springframework.data.domain.Pageable
11+
12+
@UseCase
13+
class NotificationHighlightsReadUseCase(
14+
val notificationDomainService: NotificationDomainService,
15+
) {
16+
fun execute(promiseId: Long, pageable: Pageable): HighlightsResponse {
17+
val userId = SecurityUtils.currentUserId
18+
val highlights = notificationDomainService.getNotificationHighlights(promiseId, userId, pageable)
19+
.map { notification ->
20+
when (notification) {
21+
is ArrivalNotification -> {
22+
ArrivalNotificationResponse.from(notification)
23+
}
24+
// TODO: 만났다 이벤트 생성 된 이후 MEET Type Notification 추가
25+
else -> throw NotHighlightsTypeException.EXCEPTION
26+
}
27+
}.toList()
28+
return HighlightsResponse(highlights)
29+
}
30+
31+
fun executeTop3(promiseId: Long): HighlightsResponse {
32+
val listMap = notificationDomainService.getNotificationHighlightsTop3(promiseId)
33+
.map { notification ->
34+
when (notification) {
35+
is ArrivalNotification ->
36+
ArrivalNotificationResponse.from(notification)
37+
// TODO: 만났다 이벤트 생성 된 이후 MEET Type Notification 추가
38+
else -> throw NotHighlightsTypeException.EXCEPTION
39+
}
40+
}
41+
.groupBy { it.senderUserId }
42+
43+
val result = listMap.values.map { it.first() }
44+
return HighlightsResponse(result.sortedByDescending { it.createdAt })
45+
}
46+
}

Whatnow-Api/src/main/kotlin/com/depromeet/whatnow/api/promise/dto/PromiseDetailDto.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.depromeet.whatnow.api.promise.dto
22

3+
import com.depromeet.whatnow.api.notification.dto.HighlightsResponse
34
import com.depromeet.whatnow.common.vo.CoordinateVo
45
import com.depromeet.whatnow.domains.promise.domain.Promise
56
import java.time.LocalDateTime
@@ -12,18 +13,17 @@ data class PromiseDetailDto(
1213
val endTime: LocalDateTime,
1314
// 유저의 마지막 위치 리스트
1415
val promiseUsers: List<PromiseUserInfoVo>,
15-
// TODO : 약속 기록 사진 기능 추가시 함께 추가할게요.
1616
val promiseImageUrls: List<String>?,
1717
val timeOverLocations: List<LocationCapture>,
18-
// TODO : highlight 기능 추가시 함께 추가할게요. ( 최대 3개 제한 )
19-
// x val highlights: List<NotificationDto>,
18+
val highlights: HighlightsResponse,
2019
) {
2120
companion object {
2221
fun of(
2322
promise: Promise,
2423
promiseUsers: List<PromiseUserInfoVo>,
2524
promiseImageUrls: List<String>,
2625
timeOverLocations: List<LocationCapture>,
26+
highlights: HighlightsResponse,
2727
): PromiseDetailDto {
2828
return PromiseDetailDto(
2929
promiseId = promise.id!!,
@@ -34,6 +34,7 @@ data class PromiseDetailDto(
3434
promiseUsers = promiseUsers,
3535
promiseImageUrls = promiseImageUrls,
3636
timeOverLocations = timeOverLocations,
37+
highlights = highlights,
3738
)
3839
}
3940
}

Whatnow-Api/src/main/kotlin/com/depromeet/whatnow/api/promise/usecase/PromiseReadUseCase.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.depromeet.whatnow.api.promise.usecase
22

33
import com.depromeet.whatnow.annotation.UseCase
44
import com.depromeet.whatnow.api.interaction.dto.InteractionDto
5+
import com.depromeet.whatnow.api.notification.usecase.NotificationHighlightsReadUseCase
56
import com.depromeet.whatnow.api.promise.dto.LocationCapture
67
import com.depromeet.whatnow.api.promise.dto.PromiseDetailDto
78
import com.depromeet.whatnow.api.promise.dto.PromiseFindDto
@@ -29,6 +30,7 @@ class PromiseReadUseCase(
2930
val userRepository: UserRepository,
3031
val interactionAdapter: InteractionAdapter,
3132
val promiseImageAdapter: PromiseImageAdapter,
33+
val notificationHighlightsReadUseCase: NotificationHighlightsReadUseCase,
3234
) {
3335
/**
3436
* method desc: 유저가 참여한 약속들을 약속 종류(BEFORE, PAST)에 따라 분리해서 조회
@@ -127,6 +129,8 @@ class PromiseReadUseCase(
127129
.sortedByDescending { it.createdAt }
128130
.map { it.uri }
129131

132+
val highlights = notificationHighlightsReadUseCase.executeTop3(promise.id!!)
133+
130134
val timeOverLocations = promiseUsers.mapNotNull { promiseUser ->
131135
promiseUser.userLocation?.let { location ->
132136
LocationCapture(userId = promiseUser.userId, coordinateVo = location)
@@ -139,6 +143,7 @@ class PromiseReadUseCase(
139143
promiseUsers = promiseUserInfoVos,
140144
timeOverLocations = timeOverLocations,
141145
promiseImageUrls = promiseImagesUrls,
146+
highlights = highlights,
142147
),
143148
)
144149
}

Whatnow-Api/src/test/kotlin/com/depromeet/whatnow/api/promise/usecase/PromiseReadUseCaseTest.kt

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package com.depromeet.whatnow.api.promise.usecase
22

3+
import com.depromeet.whatnow.api.notification.dto.ArrivalNotificationResponse
4+
import com.depromeet.whatnow.api.notification.dto.HighlightsResponse
5+
import com.depromeet.whatnow.api.notification.usecase.NotificationHighlightsReadUseCase
36
import com.depromeet.whatnow.common.vo.CoordinateVo
47
import com.depromeet.whatnow.common.vo.PlaceVo
58
import com.depromeet.whatnow.domains.image.adapter.PromiseImageAdapter
@@ -25,7 +28,6 @@ import org.junit.jupiter.api.extension.ExtendWith
2528
import org.mockito.InjectMocks
2629
import org.mockito.Mock
2730
import org.mockito.Mockito.`when`
28-
import org.mockito.MockitoAnnotations
2931
import org.mockito.junit.jupiter.MockitoExtension
3032
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
3133
import org.springframework.security.core.authority.SimpleGrantedAuthority
@@ -53,20 +55,14 @@ class PromiseReadUseCaseTest {
5355
@Mock
5456
private lateinit var promiseImageAdapter: PromiseImageAdapter
5557

58+
@Mock
59+
private lateinit var notificationHighlightsReadUseCase: NotificationHighlightsReadUseCase
60+
5661
@InjectMocks
5762
private lateinit var promiseReadUseCase: PromiseReadUseCase
5863

5964
@BeforeEach
6065
fun setUp() {
61-
MockitoAnnotations.openMocks(this)
62-
promiseReadUseCase = PromiseReadUseCase(
63-
userRepository = userRepository,
64-
promiseUserAdaptor = promiseUserAdaptor,
65-
promiseAdaptor = promiseAdaptor,
66-
userAdapter = userAdapter,
67-
interactionAdapter = interactionAdapter,
68-
promiseImageAdapter = promiseImageAdapter,
69-
)
7066
val securityContext = SecurityContextHolder.createEmptyContext()
7167
val authentication = UsernamePasswordAuthenticationToken("1", null, setOf(SimpleGrantedAuthority("ROLE_USER")))
7268
securityContext.authentication = authentication
@@ -145,13 +141,20 @@ class PromiseReadUseCaseTest {
145141
Interaction(InteractionType.POOP, 1, 2, 12),
146142
Interaction(InteractionType.STEP, 1, 2, 2934),
147143
)
144+
val highlightsResponse = HighlightsResponse(
145+
listOf(
146+
ArrivalNotificationResponse(1, 2, LocalDateTime.now()),
147+
),
148+
)
148149

149150
`when`(promiseUserAdaptor.findByUserId(userId)).thenReturn(promiseUsers)
150151
`when`(promiseAdaptor.queryPromises(listOf(1, 2))).thenReturn(promises)
151152
`when`(userAdapter.queryUsers(listOf(1))).thenReturn(users)
152153
// `when`(interactionAdapter.queryAllInteraction(1, 1)).thenReturn(interactions)
153154
`when`(interactionAdapter.queryAllInteraction(1, 1)).thenReturn(interactionsPromise1)
154155
`when`(interactionAdapter.queryAllInteraction(2, 1)).thenReturn(interactionsPromise2)
156+
`when`(notificationHighlightsReadUseCase.executeTop3(1)).thenReturn(highlightsResponse)
157+
`when`(notificationHighlightsReadUseCase.executeTop3(2)).thenReturn(highlightsResponse)
155158

156159
// When
157160
val result = promiseReadUseCase.findPromiseDetailByStatus(PromiseType.BEFORE)

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import com.depromeet.whatnow.domains.interaction.domain.InteractionType
66
import com.depromeet.whatnow.events.domainEvent.InteractionHistoryRegisterEvent
77
import javax.persistence.Column
88
import javax.persistence.Entity
9+
import javax.persistence.EnumType
10+
import javax.persistence.Enumerated
911
import javax.persistence.GeneratedValue
1012
import javax.persistence.GenerationType
1113
import javax.persistence.Id
@@ -17,6 +19,7 @@ import javax.persistence.Table
1719
class InteractionHistory(
1820
var promiseId: Long,
1921

22+
@Enumerated(EnumType.STRING)
2023
var interactionType: InteractionType,
2124

2225
var userId: Long,

Whatnow-Domain/src/main/kotlin/com/depromeet/whatnow/domains/notification/adapter/NotificationAdapter.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,12 @@ class NotificationAdapter(
1717
fun getMyNotifications(userId: Long, pageable: Pageable): Slice<Notification> {
1818
return notificationRepository.findAllByTargetUserIdOrderByCreatedAtDesc(userId, pageable)
1919
}
20+
21+
fun getNotificationHighlights(promiseId: Long, userId: Long, pageable: Pageable): Slice<Notification> {
22+
return notificationRepository.findAllHighlights(promiseId, userId, pageable)
23+
}
24+
25+
fun getNotificationHighlightsTop3(promiseId: Long): List<Notification> {
26+
return notificationRepository.findAllHighlightsTop3(promiseId)
27+
}
2028
}

0 commit comments

Comments
 (0)