-
Notifications
You must be signed in to change notification settings - Fork 0
[Refactor&Feature] FCM Sender & Notification Domain #70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,4 +4,5 @@ data class FcmSendRequest( | |
| val fcmToken: String, | ||
| val title: String, | ||
| val body: String, | ||
| val destination: String, | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| package com.pida.notification | ||
|
|
||
| data class CreateNotificationStoredCommand( | ||
| val notificationStoredKey: String, | ||
| val userId: Long, | ||
| val type: NotificationType, | ||
| val parameterValue: String, | ||
| val topic: String, | ||
| val contents: String, | ||
| val readStatus: ReadStatus = ReadStatus.UNREAD, | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,7 @@ data class FirebaseCloudMessage( | |
| val fcmToken: String, | ||
| val title: String, | ||
| val body: String, | ||
| val destination: String, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ๐งฉ Analysis chain๐ Script executed: #!/bin/bash
# First, find the FirebaseCloudMessage file and examine its structure
echo "=== FirebaseCloudMessage.kt file structure ==="
fd -t f "FirebaseCloudMessage.kt" --exec wc -l {} \;
echo ""
# Read the file to understand the class definition
echo "=== FirebaseCloudMessage.kt contents ==="
fd -t f "FirebaseCloudMessage.kt" --exec cat -n {} \;
echo ""
# Search for destination field usage in message construction and validation
echo "=== Destination field usage patterns ==="
rg -nP --type=kt -C3 'destination\s*[=:]' -g '!*Test.kt' || echo "No matches found"
echo ""
# Search for empty/blank checks on destination
echo "=== Empty/blank checks on destination ==="
rg -nP --type=kt 'destination\.(isNotBlank|isNotEmpty|isEmpty|isBlank)' -g '!*Test.kt' || echo "No matches found"
echo ""
# Search for where FirebaseCloudMessage is instantiated
echo "=== FirebaseCloudMessage instantiation ==="
rg -nP --type=kt 'FirebaseCloudMessage\s*\(' -C2 -g '!*Test.kt' | head -50Repository: Team-PIDA/Pida-Server Length of output: 1132 ๐ Script executed: #!/bin/bash
# Search for destination field usage without file type restriction
echo "=== Destination field usage in codebase ==="
rg -n 'destination' --type-list | grep -i kotlin || echo "Checking Kotlin type support..."
echo ""
# Try broader search
echo "=== All destination references ==="
rg -n '\.destination' -g '*.kt' -C2 | head -100
echo ""
# Search for FirebaseCloudMessage usage
echo "=== FirebaseCloudMessage instantiation/usage ==="
rg -n 'FirebaseCloudMessage(' -g '*.kt' -C3 | head -100
echo ""
# Search for validation in notification package
echo "=== Validation in notification package ==="
rg -n 'require\|check\|assert' pida-core/core-domain/src/main/kotlin/com/pida/notification/ | head -50
echo ""
# Check if there are any constructor validations
echo "=== Constructor or init blocks ==="
rg -n 'init\s*\{' pida-core/core-domain/src/main/kotlin/com/pida/notification/ -A5Repository: Team-PIDA/Pida-Server Length of output: 5667 ๐ Script executed: #!/bin/bash
# Examine the FirebaseCloudMessageSender implementation
echo "=== FirebaseCloudMessageSender.kt contents ==="
cat -n pida-clients/notification/src/main/kotlin/com/pida/client/notification/FirebaseCloudMessageSender.kt
echo ""
# Look for any custom data validation in related files
echo "=== NewFirebaseCloudMessage request creation ==="
rg -n 'NewFirebaseCloudMessage' pida-clients/notification/src/main/kotlin/com/pida/client/notification/ -A3 -B1
echo ""
# Check if there are any tests that show validation expectations
echo "=== Test files for destination ==="
find . -name '*Test.kt' -o -name '*Tests.kt' | xargs grep -l 'destination' 2>/dev/null | head -10
echo ""
# Examine any validation in request/domain models
echo "=== FcmRequest or related request models ==="
rg -n 'data class.*Request' pida-clients/notification/src/main/kotlin/com/pida/client/notification/ -A5Repository: Team-PIDA/Pida-Server Length of output: 4347 Add validation to ensure The ๐ค Prompt for AI Agents |
||
| val tryCount: Int, | ||
| val sent: Boolean, | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| package com.pida.notification | ||
|
|
||
| import com.pida.support.cursor.Cursor | ||
| import com.pida.support.cursor.CursorRequest | ||
| import org.springframework.stereotype.Service | ||
|
|
||
| @Service | ||
| class NotificationService( | ||
| private val notificationStoredAppender: NotificationStoredAppender, | ||
| private val notificationStoredReader: NotificationStoredReader, | ||
| private val notificationStoredUpdater: NotificationStoredUpdater, | ||
| private val notificationStoredKeyGenerator: NotificationStoredKeyGenerator, | ||
| ) { | ||
| fun append(notificationStored: NotificationStored.Create): NotificationStored.Info = | ||
| notificationStoredAppender.append( | ||
| notificationStored.copy( | ||
| notificationStoredKey = notificationStoredKeyGenerator.generate(), | ||
| ), | ||
| ) | ||
|
|
||
| fun appendAll(createAll: List<CreateNotificationStoredCommand>): List<NotificationStored.Info> = | ||
| notificationStoredAppender.appendAll( | ||
| createAll.map { notification -> | ||
| NotificationStored.Create( | ||
| notificationStoredKey = notificationStoredKeyGenerator.generate(), | ||
| userId = notification.userId, | ||
| type = notification.type, | ||
| parameterValue = notification.parameterValue, | ||
| topic = notification.topic, | ||
| contents = notification.contents, | ||
| readStatus = ReadStatus.UNREAD, | ||
| ) | ||
| }, | ||
| ) | ||
|
|
||
| fun findAllNotifications( | ||
| userId: Long, | ||
| cursorRequest: CursorRequest, | ||
| ): Cursor<NotificationStored.Info> = notificationStoredReader.findAllBy(userId, cursorRequest) | ||
|
|
||
| fun markAsRead(notificationId: Long) = notificationStoredUpdater.markAsRead(notificationId) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| package com.pida.notification | ||
|
|
||
| sealed class NotificationStored { | ||
| data class Create( | ||
| val notificationStoredKey: String, | ||
| val userId: Long, | ||
| val type: NotificationType, | ||
| val parameterValue: String, | ||
| val topic: String, | ||
| val contents: String, | ||
| val readStatus: ReadStatus, | ||
| ) | ||
|
|
||
| data class Info( | ||
| val id: Long, | ||
| val notificationStoredKey: String, | ||
| val userId: Long, | ||
| val type: NotificationType, | ||
| val parameterValue: String, | ||
| val topic: String, | ||
| val contents: String, | ||
| val readStatus: ReadStatus, | ||
| val createdAt: String, | ||
| ) | ||
|
|
||
| data class Result( | ||
| val id: Long, | ||
| val userId: Long, | ||
| val nickname: String?, | ||
| val spotName: String?, | ||
| val site: String?, | ||
| val type: String, | ||
| val parameterValue: String, | ||
| val topic: String, | ||
| val contents: String, | ||
| val readStatus: ReadStatus, | ||
| val createdAt: String, | ||
| ) | ||
|
Comment on lines
+26
to
+38
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ์๊ธฐ Result๋ ์ด๋ค ์ญํ ์ธ๊ฐ์~
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ์ฌ๊ธฐ Result๋ Facade ๋ ์ด์ด์์ User ๋๋ฉ์ธ๊ณผ Notification ๋๋ฉ์ธ์์ ๋น์ฆ๋์ค ๋ก์ง์ ๊ฒฐํฉํ ๋ฐ์ดํฐ ๋ชจ๋ธ์ ๊ฒฐ๊ณผ๋ผ๊ณ ๋ด์ฃผ์ฌ ๋ ๊ฑฐ ๊ฐ์ต๋๋ค! |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| package com.pida.notification | ||
|
|
||
| import org.springframework.stereotype.Component | ||
|
|
||
| @Component | ||
| class NotificationStoredAppender( | ||
| private val notificationStoredRepository: NotificationStoredRepository, | ||
| ) { | ||
| fun append(create: NotificationStored.Create): NotificationStored.Info = notificationStoredRepository.save(create) | ||
|
|
||
| fun appendAll(createAll: List<NotificationStored.Create>) = notificationStoredRepository.saveAll(createAll) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| package com.pida.notification | ||
|
|
||
| import org.springframework.stereotype.Component | ||
| import java.time.LocalDate | ||
| import java.time.format.DateTimeFormatter | ||
| import java.util.UUID | ||
|
|
||
| @Component | ||
| class NotificationStoredKeyGenerator { | ||
| companion object { | ||
| private val FORMAT_YYYYMMDD = DateTimeFormatter.ofPattern("yyyyMMdd") | ||
| } | ||
|
|
||
| fun generate(): String = "${generateDate()}_NHK_${generateUUID()}" | ||
|
|
||
| private fun generateUUID(): String = UUID.randomUUID().toString().replace("-", "") | ||
|
|
||
| private fun generateDate(): String = FORMAT_YYYYMMDD.format(LocalDate.now()) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package com.pida.notification | ||
|
|
||
| import com.pida.support.cursor.Cursor | ||
| import com.pida.support.cursor.CursorRequest | ||
| import org.springframework.stereotype.Component | ||
|
|
||
| @Component | ||
| class NotificationStoredReader( | ||
| private val notificationStoredRepository: NotificationStoredRepository, | ||
| ) { | ||
| fun findAllBy( | ||
| userId: Long, | ||
| cursorRequest: CursorRequest, | ||
| ): Cursor<NotificationStored.Info> = notificationStoredRepository.findAllBy(userId, cursorRequest) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| package com.pida.notification | ||
|
|
||
| import com.pida.support.cursor.Cursor | ||
| import com.pida.support.cursor.CursorRequest | ||
|
|
||
| interface NotificationStoredRepository { | ||
| fun save(notificationStored: NotificationStored.Create): NotificationStored.Info | ||
|
|
||
| fun saveAll(createAll: List<NotificationStored.Create>): List<NotificationStored.Info> | ||
|
|
||
| fun findById(notificationStoredId: Long): NotificationStored.Info | ||
|
|
||
| fun findByUserIdAndReadStatus( | ||
| userId: Long, | ||
| readStatus: ReadStatus, | ||
| ): List<NotificationStored.Info> | ||
|
|
||
| fun findAllBy( | ||
| userId: Long, | ||
| cursorRequest: CursorRequest, | ||
| ): Cursor<NotificationStored.Info> | ||
|
|
||
| fun countByUserIdAndReadStatus( | ||
| userId: Long, | ||
| readStatus: ReadStatus?, | ||
| ): Long | ||
|
|
||
| fun markAsRead(notificationId: Long) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package com.pida.notification | ||
|
|
||
| import org.springframework.stereotype.Component | ||
|
|
||
| @Component | ||
| class NotificationStoredUpdater( | ||
| private val notificationStoredRepository: NotificationStoredRepository, | ||
| ) { | ||
| fun markAsRead(notificationId: Long) = notificationStoredRepository.markAsRead(notificationId) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| package com.pida.notification | ||
|
|
||
| enum class NotificationType { | ||
| ADMIN_PUSH, // ๊ด๋ฆฌ์ ํธ์ | ||
| REGULAR, // ์ ๊ธฐ ํธ์ ์๋ฆผ (ํ์์ผ ์ค์ 9์) | ||
| ; | ||
|
|
||
| companion object { | ||
| fun of(type: String): NotificationType = | ||
| when (type) { | ||
| "ADMIN_PUSH" -> ADMIN_PUSH | ||
| "REGULAR" -> REGULAR | ||
| else -> throw IllegalArgumentException("Unknown NotificationType: $type") | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| package com.pida.notification | ||
|
|
||
| enum class ReadStatus { | ||
| READ, | ||
| UNREAD, | ||
| ; | ||
|
|
||
| companion object { | ||
| fun of(readStatus: String): ReadStatus = | ||
| when (readStatus) { | ||
| "READ" -> READ | ||
| "UNREAD" -> UNREAD | ||
| else -> throw IllegalArgumentException("Unknown ReadStatus: $readStatus") | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| package com.pida.support.cursor | ||
|
|
||
| data class Cursor<T>( | ||
| val content: List<T>, | ||
| val nextCursor: Long?, | ||
| val size: Long, | ||
| ) { | ||
| companion object { | ||
| const val DEFAULT_CURSOR = 0L | ||
| const val DEFAULT_SIZE = 20 | ||
|
|
||
| fun <T> of( | ||
| content: List<T>, | ||
| nextCursor: Long?, | ||
| size: Long, | ||
| ): Cursor<T> { | ||
| require(size >= 0) { "size ($size)๋ 0 ์ด์์ด์ด์ผ ํฉ๋๋ค" } | ||
| require(size >= content.size) { | ||
| "size ($size)๋ content.size (${content.size})๋ณด๋ค ์์ ์ ์์ต๋๋ค" | ||
| } | ||
| return Cursor(content, nextCursor, size) | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package com.pida.support.cursor | ||
|
|
||
| data class CursorRequest( | ||
| val lastId: Long?, | ||
| val size: Long, | ||
| ) { | ||
| init { | ||
| require(size in 1..100) { "ํฌ๊ธฐ๋ 1 ~ 100 ์ฌ์ด์ฌ์ผ ํฉ๋๋ค." } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
์ด๊ฑด ์ ๊ฐ FCM์ ๋ํ ์ดํด๊ฐ ๋ถ์กฑํด์ ์ผ ์๋ ์๋๋ฐ, destination๊ณผ topic์ ์ด๋ค ์ฐจ์ด๊ฐ ์๋์?
๋๋ค ์ ์ก ๊ฒฝ๋ก๋ผ๋ ๋น์ทํ ์๋ฏธ๋ก ์๊ณ ์์ด์ ์ฌ์ญค๋ด ๋๋ค!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
destination์ FCM ๊ด๋ จํ ํ๋๊ฐ ์๋๋๋ค!
putCustomData์ผ๋ก APNS์์ ์ ํฌ๊ฐ ์์๋ก ์ปค์คํ ํ๋๋ฅผ ์ง์ ํ ์ ์๊ณ , ํธ์ ์๋ฆผ์์ UI ์ ํ์ ์ํ ์ฑ์คํค๋ง ์ปจํ ์คํธ๋ผ๊ณ ๋ด์ฃผ์ฌ ์ข์ ๊ฑฐ ๊ฐ์ต๋๋ค!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
์ํ ๊ทธ๋ ๊ตฐ์ ๋ต๋ณ ๊ฐ์ฌํฉ๋๋ค!