Skip to content

Commit 6005b83

Browse files
authored
๐Ÿ”– release: v0.0.4
2 parents 33185b2 + 0597d47 commit 6005b83

File tree

17 files changed

+327
-17
lines changed

17 files changed

+327
-17
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/question/app/QuestionService.javaโ€Ž

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,18 @@
1010
import io.f1.backend.domain.question.entity.TextQuestion;
1111
import io.f1.backend.domain.quiz.entity.Quiz;
1212
import io.f1.backend.global.exception.CustomException;
13+
import io.f1.backend.global.exception.errorcode.AuthErrorCode;
1314
import io.f1.backend.global.exception.errorcode.QuestionErrorCode;
15+
import io.f1.backend.global.security.enums.Role;
16+
import io.f1.backend.global.util.SecurityUtils;
1417

1518
import lombok.RequiredArgsConstructor;
1619

1720
import org.springframework.stereotype.Service;
1821
import org.springframework.transaction.annotation.Transactional;
1922

23+
import java.util.Objects;
24+
2025
@Service
2126
@RequiredArgsConstructor
2227
public class QuestionService {
@@ -47,10 +52,21 @@ public void updateQuestionContent(Long questionId, String content) {
4752
.orElseThrow(
4853
() -> new CustomException(QuestionErrorCode.QUESTION_NOT_FOUND));
4954

55+
verifyUserAuthority(question.getQuiz());
56+
5057
TextQuestion textQuestion = question.getTextQuestion();
5158
textQuestion.changeContent(content);
5259
}
5360

61+
private static void verifyUserAuthority(Quiz quiz) {
62+
if (SecurityUtils.getCurrentUserRole() == Role.ADMIN) {
63+
return;
64+
}
65+
if (!Objects.equals(SecurityUtils.getCurrentUserId(), quiz.getCreator().getId())) {
66+
throw new CustomException(AuthErrorCode.FORBIDDEN);
67+
}
68+
}
69+
5470
@Transactional
5571
public void updateQuestionAnswer(Long questionId, String answer) {
5672

@@ -62,6 +78,8 @@ public void updateQuestionAnswer(Long questionId, String answer) {
6278
.orElseThrow(
6379
() -> new CustomException(QuestionErrorCode.QUESTION_NOT_FOUND));
6480

81+
verifyUserAuthority(question.getQuiz());
82+
6583
question.changeAnswer(answer);
6684
}
6785

@@ -74,6 +92,8 @@ public void deleteQuestion(Long questionId) {
7492
.orElseThrow(
7593
() -> new CustomException(QuestionErrorCode.QUESTION_NOT_FOUND));
7694

95+
verifyUserAuthority(question.getQuiz());
96+
7797
questionRepository.delete(question);
7898
}
7999

โ€Žbackend/src/main/java/io/f1/backend/domain/quiz/app/QuizService.javaโ€Ž

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
import io.f1.backend.global.exception.CustomException;
2121
import io.f1.backend.global.exception.errorcode.AuthErrorCode;
2222
import io.f1.backend.global.exception.errorcode.QuizErrorCode;
23+
import io.f1.backend.global.exception.errorcode.UserErrorCode;
24+
import io.f1.backend.global.security.enums.Role;
25+
import io.f1.backend.global.util.SecurityUtils;
2326

2427
import lombok.RequiredArgsConstructor;
2528
import lombok.extern.slf4j.Slf4j;
@@ -37,6 +40,7 @@
3740
import java.nio.file.Paths;
3841
import java.util.List;
3942
import java.util.NoSuchElementException;
43+
import java.util.Objects;
4044
import java.util.UUID;
4145

4246
@Slf4j
@@ -53,7 +57,6 @@ public class QuizService {
5357
private final String DEFAULT = "default";
5458
private static final long MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
5559

56-
// TODO : ์‹œํ๋ฆฌํ‹ฐ ๊ตฌํ˜„ ์ดํ›„ ์‚ญ์ œํ•ด๋„ ๋˜๋Š” ์˜์กด์„ฑ ์ฃผ์ž…
5760
private final UserRepository userRepository;
5861
private final QuestionService questionService;
5962
private final QuizRepository quizRepository;
@@ -67,10 +70,13 @@ public QuizCreateResponse saveQuiz(MultipartFile thumbnailFile, QuizCreateReques
6770
thumbnailPath = convertToThumbnailPath(thumbnailFile);
6871
}
6972

70-
// TODO : ์‹œํ๋ฆฌํ‹ฐ ๊ตฌํ˜„ ์ดํ›„ ์‚ญ์ œ (data.sql๋กœ ์ดˆ๊ธฐ ์ €์žฅํ•ด๋‘” ์œ ์ € get), ๋‚˜์ค‘์—” ํ˜„์žฌ ๋กœ๊ทธ์ธํ•œ ์œ ์ €์˜ ์•„์ด๋””๋ฅผ ๋ฐ›์•„์˜ค๋„๋ก ์ˆ˜์ •
71-
User user = userRepository.findById(1L).orElseThrow(RuntimeException::new);
73+
Long creatorId = SecurityUtils.getCurrentUserId();
74+
User creator =
75+
userRepository
76+
.findById(creatorId)
77+
.orElseThrow(() -> new CustomException(UserErrorCode.USER_NOT_FOUND));
7278

73-
Quiz quiz = quizCreateRequestToQuiz(request, thumbnailPath, user);
79+
Quiz quiz = quizCreateRequestToQuiz(request, thumbnailPath, creator);
7480

7581
Quiz savedQuiz = quizRepository.save(quiz);
7682

@@ -126,22 +132,30 @@ public void deleteQuiz(Long quizId) {
126132
.findById(quizId)
127133
.orElseThrow(() -> new CustomException(QuizErrorCode.QUIZ_NOT_FOUND));
128134

129-
// TODO : util ๋ฉ”์„œ๋“œ์—์„œ ์‚ฌ์šฉ์ž ID ๊บผ๋‚ด์“ฐ๋Š” ์‹์œผ๋กœ ์ˆ˜์ •ํ•˜๊ธฐ
130-
if (1L != quiz.getCreator().getId()) {
131-
throw new CustomException(AuthErrorCode.FORBIDDEN);
132-
}
135+
verifyUserAuthority(quiz);
133136

134137
deleteThumbnailFile(quiz.getThumbnailUrl());
135138
quizRepository.deleteById(quizId);
136139
}
137140

141+
private static void verifyUserAuthority(Quiz quiz) {
142+
if (SecurityUtils.getCurrentUserRole() == Role.ADMIN) {
143+
return;
144+
}
145+
if (!Objects.equals(SecurityUtils.getCurrentUserId(), quiz.getCreator().getId())) {
146+
throw new CustomException(AuthErrorCode.FORBIDDEN);
147+
}
148+
}
149+
138150
@Transactional
139151
public void updateQuizTitle(Long quizId, String title) {
140152
Quiz quiz =
141153
quizRepository
142154
.findById(quizId)
143155
.orElseThrow(() -> new CustomException(QuizErrorCode.QUIZ_NOT_FOUND));
144156

157+
verifyUserAuthority(quiz);
158+
145159
validateTitle(title);
146160
quiz.changeTitle(title);
147161
}
@@ -154,6 +168,8 @@ public void updateQuizDesc(Long quizId, String description) {
154168
.findById(quizId)
155169
.orElseThrow(() -> new CustomException(QuizErrorCode.QUIZ_NOT_FOUND));
156170

171+
verifyUserAuthority(quiz);
172+
157173
validateDesc(description);
158174
quiz.changeDescription(description);
159175
}
@@ -166,6 +182,8 @@ public void updateThumbnail(Long quizId, MultipartFile thumbnailFile) {
166182
.findById(quizId)
167183
.orElseThrow(() -> new CustomException(QuizErrorCode.QUIZ_NOT_FOUND));
168184

185+
verifyUserAuthority(quiz);
186+
169187
validateImageFile(thumbnailFile);
170188
String newThumbnailPath = convertToThumbnailPath(thumbnailFile);
171189

@@ -257,8 +275,6 @@ public List<Question> getRandomQuestionsWithoutAnswer(Long quizId, Integer round
257275
.findById(quizId)
258276
.orElseThrow(() -> new NoSuchElementException("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํ€ด์ฆˆ์ž…๋‹ˆ๋‹ค."));
259277

260-
List<Question> randomQuestions = quizRepository.findRandQuestionsByQuizId(quizId, round);
261-
262-
return randomQuestions;
278+
return quizRepository.findRandQuestionsByQuizId(quizId, round);
263279
}
264280
}

โ€Žbackend/src/main/java/io/f1/backend/domain/quiz/mapper/QuizMapper.javaโ€Ž

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,18 @@
1818

1919
public class QuizMapper {
2020

21-
// TODO : ์ดํ›„ ํŒŒ๋ผ๋ฏธํ„ฐ์—์„œ user ์‚ญ์ œํ•˜๊ธฐ
2221
public static Quiz quizCreateRequestToQuiz(
23-
QuizCreateRequest quizCreateRequest, String imgUrl, User user) {
22+
QuizCreateRequest quizCreateRequest, String imgUrl, User creator) {
2423

2524
return new Quiz(
2625
quizCreateRequest.getTitle(),
2726
quizCreateRequest.getDescription(),
2827
quizCreateRequest.getQuizType(),
2928
imgUrl,
30-
user // TODO : ์ดํ›„ creator์— ๋“ค์–ด๊ฐˆ User์€ ํ˜„์žฌ ๋กœ๊ทธ์ธ ์ค‘์ธ ์œ ์ €๋ฅผ ๊ฐ€์ ธ์˜ค๋„๋ก ๋ณ€๊ฒฝ
31-
);
29+
creator);
3230
}
3331

3432
public static QuizCreateResponse quizToQuizCreateResponse(Quiz quiz) {
35-
// TODO : creatorId ๋„ฃ์–ด์ฃผ๋Š” ๋ถ€๋ถ„์—์„œ Getter๋ฅผ ์•ˆ ์“ฐ๊ณ , ํ˜„์žฌ ๋กœ๊ทธ์ธํ•œ ์œ ์ €์˜ id๋ฅผ ๋‹ด๋Š” ์‹์œผ๋กœ ๋ฐ”๊ฟ”๋„ ๋  ๋“ฏ
3633
return new QuizCreateResponse(
3734
quiz.getId(),
3835
quiz.getTitle(),

โ€Žbackend/src/main/java/io/f1/backend/domain/user/dao/UserRepository.javaโ€Ž

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
package io.f1.backend.domain.user.dao;
22

3+
import io.f1.backend.domain.admin.dto.UserResponse;
34
import io.f1.backend.domain.user.entity.User;
45

6+
import org.springframework.data.domain.Page;
7+
import org.springframework.data.domain.Pageable;
58
import org.springframework.data.jpa.repository.JpaRepository;
9+
import org.springframework.data.jpa.repository.Query;
610
import org.springframework.stereotype.Repository;
711

812
import java.util.Optional;
@@ -13,4 +17,9 @@ public interface UserRepository extends JpaRepository<User, Long> {
1317
Optional<User> findByProviderAndProviderId(String provider, String providerId);
1418

1519
Boolean existsUserByNickname(String nickname);
20+
21+
@Query(
22+
"SELECT new io.f1.backend.domain.admin.dto.UserResponse(u.id, u.nickname, u.lastLogin,"
23+
+ " u.createdAt)FROM User u ORDER BY u.id")
24+
Page<UserResponse> findAllUsersWithPaging(Pageable pageable);
1625
}

0 commit comments

Comments
ย (0)