Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
a551848
chore #7: webSocket 관련 build.gradle 설정 추가
ghkdwldus0807 May 4, 2025
633de32
feat #7: webSocket config 추가
ghkdwldus0807 May 4, 2025
a59e4d5
feat #7: SendChatMessageRequest DTO 추가 및 ChatMessage 정적 팩토리 메서드 추가
ghkdwldus0807 May 5, 2025
05f4e08
feat #7: ChatMessage 및 ChatRoom Entity Builder, 정적 팩토리 메소드 추가
ghkdwldus0807 May 7, 2025
880a07a
feat #7: ChatMessage 및 ChatRoom의 Request DTO,Response DTO 추가
ghkdwldus0807 May 7, 2025
52300ae
remove #7: SendChatMessageRequest DTO 삭제
ghkdwldus0807 May 7, 2025
ea4dcb7
feat #7: ChatMessage 및 ChatRoom DTO Mappper 추가
ghkdwldus0807 May 7, 2025
6da2304
feat #7: ChatRoom & ChatMessage Create API init
ghkdwldus0807 May 7, 2025
ca170d2
feat #7: ChatRoom Error Code
ghkdwldus0807 May 7, 2025
dc52ac7
Merge branch 'dev' of https://github.com/URECA-DangSim/dangsim-be int…
ghkdwldus0807 May 7, 2025
21b4d1d
feat #7: ChatRoom Create API 추가
ghkdwldus0807 May 7, 2025
1ee06ee
fix #7: 불필요한 @Transactional 제거
ghkdwldus0807 May 8, 2025
2622d59
feat #7: 채팅방 생성 및 조회 API
ghkdwldus0807 May 8, 2025
e25e52b
rename #7: 채팅방 생성 Request DTO 명칭 변경
ghkdwldus0807 May 8, 2025
4f04da2
Merge branch 'dev' of https://github.com/URECA-DangSim/dangsim-be int…
ghkdwldus0807 May 8, 2025
48607a5
feat #7: ErrorCode Enum 추가
ghkdwldus0807 May 9, 2025
14af7d0
fix #7: 채팅방 목록 조회 query dsl repository where절 하나로 병합
ghkdwldus0807 May 9, 2025
9c37663
fix #7: User ErrorCode로 예외처리 변경
ghkdwldus0807 May 9, 2025
9ff2839
feat #7: WebSocket 환경 설정 & 웹소켓에서의 jwt token 검증
ghkdwldus0807 May 9, 2025
3a9540e
feat #7: WebSocket 실시간 채팅 Controller 추가
ghkdwldus0807 May 9, 2025
15154a9
feat #7: WebSocket 실시간 채팅 메시지 DTO 추가
ghkdwldus0807 May 9, 2025
fbadaae
feat #7: websocket 브로드캐스트 경로 추가
ghkdwldus0807 May 9, 2025
5c2b4b9
fix #7: ChatMessage 정적 팩토리 메서드 수정
ghkdwldus0807 May 9, 2025
a2055b1
Merge branch 'dev' of https://github.com/URECA-DangSim/dangsim-be int…
ghkdwldus0807 May 9, 2025
a456b43
feat #7: 채팅방 상세 조회 Controller 추가
ghkdwldus0807 May 12, 2025
fa999ea
feat #7: 채팅방 상세 조회 Service 추가
ghkdwldus0807 May 12, 2025
dc5a326
feat #7: 채팅방 상세 조회 Query DSL 추가
ghkdwldus0807 May 12, 2025
bba1e58
feat #7: 채팅방 상세 조회 Response DTO 추가
ghkdwldus0807 May 12, 2025
bd38b73
fix #7: 채팅방 마지막 메시지 날짜 형 변환 수정
ghkdwldus0807 May 12, 2025
23855ef
style #7: 코드 포맷팅
ghkdwldus0807 May 12, 2025
6747cf7
refactor #7: String 변수 상수화
ghkdwldus0807 May 12, 2025
bdfc7e7
fix #7: MessageType enum에 string messageType 추가
ghkdwldus0807 May 12, 2025
d320403
fix #7: 누락된 어노테이션 추가 및 조건절 수정
ghkdwldus0807 May 12, 2025
b73c9af
refactor #7:Boolean Expression을 공통 메서드로 추출
ghkdwldus0807 May 12, 2025
afe2331
Merge branch 'dev' of https://github.com/URECA-DangSim/dangsim-be int…
ghkdwldus0807 May 12, 2025
e2417eb
Merge branch 'dev' of https://github.com/URECA-DangSim/dangsim-be int…
ghkdwldus0807 May 14, 2025
c0a5465
Merge branch 'dev' of https://github.com/URECA-DangSim/dangsim-be int…
ghkdwldus0807 May 14, 2025
6e4f77e
Merge branch 'dev' of https://github.com/URECA-DangSim/dangsim-be int…
ghkdwldus0807 May 14, 2025
c86257d
fix #52: chatRoomTaskInfo 조회 시 자신의 userId를 응답으로 반환하도록 변경
ghkdwldus0807 May 16, 2025
b86c3a0
fix #52: chatRoom 생성 시 첫 메시지도 생성되도록 변경
ghkdwldus0807 May 16, 2025
079c660
fix #52: chatRoom 및 message ID 기반 조회로 변경 (QueryDSL)
ghkdwldus0807 May 16, 2025
a5ce7b5
fix #52: 웹소켓 관련 환경 설정 변경 및 security 변경
ghkdwldus0807 May 16, 2025
2c11ec9
conflict merge
ghkdwldus0807 May 16, 2025
4d20a34
test #52: TaskService Mock 추가
ghkdwldus0807 May 16, 2025
6996858
fix #59: 웹소켓 기반 실시간 메시지 전송 시 userId를 body에서 받은 후 인증하도록 수정
ghkdwldus0807 May 16, 2025
c2e05dc
fix #59: Task 완료 boolean 값 수정
ghkdwldus0807 May 16, 2025
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
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.dangsim.chat.controller;

import java.security.Principal;

import org.springframework.messaging.handler.annotation.DestinationVariable;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.stereotype.Controller;
Expand All @@ -21,10 +19,10 @@ public class ChatMessageStompController {
@MessageMapping("/chat-rooms/{chatRoomId}")
public void createChatMessage(
@DestinationVariable Long chatRoomId,
Principal principal,
@Valid CreateChatMessageRequest request
) {
Long userId = Long.valueOf(principal.getName());

Long userId = request.senderId();
chatMessageService.createChatMessage(request, userId, chatRoomId);
}

Expand Down
9 changes: 5 additions & 4 deletions src/main/java/com/dangsim/chat/dto/ChatRoomMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,13 @@ public static TaskInfoResponse toTaskInfoResponse(Task task) {
}

public static ChatRoomInfoResponse toChatRoomInfoResponse(Long chatRoomId, User chatPartner,
TaskInfoResponse taskInfoResponse) {
TaskInfoResponse taskInfoResponse, Long userId) {
return new ChatRoomInfoResponse(
chatRoomId,
taskInfoResponse,
chatPartner.getId(),
chatPartner.getNickname()
chatPartner.getNickname(),
userId
);
}

Expand All @@ -56,9 +57,9 @@ public static String getThumbNail(List<TaskImage> imageUrls) {

public static boolean isCompletedTask(Task task) {
if (Objects.equals(task.getStatus(), TASK_COMPLETE)) {
return false;
return true;
}
return true;
return false;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ public record CreateChatMessageRequest(
MessageType type,
@Size(max = 300, message = "최대 300자까지 입력이 가능합니다.")
@NotBlank
String content
String content,
@NotNull
Long senderId
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ public record ChatRoomInfoResponse(
Long chatRoomId,
TaskInfoResponse taskInfo,
Long chatPartnerId,
String partnerNickname
String partnerNickname,
Long myId
) {
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.dangsim.chat.dto.response;

import java.util.Objects;

import com.dangsim.chat.entity.ChatMessage;
import com.dangsim.chat.entity.ChatRoom;
import com.dangsim.common.util.DateTimeFormatUtils;
Expand All @@ -12,7 +14,8 @@ public record ChatRoomSimpleResponse(
String nickname,
String content,
String timestamp,
Boolean isRead
Boolean isRead,
Long chatMessageId

) {
@QueryProjection
Expand All @@ -21,9 +24,35 @@ public ChatRoomSimpleResponse(ChatRoom chatRoom, ChatMessage lastChatMessage, Us
chatRoom.getId(),
partner.getId(),
partner.getNickname(),
lastChatMessage.getMessage(),
DateTimeFormatUtils.formatDateTime(lastChatMessage.getCreatedAt()),
lastChatMessage.isRead()
initChatMessage(lastChatMessage),
initTimeStamp(lastChatMessage),
initIsRead(lastChatMessage),
lastChatMessage.getId()
);
}

private static final String DEFAULT = "채팅을 시작해보세요";

public static String initChatMessage(ChatMessage lastChatMessage) {
if (Objects.isNull(lastChatMessage)) {
return DEFAULT;
}
return lastChatMessage.getMessage();
}

public static String initTimeStamp(ChatMessage lastChatMessage) {
if (Objects.isNull(lastChatMessage)) {
return "";
}
return DateTimeFormatUtils.formatDateTime(lastChatMessage.getCreatedAt());
}

public static Boolean initIsRead(ChatMessage lastChatMessage) {
if (Objects.isNull(lastChatMessage)) {
return false;
}
return lastChatMessage.isRead();
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,26 @@ public class ChatRoomQueryRepositoryImpl implements ChatRoomQueryRepository {
private final JPAQueryFactory queryFactory;

@Override
public CursorPageResponse<ChatRoomSimpleResponse> findChatRoomsByCursor(String cursor, int size, Long userId) {
public CursorPageResponse<ChatRoomSimpleResponse> findChatRoomsByCursor(
String cursor, int size, Long userId) {

QChatMessage lastMessage = new QChatMessage("lastMessage");
QUser partner = new QUser("partner");

BooleanExpression isRequester = chatRoom.requester.id.eq(userId)
.and(partner.id.eq(chatRoom.performer.id));
BooleanExpression isPerformer = chatRoom.performer.id.eq(userId)
.and(partner.id.eq(chatRoom.requester.id));
BooleanExpression partnerJoinCondition = isRequester.or(isPerformer);

BooleanExpression cursorFilter = null;
if (cursor != null && !cursor.isBlank()) {
cursorFilter = lastMessage.id.lt(Long.parseLong(cursor));
}

List<ChatRoomSimpleResponse> chatRooms = queryFactory
.select(new QChatRoomSimpleResponse(chatRoom, lastMessage, partner))
.from(chatRoom)
//최근 메시지 조인
.leftJoin(lastMessage)
.on(lastMessage.chatRoomId.eq(chatRoom.id),
lastMessage.id.eq(
Expand All @@ -50,17 +61,14 @@ public CursorPageResponse<ChatRoomSimpleResponse> findChatRoomsByCursor(String c
.from(chatMessage)
.where(chatMessage.chatRoomId.eq(chatRoom.id))
))
//상대방 유저 조인
.leftJoin(partner)
.on(
chatRoom.requester.id.eq(userId).and(partner.id.eq(chatRoom.performer.id))
.or(chatRoom.performer.id.eq(userId).and(partner.id.eq(chatRoom.requester.id)))
)
.on(partnerJoinCondition)
.where(
chatRoom.requester.id.eq(userId).or(chatRoom.performer.id.eq(userId)),
isBeforeCursor(lastMessage.createdAt, cursor)
chatRoom.requester.id.eq(userId)
.or(chatRoom.performer.id.eq(userId)),
cursorFilter
)
.orderBy(lastMessage.createdAt.desc())
.orderBy(lastMessage.id.desc())
.limit(size + 1)
.fetch();

Expand All @@ -74,15 +82,14 @@ public CursorPageResponse<ChatRoomSimpleResponse> findChatRoomsByCursor(String c
@Override
public CursorPageResponse<ChatRoomDetailResponse> findChatMessagesByCursor(Long chatRoomId, String cursor, int size,
Long userId) {

List<ChatMessageDetailResponse> allMessages = queryFactory
.select(new QChatMessageDetailResponse(chatMessage))
.from(chatMessage)
.where(
chatMessage.chatRoomId.eq(chatRoomId),
isBeforeCursor(chatMessage.createdAt, cursor)
chatMessageCursorFilter(cursor)
)
.orderBy(chatMessage.createdAt.desc())
.orderBy(chatMessage.id.desc())
.limit(size + 1)
.fetch();

Expand All @@ -95,6 +102,22 @@ public CursorPageResponse<ChatRoomDetailResponse> findChatMessagesByCursor(Long
return new CursorPageResponse<>(Collections.singletonList(detailResponse), nextCursor, hasNext);
}

private BooleanExpression chatRoomCursorFilter(String cursor) {
if (Objects.isNull(cursor) || cursor.isBlank()) {
return null;
}

return chatRoom.id.lt(Long.parseLong(cursor));
}

private BooleanExpression chatMessageCursorFilter(String cursor) {
if (Objects.isNull(cursor) || cursor.isBlank()) {
return null;
}

return chatMessage.id.lt(Long.parseLong(cursor));
}

private BooleanExpression isBeforeCursor(DateTimePath<LocalDateTime> dateTimePath, String cursor) {
if (cursor == null) {
return null;
Expand All @@ -106,7 +129,7 @@ private String getNextCursor(List<ChatRoomSimpleResponse> chatrooms, boolean has
if (Objects.isNull(chatrooms) || chatrooms.isEmpty() || !hasNext) {
return null;
}
return chatrooms.get(chatrooms.size() - 1).timestamp();
return String.valueOf(chatrooms.get(chatrooms.size() - 1).chatMessageId());
}

private String getNextCursorByMessage(List<ChatMessageDetailResponse> messages, boolean hasNext) {
Expand Down
13 changes: 2 additions & 11 deletions src/main/java/com/dangsim/chat/service/ChatRoomService.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
package com.dangsim.chat.service;

import java.time.LocalDateTime;
import java.util.Objects;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -17,7 +14,6 @@
import com.dangsim.chat.repository.ChatRoomRepository;
import com.dangsim.common.CursorPageResponse;
import com.dangsim.common.exception.runtime.BaseException;
import com.dangsim.common.util.DateTimeFormatUtils;
import com.dangsim.task.dto.response.TaskInfoResponse;
import com.dangsim.task.entity.Task;
import com.dangsim.task.exception.TaskErrorCode;
Expand Down Expand Up @@ -51,18 +47,13 @@ public ChatRoomResponse createChatRoom(@Valid CreateChatRoomRequest request, Use

@Transactional(readOnly = true)
public CursorPageResponse<ChatRoomSimpleResponse> getChatRoomsByCursor(String cursor, int size, Long userId) {
if (Objects.isNull(cursor) || cursor.isBlank()) {
cursor = DateTimeFormatUtils.formatDateTime(LocalDateTime.now());
}

return chatRoomRepository.findChatRoomsByCursor(cursor, size, userId);
}

public CursorPageResponse<ChatRoomDetailResponse> getChatMessagesByCursor(Long chatRoomId, String cursor, int size,
Long userId) {
if (Objects.isNull(cursor) || cursor.isBlank()) {
cursor = DateTimeFormatUtils.formatDateTime(LocalDateTime.now());
}

return chatRoomRepository.findChatMessagesByCursor(chatRoomId, cursor, size, userId);
}

Expand All @@ -84,7 +75,7 @@ public ChatRoomInfoResponse getChatRoomInfo(Long chatRoomId, Long userId) {
.orElseThrow(() -> new BaseException(UserErrorCode.USER_NOT_FOUND));

return ChatRoomMapper.toChatRoomInfoResponse(chatRoomId, chatPartner,
taskInfoResponse);
taskInfoResponse, userId);

}
}
2 changes: 1 addition & 1 deletion src/main/java/com/dangsim/common/config/CorsConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@
// }
// };
// }
// }
// }
12 changes: 11 additions & 1 deletion src/main/java/com/dangsim/common/config/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
Expand All @@ -21,6 +22,14 @@
@EnableWebSecurity
public class SecurityConfig {

@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return web -> web.ignoring()
.requestMatchers(
"/error", "/favicon.ico", "/ws/**"
);
}

@Bean
public SecurityFilterChain filterChain(HttpSecurity http, JwtProvider jwtProvider,
UserRepository userRepository) throws Exception {
Expand All @@ -32,6 +41,7 @@ public SecurityFilterChain filterChain(HttpSecurity http, JwtProvider jwtProvide
// .anyRequest().permitAll()
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers(HttpMethod.GET, "/api/tasks").permitAll()
.requestMatchers("/ws-chat/**").permitAll()
.requestMatchers(
"/swagger-ui/**",
"/v3/api-docs/**",
Expand All @@ -53,7 +63,7 @@ public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of(
"http://localhost:3000",
"https://dangsim-fe.pages.dev"
"https://dangsim-fe.pages.dev", "/ws/**"
));
configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"));
configuration.setAllowedHeaders(List.of("*"));
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/dangsim/common/config/WebSocketConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws-chat") //웹소켓 연결 주소
.setAllowedOriginPatterns("*") //cors 허용
.withSockJS();
.setAllowedOrigins("http://localhost:3000"); //cors 허용
// .withSockJS();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public Message<?> preSend(Message<?> message, MessageChannel channel) {
Long userId = jwtProvider.getUserIdFromToken(token);
Principal principal = () -> String.valueOf(userId);
accessor.setUser(principal);
accessor.setLeaveMutable(true);
}
return message;
}
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/com/dangsim/task/service/TaskService.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.dangsim.chat.entity.ChatMessage;
import com.dangsim.chat.entity.ChatRoom;
import com.dangsim.chat.repository.ChatMessageRepository;
import com.dangsim.chat.repository.ChatRoomRepository;
import com.dangsim.common.CursorPageResponse;
import com.dangsim.common.exception.runtime.BaseException;
Expand Down Expand Up @@ -37,9 +39,12 @@
@RequiredArgsConstructor
public class TaskService {

private static final String HELLO_CHAT_MESSAGE = "채팅을 시작해보세요";

private final TaskRepository taskRepository;
private final PaymentRepository paymentRepository;
private final ChatRoomRepository chatRoomRepository;
private final ChatMessageRepository chatMessageRepository;

@Transactional
public TaskResponseDto createTask(TaskRequestDto requestDto, User user) {
Expand Down Expand Up @@ -113,6 +118,9 @@ public TaskMatchResponse matchPerformer(Long taskId, User performer) {

ChatRoom savedChatRoom = chatRoomRepository.save(ChatRoom.of(findTask, findTask.getUser(), performer));

ChatMessage chatMessage = ChatMessage.of(savedChatRoom.getId(), performer.getId(), HELLO_CHAT_MESSAGE);
chatMessageRepository.save(chatMessage);

findTask.updateStatus(TASK_IN_PROGRESS);

return TaskMapper.toTaskMatchResponse(savedChatRoom);
Expand Down
Loading