mirror of https://github.com/usbharu/Hideout.git
feat: 通知の仕組みを実装
This commit is contained in:
parent
ffdf34e934
commit
90da507e41
|
@ -27,6 +27,7 @@ class ExposedNotificationRepository(private val idGenerateService: IdGenerateSer
|
||||||
if (singleOrNull == null) {
|
if (singleOrNull == null) {
|
||||||
Notifications.insert {
|
Notifications.insert {
|
||||||
it[id] = notification.id
|
it[id] = notification.id
|
||||||
|
it[type] = notification.type
|
||||||
it[userId] = notification.userId
|
it[userId] = notification.userId
|
||||||
it[sourceActorId] = notification.sourceActorId
|
it[sourceActorId] = notification.sourceActorId
|
||||||
it[postId] = notification.postId
|
it[postId] = notification.postId
|
||||||
|
@ -36,6 +37,7 @@ class ExposedNotificationRepository(private val idGenerateService: IdGenerateSer
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Notifications.update({ Notifications.id eq notification.id }) {
|
Notifications.update({ Notifications.id eq notification.id }) {
|
||||||
|
it[type] = notification.type
|
||||||
it[userId] = notification.userId
|
it[userId] = notification.userId
|
||||||
it[sourceActorId] = notification.sourceActorId
|
it[sourceActorId] = notification.sourceActorId
|
||||||
it[postId] = notification.postId
|
it[postId] = notification.postId
|
||||||
|
@ -62,6 +64,7 @@ class ExposedNotificationRepository(private val idGenerateService: IdGenerateSer
|
||||||
|
|
||||||
fun ResultRow.toNotifications() = Notification(
|
fun ResultRow.toNotifications() = Notification(
|
||||||
this[Notifications.id],
|
this[Notifications.id],
|
||||||
|
this[Notifications.type],
|
||||||
this[Notifications.userId],
|
this[Notifications.userId],
|
||||||
this[Notifications.sourceActorId],
|
this[Notifications.sourceActorId],
|
||||||
this[Notifications.postId],
|
this[Notifications.postId],
|
||||||
|
@ -72,6 +75,7 @@ fun ResultRow.toNotifications() = Notification(
|
||||||
|
|
||||||
object Notifications : Table("notifications") {
|
object Notifications : Table("notifications") {
|
||||||
val id = long("id")
|
val id = long("id")
|
||||||
|
val type = varchar("type", 100)
|
||||||
val userId = long("user_id").references(Actors.id)
|
val userId = long("user_id").references(Actors.id)
|
||||||
val sourceActorId = long("source_actor_id").references(Actors.id).nullable()
|
val sourceActorId = long("source_actor_id").references(Actors.id).nullable()
|
||||||
val postId = long("post_id").references(Posts.id).nullable()
|
val postId = long("post_id").references(Posts.id).nullable()
|
||||||
|
|
|
@ -4,6 +4,7 @@ import java.time.Instant
|
||||||
|
|
||||||
data class Notification(
|
data class Notification(
|
||||||
val id: Long,
|
val id: Long,
|
||||||
|
val type: String,
|
||||||
val userId: Long,
|
val userId: Long,
|
||||||
val sourceActorId: Long?,
|
val sourceActorId: Long?,
|
||||||
val postId: Long?,
|
val postId: Long?,
|
||||||
|
|
|
@ -13,6 +13,7 @@ interface ReactionRepository {
|
||||||
suspend fun deleteByActorId(actorId: Long): Int
|
suspend fun deleteByActorId(actorId: Long): Int
|
||||||
suspend fun deleteByPostIdAndActorId(postId: Long, actorId: Long)
|
suspend fun deleteByPostIdAndActorId(postId: Long, actorId: Long)
|
||||||
suspend fun deleteByPostIdAndActorIdAndEmoji(postId: Long, actorId: Long, emoji: Emoji)
|
suspend fun deleteByPostIdAndActorIdAndEmoji(postId: Long, actorId: Long, emoji: Emoji)
|
||||||
|
suspend fun findById(id: Long): Reaction?
|
||||||
suspend fun findByPostId(postId: Long): List<Reaction>
|
suspend fun findByPostId(postId: Long): List<Reaction>
|
||||||
suspend fun findByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Reaction?
|
suspend fun findByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Reaction?
|
||||||
suspend fun existByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Boolean
|
suspend fun existByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Boolean
|
||||||
|
|
|
@ -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<Reaction> = query {
|
override suspend fun findByPostId(postId: Long): List<Reaction> = query {
|
||||||
return@query Reactions.leftJoin(CustomEmojis).select { Reactions.postId eq postId }.map { it.toReaction() }
|
return@query Reactions.leftJoin(CustomEmojis).select { Reactions.postId eq postId }.map { it.toReaction() }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
package dev.usbharu.hideout.core.service.notification
|
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 {
|
interface PostId {
|
||||||
val postId: Long
|
val postId: Long
|
||||||
|
@ -9,28 +14,94 @@ interface PostId {
|
||||||
data class MentionNotificationRequest(
|
data class MentionNotificationRequest(
|
||||||
override val userId: Long, override val sourceActorId: Long, override val postId: Long
|
override val userId: Long, override val sourceActorId: Long, override val postId: Long
|
||||||
) : NotificationRequest(
|
) : NotificationRequest(
|
||||||
userId, sourceActorId
|
userId, sourceActorId,
|
||||||
), PostId
|
"mention"
|
||||||
|
), PostId {
|
||||||
|
override fun buildNotification(id: Long, createdAt: Instant): Notification = Notification(
|
||||||
|
id,
|
||||||
|
type,
|
||||||
|
userId,
|
||||||
|
sourceActorId,
|
||||||
|
postId,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
createdAt
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
data class PostNotificationRequest(
|
data class PostNotificationRequest(
|
||||||
override val userId: Long, override val sourceActorId: Long, override val postId: Long
|
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(
|
data class RepostNotificationRequest(
|
||||||
override val userId: Long, override val sourceActorId: Long, override val postId: Long
|
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(
|
data class FollowNotificationRequest(
|
||||||
override val userId: Long, override val sourceActorId: Long, override val postId: Long
|
override val userId: Long, override val sourceActorId: Long
|
||||||
|
) : NotificationRequest(userId, sourceActorId, "follow") {
|
||||||
) : NotificationRequest(userId, sourceActorId), PostId
|
override fun buildNotification(id: Long, createdAt: Instant): Notification = Notification(
|
||||||
|
id,
|
||||||
|
type,
|
||||||
|
userId,
|
||||||
|
sourceActorId,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
createdAt
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
data class FollowRequestNotificationRequest(
|
data class FollowRequestNotificationRequest(
|
||||||
override val userId: Long, override val sourceActorId: Long, override val postId: Long
|
override val userId: Long, override val sourceActorId: Long
|
||||||
) : NotificationRequest(userId, sourceActorId), PostId
|
) : 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(
|
data class ReactionNotificationRequest(
|
||||||
override val userId: Long, override val sourceActorId: Long, override val postId: Long, val reactionId: Long
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,6 @@ package dev.usbharu.hideout.core.service.notification
|
||||||
import dev.usbharu.hideout.core.domain.model.notification.Notification
|
import dev.usbharu.hideout.core.domain.model.notification.Notification
|
||||||
|
|
||||||
interface NotificationService {
|
interface NotificationService {
|
||||||
suspend fun publishNotify(notificationRequest: NotificationRequest): Notification
|
suspend fun publishNotify(notificationRequest: NotificationRequest): Notification?
|
||||||
suspend fun unpublishNotify(notificationId: Long)
|
suspend fun unpublishNotify(notificationId: Long)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<NotificationStore>,
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -2,6 +2,6 @@ package dev.usbharu.hideout.core.service.notification
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.relationship.Relationship
|
import dev.usbharu.hideout.core.domain.model.relationship.Relationship
|
||||||
|
|
||||||
interface NotificationManagimentService {
|
interface RelationshipNotificationManagementService {
|
||||||
fun sendNotification(relationship: Relationship, notificationRequest: NotificationRequest): Boolean
|
fun sendNotification(relationship: Relationship, notificationRequest: NotificationRequest): Boolean
|
||||||
}
|
}
|
|
@ -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()
|
||||||
|
}
|
Loading…
Reference in New Issue