diff --git a/src/main/java/com/back/web7_9_codecrete_be/domain/chats/controller/ChatMessageController.java b/src/main/java/com/back/web7_9_codecrete_be/domain/chats/controller/ChatMessageController.java index 8929b55a..5cbf33f7 100644 --- a/src/main/java/com/back/web7_9_codecrete_be/domain/chats/controller/ChatMessageController.java +++ b/src/main/java/com/back/web7_9_codecrete_be/domain/chats/controller/ChatMessageController.java @@ -5,7 +5,7 @@ import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.stereotype.Controller; -import com.back.web7_9_codecrete_be.domain.chats.dto.ChatMessageRequest; +import com.back.web7_9_codecrete_be.domain.chats.dto.request.ChatMessageRequest; import com.back.web7_9_codecrete_be.domain.chats.service.ChatMessageService; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/back/web7_9_codecrete_be/domain/chats/controller/ChatMessageReadController.java b/src/main/java/com/back/web7_9_codecrete_be/domain/chats/controller/ChatMessageReadController.java new file mode 100644 index 00000000..dd7539ad --- /dev/null +++ b/src/main/java/com/back/web7_9_codecrete_be/domain/chats/controller/ChatMessageReadController.java @@ -0,0 +1,67 @@ +package com.back.web7_9_codecrete_be.domain.chats.controller; + +import java.util.List; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import com.back.web7_9_codecrete_be.domain.chats.dto.response.ChatReadResponse; +import com.back.web7_9_codecrete_be.domain.chats.service.ChatMessageReadService; +import com.back.web7_9_codecrete_be.global.rsData.RsData; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; + +@Tag(name = "Chat", description = "채팅 메시지 조회 API") +@RestController +@RequestMapping("/api/v1/chats") +@RequiredArgsConstructor +public class ChatMessageReadController { + + private final ChatMessageReadService chatMessageReadService; + + @Operation( + summary = "채팅 메시지 조회 (cursor 기반 무한 스크롤)", + description = """ + 채팅 메시지를 cursor(before) 기준으로 조회합니다. + + - before 미전달 시: 최신 메시지를 조회합니다. + - before 전달 시: 해당 cursor 이전의 메시지를 조회합니다. + """ + ) + @GetMapping("/{concertId}/messages") + public RsData> getMessages( + @Parameter( + description = "공연 ID", + example = "1", + required = true + ) + @PathVariable Long concertId, + + @Parameter( + description = """ + 조회 기준 cursor (Redis Stream ID). + 해당 값이 주어지면, 그 이전의 채팅 메시지를 조회합니다. + """, + example = "1734940012345-0", + required = false + ) + @RequestParam(required = false) String before, + + @Parameter( + description = "한 번에 조회할 메시지 개수 (기본값: 20)", + example = "20", + required = false + ) + @RequestParam(required = false) Integer size + ) { + return RsData.success( + chatMessageReadService.getMessages(concertId, before, size) + ); + } +} diff --git a/src/main/java/com/back/web7_9_codecrete_be/domain/chats/controller/ChatRoomController.java b/src/main/java/com/back/web7_9_codecrete_be/domain/chats/controller/ChatRoomController.java index b9aa1fca..05527821 100644 --- a/src/main/java/com/back/web7_9_codecrete_be/domain/chats/controller/ChatRoomController.java +++ b/src/main/java/com/back/web7_9_codecrete_be/domain/chats/controller/ChatRoomController.java @@ -14,7 +14,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; -@Tag(name = "Chat Room", description = "공연 채팅방 입장 및 퇴장 API") +@Tag(name = "Chat", description = "공연 채팅방 입장 API") @RestController @RequestMapping("/api/v1/chat-room") @RequiredArgsConstructor @@ -34,8 +34,13 @@ public class ChatRoomController { Redis Stream 기반 과거 채팅과 실시간 채팅을 수신합니다. """ ) @ApiResponse( responseCode = "200", description = "채팅방 입장에 성공하였습니다." ) - @ApiResponse( responseCode = "403", description = "로그인이 필요합니다." ) - @ApiResponse( responseCode = "403", description = "채팅 가능한 기간이 아닙니다." ) + @ApiResponse( + responseCode = "403", + description = """ + - 로그인이 필요합니다. + - 채팅 가능한 기간이 아닙니다. + """ + ) @ApiResponse( responseCode = "404", description = "콘서트가 존재하지 않습니다." ) @PostMapping("/concert/{concertId}/join") public RsData joinChatRoom(@PathVariable Long concertId) { diff --git a/src/main/java/com/back/web7_9_codecrete_be/domain/chats/controller/ChatStompDocsController.java b/src/main/java/com/back/web7_9_codecrete_be/domain/chats/controller/ChatStompDocsController.java index ed625105..01f7c1b1 100644 --- a/src/main/java/com/back/web7_9_codecrete_be/domain/chats/controller/ChatStompDocsController.java +++ b/src/main/java/com/back/web7_9_codecrete_be/domain/chats/controller/ChatStompDocsController.java @@ -4,8 +4,8 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import com.back.web7_9_codecrete_be.domain.chats.dto.ChatMessageRequest; -import com.back.web7_9_codecrete_be.domain.chats.dto.ChatMessageResponse; +import com.back.web7_9_codecrete_be.domain.chats.dto.request.ChatMessageRequest; +import com.back.web7_9_codecrete_be.domain.chats.dto.response.ChatMessageResponse; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; @@ -27,7 +27,7 @@ public class ChatStompDocsController { ``` ws://localhost:8080/ws-chat or - wss://www.naeconcertbutakhae.shop/ws-chat + wss://api.naeconcertbutakhae.shop/ws-chat ``` #### 2️⃣ SEND Destination diff --git a/src/main/java/com/back/web7_9_codecrete_be/domain/chats/dto/ChatMessageRequest.java b/src/main/java/com/back/web7_9_codecrete_be/domain/chats/dto/request/ChatMessageRequest.java similarity index 87% rename from src/main/java/com/back/web7_9_codecrete_be/domain/chats/dto/ChatMessageRequest.java rename to src/main/java/com/back/web7_9_codecrete_be/domain/chats/dto/request/ChatMessageRequest.java index 06bfc514..1954734e 100644 --- a/src/main/java/com/back/web7_9_codecrete_be/domain/chats/dto/ChatMessageRequest.java +++ b/src/main/java/com/back/web7_9_codecrete_be/domain/chats/dto/request/ChatMessageRequest.java @@ -1,4 +1,4 @@ -package com.back.web7_9_codecrete_be.domain.chats.dto; +package com.back.web7_9_codecrete_be.domain.chats.dto.request; import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; diff --git a/src/main/java/com/back/web7_9_codecrete_be/domain/chats/dto/ChatMessageResponse.java b/src/main/java/com/back/web7_9_codecrete_be/domain/chats/dto/response/ChatMessageResponse.java similarity index 92% rename from src/main/java/com/back/web7_9_codecrete_be/domain/chats/dto/ChatMessageResponse.java rename to src/main/java/com/back/web7_9_codecrete_be/domain/chats/dto/response/ChatMessageResponse.java index d7e7d8f4..7c06e591 100644 --- a/src/main/java/com/back/web7_9_codecrete_be/domain/chats/dto/ChatMessageResponse.java +++ b/src/main/java/com/back/web7_9_codecrete_be/domain/chats/dto/response/ChatMessageResponse.java @@ -1,4 +1,4 @@ -package com.back.web7_9_codecrete_be.domain.chats.dto; +package com.back.web7_9_codecrete_be.domain.chats.dto.response; import java.time.LocalDateTime; diff --git a/src/main/java/com/back/web7_9_codecrete_be/domain/chats/dto/response/ChatReadResponse.java b/src/main/java/com/back/web7_9_codecrete_be/domain/chats/dto/response/ChatReadResponse.java new file mode 100644 index 00000000..1eb9a529 --- /dev/null +++ b/src/main/java/com/back/web7_9_codecrete_be/domain/chats/dto/response/ChatReadResponse.java @@ -0,0 +1,35 @@ +package com.back.web7_9_codecrete_be.domain.chats.dto.response; + +import java.time.LocalDateTime; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Schema(description = "채팅 메시지 조회 응답") +public class ChatReadResponse { + + @Schema(description = "Redis Stream 메시지 ID (before로 전달하면, 해당 메시지 이전 메시지를 조회할 수 있습니다)", example = "1700000123456-0") + private String messageId; + + @Schema(description = "공연 ID", example = "1") + private Long concertId; + + @Schema(description = "발신자 ID", example = "2") + private Long senderId; + + @Schema(description = "발신자 닉네임", example = "테스트 유저") + private String senderName; + + @Schema(description = "메시지 내용", example = "안녕하세요") + private String content; + + @Schema(description = "전송 시각") + private LocalDateTime sentDate; +} diff --git a/src/main/java/com/back/web7_9_codecrete_be/domain/chats/dto/response/ChatUserCache.java b/src/main/java/com/back/web7_9_codecrete_be/domain/chats/dto/response/ChatUserCache.java new file mode 100644 index 00000000..772a78fe --- /dev/null +++ b/src/main/java/com/back/web7_9_codecrete_be/domain/chats/dto/response/ChatUserCache.java @@ -0,0 +1,14 @@ +package com.back.web7_9_codecrete_be.domain.chats.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class ChatUserCache { + + private Long userId; + private String nickname; +} diff --git a/src/main/java/com/back/web7_9_codecrete_be/domain/chats/repository/ChatStreamRepository.java b/src/main/java/com/back/web7_9_codecrete_be/domain/chats/repository/ChatStreamRepository.java new file mode 100644 index 00000000..b628f644 --- /dev/null +++ b/src/main/java/com/back/web7_9_codecrete_be/domain/chats/repository/ChatStreamRepository.java @@ -0,0 +1,128 @@ +package com.back.web7_9_codecrete_be.domain.chats.repository; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.springframework.data.domain.Range; +import org.springframework.data.redis.connection.Limit; +import org.springframework.data.redis.connection.stream.MapRecord; +import org.springframework.data.redis.connection.stream.StreamRecords; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Repository; + +import com.back.web7_9_codecrete_be.domain.chats.dto.response.ChatMessageResponse; +import com.back.web7_9_codecrete_be.domain.chats.dto.response.ChatReadResponse; + +import lombok.RequiredArgsConstructor; + +@Repository +@RequiredArgsConstructor +public class ChatStreamRepository { + + private final RedisTemplate redisTemplate; + + private String streamKey(Long concertId) { + return "chat:stream:" + concertId; + } + + /** 저장 */ + public void save(ChatMessageResponse message) { + String key = streamKey(message.getConcertId()); + + Map fields = new HashMap<>(); + fields.put("concertId", message.getConcertId().toString()); + fields.put("senderId", message.getSenderId().toString()); + fields.put("senderName", message.getSenderName()); + fields.put("content", message.getContent()); + fields.put("sentDate", message.getSentDate().toString()); + + redisTemplate.opsForStream().add( + StreamRecords.newRecord() + .in(key) + .ofMap(fields) + ); + } + + public boolean hasTtl(Long concertId) { + Long ttl = redisTemplate.getExpire(streamKey(concertId)); + return ttl != null && ttl >= 0; + } + + public void setTtl(Long concertId, Duration ttl) { + redisTemplate.expire(streamKey(concertId), ttl); + } + + /** 조회 */ + // 채팅방 진입시 가장 최신 메시지 size개 조회 + public List findLatest(Long concertId, int size) { + + List> records = + redisTemplate.opsForStream().reverseRange( + streamKey(concertId), + Range.unbounded(), + Limit.limit().count(size) + ); + + return toResponses(records); + } + + // cursor(before) 기반 과거 메시지 조회 + // beforeMessageId보다 이전에 있던 메시지들 중 최신 size개 조회 + public List findBefore( + Long concertId, + String beforeMessageId, + int size + ) { + String exclusiveBeforeId = exclusiveBefore(beforeMessageId); + + List> records = + redisTemplate.opsForStream().reverseRange( + streamKey(concertId), + Range.leftOpen("0-0", exclusiveBeforeId), + Limit.limit().count(size) + ); + + return toResponses(records); + } + + private List toResponses( + List> records + ) { + if (records == null || records.isEmpty()) { + return List.of(); + } + + return records.stream() + .map(record -> { + Map v = record.getValue(); + + return new ChatReadResponse( + record.getId().getValue(), + Long.valueOf(v.get("concertId").toString()), + Long.valueOf(v.get("senderId").toString()), + v.get("senderName").toString(), + v.get("content").toString(), + LocalDateTime.parse(v.get("sentDate").toString()) + ); + }) + .filter(Objects::nonNull) + .toList(); + } + + private String exclusiveBefore(String messageId) { + String[] parts = messageId.split("-"); + long time = Long.parseLong(parts[0]); // 밀리초 타임스탬프 + long seq = Long.parseLong(parts[1]); // 같은 밀리초 내 순번 + + if (seq > 0) { + return time + "-" + (seq - 1); + } + + // seq == 0 인 경우, timestamp를 1 감소 + return (time - 1) + "-0"; + } +} diff --git a/src/main/java/com/back/web7_9_codecrete_be/domain/chats/service/ChatMessageReadService.java b/src/main/java/com/back/web7_9_codecrete_be/domain/chats/service/ChatMessageReadService.java new file mode 100644 index 00000000..9742f81a --- /dev/null +++ b/src/main/java/com/back/web7_9_codecrete_be/domain/chats/service/ChatMessageReadService.java @@ -0,0 +1,39 @@ +package com.back.web7_9_codecrete_be.domain.chats.service; + +import java.util.List; + +import org.springframework.stereotype.Service; + +import com.back.web7_9_codecrete_be.domain.chats.dto.response.ChatReadResponse; +import com.back.web7_9_codecrete_be.domain.chats.repository.ChatStreamRepository; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class ChatMessageReadService { + + private static final int DEFAULT_SIZE = 20; + private static final int MAX_SIZE = 50; + + private final ChatStreamRepository chatStreamRepository; + + public List getMessages( + Long concertId, + String before, + Integer size + ) { + int limit = Math.min( + size != null ? size : DEFAULT_SIZE, + MAX_SIZE + ); + + // 최초 진입 + if (before == null || before.isBlank()) { + return chatStreamRepository.findLatest(concertId, limit); + } + + // 이전 메시지 조회 (무한 스크롤) + return chatStreamRepository.findBefore(concertId, before, limit); + } +} diff --git a/src/main/java/com/back/web7_9_codecrete_be/domain/chats/service/ChatMessageService.java b/src/main/java/com/back/web7_9_codecrete_be/domain/chats/service/ChatMessageService.java index 77af3a29..97cd56f1 100644 --- a/src/main/java/com/back/web7_9_codecrete_be/domain/chats/service/ChatMessageService.java +++ b/src/main/java/com/back/web7_9_codecrete_be/domain/chats/service/ChatMessageService.java @@ -6,12 +6,10 @@ import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Service; -import com.back.web7_9_codecrete_be.domain.chats.dto.ChatMessageRequest; -import com.back.web7_9_codecrete_be.domain.chats.dto.ChatMessageResponse; -import com.back.web7_9_codecrete_be.domain.users.entity.User; -import com.back.web7_9_codecrete_be.domain.users.repository.UserRepository; -import com.back.web7_9_codecrete_be.global.error.code.AuthErrorCode; -import com.back.web7_9_codecrete_be.global.error.exception.BusinessException; +import com.back.web7_9_codecrete_be.domain.chats.dto.request.ChatMessageRequest; +import com.back.web7_9_codecrete_be.domain.chats.dto.response.ChatMessageResponse; +import com.back.web7_9_codecrete_be.domain.chats.dto.response.ChatUserCache; +import com.back.web7_9_codecrete_be.domain.chats.repository.ChatStreamRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -21,32 +19,31 @@ @RequiredArgsConstructor public class ChatMessageService { - private final UserRepository userRepository; private final SimpMessagingTemplate messagingTemplate; + private final ChatStreamRepository chatStreamRepository; + private final ChatUserCacheService chatUserCacheService; - public void sendMessage(ChatMessageRequest message, Principal principal) { + public void sendMessage(ChatMessageRequest request, Principal principal) { String email = principal.getName(); - // TODO: 캐싱처리 - User user = userRepository.findByEmail(email) - .orElseThrow(() -> new BusinessException(AuthErrorCode.USER_NOT_FOUND)); - - Long senderId = user.getId(); - String senderName = user.getNickname(); + ChatUserCache chatUser = chatUserCacheService.getChatUser(email); ChatMessageResponse response = new ChatMessageResponse( - message.getConcertId(), - senderId, - senderName, - message.getContent(), + request.getConcertId(), + chatUser.getUserId(), + chatUser.getNickname(), + request.getContent(), LocalDateTime.now() ); - log.info("[SEND MESSAGE] From User ID: {}, Content: {}", senderId, message.getContent()); + log.info("[SEND MESSAGE] From User ID: {}, Content: {}", chatUser.getUserId(), request.getContent()); + + chatStreamRepository.save(response); + // WebSocket 브로드캐스트 messagingTemplate.convertAndSend( - "/topic/chat/" + message.getConcertId(), + "/topic/chat/" + request.getConcertId(), response ); } diff --git a/src/main/java/com/back/web7_9_codecrete_be/domain/chats/service/ChatPolicyService.java b/src/main/java/com/back/web7_9_codecrete_be/domain/chats/service/ChatPolicyService.java index fee012ca..ed0a14ea 100644 --- a/src/main/java/com/back/web7_9_codecrete_be/domain/chats/service/ChatPolicyService.java +++ b/src/main/java/com/back/web7_9_codecrete_be/domain/chats/service/ChatPolicyService.java @@ -79,4 +79,28 @@ private Duration calculateTtl(LocalDateTime now, LocalDateTime policyStart, Loca // 정책 기간 종료 후 return Duration.ofMinutes(10); } + + public Duration calculateChatRemainingTtl(Long concertId) { + + Concert concert = concertRepository.getReferenceById(concertId); + + LocalDateTime ticketTime = concert.getTicketTime(); + if (ticketTime == null) { + return Duration.ofMinutes(10); + } + + LocalDateTime now = LocalDateTime.now(); + + LocalDateTime policyEnd = ticketTime.toLocalDate() + .plusDays(3) + .atTime(LocalTime.MAX); + + if (now.isAfter(policyEnd)) { + return Duration.ofMinutes(10); + } + + return Duration.between(now, policyEnd); + } + + } diff --git a/src/main/java/com/back/web7_9_codecrete_be/domain/chats/service/ChatRoomService.java b/src/main/java/com/back/web7_9_codecrete_be/domain/chats/service/ChatRoomService.java index bece20ca..40267ff5 100644 --- a/src/main/java/com/back/web7_9_codecrete_be/domain/chats/service/ChatRoomService.java +++ b/src/main/java/com/back/web7_9_codecrete_be/domain/chats/service/ChatRoomService.java @@ -1,11 +1,14 @@ package com.back.web7_9_codecrete_be.domain.chats.service; +import java.time.Duration; + import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.back.web7_9_codecrete_be.domain.chats.entity.ChatRoom; import com.back.web7_9_codecrete_be.domain.chats.repository.ChatRoomRepository; +import com.back.web7_9_codecrete_be.domain.chats.repository.ChatStreamRepository; import com.back.web7_9_codecrete_be.domain.concerts.entity.Concert; import com.back.web7_9_codecrete_be.domain.concerts.repository.ConcertRepository; import com.back.web7_9_codecrete_be.global.error.code.ChatErrorCode; @@ -21,6 +24,7 @@ public class ChatRoomService { private final ChatRoomRepository chatRoomRepository; private final ChatPolicyService chatPolicyService; private final ConcertRepository concertRepository; + private final ChatStreamRepository chatStreamRepository; /** * 채팅방 입장 @@ -38,7 +42,15 @@ public void joinChatRoom(Long concertId) { chatRoomRepository.findByConcert_ConcertId(concertId) .orElseGet(() -> createChatRoomSafely(concertId)); - // TODO: 입장 처리 (WebSocket) + // Redis Stream TTL 설정 + if (!chatStreamRepository.hasTtl(concertId)) { + Duration ttl = chatPolicyService.calculateChatRemainingTtl(concertId); + + if (!ttl.isZero() && !ttl.isNegative()) { + chatStreamRepository.setTtl(concertId, ttl); + } + } + } /** diff --git a/src/main/java/com/back/web7_9_codecrete_be/domain/chats/service/ChatUserCacheService.java b/src/main/java/com/back/web7_9_codecrete_be/domain/chats/service/ChatUserCacheService.java new file mode 100644 index 00000000..3ed517c4 --- /dev/null +++ b/src/main/java/com/back/web7_9_codecrete_be/domain/chats/service/ChatUserCacheService.java @@ -0,0 +1,55 @@ +package com.back.web7_9_codecrete_be.domain.chats.service; + +import java.time.Duration; + +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +import com.back.web7_9_codecrete_be.domain.chats.dto.response.ChatUserCache; +import com.back.web7_9_codecrete_be.domain.users.entity.User; +import com.back.web7_9_codecrete_be.domain.users.repository.UserRepository; +import com.back.web7_9_codecrete_be.global.error.code.AuthErrorCode; +import com.back.web7_9_codecrete_be.global.error.exception.BusinessException; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class ChatUserCacheService { + + private static final Duration ttl = Duration.ofMinutes(120); + + private final RedisTemplate redisTemplate; + private final UserRepository userRepository; + + private String cacheKey(String email) { + return "chat:user:" + email; + } + + public ChatUserCache getChatUser(String email) { + String key = cacheKey(email); + + ChatUserCache cached = + (ChatUserCache)redisTemplate.opsForValue().get(key); + + if (cached != null) { + return cached; + } + + User user = userRepository.findByEmail(email) + .orElseThrow(() -> new BusinessException(AuthErrorCode.USER_NOT_FOUND)); + + ChatUserCache cache = new ChatUserCache( + user.getId(), + user.getNickname() + ); + + redisTemplate.opsForValue().set(key, cache, ttl); + + return cache; + } + + public void removeChatUserCache(String email) { + redisTemplate.delete(cacheKey(email)); + } +} diff --git a/src/main/java/com/back/web7_9_codecrete_be/domain/users/service/UserService.java b/src/main/java/com/back/web7_9_codecrete_be/domain/users/service/UserService.java index 4a35ff55..8cebb3e6 100644 --- a/src/main/java/com/back/web7_9_codecrete_be/domain/users/service/UserService.java +++ b/src/main/java/com/back/web7_9_codecrete_be/domain/users/service/UserService.java @@ -1,8 +1,17 @@ package com.back.web7_9_codecrete_be.domain.users.service; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.UUID; + +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; import com.back.web7_9_codecrete_be.domain.auth.dto.request.SignupRequest; import com.back.web7_9_codecrete_be.domain.auth.service.TokenService; +import com.back.web7_9_codecrete_be.domain.chats.service.ChatUserCacheService; import com.back.web7_9_codecrete_be.domain.email.service.EmailService; import com.back.web7_9_codecrete_be.domain.users.dto.request.UserSettingUpdateRequest; import com.back.web7_9_codecrete_be.domain.users.dto.request.UserUpdateNicknameRequest; @@ -18,16 +27,9 @@ import com.back.web7_9_codecrete_be.global.error.exception.BusinessException; import com.back.web7_9_codecrete_be.global.storage.FileStorageService; import com.back.web7_9_codecrete_be.global.storage.ImageFileValidator; + import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.multipart.MultipartFile; - -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.UUID; @Slf4j @Service @@ -40,6 +42,7 @@ public class UserService { private final TokenService tokenService; private final UserRestoreTokenRedisRepository userRestoreTokenRedisRepository; private final EmailService emailService; + private final ChatUserCacheService chatUserCacheService; private final ImageFileValidator imageFileValidator; @@ -110,6 +113,9 @@ public UserResponse updateNickname(User user, UserUpdateNicknameRequest req) { user.updateNickname(req.getNickname()); userRepository.save(user); + + chatUserCacheService.removeChatUserCache(user.getEmail()); + return UserResponse.from(user); } diff --git a/src/main/java/com/back/web7_9_codecrete_be/global/security/SecurityConfig.java b/src/main/java/com/back/web7_9_codecrete_be/global/security/SecurityConfig.java index 2a0c3b17..25d2170d 100644 --- a/src/main/java/com/back/web7_9_codecrete_be/global/security/SecurityConfig.java +++ b/src/main/java/com/back/web7_9_codecrete_be/global/security/SecurityConfig.java @@ -59,7 +59,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti "/api/v1/location/**", //location 정보 조회 도메인(임시) "/api/v1/concerts/**", // concert 정보 조회 도메인 "/api/v1/artists/**", // artist 정보 저장 도메인(임시) - "/api/v1/users/**" + "/api/v1/users/**", + "/api/v1/chats/**" ).permitAll() // ADMIN 전용