diff --git a/_endpoint_test/member.http b/_endpoint_test/member.http index d4709c74..81d9b930 100644 --- a/_endpoint_test/member.http +++ b/_endpoint_test/member.http @@ -32,12 +32,22 @@ Authorization: {{authorization}} ### 친구 관계 생성 API POST {{host}}/member/friend Content-Type: application/json +Authorization: {{authorization}} { "fromMemberId": "22ee8f4c-c2db-49c5-ad5d-22db7bb67796", "toMemberId": "71400a62-b535-48c3-967b-5a25a64137ae" } +### 친구 관계 해제 API +DELETE {{host}}/member/friend +Content-Type: application/json +Authorization: {{authorization}} + +{ + "fromMemberId": "22ee8f4c-c2db-49c5-ad5d-22db7bb67796", + "toMemberId": "71400a62-b535-48c3-967b-5a25a64137ae" +} ### 마이스페이스 내가 받은 픽 조회 API GET {{host}}/member/my-space/pick diff --git a/api/src/main/kotlin/com/mashup/dojo/MemberController.kt b/api/src/main/kotlin/com/mashup/dojo/MemberController.kt index 2f93f820..54cc76dc 100644 --- a/api/src/main/kotlin/com/mashup/dojo/MemberController.kt +++ b/api/src/main/kotlin/com/mashup/dojo/MemberController.kt @@ -8,6 +8,7 @@ import com.mashup.dojo.domain.MemberRelationId import com.mashup.dojo.dto.FriendSpacePickResponse import com.mashup.dojo.dto.MemberCreateFriendRelationRequest import com.mashup.dojo.dto.MemberCreateRequest +import com.mashup.dojo.dto.MemberDeleteFriendRelationRequest import com.mashup.dojo.dto.MemberLoginRequest import com.mashup.dojo.dto.MemberProfileResponse import com.mashup.dojo.dto.MemberSearchResponse @@ -21,6 +22,7 @@ import io.github.oshai.kotlinlogging.KotlinLogging import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.responses.ApiResponse import io.swagger.v3.oas.annotations.tags.Tag +import org.springframework.web.bind.annotation.DeleteMapping import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PatchMapping import org.springframework.web.bind.annotation.PathVariable @@ -206,6 +208,23 @@ class MemberController( return DojoApiResponse.success(memberUseCase.updateFriendRelation(MemberUseCase.UpdateFriendCommand(request.fromMemberId, request.toMemberId))) } + @DeleteMapping("/member/friend") + @Operation( + summary = "친구(팔로우)해제 API", + description = "from 이 to 에 대한 친구(팔로우)를 해제합니다. 이미 친구 관계가 아니라면 예외를 반환해요." + ) + fun deleteFriend( + @RequestBody request: MemberDeleteFriendRelationRequest, + ): DojoApiResponse { + val command = + MemberUseCase.UpdateFriendCommand( + fromId = request.fromMemberId, + toId = request.toMemberId + ) + memberUseCase.deleteFriendRelation(command) + return DojoApiResponse.success() + } + @GetMapping("/member/my-space/pick") @Operation( summary = "마이 스페이스 내가 받은 픽 API", diff --git a/api/src/main/kotlin/com/mashup/dojo/dto/MemberCreateRequest.kt b/api/src/main/kotlin/com/mashup/dojo/dto/MemberCreateRequest.kt index a3e4d1fa..e5af701f 100644 --- a/api/src/main/kotlin/com/mashup/dojo/dto/MemberCreateRequest.kt +++ b/api/src/main/kotlin/com/mashup/dojo/dto/MemberCreateRequest.kt @@ -35,3 +35,13 @@ data class MemberCreateFriendRelationRequest( @Schema(description = "팔로우 대상 유저 id") val toMemberId: MemberId, ) + +@Schema(description = "팔로우 해제 요청") +data class MemberDeleteFriendRelationRequest( + @field:NotBlank + @Schema(description = "팔로우 해제 요청한 유저 id") + val fromMemberId: MemberId, + @field:NotBlank + @Schema(description = "팔로우 해제 대상 유저 id") + val toMemberId: MemberId, +) diff --git a/common/src/main/kotlin/com/mashup/dojo/DojoExceptionType.kt b/common/src/main/kotlin/com/mashup/dojo/DojoExceptionType.kt index 7c3c4a70..b2b82ba0 100644 --- a/common/src/main/kotlin/com/mashup/dojo/DojoExceptionType.kt +++ b/common/src/main/kotlin/com/mashup/dojo/DojoExceptionType.kt @@ -42,6 +42,7 @@ enum class DojoExceptionType( // friend FRIEND_NOT_FOUND("Friend not found", "C070_FRIEND_NOT_FOUND", 400), ALREADY_FRIEND("Already Friend", "C071_ALREADY_FRIEND", 400), + ALREADY_ACCOMPANY("Already Accompany", "C071_ALREADY_ACCOMPANY", 400), // auth INVALID_TOKEN("invalid token", "C100_INVALID_TOKEN", 401), diff --git a/service/src/main/kotlin/com/mashup/dojo/domain/MemberRelation.kt b/service/src/main/kotlin/com/mashup/dojo/domain/MemberRelation.kt index 7201d58a..08b4fe50 100644 --- a/service/src/main/kotlin/com/mashup/dojo/domain/MemberRelation.kt +++ b/service/src/main/kotlin/com/mashup/dojo/domain/MemberRelation.kt @@ -35,6 +35,10 @@ data class MemberRelation( fun updateToFriend(): MemberRelation { return this.copy(relation = RelationType.FRIEND, lastUpdatedAt = LocalDateTime.now()) } + + fun accompany(): MemberRelation { + return this.copy(relation = RelationType.ACCOMPANY, lastUpdatedAt = LocalDateTime.now()) + } } enum class RelationType { diff --git a/service/src/main/kotlin/com/mashup/dojo/service/MemberRelationService.kt b/service/src/main/kotlin/com/mashup/dojo/service/MemberRelationService.kt index ecbd137d..aa1460bd 100644 --- a/service/src/main/kotlin/com/mashup/dojo/service/MemberRelationService.kt +++ b/service/src/main/kotlin/com/mashup/dojo/service/MemberRelationService.kt @@ -34,6 +34,11 @@ interface MemberRelationService { toId: MemberId, ): MemberRelationId + fun deleteFriendRelation( + fromId: MemberId, + toId: MemberId, + ) + fun isFriend( fromId: MemberId, toId: MemberId, @@ -107,6 +112,18 @@ class DefaultMemberRelationService( return MemberRelationId(memberRelationRepository.save(updatedRelation.toEntity()).id) } + @Transactional + override fun deleteFriendRelation( + fromId: MemberId, + toId: MemberId, + ) { + val relation = memberRelationRepository.findByFromIdAndToId(fromId.value, toId.value)?.toDomain() ?: throw DojoException.of(DojoExceptionType.FRIEND_NOT_FOUND) + if (relation.relation == RelationType.ACCOMPANY) { + throw DojoException.of(DojoExceptionType.ALREADY_ACCOMPANY) + } + relation.accompany() + } + override fun isFriend( fromId: MemberId, toId: MemberId, diff --git a/service/src/main/kotlin/com/mashup/dojo/usecase/MemberUseCase.kt b/service/src/main/kotlin/com/mashup/dojo/usecase/MemberUseCase.kt index 201bf228..2439588b 100644 --- a/service/src/main/kotlin/com/mashup/dojo/usecase/MemberUseCase.kt +++ b/service/src/main/kotlin/com/mashup/dojo/usecase/MemberUseCase.kt @@ -84,6 +84,8 @@ interface MemberUseCase { fun updateFriendRelation(command: UpdateFriendCommand): MemberRelationId + fun deleteFriendRelation(command: UpdateFriendCommand) + fun receivedMySpacePicks(currentMemberId: MemberId): List fun searchMember( @@ -196,6 +198,11 @@ class DefaultMemberUseCase( return memberRelationService.updateRelationToFriend(command.fromId, command.toId) } + @Transactional + override fun deleteFriendRelation(command: MemberUseCase.UpdateFriendCommand) { + return memberRelationService.deleteFriendRelation(command.fromId, command.toId) + } + override fun searchMember( memberId: MemberId, keyword: String,