Skip to content

Commit

Permalink
[Feature] 커피챗 상태 전환에 대한 알림 관련 API를 구현한다 (#173)
Browse files Browse the repository at this point in the history
* feat: 특정 사용자 Notification 조회, 전체 읽음 처리 쿼리

* feat: Notification 단건, 전체 읽음 처리 Command/UseCase

* remove unuse class

* feat: 멘토, 멘티 알림 조회 쿼리

* feat: 멘토, 멘티 알림 조회 Query/UseCase 로직

* feat: Notification 단건, 전체 읽음 처리 Endpoint

* refactor: 알림 조회 UseCase 응답 스펙 NotificationDetails -> NotificationSummary 수정

* feat: 알림 조회 Endpoint

* test: 알림 인수테스트 관련 Step 작성

* refactor: 알림 조회 데이터 스펙 알림 타입 추가, 커피챗 상태 제거

* refactor: 학교 인증코드 길이 6자리로 수정

* fix: MDC Context Map 없는 경우 `runnable.run()` 하지 못하는 오류 수정

* test: 알림 조회, 단건 + 전체 읽음 처리 관련 인수테스트 작성

* feat: Notification 알림 시점 커피챗 상태 스냅샷 필드 추가

* refactor: 알림 조회 응답 스펙에 알림 시점의 커피챗 상태 스냅샷 필드 추가
  • Loading branch information
sjiwon authored Feb 25, 2024
1 parent 07cbbb0 commit 2c94af4
Show file tree
Hide file tree
Showing 29 changed files with 1,179 additions and 55 deletions.
2 changes: 2 additions & 0 deletions src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ include::member.adoc[leveloffset=+1]

include::coffeechat.adoc[leveloffset=+1]

include::notification.adoc[leveloffset=+1]

include::etc.adoc[leveloffset=+1]
41 changes: 41 additions & 0 deletions src/docs/asciidoc/notification.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
= [알림 관련 기능]
:doctype: book
:icons: font
:source-highlighter: highlightjs
:toc: left
:toclevels: 3

== 알림 처리

=== 단건 읽음 처리

*HTTP Request*

include::{snippets}/NotificationApi/ReadProcessing/Single/http-request.adoc[]
include::{snippets}/NotificationApi/ReadProcessing/Single/path-parameters.adoc[]

*HTTP Response*

include::{snippets}/NotificationApi/ReadProcessing/Single/http-response.adoc[]

=== 전체 읽음 처리

*HTTP Request*

include::{snippets}/NotificationApi/ReadProcessing/All/http-request.adoc[]

*HTTP Response*

include::{snippets}/NotificationApi/ReadProcessing/All/http-response.adoc[]

== 알림 조회

*HTTP Request*

include::{snippets}/NotificationApi/GetMyNotifications/http-request.adoc[]
include::{snippets}/NotificationApi/GetMyNotifications/query-parameters.adoc[]

*HTTP Response*

include::{snippets}/NotificationApi/GetMyNotifications/http-response.adoc[]
include::{snippets}/NotificationApi/GetMyNotifications/response-fields.adoc[]
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ class DefaultAuthCodeGenerator : AuthCodeGenerator {
UUID.randomUUID()
.toString()
.replace("-".toRegex(), "")
.substring(0, 8)
.substring(0, 6)
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ import org.springframework.core.task.TaskDecorator

class MdcTaskDecorator : TaskDecorator {
override fun decorate(runnable: Runnable): Runnable {
val copyOfContextMap: Map<String, String>? = MDC.getCopyOfContextMap()
val copyOfContextMap = MDC.getCopyOfContextMap()

return Runnable {
if (copyOfContextMap.isNullOrEmpty()) {
runnable.run()
}
MDC.setContextMap(copyOfContextMap)
runnable.run()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.koddy.server.notification.application.usecase

import com.koddy.server.auth.domain.model.Authenticated
import com.koddy.server.global.annotation.KoddyReadOnlyTransactional
import com.koddy.server.global.annotation.UseCase
import com.koddy.server.global.query.PageCreator
import com.koddy.server.global.query.SliceResponse
import com.koddy.server.notification.application.usecase.query.GetNotifications
import com.koddy.server.notification.application.usecase.query.response.NotificationSummary
import com.koddy.server.notification.domain.repository.query.NotificationQueryRepository
import com.koddy.server.notification.domain.repository.query.response.NotificationDetails
import org.springframework.data.domain.Pageable
import org.springframework.data.domain.Slice

@UseCase
class GetNotificationsUseCase(
private val notificationQueryRepository: NotificationQueryRepository,
) {
@KoddyReadOnlyTransactional
fun invoke(query: GetNotifications): SliceResponse<List<NotificationSummary>> {
val authenticated: Authenticated = query.authenticated
val pageable: Pageable = PageCreator.create(query.page)

val result: Slice<NotificationDetails> = when (authenticated.isMentor) {
true -> notificationQueryRepository.fetchMentorNotifications(authenticated.id, pageable)
false -> notificationQueryRepository.fetchMenteeNotifications(authenticated.id, pageable)
}

return SliceResponse(
result = result.content.map { NotificationSummary.from(it) },
hasNext = result.hasNext(),
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.koddy.server.notification.application.usecase

import com.koddy.server.global.annotation.KoddyWritableTransactional
import com.koddy.server.global.annotation.UseCase
import com.koddy.server.notification.application.usecase.command.ReadSingleNotificationCommand
import com.koddy.server.notification.domain.model.Notification
import com.koddy.server.notification.domain.repository.NotificationRepository

@UseCase
class ReadNotificationUseCase(
private val notificationRepository: NotificationRepository,
) {
@KoddyWritableTransactional
fun readSingle(command: ReadSingleNotificationCommand) {
val notification: Notification = notificationRepository.getByIdAndTargetId(command.notificationId, command.memberId)
notification.read()
}

fun readAll(memberId: Long) {
notificationRepository.readAll(memberId)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.koddy.server.notification.application.usecase.command

data class ReadSingleNotificationCommand(
val memberId: Long,
val notificationId: Long,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.koddy.server.notification.application.usecase.query

import com.koddy.server.auth.domain.model.Authenticated

data class GetNotifications(
val authenticated: Authenticated,
val page: Int,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.koddy.server.notification.application.usecase.query.response

import com.koddy.server.notification.domain.repository.query.response.NotificationDetails
import java.time.LocalDate
import java.time.LocalDateTime

data class NotificationSummary(
val id: Long,
val read: Boolean,
val coffeeChatStatusSnapshot: String,
val type: String,
val createdAt: LocalDateTime,
val member: NotifyMember,
val coffeeChat: NotifyCoffeeChat,
) {
companion object {
fun from(details: NotificationDetails): NotificationSummary {
return NotificationSummary(
id = details.id,
read = details.read,
coffeeChatStatusSnapshot = details.coffeeChatStatusSnapshot,
type = details.type.name,
createdAt = details.createdAt,
member = NotifyMember(
id = details.memberId,
name = details.memberName,
profileImageUrl = details.memberProfileImageUrl,
),
coffeeChat = NotifyCoffeeChat(
id = details.coffeeChatId,
cancelReason = details.coffeeChatReason?.cancelReason,
rejectReason = details.coffeeChatReason?.rejectReason,
reservedDay = details.coffeeChatReservation?.start?.toLocalDate(),
),
)
}
}
}

data class NotifyMember(
val id: Long,
val name: String,
val profileImageUrl: String?,
)

data class NotifyCoffeeChat(
val id: Long,
val cancelReason: String?,
val rejectReason: String?,
val reservedDay: LocalDate?,
)
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ protected Notification() {
@Column(name = "coffee_chat_id", nullable = false, updatable = false)
private Long coffeeChatId;

@Column(name = "coffee_chat_status_snapshot", nullable = false, updatable = false)
private String coffeeChatStatusSnapshot;

@Enumerated(STRING)
@Column(name = "notification_type", nullable = false, updatable = false, columnDefinition = "VARCHAR(50)")
private NotificationType type;
Expand All @@ -37,6 +40,7 @@ private Notification(
) {
this.targetId = target.getId();
this.coffeeChatId = coffeeChat.getId();
this.coffeeChatStatusSnapshot = coffeeChat.getStatus().name();
this.type = type;
this.read = read;
}
Expand All @@ -61,6 +65,10 @@ public Long getCoffeeChatId() {
return coffeeChatId;
}

public String getCoffeeChatStatusSnapshot() {
return coffeeChatStatusSnapshot;
}

public NotificationType getType() {
return type;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,31 @@
package com.koddy.server.notification.domain.repository;

import com.koddy.server.global.annotation.KoddyWritableTransactional;
import com.koddy.server.notification.domain.model.Notification;
import com.koddy.server.notification.exception.NotificationException;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.Optional;

import static com.koddy.server.notification.exception.NotificationExceptionCode.NOTIFICATION_NOT_FOUND;

public interface NotificationRepository extends JpaRepository<Notification, Long> {
Optional<Notification> findByIdAndTargetId(final long id, final long targetId);

default Notification getByIdAndTargetId(final long id, final long targetId) {
return findByIdAndTargetId(id, targetId)
.orElseThrow(() -> new NotificationException(NOTIFICATION_NOT_FOUND));
}

@KoddyWritableTransactional
@Modifying(flushAutomatically = true, clearAutomatically = true)
@Query("""
UPDATE Notification n
SET n.read = true
WHERE n.targetId = :targetId
""")
void readAll(@Param("targetId") final long targetId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.koddy.server.notification.domain.repository.query;

import com.koddy.server.notification.domain.repository.query.response.NotificationDetails;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;

public interface NotificationQueryRepository {
Slice<NotificationDetails> fetchMentorNotifications(
final long mentorId,
final Pageable pageable
);

Slice<NotificationDetails> fetchMenteeNotifications(
final long menteeId,
final Pageable pageable
);
}
Loading

0 comments on commit 2c94af4

Please sign in to comment.