Skip to content
Merged
Changes from all 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
Expand Up @@ -78,7 +78,10 @@ public ChatRoomResponse createOrGetChatRoom(Integer currentUserId, ChatRoomCreat
if (currentUser.getId().equals(targetUser.getId())) {
throw CustomException.of(CANNOT_CREATE_CHAT_ROOM_WITH_SELF);
}
ChatRoom.validateIsNotSameParticipant(currentUser, targetUser);

if (currentUser.getRole() == UserRole.ADMIN && targetUser.getRole() != UserRole.ADMIN) {
return getOrCreateSystemAdminChatRoomForUser(targetUser, currentUser);
Comment on lines +82 to +83

Copilot AI Feb 20, 2026

Copy link

Choose a reason for hiding this comment

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

어드민이 다른 어드민과 채팅방을 생성하려고 할 때의 동작이 명확하지 않습니다. 82줄의 조건문은 "currentUser가 ADMIN이고 targetUser가 ADMIN이 아닌 경우"만 처리합니다. 만약 어드민이 다른 어드민(SYSTEM_ADMIN이 아닌)과 채팅방을 생성하려고 하면, 기존 로직(88-95줄)을 따르게 되어 일반적인 1:1 채팅방이 생성됩니다. 이것이 의도된 동작인지 확인이 필요하며, 비즈니스 요구사항에 따라 어드민 간의 채팅도 시스템 관리자를 통해야 할 수 있습니다.

Suggested change
if (currentUser.getRole() == UserRole.ADMIN && targetUser.getRole() != UserRole.ADMIN) {
return getOrCreateSystemAdminChatRoomForUser(targetUser, currentUser);
if (currentUser.getRole() == UserRole.ADMIN) {
// 어드민이 비(非)어드민과 채팅을 생성하려는 경우: 시스템 관리자와의 중계 채팅방을 사용
if (targetUser.getRole() != UserRole.ADMIN) {
return getOrCreateSystemAdminChatRoomForUser(targetUser, currentUser);
}
// 어드민 ↔ 어드민 간에는 일반 1:1 채팅방을 사용한다 (기존 동작 유지)

Copilot uses AI. Check for mistakes.
}

ChatRoom chatRoom = chatRoomRepository.findByTwoUsers(currentUser.getId(), targetUser.getId())
.orElseGet(() -> chatRoomRepository.save(ChatRoom.directOf()));
Expand All @@ -90,6 +93,27 @@ public ChatRoomResponse createOrGetChatRoom(Integer currentUserId, ChatRoomCreat
return ChatRoomResponse.from(chatRoom);
}

private ChatRoomResponse getOrCreateSystemAdminChatRoomForUser(User targetUser, User adminUser) {
ChatRoom chatRoom = chatRoomRepository.findByTwoUsers(SYSTEM_ADMIN_ID, targetUser.getId())
.orElseGet(() -> {
ChatRoom newRoom = chatRoomRepository.save(ChatRoom.directOf());
User systemAdmin = userRepository.getById(SYSTEM_ADMIN_ID);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if there's any startup validation or seed data ensuring SYSTEM_ADMIN_ID exists

# Search for SYSTEM_ADMIN_ID usage and initialization
rg -n "SYSTEM_ADMIN_ID|system.*admin" --type java -C3

# Check for database seed/migration files that might create the system admin
fd -e sql -e java -e kt | xargs rg -l -i "insert.*user|seed|initial" 2>/dev/null | head -20

Repository: BCSDLab/KONECT_BACK_END

Length of output: 6652


🏁 Script executed:

# Look for the definition of SYSTEM_ADMIN_ID constant
rg -n "SYSTEM_ADMIN_ID\s*=" --type java -A1 -B1

# Check for any startup/initialization classes that might handle system admin creation
fd --type java | xargs rg -l -i "application.*startup|startup.*listener|init|dataseed|bootstrapp" 2>/dev/null | head -10

Repository: BCSDLab/KONECT_BACK_END

Length of output: 992


🏁 Script executed:

# Check the ChatService.java file to understand the context of this call
cat -n src/main/java/gg/agit/konect/domain/chat/service/ChatService.java | head -120

Repository: BCSDLab/KONECT_BACK_END

Length of output: 6607


🏁 Script executed:

# Look for migration files that might create the system admin user
find . -name "*.sql" -type f | head -30

Repository: BCSDLab/KONECT_BACK_END

Length of output: 2178


🏁 Script executed:

# Check migration files for system admin user creation
rg -n "insert.*user|admin|id.*1" src/main/resources/db/migration/ -i

# Look for any startup listener or initialization code
rg -l "ApplicationListener|@PostConstruct|InitializingBean|CommandLineRunner" --type java

Repository: BCSDLab/KONECT_BACK_END

Length of output: 1188


🏁 Script executed:

# Check if UserRepository.getById() has specific error handling
rg -B5 -A5 "\.getById\(" src/main/java/gg/agit/konect/domain/user/ --type java

Repository: BCSDLab/KONECT_BACK_END

Length of output: 2477


🏁 Script executed:

# Look for test files that might show expected behavior with system admin
find . -name "*Test.java" -o -name "*Tests.java" | xargs rg -l "SYSTEM_ADMIN|system.*admin" 2>/dev/null | head -5

Repository: BCSDLab/KONECT_BACK_END

Length of output: 49


🏁 Script executed:

# Check the initial migration file for system admin user creation
cat src/main/resources/db/migration/V1__init.sql | head -200

Repository: BCSDLab/KONECT_BACK_END

Length of output: 10842


🏁 Script executed:

# Look for UserRepository implementation or JpaRepository to understand getById() behavior
find . -name "UserRepository.java" -type f -exec cat {} \;

Repository: BCSDLab/KONECT_BACK_END

Length of output: 2207


🏁 Script executed:

# Search for any exception handling or custom error in getById calls
rg -B2 -A8 "userRepository\.getById" src/main/java/gg/agit/konect/domain/chat/service/ChatService.java

Repository: BCSDLab/KONECT_BACK_END

Length of output: 3887


🏁 Script executed:

# Check for any validation or database checks at application start
rg -n "ApplicationRunner|EventListener.*ContextRefreshed" src/main/java --type java

Repository: BCSDLab/KONECT_BACK_END

Length of output: 49


🏁 Script executed:

# Search V1__init.sql for INSERT statements, especially for the users table
grep -n "INSERT INTO users" src/main/resources/db/migration/V1__init.sql

Repository: BCSDLab/KONECT_BACK_END

Length of output: 49


🏁 Script executed:

# Check the end of V1__init.sql to see if there are any INSERT statements
tail -100 src/main/resources/db/migration/V1__init.sql

Repository: BCSDLab/KONECT_BACK_END

Length of output: 5706


🏁 Script executed:

# Search all migration files for any INSERT statements into users table
grep -r "INSERT INTO users" src/main/resources/db/migration/

Repository: BCSDLab/KONECT_BACK_END

Length of output: 49


Ensure system admin user (ID=1) exists via startup validation or seed data.

userRepository.getById(SYSTEM_ADMIN_ID) throws a CustomException if no user with ID=1 exists. No database migrations create this user, and there is no startup validation confirming it exists. If the system admin user is missing or deleted, the application will fail at runtime when attempting to create chat rooms with admins.

Add startup validation to check system admin existence and throw a clear error at application startup, or create the system admin user via database seed data.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/gg/agit/konect/domain/chat/service/ChatService.java` at line
102, Add startup validation to ensure the system admin user with ID =
SYSTEM_ADMIN_ID exists before runtime uses it: call
userRepository.getById(SYSTEM_ADMIN_ID) during application startup (e.g., in an
ApplicationRunner/@PostConstruct bean or a dedicated StartupValidator) and if it
throws or returns null throw a clear, descriptive CustomException (or create the
missing user via seed logic) so ChatService.createRoom (which uses
SYSTEM_ADMIN_ID) will never fail at runtime due to a missing admin; reference
userRepository.getById, SYSTEM_ADMIN_ID and ChatService to locate where the
check or seed must be wired.

LocalDateTime joinedAt = Objects.requireNonNull(
newRoom.getCreatedAt(), "chatRoom.createdAt must not be null"
);
ensureRoomMember(newRoom, systemAdmin, joinedAt);
Comment on lines +100 to +104

Copilot AI Feb 20, 2026

Copy link

Choose a reason for hiding this comment

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

SYSTEM_ADMIN_ID로 지정된 사용자가 데이터베이스에 존재하지 않는 경우에 대한 처리가 필요합니다. userRepository.getById()는 사용자를 찾지 못할 경우 예외를 발생시킬 수 있습니다. 기존의 채팅방이 존재하지 않아서 새로운 채팅방을 생성하는 경우에만 이 코드가 실행되므로, SYSTEM_ADMIN_ID 사용자의 존재를 보장할 수 없습니다. UserService의 sendWelcomeMessage 메서드에서는 관리자를 찾지 못한 경우 null 체크를 수행하는 것과 일관성이 있어야 합니다.

Suggested change
User systemAdmin = userRepository.getById(SYSTEM_ADMIN_ID);
LocalDateTime joinedAt = Objects.requireNonNull(
newRoom.getCreatedAt(), "chatRoom.createdAt must not be null"
);
ensureRoomMember(newRoom, systemAdmin, joinedAt);
User systemAdmin = userRepository.findById(SYSTEM_ADMIN_ID).orElse(null);
LocalDateTime joinedAt = Objects.requireNonNull(
newRoom.getCreatedAt(), "chatRoom.createdAt must not be null"
);
if (systemAdmin != null) {
ensureRoomMember(newRoom, systemAdmin, joinedAt);
}

Copilot uses AI. Check for mistakes.
ensureRoomMember(newRoom, targetUser, joinedAt);
return newRoom;
});

LocalDateTime joinedAt = Objects.requireNonNull(
chatRoom.getCreatedAt(), "chatRoom.createdAt must not be null"
);
ensureRoomMember(chatRoom, adminUser, joinedAt);

Copilot AI Feb 20, 2026

Copy link

Choose a reason for hiding this comment

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

어드민 사용자가 시스템 관리자(SYSTEM_ADMIN_ID)와 동일한 경우에 대한 처리가 누락되어 있습니다. 만약 adminUser.getId()가 SYSTEM_ADMIN_ID(1)와 동일하다면, 채팅방에 중복으로 시스템 관리자를 추가하려고 시도하게 됩니다. ensureRoomMember는 이미 멤버인 경우를 처리하지만, 이 경우 불필요한 데이터베이스 쿼리가 발생합니다. 시스템 관리자가 본인과 채팅방을 생성하려는 경우에 대한 검증이나 최적화가 필요합니다.

Suggested change
ensureRoomMember(chatRoom, adminUser, joinedAt);
if (!adminUser.getId().equals(SYSTEM_ADMIN_ID)) {
ensureRoomMember(chatRoom, adminUser, joinedAt);
}

Copilot uses AI. Check for mistakes.

Comment on lines +104 to +113

Copilot AI Feb 20, 2026

Copy link

Choose a reason for hiding this comment

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

이 로직은 시스템 관리자와 대상 사용자 간의 채팅방을 생성하거나 가져온 후, 요청한 어드민 사용자를 채팅방 멤버로 추가합니다. 그러나 어드민 사용자가 이미 해당 채팅방의 멤버인 경우에도 매번 ensureRoomMember를 호출하게 됩니다. ensureRoomMember 메서드는 이미 멤버인 경우를 처리하지만, 불필요한 데이터베이스 쿼리가 발생할 수 있습니다. 또한, 여러 어드민이 동일한 시스템 관리자 채팅방에 참여할 수 있는지에 대한 비즈니스 로직이 명확하지 않습니다.

Suggested change
ensureRoomMember(newRoom, systemAdmin, joinedAt);
ensureRoomMember(newRoom, targetUser, joinedAt);
return newRoom;
});
LocalDateTime joinedAt = Objects.requireNonNull(
chatRoom.getCreatedAt(), "chatRoom.createdAt must not be null"
);
ensureRoomMember(chatRoom, adminUser, joinedAt);
// 시스템 관리자와 대상 사용자만 이 방의 멤버로 유지한다.
ensureRoomMember(newRoom, systemAdmin, joinedAt);
ensureRoomMember(newRoom, targetUser, joinedAt);
return newRoom;
});
// 요청한 adminUser는 시스템 관리자 채팅방의 추가 멤버로 등록하지 않는다.
// 이 방은 SYSTEM_ADMIN과 targetUser 간의 1:1 방으로 유지한다.

Copilot uses AI. Check for mistakes.
return ChatRoomResponse.from(chatRoom);
}
Comment on lines +96 to +115

Copilot AI Feb 20, 2026

Copy link

Choose a reason for hiding this comment

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

새로운 getOrCreateSystemAdminChatRoomForUser 메서드와 어드민이 일반 사용자에게 채팅을 시작할 때의 로직에 대한 테스트 케이스가 누락되어 있습니다. 이 기능은 어드민이 "동아리 문의하기"를 눌렀을 때 발생하는 오류를 수정하는 중요한 버그 수정이므로, 다음과 같은 시나리오를 테스트해야 합니다: (1) 어드민이 일반 사용자와 처음 채팅을 시작하는 경우, (2) 시스템 관리자와 일반 사용자 간의 채팅방이 이미 존재하는 경우, (3) 여러 어드민이 동일한 일반 사용자와 채팅을 시도하는 경우.

Copilot uses AI. Check for mistakes.

@Transactional
public ChatRoomResponse createOrGetAdminChatRoom(Integer currentUserId) {
User adminUser = userRepository.findFirstByRoleOrderByIdAsc(UserRole.ADMIN)
Expand Down