diff --git a/src/main/java/com/mos/backend/common/config/SecurityConfig.java b/src/main/java/com/mos/backend/common/config/SecurityConfig.java index 4372b141..b17279ff 100644 --- a/src/main/java/com/mos/backend/common/config/SecurityConfig.java +++ b/src/main/java/com/mos/backend/common/config/SecurityConfig.java @@ -71,6 +71,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .requestMatchers(HttpMethod.GET, "/study-schedules", "/studies/*/schedules").permitAll() .requestMatchers(HttpMethod.GET, "/users/tokens").permitAll() .requestMatchers(HttpMethod.GET, "/likes").permitAll() + .requestMatchers(HttpMethod.GET, "/tokens").permitAll() .anyRequest().authenticated() ) diff --git a/src/main/java/com/mos/backend/common/infrastructure/EntityFacade.java b/src/main/java/com/mos/backend/common/infrastructure/EntityFacade.java index 46b90984..3806deaf 100644 --- a/src/main/java/com/mos/backend/common/infrastructure/EntityFacade.java +++ b/src/main/java/com/mos/backend/common/infrastructure/EntityFacade.java @@ -8,6 +8,7 @@ import com.mos.backend.notifications.entity.exception.NotificationLogErrorCode; import com.mos.backend.notifications.infrastructure.notificationlog.NotificationLogRepository; import com.mos.backend.privatechatroommember.entity.PrivateChatRoomMember; +import com.mos.backend.privatechatroommember.entity.PrivateChatRoomMemberErrorCode; import com.mos.backend.privatechatroommember.infrastructure.PrivateChatRoomMemberRepository; import com.mos.backend.privatechatrooms.entity.PrivateChatRoom; import com.mos.backend.privatechatrooms.entity.PrivateChatRoomErrorCode; @@ -152,6 +153,7 @@ public UserStudySetting getUserStudySetting(Long userId, Long studyId) { } public PrivateChatRoomMember getPrivateChatRoomMember(Long userId, Long privateChatRoomId) { - return privateChatRoomMemberRepository.findByUserIdAndPrivateChatRoomId(userId, privateChatRoomId); + return privateChatRoomMemberRepository.findByUserIdAndPrivateChatRoomId(userId, privateChatRoomId) + .orElseThrow(() -> new MosException(PrivateChatRoomMemberErrorCode.NOT_FOUND)); } } diff --git a/src/main/java/com/mos/backend/privatechatmessages/application/PrivateChatMessageService.java b/src/main/java/com/mos/backend/privatechatmessages/application/PrivateChatMessageService.java index 25f3f2d8..38d89c34 100644 --- a/src/main/java/com/mos/backend/privatechatmessages/application/PrivateChatMessageService.java +++ b/src/main/java/com/mos/backend/privatechatmessages/application/PrivateChatMessageService.java @@ -66,9 +66,10 @@ private PrivateChatMessage savePrivateChatMessage(User user, PrivateChatRoom pri @Transactional(readOnly = true) public InfinityScrollRes getPrivateChatMessages(Long userId, Long privateChatRoomId, Long lastPrivateChatMessageId, int size) { PrivateChatRoom privateChatRoom = entityFacade.getPrivateChatRoom(privateChatRoomId); + PrivateChatRoomMember privateChatRoomMember = entityFacade.getPrivateChatRoomMember(userId, privateChatRoomId); List privateChatMessages = privateChatMessageRepository.findAllByChatRoomIdForInfiniteScroll( - privateChatRoom.getId(), lastPrivateChatMessageId, size + privateChatRoom.getId(), lastPrivateChatMessageId, size, privateChatRoomMember.getDeletedAt() ); boolean hasNext = InfinityScrollUtil.hasNext(privateChatMessages, size); diff --git a/src/main/java/com/mos/backend/privatechatmessages/infrastructure/PrivateChatMessageQueryDslRepository.java b/src/main/java/com/mos/backend/privatechatmessages/infrastructure/PrivateChatMessageQueryDslRepository.java index 02c140f0..85aa15da 100644 --- a/src/main/java/com/mos/backend/privatechatmessages/infrastructure/PrivateChatMessageQueryDslRepository.java +++ b/src/main/java/com/mos/backend/privatechatmessages/infrastructure/PrivateChatMessageQueryDslRepository.java @@ -1,13 +1,14 @@ package com.mos.backend.privatechatmessages.infrastructure; import com.mos.backend.privatechatmessages.entity.PrivateChatMessage; -import com.querydsl.core.types.Predicate; import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; +import java.time.LocalDateTime; import java.util.List; +import java.util.Objects; import static com.mos.backend.privatechatmessages.entity.QPrivateChatMessage.privateChatMessage; @@ -16,18 +17,26 @@ public class PrivateChatMessageQueryDslRepository { private final JPAQueryFactory queryFactory; - public List findByChatRoomIdForInfiniteScroll(Long privateChatRoomId, Long lastPrivateChatMessageId, int size) { + public List findByChatRoomIdForInfiniteScroll(Long privateChatRoomId, + Long lastPrivateChatMessageId, + int size, + LocalDateTime deletedAt) { return queryFactory .selectFrom(privateChatMessage) .where( eqPrivateChatRoomId(privateChatRoomId), - ltPrivateChatMessageId(lastPrivateChatMessageId) + ltPrivateChatMessageId(lastPrivateChatMessageId), + gtPrivateChatMessageCreatedAt(deletedAt) ) .orderBy(privateChatMessage.id.desc()) .limit(size + 1) .fetch(); } + private static BooleanExpression gtPrivateChatMessageCreatedAt(LocalDateTime deletedAt) { + return Objects.isNull(deletedAt) ? null : privateChatMessage.createdAt.gt(deletedAt); + } + private static BooleanExpression eqPrivateChatRoomId(Long privateChatRoomId) { return privateChatMessage.privateChatRoom.id.eq(privateChatRoomId); } diff --git a/src/main/java/com/mos/backend/privatechatmessages/infrastructure/PrivateChatMessageRepository.java b/src/main/java/com/mos/backend/privatechatmessages/infrastructure/PrivateChatMessageRepository.java index 09a2df28..94ae7737 100644 --- a/src/main/java/com/mos/backend/privatechatmessages/infrastructure/PrivateChatMessageRepository.java +++ b/src/main/java/com/mos/backend/privatechatmessages/infrastructure/PrivateChatMessageRepository.java @@ -12,7 +12,7 @@ public interface PrivateChatMessageRepository { Optional findFirstByPrivateChatRoomOrderByCreatedAtDesc(PrivateChatRoom privateChatRoom); - List findAllByChatRoomIdForInfiniteScroll(Long privateChatRoomId, Long lastPrivateChatMessageId, int size); + List findAllByChatRoomIdForInfiniteScroll(Long privateChatRoomId, Long lastPrivateChatMessageId, int size, LocalDateTime deletedAt); int countByPrivateChatRoomIdAndCreatedAtAfter(Long privateChatRoomId, LocalDateTime lastEntryAt); } diff --git a/src/main/java/com/mos/backend/privatechatmessages/infrastructure/PrivateChatMessageRepositoryImpl.java b/src/main/java/com/mos/backend/privatechatmessages/infrastructure/PrivateChatMessageRepositoryImpl.java index 14f6d335..648411d8 100644 --- a/src/main/java/com/mos/backend/privatechatmessages/infrastructure/PrivateChatMessageRepositoryImpl.java +++ b/src/main/java/com/mos/backend/privatechatmessages/infrastructure/PrivateChatMessageRepositoryImpl.java @@ -26,8 +26,8 @@ public Optional findFirstByPrivateChatRoomOrderByCreatedAtDe } @Override - public List findAllByChatRoomIdForInfiniteScroll(Long privateChatRoomId, Long lastPrivateChatMessageId, int size) { - return privateChatMessageQueryDslRepository.findByChatRoomIdForInfiniteScroll(privateChatRoomId, lastPrivateChatMessageId, size); + public List findAllByChatRoomIdForInfiniteScroll(Long privateChatRoomId, Long lastPrivateChatMessageId, int size, LocalDateTime deletedAt) { + return privateChatMessageQueryDslRepository.findByChatRoomIdForInfiniteScroll(privateChatRoomId, lastPrivateChatMessageId, size, deletedAt); } @Override diff --git a/src/main/java/com/mos/backend/privatechatroommember/application/PrivateChatRoomMemberService.java b/src/main/java/com/mos/backend/privatechatroommember/application/PrivateChatRoomMemberService.java index a1451f3a..6710b717 100644 --- a/src/main/java/com/mos/backend/privatechatroommember/application/PrivateChatRoomMemberService.java +++ b/src/main/java/com/mos/backend/privatechatroommember/application/PrivateChatRoomMemberService.java @@ -44,4 +44,12 @@ public void updateLastEntryAt(Long userId, Long privateChatRoomId) { public List findByPrivateChatRoom(PrivateChatRoom privateChatRoom) { return privateChatRoomMemberRepository.findByPrivateChatRoom(privateChatRoom); } + + @Transactional + public void leavePrivateChatRoom(Long userId, Long privateChatRoomId) { + PrivateChatRoomMember privateChatRoomMember = privateChatRoomMemberRepository.findByUserIdAndPrivateChatRoomId(userId, privateChatRoomId) + .orElseThrow(() -> new MosException(PrivateChatRoomMemberErrorCode.NOT_FOUND)); + + privateChatRoomMemberRepository.delete(privateChatRoomMember); + } } diff --git a/src/main/java/com/mos/backend/privatechatroommember/entity/PrivateChatRoomMember.java b/src/main/java/com/mos/backend/privatechatroommember/entity/PrivateChatRoomMember.java index b7ed8347..78d5e6e5 100644 --- a/src/main/java/com/mos/backend/privatechatroommember/entity/PrivateChatRoomMember.java +++ b/src/main/java/com/mos/backend/privatechatroommember/entity/PrivateChatRoomMember.java @@ -3,16 +3,27 @@ import com.mos.backend.common.entity.BaseAuditableEntity; import com.mos.backend.privatechatrooms.entity.PrivateChatRoom; import com.mos.backend.users.entity.User; -import jakarta.persistence.*; +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.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; +import org.hibernate.annotations.ColumnDefault; +import org.hibernate.annotations.SQLDelete; import java.time.LocalDateTime; @Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) +@SQLDelete(sql = "UPDATE private_chat_room_members SET deleted_at = NOW() WHERE private_chat_room_member_id = ?") @Table(name = "private_chat_room_members") public class PrivateChatRoomMember extends BaseAuditableEntity { @@ -31,6 +42,9 @@ public class PrivateChatRoomMember extends BaseAuditableEntity { private LocalDateTime lastEntryAt = LocalDateTime.now(); + @ColumnDefault("NULL") + private LocalDateTime deletedAt; + public static PrivateChatRoomMember of(PrivateChatRoom privateChatRoom, User user) { PrivateChatRoomMember privateChatRoomMember = new PrivateChatRoomMember(); privateChatRoomMember.privateChatRoom = privateChatRoom; @@ -42,4 +56,4 @@ public static PrivateChatRoomMember of(PrivateChatRoom privateChatRoom, User use public void updateLastEntryAt() { this.lastEntryAt = LocalDateTime.now(); } -} \ No newline at end of file +} diff --git a/src/main/java/com/mos/backend/privatechatroommember/infrastructure/PrivateChatRoomMemberJpaRepository.java b/src/main/java/com/mos/backend/privatechatroommember/infrastructure/PrivateChatRoomMemberJpaRepository.java index b6d3745a..79ac57a2 100644 --- a/src/main/java/com/mos/backend/privatechatroommember/infrastructure/PrivateChatRoomMemberJpaRepository.java +++ b/src/main/java/com/mos/backend/privatechatroommember/infrastructure/PrivateChatRoomMemberJpaRepository.java @@ -13,7 +13,7 @@ public interface PrivateChatRoomMemberJpaRepository extends JpaRepository findByUserAndPrivateChatRoom(User user, PrivateChatRoom privateChatRoom); - PrivateChatRoomMember findByUserIdAndPrivateChatRoomId(Long userId, Long privateChatRoomId); + Optional findByUserIdAndPrivateChatRoomId(Long userId, Long privateChatRoomId); List findByPrivateChatRoom(PrivateChatRoom privateChatRoom); } diff --git a/src/main/java/com/mos/backend/privatechatroommember/infrastructure/PrivateChatRoomMemberRepository.java b/src/main/java/com/mos/backend/privatechatroommember/infrastructure/PrivateChatRoomMemberRepository.java index e53d510c..78b2ce0c 100644 --- a/src/main/java/com/mos/backend/privatechatroommember/infrastructure/PrivateChatRoomMemberRepository.java +++ b/src/main/java/com/mos/backend/privatechatroommember/infrastructure/PrivateChatRoomMemberRepository.java @@ -14,7 +14,9 @@ public interface PrivateChatRoomMemberRepository { Optional findByUserAndPrivateChatRoom(User user, PrivateChatRoom privateChatRoom); - PrivateChatRoomMember findByUserIdAndPrivateChatRoomId(Long userId, Long privateChatRoomId); + Optional findByUserIdAndPrivateChatRoomId(Long userId, Long privateChatRoomId); List findByPrivateChatRoom(PrivateChatRoom privateChatRoom); + + void delete(PrivateChatRoomMember privateChatRoomMember); } diff --git a/src/main/java/com/mos/backend/privatechatroommember/infrastructure/PrivateChatRoomMemberRepositoryImpl.java b/src/main/java/com/mos/backend/privatechatroommember/infrastructure/PrivateChatRoomMemberRepositoryImpl.java index 0dd6ec76..2fd32cf1 100644 --- a/src/main/java/com/mos/backend/privatechatroommember/infrastructure/PrivateChatRoomMemberRepositoryImpl.java +++ b/src/main/java/com/mos/backend/privatechatroommember/infrastructure/PrivateChatRoomMemberRepositoryImpl.java @@ -30,7 +30,7 @@ public Optional findByUserAndPrivateChatRoom(User user, P } @Override - public PrivateChatRoomMember findByUserIdAndPrivateChatRoomId(Long userId, Long privateChatRoomId) { + public Optional findByUserIdAndPrivateChatRoomId(Long userId, Long privateChatRoomId) { return privateChatRoomMemberJpaRepository.findByUserIdAndPrivateChatRoomId(userId, privateChatRoomId); } @@ -38,4 +38,9 @@ public PrivateChatRoomMember findByUserIdAndPrivateChatRoomId(Long userId, Long public List findByPrivateChatRoom(PrivateChatRoom privateChatRoom) { return privateChatRoomMemberJpaRepository.findByPrivateChatRoom(privateChatRoom); } + + @Override + public void delete(PrivateChatRoomMember privateChatRoomMember) { + privateChatRoomMemberJpaRepository.delete(privateChatRoomMember); + } } diff --git a/src/main/java/com/mos/backend/privatechatroommember/presentation/PrivateChatRoomMemberController.java b/src/main/java/com/mos/backend/privatechatroommember/presentation/PrivateChatRoomMemberController.java new file mode 100644 index 00000000..0b8d6d8c --- /dev/null +++ b/src/main/java/com/mos/backend/privatechatroommember/presentation/PrivateChatRoomMemberController.java @@ -0,0 +1,19 @@ +package com.mos.backend.privatechatroommember.presentation; + +import com.mos.backend.privatechatroommember.application.PrivateChatRoomMemberService; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +@RequiredArgsConstructor +@RestController +public class PrivateChatRoomMemberController { + private final PrivateChatRoomMemberService privateChatRoomMemberService; + + @DeleteMapping("/private-chat-rooms/{privateChatRoomId}/members") + public void leavePrivateChatRoom(@AuthenticationPrincipal Long userId, @PathVariable Long privateChatRoomId) { + privateChatRoomMemberService.leavePrivateChatRoom(userId, privateChatRoomId); + } +}