8000 Feat/chat #133 by lsh2613 Β· Pull Request #134 Β· project-mos/backend Β· GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Feat/chat #133 #134

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

Merged
merged 4 commits into from
May 6, 2025
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
29 changes: 29 additions & 0 deletions src/main/java/com/mos/backend/common/dto/InfinityScrollRes.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.mos.backend.common.dto;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.util.List;

@ToString
@NoArgsConstructor
public class InfinityScrollRes<T> {
@Getter
private List<T> content;
@Getter
private Long lastElementId;
private boolean hasNext;

public static <T> InfinityScrollRes<T> of(List<T> content, Long lastElementId, boolean hasNext) {
InfinityScrollRes<T> infinityScrollRes = new InfinityScrollRes<>();
infinityScrollRes.content = content;
infinityScrollRes.hasNext = hasNext;
infinityScrollRes.lastElementId = lastElementId;
return infinityScrollRes;
}

public boolean hasNext() {
return hasNext;
}
}
19 changes: 19 additions & 0 deletions src/main/java/com/mos/backend/common/utils/InfinityScrollUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.mos.backend.common.utils;

import java.util.List;
import java.util.Optional;

public class InfinityScrollUtil {
public static <T> Optional<T> getLastElement(List<T> content) {
if (content.isEmpty()) return Optional.empty();

T t = content.get(content.size() - 1);

return Optional.ofNullable(t);
}

public static <T> boolean hasNext(List<T> list, int pageSize) {
return list.size() > pageSize;
}

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package com.mos.backend.privatechatmessages.application;

import com.mos.backend.common.dto.InfinityScrollRes;
import com.mos.backend.common.exception.MosException;
import com.mos.backend.common.infrastructure.EntityFacade;
import com.mos.backend.common.redis.RedisPublisher;
import com.mos.backend.common.utils.InfinityScrollUtil;
import com.mos.backend.privatechatmessages.application.dto.PrivateChatMessageDto;
import com.mos.backend.privatechatmessages.application.res.PrivateChatMessageRes;
import com.mos.backend.privatechatmessages.entity.PrivateChatMessage;
import com.mos.backend.privatechatmessages.infrastructure.PrivateChatMessageRepository;
import com.mos.backend.privatechatmessages.presentation.req.PrivateChatMessagePublishReq;
Expand All @@ -14,6 +17,9 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;

@RequiredArgsConstructor
@Service
public class PrivateChatMessageService {
Expand Down Expand Up @@ -47,4 +53,30 @@ private PrivateChatMessage savePrivateChatMessage(User user, PrivateChatRoom pri
PrivateChatMessage privateChatMessage = PrivateChatMessage.of(user, privateChatRoom, message);
return privateChatMessageRepository.save(privateChatMessage);
}

@Transactional(readOnly = true)
public InfinityScrollRes<PrivateChatMessageRes> getPrivateChatMessages(Long userId, Long privateChatRoomId, Long lastPrivateChatMessageId, int size) {
User user = entityFacade.getUser(userId);
PrivateChatRoom privateChatRoom = entityFacade.getPrivateChatRoom(privateChatRoomId);

if (isNotChatRoomMember(user, privateChatRoom))
throw new MosException(PrivateChatRoomErrorCode.FORBIDDEN);

List<PrivateChatMessage> privateChatMessages = privateChatMessageRepository.findAllByChatRoomIdForInfiniteScroll(
privateChatRoom.getId(), lastPrivateChatMessageId, size
);

boolean hasNext = InfinityScrollUtil.hasNext(privateChatMessages, size);
if (hasNext)
privateChatMessages.remove(privateChatMessages.size() - 1);

Optional<PrivateChatMessage> lastElement = InfinityScrollUtil.getLastElement(privateChatMessages);
Long lastElementId = lastElement.map(PrivateChatMessage::getId).orElse(null);

List<PrivateChatMessageRes> privateChatMessageResList = privateChatMessages.stream()
.map(PrivateChatMessageRes::of)
.toList();

return InfinityScrollRes.of(privateChatMessageResList, lastElementId, hasNext);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.mos.backend.privatechatmessages.application.res;

import com.mos.backend.privatechatmessages.entity.PrivateChatMessage;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@Getter
@NoArgsConstructor
public class PrivateChatMessageRes {
private Long privateChatMessageId;
private String message;
private LocalDateTime messageCreatedAt;

private Long userId;
private String nickname;

public static PrivateChatMessageRes of(PrivateChatMessage privateChatMessage) {
PrivateChatMessageRes privateChatMessageRes = new PrivateChatMessageRes();
privateChatMessageRes.privateChatMessageId = privateChatMessage.getId();
privateChatMessageRes.message = privateChatMessage.getMessage();
privateChatMessageRes.messageCreatedAt = privateChatMessage.getCreatedAt();
privateChatMessageRes.userId = privateChatMessage.getUser().getId();
privateChatMessageRes.nickname = privateChatMessage.getUser().getNickname();
return privateChatMessageRes;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
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.util.List;

import static com.mos.backend.privatechatmessages.entity.QPrivateChatMessage.privateChatMessage;

@RequiredArgsConstructor
@Repository
public class PrivateChatMessageQueryDslRepository {
private final JPAQueryFactory queryFactory;

public List<PrivateChatMessage> findByChatRoomIdForInfiniteScroll(Long privateChatRoomId, Long lastPrivateChatMessageId, int size) {
return queryFactory
.selectFrom(privateChatMessage)
.where(
eqPrivateChatRoomId(privateChatRoomId),
ltPrivateChatMessageId(lastPrivateChatMessageId)
)
.orderBy(privateChatMessage.id.desc())
.limit(size + 1)
.fetch();
}

private static BooleanExpression eqPrivateChatRoomId(Long privateChatRoomId) {
return privateChatMessage.privateChatRoom.id.eq(privateChatRoomId);
}

private BooleanExpression ltPrivateChatMessageId(Long lastPrivateChatMessageId) {
return lastPrivateChatMessageId == null ? null : privateChatMessage.id.lt(lastPrivateChatMessageId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
import com.mos.backend.privatechatmessages.entity.PrivateChatMessage;
import com.mos.backend.privatechatrooms.entity.PrivateChatRoom;

import java.util.List;
import java.util.Optional;

public interface PrivateChatMessageRepository {
PrivateChatMessage save(PrivateChatMessage privateChatMessage);

Optional<PrivateChatMessage> findFirstByPrivateChatRoomOrderByCreatedByDesc(PrivateChatRoom privateChatRoom);

List<PrivateChatMessage> findAllByChatRoomIdForInfiniteScroll(Long privateChatRoomId, Long lastPrivateChatMessageId, int size);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

@Repository
@RequiredArgsConstructor
public class PrivateChatMessageRepositoryImpl implements PrivateChatMessageRepository {
private final PrivateChatMessageJpaRepository privateChatMessageJpaRepository;
private final PrivateChatMessageQueryDslRepository privateChatMessageQueryDslRepository;

@Override
public PrivateChatMessage save(PrivateChatMessage privateChatMessage) {
Expand All @@ -21,4 +23,9 @@ public PrivateChatMessage save(PrivateChatMessage privateChatMessage) {
public Optional<PrivateChatMessage> findFirstByPrivateChatRoomOrderByCreatedByDesc(PrivateChatRoom privateChatRoom) {
return privateChatMessageJpaRepository.findFirstByPrivateChatRoomOrderByCreatedByDesc(privateChatRoom);
}

@Override
public List<PrivateChatMessage> findAllByChatRoomIdForInfiniteScroll(Long privateChatRoomId, Long lastPrivateChatMessageId, int size) {
return privateChatMessageQueryDslRepository.findByChatRoomIdForInfiniteScroll(privateChatRoomId, lastPrivateChatMessageId, size);
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package com.mos.backend.privatechatmessages.presentation.controller.api;

import com.mos.backend.common.annotation.Sender;
import com.mos.backend.common.dto.InfinityScrollRes;
import com.mos.backend.privatechatmessages.application.PrivateChatMessageService;
import com.mos.backend.privatechatmessages.application.res.PrivateChatMessageRes;
import com.mos.backend.privatechatmessages.presentation.req.PrivateChatMessagePublishReq;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RequiredArgsConstructor
@RestController
Expand All @@ -16,4 +22,13 @@ public class PrivateChatMessageController {
public void publishPrivateChatMessage(@Sender Long userId, PrivateChatMessagePublishReq privateChatMessagePublishReq) {
privateChatMessageService.publish(userId, privateChatMessagePublishReq);
}

@ResponseStatus(HttpStatus.OK)
@GetMapping("/private-chat-rooms/{privateChatRoomId}/messages")
public InfinityScrollRes<PrivateChatMessageRes> getPrivateChatMessages(@AuthenticationPrincipal Long userId,
@PathVariable Long privateChatRoomId,
@RequestParam(required = false) Long lastPrivateChatMessageId,
@RequestParam(defaultValue = "10") int size) {
return privateChatMessageService.getPrivateChatMessages(userId, privateChatRoomId, lastPrivateChatMessageId, size);
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package com.mos.backend.studychatmessages.application;

import com.mos.backend.common.dto.InfinityScrollRes;
import com.mos.backend.common.exception.MosException;
import com.mos.backend.common.infrastructure.EntityFacade;
import com.mos.backend.common.redis.RedisPublisher;
import com.mos.backend.common.utils.InfinityScrollUtil;
import com.mos.backend.studychatmessages.application.dto.StudyChatMessageDto;
import com.mos.backend.studychatmessages.application.res.StudyChatMessageRes;
import com.mos.backend.studychatmessages.entity.StudyChatMessage;
import com.mos.backend.studychatmessages.infrastructure.StudyChatMessageRepository;
import com.mos.backend.studychatmessages.presentation.req.StudyChatMessagePublishReq;
Expand All @@ -15,6 +18,9 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;

@RequiredArgsConstructor
@Service
public class StudyChatMessageService {
Expand All @@ -41,4 +47,30 @@ private StudyChatMessage saveStudyChatMessage(StudyChatMessagePublishReq req, Us
StudyChatMessage studyChatMessage = StudyChatMessage.of(user, studyChatRoom, req.getMessage());
return studyChatMessageRepository.save(studyChatMessage);
}

@Transactional(readOnly = true)
public InfinityScrollRes<StudyChatMessageRes> getStudyChatMessages(Long userId, Long studyChatRoomId, Long lastStudyChatMessageId, Integer size) {
User user = entityFacade.getUser(userId);
StudyChatRoom studyChatRoom = entityFacade.getStudyChatRoom(studyChatRoomId);

if (!studyMemberRepository.existsByUserAndStudy(user, studyChatRoom.getStudy()))
throw new MosException(StudyChatRoomErrorCode.FORBIDDEN);

List<StudyChatMessage> studyChatMessages = studyChatMessageRepository.findAllByChatRoomIdForInfiniteScroll(
studyChatRoom.getId(), lastStudyChatMessageId, size
);

boolean hasNext = InfinityScrollUtil.hasNext(studyChatMessages, size);
if (hasNext)
studyChatMessages.remove(studyChatMessages.size() - 1);

Optional<StudyChatMessage> lastElement = InfinityScrollUtil.getLastElement(studyChatMessages);
Long lastElementId = lastElement.map(StudyChatMessage::getId).orElse(null);

List<StudyChatMessageRes> studyChatMessageResList = studyChatMessages.stream()
.map(StudyChatMessageRes::of)
.toList();

return InfinityScrollRes.of(studyChatMessageResList, lastElementId, hasNext);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.mos.backend.studychatmessages.application.res;

import com.mos.backend.studychatmessages.entity.StudyChatMessage;
import com.mos.backend.users.entity.User;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@NoArgsConstructor
@Getter
public class StudyChatMessageRes {
private Long studyChatMessageId;
private String message;
private LocalDateTime messageCreatedAt;

private Long userId;
private String nickname;

public static StudyChatMessageRes of(StudyChatMessage studyChatMessage) {
User user = studyChatMessage.getUser();

StudyChatMessageRes studyChatMessageRes = new StudyChatMessageRes();
studyChatMessageRes.studyChatMessageId = studyChatMessage.getId();
studyChatMessageRes.message = studyChatMessage.getMessage();
studyChatMessageRes.messageCreatedAt = studyChatMessage.getCreatedAt();

studyChatMessageRes.userId = user.getId();
studyChatMessageRes.nickname = user.getNickname();

return studyChatMessageRes;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.mos.backend.studychatmessages.infrastructure;

import com.mos.backend.studychatmessages.entity.StudyChatMessage;
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 static com.mos.backend.studychatmessages.entity.QStudyChatMessage.studyChatMessage;

@Repository
@RequiredArgsConstructor
public class StudyChatMessageQueryDslRepository {
private final JPAQueryFactory queryFactory;

public List<StudyChatMessage> findAllByChatRoomIdForInfiniteScroll(Long studyChatRoomId, Long lastStudyChatMessageId, Integer size) {
return queryFactory
.selectFrom(studyChatMessage)
.where(
eqStudyChatRoomId(studyChatRoomId),
ltStudyChatMessageId(lastStudyChatMessageId)
)
.orderBy(studyChatMessage.id.desc())
.limit(size + 1)
.fetch();
}

private static BooleanExpression ltStudyChatMessageId(Long lastStudyChatMessageId) {
return lastStudyChatMessageId == null ? null : studyChatMessage.id.lt(lastStudyChatMessageId);
}

private static BooleanExpression eqStudyChatRoomId(Long studyChatRoomId) {
return studyChatMessage.studyChatRoom.id.eq(studyChatRoomId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

import com.mos.backend.studychatmessages.entity.StudyChatMessage;

import java.util.List;

public interface StudyChatMessageRepository {
StudyChatMessage save(StudyChatMessage studyChatMessage);

List<StudyChatMessage> findAllByChatRoomIdForInfiniteScroll(Long studyChatRoomId, Long lastStudyChatMessageId, Integer size);
}
Loading
0