Skip to content

Commit 2a132ad

Browse files
authored
Merge branch 'develop' into feat/request/2
2 parents b301eb4 + 7bdd9b4 commit 2a132ad

File tree

8 files changed

+201
-1
lines changed

8 files changed

+201
-1
lines changed

src/main/java/org/dfbf/soundlink/domain/chat/controller/ChatController.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,16 @@ public ResponseResult requestChat(@AuthenticationPrincipal Long id, @RequestBody
2828
public ResponseResult cancelChatRequest(@AuthenticationPrincipal Long id, @RequestBody Long emotionRecordId) {
2929
return chatRoomService.deleteRequestFromRedis(id, emotionRecordId);
3030
}
31+
32+
@PostMapping("/create")
33+
@Operation(summary = "채팅요청 시 채팅방 생성", description = "requestId, responseId 값 확인")
34+
public ResponseResult create(@AuthenticationPrincipal Long userId, @RequestParam Long recordId) {
35+
return chatRoomService.createChatRoom(userId, recordId);
36+
}
37+
38+
@PostMapping("/close")
39+
@Operation(summary = "채팅방 닫기" , description="닫을 시 상태값 'close'변경, 레디스에서 삭제")
40+
public ResponseResult close(@AuthenticationPrincipal Long userId, Long chatRoomId) {
41+
return chatRoomService.closeChatRoom(userId, chatRoomId);
42+
}
3143
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package org.dfbf.soundlink.domain.chat.dto;
2+
3+
4+
public record ChatReqDto (
5+
Long requestId,
6+
Long responseId
7+
){}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package org.dfbf.soundlink.domain.chat.entity;
2+
3+
import jakarta.persistence.*;
4+
import lombok.AccessLevel;
5+
import lombok.Builder;
6+
import lombok.Getter;
7+
import lombok.NoArgsConstructor;
8+
import org.dfbf.soundlink.domain.emotionRecord.entity.EmotionRecord;
9+
import org.dfbf.soundlink.domain.user.entity.User;
10+
import org.dfbf.soundlink.global.comm.enums.RoomStatus;
11+
import org.hibernate.annotations.CreationTimestamp;
12+
import org.hibernate.annotations.UpdateTimestamp;
13+
14+
import java.sql.Timestamp;
15+
16+
@Entity
17+
@Getter
18+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
19+
public class ChatRoom {
20+
@Id
21+
@GeneratedValue(strategy = GenerationType.IDENTITY)
22+
@Column(name = "chat_room_id")
23+
private Long chatRoomId;
24+
25+
@Column(name = "start_time")
26+
private Timestamp startTime;
27+
28+
@Column(name = "end_time")
29+
private Timestamp endTime;
30+
31+
@ManyToOne
32+
@JoinColumn (name = "request_user_id")
33+
private User requestUserId;
34+
35+
@ManyToOne
36+
@JoinColumn(name = "record_id")
37+
private EmotionRecord recordId;
38+
39+
@Enumerated(EnumType.STRING)
40+
@Column(name = "status")
41+
private RoomStatus status;
42+
43+
@CreationTimestamp
44+
@Column(name = "created_at")
45+
private Timestamp createdAt;
46+
47+
@UpdateTimestamp
48+
@Column(name = "updated_at")
49+
private Timestamp updatedAt;
50+
51+
@Builder
52+
public ChatRoom(User requestUserId, EmotionRecord recordId, RoomStatus status,
53+
Timestamp startTime, Timestamp endTime) {
54+
this.requestUserId = requestUserId;
55+
this.recordId = recordId;
56+
this.status = status;
57+
this.startTime = startTime;
58+
this.endTime = endTime;
59+
}
60+
61+
//채팅방 상태 업데이트
62+
public void updateChatRoomStatus(RoomStatus status){
63+
this.status = status;
64+
if(status == RoomStatus.CLOSED){
65+
this.endTime = new Timestamp(System.currentTimeMillis());
66+
}
67+
}
68+
69+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package org.dfbf.soundlink.domain.chat.exception;
2+
3+
import org.dfbf.soundlink.global.exception.BusinessException;
4+
import org.dfbf.soundlink.global.exception.ErrorCode;
5+
6+
public class ChatRoomNotFoundException extends BusinessException {
7+
public ChatRoomNotFoundException() {super(ErrorCode.CHATROOM_NOT_FOUND); }
8+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package org.dfbf.soundlink.domain.chat.exception;
2+
3+
import org.dfbf.soundlink.global.exception.BusinessException;
4+
import org.dfbf.soundlink.global.exception.ErrorCode;
5+
6+
public class UnauthorizedAccessException extends BusinessException {
7+
public UnauthorizedAccessException() {
8+
super(ErrorCode.CHAT_UNAUTHORIZED);
9+
}
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package org.dfbf.soundlink.domain.chat.repository;
2+
3+
import org.dfbf.soundlink.domain.chat.entity.ChatRoom;
4+
import org.dfbf.soundlink.domain.emotionRecord.entity.EmotionRecord;
5+
import org.dfbf.soundlink.domain.user.entity.User;
6+
import org.springframework.data.jpa.repository.JpaRepository;
7+
8+
public interface ChatRoomRepository extends JpaRepository<ChatRoom, Long> {
9+
boolean existsByRequestUserIdAndRecordId(User requestUserId, EmotionRecord recordId);
10+
}

src/main/java/org/dfbf/soundlink/domain/chat/service/ChatRoomService.java

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,37 @@
22

33
import lombok.RequiredArgsConstructor;
44
import org.dfbf.soundlink.domain.chat.entity.redis.ChatRequest;
5+
import org.dfbf.soundlink.domain.chat.dto.ChatReqDto;
6+
import org.dfbf.soundlink.domain.chat.entity.ChatRoom;
7+
import org.dfbf.soundlink.domain.chat.exception.ChatRoomNotFoundException;
8+
import org.dfbf.soundlink.domain.chat.exception.UnauthorizedAccessException;
9+
import org.dfbf.soundlink.domain.chat.repository.ChatRoomRepository;
10+
import org.dfbf.soundlink.domain.emotionRecord.entity.EmotionRecord;
511
import org.dfbf.soundlink.domain.emotionRecord.exception.EmotionRecordNotFoundException;
12+
import org.dfbf.soundlink.domain.emotionRecord.exception.UserNotFoundException;
613
import org.dfbf.soundlink.domain.emotionRecord.repository.EmotionRecordRepository;
14+
import org.dfbf.soundlink.domain.user.entity.User;
15+
import org.dfbf.soundlink.domain.user.repository.UserRepository;
16+
import org.dfbf.soundlink.global.comm.enums.RoomStatus;
717
import org.dfbf.soundlink.global.exception.ErrorCode;
818
import org.dfbf.soundlink.global.exception.ResponseResult;
19+
import org.springframework.dao.DataIntegrityViolationException;
920
import org.springframework.data.redis.core.RedisTemplate;
21+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
1022
import org.springframework.stereotype.Service;
23+
import org.springframework.transaction.annotation.Transactional;
1124

1225
import java.time.Duration;
26+
import java.sql.Timestamp;
1327

1428
@Service
1529
@RequiredArgsConstructor
1630
public class ChatRoomService {
31+
1732
private final RedisTemplate<String, Object> redisTemplate;
1833
private final EmotionRecordRepository emotionRecordRepository;
34+
private final ChatRoomRepository chatRoomRepository;
35+
private final UserRepository userRepository;
1936

2037
private static final String CHAT_REQUEST_KEY = "chatRequest";
2138

@@ -73,7 +90,68 @@ public ResponseResult deleteRequestFromRedis(Long requestUserId, Long emotionRec
7390
return new ResponseResult(ErrorCode.FAIL_TO_FIND_EMOTION_RECORD);
7491
} catch (Exception e) {
7592
return new ResponseResult(400, "Chat request failed.");
93+
}
94+
95+
@Transactional
96+
public ResponseResult createChatRoom(@AuthenticationPrincipal Long userId, Long recordId){
97+
try {
98+
// 요청 보내는사람
99+
User requestUserId = userRepository.findById(userId)
100+
.orElseThrow(UserNotFoundException::new);
101+
102+
// 감정기록 조회
103+
EmotionRecord emotionRecord = emotionRecordRepository.findById(recordId)
104+
.orElseThrow(EmotionRecordNotFoundException::new);
105+
106+
// 이미 존재하는 채팅방인지 확인
107+
if(chatRoomRepository.existsByRequestUserIdAndRecordId(requestUserId,emotionRecord)){
108+
return new ResponseResult(ErrorCode.CHAT_FAILED, "이미 존재하는 채팅방입니다.");
109+
}
110+
111+
Long responseUserId = emotionRecord.getUser().getUserId();
112+
113+
ChatRoom chatRoom = ChatRoom.builder()
114+
.requestUserId(requestUserId)
115+
.recordId(emotionRecord)
116+
.status(RoomStatus.WAITING) //상태 : 대기
117+
.startTime(new Timestamp(System.currentTimeMillis()))
118+
.endTime(null)
119+
.build();
120+
121+
// DB에 저장
122+
chatRoomRepository.save(chatRoom);
123+
124+
ChatReqDto chatReqDto = new ChatReqDto(userId, responseUserId);
125+
126+
// 레디스에 저장
127+
redisTemplate.opsForValue().set("Room::"+chatRoom.getChatRoomId(), String.valueOf(chatReqDto));
128+
129+
return new ResponseResult(ErrorCode.SUCCESS, chatRoom);
130+
} catch (Exception e) {
131+
return new ResponseResult(ErrorCode.INTERNAL_SERVER_ERROR, e.getMessage());
76132
}
77133
}
78134

135+
// 채팅방 닫기
136+
@Transactional
137+
public ResponseResult closeChatRoom(@AuthenticationPrincipal Long userId, Long chatRoomId) {
138+
try {
139+
ChatRoom chatRoom = chatRoomRepository.findById(chatRoomId)
140+
.orElseThrow(ChatRoomNotFoundException::new);
141+
142+
// 요청자 또는 응답자가 아니면 예외 처리
143+
if(!chatRoom.getRequestUserId().getUserId().equals(userId) &&
144+
!chatRoom.getRecordId().getUser().getUserId().equals(userId)) {
145+
throw new UnauthorizedAccessException(); // 권한이 없을 경우 예외 발생
146+
}
147+
148+
chatRoom.updateChatRoomStatus(RoomStatus.CLOSED); // 삳태 '닫기'로 변경
149+
chatRoomRepository.save(chatRoom); // DB에 저장
150+
151+
redisTemplate.delete("Room::"+chatRoomId); // 레디스에서 삭제
152+
return new ResponseResult(ErrorCode.SUCCESS);
153+
} catch (Exception e) {
154+
return new ResponseResult(ErrorCode.INTERNAL_SERVER_ERROR, e.getMessage());
155+
}
156+
}
79157
}

src/main/java/org/dfbf/soundlink/global/exception/ErrorCode.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,13 @@ public enum ErrorCode {
5656
KAKAOPAY_READY_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "카카오페이 결제 준비 에러"),
5757
KAKAOPAY_APPROVE_REDIS_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "TID가 REDIS 안에 없습니다."),
5858
KAKAOPAY_APPROVE_FEIGN_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "카카오페이 승인 API 통신 실패"),
59-
KAKAOPAY_APPROVE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "카카오페이 결제 승인 에러");
59+
KAKAOPAY_APPROVE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "카카오페이 결제 승인 에러"),
60+
61+
//채팅방 관련 에러
62+
CHAT_UNAUTHORIZED(HttpStatus.FORBIDDEN,"권한이 없습니다"),
63+
CHATROOM_NOT_FOUND(HttpStatus.NOT_FOUND,"채팅방을 찾을 수 없습니다."),
64+
CHAT_FAILED(HttpStatus.INTERNAL_SERVER_ERROR,"서버 내부 에러. 중복된 레코드가 존재합니다.");
65+
6066

6167
private final HttpStatus status;
6268
private final String message;

0 commit comments

Comments
 (0)