Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
@@ -0,0 +1,14 @@
package com.codeit.todo.common.exception.follow;

import com.codeit.todo.common.exception.EntityNotFoundException;

public class FollowNotFoundException extends EntityNotFoundException {
private static final String ENTITY_TYPE = "follow";

/**
* @param request 엔티티를 찾기 위해 요청한 값
*/
public FollowNotFoundException(String request) {
super(request, ENTITY_TYPE);
}
}
7 changes: 6 additions & 1 deletion src/main/java/com/codeit/todo/domain/Follow.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,10 @@ public Follow(User follower, User followee){
this.followee = followee;
}


public static Follow from(User follower, User followee) {
return Follow.builder()
.follower(follower)
.followee(followee)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.springframework.data.repository.query.Param;

import java.util.List;
import java.util.Optional;

public interface FollowRepository extends JpaRepository<Follow, Integer> {
@Query(
Expand All @@ -28,5 +29,7 @@ public interface FollowRepository extends JpaRepository<Follow, Integer> {

@Query("select f.followee.userId from Follow f where f.follower.userId = :userId")
List<Integer> findFolloweeIdsByFollowerId(@Param("userId") int userId);

Optional<Follow> findByFollower_UserIdAndFollowee_UserId(int followerId, int followeeId);
}

Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package com.codeit.todo.service.follow;

import com.codeit.todo.web.dto.request.follow.ReadFollowRequest;
import com.codeit.todo.web.dto.response.follow.CreateFollowResponse;
import com.codeit.todo.web.dto.response.follow.DeleteFollowResponse;
import com.codeit.todo.web.dto.response.follow.ReadFollowResponse;
import org.springframework.data.domain.Slice;

public interface FollowService {
Slice<ReadFollowResponse> readFollows(int userId, ReadFollowRequest request);

CreateFollowResponse registerFollow(int followerId, int followeeId);

DeleteFollowResponse cancelFollow(int followerId, int followeeId);
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
package com.codeit.todo.service.follow.impl;

import com.codeit.todo.common.exception.auth.AuthorizationDeniedException;
import com.codeit.todo.common.exception.follow.FollowNotFoundException;
import com.codeit.todo.common.exception.user.UserNotFoundException;
import com.codeit.todo.domain.Complete;
import com.codeit.todo.domain.Follow;
import com.codeit.todo.domain.User;
import com.codeit.todo.repository.CompleteRepository;
import com.codeit.todo.repository.FollowRepository;
import com.codeit.todo.repository.LikesRepository;
import com.codeit.todo.repository.UserRepository;
import com.codeit.todo.service.follow.FollowService;
import com.codeit.todo.web.dto.request.follow.ReadFollowRequest;
import com.codeit.todo.web.dto.response.follow.CreateFollowResponse;
import com.codeit.todo.web.dto.response.follow.DeleteFollowResponse;
import com.codeit.todo.web.dto.response.follow.ReadFollowResponse;
import com.codeit.todo.web.dto.response.slice.CustomSlice;
import lombok.RequiredArgsConstructor;
Expand All @@ -26,6 +34,7 @@ public class FollowServiceImpl implements FollowService {
private final FollowRepository followRepository;
private final CompleteRepository completeRepository;
private final LikesRepository likesRepository;
private final UserRepository userRepository;

@Transactional(readOnly = true)
@Override
Expand Down Expand Up @@ -55,4 +64,38 @@ public Slice<ReadFollowResponse> readFollows(int userId, ReadFollowRequest reque

return new CustomSlice<>(responses, pageable, completes.hasNext(), nextCursor);
}

@Override
public CreateFollowResponse registerFollow(int followerId, int followeeId) {
if (followRepository.existsByFollower_FollowerIdAndFollowee_FolloweeId(followeeId, followerId)) {
throw new AuthorizationDeniedException("이미 팔로우로 등록한 회원입니다.");
}

User follower = getUser(followerId);
User followee = getUser(followeeId);

Follow follow = Follow.from(follower, followee);
Follow savedFollow = followRepository.save(follow);

return CreateFollowResponse.fromEntity(savedFollow);
}

@Override
public DeleteFollowResponse cancelFollow(int followerId, int followeeId) {
if (!followRepository.existsByFollower_FollowerIdAndFollowee_FolloweeId(followeeId, followerId)) {
throw new AuthorizationDeniedException("팔로우 내역이 존재하지 않습니다.");
}

Follow follow = followRepository.findByFollower_UserIdAndFollowee_UserId(followerId, followeeId)
.orElseThrow(() -> new FollowNotFoundException("FollowerId : " + followerId + ", FolloweeId : " + followeeId));

followRepository.delete(follow);

return DeleteFollowResponse.from(followerId, followeeId);
}

private User getUser(int userId) {
return userRepository.findById(userId)
.orElseThrow(() -> new UserNotFoundException(String.valueOf(userId), "User"));
}
}
41 changes: 36 additions & 5 deletions src/main/java/com/codeit/todo/web/controller/FollowController.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@
import com.codeit.todo.repository.CustomUserDetails;
import com.codeit.todo.service.follow.FollowService;
import com.codeit.todo.web.dto.request.follow.ReadFollowRequest;
import com.codeit.todo.web.dto.request.todo.ReadTodoCompleteWithGoalRequest;
import com.codeit.todo.web.dto.response.Response;
import com.codeit.todo.web.dto.response.follow.CreateFollowResponse;
import com.codeit.todo.web.dto.response.follow.DeleteFollowResponse;
import com.codeit.todo.web.dto.response.follow.ReadFollowResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Slice;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
Expand All @@ -23,6 +24,10 @@ public class FollowController {
private final FollowService followService;


@Operation(summary = "팔로우의 최근 등록한 인증글 목록 조회", description = "팔로우의 최근 등록한 인증글 목록 조회 API")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "조회 성공")
})
@GetMapping
public Response<Slice<ReadFollowResponse>> getFollows(
@AuthenticationPrincipal CustomUserDetails userDetails,
Expand All @@ -32,4 +37,30 @@ public Response<Slice<ReadFollowResponse>> getFollows(
int userId = userDetails.getUserId();
return Response.ok(followService.readFollows(userId, request));
}

@Operation(summary = "팔로우 등록", description = "팔로우 등록 API")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "조회 성공")
})
@PostMapping("{followerId}")
public Response<CreateFollowResponse> createFollow(
@AuthenticationPrincipal CustomUserDetails userDetails,
@PathVariable int followerId
) {
int followeeId = userDetails.getUserId();
return Response.ok(followService.registerFollow(followerId, followeeId));
}

@Operation(summary = "팔로우 취소", description = "팔로우 취소 API")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "조회 성공")
})
@DeleteMapping("{followerId}")
public Response<DeleteFollowResponse> cancelFollow(
@AuthenticationPrincipal CustomUserDetails userDetails,
@PathVariable int followerId
) {
int followeeId = userDetails.getUserId();
return Response.ok(followService.cancelFollow(followerId, followeeId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.codeit.todo.web.dto.response.follow;

import com.codeit.todo.domain.Follow;
import lombok.Builder;

@Builder
public record CreateFollowResponse(
int followId
) {
public static CreateFollowResponse fromEntity(Follow follow) {
return CreateFollowResponse.builder()
.followId(follow.getFollowId())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.codeit.todo.web.dto.response.follow;

import lombok.Builder;

@Builder
public record DeleteFollowResponse(int followerId, int followeeId) {
public static DeleteFollowResponse from(int followerId, int followeeId) {
return DeleteFollowResponse.builder()
.followerId(followerId)
.followeeId(followeeId)
.build();
}
}
Loading