Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import gg.agit.konect.domain.club.model.Club;
import gg.agit.konect.domain.club.model.ClubMember;
import gg.agit.konect.domain.club.repository.ClubMemberRepository;
import gg.agit.konect.domain.user.enums.UserRole;
import gg.agit.konect.domain.user.model.User;
import gg.agit.konect.domain.user.repository.UserRepository;
import gg.agit.konect.global.exception.CustomException;
Expand All @@ -33,7 +32,7 @@
@Transactional(readOnly = true)
public class ChatRoomMembershipService {

private static final int SYSTEM_ADMIN_ID = 1;
public static final int SYSTEM_ADMIN_ID = 1;

private final ChatRoomRepository chatRoomRepository;
private final ChatRoomMemberRepository chatRoomMemberRepository;
Expand Down Expand Up @@ -68,7 +67,7 @@ public void updateLastReadAt(Integer roomId, Integer userId, LocalDateTime readA
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateDirectRoomLastReadAt(Integer roomId, User user, LocalDateTime readAt, ChatRoom room) {
// 어드민이 SYSTEM_ADMIN 방의 메시지를 읽으면 SYSTEM_ADMIN의 lastReadAt을 업데이트
if (user.getRole() == UserRole.ADMIN && isSystemAdminRoom(roomId)) {
if (user.isAdmin() && isSystemAdminRoom(roomId)) {
chatRoomMemberRepository.updateLastReadAtIfOlder(roomId, SYSTEM_ADMIN_ID, readAt);
return;
}
Expand Down Expand Up @@ -134,7 +133,7 @@ private void ensureDirectRoomMemberExists(ChatRoom room, User user, LocalDateTim

// 어드민은 SYSTEM_ADMIN 방의 메시지를 조회할 수 있지만, 멤버로 추가되지는 않는다
// (멤버가 추가되면 findByTwoUsers에서 해당 방을 찾지 못해 채팅방이 중복 생성됨)
if (user.getRole() == UserRole.ADMIN && isSystemAdminRoom(room.getId())) {
if (user.isAdmin() && isSystemAdminRoom(room.getId())) {
return;
}

Expand Down
150 changes: 72 additions & 78 deletions src/main/java/gg/agit/konect/domain/chat/service/ChatService.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package gg.agit.konect.domain.chat.service;

import static gg.agit.konect.domain.chat.service.ChatRoomMembershipService.SYSTEM_ADMIN_ID;
import static gg.agit.konect.global.code.ApiResponseCode.*;

import java.time.LocalDateTime;
Expand Down Expand Up @@ -69,7 +70,6 @@
@Transactional(readOnly = true)
public class ChatService {

private static final int SYSTEM_ADMIN_ID = 1;
private static final String ETC_SECTION_NAME = "기타";
private static final String DEFAULT_GROUP_ROOM_NAME = "그룹 채팅";

Expand All @@ -94,7 +94,7 @@ public ChatRoomResponse createOrGetChatRoom(Integer currentUserId, ChatRoomCreat
throw CustomException.of(CANNOT_CREATE_CHAT_ROOM_WITH_SELF);
}

if (currentUser.getRole() == UserRole.ADMIN && targetUser.getRole() != UserRole.ADMIN) {
if (currentUser.isAdmin() && !targetUser.isAdmin()) {
return getOrCreateSystemAdminChatRoomForUser(targetUser, currentUser);
}

Expand Down Expand Up @@ -396,7 +396,7 @@ public ChatMessagePageResponse getMessages(Integer userId, Integer roomId, Integ
LocalDateTime readAt = LocalDateTime.now();

if (room.isDirectRoom()) {
boolean isAdminViewingSystemRoom = user.getRole() == UserRole.ADMIN && isSystemAdminRoom(room);
boolean isAdminViewingSystemRoom = user.isAdmin() && isSystemAdminRoom(room);
if (isAdminViewingSystemRoom) {
chatRoomMembershipService.updateLastReadAt(roomId, SYSTEM_ADMIN_ID, readAt);
recordPresenceSafely(roomId, userId);
Expand Down Expand Up @@ -448,7 +448,7 @@ public ChatMuteResponse toggleMute(Integer userId, Integer roomId) {
ensureRoomMember(room, member.getUser(), member.getCreatedAt());
} else if (room.isDirectRoom()) {
// 어드민이 SYSTEM_ADMIN 방에 접근하는 경우는 멤버십 체크를 건너뜀
boolean isAdminAccessingSystemAdminRoom = user.getRole() == UserRole.ADMIN
boolean isAdminAccessingSystemAdminRoom = user.isAdmin()
&& isSystemAdminRoom(room);
if (!isAdminAccessingSystemAdminRoom) {
getAccessibleDirectRoomMember(room, user);
Expand Down Expand Up @@ -491,7 +491,7 @@ public void updateChatRoomName(Integer userId, Integer roomId, ChatRoomNameUpdat
private List<ChatRoomSummaryResponse> getDirectChatRooms(Integer userId) {
User user = userRepository.getById(userId);

if (user.getRole() == UserRole.ADMIN) {
if (user.isAdmin()) {
return getAdminDirectChatRooms(userId);
}

Expand Down Expand Up @@ -620,37 +620,38 @@ private List<ChatRoomSummaryResponse> getGroupChatRooms(Integer userId) {
.toList();
}

private ChatMessagePageResponse getDirectChatRoomMessages(
Integer userId,
private ChatMessagePageResponse buildDirectChatRoomMessages(
User user,
Integer roomId,
Integer page,
Integer limit,
LocalDateTime readAt
LocalDateTime readAt,
LocalDateTime visibleMessageFrom,
List<LocalDateTime> sortedReadBaselines,
Integer maskedAdminId
) {
ChatRoom chatRoom = getDirectRoom(roomId);
User user = userRepository.getById(userId);
List<ChatRoomMember> members = chatRoomMemberRepository.findByChatRoomId(roomId);
LocalDateTime visibleMessageFrom = prepareDirectRoomAccess(getOrCreateDirectRoomMember(chatRoom, user),
chatRoom);

PageRequest pageable = PageRequest.of(page - 1, limit);
Page<ChatMessage> messages = chatMessageRepository.findByChatRoomId(roomId, visibleMessageFrom, pageable);

List<LocalDateTime> sortedReadBaselines = toSortedReadBaselines(members);

List<ChatMessageDetailResponse> responseMessages = messages.getContent().stream()
.map(message -> {
boolean isRead = message.isSentBy(userId) || !message.getCreatedAt().isAfter(readAt);
Integer senderId = maskedAdminId != null
? resolveDirectSenderId(message, maskedAdminId)
: message.getSender().getId();
boolean isMine = maskedAdminId != null
? shouldDisplayAsOwnMessage(user, message, true)
: message.isSentBy(user.getId());
boolean isRead = isMine || !message.getCreatedAt().isAfter(readAt);
int unreadCount = countUnreadSince(message.getCreatedAt(), sortedReadBaselines);
return new ChatMessageDetailResponse(
message.getId(),
message.getSender().getId(),
senderId,
null,
message.getContent(),
message.getCreatedAt(),
isRead,
unreadCount,
message.isSentBy(userId)
isMine
);
})
.toList();
Expand All @@ -665,6 +666,25 @@ private ChatMessagePageResponse getDirectChatRoomMessages(
);
}

private ChatMessagePageResponse getDirectChatRoomMessages(
Integer userId,
Integer roomId,
Integer page,
Integer limit,
LocalDateTime readAt
) {
ChatRoom chatRoom = getDirectRoom(roomId);
User user = userRepository.getById(userId);
List<ChatRoomMember> members = chatRoomMemberRepository.findByChatRoomId(roomId);
LocalDateTime visibleMessageFrom = prepareDirectRoomAccess(getOrCreateDirectRoomMember(chatRoom, user),
chatRoom);

List<LocalDateTime> sortedReadBaselines = toSortedReadBaselines(members);

return buildDirectChatRoomMessages(user, roomId, page, limit, readAt,
visibleMessageFrom, sortedReadBaselines, null);
}

private ChatMessagePageResponse getAdminSystemDirectChatRoomMessages(
User user,
ChatRoom chatRoom,
Expand All @@ -676,39 +696,11 @@ private ChatMessagePageResponse getAdminSystemDirectChatRoomMessages(
List<ChatRoomMember> members = chatRoomMemberRepository.findByChatRoomId(roomId);
LocalDateTime visibleMessageFrom = resolveAdminSystemRoomVisibleMessageFrom(members);

PageRequest pageable = PageRequest.of(page - 1, limit);
Page<ChatMessage> messages = chatMessageRepository.findByChatRoomId(roomId, visibleMessageFrom, pageable);

List<LocalDateTime> sortedReadBaselines = toAdminChatReadBaselines(members);

Integer maskedAdminId = getMaskedAdminId(user, chatRoom);
List<ChatMessageDetailResponse> responseMessages = messages.getContent().stream()
.map(message -> {
Integer senderId = resolveDirectSenderId(message, maskedAdminId);
boolean isMine = shouldDisplayAsOwnMessage(user, message, true);
boolean isRead = isMine || !message.getCreatedAt().isAfter(readAt);
int unreadCount = countUnreadSince(message.getCreatedAt(), sortedReadBaselines);
return new ChatMessageDetailResponse(
message.getId(),
senderId,
null,
message.getContent(),
message.getCreatedAt(),
isRead,
unreadCount,
isMine
);
})
.toList();

return new ChatMessagePageResponse(
messages.getTotalElements(),
messages.getNumberOfElements(),
messages.getTotalPages(),
messages.getNumber() + 1,
null,
responseMessages
);
return buildDirectChatRoomMessages(user, roomId, page, limit, readAt,
visibleMessageFrom, sortedReadBaselines, maskedAdminId);
}

private ChatMessageDetailResponse sendDirectMessage(
Expand All @@ -720,7 +712,7 @@ private ChatMessageDetailResponse sendDirectMessage(
User sender = userRepository.getById(userId);

// 어드민이 SYSTEM_ADMIN 방에 메시지를 보내는 경우
boolean isAdminSendingToSystemAdminRoom = sender.getRole() == UserRole.ADMIN
boolean isAdminSendingToSystemAdminRoom = sender.isAdmin()
&& isSystemAdminRoom(chatRoom);

ChatRoomMember senderMember = null;
Expand Down Expand Up @@ -1175,7 +1167,7 @@ private Map<Integer, Integer> getUnreadCountMap(List<Integer> chatRoomIds, Integ
}

private Integer getMaskedAdminId(User user, ChatRoom chatRoom) {
if (user.getRole() == UserRole.ADMIN) {
if (user.isAdmin()) {
return null;
}

Expand All @@ -1197,7 +1189,7 @@ private Integer getMaskedAdminId(User user, ChatRoom chatRoom) {
}

private void publishAdminChatEventIfNeeded(boolean isSystemAdminRoom, User sender, String content) {
if (isSystemAdminRoom && sender.getRole() != UserRole.ADMIN) {
if (isSystemAdminRoom && !sender.isAdmin()) {
eventPublisher.publishEvent(AdminChatReceivedEvent.of(sender.getId(), sender.getName(), content));
}
}
Expand Down Expand Up @@ -1260,7 +1252,7 @@ private void ensureDirectRoomRequester(ChatRoom room, User user, LocalDateTime j
private boolean shouldSkipSystemAdminMembership(ChatRoom room, User user) {
// 문의방은 SYSTEM_ADMIN + 일반 사용자 2인 구조를 전제로 재사용(findByTwoUsers)되므로,
// 생성/재오픈 경로에서도 일반 ADMIN을 멤버로 추가하면 안 된다.
return user.getRole() == UserRole.ADMIN && isSystemAdminRoom(room);
return user.isAdmin() && isSystemAdminRoom(room);
}

private String normalizeCustomRoomName(String roomName) {
Expand Down Expand Up @@ -1296,19 +1288,18 @@ private void updateClubMessageLastReadAt(Integer roomId, Integer userId, LocalDa
}

private List<LocalDateTime> toSortedReadBaselines(List<ChatRoomMember> members) {
List<LocalDateTime> baselines = members.stream()
return members.stream()
.map(ChatRoomMember::getLastReadAt)
.sorted()
.toList();
return baselines;
}

private List<LocalDateTime> toAdminChatReadBaselines(List<ChatRoomMember> members) {
LocalDateTime adminLastReadAt = null;
LocalDateTime userLastReadAt = null;

for (ChatRoomMember member : members) {
if (member.getUser().getRole() == UserRole.ADMIN) {
if (member.getUser().isAdmin()) {
if (adminLastReadAt == null || member.getLastReadAt().isAfter(adminLastReadAt)) {
adminLastReadAt = member.getLastReadAt();
}
Expand Down Expand Up @@ -1383,7 +1374,7 @@ private ChatRoomMember getOrCreateDirectRoomMember(ChatRoom chatRoom, User user)
return chatRoomMemberRepository.findByChatRoomIdAndUserId(chatRoom.getId(), user.getId())
.orElseGet(() -> {
// 어드민은 SYSTEM_ADMIN 방에 멤버로 추가되지 않음
if (user.getRole() == UserRole.ADMIN && isSystemAdminRoom(chatRoom)) {
if (user.isAdmin() && isSystemAdminRoom(chatRoom)) {
throw CustomException.of(FORBIDDEN_CHAT_ROOM_ACCESS);
}
throw CustomException.of(FORBIDDEN_CHAT_ROOM_ACCESS);
Expand Down Expand Up @@ -1440,13 +1431,13 @@ private boolean shouldDisplayAsOwnMessage(
boolean isAdminViewingSystemRoom
) {
if (isAdminViewingSystemRoom) {
return message.getSender().getRole() == UserRole.ADMIN;
return message.getSender().isAdmin();
}
return message.isSentBy(currentUser.getId());
}

private Integer resolveDirectSenderId(ChatMessage message, Integer maskedAdminId) {
if (maskedAdminId != null && message.getSender().getRole() == UserRole.ADMIN) {
if (maskedAdminId != null && message.getSender().isAdmin()) {
return maskedAdminId;
}
return message.getSender().getId();
Expand Down Expand Up @@ -1531,26 +1522,29 @@ private User resolveDirectChatPartner(List<ChatRoomMember> members, Integer user
}

private User findNonAdminUser(List<ChatRoomMember> members) {
return members.stream()
.map(ChatRoomMember::getUser)
.filter(memberUser -> memberUser.getRole() != UserRole.ADMIN)
.findFirst()
.orElse(null);
Map<Integer, User> userMap = members.stream()
.collect(Collectors.toMap(
ChatRoomMember::getUserId,
ChatRoomMember::getUser,
(existing, replacement) -> existing
));
List<MemberInfo> memberInfos = members.stream()
.map(m -> new MemberInfo(m.getUserId(), m.getCreatedAt()))
.toList();
return findNonAdminUserFromMemberInfo(memberInfos, userMap);
}

private User resolveDirectMessageReceiver(List<ChatRoomMember> members, User sender) {
if (sender.getRole() == UserRole.ADMIN) {
User nonAdminUser = findNonAdminUser(members);
if (nonAdminUser != null) {
return nonAdminUser;
}
}

User partner = resolveDirectChatPartner(members, sender.getId());
if (partner == null) {
throw CustomException.of(FORBIDDEN_CHAT_ROOM_ACCESS);
}
return partner;
Map<Integer, User> userMap = members.stream()
.collect(Collectors.toMap(
ChatRoomMember::getUserId,
ChatRoomMember::getUser,
(existing, replacement) -> existing
));
List<MemberInfo> memberInfos = members.stream()
.map(m -> new MemberInfo(m.getUserId(), m.getCreatedAt()))
.toList();
return resolveMessageReceiverFromMemberInfo(sender, memberInfos, userMap);
}

private User findDirectPartnerFromMemberInfo(
Expand Down Expand Up @@ -1585,7 +1579,7 @@ private User findNonAdminUserFromMemberInfo(List<MemberInfo> memberInfos, Map<In
.sorted(Comparator.comparing(MemberInfo::createdAt))
.map(info -> userMap.get(info.userId()))
.filter(Objects::nonNull)
.filter(user -> user.getRole() != UserRole.ADMIN)
.filter(user -> !user.isAdmin())
.findFirst()
.orElse(null);
}
Expand All @@ -1595,7 +1589,7 @@ private User resolveMessageReceiverFromMemberInfo(
List<MemberInfo> memberInfos,
Map<Integer, User> userMap
) {
if (sender.getRole() == UserRole.ADMIN) {
if (sender.isAdmin()) {
User nonAdminUser = findNonAdminUserFromMemberInfo(memberInfos, userMap);
if (nonAdminUser != null) {
return nonAdminUser;
Expand Down
Loading