-
Notifications
You must be signed in to change notification settings - Fork 4
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
채팅방 생성 api 추가 #234
채팅방 생성 api 추가 #234
Changes from 23 commits
6fb3d89
21ade4c
5dfba44
537de24
1c8ba25
c72d0ae
db1f7e8
927a6c2
9e23903
c8dabf1
0dcf9d8
f8cab38
6402af0
8e8eaf1
8099e62
6b953d2
ea2f171
2930458
ecc4f9d
473695d
da6c18f
8df6a77
b4fcd7e
3dd1da0
acbb62f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package com.ddang.ddang.auction.domain.exception; | ||
|
||
public class WinnerNotFoundException extends IllegalArgumentException { | ||
|
||
public WinnerNotFoundException(final String message) { | ||
super(message); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,13 @@ | ||
package com.ddang.ddang.chat.application; | ||
|
||
import com.ddang.ddang.auction.application.exception.AuctionNotFoundException; | ||
import com.ddang.ddang.auction.domain.Auction; | ||
import com.ddang.ddang.auction.domain.exception.WinnerNotFoundException; | ||
import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; | ||
import com.ddang.ddang.chat.application.dto.CreateChatRoomDto; | ||
import com.ddang.ddang.chat.application.dto.ReadParticipatingChatRoomDto; | ||
import com.ddang.ddang.chat.application.exception.ChatRoomNotFoundException; | ||
import com.ddang.ddang.chat.application.exception.InvalidAuctionToChatException; | ||
import com.ddang.ddang.chat.application.exception.UserNotAccessibleException; | ||
import com.ddang.ddang.chat.domain.ChatRoom; | ||
import com.ddang.ddang.chat.infrastructure.persistence.JpaChatRoomRepository; | ||
|
@@ -22,23 +28,69 @@ public class ChatRoomService { | |
|
||
private final JpaChatRoomRepository chatRoomRepository; | ||
private final JpaUserRepository userRepository; | ||
private final JpaAuctionRepository auctionRepository; | ||
|
||
@Transactional | ||
public Long create(final Long userId, final CreateChatRoomDto chatRoomDto) { | ||
final User findUser = userRepository.findById(userId) | ||
.orElseThrow(() -> new UserNotFoundException("사용자 정보를 찾을 수 없습니다.")); | ||
final Auction findAuction = auctionRepository.findById(chatRoomDto.auctionId()) | ||
.orElseThrow(() -> | ||
new AuctionNotFoundException("해당 경매를 찾을 수 없습니다.")); | ||
|
||
final ChatRoom persistChatRoom = findOrCreateChatRoomByAuction(findUser, findAuction); | ||
|
||
return persistChatRoom.getId(); | ||
} | ||
|
||
private ChatRoom findOrCreateChatRoomByAuction(final User user, final Auction auction) { | ||
return chatRoomRepository.findByAuctionId(auction.getId()) | ||
.orElseGet(() -> createAndSaveChatRoom(user, auction)); | ||
} | ||
|
||
private ChatRoom createAndSaveChatRoom(final User user, final Auction auction) { | ||
checkAuctionStatus(auction); | ||
final User winner = auction.findWinner(LocalDateTime.now()) | ||
.orElseThrow(() -> new WinnerNotFoundException("낙찰자가 존재하지 않습니다")); | ||
checkUserCanParticipate(user, auction); | ||
|
||
final ChatRoom chatRoom = new ChatRoom(auction, winner); | ||
|
||
return chatRoomRepository.save(chatRoom); | ||
} | ||
|
||
private void checkAuctionStatus(final Auction findAuction) { | ||
if (!findAuction.isClosed(LocalDateTime.now())) { | ||
throw new InvalidAuctionToChatException("경매가 아직 종료되지 않았습니다."); | ||
} | ||
if (findAuction.isDeleted()) { | ||
throw new InvalidAuctionToChatException("삭제된 경매입니다."); | ||
} | ||
} | ||
|
||
private void checkUserCanParticipate(final User findUser, final Auction findAuction) { | ||
if (!isSellerOrWinner(findUser, findAuction)) { | ||
throw new UserNotAccessibleException("경매의 판매자 또는 최종 낙찰자만 채팅이 가능합니다."); | ||
} | ||
} | ||
|
||
private boolean isSellerOrWinner(final User findUser, final Auction findAuction) { | ||
return findAuction.isOwner(findUser) || findAuction.isWinner(findUser, LocalDateTime.now()); | ||
} | ||
|
||
public List<ReadParticipatingChatRoomDto> readAllByUserId(final Long userId) { | ||
final User findUser = findUser(userId); | ||
final User findUser = userRepository.findById(userId) | ||
.orElseThrow(() -> new UserNotFoundException("사용자 정보를 찾을 수 없습니다.")); | ||
final List<ChatRoom> chatRooms = chatRoomRepository.findAllByUserId(findUser.getId()); | ||
|
||
return chatRooms.stream() | ||
.map(chatRoom -> toDto(findUser, chatRoom)) | ||
.map(chatRoom -> ReadParticipatingChatRoomDto.of(findUser, chatRoom, LocalDateTime.now())) | ||
.toList(); | ||
} | ||
|
||
private User findUser(final Long userId) { | ||
return userRepository.findById(userId) | ||
.orElseThrow(() -> new UserNotFoundException("사용자 정보를 찾을 수 없습니다.")); | ||
} | ||
|
||
public ReadParticipatingChatRoomDto readByChatRoomId(final Long chatRoomId, final Long userId) { | ||
final User findUser = findUser(userId); | ||
final User findUser = userRepository.findById(userId) | ||
.orElseThrow(() -> new UserNotFoundException("사용자 정보를 찾을 수 없습니다.")); | ||
final ChatRoom chatRoom = chatRoomRepository.findById(chatRoomId) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 부분과 관련해 갑자기 궁금해진 부분이 있어 여쭤봅니다. |
||
.orElseThrow(() -> | ||
new ChatRoomNotFoundException( | ||
|
@@ -47,19 +99,12 @@ public ReadParticipatingChatRoomDto readByChatRoomId(final Long chatRoomId, fina | |
); | ||
checkAccessible(findUser, chatRoom); | ||
|
||
return toDto(findUser, chatRoom); | ||
return ReadParticipatingChatRoomDto.of(findUser, chatRoom, LocalDateTime.now()); | ||
} | ||
|
||
private void checkAccessible(final User findUser, final ChatRoom chatRoom) { | ||
if (!chatRoom.isParticipant(findUser)) { | ||
throw new UserNotAccessibleException("해당 채팅방에 접근할 권한이 없습니다."); | ||
} | ||
} | ||
|
||
private ReadParticipatingChatRoomDto toDto(final User findUser, final ChatRoom chatRoom) { | ||
return ReadParticipatingChatRoomDto.of( | ||
chatRoom.calculateChatPartnerOf(findUser), | ||
chatRoom, | ||
chatRoom.isChatAvailableTime(LocalDateTime.now())); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package com.ddang.ddang.chat.application.dto; | ||
|
||
import com.ddang.ddang.auction.domain.Auction; | ||
import com.ddang.ddang.auction.domain.exception.WinnerNotFoundException; | ||
import com.ddang.ddang.chat.domain.ChatRoom; | ||
import com.ddang.ddang.chat.presentation.dto.request.CreateChatRoomRequest; | ||
import com.ddang.ddang.user.domain.User; | ||
|
||
import java.time.LocalDateTime; | ||
|
||
public record CreateChatRoomDto(Long auctionId) { | ||
|
||
public static CreateChatRoomDto from(final CreateChatRoomRequest chatRoomRequest) { | ||
return new CreateChatRoomDto(chatRoomRequest.auctionId()); | ||
} | ||
|
||
public ChatRoom toEntity(final Auction auction) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 메서드는 테스트에서만 사용되는 메서드인 것 같아요! 그리고 dto에는 비즈니스 로직을 최대한 빼는 것이 좋을 것 같다고 생각하는데, 예외를 던지는 부분은 비즈니스 로직이라고 생각해서 dto가 아닌 다른 도메인이나 서비스에서 수행되는 것이 좋아보입니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아 그렇네요! 제거하였습니다~ |
||
final User winner = auction.findWinner(LocalDateTime.now()) | ||
.orElseThrow(() -> new WinnerNotFoundException("낙찰자가 존재하지 않습니다")); | ||
|
||
|
||
return new ChatRoom(auction, winner); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package com.ddang.ddang.chat.application.exception; | ||
|
||
public class InvalidAuctionToChatException extends IllegalArgumentException { | ||
|
||
public InvalidAuctionToChatException(final String message) { | ||
super(message); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.ddang.ddang.chat.infrastructure.persistence; | ||
|
||
import com.ddang.ddang.chat.domain.ChatRoom; | ||
|
||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
public interface QuerydslChatRoomRepository { | ||
|
||
List<ChatRoom> findAllByUserId(final Long userId); | ||
|
||
Optional<ChatRoom> findByAuctionId(final Long auctionId); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package com.ddang.ddang.chat.infrastructure.persistence; | ||
|
||
import com.ddang.ddang.chat.domain.ChatRoom; | ||
import com.querydsl.core.types.dsl.BooleanExpression; | ||
import com.querydsl.jpa.impl.JPAQueryFactory; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Repository; | ||
|
||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
import static com.ddang.ddang.auction.domain.QAuction.auction; | ||
import static com.ddang.ddang.chat.domain.QChatRoom.chatRoom; | ||
|
||
@Repository | ||
@RequiredArgsConstructor | ||
public class QuerydslChatRoomRepositoryImpl implements QuerydslChatRoomRepository { | ||
|
||
private final JPAQueryFactory queryFactory; | ||
|
||
@Override | ||
public List<ChatRoom> findAllByUserId(final Long userId) { | ||
return queryFactory.selectFrom(chatRoom) | ||
.leftJoin(chatRoom.auction, auction).fetchJoin() | ||
.where(isSellerOrWinner(userId)) | ||
.orderBy(chatRoom.id.desc()) | ||
.fetch(); | ||
} | ||
Comment on lines
+22
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 목록을 조회할 때도 auction관련 검증이나 필요한 정보가 있는지 궁금합니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아! 동일한 dto를 사용하기 위함일까요?? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. chatRoom에는 seller가 없고 buyer만 존재하기 때문에 auction에 있는 seller까지 함께 가져오기 위해 fetchJoin을 사용했습니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 앗 제가 chatRoom에 seller가 없는 것을 잠시 간과했네요..! |
||
|
||
private BooleanExpression isSellerOrWinner(final Long userId) { | ||
return (auction.seller.id.eq(userId)) | ||
.or(chatRoom.buyer.id.eq(userId)); | ||
} | ||
|
||
@Override | ||
public Optional<ChatRoom> findByAuctionId(final Long auctionId) { | ||
final ChatRoom findChatRoom = queryFactory.selectFrom(chatRoom) | ||
.leftJoin(chatRoom.auction, auction).fetchJoin() | ||
.where(auction.id.eq(auctionId)) | ||
.fetchOne(); | ||
|
||
return Optional.ofNullable(findChatRoom); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.ddang.ddang.chat.presentation.dto.request; | ||
|
||
import jakarta.validation.constraints.NotNull; | ||
import jakarta.validation.constraints.Positive; | ||
|
||
public record CreateChatRoomRequest( | ||
@NotNull(message = "경매 아이디가 입력되지 않았습니다.") | ||
@Positive(message = "경매 아이디는 양수입니다.") | ||
Long auctionId | ||
) { | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기에서는 서비스에서 예외를 처리해주고 있었네요
Auction.checkWinnerExist()랑 이거랑 통일성을 지켜주는 방향은 어떨까 건의드려봅니다