Skip to content

Commit 18a6cb5

Browse files
committed
Merge remote-tracking branch 'origin/dev' into refactor/86
# Conflicts: # backend/src/main/java/io/f1/backend/domain/game/websocket/GameSocketController.java
2 parents 7e11821 + 83554bb commit 18a6cb5

File tree

23 files changed

+384
-52
lines changed

23 files changed

+384
-52
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package io.f1.backend.domain.admin.api;
2+
3+
import io.f1.backend.domain.admin.app.AdminService;
4+
import io.f1.backend.domain.admin.dto.UserPageResponse;
5+
import io.f1.backend.global.validation.LimitPageSize;
6+
7+
import lombok.RequiredArgsConstructor;
8+
9+
import org.springframework.data.domain.Pageable;
10+
import org.springframework.http.ResponseEntity;
11+
import org.springframework.web.bind.annotation.GetMapping;
12+
import org.springframework.web.bind.annotation.RequestMapping;
13+
import org.springframework.web.bind.annotation.RestController;
14+
15+
@RestController
16+
@RequestMapping("/admin")
17+
@RequiredArgsConstructor
18+
public class AdminController {
19+
20+
private final AdminService adminService;
21+
22+
@LimitPageSize
23+
@GetMapping("/users")
24+
public ResponseEntity<UserPageResponse> getUsers(Pageable pageable) {
25+
UserPageResponse response = adminService.getAllUsers(pageable);
26+
return ResponseEntity.ok().body(response);
27+
}
28+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package io.f1.backend.domain.admin.app;
2+
3+
import static io.f1.backend.domain.admin.mapper.AdminMapper.toUserListPageResponse;
4+
5+
import io.f1.backend.domain.admin.dto.UserPageResponse;
6+
import io.f1.backend.domain.admin.dto.UserResponse;
7+
import io.f1.backend.domain.user.dao.UserRepository;
8+
9+
import lombok.RequiredArgsConstructor;
10+
11+
import org.springframework.data.domain.Page;
12+
import org.springframework.data.domain.Pageable;
13+
import org.springframework.stereotype.Service;
14+
import org.springframework.transaction.annotation.Transactional;
15+
16+
@Service
17+
@RequiredArgsConstructor
18+
public class AdminService {
19+
20+
private final UserRepository userRepository;
21+
22+
@Transactional(readOnly = true)
23+
public UserPageResponse getAllUsers(Pageable pageable) {
24+
Page<UserResponse> users = userRepository.findAllUsersWithPaging(pageable);
25+
return toUserListPageResponse(users);
26+
}
27+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package io.f1.backend.domain.admin.dto;
2+
3+
import java.util.List;
4+
5+
public record UserPageResponse(
6+
int totalPages, int currentPage, int totalElements, List<UserResponse> users) {}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package io.f1.backend.domain.admin.dto;
2+
3+
import java.time.LocalDateTime;
4+
5+
public record UserResponse(
6+
Long id, String nickname, LocalDateTime lastLogin, LocalDateTime createdAt) {}

backend/src/main/java/io/f1/backend/domain/admin/entity/Admin.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@
88
import jakarta.persistence.GenerationType;
99
import jakarta.persistence.Id;
1010

11+
import lombok.Builder;
1112
import lombok.Getter;
13+
import lombok.NoArgsConstructor;
1214

1315
import java.time.LocalDateTime;
1416

1517
@Entity
1618
@Getter
19+
@NoArgsConstructor
1720
public class Admin extends BaseEntity {
1821

1922
@Id
@@ -32,4 +35,12 @@ public class Admin extends BaseEntity {
3235
public void updateLastLogin(LocalDateTime lastLogin) {
3336
this.lastLogin = lastLogin;
3437
}
38+
39+
@Builder
40+
public Admin(Long id, String username, String password, LocalDateTime lastLogin) {
41+
this.id = id;
42+
this.username = username;
43+
this.password = password;
44+
this.lastLogin = LocalDateTime.now();
45+
}
3546
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package io.f1.backend.domain.admin.mapper;
2+
3+
import io.f1.backend.domain.admin.dto.UserPageResponse;
4+
import io.f1.backend.domain.admin.dto.UserResponse;
5+
6+
import org.springframework.data.domain.Page;
7+
8+
public class AdminMapper {
9+
10+
private AdminMapper() {}
11+
12+
public static UserPageResponse toUserListPageResponse(Page<UserResponse> userPage) {
13+
int curPage = userPage.getNumber() + 1;
14+
15+
return new UserPageResponse(
16+
userPage.getTotalPages(),
17+
curPage,
18+
userPage.getNumberOfElements(),
19+
userPage.getContent());
20+
}
21+
}

backend/src/main/java/io/f1/backend/domain/game/app/GameService.java

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,16 @@
22

33
import static io.f1.backend.domain.quiz.mapper.QuizMapper.toGameStartResponse;
44

5-
import io.f1.backend.domain.game.dto.request.GameStartRequest;
65
import io.f1.backend.domain.game.dto.response.GameStartResponse;
76
import io.f1.backend.domain.game.event.RoomUpdatedEvent;
8-
import io.f1.backend.domain.game.model.GameSetting;
97
import io.f1.backend.domain.game.model.Player;
108
import io.f1.backend.domain.game.model.Room;
119
import io.f1.backend.domain.game.model.RoomState;
1210
import io.f1.backend.domain.game.store.RoomRepository;
1311
import io.f1.backend.domain.question.entity.Question;
1412
import io.f1.backend.domain.quiz.app.QuizService;
1513
import io.f1.backend.domain.quiz.entity.Quiz;
14+
import io.f1.backend.domain.user.dto.UserPrincipal;
1615
import io.f1.backend.global.exception.CustomException;
1716
import io.f1.backend.global.exception.errorcode.GameErrorCode;
1817
import io.f1.backend.global.exception.errorcode.RoomErrorCode;
@@ -24,6 +23,7 @@
2423

2524
import java.util.List;
2625
import java.util.Map;
26+
import java.util.Objects;
2727

2828
@Service
2929
@RequiredArgsConstructor
@@ -33,53 +33,54 @@ public class GameService {
3333
private final RoomRepository roomRepository;
3434
private final ApplicationEventPublisher eventPublisher;
3535

36-
public GameStartResponse gameStart(Long roomId, GameStartRequest gameStartRequest) {
37-
38-
Long quizId = gameStartRequest.quizId();
36+
public GameStartResponse gameStart(Long roomId, UserPrincipal principal) {
3937

4038
Room room =
4139
roomRepository
4240
.findRoom(roomId)
4341
.orElseThrow(() -> new CustomException(RoomErrorCode.ROOM_NOT_FOUND));
4442

45-
if (!validateReadyStatus(room)) {
46-
throw new CustomException(RoomErrorCode.PLAYER_NOT_READY);
47-
}
48-
49-
// 방의 gameSetting에 설정된 퀴즈랑 요청 퀴즈랑 같은지 체크 후 GameSetting에서 라운드 가져오기
50-
Integer round = checkGameSetting(room, quizId);
43+
validateRoomStart(room, principal);
5144

45+
Long quizId = room.getGameSetting().getQuizId();
5246
Quiz quiz = quizService.getQuizWithQuestionsById(quizId);
47+
List<Question> questions = prepareQuestions(room, quiz);
5348

54-
// 라운드 수만큼 랜덤 Question 추출
55-
List<Question> questions = quizService.getRandomQuestionsWithoutAnswer(quizId, round);
5649
room.updateQuestions(questions);
5750

58-
GameStartResponse gameStartResponse = toGameStartResponse(questions);
59-
6051
// 방 정보 게임 중으로 변경
6152
room.updateRoomState(RoomState.PLAYING);
6253

6354
eventPublisher.publishEvent(new RoomUpdatedEvent(room, quiz));
6455

65-
return gameStartResponse;
56+
return toGameStartResponse(questions);
6657
}
6758

68-
private Integer checkGameSetting(Room room, Long quizId) {
69-
70-
GameSetting gameSetting = room.getGameSetting();
59+
private boolean validateReadyStatus(Room room) {
7160

72-
if (!gameSetting.validateQuizId(quizId)) {
73-
throw new CustomException(GameErrorCode.GAME_SETTING_CONFLICT);
74-
}
61+
Map<String, Player> playerSessionMap = room.getPlayerSessionMap();
7562

76-
return gameSetting.getRound();
63+
return playerSessionMap.values().stream().allMatch(Player::isReady);
7764
}
7865

79-
private boolean validateReadyStatus(Room room) {
66+
private void validateRoomStart(Room room, UserPrincipal principal) {
67+
if (!Objects.equals(principal.getUserId(), room.getHost().getId())) {
68+
throw new CustomException(RoomErrorCode.NOT_ROOM_OWNER);
69+
}
8070

81-
Map<String, Player> playerSessionMap = room.getPlayerSessionMap();
71+
if (!validateReadyStatus(room)) {
72+
throw new CustomException(GameErrorCode.PLAYER_NOT_READY);
73+
}
8274

83-
return playerSessionMap.values().stream().allMatch(Player::isReady);
75+
if (room.getState() == RoomState.PLAYING) {
76+
throw new CustomException(RoomErrorCode.GAME_ALREADY_PLAYING);
77+
}
78+
}
79+
80+
// 라운드 수만큼 랜덤 Question 추출
81+
private List<Question> prepareQuestions(Room room, Quiz quiz) {
82+
Long quizId = quiz.getId();
83+
Integer round = room.getGameSetting().getRound();
84+
return quizService.getRandomQuestionsWithoutAnswer(quizId, round);
8485
}
8586
}

backend/src/main/java/io/f1/backend/domain/game/dto/request/GameStartRequest.java

Lines changed: 0 additions & 3 deletions
This file was deleted.

backend/src/main/java/io/f1/backend/domain/game/websocket/GameSocketController.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,12 @@
55
import io.f1.backend.domain.game.dto.ChatMessage;
66
import io.f1.backend.domain.game.dto.MessageType;
77
import io.f1.backend.domain.game.dto.request.DefaultWebSocketRequest;
8-
import io.f1.backend.domain.game.dto.request.GameStartRequest;
98
import io.f1.backend.domain.game.dto.response.GameStartResponse;
109
import io.f1.backend.domain.user.dto.UserPrincipal;
1110
import lombok.RequiredArgsConstructor;
1211
import org.springframework.messaging.Message;
1312
import org.springframework.messaging.handler.annotation.DestinationVariable;
1413
import org.springframework.messaging.handler.annotation.MessageMapping;
15-
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
16-
import org.springframework.security.core.Authentication;
1714
import org.springframework.stereotype.Controller;
1815

1916
@Controller
@@ -48,9 +45,13 @@ public void exitRoom(@DestinationVariable Long roomId, Message<?> message) {
4845
public void gameStart(
4946
@DestinationVariable Long roomId,
5047
Message<DefaultWebSocketRequest<GameStartRequest>> message) {
48+
public void gameStart(@DestinationVariable Long roomId, Message<?> message) {
5149

5250
GameStartResponse gameStartResponse =
5351
gameService.gameStart(roomId, message.getPayload().getMessage());
52+
UserPrincipal principal = getSessionUser(message);
53+
54+
GameStartResponse gameStartResponse = gameService.gameStart(roomId, principal);
5455

5556
String destination = getDestination(roomId);
5657

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package io.f1.backend.domain.game.websocket;
2+
3+
import io.f1.backend.domain.user.dto.UserPrincipal;
4+
5+
import org.springframework.messaging.Message;
6+
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
7+
import org.springframework.security.core.Authentication;
8+
9+
public class WebSocketUtils {
10+
11+
public static String getSessionId(Message<?> message) {
12+
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
13+
return accessor.getSessionId();
14+
}
15+
16+
public static UserPrincipal getSessionUser(Message<?> message) {
17+
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
18+
Authentication auth = (Authentication) accessor.getUser();
19+
return (UserPrincipal) auth.getPrincipal();
20+
}
21+
}

0 commit comments

Comments
 (0)