diff --git a/src/main/java/com/codeit/side/chat/adapter/in/web/response/ChatMessageResponse.java b/src/main/java/com/codeit/side/chat/adapter/in/web/response/ChatMessageResponse.java index effce54..9b455ee 100644 --- a/src/main/java/com/codeit/side/chat/adapter/in/web/response/ChatMessageResponse.java +++ b/src/main/java/com/codeit/side/chat/adapter/in/web/response/ChatMessageResponse.java @@ -1,6 +1,7 @@ package com.codeit.side.chat.adapter.in.web.response; -import com.codeit.side.chat.domain.ChatMessage; +import com.codeit.side.chat.domain.UserChatMessage; +import com.codeit.side.user.domain.User; import java.time.LocalDateTime; @@ -10,16 +11,19 @@ public record ChatMessageResponse( Long userId, String userNickname, String content, - LocalDateTime createdAt + LocalDateTime createdAt, + String userImage ) { - public static ChatMessageResponse from(ChatMessage chatMessage) { + public static ChatMessageResponse from(UserChatMessage chatMessage) { + User user = chatMessage.getUser(); return new ChatMessageResponse( - chatMessage.getId(), - chatMessage.getRoomId(), - chatMessage.getUserId(), - chatMessage.getUserNickname(), - chatMessage.getContent(), - chatMessage.getCreatedAt() + chatMessage.getChatMessage().getId(), + chatMessage.getChatMessage().getRoomId(), + chatMessage.getChatMessage().getUserId(), + chatMessage.getChatMessage().getUserNickname(), + chatMessage.getChatMessage().getContent(), + chatMessage.getChatMessage().getCreatedAt(), + user.isHasImage() ? "https://codeit-doit.s3.ap-northeast-2.amazonaws.com/user/%s/image.jpg".formatted(user.getId()) : "" ); } } diff --git a/src/main/java/com/codeit/side/chat/adapter/in/websocket/ChatWebsocketController.java b/src/main/java/com/codeit/side/chat/adapter/in/websocket/ChatWebsocketController.java index 3c96679..3008ed2 100644 --- a/src/main/java/com/codeit/side/chat/adapter/in/websocket/ChatWebsocketController.java +++ b/src/main/java/com/codeit/side/chat/adapter/in/websocket/ChatWebsocketController.java @@ -41,9 +41,21 @@ public void sendMessage( ChatType.CHAT, chatMessageReceived.getContent() ); - String destination = "/topic/room/%s".formatted(id); - messagingTemplate.convertAndSend(destination, ChatMessageSend.of(chatMessage, "")); + String destination = "/topic/room/" + id; + + String userImage = user.isHasImage() ? "https://codeit-doit.s3.ap-northeast-2.amazonaws.com/user/%s/image.jpg".formatted(user.getId()) : ""; chatMessageUseCase.save(chatMessage); + messagingTemplate.convertAndSend(destination, ChatMessageSend.of(chatMessage, userImage)); + } + + @MessageMapping("/room/{id}/read") + public void readMessage( + @Header(name = "simpSessionAttributes") Map sessionAttributes, + @DestinationVariable Long id + ) { + User user = (User) sessionAttributes.get("user"); + chatMessageUseCase.findChatRoomBy(id, user.getId()); + chatMessageUseCase.read(id, user.getId()); } } diff --git a/src/main/java/com/codeit/side/chat/adapter/out/persistence/ChatMemberRepositoryImpl.java b/src/main/java/com/codeit/side/chat/adapter/out/persistence/ChatMemberRepositoryImpl.java index ea9369e..562b0f8 100644 --- a/src/main/java/com/codeit/side/chat/adapter/out/persistence/ChatMemberRepositoryImpl.java +++ b/src/main/java/com/codeit/side/chat/adapter/out/persistence/ChatMemberRepositoryImpl.java @@ -41,4 +41,14 @@ public boolean existsByChatRoomIdAndUserId(Long id, Long userId) { public void join(Long id, Long userId) { chatMemberJpaRepository.save(ChatMemberEntity.of(id, userId)); } + + @Override + public List findAllMemberById(Long roomId) { + return chatMemberJpaRepository.findAllByChatRoomId(roomId); + } + + @Override + public void leave(Long chatRoomId, Long userId) { + chatMemberJpaRepository.deleteByChatRoomIdAndUserId(chatRoomId, userId); + } } diff --git a/src/main/java/com/codeit/side/chat/adapter/out/persistence/ChatMessageReadRepositoryImpl.java b/src/main/java/com/codeit/side/chat/adapter/out/persistence/ChatMessageReadRepositoryImpl.java new file mode 100644 index 0000000..d18d9a1 --- /dev/null +++ b/src/main/java/com/codeit/side/chat/adapter/out/persistence/ChatMessageReadRepositoryImpl.java @@ -0,0 +1,40 @@ +package com.codeit.side.chat.adapter.out.persistence; + +import com.codeit.side.chat.adapter.out.persistence.entity.ChatMessageReadEntity; +import com.codeit.side.chat.adapter.out.persistence.jpa.ChatMessageReadJpaRepository; +import com.codeit.side.chat.application.port.out.ChatMessageReadRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +@RequiredArgsConstructor +public class ChatMessageReadRepositoryImpl implements ChatMessageReadRepository { + private final ChatMessageReadJpaRepository chatMessageReadJpaRepository; + + @Override + public void save(Long roomId, List userIds) { + List chatMessageReadEntities = userIds.stream() + .map(userId -> ChatMessageReadEntity.of(roomId, userId)) + .toList(); + chatMessageReadJpaRepository.saveAll(chatMessageReadEntities); + } + + @Override + public void read(Long roomId, Long userId) { + chatMessageReadJpaRepository.findAllByChatRoomIdAndUserIdAndIsReadFalse(roomId, userId) + .forEach(ChatMessageReadEntity::read); + } + + @Override + public List findAllUnreadMessages(Long userId, List chatRoomIds) { + return chatMessageReadJpaRepository.findAllByUserIdAndChatRoomIdInAndIsReadFalse(userId, chatRoomIds); + } + + @Override + public int findUnreadMessagesBy(Long chatRoomId, Long userId) { + return chatMessageReadJpaRepository.findAllByChatRoomIdAndUserIdAndIsReadFalse(chatRoomId, userId) + .size(); + } +} diff --git a/src/main/java/com/codeit/side/chat/adapter/out/persistence/ChatRepositoryImpl.java b/src/main/java/com/codeit/side/chat/adapter/out/persistence/ChatRepositoryImpl.java index e8ef62d..9af4fe8 100644 --- a/src/main/java/com/codeit/side/chat/adapter/out/persistence/ChatRepositoryImpl.java +++ b/src/main/java/com/codeit/side/chat/adapter/out/persistence/ChatRepositoryImpl.java @@ -22,8 +22,9 @@ public class ChatRepositoryImpl implements ChatMessageRepository { @Override @Transactional - public void save(ChatMessage chatMessage) { - chatMessageJpaRepository.save(ChatMessageEntity.from(chatMessage)); + public ChatMessage save(ChatMessage chatMessage) { + return chatMessageJpaRepository.save(ChatMessageEntity.from(chatMessage)) + .toDomain(); } @Override diff --git a/src/main/java/com/codeit/side/chat/adapter/out/persistence/entity/ChatMessageReadEntity.java b/src/main/java/com/codeit/side/chat/adapter/out/persistence/entity/ChatMessageReadEntity.java new file mode 100644 index 0000000..d455b34 --- /dev/null +++ b/src/main/java/com/codeit/side/chat/adapter/out/persistence/entity/ChatMessageReadEntity.java @@ -0,0 +1,34 @@ +package com.codeit.side.chat.adapter.out.persistence.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Entity +@NoArgsConstructor +@AllArgsConstructor +@Table( + name = "chat_message_read", + indexes = @Index(name = "idx_chat_room", columnList = "chatRoomId") +) +public class ChatMessageReadEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private Long chatRoomId; + + private Long userId; + + private Boolean isRead; + + public static ChatMessageReadEntity of(Long chatRoomId, Long userId) { + return new ChatMessageReadEntity(null, chatRoomId, userId, false); + } + + public void read() { + isRead = true; + } +} diff --git a/src/main/java/com/codeit/side/chat/adapter/out/persistence/jpa/ChatMemberJpaRepository.java b/src/main/java/com/codeit/side/chat/adapter/out/persistence/jpa/ChatMemberJpaRepository.java index 4493ae0..799e386 100644 --- a/src/main/java/com/codeit/side/chat/adapter/out/persistence/jpa/ChatMemberJpaRepository.java +++ b/src/main/java/com/codeit/side/chat/adapter/out/persistence/jpa/ChatMemberJpaRepository.java @@ -10,4 +10,6 @@ public interface ChatMemberJpaRepository extends JpaRepository findAllByChatRoomId(Long chatRoomId); Optional findByChatRoomIdAndUserId(Long chatRoomId, Long userId); + + void deleteByChatRoomIdAndUserId(Long chatRoomId, Long userId); } diff --git a/src/main/java/com/codeit/side/chat/adapter/out/persistence/jpa/ChatMessageReadJpaRepository.java b/src/main/java/com/codeit/side/chat/adapter/out/persistence/jpa/ChatMessageReadJpaRepository.java new file mode 100644 index 0000000..e68ca79 --- /dev/null +++ b/src/main/java/com/codeit/side/chat/adapter/out/persistence/jpa/ChatMessageReadJpaRepository.java @@ -0,0 +1,13 @@ +package com.codeit.side.chat.adapter.out.persistence.jpa; + +import com.codeit.side.chat.adapter.out.persistence.entity.ChatMessageReadEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Collection; +import java.util.List; + +public interface ChatMessageReadJpaRepository extends JpaRepository { + List findAllByChatRoomIdAndUserIdAndIsReadFalse(Long roomId, Long userId); + + List findAllByUserIdAndChatRoomIdInAndIsReadFalse(Long userId, Collection chatRoomIds); +} diff --git a/src/main/java/com/codeit/side/chat/application/port/in/ChatMessageUseCase.java b/src/main/java/com/codeit/side/chat/application/port/in/ChatMessageUseCase.java index 33bfb0c..fcc47ed 100644 --- a/src/main/java/com/codeit/side/chat/application/port/in/ChatMessageUseCase.java +++ b/src/main/java/com/codeit/side/chat/application/port/in/ChatMessageUseCase.java @@ -6,11 +6,12 @@ import com.codeit.side.chat.domain.ChatRoomInfo; import java.util.List; +import java.util.Map; public interface ChatMessageUseCase { void save(ChatMessage chatMessage); - void createChatRoom(String email, Long lighteningId, String chatRoomName); + ChatRoom createChatRoom(String email, Long lighteningId, String chatRoomName); List findAllChatRooms(String email); @@ -23,4 +24,14 @@ public interface ChatMessageUseCase { ChatRoom getChatRoomByLighteningId(Long lighteningId); List findAllChatRoomsByLighteningIds(List lighteningIds); + + void read(Long roomId, Long userId); + + void leaveChatRoom(Long id, String email); + + void joinChatRoomByLighteningId(Long id, String email); + + Map countAllUnreadMessages(String email, List chatRoomIds); + + int countUnreadMessages(Long lighteningId, String email); } diff --git a/src/main/java/com/codeit/side/chat/application/port/out/ChatMemberRepository.java b/src/main/java/com/codeit/side/chat/application/port/out/ChatMemberRepository.java index 7b5222d..3bb66a7 100644 --- a/src/main/java/com/codeit/side/chat/application/port/out/ChatMemberRepository.java +++ b/src/main/java/com/codeit/side/chat/application/port/out/ChatMemberRepository.java @@ -1,5 +1,6 @@ package com.codeit.side.chat.application.port.out; +import com.codeit.side.chat.adapter.out.persistence.entity.ChatMemberEntity; import com.codeit.side.chat.domain.command.ChatRoomCommand; import java.util.List; @@ -13,4 +14,8 @@ public interface ChatMemberRepository { boolean existsByChatRoomIdAndUserId(Long id, Long userId); void join(Long id, Long userId); + + List findAllMemberById(Long roomId); + + void leave(Long chatRoomId, Long userId); } diff --git a/src/main/java/com/codeit/side/chat/application/port/out/ChatMessageReadRepository.java b/src/main/java/com/codeit/side/chat/application/port/out/ChatMessageReadRepository.java new file mode 100644 index 0000000..32fe103 --- /dev/null +++ b/src/main/java/com/codeit/side/chat/application/port/out/ChatMessageReadRepository.java @@ -0,0 +1,15 @@ +package com.codeit.side.chat.application.port.out; + +import com.codeit.side.chat.adapter.out.persistence.entity.ChatMessageReadEntity; + +import java.util.List; + +public interface ChatMessageReadRepository { + void save(Long roomId, List userIds); + + void read(Long roomId, Long userId); + + List findAllUnreadMessages(Long id, List chatRoomIds); + + int findUnreadMessagesBy(Long lighteningId, Long userId); +} diff --git a/src/main/java/com/codeit/side/chat/application/port/out/ChatMessageRepository.java b/src/main/java/com/codeit/side/chat/application/port/out/ChatMessageRepository.java index 0f7c45b..5b6bfb5 100644 --- a/src/main/java/com/codeit/side/chat/application/port/out/ChatMessageRepository.java +++ b/src/main/java/com/codeit/side/chat/application/port/out/ChatMessageRepository.java @@ -6,7 +6,7 @@ import java.util.Map; public interface ChatMessageRepository { - void save(ChatMessage chatMessage); + ChatMessage save(ChatMessage chatMessage); Map findAllLastMessageByIds(List chatRoomIds); diff --git a/src/main/java/com/codeit/side/chat/application/service/ChatMessageService.java b/src/main/java/com/codeit/side/chat/application/service/ChatMessageService.java index 2051a6e..18ca173 100644 --- a/src/main/java/com/codeit/side/chat/application/service/ChatMessageService.java +++ b/src/main/java/com/codeit/side/chat/application/service/ChatMessageService.java @@ -1,13 +1,13 @@ package com.codeit.side.chat.application.service; +import com.codeit.side.chat.adapter.out.persistence.entity.ChatMemberEntity; +import com.codeit.side.chat.adapter.out.persistence.entity.ChatMessageReadEntity; import com.codeit.side.chat.application.port.in.ChatMessageUseCase; import com.codeit.side.chat.application.port.out.ChatMemberRepository; +import com.codeit.side.chat.application.port.out.ChatMessageReadRepository; import com.codeit.side.chat.application.port.out.ChatMessageRepository; import com.codeit.side.chat.application.port.out.ChatRoomRepository; -import com.codeit.side.chat.domain.ChatMessage; -import com.codeit.side.chat.domain.ChatMessages; -import com.codeit.side.chat.domain.ChatRoom; -import com.codeit.side.chat.domain.ChatRoomInfo; +import com.codeit.side.chat.domain.*; import com.codeit.side.chat.domain.command.ChatRoomCommand; import com.codeit.side.common.adapter.exception.IllegalRequestException; import com.codeit.side.user.application.port.out.UserQueryRepository; @@ -19,6 +19,8 @@ import java.util.List; import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; @Primary @Service @@ -29,21 +31,28 @@ public class ChatMessageService implements ChatMessageUseCase { private final UserQueryRepository userQueryRepository; private final ChatRoomRepository chatRoomRepository; private final ChatMemberRepository chatMemberRepository; + private final ChatMessageReadRepository chatMessageReadRepository; @Override @Transactional public void save(ChatMessage chatMessage) { - chatMessageRepository.save(chatMessage); + ChatMessage savedChatMessage = chatMessageRepository.save(chatMessage); + List chatMemberEntities = chatMemberRepository.findAllMemberById(savedChatMessage.getRoomId()); + List userIds = chatMemberEntities.stream() + .map(ChatMemberEntity::getUserId) + .toList(); + chatMessageReadRepository.save(savedChatMessage.getRoomId(), userIds); } @Override @Transactional - public void createChatRoom(String email, Long lighteningId, String chatRoomName) { + public ChatRoom createChatRoom(String email, Long lighteningId, String chatRoomName) { User host = userQueryRepository.getByEmail(email) .toDomain(); ChatRoomCommand userChatRoomCommand = ChatRoomCommand.of(chatRoomName, lighteningId, host.getId()); ChatRoom chatRoom = chatRoomRepository.save(userChatRoomCommand); chatMemberRepository.save(chatRoom.getId(), userChatRoomCommand); + return chatRoom; } @Override @@ -77,7 +86,21 @@ public ChatMessages findAllMessagesByRoomId(Long roomId, String email, Long offs List messages = chatMessages.stream() .limit(size) .toList(); - return ChatMessages.of(messages, isLast); + List userIds = extractUserIds(chatMessages); + Map idToUser = userQueryRepository.findAllByIds(userIds) + .stream() + .collect(Collectors.toMap(User::getId, Function.identity())); + List userChatMessages = messages.stream() + .map(message -> UserChatMessage.of(message, idToUser.get(message.getUserId()))) + .toList(); + return ChatMessages.of(userChatMessages, isLast); + } + + private List extractUserIds(List chatMessages) { + return chatMessages.stream() + .map(ChatMessage::getUserId) + .distinct() + .toList(); } @Override @@ -86,10 +109,38 @@ public void joinChatRoom(Long id, String email) { User user = userQueryRepository.getByEmail(email) .toDomain(); ChatRoom chatRoom = chatRoomRepository.getBy(id); - if (chatMemberRepository.existsByChatRoomIdAndUserId(chatRoom.getId(), user.getId())) { - throw new IllegalRequestException("이미 채팅방에 참여하고 있는 사용자입니다."); + chatMemberRepository.join(chatRoom.getId(), user.getId()); + } + + @Override + @Transactional + public void joinChatRoomByLighteningId(Long id, String email) { + User user = userQueryRepository.getByEmail(email) + .toDomain(); + ChatRoom chatRoom = chatRoomRepository.getByLighteningId(id); + chatMemberRepository.join(chatRoom.getId(), user.getId()); + } + + @Override + public Map countAllUnreadMessages(String email, List chatRoomIds) { + if ("".equals(email)) { + return Map.of(); + } + User user = userQueryRepository.getByEmail(email) + .toDomain(); + List chatMessageReadEntities = chatMessageReadRepository.findAllUnreadMessages(user.getId(), chatRoomIds); + return chatMessageReadEntities.stream() + .collect(Collectors.groupingBy(ChatMessageReadEntity::getChatRoomId, Collectors.collectingAndThen(Collectors.counting(), Long::intValue))); + } + + @Override + public int countUnreadMessages(Long chatRoomId, String email) { + if ("".equals(email)) { + return 0; } - chatMemberRepository.join(id, user.getId()); + User user = userQueryRepository.getByEmail(email) + .toDomain(); + return chatMessageReadRepository.findUnreadMessagesBy(chatRoomId, user.getId()); } @Override @@ -102,6 +153,21 @@ public List findAllChatRoomsByLighteningIds(List lighteningIds) return chatRoomRepository.findAllByLighteningIds(lighteningIds); } + @Override + @Transactional + public void read(Long roomId, Long userId) { + chatMessageReadRepository.read(roomId, userId); + } + + @Override + @Transactional + public void leaveChatRoom(Long id, String email) { + User user = userQueryRepository.getByEmail(email) + .toDomain(); + ChatRoom chatRoom = chatRoomRepository.getByLighteningId(id); + chatMemberRepository.leave(chatRoom.getId(), user.getId()); + } + private List createChatRoomInfos(List chatRooms, Map allLastMessageByIds, Map idToMemberSize) { return chatRooms.stream() .map(chatRoom -> createChatRoomInfo(chatRoom, allLastMessageByIds.get(chatRoom.getId()), idToMemberSize.get(chatRoom.getId()))) diff --git a/src/main/java/com/codeit/side/chat/domain/ChatMessages.java b/src/main/java/com/codeit/side/chat/domain/ChatMessages.java index 4dfc613..1a5bd17 100644 --- a/src/main/java/com/codeit/side/chat/domain/ChatMessages.java +++ b/src/main/java/com/codeit/side/chat/domain/ChatMessages.java @@ -10,14 +10,14 @@ @Getter @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public class ChatMessages { - private final List messages; + private final List messages; private final boolean isLast; - public static ChatMessages of(List messages, boolean isLast) { + public static ChatMessages of(List messages, boolean isLast) { return new ChatMessages(messages, isLast); } - public Stream stream() { + public Stream stream() { return messages.stream(); } } diff --git a/src/main/java/com/codeit/side/chat/domain/UserChatMessage.java b/src/main/java/com/codeit/side/chat/domain/UserChatMessage.java new file mode 100644 index 0000000..ca84311 --- /dev/null +++ b/src/main/java/com/codeit/side/chat/domain/UserChatMessage.java @@ -0,0 +1,17 @@ +package com.codeit.side.chat.domain; + +import com.codeit.side.user.domain.User; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public class UserChatMessage { + private final ChatMessage chatMessage; + private final User user; + + public static UserChatMessage of(ChatMessage chatMessage, User user) { + return new UserChatMessage(chatMessage, user); + } +} diff --git a/src/main/java/com/codeit/side/common/config/CorsMvcConfig.java b/src/main/java/com/codeit/side/common/config/CorsMvcConfig.java index d3a022e..ab05886 100644 --- a/src/main/java/com/codeit/side/common/config/CorsMvcConfig.java +++ b/src/main/java/com/codeit/side/common/config/CorsMvcConfig.java @@ -6,7 +6,14 @@ @Configuration public class CorsMvcConfig implements WebMvcConfigurer { - private static final String[] ALLOWED_ORIGINS = {"http://localhost:3000", "http://127.0.0.1:5500", "https://doitz.netlify.app/", "https://calit.netlify.app/"}; + private static final String[] ALLOWED_ORIGINS = { + "http://localhost:3000", + "http://127.0.0.1:5500", + "https://doitz.netlify.app/", + "https://calit.netlify.app/", + "https://www.thunderting.site", + "https://thunderting.netlify.app" + }; private static final String[] ALLOWED_METHODS = {"GET", "POST", "PUT", "DELETE"}; @Override diff --git a/src/main/java/com/codeit/side/common/config/SecurityConfig.java b/src/main/java/com/codeit/side/common/config/SecurityConfig.java index 9c43bcf..83840ab 100644 --- a/src/main/java/com/codeit/side/common/config/SecurityConfig.java +++ b/src/main/java/com/codeit/side/common/config/SecurityConfig.java @@ -30,7 +30,9 @@ public class SecurityConfig { "http://localhost:3000", "http://127.0.0.1:5500", "https://doitz.netlify.app", - "https://calit.netlify.app" + "https://calit.netlify.app", + "https://www.thunderting.site", + "https://thunderting.netlify.app" ); private static final List ALLOWED_METHODS = List.of("GET", "POST", "PUT", "DELETE"); private static final String[] WHITE_LIST = { @@ -38,7 +40,8 @@ public class SecurityConfig { "/api/v1/login", "/api/v1/join", "/api/v1/**", - "/api/v1/ws/**" + "/api/v1/ws/**", + "/api/v1/ws", }; private final JwtAuthenticationFilter jwtAuthenticationFilter; diff --git a/src/main/java/com/codeit/side/common/config/WebSocketConfig.java b/src/main/java/com/codeit/side/common/config/WebSocketConfig.java index 2a524af..fd5304e 100644 --- a/src/main/java/com/codeit/side/common/config/WebSocketConfig.java +++ b/src/main/java/com/codeit/side/common/config/WebSocketConfig.java @@ -3,9 +3,13 @@ import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; +import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; +import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration; +import org.springframework.web.socket.handler.WebSocketHandlerDecorator; +import org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory; @Configuration @EnableWebSocketMessageBroker @@ -31,7 +35,25 @@ public void registerStompEndpoints(StompEndpointRegistry registry) { * */ @Override //메시지 라우팅 public void configureMessageBroker(MessageBrokerRegistry registry) { - registry.enableSimpleBroker("/topic", "/queue"); + registry.enableSimpleBroker("/topic"); registry.setApplicationDestinationPrefixes("/app"); } + + @Override + public void configureWebSocketTransport(WebSocketTransportRegistration registration) { + registration.setMessageSizeLimit(128 * 1024); + registration.setSendBufferSizeLimit(512 * 1024); + registration.setSendTimeLimit(20 * 1000); + registration.setDecoratorFactories(webSocketHandlerDecoratorFactory()); + } + + private WebSocketHandlerDecoratorFactory webSocketHandlerDecoratorFactory() { + return handler -> new WebSocketHandlerDecorator(handler) { + @Override + public void afterConnectionEstablished(WebSocketSession session) throws Exception { + System.out.println("WebSocket connection established: " + session.getId()); + super.afterConnectionEstablished(session); + } + }; + } } diff --git a/src/main/java/com/codeit/side/lightening/adapter/in/web/LighteningController.java b/src/main/java/com/codeit/side/lightening/adapter/in/web/LighteningController.java index c958415..99a84cf 100644 --- a/src/main/java/com/codeit/side/lightening/adapter/in/web/LighteningController.java +++ b/src/main/java/com/codeit/side/lightening/adapter/in/web/LighteningController.java @@ -1,6 +1,7 @@ package com.codeit.side.lightening.adapter.in.web; import com.codeit.side.common.adapter.exception.AuthenticationFailedException; +import com.codeit.side.lightening.adapter.in.web.request.LighteningLikesRequest; import com.codeit.side.lightening.adapter.in.web.request.LighteningRequest; import com.codeit.side.lightening.adapter.in.web.request.LighteningUpdateRequest; import com.codeit.side.lightening.adapter.in.web.response.CreateLighteningResponse; @@ -31,8 +32,8 @@ public ResponseEntity create( ) { String email = getEmail(true); Lightening lighteningModel = lighteningRequest.toModel(hasImage(image)); - Lightening savedLightening = lighteningUseCase.save(email, lighteningModel, image); - return ResponseEntity.ok(CreateLighteningResponse.from(savedLightening.getId())); + LighteningChatRoom lighteningChatRoom = lighteningUseCase.save(email, lighteningModel, image); + return ResponseEntity.ok(CreateLighteningResponse.from(lighteningChatRoom)); } @PostMapping("/{id}/like") @@ -43,6 +44,14 @@ public ResponseEntity like(@PathVariable Long id) { .build(); } + @PostMapping("/like") + public ResponseEntity likesAll(@RequestBody @Valid LighteningLikesRequest request) { + String email = getEmail(true); + lighteningUseCase.likesAll(email, request.lighteningIds()); + return ResponseEntity.ok() + .build(); + } + @PostMapping("/{id}/join") public ResponseEntity join(@PathVariable Long id) { String email = getEmail(true); diff --git a/src/main/java/com/codeit/side/lightening/adapter/in/web/request/LighteningLikesRequest.java b/src/main/java/com/codeit/side/lightening/adapter/in/web/request/LighteningLikesRequest.java new file mode 100644 index 0000000..c5fb793 --- /dev/null +++ b/src/main/java/com/codeit/side/lightening/adapter/in/web/request/LighteningLikesRequest.java @@ -0,0 +1,10 @@ +package com.codeit.side.lightening.adapter.in.web.request; + +import jakarta.validation.constraints.NotNull; + +import java.util.Set; + +public record LighteningLikesRequest( + @NotNull(message = "lighteningIds는 필수입니다.") Set lighteningIds +) { +} diff --git a/src/main/java/com/codeit/side/lightening/adapter/in/web/response/CreateLighteningResponse.java b/src/main/java/com/codeit/side/lightening/adapter/in/web/response/CreateLighteningResponse.java index 3831f7a..99f9eb8 100644 --- a/src/main/java/com/codeit/side/lightening/adapter/in/web/response/CreateLighteningResponse.java +++ b/src/main/java/com/codeit/side/lightening/adapter/in/web/response/CreateLighteningResponse.java @@ -1,7 +1,9 @@ package com.codeit.side.lightening.adapter.in.web.response; -public record CreateLighteningResponse(long id) { - public static CreateLighteningResponse from(long id) { - return new CreateLighteningResponse(id); +import com.codeit.side.lightening.domain.LighteningChatRoom; + +public record CreateLighteningResponse(long id, long chatRoomId) { + public static CreateLighteningResponse from(LighteningChatRoom lighteningChatRoom) { + return new CreateLighteningResponse(lighteningChatRoom.getLightening().getId(), lighteningChatRoom.getChatRoom().getId()); } } diff --git a/src/main/java/com/codeit/side/lightening/adapter/in/web/response/LighteningResponse.java b/src/main/java/com/codeit/side/lightening/adapter/in/web/response/LighteningResponse.java index 5382ee7..1ad3e75 100644 --- a/src/main/java/com/codeit/side/lightening/adapter/in/web/response/LighteningResponse.java +++ b/src/main/java/com/codeit/side/lightening/adapter/in/web/response/LighteningResponse.java @@ -30,6 +30,7 @@ public record LighteningResponse( boolean isConfirmed, boolean isCompleted, Long chatRoomId, + Integer unreadCount, List participants ) { public static LighteningResponse from(String email, LighteningInfo lighteningInfo) { @@ -62,6 +63,7 @@ public static LighteningResponse from(String email, LighteningInfo lighteningInf lighteningMemberResponse.size() >= lightening.getMinCapacity(), lighteningMemberResponse.size() >= lightening.getCapacity(), lighteningInfo.getChatRoom().getId(), + lighteningInfo.getUnreadCount(), lighteningMemberResponse ); } diff --git a/src/main/java/com/codeit/side/lightening/adapter/out/persistence/LighteningCommandRepositoryImpl.java b/src/main/java/com/codeit/side/lightening/adapter/out/persistence/LighteningCommandRepositoryImpl.java index 4078551..987059c 100644 --- a/src/main/java/com/codeit/side/lightening/adapter/out/persistence/LighteningCommandRepositoryImpl.java +++ b/src/main/java/com/codeit/side/lightening/adapter/out/persistence/LighteningCommandRepositoryImpl.java @@ -12,6 +12,9 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; +import java.util.Optional; +import java.util.Set; + @Repository @RequiredArgsConstructor public class LighteningCommandRepositoryImpl implements LighteningCommandRepository { @@ -29,11 +32,12 @@ public Lightening save(String email, Lightening lightening) { @Override public void like(String email, Long lighteningId) { - lighteningLikeJpaEntityRepository.findByLighteningIdAndEmail(lighteningId, email) - .ifPresentOrElse( - LighteningLikeEntity::update, - () -> lighteningLikeJpaEntityRepository.save(LighteningLikeEntity.of(lighteningId, email)) - ); + Optional lighteningLikeEntity = lighteningLikeJpaEntityRepository.findByLighteningIdAndEmail(lighteningId, email); + if (lighteningLikeEntity.isPresent()) { + lighteningLikeEntity.get().update(); + return; + } + lighteningLikeJpaEntityRepository.save(LighteningLikeEntity.of(lighteningId, email)); } @Override @@ -62,4 +66,20 @@ public void delete(Long id) { lighteningJpaEntityRepository.findById(id) .ifPresent(LighteningEntity::delete); } + + @Override + public void likesAll(String email, Set lighteningIds) { + lighteningIds.forEach(lighteningId -> { + likeLightening(email, lighteningId); + }); + } + + private void likeLightening(String email, Long lighteningId) { + Optional lighteningLikeEntity = lighteningLikeJpaEntityRepository.findByLighteningIdAndEmail(lighteningId, email); + if (lighteningLikeEntity.isPresent()) { + lighteningLikeEntity.get().like(); + return; + } + lighteningLikeJpaEntityRepository.save(LighteningLikeEntity.of(lighteningId, email)); + } } diff --git a/src/main/java/com/codeit/side/lightening/adapter/out/persistence/entity/LighteningLikeEntity.java b/src/main/java/com/codeit/side/lightening/adapter/out/persistence/entity/LighteningLikeEntity.java index cedf5f0..f101fc4 100644 --- a/src/main/java/com/codeit/side/lightening/adapter/out/persistence/entity/LighteningLikeEntity.java +++ b/src/main/java/com/codeit/side/lightening/adapter/out/persistence/entity/LighteningLikeEntity.java @@ -9,7 +9,12 @@ import lombok.NoArgsConstructor; @Entity -@Table(name = "lightening_likes") +@Table( + name = "lightening_likes", + uniqueConstraints = { + @UniqueConstraint(columnNames = {"lightening_id", "email"}) + } +) @Builder(access = AccessLevel.PACKAGE) @AllArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -40,6 +45,10 @@ public void update() { isDeleted = !isDeleted; } + public void like() { + isDeleted = false; + } + public LighteningLike toDomain() { return LighteningLike.of(lighteningId, email); } diff --git a/src/main/java/com/codeit/side/lightening/adapter/out/persistence/jpa/LighteningQueryBuilder.java b/src/main/java/com/codeit/side/lightening/adapter/out/persistence/jpa/LighteningQueryBuilder.java index 312239d..022fac9 100644 --- a/src/main/java/com/codeit/side/lightening/adapter/out/persistence/jpa/LighteningQueryBuilder.java +++ b/src/main/java/com/codeit/side/lightening/adapter/out/persistence/jpa/LighteningQueryBuilder.java @@ -88,4 +88,11 @@ public LighteningQueryBuilder addMyJoinedCondition(String email) { } return this; } + + public LighteningQueryBuilder addNotHostCondition(String email) { + if (email != null) { + booleanBuilder.and(lighteningEntity.host.ne(email)); + } + return this; + } } diff --git a/src/main/java/com/codeit/side/lightening/adapter/out/persistence/jpa/LighteningQueryEntityRepository.java b/src/main/java/com/codeit/side/lightening/adapter/out/persistence/jpa/LighteningQueryEntityRepository.java index 4aafe09..67bf4e5 100644 --- a/src/main/java/com/codeit/side/lightening/adapter/out/persistence/jpa/LighteningQueryEntityRepository.java +++ b/src/main/java/com/codeit/side/lightening/adapter/out/persistence/jpa/LighteningQueryEntityRepository.java @@ -52,14 +52,14 @@ private OrderSpecifier createOrder(String order) { private BooleanBuilder createQueryBuilder(LighteningCondition lighteningCondition, String email) { return switch (lighteningCondition.getConditionType()) { - case LIST -> listConditionBuilder(lighteningCondition); - case MY_CREATED -> myCreatedConditionBuilder(lighteningCondition); + case LIST -> listConditionBuilder(lighteningCondition, email); + case MY_CREATED -> myCreatedConditionBuilder(lighteningCondition, email); case LIKE -> likeConditionBuilder(lighteningCondition, email); case MY_JOINED -> myJoinedConditionBuilder(lighteningCondition, email); }; } - private BooleanBuilder listConditionBuilder(LighteningCondition lighteningCondition) { + private BooleanBuilder listConditionBuilder(LighteningCondition lighteningCondition, String email) { return LighteningQueryBuilder.Builder() .addIsInactiveCondition(false) .addCategoryCondition(lighteningCondition.getCategory()) @@ -69,7 +69,7 @@ private BooleanBuilder listConditionBuilder(LighteningCondition lighteningCondit .build(); } - private BooleanBuilder myCreatedConditionBuilder(LighteningCondition lighteningCondition) { + private BooleanBuilder myCreatedConditionBuilder(LighteningCondition lighteningCondition, String email) { return LighteningQueryBuilder.Builder() .addIsInactiveCondition(false) .addCategoryCondition(lighteningCondition.getCategory()) @@ -93,6 +93,7 @@ private BooleanBuilder myJoinedConditionBuilder(LighteningCondition lighteningCo .addIsInactiveCondition(false) .addCategoryCondition(lighteningCondition.getCategory()) .addMyJoinedCondition(email) + .addNotHostCondition(email) .build(); } diff --git a/src/main/java/com/codeit/side/lightening/application/port/in/LighteningUseCase.java b/src/main/java/com/codeit/side/lightening/application/port/in/LighteningUseCase.java index 7581d58..1bf1d1e 100644 --- a/src/main/java/com/codeit/side/lightening/application/port/in/LighteningUseCase.java +++ b/src/main/java/com/codeit/side/lightening/application/port/in/LighteningUseCase.java @@ -1,14 +1,16 @@ package com.codeit.side.lightening.application.port.in; import com.codeit.side.lightening.domain.Lightening; +import com.codeit.side.lightening.domain.LighteningChatRoom; import com.codeit.side.lightening.domain.LighteningCondition; import com.codeit.side.lightening.domain.LighteningInfo; import org.springframework.web.multipart.MultipartFile; import java.util.List; +import java.util.Set; public interface LighteningUseCase { - Lightening save(String email, Lightening lightening, MultipartFile image); + LighteningChatRoom save(String email, Lightening lightening, MultipartFile image); void like(String email, Long id); @@ -23,4 +25,6 @@ public interface LighteningUseCase { void update(String email, Long id, String description); void delete(String email, Long id); + + void likesAll(String email, Set lighteningIds); } diff --git a/src/main/java/com/codeit/side/lightening/application/port/out/LighteningCommandRepository.java b/src/main/java/com/codeit/side/lightening/application/port/out/LighteningCommandRepository.java index cd6fc86..ce3ce65 100644 --- a/src/main/java/com/codeit/side/lightening/application/port/out/LighteningCommandRepository.java +++ b/src/main/java/com/codeit/side/lightening/application/port/out/LighteningCommandRepository.java @@ -2,6 +2,8 @@ import com.codeit.side.lightening.domain.Lightening; +import java.util.Set; + public interface LighteningCommandRepository { Lightening save(String email, Lightening lightening); @@ -14,4 +16,6 @@ public interface LighteningCommandRepository { void update(Long id, String description); void delete(Long id); + + void likesAll(String email, Set lighteningIds); } diff --git a/src/main/java/com/codeit/side/lightening/application/service/LighteningService.java b/src/main/java/com/codeit/side/lightening/application/service/LighteningService.java index 2962f09..0b94f41 100644 --- a/src/main/java/com/codeit/side/lightening/application/service/LighteningService.java +++ b/src/main/java/com/codeit/side/lightening/application/service/LighteningService.java @@ -16,8 +16,10 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; +import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; @@ -32,13 +34,13 @@ public class LighteningService implements LighteningUseCase { @Override @Transactional - public Lightening save(String email, Lightening lightening, MultipartFile image) { + public LighteningChatRoom save(String email, Lightening lightening, MultipartFile image) { fileUploader.validateImage(image); Lightening savedLightening = lighteningCommandRepository.save(email, lightening); lighteningCommandRepository.join(email, savedLightening.getId()); - chatMessageUseCase.createChatRoom(email, savedLightening.getId(), savedLightening.getTitle()); + ChatRoom chatRoom = chatMessageUseCase.createChatRoom(email, savedLightening.getId(), savedLightening.getTitle()); fileUploader.uploadImageToS3(image, "lightening/" + savedLightening.getId(), "image.jpg", "jpg"); - return savedLightening; + return LighteningChatRoom.of(savedLightening, chatRoom); } @Override @@ -54,6 +56,7 @@ public void join(String email, Long id) { Lightening lightening = lighteningReadRepository.getById(id); validateLightening(email, lightening); lighteningCommandRepository.join(email, lightening.getId()); + chatMessageUseCase.joinChatRoomByLighteningId(id, email); } @Override @@ -64,6 +67,7 @@ public void leave(String email, Long id) { throw new UserNotJoinedException(lightening.getId()); } lighteningCommandRepository.leave(email, lightening.getId()); + chatMessageUseCase.leaveChatRoom(id, email); } @Override @@ -72,7 +76,8 @@ public LighteningInfo getById(String email, Long id) { List lighteningMember = lighteningReadRepository.findAllMembersBy(lightening.getId()); boolean isLighteningLike = lighteningReadRepository.findLighteningLikeBy(email, lightening.getId()); ChatRoom chatRoom = chatMessageUseCase.getChatRoomByLighteningId(lightening.getId()); - return createLighteningInfo(lightening, lighteningMember, chatRoom, isLighteningLike); + int unreadCount = chatMessageUseCase.countUnreadMessages(chatRoom.getId(), email); + return createLighteningInfo(lightening, lighteningMember, chatRoom, isLighteningLike, unreadCount); } @Override @@ -83,8 +88,12 @@ public List findAllBy(String email, LighteningCondition lighteni .toList(); List lighteningMembers = lighteningReadRepository.findAllMembersBy(lighteningIds); List lighteningLikes = lighteningReadRepository.findLighteningLikesBy(email, lighteningIds); - List chatRoomIds = chatMessageUseCase.findAllChatRoomsByLighteningIds(lighteningIds); - return createLighteningInfos(lightenings, lighteningMembers, chatRoomIds, lighteningLikes); + List chatRooms = chatMessageUseCase.findAllChatRoomsByLighteningIds(lighteningIds); + List chatRoomIds = chatRooms.stream() + .map(ChatRoom::getId) + .toList(); + Map idToUnreadCount = chatMessageUseCase.countAllUnreadMessages(email, chatRoomIds); + return createLighteningInfos(lightenings, lighteningMembers, chatRooms, lighteningLikes, idToUnreadCount); } @Override @@ -107,7 +116,26 @@ public void delete(String email, Long id) { lighteningCommandRepository.delete(id); } - private List createLighteningInfos(List lightenings, List lighteningMembers, List chatRooms, List lighteningLikes) { + @Override + @Transactional + public void likesAll(String email, Set lighteningIds) { + List lightenings = lighteningReadRepository.findAllBy(new ArrayList<>(lighteningIds)); + if (lightenings.size() != lighteningIds.size()) { + List extractIds = extractNotExistingLighteningIds(lightenings, new ArrayList<>(lighteningIds)); + throw new IllegalRequestException("존재하지 않는 번개가 포함되어 있습니다. lighteningIds: %s".formatted(extractIds)); + } + lighteningCommandRepository.likesAll(email, lighteningIds); + } + + private List extractNotExistingLighteningIds(List lightenings, List lighteningIds) { + Set target = lightenings.stream() + .map(Lightening::getId) + .collect(Collectors.toSet()); + lighteningIds.removeAll(target); + return lighteningIds; + } + + private List createLighteningInfos(List lightenings, List lighteningMembers, List chatRooms, List lighteningLikes, Map idToUnreadCount) { Map> idToLighteningMembers = lighteningMembers.stream() .collect(Collectors.groupingBy(LighteningMember::getLighteningId)); Map idToLighteningLike = lighteningLikes.stream() @@ -116,12 +144,21 @@ private List createLighteningInfos(List lightenings, .collect(Collectors.toMap(ChatRoom::getLighteningId, Function.identity())); return lightenings.stream() - .map(lightening -> createLighteningInfo(lightening, idToLighteningMembers.getOrDefault(lightening.getId(), List.of()), idToChatRoom.get(lightening.getId()), idToLighteningLike.containsKey(lightening.getId()))) + .map(lightening -> { + ChatRoom chatRoom = idToChatRoom.get(lightening.getId()); + return createLighteningInfo( + lightening, + idToLighteningMembers.getOrDefault(lightening.getId(), List.of()), + chatRoom, + idToLighteningLike.containsKey(lightening.getId()), + idToUnreadCount.getOrDefault(chatRoom.getLighteningId(), 0) + ); + }) .toList(); } - private LighteningInfo createLighteningInfo(Lightening lightening, List lighteningMember, ChatRoom chatRoom, boolean isLighteningLike) { - return LighteningInfo.of(lightening, lighteningMember, chatRoom, isLighteningLike); + private LighteningInfo createLighteningInfo(Lightening lightening, List lighteningMember, ChatRoom chatRoom, boolean isLighteningLike, Integer unreadCount) { + return LighteningInfo.of(lightening, lighteningMember, chatRoom, isLighteningLike, unreadCount); } private void validateLightening(String email, Lightening lightening) { diff --git a/src/main/java/com/codeit/side/lightening/domain/LighteningChatRoom.java b/src/main/java/com/codeit/side/lightening/domain/LighteningChatRoom.java new file mode 100644 index 0000000..b919755 --- /dev/null +++ b/src/main/java/com/codeit/side/lightening/domain/LighteningChatRoom.java @@ -0,0 +1,17 @@ +package com.codeit.side.lightening.domain; + +import com.codeit.side.chat.domain.ChatRoom; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public class LighteningChatRoom { + private final Lightening lightening; + private final ChatRoom chatRoom; + + public static LighteningChatRoom of(Lightening lightening, ChatRoom chatRoom){ + return new LighteningChatRoom(lightening, chatRoom); + } +} diff --git a/src/main/java/com/codeit/side/lightening/domain/LighteningInfo.java b/src/main/java/com/codeit/side/lightening/domain/LighteningInfo.java index f104c50..4a8f7cb 100644 --- a/src/main/java/com/codeit/side/lightening/domain/LighteningInfo.java +++ b/src/main/java/com/codeit/side/lightening/domain/LighteningInfo.java @@ -14,13 +14,15 @@ public class LighteningInfo { private final List lighteningMembers; private final ChatRoom chatRoom; private final boolean isLiked; + private final int unreadCount; public static LighteningInfo of( Lightening lightening, List lighteningMembers, ChatRoom chatRoom, - boolean isLiked - ){ - return new LighteningInfo(lightening, lighteningMembers, chatRoom, isLiked); + boolean isLiked, + int unreadCount + ) { + return new LighteningInfo(lightening, lighteningMembers, chatRoom, isLiked, unreadCount); } } diff --git a/src/main/java/com/codeit/side/user/adapter/in/web/response/ReviewInfoResponse.java b/src/main/java/com/codeit/side/user/adapter/in/web/response/ReviewInfoResponse.java index 52adfd4..9d4b677 100644 --- a/src/main/java/com/codeit/side/user/adapter/in/web/response/ReviewInfoResponse.java +++ b/src/main/java/com/codeit/side/user/adapter/in/web/response/ReviewInfoResponse.java @@ -1,5 +1,6 @@ package com.codeit.side.user.adapter.in.web.response; +import com.codeit.side.lightening.domain.Category; import com.codeit.side.lightening.domain.Lightening; import com.codeit.side.lightening.domain.Review; import com.codeit.side.user.domain.ReviewInfo; @@ -13,6 +14,7 @@ public record ReviewInfoResponse( Integer rating, LocalDateTime createdAt, Long lighteningId, + Category category, String title, String city, String town, @@ -33,6 +35,7 @@ public static ReviewInfoResponse from(ReviewInfo reviewInfo) { review.getScore(), review.getCreatedAt(), lightening.getId(), + lightening.getCategory(), lightening.getTitle(), lightening.getCity(), lightening.getTown(), diff --git a/src/main/java/com/codeit/side/user/adapter/in/web/response/ReviewInfoResponses.java b/src/main/java/com/codeit/side/user/adapter/in/web/response/ReviewInfoResponses.java index da8c364..1313779 100644 --- a/src/main/java/com/codeit/side/user/adapter/in/web/response/ReviewInfoResponses.java +++ b/src/main/java/com/codeit/side/user/adapter/in/web/response/ReviewInfoResponses.java @@ -4,11 +4,11 @@ import java.util.List; -public record ReviewInfoResponses(List reviews, Integer totalCount) { +public record ReviewInfoResponses(List reviews) { public static ReviewInfoResponses from(ReviewInfos reviewInfos) { List reviewInfoResponses = reviewInfos.stream() .map(ReviewInfoResponse::from) .toList(); - return new ReviewInfoResponses(reviewInfoResponses, reviewInfos.getTotalCount()); + return new ReviewInfoResponses(reviewInfoResponses); } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index ef46c2a..3ce7910 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,3 +1,7 @@ spring: profiles: - active: local \ No newline at end of file + active: local + servlet: + multipart: + max-file-size: 10MB + max-request-size: 10MB diff --git a/src/test/java/com/codeit/side/mock/FakeLighteningCommandRepository.java b/src/test/java/com/codeit/side/mock/FakeLighteningCommandRepository.java index 8922278..689c841 100644 --- a/src/test/java/com/codeit/side/mock/FakeLighteningCommandRepository.java +++ b/src/test/java/com/codeit/side/mock/FakeLighteningCommandRepository.java @@ -5,6 +5,7 @@ import com.codeit.side.lightening.domain.LighteningFixture; import java.util.List; +import java.util.Set; public class FakeLighteningCommandRepository implements LighteningCommandRepository { private final List lightenings; @@ -45,4 +46,9 @@ public void update(Long id, String description) { public void delete(Long id) { } + + @Override + public void likesAll(String email, Set lighteningIds) { + + } }