Skip to content

Commit 33185b2

Browse files
authored
๐Ÿ”– release: v0.0.3
2 parents ce9f137 + 34b0ed2 commit 33185b2

File tree

68 files changed

+1016
-168
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+1016
-168
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: slack-notify-pr-open
2+
3+
on:
4+
pull_request:
5+
types: [opened, reopened]
6+
7+
jobs:
8+
notify:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- name: Send Slack notification
12+
uses: rtCamp/action-slack-notify@v2
13+
env:
14+
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
15+
SLACK_USERNAME: Github CI
16+
SLACK_ICON: https://github.com/github.png
17+
MSG_MINIMAL: ref,event
18+
SLACK_COLOR: '#36a64f'
19+
SLACK_TITLE: 'New Pull Request ๐Ÿš€'
20+
SLACK_MESSAGE: |
21+
#${{ github.event.pull_request.number }} ${{ github.event.pull_request.title }}
22+
๐Ÿ”— ${{ github.event.pull_request.html_url }}

โ€Žbackend/build.gradleโ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ dependencies {
4242
testAnnotationProcessor 'org.projectlombok:lombok'
4343
testRuntimeOnly 'com.h2database:h2'
4444
testImplementation 'org.springframework.security:spring-security-test'
45+
testImplementation 'com.github.database-rider:rider-spring:1.44.0'
4546

4647
/* ETC */
4748
implementation 'org.apache.commons:commons-lang3:3.12.0'

โ€Žbackend/src/main/java/io/f1/backend/domain/admin/app/AdminDetailService.javaโ€Ž

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import io.f1.backend.domain.admin.dao.AdminRepository;
44
import io.f1.backend.domain.admin.dto.AdminPrincipal;
55
import io.f1.backend.domain.admin.entity.Admin;
6+
import io.f1.backend.global.exception.errorcode.AdminErrorCode;
67

78
import lombok.RequiredArgsConstructor;
89

@@ -23,7 +24,9 @@ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundEx
2324
adminRepository
2425
.findByUsername(username)
2526
.orElseThrow(
26-
() -> new UsernameNotFoundException("E404007: ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ด€๋ฆฌ์ž์ž…๋‹ˆ๋‹ค."));
27+
() ->
28+
new UsernameNotFoundException(
29+
AdminErrorCode.ADMIN_NOT_FOUND.getMessage()));
2730
// ํ”„๋ก ํŠธ์—”๋“œ๋กœ ๋‚ด๋ ค๊ฐ€์ง€ ์•Š๋Š” ์˜ˆ์™ธ
2831
return new AdminPrincipal(admin);
2932
}

โ€Žbackend/src/main/java/io/f1/backend/domain/admin/app/handler/AdminLoginFailureHandler.javaโ€Ž

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.fasterxml.jackson.databind.ObjectMapper;
44

55
import io.f1.backend.domain.admin.dto.AdminLoginFailResponse;
6+
import io.f1.backend.global.exception.errorcode.AuthErrorCode;
67

78
import jakarta.servlet.http.HttpServletRequest;
89
import jakarta.servlet.http.HttpServletResponse;
@@ -31,7 +32,9 @@ public void onAuthenticationFailure(
3132
response.setContentType("application/json;charset=UTF-8");
3233

3334
AdminLoginFailResponse errorResponse =
34-
new AdminLoginFailResponse("E401005", "์•„์ด๋”” ๋˜๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.");
35+
new AdminLoginFailResponse(
36+
AuthErrorCode.LOGIN_FAILED.getCode(),
37+
AuthErrorCode.LOGIN_FAILED.getMessage());
3538

3639
response.getWriter().write(objectMapper.writeValueAsString(errorResponse));
3740
}

โ€Žbackend/src/main/java/io/f1/backend/domain/admin/app/handler/AdminLoginSuccessHandler.javaโ€Ž

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import io.f1.backend.domain.admin.dao.AdminRepository;
77
import io.f1.backend.domain.admin.dto.AdminPrincipal;
88
import io.f1.backend.domain.admin.entity.Admin;
9+
import io.f1.backend.global.exception.CustomException;
10+
import io.f1.backend.global.exception.errorcode.AdminErrorCode;
911

1012
import jakarta.servlet.http.HttpServletRequest;
1113
import jakarta.servlet.http.HttpServletResponse;
@@ -36,7 +38,7 @@ public void onAuthenticationSuccess(
3638
Admin admin =
3739
adminRepository
3840
.findByUsername(principal.getUsername())
39-
.orElseThrow(() -> new RuntimeException("E404007: ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ด€๋ฆฌ์ž์ž…๋‹ˆ๋‹ค."));
41+
.orElseThrow(() -> new CustomException(AdminErrorCode.ADMIN_NOT_FOUND));
4042

4143
admin.updateLastLogin(LocalDateTime.now());
4244
adminRepository.save(admin);
Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,28 @@
11
package io.f1.backend.domain.game.app;
22

3-
import io.f1.backend.domain.game.dto.GameStartData;
3+
import static io.f1.backend.domain.quiz.mapper.QuizMapper.toGameStartResponse;
4+
5+
import io.f1.backend.domain.game.dto.request.GameStartRequest;
46
import io.f1.backend.domain.game.dto.response.GameStartResponse;
57
import io.f1.backend.domain.game.event.RoomUpdatedEvent;
68
import io.f1.backend.domain.game.model.GameSetting;
79
import io.f1.backend.domain.game.model.Player;
810
import io.f1.backend.domain.game.model.Room;
911
import io.f1.backend.domain.game.model.RoomState;
1012
import io.f1.backend.domain.game.store.RoomRepository;
13+
import io.f1.backend.domain.question.entity.Question;
1114
import io.f1.backend.domain.quiz.app.QuizService;
1215
import io.f1.backend.domain.quiz.entity.Quiz;
16+
import io.f1.backend.global.exception.CustomException;
17+
import io.f1.backend.global.exception.errorcode.GameErrorCode;
18+
import io.f1.backend.global.exception.errorcode.RoomErrorCode;
1319

1420
import lombok.RequiredArgsConstructor;
1521

1622
import org.springframework.context.ApplicationEventPublisher;
1723
import org.springframework.stereotype.Service;
1824

25+
import java.util.List;
1926
import java.util.Map;
2027

2128
@Service
@@ -26,15 +33,17 @@ public class GameService {
2633
private final RoomRepository roomRepository;
2734
private final ApplicationEventPublisher eventPublisher;
2835

29-
public GameStartData gameStart(Long roomId, Long quizId) {
36+
public GameStartResponse gameStart(Long roomId, GameStartRequest gameStartRequest) {
37+
38+
Long quizId = gameStartRequest.quizId();
3039

3140
Room room =
3241
roomRepository
3342
.findRoom(roomId)
34-
.orElseThrow(() -> new IllegalArgumentException("404 ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๋ฐฉ์ž…๋‹ˆ๋‹ค."));
43+
.orElseThrow(() -> new CustomException(RoomErrorCode.ROOM_NOT_FOUND));
3544

3645
if (!validateReadyStatus(room)) {
37-
throw new IllegalArgumentException("E403004 : ๋ ˆ๋”” ์ƒํƒœ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.");
46+
throw new CustomException(RoomErrorCode.PLAYER_NOT_READY);
3847
}
3948

4049
// ๋ฐฉ์˜ gameSetting์— ์„ค์ •๋œ ํ€ด์ฆˆ๋ž‘ ์š”์ฒญ ํ€ด์ฆˆ๋ž‘ ๊ฐ™์€์ง€ ์ฒดํฌ ํ›„ GameSetting์—์„œ ๋ผ์šด๋“œ ๊ฐ€์ ธ์˜ค๊ธฐ
@@ -43,22 +52,25 @@ public GameStartData gameStart(Long roomId, Long quizId) {
4352
Quiz quiz = quizService.getQuizWithQuestionsById(quizId);
4453

4554
// ๋ผ์šด๋“œ ์ˆ˜๋งŒํผ ๋žœ๋ค Question ์ถ”์ถœ
46-
GameStartResponse questions = quizService.getRandomQuestionsWithoutAnswer(quizId, round);
55+
List<Question> questions = quizService.getRandomQuestionsWithoutAnswer(quizId, round);
56+
room.updateQuestions(questions);
57+
58+
GameStartResponse gameStartResponse = toGameStartResponse(questions);
4759

4860
// ๋ฐฉ ์ •๋ณด ๊ฒŒ์ž„ ์ค‘์œผ๋กœ ๋ณ€๊ฒฝ
4961
room.updateRoomState(RoomState.PLAYING);
5062

5163
eventPublisher.publishEvent(new RoomUpdatedEvent(room, quiz));
5264

53-
return new GameStartData(getDestination(roomId), questions);
65+
return gameStartResponse;
5466
}
5567

5668
private Integer checkGameSetting(Room room, Long quizId) {
5769

5870
GameSetting gameSetting = room.getGameSetting();
5971

60-
if (!gameSetting.checkQuizId(quizId)) {
61-
throw new IllegalArgumentException("E409002 : ๊ฒŒ์ž„ ์„ค์ •์ด ๋‹ค๋ฆ…๋‹ˆ๋‹ค. (๊ฒŒ์ž„์„ ์‹œ์ž‘ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.)");
72+
if (!gameSetting.validateQuizId(quizId)) {
73+
throw new CustomException(GameErrorCode.GAME_SETTING_CONFLICT);
6274
}
6375

6476
return gameSetting.getRound();
@@ -70,8 +82,4 @@ private boolean validateReadyStatus(Room room) {
7082

7183
return playerSessionMap.values().stream().allMatch(Player::isReady);
7284
}
73-
74-
private static String getDestination(Long roomId) {
75-
return "/sub/room/" + roomId;
76-
}
7785
}

โ€Žbackend/src/main/java/io/f1/backend/domain/game/app/RoomService.javaโ€Ž

Lines changed: 72 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,19 @@
44
import static io.f1.backend.domain.game.mapper.RoomMapper.toGameSetting;
55
import static io.f1.backend.domain.game.mapper.RoomMapper.toGameSettingResponse;
66
import static io.f1.backend.domain.game.mapper.RoomMapper.toPlayerListResponse;
7+
import static io.f1.backend.domain.game.mapper.RoomMapper.toQuestionResultResponse;
8+
import static io.f1.backend.domain.game.mapper.RoomMapper.toRankUpdateResponse;
79
import static io.f1.backend.domain.game.mapper.RoomMapper.toRoomResponse;
810
import static io.f1.backend.domain.game.mapper.RoomMapper.toRoomSetting;
911
import static io.f1.backend.domain.game.mapper.RoomMapper.toRoomSettingResponse;
1012
import static io.f1.backend.global.util.SecurityUtils.getCurrentUserId;
1113
import static io.f1.backend.global.util.SecurityUtils.getCurrentUserNickname;
1214

15+
import io.f1.backend.domain.game.dto.ChatMessage;
1316
import io.f1.backend.domain.game.dto.RoomEventType;
1417
import io.f1.backend.domain.game.dto.RoomExitData;
1518
import io.f1.backend.domain.game.dto.RoomInitialData;
19+
import io.f1.backend.domain.game.dto.RoundResult;
1620
import io.f1.backend.domain.game.dto.request.RoomCreateRequest;
1721
import io.f1.backend.domain.game.dto.request.RoomValidationRequest;
1822
import io.f1.backend.domain.game.dto.response.GameSettingResponse;
@@ -22,15 +26,17 @@
2226
import io.f1.backend.domain.game.dto.response.RoomResponse;
2327
import io.f1.backend.domain.game.dto.response.RoomSettingResponse;
2428
import io.f1.backend.domain.game.dto.response.SystemNoticeResponse;
25-
import io.f1.backend.domain.game.event.RoomCreatedEvent;
2629
import io.f1.backend.domain.game.model.GameSetting;
2730
import io.f1.backend.domain.game.model.Player;
2831
import io.f1.backend.domain.game.model.Room;
2932
import io.f1.backend.domain.game.model.RoomSetting;
3033
import io.f1.backend.domain.game.model.RoomState;
3134
import io.f1.backend.domain.game.store.RoomRepository;
35+
import io.f1.backend.domain.question.entity.Question;
3236
import io.f1.backend.domain.quiz.app.QuizService;
37+
import io.f1.backend.domain.quiz.dto.QuizMinData;
3338
import io.f1.backend.domain.quiz.entity.Quiz;
39+
import io.f1.backend.domain.user.dto.UserPrincipal;
3440
import io.f1.backend.global.exception.CustomException;
3541
import io.f1.backend.global.exception.errorcode.RoomErrorCode;
3642

@@ -60,10 +66,10 @@ public class RoomService {
6066

6167
public RoomCreateResponse saveRoom(RoomCreateRequest request) {
6268

63-
Long quizMinId = quizService.getQuizMinId();
64-
Quiz quiz = quizService.getQuizWithQuestionsById(quizMinId);
69+
QuizMinData quizMinData = quizService.getQuizMinData();
70+
// Quiz quiz = quizService.getQuizWithQuestionsById(quizMinId);
6571

66-
GameSetting gameSetting = toGameSetting(quiz);
72+
GameSetting gameSetting = toGameSetting(quizMinData);
6773

6874
Player host = createPlayer();
6975

@@ -77,7 +83,7 @@ public RoomCreateResponse saveRoom(RoomCreateRequest request) {
7783

7884
roomRepository.saveRoom(room);
7985

80-
eventPublisher.publishEvent(new RoomCreatedEvent(room, quiz));
86+
// eventPublisher.publishEvent(new RoomCreatedEvent(room, quiz));
8187

8288
return new RoomCreateResponse(newId);
8389
}
@@ -110,11 +116,12 @@ public void enterRoom(RoomValidationRequest request) {
110116
}
111117
}
112118

113-
public RoomInitialData initializeRoomSocket(Long roomId, String sessionId) {
119+
public RoomInitialData initializeRoomSocket(
120+
Long roomId, String sessionId, UserPrincipal principal) {
114121

115122
Room room = findRoom(roomId);
116123

117-
Player player = createPlayer();
124+
Player player = createPlayer(principal);
118125

119126
Map<String, Player> playerSessionMap = room.getPlayerSessionMap();
120127
Map<Long, String> userIdSessionMap = room.getUserIdSessionMap();
@@ -140,30 +147,25 @@ public RoomInitialData initializeRoomSocket(Long roomId, String sessionId) {
140147

141148
PlayerListResponse playerListResponse = toPlayerListResponse(room);
142149

143-
SystemNoticeResponse systemNoticeResponse = ofPlayerEvent(player, RoomEventType.ENTER);
150+
SystemNoticeResponse systemNoticeResponse =
151+
ofPlayerEvent(player.getNickname(), RoomEventType.ENTER);
144152

145153
return new RoomInitialData(
146-
getDestination(roomId),
147-
roomSettingResponse,
148-
gameSettingResponse,
149-
playerListResponse,
150-
systemNoticeResponse);
154+
roomSettingResponse, gameSettingResponse, playerListResponse, systemNoticeResponse);
151155
}
152156

153-
public RoomExitData exitRoom(Long roomId, String sessionId) {
157+
public RoomExitData exitRoom(Long roomId, String sessionId, UserPrincipal principal) {
154158

155159
Object lock = roomLocks.computeIfAbsent(roomId, k -> new Object());
156160

157161
synchronized (lock) {
158162
Room room = findRoom(roomId);
159163

160-
String destination = getDestination(roomId);
161-
162-
Player removePlayer = getRemovePlayer(room, sessionId);
164+
Player removePlayer = getRemovePlayer(room, sessionId, principal);
163165

164166
/* ๋ฐฉ ์‚ญ์ œ */
165167
if (isLastPlayer(room, sessionId)) {
166-
return removeRoom(room, destination);
168+
return removeRoom(room);
167169
}
168170

169171
/* ๋ฐฉ์žฅ ๋ณ€๊ฒฝ */
@@ -175,14 +177,27 @@ public RoomExitData exitRoom(Long roomId, String sessionId) {
175177
removePlayer(room, sessionId, removePlayer);
176178

177179
SystemNoticeResponse systemNoticeResponse =
178-
ofPlayerEvent(removePlayer, RoomEventType.EXIT);
180+
ofPlayerEvent(removePlayer.nickname, RoomEventType.EXIT);
179181

180182
PlayerListResponse playerListResponse = toPlayerListResponse(room);
181183

182-
return new RoomExitData(destination, playerListResponse, systemNoticeResponse, false);
184+
return new RoomExitData(playerListResponse, systemNoticeResponse, false);
183185
}
184186
}
185187

188+
public PlayerListResponse handlePlayerReady(Long roomId, String sessionId) {
189+
Player player =
190+
roomRepository
191+
.findPlayerInRoomBySessionId(roomId, sessionId)
192+
.orElseThrow(() -> new CustomException(RoomErrorCode.PLAYER_NOT_FOUND));
193+
194+
player.toggleReady();
195+
196+
Room room = findRoom(roomId);
197+
198+
return toPlayerListResponse(room);
199+
}
200+
186201
public RoomListResponse getAllRooms() {
187202
List<Room> rooms = roomRepository.findAll();
188203
List<RoomResponse> roomResponses =
@@ -198,17 +213,44 @@ public RoomListResponse getAllRooms() {
198213
return new RoomListResponse(roomResponses);
199214
}
200215

201-
private Player getRemovePlayer(Room room, String sessionId) {
216+
// todo ๋™์‹œ์„ฑ์ ์šฉ
217+
public RoundResult chat(Long roomId, String sessionId, ChatMessage chatMessage) {
218+
Room room = findRoom(roomId);
219+
220+
if (!room.isPlaying()) {
221+
return buildResultOnlyChat(chatMessage);
222+
}
223+
224+
Question currentQuestion = room.getCurrentQuestion();
225+
226+
String answer = currentQuestion.getAnswer();
227+
228+
if (!answer.equals(chatMessage.message())) {
229+
return buildResultOnlyChat(chatMessage);
230+
}
231+
232+
room.increasePlayerCorrectCount(sessionId);
233+
234+
return RoundResult.builder()
235+
.questionResult(
236+
toQuestionResultResponse(currentQuestion.getId(), chatMessage, answer))
237+
.rankUpdate(toRankUpdateResponse(room))
238+
.systemNotice(ofPlayerEvent(chatMessage.nickname(), RoomEventType.ENTER))
239+
.chat(chatMessage)
240+
.build();
241+
}
242+
243+
private Player getRemovePlayer(Room room, String sessionId, UserPrincipal principal) {
202244
Player removePlayer = room.getPlayerSessionMap().get(sessionId);
203245
if (removePlayer == null) {
204-
room.removeUserId(getCurrentUserId());
246+
room.removeUserId(principal.getUserId());
205247
throw new CustomException(RoomErrorCode.SOCKET_SESSION_NOT_FOUND);
206248
}
207249
return removePlayer;
208250
}
209251

210-
private static String getDestination(Long roomId) {
211-
return "/sub/room/" + roomId;
252+
private Player createPlayer(UserPrincipal principal) {
253+
return new Player(principal.getUserId(), principal.getUserNickname());
212254
}
213255

214256
private Player createPlayer() {
@@ -226,12 +268,12 @@ private boolean isLastPlayer(Room room, String sessionId) {
226268
return playerSessionMap.size() == 1 && playerSessionMap.containsKey(sessionId);
227269
}
228270

229-
private RoomExitData removeRoom(Room room, String destination) {
271+
private RoomExitData removeRoom(Room room) {
230272
Long roomId = room.getId();
231273
roomRepository.removeRoom(roomId);
232274
roomLocks.remove(roomId);
233275
log.info("{}๋ฒˆ ๋ฐฉ ์‚ญ์ œ", roomId);
234-
return RoomExitData.builder().destination(destination).removedRoom(true).build();
276+
return RoomExitData.builder().removedRoom(true).build();
235277
}
236278

237279
private void changeHost(Room room, String hostSessionId) {
@@ -255,4 +297,8 @@ private void removePlayer(Room room, String sessionId, Player removePlayer) {
255297
room.removeUserId(removePlayer.getId());
256298
room.removeSessionId(sessionId);
257299
}
300+
301+
private RoundResult buildResultOnlyChat(ChatMessage chatMessage) {
302+
return RoundResult.builder().chat(chatMessage).build();
303+
}
258304
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package io.f1.backend.domain.game.dto;
2+
3+
import java.time.Instant;
4+
5+
public record ChatMessage(String nickname, String message, Instant timestamp) {}

0 commit comments

Comments
ย (0)