refactor: Actorsテーブルにフォロワー数情報とフォロー数情報を追加

This commit is contained in:
usbharu 2023-12-14 00:16:45 +09:00
parent e0368ab7c5
commit 77d79e2279
4 changed files with 121 additions and 31 deletions

View File

@ -22,7 +22,11 @@ data class Actor private constructor(
val followers: String? = null, val followers: String? = null,
val following: String? = null, val following: String? = null,
val instance: Long? = null, val instance: Long? = null,
val locked: Boolean val locked: Boolean,
val followersCount: Int = 0,
val followingCount: Int = 0,
val postsCount: Int = 0,
val lastPostDate: Instant? = null
) { ) {
@Component @Component
@ -47,7 +51,11 @@ data class Actor private constructor(
following: String? = null, following: String? = null,
followers: String? = null, followers: String? = null,
instance: Long? = null, instance: Long? = null,
locked: Boolean locked: Boolean,
followersCount: Int,
followingCount: Int,
postsCount: Int,
lastPostDate: Instant?
): Actor { ): Actor {
// idは0未満ではいけない // idは0未満ではいけない
require(id >= 0) { "id must be greater than or equal to 0." } require(id >= 0) { "id must be greater than or equal to 0." }
@ -123,6 +131,18 @@ data class Actor private constructor(
"keyId must contain non-blank characters." "keyId must contain non-blank characters."
} }
require(postsCount >= 0) {
"postsCount must be greater than or equal to 0"
}
require(followersCount >= 0) {
"followersCount must be greater than or equal to 0"
}
require(followingCount >= 0) {
"followingCount must be greater than or equal to 0"
}
return Actor( return Actor(
id = id, id = id,
name = limitedName, name = limitedName,
@ -139,29 +159,47 @@ data class Actor private constructor(
followers = followers, followers = followers,
following = following, following = following,
instance = instance, instance = instance,
locked locked = locked,
followersCount = followersCount,
followingCount = followingCount,
postsCount = postsCount,
lastPostDate = lastPostDate
) )
} }
} }
fun incrementFollowing(): Actor = this.copy(followingCount = this.followingCount + 1)
fun decrementFollowing(): Actor = this.copy(followingCount = this.followingCount - 1)
fun incrementFollowers(): Actor = this.copy(followersCount = this.followersCount + 1)
fun decrementFollowers(): Actor = this.copy(followersCount = this.followersCount - 1)
fun incrementPostsCount(): Actor = this.copy(postsCount = this.postsCount + 1)
fun decrementPostsCount(): Actor = this.copy(postsCount = this.postsCount - 1)
fun withLastPostAt(lastPostDate: Instant): Actor = this.copy(lastPostDate = lastPostDate)
override fun toString(): String { override fun toString(): String {
return "Actor(" + return "Actor(" +
"id=$id, " + "id=$id, " +
"name='$name', " + "name='$name', " +
"domain='$domain', " + "domain='$domain', " +
"screenName='$screenName', " + "screenName='$screenName', " +
"description='$description', " + "description='$description', " +
"inbox='$inbox', " + "inbox='$inbox', " +
"outbox='$outbox', " + "outbox='$outbox', " +
"url='$url', " + "url='$url', " +
"publicKey='$publicKey', " + "publicKey='$publicKey', " +
"privateKey=$privateKey, " + "privateKey=$privateKey, " +
"createdAt=$createdAt, " + "createdAt=$createdAt, " +
"keyId='$keyId', " + "keyId='$keyId', " +
"followers=$followers, " + "followers=$followers, " +
"following=$following, " + "following=$following, " +
"instance=$instance, " + "instance=$instance, " +
"locked=$locked" + "locked=$locked" +
")" ")"
} }
} }

View File

@ -26,7 +26,11 @@ class UserResultRowMapper(private val actorBuilder: Actor.UserBuilder) : ResultR
followers = resultRow[Actors.followers], followers = resultRow[Actors.followers],
following = resultRow[Actors.following], following = resultRow[Actors.following],
instance = resultRow[Actors.instance], instance = resultRow[Actors.instance],
locked = resultRow[Actors.locked] locked = resultRow[Actors.locked],
followingCount = resultRow[Actors.followingCount],
followersCount = resultRow[Actors.followersCount],
postsCount = resultRow[Actors.postsCount],
lastPostDate = resultRow[Actors.lastPostAt],
) )
} }
} }

View File

@ -6,6 +6,7 @@ import dev.usbharu.hideout.core.domain.model.actor.Actor
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.javatime.timestamp
import org.springframework.stereotype.Repository import org.springframework.stereotype.Repository
@Repository @Repository
@ -35,6 +36,10 @@ class ActorRepositoryImpl(
it[followers] = actor.followers it[followers] = actor.followers
it[instance] = actor.instance it[instance] = actor.instance
it[locked] = actor.locked it[locked] = actor.locked
it[followersCount] = actor.followersCount
it[followingCount] = actor.followingCount
it[postsCount] = actor.postsCount
it[lastPostAt] = actor.lastPostDate
} }
} else { } else {
Actors.update({ Actors.id eq actor.id }) { Actors.update({ Actors.id eq actor.id }) {
@ -53,6 +58,10 @@ class ActorRepositoryImpl(
it[followers] = actor.followers it[followers] = actor.followers
it[instance] = actor.instance it[instance] = actor.instance
it[locked] = actor.locked it[locked] = actor.locked
it[followersCount] = actor.followersCount
it[followingCount] = actor.followingCount
it[postsCount] = actor.postsCount
it[lastPostAt] = actor.lastPostDate
} }
} }
return actor return actor
@ -91,6 +100,10 @@ object Actors : Table("actors") {
val followers = varchar("followers", length = 1000).nullable() val followers = varchar("followers", length = 1000).nullable()
val instance = long("instance").references(Instance.id).nullable() val instance = long("instance").references(Instance.id).nullable()
val locked = bool("locked") val locked = bool("locked")
val followingCount = integer("following_count")
val followersCount = integer("followers_count")
val postsCount = integer("posts_count")
val lastPostAt = timestamp("last_post_at").nullable()
override val primaryKey: PrimaryKey = PrimaryKey(id) override val primaryKey: PrimaryKey = PrimaryKey(id)

View File

@ -8,6 +8,7 @@ import dev.usbharu.hideout.activitypub.service.activity.undo.APSendUndoService
import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.application.config.ApplicationConfig
import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException
import dev.usbharu.hideout.core.domain.model.actor.Actor import dev.usbharu.hideout.core.domain.model.actor.Actor
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.relationship.Relationship import dev.usbharu.hideout.core.domain.model.relationship.Relationship
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
import dev.usbharu.hideout.core.query.ActorQueryService import dev.usbharu.hideout.core.query.ActorQueryService
@ -24,7 +25,8 @@ class RelationshipServiceImpl(
private val apSendBlockService: APSendBlockService, private val apSendBlockService: APSendBlockService,
private val apSendAcceptService: ApSendAcceptService, private val apSendAcceptService: ApSendAcceptService,
private val apSendRejectService: ApSendRejectService, private val apSendRejectService: ApSendRejectService,
private val apSendUndoService: APSendUndoService private val apSendUndoService: APSendUndoService,
private val actorRepository: ActorRepository
) : RelationshipService { ) : RelationshipService {
override suspend fun followRequest(actorId: Long, targetId: Long) { override suspend fun followRequest(actorId: Long, targetId: Long) {
logger.info("START Follow Request userId: {} targetId: {}", actorId, targetId) logger.info("START Follow Request userId: {} targetId: {}", actorId, targetId)
@ -90,6 +92,15 @@ class RelationshipServiceImpl(
override suspend fun block(actorId: Long, targetId: Long) { override suspend fun block(actorId: Long, targetId: Long) {
val relationship = relationshipRepository.findByUserIdAndTargetUserId(actorId, targetId) val relationship = relationshipRepository.findByUserIdAndTargetUserId(actorId, targetId)
val user = actorQueryService.findById(actorId)
val targetActor = actorQueryService.findById(targetId)
if (relationship?.following == true) {
actorRepository.save(user.decrementFollowing())
actorRepository.save(targetActor.decrementFollowers())
}
val blockedRelationship = relationship
?.copy(blocking = true, followRequest = false, following = false) ?: Relationship( ?.copy(blocking = true, followRequest = false, following = false) ?: Relationship(
actorId = actorId, actorId = actorId,
targetActorId = targetId, targetActorId = targetId,
@ -101,17 +112,26 @@ class RelationshipServiceImpl(
) )
val inverseRelationship = relationshipRepository.findByUserIdAndTargetUserId(targetId, actorId) val inverseRelationship = relationshipRepository.findByUserIdAndTargetUserId(targetId, actorId)
if (inverseRelationship?.following == true) {
actorRepository.save(targetActor.decrementFollowing())
actorRepository.save(user.decrementFollowers())
}
val blockedInverseRelationship = inverseRelationship
?.copy(followRequest = false, following = false) ?.copy(followRequest = false, following = false)
relationshipRepository.save(relationship) relationshipRepository.save(blockedRelationship)
if (inverseRelationship != null) { if (blockedInverseRelationship != null) {
relationshipRepository.save(inverseRelationship) relationshipRepository.save(blockedInverseRelationship)
} }
val remoteUser = isRemoteUser(targetId) val remoteUser = isRemoteUser(targetId)
if (remoteUser != null) { if (remoteUser != null) {
val user = actorQueryService.findById(actorId)
apSendBlockService.sendBlock(user, remoteUser) apSendBlockService.sendBlock(user, remoteUser)
} }
} }
@ -157,13 +177,18 @@ class RelationshipServiceImpl(
val copy = relationship.copy(followRequest = false, following = true, blocking = false) val copy = relationship.copy(followRequest = false, following = true, blocking = false)
val user = actorQueryService.findById(actorId)
actorRepository.save(user.incrementFollowers())
relationshipRepository.save(copy) relationshipRepository.save(copy)
val remoteUser = isRemoteUser(targetId) val remoteActor = actorQueryService.findById(targetId)
if (remoteUser != null) { actorRepository.save(remoteActor.incrementFollowing())
val user = actorQueryService.findById(actorId)
apSendAcceptService.sendAcceptFollow(user, remoteUser) if (isRemoteActor(remoteActor)) {
apSendAcceptService.sendAcceptFollow(user, remoteActor)
} }
} }
@ -216,6 +241,14 @@ class RelationshipServiceImpl(
return return
} }
val user = actorQueryService.findById(actorId)
val targetActor = actorQueryService.findById(targetId)
if (relationship.following) {
actorRepository.save(user.decrementFollowing())
actorRepository.save(targetActor.decrementFollowers())
}
if (relationship.following.not()) { if (relationship.following.not()) {
logger.warn("SUCCESS User already unfollow. userId: {} targetId: {}", actorId, targetId) logger.warn("SUCCESS User already unfollow. userId: {} targetId: {}", actorId, targetId)
return return
@ -228,7 +261,7 @@ class RelationshipServiceImpl(
val remoteUser = isRemoteUser(targetId) val remoteUser = isRemoteUser(targetId)
if (remoteUser != null) { if (remoteUser != null) {
val user = actorQueryService.findById(actorId)
apSendUndoService.sendUndoFollow(user, remoteUser) apSendUndoService.sendUndoFollow(user, remoteUser)
} }
} }
@ -282,6 +315,8 @@ class RelationshipServiceImpl(
relationshipRepository.save(relationship) relationshipRepository.save(relationship)
} }
private fun isRemoteActor(actor: Actor): Boolean = actor.domain != applicationConfig.url.host
private suspend fun isRemoteUser(userId: Long): Actor? { private suspend fun isRemoteUser(userId: Long): Actor? {
logger.trace("isRemoteUser({})", userId) logger.trace("isRemoteUser({})", userId)
val user = try { val user = try {