Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
bce7dbc
feat : ๋‹‰๋„ค์ž„ ์ค‘๋ณต ์ฒดํฌ (nickname unique), ์ธ์ฆ์ฝ”๋“œ ๊ฒ€์‚ฌ ์˜ˆ์™ธ์ฒ˜๋ฆฌ ์ถ”๊ฐ€
yyytir777 Dec 8, 2025
3573613
fix : user_id IDENTITY strategy & dev redis host ์ด๋ฆ„๋ณ€๊ฒฝ (localhost -> rโ€ฆ
yyytir777 Dec 8, 2025
08cfb63
test์ฝ”๋“œ ์ƒ์„ฑ & swagger url ์‚ญ์ œ & ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์ค‘๋ณต ์‚ญ์ œ
yyytir777 Dec 8, 2025
ac96fa0
fix : ์—”๋“œํฌ์ธํŠธ ์ถ”๊ฐ€
yyytir777 Dec 9, 2025
43e75ed
Merge pull request #27 from tinybite-2025/feature/13-user
yyytir777 Dec 9, 2025
a70941b
Feature/26 notification (#29)
marshmallowing Dec 11, 2025
cbbf597
feat : google login ๊ตฌํ˜„ ์™„๋ฃŒ
yyytir777 Dec 11, 2025
d03fd30
fix : main push ์‹œ์—๋งŒ workflow trigger
yyytir777 Dec 11, 2025
00cc289
Merge branch 'main' of https://github.com/tinybite-2025/tinybite-servโ€ฆ
yyytir777 Dec 11, 2025
729ae0a
Feature/#28 google apple login
yyytir777 Dec 16, 2025
b5c29db
Merge branch 'main' into develop
yyytir777 Dec 16, 2025
2e5da55
Merge branch 'develop' of https://github.com/tinybite-2025/tinybite-sโ€ฆ
yyytir777 Dec 18, 2025
1fbd896
merge main into develop : main์˜ ํ•ซํ”ฝ์Šค ๋ณ€๊ฒฝ์‚ฌํ•ญ develop์— ๋ฐ˜์˜
yyytir777 Dec 18, 2025
dd9771a
workflow ์ค„๋ฐ”๊ฟˆ ์—๋Ÿฌ ์ˆ˜์ •
yyytir777 Dec 18, 2025
38de611
hotifx : ์—๋Ÿฌ ํ•ธ๋“ค๋ง ์ˆ˜์ • ๋ฐ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ์‚ญ์ œ (๋ฆฌ์†Œ์Šค ๋„ˆ๋ฌด ๋งŽ์ด ๋จน์Œ)
yyytir777 Dec 19, 2025
91b8cba
main์˜ ํ•ซํ”ฝ์Šค develop์— ๋ฐ˜์˜
yyytir777 Dec 22, 2025
fbc5e90
์ˆ˜์ •์‚ฌํ•ญ ๋ฐ˜์˜ (API ์ธ์ฆ ๊ด€๋ จ, db schema, ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๋“ฑ..)
yyytir777 Dec 23, 2025
7b9fb7b
main๋ธŒ๋žœ์น˜ ํ•ซํ”ฝ์Šค ๋ฐ˜์˜
yyytir777 Dec 24, 2025
bbb080f
Feature/35 term (#38)
yyytir777 Dec 24, 2025
4c352aa
fix : docker compose ๋ช…๋ น์–ด ์ˆ˜์ •
yyytir777 Dec 24, 2025
27dfcbb
Feature : ํŒŒํ‹ฐ ๊ธฐ๋Šฅ (#42)
milowon Dec 27, 2025
0de3a43
Merge branch 'main' into develop
milowon Dec 31, 2025
2f27dee
hotfix : url parser ๊ฒฝ๋กœ ์ œ๊ฑฐ
milowon Dec 31, 2025
6eb0d8d
Merge branch 'main' of https://github.com/tinybite-2025/tinybite-servโ€ฆ
milowon Jan 1, 2026
3f69bc1
Merge branch 'main' of https://github.com/tinybite-2025/tinybite-servโ€ฆ
milowon Jan 2, 2026
45191a9
hotfix : ํŒŒํ‹ฐ ๊ฑฐ๋ฆฌ ๊ณ„์‚ฐ ๋กœ์ง ์ž„์‹œ ์ฃผ์„ ์ฒ˜๋ฆฌ
milowon Jan 2, 2026
3a9a6e6
Merge branch 'main' of https://github.com/tinybite-2025/tinybite-servโ€ฆ
milowon Jan 2, 2026
b2ca1f4
hotfix : ํŒŒํ‹ฐ ์ˆ˜์ •, ์‚ญ์ œ controller ์ถ”๊ฐ€
milowon Jan 2, 2026
38ab16c
hotfix : ์„ ํƒ ๊ฐ’๋“ค์ด ์กด์žฌํ• ๋•Œ๋งŒ ๋„ฃ๋„๋ก ์ˆ˜์ •
milowon Jan 2, 2026
037d2e4
hotfix : ์œ„๋„, ๊ฒฝ๋„ ๋กœ์ง ์‚ญ์ œ
milowon Jan 2, 2026
9d305f8
Feat : ๋งˆ์ดํŽ˜์ด์ง€ ์ฐธ์—ฌ์ค‘์ธ ํŒŒํ‹ฐ ์กฐํšŒ (#50)
milowon Jan 2, 2026
1cee657
hotfix : user service์— transactional ์–ด๋…ธํ…Œ์ด์…˜ ์ถ”๊ฐ€
milowon Jan 2, 2026
5213214
Merge branch 'main' of https://github.com/tinybite-2025/tinybite-servโ€ฆ
milowon Jan 2, 2026
ed3a399
hotfix : ์ฐธ์—ฌ์ค‘ ํŒŒํ‹ฐ ์กฐํšŒ ๋ฐ˜ํ™˜ ํ˜•์‹ ํ†ต์ผ
milowon Jan 2, 2026
374f720
hotfix : ํŒŒํ‹ฐ ์ƒ์„ฑ, ์กฐํšŒ ์‹œ, ๊ฑฐ๋ฆฌ ๊ณ„์‚ฐ ๋กœ์ง ๋ฐ˜์˜
milowon Jan 2, 2026
4ddfa39
Hotfix: ์œ ์ € ์ขŒํ‘œ ์ž…๋ ฅ requestParam ํ˜•์‹์œผ๋กœ ๋ณ€๊ฒฝ
milowon Jan 2, 2026
42bc4d4
Merge branch 'main' into develop
milowon Jan 2, 2026
9ee078a
Merge branch 'main' into develop
milowon Jan 2, 2026
3be9d38
hotfix : ๋ˆ„๋ฝ๋œ swagger ๋ฌธ์„œ ์ˆ˜์ •์‚ฌํ•ญ ๋ฐ˜์˜
milowon Jan 2, 2026
89bbef3
Merge branch 'main' into develop
milowon Jan 2, 2026
b281a29
feat : ํšŒ์› ํƒˆํ‡ด ๋ฐ ์žฌ๊ฐ€์ž… ๋ฐฉ์ง€, ๊ฒ€์ฆ (#65)
milowon Jan 2, 2026
35a62b7
fix : ํŒŒํ‹ฐ ์ˆ˜์ • ๋ฒ„๊ทธ ํ”ฝ์Šค (#67)
milowon Jan 2, 2026
a4f3582
hotfix : ํƒˆํ‡ด ์œ ์ € ๋งˆ์Šคํ‚น ๋กœ์ง ๋ณ€๊ฒฝ
milowon Jan 3, 2026
9f05a6a
Merge branch 'main' of https://github.com/tinybite-2025/tinybite-servโ€ฆ
milowon Jan 3, 2026
d802e15
feat : ๋งˆ์ดํŽ˜์ด์ง€์—์„œ ์ฐธ์—ฌ์ค‘,ํ˜ธ์ŠคํŠธ์ธ ํŒŒํ‹ฐ ๊ตฌ๋ถ„ํ•ด์„œ ์กฐํšŒ (#71)
milowon Jan 4, 2026
8715584
Feature/73 search party (#74)
yyytir777 Jan 4, 2026
78349d6
fix : ์Šค์›จ๊ฑฐ description ์ถ”๊ฐ€
yyytir777 Jan 4, 2026
ba3d9d8
Merge branch 'main' into develop
yyytir777 Jan 4, 2026
c15e45b
Feature/73 search party (#76)
yyytir777 Jan 4, 2026
92bef29
Merge branch 'main' into develop
yyytir777 Jan 4, 2026
bd136de
Fix : ํ˜ธ์ŠคํŠธ๋งŒ ์žˆ์„๋•Œ๋Š” ํŒŒํ‹ฐ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ณ€๊ฒฝ (#78)
milowon Jan 4, 2026
5d3e13d
fix : ํŒŒํ‹ฐ ์‚ญ์ œ์‹œ ํ˜ธ์ŠคํŠธ๋Š” ํ˜„์žฌ์ธ์›์—์„œ ์ œ์™ธํ•˜๋„๋ก ์ˆ˜์ • (#80)
milowon Jan 4, 2026
1e5b600
hotfix : jpa ๋„ค์ด๋ฐ ๋ฐ ์ฟผ๋ฆฌ ์ˆ˜์ •
yyytir777 Jan 4, 2026
ba4ed25
hotfix : jpa ๋„ค์ด๋ฐ ๋ฐ ์ฟผ๋ฆฌ ์ˆ˜์ •
yyytir777 Jan 4, 2026
c2d9465
Merge branch 'main' into develop
yyytir777 Jan 4, 2026
0b783c7
feat : ํŒŒํ‹ฐ ์นดํ…Œ๊ณ ๋ฆฌ, ์ตœ์‹ ์ˆœ, ๊ฑฐ๋ฆฌ์ˆœ ์ •๋ ฌ (#83)
milowon Jan 4, 2026
4474664
Merge branch 'main' of https://github.com/tinybite-2025/tinybite-servโ€ฆ
milowon Jan 4, 2026
3353068
Feature/44 chat (#82)
yyytir777 Jan 6, 2026
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
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ dependencies {
// s3
implementation(platform("software.amazon.awssdk:bom:2.25.8"))
implementation 'software.amazon.awssdk:s3'

// websocket
implementation 'org.springframework.boot:spring-boot-starter-websocket'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package ita.tinybite.domain.chat.controller;

import ita.tinybite.domain.chat.dto.req.ChatMessageReqDto;
import ita.tinybite.domain.chat.dto.res.ChatMessageResDto;
import ita.tinybite.domain.chat.entity.ChatMessage;
import ita.tinybite.domain.chat.service.ChatService;
import ita.tinybite.global.response.APIResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;


import static ita.tinybite.global.response.APIResponse.*;


@Controller
@RequiredArgsConstructor
public class ChatController {

private final SimpMessagingTemplate simpMessagingTemplate;
private final ChatService chatService;

/**
* 1. ws://{server name}/publish/send ์œผ๋กœ ์š”์ฒญ <br>
* 2. message ์ €์žฅ <br>
* 3. ํ•ด๋‹น ์ฑ„ํŒ…๋ฐฉ์„ ๊ตฌ๋…์ค‘์ธ ์„ธ์…˜์—๊ฒŒ ํ•ด๋‹น ๋ฉ”์‹œ์ง€ ์ „๋‹ฌ <br>
* 4. ๋งŒ์•ฝ ํ•ด๋‹น ์ฑ„ํŒ…๋ฐฉ์„ ๊ตฌ๋…์ค‘์ด์ง€ ์•Š๋‹ค๋ฉด (์ฑ„ํŒ…๋ฐฉ์„ ๋‚˜๊ฐ„์ƒํƒœ), ์•Œ๋ฆผ์„ ๋ณด๋ƒ„ <br>
*/
@MessageMapping("/send")
public void sendMessage(ChatMessageReqDto req,
SimpMessageHeaderAccessor accessor) {
// message entity ์ƒ์„ฑ
ChatMessage message = ChatMessage.builder()
.chatRoomId(req.chatRoomId())
.senderId((Long) accessor.getSessionAttributes().get("userId"))
.senderName(req.nickname())
.content(req.content()).build();

// message ์ €์žฅ
ChatMessage saved = chatService.saveMessage(message);

// subscribe ํ•œ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ „์†ก
simpMessagingTemplate.convertAndSend("/subscribe/chat/room/" + saved.getChatRoomId(), ChatMessageResDto.of(saved));

// subscribeํ•˜์ง€ ์•Š์•˜์œผ๋‚˜, ์ฑ„ํŒ…๋ฐฉ์— ์กด์žฌํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์—๊ฒŒ ์ „์†ก
chatService.sendNotification(saved, req.chatRoomId());
}

@ResponseBody
@GetMapping("/api/v1/chat/{chatRoomId}")
public APIResponse<?> getChatMessages(@PathVariable Long chatRoomId,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size) {
return success(chatService.getChatMessage(chatRoomId, page, size));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ita.tinybite.domain.chat.dto.req;

import lombok.Builder;

@Builder
public record ChatMessageReqDto(
Long chatRoomId,
Long userId,
String nickname,
String content
) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package ita.tinybite.domain.chat.dto.res;

import ita.tinybite.domain.chat.entity.ChatMessage;
import lombok.Builder;

@Builder
public record ChatMessageResDto(
Long userId,
String nickname,
String content
) {
public static ChatMessageResDto of(ChatMessage chatMessage) {
return ChatMessageResDto.builder()
.userId(chatMessage.getSenderId())
.nickname(chatMessage.getSenderName())
.content(chatMessage.getContent())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ita.tinybite.domain.chat.dto.res;

import lombok.Builder;

import java.util.List;

@Builder
public record ChatMessageSliceResDto(
List<ChatMessageResDto> messages,
Boolean hasNext
) {

}
31 changes: 31 additions & 0 deletions src/main/java/ita/tinybite/domain/chat/entity/ChatMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package ita.tinybite.domain.chat.entity;

import ita.tinybite.global.entity.BaseEntity;
import jakarta.persistence.*;
import lombok.*;

@Entity
@Table(name = "chat_messages")
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ChatMessage extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

// ์ฑ„ํŒ…๋ฃธ ์•„์ด๋””
private Long chatRoomId;

// ์ „์†ก์ž ์•„์ด๋””
private Long senderId;

// ์ „์†ก์ž ์ด๋ฆ„ (nickname)
private String senderName;

// ๋ฉ”์‹œ์ง€ ๋‚ด์šฉ
private String content;
}

11 changes: 8 additions & 3 deletions src/main/java/ita/tinybite/domain/chat/entity/ChatRoom.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import ita.tinybite.domain.chat.enums.ChatRoomType;
import ita.tinybite.domain.party.entity.Party;
import ita.tinybite.domain.user.entity.User;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.CreationTimestamp;
Expand Down Expand Up @@ -43,20 +44,24 @@ public class ChatRoom {

@OneToMany(mappedBy = "chatRoom", cascade = CascadeType.ALL, orphanRemoval = true)
@Builder.Default
private List<ChatRoomMember> members = new ArrayList<>();
private List<ChatRoomMember> participants = new ArrayList<>();

// ========== ๋น„์ฆˆ๋‹ˆ์Šค ๋ฉ”์„œ๋“œ ==========

public void addParticipants(ChatRoomMember... participants) {
this.participants.addAll(List.of(participants));
}

/**
* ๋ฉค๋ฒ„ ์ถ”๊ฐ€
*/
public void addMember(ita.tinybite.domain.user.entity.User user) {
public void addMember(User user) {
ChatRoomMember member = ChatRoomMember.builder()
.chatRoom(this)
.user(user)
.isActive(true)
.build();
members.add(member);
participants.add(member);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,6 @@ public class ChatRoomMember {
@JoinColumn(name = "user_id", nullable = false)
private User user;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "party_id")
private Party party;

@Column(nullable = false)
@Builder.Default
private Boolean isActive = true;
Expand All @@ -42,6 +38,8 @@ public class ChatRoomMember {

private LocalDateTime leftAt;

private LocalDateTime lastReadAt;

/**
* ํ‡ด์žฅ
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ita.tinybite.domain.chat.repository;

import ita.tinybite.domain.chat.entity.ChatMessage;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;


@Repository
public interface ChatMessageRepository extends JpaRepository<ChatMessage, Long> {

Slice<ChatMessage> findByChatRoomId(Long roomId, Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package ita.tinybite.domain.chat.service;

import lombok.RequiredArgsConstructor;
import org.springframework.context.event.EventListener;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.messaging.SessionDisconnectEvent;
import org.springframework.web.socket.messaging.SessionSubscribeEvent;
import org.springframework.web.socket.messaging.SessionUnsubscribeEvent;

/**
* subscribe, unsubscribe, disconnect ์ด๋ฒคํŠธ ๋ฐœ์ƒ ์‹œ registry์— ์ž๋™์œผ๋กœ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋Š” ์Šคํ”„๋ง ๋นˆ ํด๋ž˜์Šค
*/
@Component
@RequiredArgsConstructor
public class ChatEventListener {

private final ChatSubscribeRegistry registry;

/**
* ๊ตฌ๋… ์‹œ, ํ˜ธ์ถœ๋˜๋Š” ๋ฉ”์„œ๋“œ <br>
* 1. StompHeader์—์„œ userId, destination, sessionId, subscriptionId ๋ฐ›์•„์˜ด (destination ์˜ˆ : /subscribe/chat/room/{chatRoomId}) <br>
* 2. destination์—์„œ roomId resolve <br>
* 3. ์ดํ›„ ์„ธ์…˜์ •๋ณด๋ฅผ registry์— ๋“ฑ๋ก
*/
@EventListener
public void onSubscribe(SessionSubscribeEvent event) {
StompHeaderAccessor acc = StompHeaderAccessor.wrap(event.getMessage());

Long userId = (Long) acc.getSessionAttributes().get("userId");
String destination = acc.getDestination();
String sessionId = acc.getSessionId();
String subscriptionId = acc.getSubscriptionId();

if (userId == null || destination == null) return;

// /subscribe/chat/room/1
Long roomId = extractRoomId(destination);
registry.register(sessionId, subscriptionId, roomId, userId);
}

/**
* ๊ตฌ๋…์ทจ์†Œ์‹œ ํ˜ธ์ถœ๋˜๋Š” ๋ฉ”์„œ๋“œ <br>
* 1. StompHeader์—์„œ sessionId, subscriptionId resolve <br>
* 2. ํ•ด๋‹น ๊ตฌ๋…์ •๋ณด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์‚ฌ์šฉ์ž๋ฅผ registry์—์„œ ์‚ญ์ œ <br>
* 3. ๋งŒ์•ฝ ๋„คํŠธ์›Œํฌ ์—๋Ÿฌ ๋“ฑ์œผ๋กœ ์œ ์ € ์ •๋ณด๊ฐ€ ์—†์„ ์‹œ, ์œ ์ €์— ํ•ด๋‹นํ•˜๋Š” ๊ตฌ๋… ์ •๋ณด๋ฅผ ์‚ญ์ œ
*/
@EventListener
public void onUnsubscribe(SessionUnsubscribeEvent event) {
StompHeaderAccessor acc = StompHeaderAccessor.wrap(event.getMessage());

String sessionId = acc.getSessionId();
String subscriptionId = acc.getSubscriptionId();

if (sessionId != null && subscriptionId != null) {
registry.unregister(sessionId, subscriptionId);
}
}

@EventListener
public void onDisconnect(SessionDisconnectEvent event) {
StompHeaderAccessor acc = StompHeaderAccessor.wrap(event.getMessage());

String sessionId = acc.getSessionId();
if (sessionId != null) {
registry.unregisterSession(sessionId);
return;
}

Long userId = acc.getSessionAttributes() != null ? (Long) acc.getSessionAttributes().get("userId") : null;
if (userId != null) {
registry.removeUserEverywhere(userId);
}
}

private Long extractRoomId(String destination) {
// /subscribe/chat/room/{roomId}
try {
return Long.parseLong(destination.substring("/subscribe/chat/room/".length()));
} catch (NumberFormatException e) {
return null;
}
}
}
76 changes: 76 additions & 0 deletions src/main/java/ita/tinybite/domain/chat/service/ChatService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package ita.tinybite.domain.chat.service;

import ita.tinybite.domain.chat.dto.res.ChatMessageResDto;
import ita.tinybite.domain.chat.dto.res.ChatMessageSliceResDto;
import ita.tinybite.domain.chat.entity.ChatMessage;
import ita.tinybite.domain.chat.entity.ChatRoomMember;
import ita.tinybite.domain.chat.repository.ChatMessageRepository;
import ita.tinybite.domain.chat.repository.ChatRoomRepository;
import ita.tinybite.domain.notification.service.facade.NotificationFacade;
import ita.tinybite.domain.user.entity.User;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Service
@Transactional
@RequiredArgsConstructor
public class ChatService {

private final ChatMessageRepository chatMessageRepository;
private final ChatRoomRepository chatRoomRepository;
private final ChatSubscribeRegistry registry;
private final NotificationFacade notificationFacade;

public ChatMessage saveMessage(ChatMessage message) {
return chatMessageRepository.save(message);
}

/**
* ๊ตฌ๋…ํ•˜์ง€ ์•Š์€ ์ฑ„ํŒ…๋ฐฉ ์‚ฌ์šฉ์ž์—๊ฒŒ fcm์•Œ๋ฆผ์„ ์ „์†กํ•จ
*/
public void sendNotification(ChatMessage message, Long chatRoomId) {
// 1. ์ฑ„ํŒ…๋ฐฉ์˜ ๋ชจ๋“  ์ฐธ์—ฌ์ž ์กฐํšŒ
Set<ChatRoomMember> participants = new HashSet<>(chatRoomRepository.findById(chatRoomId).orElseThrow().getParticipants());

// 2. ์ด ์ค‘ ๊ตฌ๋…ํ•˜๊ณ ์žˆ๋Š” ์ฐธ์—ฌ์ž ์กฐํšŒ
Set<Long> subscriberIds = registry.getSubscribers(chatRoomId);

// 3. ๋ชจ๋“  ์ฐธ์—ฌ์ž์—์„œ ๊ตฌ๋…์ค‘์ธ ์ฐธ์—ฌ์ž & ๋ณธ์ธ ์ œ์™ธ -> ๋น„๊ตฌ๋…์ž
Set<User> unsubscribers = participants.stream()
.map(ChatRoomMember::getUser)
.filter(user -> !subscriberIds.contains(user.getUserId()))
.filter(user -> !user.getUserId().equals(message.getSenderId()))
.collect(Collectors.toSet());

// 4. ํ•ด๋‹น ๋น„๊ตฌ๋…์ž ์œ ์ €๋“ค์—๊ฒŒ ๋ฉ”์‹œ์ง€๊ฐ€ ์™”๋‹ค๋Š” ์•Œ๋ฆผ ์ „์†ก
unsubscribers.forEach(unsubscriber ->
notificationFacade.notifyNewChatMessage(unsubscriber.getUserId(), chatRoomId, unsubscriber.getNickname(), message.getContent())
);
}

public ChatMessageSliceResDto getChatMessage(Long roomId, int page, int size) {
Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdAt"));
Slice<ChatMessage> messages = chatMessageRepository.findByChatRoomId(roomId, pageable);

List<ChatMessageResDto> list = messages
.getContent()
.stream()
.map(ChatMessageResDto::of)
.toList();

return ChatMessageSliceResDto.builder()
.messages(list)
.hasNext(messages.hasNext())
.build();
}
}
Loading