mirror of https://github.com/usbharu/Hideout.git
Merge pull request #289 from usbharu/bugfix/posts-count
アカウントの投稿数が増えなかった問題を修正
This commit is contained in:
commit
df68cd237a
|
@ -30,4 +30,6 @@ interface PostRepository {
|
||||||
suspend fun findByApId(apId: String): Post?
|
suspend fun findByApId(apId: String): Post?
|
||||||
suspend fun existByApIdWithLock(apId: String): Boolean
|
suspend fun existByApIdWithLock(apId: String): Boolean
|
||||||
suspend fun findByActorId(actorId: Long): List<Post>
|
suspend fun findByActorId(actorId: Long): List<Post>
|
||||||
|
|
||||||
|
suspend fun countByActorId(actorId: Long): Int
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,17 +52,21 @@ interface RelationshipRepository {
|
||||||
|
|
||||||
suspend fun findByTargetIdAndFollowing(targetId: Long, following: Boolean): List<Relationship>
|
suspend fun findByTargetIdAndFollowing(targetId: Long, following: Boolean): List<Relationship>
|
||||||
|
|
||||||
|
suspend fun countByTargetIdAndFollowing(targetId: Long, following: Boolean): Int
|
||||||
|
|
||||||
|
suspend fun countByUserIdAndFollowing(userId: Long, following: Boolean): Int
|
||||||
|
|
||||||
@Suppress("FunctionMaxLength")
|
@Suppress("FunctionMaxLength")
|
||||||
suspend fun findByTargetIdAndFollowRequestAndIgnoreFollowRequest(
|
suspend fun findByTargetIdAndFollowRequestAndIgnoreFollowRequest(
|
||||||
targetId: Long,
|
targetId: Long,
|
||||||
followRequest: Boolean,
|
followRequest: Boolean,
|
||||||
ignoreFollowRequest: Boolean,
|
ignoreFollowRequest: Boolean,
|
||||||
page: Page.PageByMaxId
|
page: Page.PageByMaxId,
|
||||||
): PaginationList<Relationship, Long>
|
): PaginationList<Relationship, Long>
|
||||||
|
|
||||||
suspend fun findByActorIdAndMuting(
|
suspend fun findByActorIdAndMuting(
|
||||||
actorId: Long,
|
actorId: Long,
|
||||||
muting: Boolean,
|
muting: Boolean,
|
||||||
page: Page.PageByMaxId
|
page: Page.PageByMaxId,
|
||||||
): PaginationList<Relationship, Long>
|
): PaginationList<Relationship, Long>
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,11 +92,31 @@ class RelationshipRepositoryImpl : RelationshipRepository, AbstractRepository()
|
||||||
.map { it.toRelationships() }
|
.map { it.toRelationships() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun countByTargetIdAndFollowing(targetId: Long, following: Boolean): Int = query {
|
||||||
|
return@query Relationships
|
||||||
|
.selectAll()
|
||||||
|
.where {
|
||||||
|
Relationships.targetActorId eq targetId and (Relationships.following eq following)
|
||||||
|
}
|
||||||
|
.count()
|
||||||
|
.toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun countByUserIdAndFollowing(userId: Long, following: Boolean): Int = query {
|
||||||
|
return@query Relationships
|
||||||
|
.selectAll()
|
||||||
|
.where {
|
||||||
|
Relationships.actorId eq userId and (Relationships.following eq following)
|
||||||
|
}
|
||||||
|
.count()
|
||||||
|
.toInt()
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun findByTargetIdAndFollowRequestAndIgnoreFollowRequest(
|
override suspend fun findByTargetIdAndFollowRequestAndIgnoreFollowRequest(
|
||||||
targetId: Long,
|
targetId: Long,
|
||||||
followRequest: Boolean,
|
followRequest: Boolean,
|
||||||
ignoreFollowRequest: Boolean,
|
ignoreFollowRequest: Boolean,
|
||||||
page: Page.PageByMaxId
|
page: Page.PageByMaxId,
|
||||||
): PaginationList<Relationship, Long> = query {
|
): PaginationList<Relationship, Long> = query {
|
||||||
val query = Relationships.selectAll().where {
|
val query = Relationships.selectAll().where {
|
||||||
Relationships.targetActorId.eq(targetId).and(Relationships.followRequest.eq(followRequest))
|
Relationships.targetActorId.eq(targetId).and(Relationships.followRequest.eq(followRequest))
|
||||||
|
@ -115,7 +135,7 @@ class RelationshipRepositoryImpl : RelationshipRepository, AbstractRepository()
|
||||||
override suspend fun findByActorIdAndMuting(
|
override suspend fun findByActorIdAndMuting(
|
||||||
actorId: Long,
|
actorId: Long,
|
||||||
muting: Boolean,
|
muting: Boolean,
|
||||||
page: Page.PageByMaxId
|
page: Page.PageByMaxId,
|
||||||
): PaginationList<Relationship, Long> = query {
|
): PaginationList<Relationship, Long> = query {
|
||||||
val query =
|
val query =
|
||||||
Relationships.selectAll().where { Relationships.actorId.eq(actorId).and(Relationships.muting.eq(muting)) }
|
Relationships.selectAll().where { Relationships.actorId.eq(actorId).and(Relationships.muting.eq(muting)) }
|
||||||
|
|
|
@ -133,6 +133,14 @@ class PostRepositoryImpl(
|
||||||
.selectAll().where { Posts.actorId eq actorId }.let(postQueryMapper::map)
|
.selectAll().where { Posts.actorId eq actorId }.let(postQueryMapper::map)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun countByActorId(actorId: Long): Int = query {
|
||||||
|
return@query Posts
|
||||||
|
.selectAll()
|
||||||
|
.where { Posts.actorId eq actorId }
|
||||||
|
.count()
|
||||||
|
.toInt()
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun delete(id: Long): Unit = query {
|
override suspend fun delete(id: Long): Unit = query {
|
||||||
Posts.deleteWhere { Posts.id eq id }
|
Posts.deleteWhere { Posts.id eq id }
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,4 +33,6 @@ interface UserService {
|
||||||
suspend fun deleteRemoteActor(actorId: Long)
|
suspend fun deleteRemoteActor(actorId: Long)
|
||||||
|
|
||||||
suspend fun deleteLocalUser(userId: Long)
|
suspend fun deleteLocalUser(userId: Long)
|
||||||
|
|
||||||
|
suspend fun updateUserStatistics(userId: Long)
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,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 dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActor
|
import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActor
|
||||||
import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActorRepository
|
import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActorRepository
|
||||||
|
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.reaction.ReactionRepository
|
||||||
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
|
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
|
||||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail
|
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail
|
||||||
|
@ -47,8 +48,8 @@ class UserServiceImpl(
|
||||||
private val reactionRepository: ReactionRepository,
|
private val reactionRepository: ReactionRepository,
|
||||||
private val relationshipRepository: RelationshipRepository,
|
private val relationshipRepository: RelationshipRepository,
|
||||||
private val postService: PostService,
|
private val postService: PostService,
|
||||||
private val apSendDeleteService: APSendDeleteService
|
private val apSendDeleteService: APSendDeleteService,
|
||||||
|
private val postRepository: PostRepository,
|
||||||
) :
|
) :
|
||||||
UserService {
|
UserService {
|
||||||
|
|
||||||
|
@ -191,6 +192,22 @@ class UserServiceImpl(
|
||||||
deletedActorRepository.save(deletedActor)
|
deletedActorRepository.save(deletedActor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun updateUserStatistics(userId: Long) {
|
||||||
|
val actor = actorRepository.findByIdWithLock(userId) ?: throw UserNotFoundException.withId(userId)
|
||||||
|
|
||||||
|
val followerCount = relationshipRepository.countByTargetIdAndFollowing(userId, true)
|
||||||
|
val followingCount = relationshipRepository.countByUserIdAndFollowing(userId, true)
|
||||||
|
val postsCount = postRepository.countByActorId(userId)
|
||||||
|
|
||||||
|
actorRepository.save(
|
||||||
|
actor.copy(
|
||||||
|
followersCount = followerCount,
|
||||||
|
followingCount = followingCount,
|
||||||
|
postsCount = postsCount
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val logger = LoggerFactory.getLogger(UserServiceImpl::class.java)
|
private val logger = LoggerFactory.getLogger(UserServiceImpl::class.java)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 usbharu
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.usbharu.hideout.mastodon.domain.exception
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.domain.mastodon.model.generated.NotFoundResponse
|
||||||
|
import dev.usbharu.hideout.mastodon.domain.model.MastodonApiErrorResponse
|
||||||
|
|
||||||
|
class AccountNotFoundException : ClientException {
|
||||||
|
constructor(response: MastodonApiErrorResponse<NotFoundResponse>) : super(response)
|
||||||
|
constructor(message: String?, response: MastodonApiErrorResponse<NotFoundResponse>) : super(message, response)
|
||||||
|
constructor(message: String?, cause: Throwable?, response: MastodonApiErrorResponse<NotFoundResponse>) : super(
|
||||||
|
message,
|
||||||
|
cause,
|
||||||
|
response
|
||||||
|
)
|
||||||
|
|
||||||
|
constructor(cause: Throwable?, response: MastodonApiErrorResponse<NotFoundResponse>) : super(cause, response)
|
||||||
|
constructor(
|
||||||
|
message: String?,
|
||||||
|
cause: Throwable?,
|
||||||
|
enableSuppression: Boolean,
|
||||||
|
writableStackTrace: Boolean,
|
||||||
|
response: MastodonApiErrorResponse<NotFoundResponse>,
|
||||||
|
) : super(message, cause, enableSuppression, writableStackTrace, response)
|
||||||
|
|
||||||
|
fun getTypedResponse(): MastodonApiErrorResponse<NotFoundResponse> =
|
||||||
|
response as MastodonApiErrorResponse<NotFoundResponse>
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun ofId(id: Long): AccountNotFoundException = AccountNotFoundException(
|
||||||
|
"id: $id was not found.",
|
||||||
|
MastodonApiErrorResponse(
|
||||||
|
NotFoundResponse(
|
||||||
|
"Record not found"
|
||||||
|
),
|
||||||
|
404
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ package dev.usbharu.hideout.mastodon.infrastructure.springweb
|
||||||
import dev.usbharu.hideout.domain.mastodon.model.generated.NotFoundResponse
|
import dev.usbharu.hideout.domain.mastodon.model.generated.NotFoundResponse
|
||||||
import dev.usbharu.hideout.domain.mastodon.model.generated.UnprocessableEntityResponse
|
import dev.usbharu.hideout.domain.mastodon.model.generated.UnprocessableEntityResponse
|
||||||
import dev.usbharu.hideout.domain.mastodon.model.generated.UnprocessableEntityResponseDetails
|
import dev.usbharu.hideout.domain.mastodon.model.generated.UnprocessableEntityResponseDetails
|
||||||
|
import dev.usbharu.hideout.mastodon.domain.exception.AccountNotFoundException
|
||||||
import dev.usbharu.hideout.mastodon.domain.exception.StatusNotFoundException
|
import dev.usbharu.hideout.mastodon.domain.exception.StatusNotFoundException
|
||||||
import dev.usbharu.hideout.mastodon.interfaces.api.account.MastodonAccountApiController
|
import dev.usbharu.hideout.mastodon.interfaces.api.account.MastodonAccountApiController
|
||||||
import dev.usbharu.hideout.mastodon.interfaces.api.apps.MastodonAppsApiController
|
import dev.usbharu.hideout.mastodon.interfaces.api.apps.MastodonAppsApiController
|
||||||
|
@ -98,6 +99,12 @@ class MastodonApiControllerAdvice {
|
||||||
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getTypedResponse().response)
|
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getTypedResponse().response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(AccountNotFoundException::class)
|
||||||
|
fun handleException(ex: AccountNotFoundException): ResponseEntity<NotFoundResponse> {
|
||||||
|
logger.warn("Account not found.", ex)
|
||||||
|
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getTypedResponse().response)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val logger = LoggerFactory.getLogger(MastodonApiControllerAdvice::class.java)
|
private val logger = LoggerFactory.getLogger(MastodonApiControllerAdvice::class.java)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package dev.usbharu.hideout.mastodon.service.account
|
||||||
import dev.usbharu.hideout.application.external.Transaction
|
import dev.usbharu.hideout.application.external.Transaction
|
||||||
import dev.usbharu.hideout.application.infrastructure.exposed.Page
|
import dev.usbharu.hideout.application.infrastructure.exposed.Page
|
||||||
import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList
|
import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList
|
||||||
|
import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException
|
||||||
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
|
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
|
||||||
import dev.usbharu.hideout.core.service.media.MediaService
|
import dev.usbharu.hideout.core.service.media.MediaService
|
||||||
import dev.usbharu.hideout.core.service.relationship.RelationshipService
|
import dev.usbharu.hideout.core.service.relationship.RelationshipService
|
||||||
|
@ -26,6 +27,7 @@ import dev.usbharu.hideout.core.service.user.UpdateUserDto
|
||||||
import dev.usbharu.hideout.core.service.user.UserCreateDto
|
import dev.usbharu.hideout.core.service.user.UserCreateDto
|
||||||
import dev.usbharu.hideout.core.service.user.UserService
|
import dev.usbharu.hideout.core.service.user.UserService
|
||||||
import dev.usbharu.hideout.domain.mastodon.model.generated.*
|
import dev.usbharu.hideout.domain.mastodon.model.generated.*
|
||||||
|
import dev.usbharu.hideout.mastodon.domain.exception.AccountNotFoundException
|
||||||
import dev.usbharu.hideout.mastodon.interfaces.api.media.MediaRequest
|
import dev.usbharu.hideout.mastodon.interfaces.api.media.MediaRequest
|
||||||
import dev.usbharu.hideout.mastodon.query.StatusQueryService
|
import dev.usbharu.hideout.mastodon.query.StatusQueryService
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
@ -127,6 +129,8 @@ class AccountApiServiceImpl(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun verifyCredentials(userid: Long): CredentialAccount = transaction.transaction {
|
override suspend fun verifyCredentials(userid: Long): CredentialAccount = transaction.transaction {
|
||||||
|
userService.updateUserStatistics(userid)
|
||||||
|
|
||||||
val account = accountService.findById(userid)
|
val account = accountService.findById(userid)
|
||||||
from(account)
|
from(account)
|
||||||
}
|
}
|
||||||
|
@ -141,9 +145,16 @@ class AccountApiServiceImpl(
|
||||||
return@transaction fetchRelationship(loginUser, followTargetUserId)
|
return@transaction fetchRelationship(loginUser, followTargetUserId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun account(id: Long): Account = transaction.transaction {
|
override suspend fun account(id: Long): Account {
|
||||||
|
return try {
|
||||||
|
transaction.transaction {
|
||||||
|
userService.updateUserStatistics(id)
|
||||||
return@transaction accountService.findById(id)
|
return@transaction accountService.findById(id)
|
||||||
}
|
}
|
||||||
|
} catch (e: UserNotFoundException) {
|
||||||
|
throw AccountNotFoundException.ofId(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun relationships(userid: Long, id: List<Long>, withSuspended: Boolean): List<Relationship> =
|
override suspend fun relationships(userid: Long, id: List<Long>, withSuspended: Boolean): List<Relationship> =
|
||||||
transaction.transaction {
|
transaction.transaction {
|
||||||
|
@ -160,7 +171,7 @@ class AccountApiServiceImpl(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun block(userid: Long, target: Long): Relationship = transaction.transaction {
|
override suspend fun block(userid: Long, target: Long) = transaction.transaction {
|
||||||
relationshipService.block(userid, target)
|
relationshipService.block(userid, target)
|
||||||
|
|
||||||
fetchRelationship(userid, target)
|
fetchRelationship(userid, target)
|
||||||
|
@ -339,6 +350,9 @@ class AccountApiServiceImpl(
|
||||||
ignoreFollowRequestToTarget = false
|
ignoreFollowRequestToTarget = false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
userService.updateUserStatistics(userid)
|
||||||
|
userService.updateUserStatistics(targetId)
|
||||||
|
|
||||||
return Relationship(
|
return Relationship(
|
||||||
id = targetId.toString(),
|
id = targetId.toString(),
|
||||||
following = relationship.following,
|
following = relationship.following,
|
||||||
|
|
|
@ -62,7 +62,8 @@ class ActorServiceTest {
|
||||||
reactionRepository = mock(),
|
reactionRepository = mock(),
|
||||||
relationshipRepository = mock(),
|
relationshipRepository = mock(),
|
||||||
postService = mock(),
|
postService = mock(),
|
||||||
apSendDeleteService = mock()
|
apSendDeleteService = mock(),
|
||||||
|
postRepository = mock()
|
||||||
)
|
)
|
||||||
userService.createLocalUser(UserCreateDto("test", "testUser", "XXXXXXXXXXXXX", "test"))
|
userService.createLocalUser(UserCreateDto("test", "testUser", "XXXXXXXXXXXXX", "test"))
|
||||||
verify(actorRepository, times(1)).save(any())
|
verify(actorRepository, times(1)).save(any())
|
||||||
|
@ -100,7 +101,8 @@ class ActorServiceTest {
|
||||||
reactionRepository = mock(),
|
reactionRepository = mock(),
|
||||||
relationshipRepository = mock(),
|
relationshipRepository = mock(),
|
||||||
postService = mock(),
|
postService = mock(),
|
||||||
apSendDeleteService = mock()
|
apSendDeleteService = mock(),
|
||||||
|
postRepository = mock()
|
||||||
)
|
)
|
||||||
val user = RemoteUserCreateDto(
|
val user = RemoteUserCreateDto(
|
||||||
name = "test",
|
name = "test",
|
||||||
|
|
Loading…
Reference in New Issue