Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import com.deepdirect.deepwebide_be.chat.service.ChatMessageWriteService;
import com.deepdirect.deepwebide_be.chat.util.RedisPublisher;
import lombok.RequiredArgsConstructor;
import org.springframework.messaging.handler.annotation.DestinationVariable;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.stereotype.Controller;
Expand All @@ -19,17 +18,17 @@ public class ChatWebSocketController {

@MessageMapping("/repositories/{repositoryId}/chat/send")
public void sendMessage(
@DestinationVariable Long repositoryId,
ChatMessageRequest request,
SimpMessageHeaderAccessor headerAccessor
) {

// 1. WebSocket 세션에서 userId 추출
Long userId = (Long) headerAccessor.getSessionAttributes().get("userId");

// 2. 메시지 저장 + DTO 응답 변환
ChatMessageBroadcast broadcast = chatMessageWriteService.saveChatMessage(userId, repositoryId, request.getMessage());
ChatMessageBroadcast broadcast = chatMessageWriteService.saveChatMessage(userId, request);

// 3. Redis 채널로 publish
redisPublisher.publish("chatroom:" + repositoryId, broadcast);
redisPublisher.publish("chatroom:" + request.getRepositoryId(), broadcast);
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package com.deepdirect.deepwebide_be.chat.domain;

import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.*;

import java.time.LocalDateTime;

@Entity
@Table(name = "chat_message_references")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Builder
public class ChatMessageReference {

@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,17 @@ public class ChatMessageRequest {
@JsonProperty("type")
private ChatMessageType type;

@Schema(description = "레포지토리 ID")
private Long repositoryId;

@Schema(description = "코드 참조 정보")
private CodeReferenceRequest codeReference;

@Builder
public ChatMessageRequest(String message, ChatMessageType type) {
public ChatMessageRequest(String message, ChatMessageType type, Long repositoryId, CodeReferenceRequest codeReference) {
this.message = message;
this.type = type;
this.repositoryId = repositoryId;
this.codeReference = codeReference;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.deepdirect.deepwebide_be.chat.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "코드 참조 요청 정보")
public class CodeReferenceRequest {

private Long referenceId;
private String path;
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,17 @@ public class ChatMessageBroadcast {
@Schema(description = "메시지 내용")
private String message;

@Schema(description = "코드 참조 정보", nullable = true)
private CodeReferenceResponse codeReference;

@Schema(description = "보낸 시간")
private LocalDateTime sentAt;

@Schema(description = "내 메시지 여부", name = "IsMine")
private boolean isMine;


public static ChatMessageBroadcast of(ChatMessage message, User sender, Long repositoryId) {
public static ChatMessageBroadcast of(ChatMessage message, User sender, Long repositoryId, CodeReferenceResponse codeReference) {
return ChatMessageBroadcast.builder()
.repositoryId(repositoryId)
.type(ChatMessageType.CHAT) //일반 채팅
Expand All @@ -51,8 +54,9 @@ public static ChatMessageBroadcast of(ChatMessage message, User sender, Long rep
.senderNickname(sender.getNickname())
.senderProfileImageUrl(sender.getProfileImageUrl())
.message(message.getMessage())
.codeReference(codeReference)
.sentAt(message.getSentAt())
.isMine(true)
.isMine(false)
.build();
}

Expand All @@ -65,6 +69,7 @@ public static ChatMessageBroadcast system(User user, ChatMessageType type, Strin
.senderNickname(user.getNickname())
.senderProfileImageUrl(user.getProfileImageUrl())
.message(message)
.codeReference(null)
.sentAt(LocalDateTime.now())
.isMine(false)
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

import com.deepdirect.deepwebide_be.chat.domain.ChatMessage;
import com.deepdirect.deepwebide_be.chat.domain.ChatMessageReference;
import com.deepdirect.deepwebide_be.chat.dto.request.ChatMessageRequest;
import com.deepdirect.deepwebide_be.chat.dto.request.CodeReferenceRequest;
import com.deepdirect.deepwebide_be.chat.dto.response.ChatMessageBroadcast;
import com.deepdirect.deepwebide_be.chat.dto.response.CodeReferenceResponse;
import com.deepdirect.deepwebide_be.chat.repository.ChatMessageReferenceRepository;
import com.deepdirect.deepwebide_be.chat.repository.ChatMessageRepository;
import com.deepdirect.deepwebide_be.global.exception.GlobalException;
Expand Down Expand Up @@ -30,7 +33,9 @@ public class ChatMessageWriteService {
private final ChatMessageReferenceRepository referenceRepository;

@Transactional
public ChatMessageBroadcast saveChatMessage(Long userId, Long repositoryId, String content) {
public ChatMessageBroadcast saveChatMessage(Long userId, ChatMessageRequest request) {
Long repositoryId = request.getRepositoryId();

Repository repository = repositoryRepository.findByIdAndDeletedAtIsNull(repositoryId)
.orElseThrow(() -> new GlobalException(REPOSITORY_NOT_FOUND));

Expand All @@ -45,14 +50,29 @@ public ChatMessageBroadcast saveChatMessage(Long userId, Long repositoryId, Stri
User sender = userRepository.findById(userId)
.orElseThrow(() -> new GlobalException(USER_NOT_FOUND));

String content = request.getMessage();
if (content == null || content.isEmpty()) {
throw new GlobalException(EMPTY_CHAT_MESSAGE);
}

// 1. 메시지 저장
ChatMessage chatMessage = chatMessageRepository.save(
ChatMessage.of(repository, sender, content)
);

return ChatMessageBroadcast.of(chatMessage, sender, repositoryId);
// 2. 참조 저장 (있으면)
CodeReferenceRequest refRequest = request.getCodeReference();
ChatMessageReference reference = null;
if (refRequest != null && refRequest.getPath() != null) {
reference = referenceRepository.save(ChatMessageReference.builder()
.chatMessage(chatMessage)
.path(refRequest.getPath())
.build());
}

// 3. 응답 DTO 변환
CodeReferenceResponse codeReferenceResponse = reference != null ? CodeReferenceResponse.from(reference) : null;

return ChatMessageBroadcast.of(chatMessage, sender, repositoryId, codeReferenceResponse);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public enum ErrorCode {
UNSUPPORTED_REPOSITORY_TYPE(HttpStatus.BAD_REQUEST, "지원하지 않는 레포지토리 타입입니다."),
REPOSITORY_FILES_NOT_FOUND(HttpStatus.BAD_REQUEST, "레포지토리 파일을 찾을 수 없습니다."),
FILE_TREE_CONVERSION_FAILED(HttpStatus.BAD_REQUEST, "파일 트리 변환에 실패했습니다."),
INVALID_PHONE_FORMAT(HttpStatus.BAD_REQUEST, "올바른 휴대전화 번호 형식이 아닙니다."),
FILE_UPLOAD_FAIL(HttpStatus.BAD_REQUEST, "파일 업로드에 실패했습니다."),
PARENT_ID_REQUIRED(HttpStatus.BAD_REQUEST, "부모 폴더 ID는 필수입니다."),
FILE_EXTENSION_REQUIRED(HttpStatus.BAD_REQUEST, "파일 확장자는 필수입니다."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import java.time.LocalDateTime;
import java.util.Random;
import java.util.regex.Pattern;

@Service
@RequiredArgsConstructor
Expand Down Expand Up @@ -55,7 +56,13 @@ private String generateRandomNumber() {
}

// 문자 발송

public int sendVerificationCode(String phoneNumber, String reqUserName, AuthType reqAuthType) {
// 휴대전화 번호 형식 정규식 (010으로 시작하고 숫자 총 10~11자리)
Pattern phonePattern = Pattern.compile("^01[0|1|6|7|8|9][0-9]{7,8}$");
if (!phonePattern.matcher(phoneNumber).matches()) {
throw new GlobalException(ErrorCode.INVALID_PHONE_FORMAT);
}

if (reqAuthType == AuthType.SIGN_UP && isUserExists(phoneNumber, reqUserName)) {
throw new GlobalException(ErrorCode.DUPLICATE_NAME_AND_PHONE);
Expand All @@ -74,7 +81,6 @@ public int sendVerificationCode(String phoneNumber, String reqUserName, AuthType
throw new GlobalException(ErrorCode.SMS_SEND_FAILED);
}

// 인증 정보 저장
phoneVerificationRepository.save(
PhoneVerification.builder()
.phoneNumber(phoneNumber)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,10 @@ public SignUpResponse signup(SignUpRequest request) {
if (userRepository.existsByEmail(request.getEmail())) {
throw new GlobalException(ErrorCode.DUPLICATE_EMAIL);
}

Pattern phonePattern = Pattern.compile("^01[0|1|6|7|8|9][0-9]{7,8}$");
if (!phonePattern.matcher(request.getPhoneNumber()).matches()) {
throw new GlobalException(ErrorCode.INVALID_PHONE_FORMAT);
}
if (userRepository.existsByPhoneNumber(request.getPhoneNumber())) {
throw new GlobalException(ErrorCode.PHONE_NUMBER_ALREADY_USED);
}
Expand Down