Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat : 친구 초대 삭제 API 추가 및 기존 API 동시성 제어, 검증 추가 #457 #466

Merged
merged 14 commits into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,32 @@ ResponseEntity<ApiSpec<String>> acceptFriendRequest(
Long friendId
);

@Operation(
summary = "소셜 친구 요청 삭제",
description = "사용자가 보낸 소셜 친구 요청을 삭제한다.",
security = {@SecurityRequirement(name = "user_token")},
tags = {"friend"}
)
@ApiResponses(value = {
@ApiResponse(
responseCode = "200",
description = "처리 완료"
),
@ApiResponse(
responseCode = "400",
description = """
잘못된 요청 파라미터 또는 존재하지 않는 사용자로 요청하거나 존재하지 않는 친구에게 친구 요청을 보낼 경우 발생
""",
content = @Content(schema = @Schema(implementation = ErrorResponse.class))
)
})
ResponseEntity<ApiSpec<String>> deleteSendingFriendInvite(
Long memberId,

@Parameter(in = ParameterIn.PATH, required = true, schema = @Schema())
Long friendId
);

@Operation(
summary = "소셜 친구 삭제",
description = "사용자의 소셜 친구를 삭제한다.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.springframework.web.bind.annotation.RestController;
import site.timecapsulearchive.core.domain.friend.data.request.SendFriendRequest;
import site.timecapsulearchive.core.domain.friend.service.command.FriendCommandService;
import site.timecapsulearchive.core.domain.member.service.MemberService;
import site.timecapsulearchive.core.global.common.response.ApiSpec;
import site.timecapsulearchive.core.global.common.response.SuccessCode;

Expand All @@ -20,6 +21,7 @@
public class FriendCommandApiController implements FriendCommandApi {

private final FriendCommandService friendCommandService;
private final MemberService memberService;

@DeleteMapping(value = "/{friend_id}")
@Override
Expand All @@ -36,6 +38,22 @@ public ResponseEntity<ApiSpec<String>> deleteFriend(
);
}

@DeleteMapping("/{friend_id}/sending-invites")
@Override
public ResponseEntity<ApiSpec<String>> deleteSendingFriendInvite(
@AuthenticationPrincipal final Long memberId,
@PathVariable("friend_id") final Long friendId
) {
friendCommandService.deleteSendingFriendInvite(memberId, friendId);

return ResponseEntity.ok(
ApiSpec.empty(
SuccessCode.SUCCESS
)
);
}


@DeleteMapping("/{friend_id}/deny-request")
@Override
public ResponseEntity<ApiSpec<String>> denyFriendRequest(
Expand Down Expand Up @@ -95,6 +113,4 @@ public ResponseEntity<ApiSpec<String>> acceptFriendRequest(
)
);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package site.timecapsulearchive.core.domain.friend.data.dto;

import java.util.Objects;

public record FriendInviteMemberIdsDto(
Long ownerId,
Long friendId
) {

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}

FriendInviteMemberIdsDto that = (FriendInviteMemberIdsDto) o;
return (Objects.equals(ownerId, that.ownerId) && Objects.equals(friendId, that.friendId))
|| (Objects.equals(ownerId, that.friendId) && Objects.equals(friendId, that.ownerId));
}

@Override
public int hashCode() {
return Objects.hash(ownerId, friendId);
}
GaBaljaintheroom marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package site.timecapsulearchive.core.domain.friend.entity;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
Expand All @@ -26,11 +27,11 @@ public class FriendInvite extends BaseEntity {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
GaBaljaintheroom marked this conversation as resolved.
Show resolved Hide resolved
@JoinColumn(name = "owner_id", nullable = false)
private Member owner;

@ManyToOne(fetch = FetchType.LAZY)
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
@JoinColumn(name = "friend_id", nullable = false)
private Member friend;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package site.timecapsulearchive.core.domain.friend.exception;

import site.timecapsulearchive.core.global.error.ErrorCode;
import site.timecapsulearchive.core.global.error.exception.BusinessException;

public class FriendInviteDuplicateException extends BusinessException {

public FriendInviteDuplicateException() {
super(ErrorCode.FRIEND_INVITE_DUPLICATE_ERROR);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
import site.timecapsulearchive.core.global.error.ErrorCode;
import site.timecapsulearchive.core.global.error.exception.BusinessException;

public class FriendDuplicateIdException extends BusinessException {
public class SelfFriendOperationException extends BusinessException {

public FriendDuplicateIdException() {
super(ErrorCode.FRIEND_DUPLICATE_ID_ERROR);
public SelfFriendOperationException() {
super(ErrorCode.SELF_FRIEND_OPERATION_ERROR);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import java.time.ZonedDateTime;
import java.util.List;
import java.util.Optional;
import org.springframework.data.domain.Slice;
import site.timecapsulearchive.core.domain.friend.data.dto.FriendInviteMemberIdsDto;
import site.timecapsulearchive.core.domain.friend.data.dto.FriendSummaryDto;

public interface FriendInviteQueryRepository {
Expand All @@ -21,4 +23,10 @@ Slice<FriendSummaryDto> findFriendSendingInvitesSlice(
final int size,
final ZonedDateTime createdAt
);

List<FriendInviteMemberIdsDto> findFriendInviteMemberIdsDtoByMemberIdsAndFriendId(
List<Long> memberIds, Long friendId);

Optional<FriendInviteMemberIdsDto> findFriendInviteMemberIdsDtoByMemberIdAndFriendId(
Long memberId, Long friendId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static site.timecapsulearchive.core.domain.friend.entity.QFriendInvite.friendInvite;
import static site.timecapsulearchive.core.domain.member.entity.QMember.member;

import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.sql.PreparedStatement;
Expand All @@ -11,13 +12,13 @@
import java.sql.Types;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.SliceImpl;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import site.timecapsulearchive.core.domain.friend.data.dto.FriendInviteMemberIdsDto;
import site.timecapsulearchive.core.domain.friend.data.dto.FriendSummaryDto;
import site.timecapsulearchive.core.global.util.SliceUtil;

Expand Down Expand Up @@ -107,4 +108,49 @@ public Slice<FriendSummaryDto> findFriendSendingInvitesSlice(

return SliceUtil.makeSlice(size, friends);
}

@Override
public List<FriendInviteMemberIdsDto> findFriendInviteMemberIdsDtoByMemberIdsAndFriendId(
List<Long> memberIds, Long friendId) {
BooleanBuilder multipleColumnsInCondition = new BooleanBuilder();
for (Long memberId : memberIds) {
multipleColumnsInCondition.or(friendInvite.owner.id.eq(memberId)
.and(friendInvite.friend.id.eq(friendId)));

multipleColumnsInCondition.or(friendInvite.owner.id.eq(friendId)
.and(friendInvite.friend.id.eq(memberId)));
}

return jpaQueryFactory
.select(
Projections.constructor(
FriendInviteMemberIdsDto.class,
friendInvite.owner.id,
friendInvite.friend.id
)
)
.from(friendInvite)
.where(multipleColumnsInCondition)
.fetch();
}

@Override
public Optional<FriendInviteMemberIdsDto> findFriendInviteMemberIdsDtoByMemberIdAndFriendId(
Long memberId, Long friendId) {
return Optional.ofNullable(
jpaQueryFactory
.select(
Projections.constructor(
FriendInviteMemberIdsDto.class,
friendInvite.owner.id,
friendInvite.friend.id
)
)
.from(friendInvite)
.where(friendInvite.owner.id.eq(memberId).and(friendInvite.friend.id.eq(friendId))
.or(friendInvite.owner.id.eq(friendId)
.and(friendInvite.friend.id.eq(memberId))))
.fetchOne()
);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package site.timecapsulearchive.core.domain.friend.repository.friend_invite;

import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
Expand All @@ -12,21 +11,21 @@ public interface FriendInviteRepository extends Repository<FriendInvite, Long>,

void save(FriendInvite friendInvite);

@Query(value = "select fi "
+ "from FriendInvite fi "
+ "join fetch fi.owner "
+ "join fetch fi.friend "
+ "where (fi.owner.id =:friendId and fi.friend.id =:memberId) "
+ "or (fi.owner.id =: memberId and fi.friend.id =: friendId)")
List<FriendInvite> findFriendInviteWithMembersByOwnerIdAndFriendId(
@Param(value = "memberId") Long memberId,
void delete(FriendInvite friendInvite);

Optional<FriendInvite> findFriendSendingInviteForUpdateByOwnerIdAndFriendId(Long memberId,
Long friendId);

@Query(value = """
select fi
from FriendInvite fi
join fetch fi.owner
join fetch fi.friend
where fi.owner.id =:ownerId and fi.friend.id =:friendId
""")
Optional<FriendInvite> findFriendReceivingInviteForUpdateByOwnerIdAndFriendId(
@Param(value = "ownerId") Long ownerId,
@Param(value = "friendId") Long friendId
);

Optional<FriendInvite> findFriendInviteByOwnerIdAndFriendId(Long memberId, Long targetId);

int deleteFriendInviteByOwnerIdAndFriendId(Long memberId, Long targetId);

void delete(FriendInvite friendInvite);
}

Loading
Loading