사용자 친구 추가 기능 구현
2024. 12. 26. 18:41ㆍTIL
아주 기본적인 채팅 구현과 사용자 초대의 기능구현을 완료했으므로 , 이 다음은 사용자의 친구 추가 기능을 구현할 것이다.
친구 추가 요청,수락 거절 등은 HTTP요청으로 처리하고 친구 요청의 상태는 WebSocket을 이용하여 실시간으로 업데이트 하는 식으로 구현해 보았다.
친구 관계를 정의하는 엔티티 생성
더보기
package hjp.hjchat.domain.member.entity
import hjp.hjchat.domain.member.dto.FriendshipStatus
import jakarta.persistence.*
import java.time.LocalDateTime
@Entity
@Table(name = "friendships")
class Friendship(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0,
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
val user: MemberEntity,
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "friend_id", nullable = false)
val friend: MemberEntity,
@Enumerated(EnumType.STRING)
@Column(name = "status", nullable = false)
var status: FriendshipStatus = FriendshipStatus.PENDING,
@Column(name = "created_at", nullable = false)
val createdAt: LocalDateTime = LocalDateTime.now()
)
package hjp.hjchat.domain.member.entity
import jakarta.persistence.*
import java.time.LocalDateTime
@Entity
@Table(name = "friend_requests")
class FriendRequest(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0,
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "sender_id", nullable = false)
val sender: MemberEntity,
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "receiver_id", nullable = false)
val receiver: MemberEntity,
@Column(name = "status", nullable = false)
@Enumerated(EnumType.STRING)
var status: RequestStatus = RequestStatus.PENDING,
@Column(name = "created_at", nullable = false)
val createdAt: LocalDateTime = LocalDateTime.now()
)
enum class RequestStatus {
PENDING, // 요청 중
ACCEPTED, // 요청 수락됨
REJECTED // 요청 거절됨
}
컨트롤러 작성
더보기
package hjp.hjchat.domain.member.controller
import hjp.hjchat.domain.member.dto.FriendNotificationDto
import hjp.hjchat.domain.member.dto.FriendRequestDto
import hjp.hjchat.domain.member.dto.FriendShipDto
import hjp.hjchat.domain.member.service.FriendsService
import hjp.hjchat.domain.member.service.MemberService
import hjp.hjchat.infra.security.jwt.UserPrincipal
import org.springframework.http.ResponseEntity
import org.springframework.messaging.simp.SimpMessagingTemplate
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.web.bind.annotation.*
@RestController
@RequestMapping("/friends")
class FriendsController(
private val friendsService: FriendsService,
private val messagingTemplate: SimpMessagingTemplate,
private val memberService: MemberService,
) {
@GetMapping("my_request")
fun getSentFriendRequests(
@AuthenticationPrincipal user: UserPrincipal,
):ResponseEntity<List<FriendShipDto>>{
return ResponseEntity.ok(friendsService.getSentFriendRequests(user.memberId))
}
@GetMapping("/request")
fun getReceivedFriendRequests(
@AuthenticationPrincipal user: UserPrincipal
): ResponseEntity<List<FriendShipDto>> {
val receivedRequests = friendsService.getReceivedFriendRequests(user.memberId)
return ResponseEntity.ok(receivedRequests)
}
@PostMapping("/request")
fun sendFriendRequest(
@AuthenticationPrincipal user: UserPrincipal,
@RequestBody request: FriendRequestDto
): ResponseEntity<FriendShipDto> {
val friendship = friendsService.sendFriendRequest(user.memberId, request.friendId)
// WebSocket 알림 전송
messagingTemplate.convertAndSend("/topic/friend/${request.friendId}",
FriendNotificationDto(type = "REQUEST", senderName = memberService.getUserInfo(user).userName)
)
return ResponseEntity.ok(friendship)
}
@PostMapping("/accept")
fun acceptFriendRequest(
@AuthenticationPrincipal user: UserPrincipal,
@RequestBody request: FriendRequestDto
): ResponseEntity<FriendShipDto> {
println(" 사용자가 수락할때의 사용자의 userId = ${user.memberId}")
val friendship = friendsService.acceptFriendRequest(userId = user.memberId, senderId = request.friendId)
// WebSocket 알림 전송
messagingTemplate.convertAndSend("/topic/friend/${request.friendId}",
FriendNotificationDto(type = "ACCEPT", senderName = memberService.getUserInfo(user).userName)
)
return ResponseEntity.ok(friendship)
}
}
비즈니스 로직 작성
더보기
package hjp.hjchat.domain.member.service
import hjp.hjchat.domain.member.dto.FriendShipDto
import hjp.hjchat.domain.member.dto.FriendshipStatus
import hjp.hjchat.domain.member.entity.FriendRequest
import hjp.hjchat.domain.member.entity.Friendship
import hjp.hjchat.domain.member.entity.RequestStatus
import hjp.hjchat.domain.member.model.FriendRequestRepository
import hjp.hjchat.domain.member.model.FriendshipRepository
import hjp.hjchat.infra.security.ouath.model.OAuthRepository
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import kotlin.jvm.optionals.getOrNull
@Service
class FriendsService(
private val oAuthRepository: OAuthRepository,
private val friendshipRepository: FriendshipRepository,
private val friendRequestRepository: FriendRequestRepository,
) {
@Transactional
fun sendFriendRequest(userId: Long, friendId: Long): FriendShipDto {
// 친구와 사용자 존재 확인
val user = oAuthRepository.findById(userId)
.orElseThrow { IllegalArgumentException("사용자를 찾을 수 없습니다.") }
val friend = oAuthRepository.findById(friendId)
.orElseThrow { IllegalArgumentException("친구를 찾을 수 없습니다.") }
// 이미 친구 요청이 존재하는지 확인
if (friendshipRepository.existsByUserAndFriend(user, friend)) {
throw IllegalArgumentException("이미 친구 요청이 존재하거나 친구 관계입니다.")
}
friendRequestRepository.save(
FriendRequest(
sender = user,
receiver = friend,
status = RequestStatus.PENDING
)
)
// 친구 요청 생성
val friendship = friendshipRepository.save(
Friendship(
user = user,
friend = friend,
status = FriendshipStatus.PENDING
)
)
return FriendShipDto(
userId = friendship.user.id,
friendId = friendship.friend.id,
status = friendship.status.toString(),
senderName = friendship.friend.userName
)
}
@Transactional
fun acceptFriendRequest(userId: Long, senderId: Long): FriendShipDto {
// 친구 요청 찾기
val friendRequest = friendRequestRepository.findBySenderIdAndReceiverId(senderId = senderId, receiverId = userId)
?: throw IllegalArgumentException("친구 요청을 찾을 수 없습니다.")
// 요청 상태 업데이트
friendRequest.status = RequestStatus.ACCEPTED
friendRequestRepository.save(friendRequest)
// 친구 관계 생성 또는 업데이트
val friendship = friendshipRepository.findByUserIdAndFriendId(userId, senderId)
?: Friendship(user = friendRequest.receiver, friend = friendRequest.sender, status = FriendshipStatus.ACCEPTED)
friendship.status = FriendshipStatus.ACCEPTED
friendshipRepository.save(friendship)
// 친구 요청 삭제
friendRequestRepository.delete(friendRequest)
return FriendShipDto(
userId = friendship.user.id,
friendId = friendship.friend.id,
status = friendship.status.toString(),
senderName = friendship.friend.userName
)
}
fun getSentFriendRequests(userId: Long): List<FriendShipDto> {
val requests = friendRequestRepository.findBySenderId(userId)
?: throw IllegalArgumentException("해당 유저를 찾을수 없음 ${userId}.")
return requests.map {
FriendShipDto(
userId = userId,
friendId = it.receiver.id,
status = it.status.toString(),
senderName = it.receiver.userName
)
}
}
fun getReceivedFriendRequests(userId: Long): List<FriendShipDto> {
val requests = friendRequestRepository.findByReceiverId(userId)
?: throw IllegalArgumentException("해당 유저를 찾을수 없음 ${userId}.")
return requests.map {
FriendShipDto(
friendId =it.sender.id,
userId = userId,
status = it.status.toString(),
senderName = it.sender.userName
)
}
}
}
시연 영상: https://www.youtube.com/watch?v=pUKUsKN6fv4
'TIL' 카테고리의 다른 글
Apache Kafka (0) | 2024.12.30 |
---|---|
친구 목록 조회 기능 구현 (1) | 2024.12.27 |
𝒘𝒆𝒃𝒔𝒐𝒄𝒌𝒆𝒕에서 사용자 정보 가져오기 오류 (0) | 2024.12.25 |
WebSocket을 이용해서 실시간 채팅 구현하기 (0) | 2024.12.24 |
채팅방 생성(유저 초대) 구현 (0) | 2024.12.18 |