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
@@ -1,9 +1,8 @@
package com.assu.server.domain.chat.controller;

import com.assu.server.domain.chat.dto.ChatRequestDTO;
import com.assu.server.domain.chat.dto.ChatResponseDTO;
import com.assu.server.domain.chat.dto.ChatRoomUpdateDTO;
import com.assu.server.domain.chat.dto.*;
import com.assu.server.domain.chat.repository.MessageRepository;
import com.assu.server.domain.chat.service.BlockService;
import com.assu.server.domain.chat.service.ChatService;
import com.assu.server.global.apiPayload.code.status.SuccessStatus;
import com.assu.server.global.util.PresenceTracker;
Expand All @@ -29,6 +28,7 @@ public class ChatController {
private final SimpMessagingTemplate simpMessagingTemplate;
private final PresenceTracker presenceTracker;
private final MessageRepository messageRepository;
private final BlockService blockService;

@Operation(
summary = "채팅방을 생성하는 API",
Expand Down Expand Up @@ -139,4 +139,64 @@ public BaseResponse<ChatResponseDTO.LeaveChattingRoomResponseDTO> leaveChattingR
Long memberId = pd.getMember().getId();
return BaseResponse.onSuccess(SuccessStatus._OK, chatService.leaveChattingRoom(roomId, memberId));
}

@Operation(
summary = "상대방을 차단하는 API" +
"상대방을 차단합니다. 메시지를 주고받을 수 없습니다.",
description = "# [v1.0 (2025-09-25)]() 상대방을 차단합니다.\n"+
"- memberId: Request Body, Long\n"
)
@PostMapping("/block")
public BaseResponse<BlockResponseDTO.BlockMemberDTO> block(
@AuthenticationPrincipal PrincipalDetails pd,
@RequestBody BlockRequestDTO.BlockMemberRequestDTO request
) {
Long memberId = pd.getMember().getId();
return BaseResponse.onSuccess(SuccessStatus._OK, blockService.blockMember(memberId, request.getOpponentId()));
}

@Operation(
summary = "상대방을 차단했는지 확인하는 API" +
"상대방을 차단했는지 여부를 알려줍니다.",
description = "# [v1.0 (2025-09-25)]() 상대방을 차단했는지 검사합니다.\n"+
"- memberId: Request Body, Long\n"
)
@GetMapping("/check/block/{opponentId}")
public BaseResponse<BlockResponseDTO.CheckBlockMemberDTO> checkBlock(
@AuthenticationPrincipal PrincipalDetails pd,
@PathVariable Long opponentId
) {
Long memberId = pd.getMember().getId();
return BaseResponse.onSuccess(SuccessStatus._OK, blockService.checkBlock(memberId, opponentId));
}

@Operation(
summary = "상대방을 차단 해제하는 API" +
"상대방을 차단해제합니다. 앞으로 다시 메시지를 주고받을 수 있습니다.",
description = "# [v1.0 (2025-09-25)]() 상대방을 차단 해제합니다.\n"+
"- memberId: Request Body, Long\n"
)
@DeleteMapping("/unblock")
public BaseResponse<BlockResponseDTO.BlockMemberDTO> unblock(
@AuthenticationPrincipal PrincipalDetails pd,
@RequestParam Long opponentId
) {
Long memberId = pd.getMember().getId();
return BaseResponse.onSuccess(SuccessStatus._OK, blockService.unblockMember(memberId, opponentId));
}

@Operation(
summary = "차단한 대상을 조회합니다." +
"본인이 차단한 대상을 모두 조회합니다.",
description = "# [v1.0 (2025-09-25)]() 차단한 대상을 조회합니다..\n"+
"- memberId: Request Body, Long\n"
)
@GetMapping("/blockList")
public BaseResponse<List<BlockResponseDTO.BlockMemberDTO>> getBlockList(
@AuthenticationPrincipal PrincipalDetails pd
) {
Long memberId = pd.getMember().getId();
return BaseResponse.onSuccess(SuccessStatus._OK, blockService.getMyBlockList(memberId));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.assu.server.domain.chat.converter;

import com.assu.server.domain.chat.dto.BlockResponseDTO;
import com.assu.server.domain.chat.entity.Block;
import com.assu.server.domain.common.enums.UserRole;
import com.assu.server.domain.member.entity.Member;

import java.util.List;
import java.util.stream.Collectors;

public class BlockConverter {
public static BlockResponseDTO.BlockMemberDTO toBlockDTO(Long blockedId, String blockedName) {
return BlockResponseDTO.BlockMemberDTO.builder()
.memberId(blockedId)
.name(blockedName)
.build();
}

public static BlockResponseDTO.CheckBlockMemberDTO toCheckBlockDTO(Long blockedId, String blockedName, boolean blocked) {
return BlockResponseDTO.CheckBlockMemberDTO.builder()
.memberId(blockedId)
.name(blockedName)
.blocked(blocked)
.build();
}

public static BlockResponseDTO.BlockMemberDTO toBlockedMemberDTO(Block block) {
// Block 엔티티에서 차단된 사용자(Member) 정보를 꺼냅니다.
Member blockedMember = block.getBlocked();
UserRole blockedRole = blockedMember.getRole();
String blockedName;
if (blockedRole == UserRole.ADMIN) {
blockedName = blockedMember.getAdminProfile().getName();
} else {
blockedName = blockedMember.getPartnerProfile().getName();
}

return BlockResponseDTO.BlockMemberDTO.builder()
.memberId(blockedMember.getId())
.name(blockedName) // 또는 getNickname() 등 실제 필드명 사용
.build();
}

public static List<BlockResponseDTO.BlockMemberDTO> toBlockedMemberListDTO(List<Block> blockList) {
return blockList.stream()
.map(BlockConverter::toBlockedMemberDTO) // 각 Block 객체에 대해 위 헬퍼 메소드를 호출
.collect(Collectors.toList());
}

}
13 changes: 13 additions & 0 deletions src/main/java/com/assu/server/domain/chat/dto/BlockRequestDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.assu.server.domain.chat.dto;

import lombok.Getter;
import lombok.Setter;

public class BlockRequestDTO {

@Getter
@Setter
public static class BlockMemberRequestDTO {
private Long opponentId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.assu.server.domain.chat.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.List;

public class BlockResponseDTO {

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class BlockMemberDTO {
private Long memberId;
private String name;
}

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class CheckBlockMemberDTO {
private Long memberId;
private String name;
private boolean blocked;
}

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class BlockedMemberListDTO {
List<BlockMemberDTO> blockedMembers;
}

}
31 changes: 31 additions & 0 deletions src/main/java/com/assu/server/domain/chat/entity/Block.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.assu.server.domain.chat.entity;

import com.assu.server.domain.common.entity.BaseEntity;
import com.assu.server.domain.member.entity.Member;
import jakarta.persistence.*;
import lombok.*;

@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Block extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "blocker_id", nullable = false)
private Member blocker; // 차단한 사람

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "blocked_id", nullable = false)
private Member blocked; // 차단당한 사람

@Builder
public Block(Member blocker, Member blocked) {
this.blocker = blocker;
this.blocked = blocked;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.assu.server.domain.chat.repository;


import com.assu.server.domain.chat.entity.Block;
import com.assu.server.domain.member.entity.Member;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;

public interface BlockRepository extends JpaRepository<Block, Long> {
boolean existsByBlockerAndBlocked(Member blocker, Member blocked);

void deleteByBlockerAndBlocked(Member blocker, Member blocked);

List<Block> findByBlocker(Member blocker);

// BlockRepository.java
@Query("SELECT COUNT(b) > 0 FROM Block b " +
"WHERE (b.blocker = :user1 AND b.blocked = :user2) " +
"OR (b.blocker = :user2 AND b.blocked = :user1)")
boolean existsBlockRelationBetween(@Param("user1") Member user1, @Param("user2") Member user2);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.assu.server.domain.chat.service;

import com.assu.server.domain.chat.dto.BlockResponseDTO;

import java.util.List;

public interface BlockService {
BlockResponseDTO.BlockMemberDTO blockMember(Long blockerId, Long blockedId);
BlockResponseDTO.CheckBlockMemberDTO checkBlock(Long blockerId, Long blockedId);
BlockResponseDTO.BlockMemberDTO unblockMember(Long blockerId, Long blockedId);
List<BlockResponseDTO.BlockMemberDTO> getMyBlockList(Long blockerId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package com.assu.server.domain.chat.service;

import com.assu.server.domain.chat.converter.BlockConverter;
import com.assu.server.domain.chat.converter.ChatConverter;
import com.assu.server.domain.chat.dto.BlockResponseDTO;
import com.assu.server.domain.chat.entity.Block;
import com.assu.server.domain.chat.repository.BlockRepository;
import com.assu.server.domain.common.enums.UserRole;
import com.assu.server.domain.member.entity.Member;
import com.assu.server.domain.member.repository.MemberRepository;
import com.assu.server.global.apiPayload.code.status.ErrorStatus;
import com.assu.server.global.exception.GeneralException;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class BlockServiceImpl implements BlockService {
private final BlockRepository blockRepository;
private final MemberRepository memberRepository;

@Transactional
@Override
public BlockResponseDTO.BlockMemberDTO blockMember(Long blockerId, Long blockedId) {
if (blockerId.equals(blockedId)) {
throw new GeneralException(ErrorStatus._BAD_REQUEST);
}

Member blocker = memberRepository.findById(blockerId)
.orElseThrow(() -> new GeneralException(ErrorStatus._BAD_REQUEST));
Member blocked = memberRepository.findById(blockedId)
.orElseThrow(() -> new GeneralException(ErrorStatus._BAD_REQUEST));


// 이미 차단했는지 확인
if (blockRepository.existsByBlockerAndBlocked(blocker, blocked)) {
// 이미 차단한 경우, 아무것도 하지 않거나 예외 처리
return null;
}

UserRole blockedRole = blocked.getRole();
String blockedName;
if (blockedRole == UserRole.ADMIN) {
blockedName = blocked.getAdminProfile().getName();
} else if (blockedRole == UserRole.PARTNER) {
blockedName = blocked.getPartnerProfile().getName();
} else {
throw new GeneralException(ErrorStatus._BAD_REQUEST);
}

Block block = Block.builder()
.blocker(blocker)
.blocked(blocked)
.build();

blockRepository.save(block);

return BlockConverter.toBlockDTO(blockedId, blockedName);
}

@Override
public BlockResponseDTO.CheckBlockMemberDTO checkBlock(Long blockerId, Long blockedId) {

Member blocker = memberRepository.findById(blockerId)
.orElseThrow(() -> new GeneralException(ErrorStatus._BAD_REQUEST));
Member blocked = memberRepository.findById(blockedId)
.orElseThrow(() -> new GeneralException(ErrorStatus._BAD_REQUEST));

UserRole blockedRole = blocked.getRole();
String blockedName;
if (blockedRole == UserRole.ADMIN) {
blockedName = blocked.getAdminProfile().getName();
} else if (blockedRole == UserRole.PARTNER) {
blockedName = blocked.getPartnerProfile().getName();
} else {
throw new GeneralException(ErrorStatus._BAD_REQUEST);
}

if (blockRepository.existsBlockRelationBetween(blocker, blocked)) {
return BlockConverter.toCheckBlockDTO(blockedId, blockedName, true);
}
else {
return BlockConverter.toCheckBlockDTO(blockedId, blockedName, false);
}
}

@Transactional
@Override
public BlockResponseDTO.BlockMemberDTO unblockMember(Long blockerId, Long blockedId) {
Member blocker = memberRepository.findById(blockerId)
.orElseThrow(() -> new GeneralException(ErrorStatus.NO_SUCH_MEMBER));
Member blocked = memberRepository.findById(blockedId)
.orElseThrow(() -> new GeneralException(ErrorStatus.NO_SUCH_MEMBER));

UserRole blockedRole = blocked.getRole();
String blockedName;
if (blockedRole == UserRole.ADMIN) {
blockedName = blocked.getAdminProfile().getName();
} else if (blockedRole == UserRole.PARTNER) {
blockedName = blocked.getPartnerProfile().getName();
} else {
throw new GeneralException(ErrorStatus._BAD_REQUEST);
}

// Transactional 환경에서는 Dirty-checking으로 delete 쿼리가 나갑니다.
blockRepository.deleteByBlockerAndBlocked(blocker, blocked);
return BlockConverter.toBlockDTO(blockedId, blockedName);
}

@Transactional
@Override
public List<BlockResponseDTO.BlockMemberDTO> getMyBlockList(Long blockerId) {
Member blocker = memberRepository.findById(blockerId)
.orElseThrow(() -> new GeneralException(ErrorStatus._BAD_REQUEST));

List<Block> blockList = blockRepository.findByBlocker(blocker);

return BlockConverter.toBlockedMemberListDTO(blockList);
}
}
Loading