Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
@@ -1,6 +1,8 @@
package org.ktb.modie.presentation.v1.controller;

import jakarta.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.stream.Collectors;

import org.ktb.modie.core.exception.BusinessException;
import org.ktb.modie.core.exception.CustomErrorCode;
import org.ktb.modie.core.response.SuccessResponse;
Expand All @@ -22,80 +24,79 @@
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.stream.Collectors;
import jakarta.servlet.http.HttpServletRequest;

@RestController
public class ChatHistoryController {

private final ChatRepository chatRepository;
private final UserRepository userRepository;
private final MeetRepository meetRepository; // MeetRepository 추가
private final HashIdUtil hashIdUtil;

// 생성자 수정: MeetRepository도 주입받도록 수정
public ChatHistoryController(ChatRepository chatRepository, UserRepository userRepository,
MeetRepository meetRepository, HashIdUtil hashIdUtil) {
this.chatRepository = chatRepository;
this.userRepository = userRepository;
this.meetRepository = meetRepository;
this.hashIdUtil = hashIdUtil;
}

@GetMapping("/api/v1/chat/{meetId}")
public ResponseEntity<SuccessResponse<List<ChatDto>>> getChatHistory(
@PathVariable("meetId") String meetHashId,
@RequestParam(value = "lastChatId", required = false) Long lastChatId,
@RequestAttribute("userId") String loggedInUserId,
HttpServletRequest request) {

Long meetId = hashIdUtil.decode(meetHashId);
System.out.println("getChatHistory start !! ");
// 유저 정보 보완 (userId로 DB에서 조회)
meetRepository.findById(meetId)
.orElseThrow(() -> new BusinessException(
CustomErrorCode.MEETING_NOT_FOUND,
"알 수 없는 모임입니다."
));

List<Chat> chatList;
Pageable pageable = PageRequest.of(0, 25);

if (lastChatId == null || lastChatId == 0) {
chatList = chatRepository.findTop25ByMeetIdOrderByCreatedAtDesc(meetId, pageable);
} else {
chatList = chatRepository.findByMeetIdAndMessageIdLessThanOrderByCreatedAtDesc(meetId, lastChatId,
pageable);
}

List<ChatDto> chatDtoList = chatList.stream()
.map(chat -> {
User user = chat.getUser();
Meet meet = chat.getMeet();

// 방장 여부 확인
boolean isOwner = meet.getOwner().getUserId().equals(chat.getUser().getUserId());

// 본인 여부 확인
boolean isMe = chat.getUser().getUserId().equals(loggedInUserId);

// 변경된 DTO 필드명에 맞춰 생성자 호출
return new ChatDto(
// chat.getMessageId().longValue(), // chatId
chat.getMessageId(), // chatId
(chat.getUser().getUserId()), // userId
chat.getMessageContent(), // content (이전의 message)
user.getUserName(), // nickname (이전의 sender)
chat.getCreatedAt().toString().split("\\.")[0], // dateTime
meetHashId, // meetId
isOwner, // isOwner
isMe // isMe
);
})
.collect(Collectors.toList());

System.out.println("chatDtoList size : " + chatDtoList.size());

return SuccessResponse.of(chatDtoList).asHttp(HttpStatus.OK);
}
private final ChatRepository chatRepository;
private final UserRepository userRepository;
private final MeetRepository meetRepository; // MeetRepository 추가
private final HashIdUtil hashIdUtil;

// 생성자 수정: MeetRepository도 주입받도록 수정
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

주석 지우기 !

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

주석 지웠습니다 감삼당!

public ChatHistoryController(ChatRepository chatRepository, UserRepository userRepository,
MeetRepository meetRepository, HashIdUtil hashIdUtil) {
this.chatRepository = chatRepository;
this.userRepository = userRepository;
this.meetRepository = meetRepository;
this.hashIdUtil = hashIdUtil;
}

@GetMapping("/api/v1/chat/{meetId}")
public ResponseEntity<SuccessResponse<List<ChatDto>>> getChatHistory(
@PathVariable("meetId") String meetHashId,
@RequestParam(value = "lastChatId", required = false) Long lastChatId,
@RequestAttribute("userId") String loggedInUserId,
HttpServletRequest request) {

Long meetId = hashIdUtil.decode(meetHashId);
System.out.println("getChatHistory start !! ");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

출력문 지우기 !

// 유저 정보 보완 (userId로 DB에서 조회)
meetRepository.findById(meetId)
.orElseThrow(() -> new BusinessException(
CustomErrorCode.MEETING_NOT_FOUND,
"알 수 없는 모임입니다."
));

List<Chat> chatList;
Pageable pageable = PageRequest.of(0, 25);

if (lastChatId == null || lastChatId == 0) {
chatList = chatRepository.findTop25ByMeetIdOrderByCreatedAtDesc(meetId, pageable);
} else {
chatList = chatRepository.findByMeetIdAndMessageIdLessThanOrderByCreatedAtDesc(meetId, lastChatId,
pageable);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

120자를 초과해서 줄을 띄워야할 때 파라미터를 모두 한 줄에 두면 더 보기 쉬울 거 같습니다 ! pageable만 다른 줄이 아닌 meetId도 다른 줄에 있도록 !

}

List<ChatDto> chatDtoList = chatList.stream()
.map(chat -> {
User user = chat.getUser();
Meet meet = chat.getMeet();

// 방장 여부 확인
boolean isOwner = meet.getOwner().getUserId().equals(chat.getUser().getUserId());

// 본인 여부 확인
boolean isMe = chat.getUser().getUserId().equals(loggedInUserId);

// 변경된 DTO 필드명에 맞춰 생성자 호출
String userHashId = hashIdUtil.encode(Long.parseLong(user.getUserId()));
return new ChatDto(
chat.getMessageId(), // chatId
userHashId, // userId
chat.getMessageContent(), // content (이전의 message)
user.getUserName(), // nickname (이전의 sender)
chat.getCreatedAt().toString().split("\\.")[0], // dateTime
meetHashId, // meetId
isOwner, // isOwner
isMe // isMe
);
})
.collect(Collectors.toList());

System.out.println("chatDtoList size : " + chatDtoList.size());

return SuccessResponse.of(chatDtoList).asHttp(HttpStatus.OK);
}
}
152 changes: 86 additions & 66 deletions src/main/java/org/ktb/modie/service/JwtService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

import javax.crypto.SecretKey;

import org.ktb.modie.core.exception.BusinessException;
import org.ktb.modie.core.exception.CustomErrorCode;
import org.ktb.modie.core.util.HashIdUtil;
import org.ktb.modie.domain.User;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
Expand All @@ -15,76 +18,93 @@
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
@RequiredArgsConstructor
public class JwtService {

private final long expirationTime = 1000L * 60 * 60 * 24 * 30; // 1개월

@Value("${jwt.secret}")
private String secretKey;

private SecretKey getSigningKey() {
byte[] keyBytes = Decoders.BASE64.decode(secretKey);
return Keys.hmacShaKeyFor(keyBytes);
}

// JWT 토큰 생성
public String createToken(User user) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + expirationTime); // 만료 시간 설정

return Jwts.builder()
.setSubject(user.getUserId())
.setIssuedAt(now) // 발급 시간 설정
.expiration(expiryDate) // 만료 시간 설정
.signWith(getSigningKey()) // SecretKey 적용
.compact(); // 토큰 생성
}

// JWT 토큰에서 사용자 정보 추출
public String extractUserId(String token) {
Claims claims = Jwts.parser()
.verifyWith(getSigningKey())
.build()
.parseSignedClaims(token)
.getPayload();
return claims.getSubject(); // 사용자 ID 반환
}

// JWT 토큰 유효성 검사
public boolean isTokenExpired(String token) {
Date expiration = Jwts.parser()
.verifyWith(getSigningKey())
.build()
.parseSignedClaims(token)
.getPayload()
.getExpiration();
return expiration.before(new Date());
}

// JWT 토큰의 유효성 검사
public boolean isTokenValid(String token) {
try {
Jwts.parser()
.verifyWith(getSigningKey())
.build()
.parseSignedClaims(token);
return !isTokenExpired(token);
} catch (ExpiredJwtException e) {
log.warn("Token expired: {}", e.getMessage());
return false;
} catch (MalformedJwtException e) {
log.warn("Invalid JWT token format: {}", e.getMessage());
return false;
} catch (SignatureException e) {
log.error("Invalid JWT signature: {}", e.getMessage());
return false;
} catch (Exception e) {
log.error("Unexpected token validation error: {}", e.getMessage());
return false;
}
}
private final long expirationTime = 1000L * 60 * 60 * 24 * 30; // 1개월
private final HashIdUtil hashIdUtil;

@Value("${jwt.secret}")
private String secretKey;

private SecretKey getSigningKey() {
byte[] keyBytes = Decoders.BASE64.decode(secretKey);
return Keys.hmacShaKeyFor(keyBytes);
}

// JWT 토큰 생성
public String createToken(User user) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + expirationTime); // 만료 시간 설정

// userId Hash encode
String userId = hashIdUtil.encode(Long.parseLong(user.getUserId()));

return Jwts.builder()
.setSubject(userId)
.setIssuedAt(now) // 발급 시간 설정
.expiration(expiryDate) // 만료 시간 설정
.signWith(getSigningKey()) // SecretKey 적용
.compact(); // 토큰 생성
}

// JWT 토큰에서 사용자 정보 추출
public String extractUserId(String token) {
Claims claims = Jwts.parser()
.verifyWith(getSigningKey())
.build()
.parseSignedClaims(token)
.getPayload();

String jwtUserId = claims.getSubject();
if (jwtUserId == null || jwtUserId.isEmpty()) {
throw new BusinessException(CustomErrorCode.INVALID_TOKEN);
}

try {
return String.valueOf(hashIdUtil.decode(jwtUserId));
} catch (Exception e) {
log.error("Failed to decode JWT userId: {}", jwtUserId, e);
throw new BusinessException(CustomErrorCode.INVALID_TOKEN);
}
}

// JWT 토큰 유효성 검사
public boolean isTokenExpired(String token) {
Date expiration = Jwts.parser()
.verifyWith(getSigningKey())
.build()
.parseSignedClaims(token)
.getPayload()
.getExpiration();
return expiration.before(new Date());
}

// JWT 토큰의 유효성 검사
public boolean isTokenValid(String token) {
try {
Jwts.parser()
.verifyWith(getSigningKey())
.build()
.parseSignedClaims(token);
return !isTokenExpired(token);
} catch (ExpiredJwtException e) {
log.warn("Token expired: {}", e.getMessage());
return false;
} catch (MalformedJwtException e) {
log.warn("Invalid JWT token format: {}", e.getMessage());
return false;
} catch (SignatureException e) {
log.error("Invalid JWT signature: {}", e.getMessage());
return false;
} catch (Exception e) {
log.error("Unexpected token validation error: {}", e.getMessage());
return false;
}
}
}
Loading