Skip to content

Commit

Permalink
Merge pull request #68 from NU-BIM/feat/user-block
Browse files Browse the repository at this point in the history
[NB-249, NB-251] 사용자 간 차단 기능 구현
  • Loading branch information
Aram-su authored Nov 5, 2024
2 parents 7e2714a + 011065d commit d507d8c
Show file tree
Hide file tree
Showing 16 changed files with 382 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.soyeon.nubim.domain.user.LoggedInUserService;
import com.soyeon.nubim.domain.user.User;
import com.soyeon.nubim.domain.user.UserService;
import com.soyeon.nubim.domain.user_block.UserBlockService;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
Expand All @@ -37,6 +38,7 @@ public class PostControllerV1 {
private final PostService postService;
private final UserService userService;
private final LoggedInUserService loggedInUserService;
private final UserBlockService userBlockService;

private static final int DEFAULT_SIMPLE_PAGE_SIZE = 10;
private static final int DEFAULT_MAIN_PAGE_SIZE = 5;
Expand Down Expand Up @@ -74,7 +76,9 @@ public ResponseEntity<Page<PostMainResponseDto>> getPostsByUserNickname(
@PathVariable String nickname,
@RequestParam(defaultValue = "0") Long page,
@RequestParam(defaultValue = "desc") String sort) {
User user = userService.getUserByNickname(nickname);
User targetUser = userService.getUserByNickname(nickname);
User currentUser = new User(loggedInUserService.getCurrentUserId());
userBlockService.checkBlockRelation(currentUser, targetUser);

PageRequest pageRequest;
if (sort.equals("desc")) {
Expand All @@ -86,7 +90,7 @@ public ResponseEntity<Page<PostMainResponseDto>> getPostsByUserNickname(
} else {
throw new InvalidQueryParameterException("sort");
}
return ResponseEntity.ok(postService.findAllPostsByUserOrderByCreatedAt(user, pageRequest));
return ResponseEntity.ok(postService.findAllPostsByUserOrderByCreatedAt(targetUser, pageRequest));
}

@DeleteMapping("{postId}")
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/com/soyeon/nubim/domain/user/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ public User findByEmail(String email) {
.orElseThrow(() -> UserNotFoundException.forEmail(email));
}

public User findByNickname(String nickname) {
return userRepository.findByNickname(nickname)
.orElseThrow(()-> UserNotFoundException.forNickname(nickname));
}

public User saveUser(User user) {
return userRepository.save(user);
}
Expand Down
46 changes: 46 additions & 0 deletions src/main/java/com/soyeon/nubim/domain/user_block/UserBlock.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.soyeon.nubim.domain.user_block;

import java.time.LocalDateTime;

import org.hibernate.annotations.CreationTimestamp;

import com.soyeon.nubim.domain.user.User;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "user_block")
public class UserBlock {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long userBlockId;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "blocking_user_id", nullable = false)
private User blockingUser;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "blocked_user_id", nullable = false)
private User blockedUser;

@CreationTimestamp
@Column(nullable = false, updatable = false)
private LocalDateTime blockedAt;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.soyeon.nubim.domain.user_block;

import java.util.List;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.soyeon.nubim.domain.user_block.dto.UserBlockCreateResponse;
import com.soyeon.nubim.domain.user_block.dto.UserBlockDeleteResponse;
import com.soyeon.nubim.domain.user_block.dto.UserBlockReadResponse;
import com.soyeon.nubim.domain.user_block.dto.UserBlockRequest;

import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;

@RestController
@RequestMapping("/v1/user-block")
@RequiredArgsConstructor
public class UserBlockControllerV1 {

private final UserBlockService userBlockService;

@Operation(description = "사용자가 다른 사용자를 차단한다")
@PostMapping
public ResponseEntity<UserBlockCreateResponse> blockUser(@RequestBody UserBlockRequest userBlockRequest) {
UserBlockCreateResponse blockCreateResponse = userBlockService.blockUser(userBlockRequest);

return ResponseEntity.ok().body(blockCreateResponse);
}

@Operation(description = "사용자가 차단한 사용자들의 리스트를 검색한다")
@GetMapping
public ResponseEntity<List<UserBlockReadResponse>> getBlockedUsers() {
List<UserBlockReadResponse> userBlockReadResponses = userBlockService.getBlockedUsers();

return ResponseEntity.ok().body(userBlockReadResponses);
}

@Operation(description = "사용자가 다른 사용자에 대한 차단을 해제한다")
@DeleteMapping
public ResponseEntity<UserBlockDeleteResponse> unblockUser(@RequestBody UserBlockRequest userBlockRequest) {
UserBlockDeleteResponse blockDeleteResponse = userBlockService.unblockUser(userBlockRequest);

return ResponseEntity.ok().body(blockDeleteResponse);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.soyeon.nubim.domain.user_block;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Component;

import com.soyeon.nubim.domain.user.User;
import com.soyeon.nubim.domain.user.UserMapper;
import com.soyeon.nubim.domain.user.dto.UserSimpleResponseDto;
import com.soyeon.nubim.domain.user_block.dto.UserBlockCreateResponse;
import com.soyeon.nubim.domain.user_block.dto.UserBlockReadResponse;

import lombok.RequiredArgsConstructor;

@Component
@RequiredArgsConstructor
public class UserBlockMapper {

private final UserMapper userMapper;

public UserBlock toEntity(User blockingUser, User blockedUser) {
return UserBlock.builder()
.blockingUser(blockingUser)
.blockedUser(blockedUser)
.build();
}

public UserBlockCreateResponse toUserBlockCreateResponse(UserBlock userBlock) {
UserSimpleResponseDto blockedUser = userMapper.toUserSimpleResponseDto(userBlock.getBlockedUser());

return UserBlockCreateResponse.builder()
.blockedUser(blockedUser)
.blockedAt(userBlock.getBlockedAt())
.build();
}

public UserBlockReadResponse toUserBlockReadResponse(UserBlock userBlock) {
UserSimpleResponseDto blockedUser = userMapper.toUserSimpleResponseDto(userBlock.getBlockedUser());

return UserBlockReadResponse.builder()
.blockedUser(blockedUser)
.blockedAt(userBlock.getBlockedAt())
.build();
}

public List<UserBlockReadResponse> toUserBlockReadResponses(List<UserBlock> userBlocks) {
List<UserBlockReadResponse> userBlockReadResponses = new ArrayList<>(userBlocks.size());
for (UserBlock userBlock : userBlocks) {
userBlockReadResponses.add(toUserBlockReadResponse(userBlock));
}
return userBlockReadResponses;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.soyeon.nubim.domain.user_block;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import com.soyeon.nubim.domain.user.User;

@Repository
public interface UserBlockRepository extends JpaRepository<UserBlock, Long> {

@Query("SELECT EXISTS( SELECT 1 FROM UserBlock ub"
+ " WHERE ub.blockingUser = :blockingUser AND ub.blockedUser = :blockedUser)")
boolean existsByBlockingUserAndBlockedUser(User blockingUser, User blockedUser);

@Query("SELECT EXISTS( SELECT 1 FROM UserBlock ub WHERE "
+ "(ub.blockingUser = :user1 AND ub.blockedUser = :user2 ) OR "
+ "(ub.blockingUser = :user2 AND ub.blockedUser = :user1 ))")
boolean existsBlockRelationBetweenUser(User user1, User user2);

@Query("SELECT ub FROM UserBlock ub WHERE ub.blockingUser = :blockingUser")
List<UserBlock> findBlockedUsersByBlockingUser(User blockingUser);

@Modifying
@Query("DELETE FROM UserBlock ub WHERE ub.blockingUser = :blockingUser AND ub.blockedUser = :blockedUser")
int deleteByBlockingUserAndBlockedUser(User blockingUser, User blockedUser);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.soyeon.nubim.domain.user_block;

import java.util.List;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.soyeon.nubim.domain.user.LoggedInUserService;
import com.soyeon.nubim.domain.user.User;
import com.soyeon.nubim.domain.user.UserService;
import com.soyeon.nubim.domain.user_block.dto.UserBlockCreateResponse;
import com.soyeon.nubim.domain.user_block.dto.UserBlockDeleteResponse;
import com.soyeon.nubim.domain.user_block.dto.UserBlockReadResponse;
import com.soyeon.nubim.domain.user_block.dto.UserBlockRequest;
import com.soyeon.nubim.domain.user_block.exception.AlreadyBlockedException;
import com.soyeon.nubim.domain.user_block.exception.BlockedUserAccessDeniedException;
import com.soyeon.nubim.domain.user_block.exception.MultipleUserBlockDeletedException;
import com.soyeon.nubim.domain.user_block.exception.SelfBlockException;
import com.soyeon.nubim.domain.user_block.exception.UserBlockDeleteFailException;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class UserBlockService {

private static final int DELETE_SUCCESS = 1;
private static final int DELETE_FAIL = 0;
private final LoggedInUserService loggedInUserService;
private final UserService userService;
private final UserBlockRepository userBlockRepository;
private final UserBlockMapper userBlockMapper;

public UserBlockCreateResponse blockUser(UserBlockRequest userBlockRequest) {
Long currentUserId = loggedInUserService.getCurrentUserId();
User blockingUser = new User(currentUserId);
User blockedUser = userService.findByNickname(userBlockRequest.getBlockedUserNickname());

checkSelfBlock(blockingUser, blockedUser);
validateUserBlockNotExists(blockingUser, blockedUser);

UserBlock userBlock = userBlockMapper.toEntity(blockingUser, blockedUser);
UserBlock savedUserBlock = userBlockRepository.save(userBlock);

return userBlockMapper.toUserBlockCreateResponse(savedUserBlock);
}

public List<UserBlockReadResponse> getBlockedUsers() {
Long currentUserId = loggedInUserService.getCurrentUserId();
User blockingUser = new User(currentUserId);

List<UserBlock> blockedUsers = userBlockRepository.findBlockedUsersByBlockingUser(blockingUser);
return userBlockMapper.toUserBlockReadResponses(blockedUsers);
}

@Transactional
public UserBlockDeleteResponse unblockUser(UserBlockRequest userBlockRequest) {
Long currentUserId = loggedInUserService.getCurrentUserId();
User blockingUser = new User(currentUserId);
User blockedUser = userService.findByNickname(userBlockRequest.getBlockedUserNickname());

int deleteResult = userBlockRepository.deleteByBlockingUserAndBlockedUser(blockingUser, blockedUser);

if (deleteResult == DELETE_SUCCESS) {
return new UserBlockDeleteResponse("unblock success");
}
if (deleteResult == DELETE_FAIL) {
throw new UserBlockDeleteFailException();
}
throw new MultipleUserBlockDeletedException();
}

public void checkBlockRelation(User currentUser, User targetUser) {
if (userBlockRepository.existsBlockRelationBetweenUser(currentUser, targetUser)) {
throw new BlockedUserAccessDeniedException();
}
}

private void checkSelfBlock(User blockingUser, User blockedUser) {
if (blockingUser.getUserId().equals(blockedUser.getUserId())) {
throw new SelfBlockException();
}
}

private void validateUserBlockNotExists(User blockingUser, User blockedUser) {
if (userBlockRepository.existsByBlockingUserAndBlockedUser(blockingUser, blockedUser)) {
throw new AlreadyBlockedException();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.soyeon.nubim.domain.user_block.dto;

import java.time.LocalDateTime;

import com.soyeon.nubim.domain.user.dto.UserSimpleResponseDto;

import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class UserBlockCreateResponse {
private UserSimpleResponseDto blockedUser;
private LocalDateTime blockedAt;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.soyeon.nubim.domain.user_block.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class UserBlockDeleteResponse {
private String message;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.soyeon.nubim.domain.user_block.dto;

import java.time.LocalDateTime;

import com.soyeon.nubim.domain.user.dto.UserSimpleResponseDto;

import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class UserBlockReadResponse {
private UserSimpleResponseDto blockedUser;
private LocalDateTime blockedAt;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.soyeon.nubim.domain.user_block.dto;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class UserBlockRequest {
private String blockedUserNickname;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.soyeon.nubim.domain.user_block.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.server.ResponseStatusException;

public class AlreadyBlockedException extends ResponseStatusException {
public AlreadyBlockedException() {
super(HttpStatus.CONFLICT, "Already Blocked User");
}
}
Loading

0 comments on commit d507d8c

Please sign in to comment.