From 0779c92ec44960bb20230111156aad78e171ad1c Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sat, 24 Feb 2024 15:13:46 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=E6=98=8E=E7=A4=BA=E7=9A=84?= =?UTF-8?q?=E3=81=AB=E3=83=95=E3=82=A9=E3=83=AD=E3=83=AF=E3=83=BC/?= =?UTF-8?q?=E3=83=95=E3=82=A9=E3=83=AD=E3=82=A4=E3=83=BC/=E6=8A=95?= =?UTF-8?q?=E7=A8=BF=E3=81=AE=E6=95=B0=E3=82=92=E6=9B=B4=E6=96=B0=E3=81=99?= =?UTF-8?q?=E3=82=8B=E9=96=A2=E6=95=B0=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/domain/model/post/PostRepository.kt | 2 ++ .../relationship/RelationshipRepository.kt | 8 +++++-- .../hideout/core/service/user/UserService.kt | 2 ++ .../core/service/user/UserServiceImpl.kt | 21 +++++++++++++++++-- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostRepository.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostRepository.kt index d02c8506..390b8d0b 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostRepository.kt @@ -30,4 +30,6 @@ interface PostRepository { suspend fun findByApId(apId: String): Post? suspend fun existByApIdWithLock(apId: String): Boolean suspend fun findByActorId(actorId: Long): List + + suspend fun countByActorId(actorId: Long): Int } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt index 827b7f49..ff1dc1cc 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt @@ -52,17 +52,21 @@ interface RelationshipRepository { suspend fun findByTargetIdAndFollowing(targetId: Long, following: Boolean): List + suspend fun countByTargetIdAndFollowing(targetId: Long, following: Boolean): Int + + suspend fun countByUserIdAndFollowing(targetId: Long, following: Boolean): Int + @Suppress("FunctionMaxLength") suspend fun findByTargetIdAndFollowRequestAndIgnoreFollowRequest( targetId: Long, followRequest: Boolean, ignoreFollowRequest: Boolean, - page: Page.PageByMaxId + page: Page.PageByMaxId, ): PaginationList suspend fun findByActorIdAndMuting( actorId: Long, muting: Boolean, - page: Page.PageByMaxId + page: Page.PageByMaxId, ): PaginationList } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserService.kt index 2c632f57..50e8d485 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserService.kt @@ -33,4 +33,6 @@ interface UserService { suspend fun deleteRemoteActor(actorId: Long) suspend fun deleteLocalUser(userId: Long) + + suspend fun updateUserStatistics(userId: Long) } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt index c15e5f79..5a459405 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt @@ -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.deletedActor.DeletedActor 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.relationship.RelationshipRepository import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail @@ -47,8 +48,8 @@ class UserServiceImpl( private val reactionRepository: ReactionRepository, private val relationshipRepository: RelationshipRepository, private val postService: PostService, - private val apSendDeleteService: APSendDeleteService - + private val apSendDeleteService: APSendDeleteService, + private val postRepository: PostRepository, ) : UserService { @@ -191,6 +192,22 @@ class UserServiceImpl( 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 { private val logger = LoggerFactory.getLogger(UserServiceImpl::class.java) } From 208d1c519a1677b84f407c4fa2b520b98899af4e Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sat, 24 Feb 2024 15:24:00 +0900 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20=E6=98=8E=E7=A4=BA=E7=9A=84?= =?UTF-8?q?=E3=81=AB=E6=83=85=E5=A0=B1=E3=82=92=E3=82=A2=E3=83=83=E3=83=97?= =?UTF-8?q?=E3=83=87=E3=83=BC=E3=83=88=E3=81=99=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/AccountNotFoundException.kt | 54 +++++++++++++++++++ .../service/account/AccountApiService.kt | 21 ++++++-- 2 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/AccountNotFoundException.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/AccountNotFoundException.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/AccountNotFoundException.kt new file mode 100644 index 00000000..0e52b36a --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/AccountNotFoundException.kt @@ -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) : super(response) + constructor(message: String?, response: MastodonApiErrorResponse) : super(message, response) + constructor(message: String?, cause: Throwable?, response: MastodonApiErrorResponse) : super( + message, + cause, + response + ) + + constructor(cause: Throwable?, response: MastodonApiErrorResponse) : super(cause, response) + constructor( + message: String?, + cause: Throwable?, + enableSuppression: Boolean, + writableStackTrace: Boolean, + response: MastodonApiErrorResponse, + ) : super(message, cause, enableSuppression, writableStackTrace, response) + + fun getTypedResponse(): MastodonApiErrorResponse = + response as MastodonApiErrorResponse + + companion object { + fun ofId(id: Long): AccountNotFoundException = AccountNotFoundException( + "id: $id was not found.", + MastodonApiErrorResponse( + NotFoundResponse( + "Record not found" + ), + 404 + ), + ) + } +} \ No newline at end of file diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt index de3ab853..7c091999 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt @@ -19,6 +19,7 @@ package dev.usbharu.hideout.mastodon.service.account import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.application.infrastructure.exposed.Page 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.service.media.MediaService 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.UserService 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.query.StatusQueryService import org.slf4j.LoggerFactory @@ -127,6 +129,8 @@ class AccountApiServiceImpl( } override suspend fun verifyCredentials(userid: Long): CredentialAccount = transaction.transaction { + userService.updateUserStatistics(userid) + val account = accountService.findById(userid) from(account) } @@ -141,8 +145,16 @@ class AccountApiServiceImpl( return@transaction fetchRelationship(loginUser, followTargetUserId) } - override suspend fun account(id: Long): Account = transaction.transaction { - return@transaction accountService.findById(id) + override suspend fun account(id: Long): Account { + return try { + transaction.transaction { + userService.updateUserStatistics(id) + return@transaction accountService.findById(id) + } + } catch (e: UserNotFoundException) { + logger.debug("Account Not found $id") + throw AccountNotFoundException.ofId(id) + } } override suspend fun relationships(userid: Long, id: List, withSuspended: Boolean): List = @@ -160,7 +172,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) fetchRelationship(userid, target) @@ -339,6 +351,9 @@ class AccountApiServiceImpl( ignoreFollowRequestToTarget = false ) + userService.updateUserStatistics(userid) + userService.updateUserStatistics(targetId) + return Relationship( id = targetId.toString(), following = relationship.following, From 5fad278b0b1193e73f709e8006b6cd13483b4d76 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sat, 24 Feb 2024 15:26:13 +0900 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=E3=82=A2=E3=82=AB=E3=82=A6?= =?UTF-8?q?=E3=83=B3=E3=83=88=E3=81=8C=E8=A6=8B=E3=81=A4=E3=81=8B=E3=82=89?= =?UTF-8?q?=E3=81=AA=E3=81=8B=E3=81=A3=E3=81=9F=E5=A0=B4=E5=90=88=E3=81=AE?= =?UTF-8?q?=E3=82=A8=E3=83=A9=E3=83=BC=E3=83=8F=E3=83=B3=E3=83=89=E3=83=AA?= =?UTF-8?q?=E3=83=B3=E3=82=B0=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../springweb/MastodonApiControllerAdvice.kt | 7 +++++++ .../hideout/mastodon/service/account/AccountApiService.kt | 1 - 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/springweb/MastodonApiControllerAdvice.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/springweb/MastodonApiControllerAdvice.kt index 003b071f..b154f52b 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/springweb/MastodonApiControllerAdvice.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/springweb/MastodonApiControllerAdvice.kt @@ -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.UnprocessableEntityResponse 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.interfaces.api.account.MastodonAccountApiController 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) } + @ExceptionHandler(AccountNotFoundException::class) + fun handleException(ex: AccountNotFoundException): ResponseEntity { + logger.warn("Account not found.", ex) + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getTypedResponse().response) + } + companion object { private val logger = LoggerFactory.getLogger(MastodonApiControllerAdvice::class.java) } diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt index 7c091999..c9f15567 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt @@ -152,7 +152,6 @@ class AccountApiServiceImpl( return@transaction accountService.findById(id) } } catch (e: UserNotFoundException) { - logger.debug("Account Not found $id") throw AccountNotFoundException.ofId(id) } } From 7848a5da295ee094bec77c82ddfb2a9a1297727c Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sat, 24 Feb 2024 15:57:51 +0900 Subject: [PATCH 4/4] =?UTF-8?q?feat:=20=E6=9C=AA=E5=AE=9F=E8=A3=85?= =?UTF-8?q?=E3=81=A0=E3=81=A3=E3=81=9F=E3=82=82=E3=81=AE=E3=82=92=E5=AE=9F?= =?UTF-8?q?=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../relationship/RelationshipRepository.kt | 2 +- .../RelationshipRepositoryImpl.kt | 24 +++++++++++++++++-- .../exposedrepository/PostRepositoryImpl.kt | 8 +++++++ .../core/service/user/ActorServiceTest.kt | 6 +++-- 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt index ff1dc1cc..4780d30d 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt @@ -54,7 +54,7 @@ interface RelationshipRepository { suspend fun countByTargetIdAndFollowing(targetId: Long, following: Boolean): Int - suspend fun countByUserIdAndFollowing(targetId: Long, following: Boolean): Int + suspend fun countByUserIdAndFollowing(userId: Long, following: Boolean): Int @Suppress("FunctionMaxLength") suspend fun findByTargetIdAndFollowRequestAndIgnoreFollowRequest( diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt index ebf46d50..481c3dd6 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt @@ -92,11 +92,31 @@ class RelationshipRepositoryImpl : RelationshipRepository, AbstractRepository() .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( targetId: Long, followRequest: Boolean, ignoreFollowRequest: Boolean, - page: Page.PageByMaxId + page: Page.PageByMaxId, ): PaginationList = query { val query = Relationships.selectAll().where { Relationships.targetActorId.eq(targetId).and(Relationships.followRequest.eq(followRequest)) @@ -115,7 +135,7 @@ class RelationshipRepositoryImpl : RelationshipRepository, AbstractRepository() override suspend fun findByActorIdAndMuting( actorId: Long, muting: Boolean, - page: Page.PageByMaxId + page: Page.PageByMaxId, ): PaginationList = query { val query = Relationships.selectAll().where { Relationships.actorId.eq(actorId).and(Relationships.muting.eq(muting)) } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/PostRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/PostRepositoryImpl.kt index 76b9b82b..ce2ec9ea 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/PostRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/PostRepositoryImpl.kt @@ -133,6 +133,14 @@ class PostRepositoryImpl( .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 { Posts.deleteWhere { Posts.id eq id } } diff --git a/src/test/kotlin/dev/usbharu/hideout/core/service/user/ActorServiceTest.kt b/src/test/kotlin/dev/usbharu/hideout/core/service/user/ActorServiceTest.kt index 0ff56011..e0eac4e4 100644 --- a/src/test/kotlin/dev/usbharu/hideout/core/service/user/ActorServiceTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/core/service/user/ActorServiceTest.kt @@ -62,7 +62,8 @@ class ActorServiceTest { reactionRepository = mock(), relationshipRepository = mock(), postService = mock(), - apSendDeleteService = mock() + apSendDeleteService = mock(), + postRepository = mock() ) userService.createLocalUser(UserCreateDto("test", "testUser", "XXXXXXXXXXXXX", "test")) verify(actorRepository, times(1)).save(any()) @@ -100,7 +101,8 @@ class ActorServiceTest { reactionRepository = mock(), relationshipRepository = mock(), postService = mock(), - apSendDeleteService = mock() + apSendDeleteService = mock(), + postRepository = mock() ) val user = RemoteUserCreateDto( name = "test",