diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/notification/ExposedNotificationRepository.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/notification/ExposedNotificationRepository.kt index 71e8acee..ab3c23b6 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/notification/ExposedNotificationRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/notification/ExposedNotificationRepository.kt @@ -27,6 +27,7 @@ class ExposedNotificationRepository(private val idGenerateService: IdGenerateSer if (singleOrNull == null) { Notifications.insert { it[id] = notification.id + it[type] = notification.type it[userId] = notification.userId it[sourceActorId] = notification.sourceActorId it[postId] = notification.postId @@ -36,6 +37,7 @@ class ExposedNotificationRepository(private val idGenerateService: IdGenerateSer } } else { Notifications.update({ Notifications.id eq notification.id }) { + it[type] = notification.type it[userId] = notification.userId it[sourceActorId] = notification.sourceActorId it[postId] = notification.postId @@ -62,6 +64,7 @@ class ExposedNotificationRepository(private val idGenerateService: IdGenerateSer fun ResultRow.toNotifications() = Notification( this[Notifications.id], + this[Notifications.type], this[Notifications.userId], this[Notifications.sourceActorId], this[Notifications.postId], @@ -72,6 +75,7 @@ fun ResultRow.toNotifications() = Notification( object Notifications : Table("notifications") { val id = long("id") + val type = varchar("type", 100) val userId = long("user_id").references(Actors.id) val sourceActorId = long("source_actor_id").references(Actors.id).nullable() val postId = long("post_id").references(Posts.id).nullable() diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/notification/Notification.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/notification/Notification.kt index 2026778c..395c143d 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/notification/Notification.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/notification/Notification.kt @@ -4,6 +4,7 @@ import java.time.Instant data class Notification( val id: Long, + val type: String, val userId: Long, val sourceActorId: Long?, val postId: Long?, diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt index df35e349..b76f5f0a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt @@ -13,6 +13,7 @@ interface ReactionRepository { suspend fun deleteByActorId(actorId: Long): Int suspend fun deleteByPostIdAndActorId(postId: Long, actorId: Long) suspend fun deleteByPostIdAndActorIdAndEmoji(postId: Long, actorId: Long, emoji: Emoji) + suspend fun findById(id: Long): Reaction? suspend fun findByPostId(postId: Long): List suspend fun findByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Reaction? suspend fun existByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Boolean diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ReactionRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ReactionRepositoryImpl.kt index 50887de2..04cc9031 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ReactionRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ReactionRepositoryImpl.kt @@ -101,6 +101,10 @@ class ReactionRepositoryImpl( } } + override suspend fun findById(id: Long): Reaction? = query { + return@query Reactions.select { Reactions.id eq id }.singleOrNull()?.toReaction() + } + override suspend fun findByPostId(postId: Long): List = query { return@query Reactions.leftJoin(CustomEmojis).select { Reactions.postId eq postId }.map { it.toReaction() } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationRequest.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationRequest.kt index 3080c32e..d089b0b3 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationRequest.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationRequest.kt @@ -1,6 +1,11 @@ package dev.usbharu.hideout.core.service.notification -sealed class NotificationRequest(open val userId: Long, open val sourceActorId: Long) +import dev.usbharu.hideout.core.domain.model.notification.Notification +import java.time.Instant + +sealed class NotificationRequest(open val userId: Long, open val sourceActorId: Long?, val type: String) { + abstract fun buildNotification(id: Long, createdAt: Instant): Notification +} interface PostId { val postId: Long @@ -9,28 +14,94 @@ interface PostId { data class MentionNotificationRequest( override val userId: Long, override val sourceActorId: Long, override val postId: Long ) : NotificationRequest( - userId, sourceActorId -), PostId + userId, sourceActorId, + "mention" +), PostId { + override fun buildNotification(id: Long, createdAt: Instant): Notification = Notification( + id, + type, + userId, + sourceActorId, + postId, + null, + null, + createdAt + ) +} data class PostNotificationRequest( override val userId: Long, override val sourceActorId: Long, override val postId: Long -) : NotificationRequest(userId, sourceActorId), PostId +) : NotificationRequest(userId, sourceActorId, "post"), PostId { + override fun buildNotification(id: Long, createdAt: Instant): Notification = Notification( + id, + type, + userId, + sourceActorId, + postId, + null, + null, + createdAt + ) +} data class RepostNotificationRequest( override val userId: Long, override val sourceActorId: Long, override val postId: Long -) : NotificationRequest(userId, sourceActorId), PostId +) : NotificationRequest(userId, sourceActorId, "repost"), PostId { + override fun buildNotification(id: Long, createdAt: Instant): Notification = Notification( + id, + type, + userId, + sourceActorId, + postId, + null, + null, + createdAt + ) +} data class FollowNotificationRequest( - override val userId: Long, override val sourceActorId: Long, override val postId: Long - -) : NotificationRequest(userId, sourceActorId), PostId + override val userId: Long, override val sourceActorId: Long +) : NotificationRequest(userId, sourceActorId, "follow") { + override fun buildNotification(id: Long, createdAt: Instant): Notification = Notification( + id, + type, + userId, + sourceActorId, + null, + null, + null, + createdAt + ) +} data class FollowRequestNotificationRequest( - override val userId: Long, override val sourceActorId: Long, override val postId: Long -) : NotificationRequest(userId, sourceActorId), PostId + override val userId: Long, override val sourceActorId: Long +) : NotificationRequest(userId, sourceActorId, "follow-request") { + override fun buildNotification(id: Long, createdAt: Instant): Notification = Notification( + id, + type, + userId, + sourceActorId, + null, + null, + null, + createdAt + ) +} data class ReactionNotificationRequest( override val userId: Long, override val sourceActorId: Long, override val postId: Long, val reactionId: Long -) : NotificationRequest(userId, sourceActorId), PostId +) : NotificationRequest(userId, sourceActorId, "reaction"), PostId { + override fun buildNotification(id: Long, createdAt: Instant): Notification = Notification( + id, + type, + userId, + sourceActorId, + postId, + null, + reactionId, + createdAt + ) +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationService.kt index 9c453e49..aa5e3a9e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationService.kt @@ -3,6 +3,6 @@ package dev.usbharu.hideout.core.service.notification import dev.usbharu.hideout.core.domain.model.notification.Notification interface NotificationService { - suspend fun publishNotify(notificationRequest: NotificationRequest): Notification + suspend fun publishNotify(notificationRequest: NotificationRequest): Notification? suspend fun unpublishNotify(notificationId: Long) } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationServiceImpl.kt new file mode 100644 index 00000000..7e37e7d7 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationServiceImpl.kt @@ -0,0 +1,70 @@ +package dev.usbharu.hideout.core.service.notification + +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository +import dev.usbharu.hideout.core.domain.model.notification.Notification +import dev.usbharu.hideout.core.domain.model.notification.NotificationRepository +import dev.usbharu.hideout.core.domain.model.post.PostRepository +import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository +import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository +import org.springframework.stereotype.Service +import java.time.Instant + +@Service +class NotificationServiceImpl( + private val relationshipNotificationManagementService: RelationshipNotificationManagementService, + private val relationshipRepository: RelationshipRepository, + private val notificationStoreList: List, + private val notificationRepository: NotificationRepository, + private val actorRepository: ActorRepository, + private val postRepository: PostRepository, + private val reactionRepository: ReactionRepository +) : NotificationService { + @Suppress("ReplaceNotNullAssertionWithElvisReturn") + override suspend fun publishNotify(notificationRequest: NotificationRequest): Notification? { + + // とりあえず個人間のRelationshipに基づいてきめる。今後増やす + if (!relationship(notificationRequest)) { + return null + } + + val id = notificationRepository.generateId() + val createdAt = Instant.now() + + val notification = notificationRequest.buildNotification(id, createdAt) + + val savedNotification = notificationRepository.save(notification) + + // saveで参照整合性違反が発生するはずなので + val user = actorRepository.findById(savedNotification.userId)!! + val sourceActor = savedNotification.sourceActorId?.let { actorRepository.findById(it) } + + val post = savedNotification.postId?.let { postRepository.findById(it) } + val reaction = savedNotification.reactionId?.let { reactionRepository.findById(it) } + + for (it in notificationStoreList) { + it.publishNotification(savedNotification, user, sourceActor, post, reaction) + } + + return savedNotification + } + + override suspend fun unpublishNotify(notificationId: Long) { + notificationRepository.deleteById(notificationId) + for (notificationStore in notificationStoreList) { + notificationStore.unpulishNotification(notificationId) + } + } + + /** + * 個人間のRelationshipに基づいて通知を送信するか判断します + * + * @param notificationRequest + * @return trueの場合送信する + */ + private suspend fun relationship(notificationRequest: NotificationRequest): Boolean { + val targetActorId = notificationRequest.sourceActorId ?: return true + val relationship = + relationshipRepository.findByUserIdAndTargetUserId(notificationRequest.userId, targetActorId) ?: return true + return relationshipNotificationManagementService.sendNotification(relationship, notificationRequest) + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationStore.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationStore.kt new file mode 100644 index 00000000..6528e5ba --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationStore.kt @@ -0,0 +1,18 @@ +package dev.usbharu.hideout.core.service.notification + +import dev.usbharu.hideout.core.domain.model.actor.Actor +import dev.usbharu.hideout.core.domain.model.notification.Notification +import dev.usbharu.hideout.core.domain.model.post.Post +import dev.usbharu.hideout.core.domain.model.reaction.Reaction + +interface NotificationStore { + suspend fun publishNotification( + notification: Notification, + user: Actor, + sourceActor: Actor?, + post: Post?, + reaction: Reaction? + ): Boolean + + suspend fun unpulishNotification(notificationId: Long): Boolean +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationManagimentService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/notification/RelationshipNotificationManagementService.kt similarity index 81% rename from src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationManagimentService.kt rename to src/main/kotlin/dev/usbharu/hideout/core/service/notification/RelationshipNotificationManagementService.kt index 3281087d..facd0a60 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationManagimentService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/notification/RelationshipNotificationManagementService.kt @@ -2,6 +2,6 @@ package dev.usbharu.hideout.core.service.notification import dev.usbharu.hideout.core.domain.model.relationship.Relationship -interface NotificationManagimentService { +interface RelationshipNotificationManagementService { fun sendNotification(relationship: Relationship, notificationRequest: NotificationRequest): Boolean } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/notification/RelationshipNotificationManagementServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/notification/RelationshipNotificationManagementServiceImpl.kt new file mode 100644 index 00000000..ca1600e0 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/notification/RelationshipNotificationManagementServiceImpl.kt @@ -0,0 +1,10 @@ +package dev.usbharu.hideout.core.service.notification + +import dev.usbharu.hideout.core.domain.model.relationship.Relationship +import org.springframework.stereotype.Service + +@Service +class RelationshipNotificationManagementServiceImpl : RelationshipNotificationManagementService { + override fun sendNotification(relationship: Relationship, notificationRequest: NotificationRequest): Boolean = + relationship.muting.not() +}