-
Notifications
You must be signed in to change notification settings - Fork 1
[feat] 관리자 페이지 구현 완료 #94
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
78e26b5
b33d662
f44612d
636a8f7
1fe3017
d222d74
8db4eb2
1e364d6
06e632e
508da9e
7018246
6ed14a8
8a3f8f1
018ac86
742ddc0
14a2dfc
5e45154
918daae
2fd6ff4
ab7d325
8131c46
e443229
659dbf1
82b8fbe
be4878a
f7731de
7ecf278
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| package com.wayble.server.admin.controller; | ||
|
|
||
| import com.wayble.server.admin.dto.SystemStatusDto; | ||
| import com.wayble.server.admin.service.AdminSystemService; | ||
| import com.wayble.server.admin.service.AdminUserService; | ||
| import com.wayble.server.admin.service.AdminWaybleZoneService; | ||
| import jakarta.servlet.http.HttpSession; | ||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.beans.factory.annotation.Value; | ||
| import org.springframework.stereotype.Controller; | ||
| import org.springframework.ui.Model; | ||
| import org.springframework.web.bind.annotation.GetMapping; | ||
| import org.springframework.web.bind.annotation.PostMapping; | ||
| import org.springframework.web.bind.annotation.RequestMapping; | ||
| import org.springframework.web.bind.annotation.RequestParam; | ||
|
|
||
| @Slf4j | ||
| @Controller | ||
| @RequestMapping("/admin") | ||
| @RequiredArgsConstructor | ||
| public class AdminController { | ||
|
|
||
| private final AdminSystemService adminSystemService; | ||
| private final AdminUserService adminUserService; | ||
| private final AdminWaybleZoneService adminWaybleZoneService; | ||
|
|
||
| @Value("${spring.admin.username}") | ||
| private String adminUsername; | ||
|
|
||
| @Value("${spring.admin.password}") | ||
| private String adminPassword; | ||
|
|
||
| @GetMapping("") | ||
| public String adminLoginPage(HttpSession session, Model model) { | ||
| // 이미 로그인된 경우 대시보드로 리다이렉트 | ||
| if (session.getAttribute("adminLoggedIn") != null) { | ||
| return "redirect:/admin/dashboard"; | ||
| } | ||
| return "admin/login"; | ||
| } | ||
|
|
||
| @PostMapping("/login") | ||
| public String adminLogin( | ||
| @RequestParam String username, | ||
| @RequestParam String password, | ||
| HttpSession session, | ||
| Model model | ||
| ) { | ||
| if (adminUsername.equals(username) && adminPassword.equals(password)) { | ||
| session.setAttribute("adminLoggedIn", true); | ||
| session.setAttribute("adminUsername", username); | ||
| log.info("관리자 로그인 성공: {}", username); | ||
| return "redirect:/admin/dashboard"; | ||
| } else { | ||
| model.addAttribute("error", "잘못된 사용자명 또는 비밀번호입니다."); | ||
| log.warn("관리자 로그인 실패 시도: {}", username); | ||
| return "admin/login"; | ||
| } | ||
| } | ||
|
|
||
| @GetMapping("/dashboard") | ||
| public String adminDashboard(HttpSession session, Model model) { | ||
| // 로그인 확인 | ||
| if (session.getAttribute("adminLoggedIn") == null) { | ||
| return "redirect:/admin"; | ||
| } | ||
|
|
||
| // 시스템 상태 조회 | ||
| SystemStatusDto systemStatus = SystemStatusDto.of( | ||
| adminSystemService.isApiServerHealthy(), | ||
| adminSystemService.isDatabaseHealthy(), | ||
| adminSystemService.isElasticsearchHealthy(), | ||
| adminSystemService.isFileStorageHealthy() | ||
| ); | ||
|
|
||
| // 통계 데이터 조회 | ||
| long totalUserCount = adminUserService.getTotalUserCount(); | ||
| long totalDeletedUserCount = adminUserService.getTotalDeletedUserCount(); | ||
| long totalWaybleZoneCount = adminWaybleZoneService.getTotalWaybleZoneCounts(); | ||
|
|
||
| model.addAttribute("adminUsername", session.getAttribute("adminUsername")); | ||
| model.addAttribute("systemStatus", systemStatus); | ||
| model.addAttribute("totalUserCount", totalUserCount); | ||
| model.addAttribute("totalDeletedUserCount", totalDeletedUserCount); | ||
| model.addAttribute("totalWaybleZoneCount", totalWaybleZoneCount); | ||
| return "admin/dashboard"; | ||
| } | ||
|
|
||
| @PostMapping("/logout") | ||
| public String adminLogout(HttpSession session) { | ||
| session.invalidate(); | ||
| log.info("관리자 로그아웃"); | ||
| return "redirect:/admin"; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| package com.wayble.server.admin.controller.user; | ||
|
|
||
| import com.wayble.server.admin.dto.user.AdminUserDetailDto; | ||
| import com.wayble.server.admin.dto.user.AdminUserThumbnailDto; | ||
| import com.wayble.server.admin.service.AdminUserService; | ||
| import com.wayble.server.common.response.CommonResponse; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.validation.annotation.Validated; | ||
| import org.springframework.web.bind.annotation.*; | ||
|
|
||
| import java.util.List; | ||
| import java.util.Optional; | ||
|
|
||
| @RestController | ||
| @RequiredArgsConstructor | ||
| @Validated | ||
| @RequestMapping("/api/v1/admin/users") | ||
| public class AdminUserController { | ||
|
|
||
| private final AdminUserService adminUserService; | ||
|
|
||
| @GetMapping("/count") | ||
| public CommonResponse<Long> getTotalUserCount() { | ||
| return CommonResponse.success(adminUserService.getTotalUserCount()); | ||
| } | ||
|
|
||
| @GetMapping() | ||
| public CommonResponse<List<AdminUserThumbnailDto>> findByCondition( | ||
| @RequestParam(name = "page", defaultValue = "0") int page, | ||
| @RequestParam(name = "size", defaultValue = "100") int size) | ||
| { | ||
| return CommonResponse.success(adminUserService.findUsersByPage(page, size)); | ||
| } | ||
|
|
||
| @GetMapping("/{userId}") | ||
| public CommonResponse<Optional<AdminUserDetailDto>> findUserById(@PathVariable("userId") long userId) { | ||
| return CommonResponse.success(adminUserService.findUserById(userId)); | ||
| } | ||
|
Comment on lines
+35
to
+38
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion REST API에서 Optional 반환 재고 필요 REST API 응답에서 Optional을 직접 반환하는 것은 일반적이지 않습니다. 클라이언트 입장에서는 명확한 응답 구조가 더 유용합니다. - public CommonResponse<Optional<AdminUserDetailDto>> findUserById(@PathVariable("userId") long userId) {
- return CommonResponse.success(adminUserService.findUserById(userId));
+ public CommonResponse<AdminUserDetailDto> findUserById(@PathVariable("userId") long userId) {
+ return adminUserService.findUserById(userId)
+ .map(CommonResponse::success)
+ .orElseThrow(() -> new ApplicationException(AdminErrorCase.USER_NOT_FOUND));
}Also applies to: 53-56 🤖 Prompt for AI Agents |
||
|
|
||
| @GetMapping("/deleted/count") | ||
| public CommonResponse<Long> getTotalDeletedUserCount() { | ||
| return CommonResponse.success(adminUserService.getTotalDeletedUserCount()); | ||
| } | ||
|
|
||
| @GetMapping("/deleted") | ||
| public CommonResponse<List<AdminUserThumbnailDto>> findDeletedUsers( | ||
| @RequestParam(name = "page", defaultValue = "0") int page, | ||
| @RequestParam(name = "size", defaultValue = "100") int size) | ||
| { | ||
| return CommonResponse.success(adminUserService.findDeletedUsersByPage(page, size)); | ||
| } | ||
|
|
||
| @GetMapping("/deleted/{userId}") | ||
| public CommonResponse<Optional<AdminUserDetailDto>> findDeletedUserById(@PathVariable("userId") long userId) { | ||
| return CommonResponse.success(adminUserService.findDeletedUserById(userId)); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,124 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package com.wayble.server.admin.controller.user; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.wayble.server.admin.dto.user.AdminUserDetailDto; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.wayble.server.admin.dto.user.AdminUserPageDto; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.wayble.server.admin.service.AdminUserService; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import jakarta.servlet.http.HttpSession; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import lombok.RequiredArgsConstructor; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import lombok.extern.slf4j.Slf4j; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.stereotype.Controller; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.ui.Model; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.web.bind.annotation.*; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.web.servlet.mvc.support.RedirectAttributes; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.Optional; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Slf4j | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Controller | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @RequestMapping("/admin/users") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @RequiredArgsConstructor | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public class AdminUserViewController { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final AdminUserService adminUserService; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @GetMapping | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public String getUsers(HttpSession session, Model model, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @RequestParam(defaultValue = "0") int page, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @RequestParam(defaultValue = "100") int size) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 로그인 확인 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (session.getAttribute("adminLoggedIn") == null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return "redirect:/admin"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+29
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 인증 로직 중복 제거를 위한 리팩토링 고려 모든 메서드에서 동일한 세션 인증 체크 로직이 반복됩니다. 다음 방법들을 고려해보세요: 방법 1: 인터셉터 사용 @Component
public class AdminAuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
HttpSession session = request.getSession();
if (session.getAttribute("adminLoggedIn") == null) {
response.sendRedirect("/admin");
return false;
}
return true;
}
}방법 2: 메서드 레벨 보안 Also applies to: 47-48, 69-70, 87-88, 109-110 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 페이징 데이터 조회 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AdminUserPageDto pageData = adminUserService.getUsersWithPaging(page, size); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| model.addAttribute("pageData", pageData); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| model.addAttribute("adminUsername", session.getAttribute("adminUsername")); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.debug("사용자 목록 조회 - 페이지: {}, 전체: {}", page, pageData.totalElements()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return "admin/user/users"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @GetMapping("/{id}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public String getUserDetail(HttpSession session, Model model, @PathVariable Long id) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 로그인 확인 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (session.getAttribute("adminLoggedIn") == null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return "redirect:/admin"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Optional<AdminUserDetailDto> userOpt = adminUserService.findUserById(id); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (userOpt.isEmpty()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return "redirect:/admin/user/users?error=notfound"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| model.addAttribute("user", userOpt.get()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| model.addAttribute("adminUsername", session.getAttribute("adminUsername")); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.debug("사용자 상세 조회 - ID: {}", id); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return "admin/user/user-detail"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+44
to
+62
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 리다이렉트 URL 오류 수정 필요 사용자를 찾을 수 없을 때의 리다이렉트 URL이 잘못되었습니다. - return "redirect:/admin/user/users?error=notfound";
+ return "redirect:/admin/users?error=notfound";📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @GetMapping("/deleted") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public String getDeletedUsers(HttpSession session, Model model, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @RequestParam(defaultValue = "0") int page, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @RequestParam(defaultValue = "100") int size) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 로그인 확인 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (session.getAttribute("adminLoggedIn") == null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return "redirect:/admin"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 페이징 데이터 조회 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AdminUserPageDto pageData = adminUserService.getDeletedUsersWithPaging(page, size); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| model.addAttribute("pageData", pageData); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| model.addAttribute("adminUsername", session.getAttribute("adminUsername")); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.debug("삭제된 사용자 목록 조회 - 페이지: {}, 전체: {}", page, pageData.totalElements()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return "admin/user/deleted-users"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @GetMapping("/deleted/{id}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public String getDeletedUserDetail(HttpSession session, Model model, @PathVariable Long id) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 로그인 확인 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (session.getAttribute("adminLoggedIn") == null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return "redirect:/admin"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Optional<AdminUserDetailDto> userOpt = adminUserService.findDeletedUserById(id); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (userOpt.isEmpty()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return "redirect:/admin/users/deleted?error=notfound"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| model.addAttribute("user", userOpt.get()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| model.addAttribute("adminUsername", session.getAttribute("adminUsername")); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.debug("삭제된 사용자 상세 조회 - ID: {}", id); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return "admin/user/deleted-user-detail"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @PostMapping("/deleted/{id}/restore") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public String restoreUser(HttpSession session, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @PathVariable Long id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RedirectAttributes redirectAttributes) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 로그인 확인 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (session.getAttribute("adminLoggedIn") == null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return "redirect:/admin"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| adminUserService.restoreUser(id); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| redirectAttributes.addFlashAttribute("successMessage", "사용자가 성공적으로 복원되었습니다."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.info("사용자 복원 완료 - ID: {}, 관리자: {}", id, session.getAttribute("adminUsername")); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return "redirect:/admin/users/" + id; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (Exception e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.error("사용자 복원 실패 - ID: {}", id, e); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| redirectAttributes.addFlashAttribute("errorMessage", "사용자 복원에 실패했습니다: " + e.getMessage()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return "redirect:/admin/users/deleted/" + id; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,73 @@ | ||||||||||||||||||||||
| package com.wayble.server.admin.controller.wayblezone; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| import com.wayble.server.admin.dto.wayblezone.AdminWaybleZoneCreateDto; | ||||||||||||||||||||||
| import com.wayble.server.admin.dto.wayblezone.AdminWaybleZoneDetailDto; | ||||||||||||||||||||||
| import com.wayble.server.admin.dto.wayblezone.AdminWaybleZoneThumbnailDto; | ||||||||||||||||||||||
| import com.wayble.server.admin.dto.wayblezone.AdminWaybleZoneUpdateDto; | ||||||||||||||||||||||
| import com.wayble.server.admin.service.AdminWaybleZoneService; | ||||||||||||||||||||||
| import com.wayble.server.common.response.CommonResponse; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| import jakarta.validation.Valid; | ||||||||||||||||||||||
| import lombok.RequiredArgsConstructor; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| import org.springframework.validation.annotation.Validated; | ||||||||||||||||||||||
| import org.springframework.web.bind.annotation.*; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| import java.util.List; | ||||||||||||||||||||||
| import java.util.Optional; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| @RestController | ||||||||||||||||||||||
| @RequiredArgsConstructor | ||||||||||||||||||||||
| @Validated | ||||||||||||||||||||||
| @RequestMapping("/api/v1/admin/wayble-zones") | ||||||||||||||||||||||
| public class AdminWaybleZoneController { | ||||||||||||||||||||||
|
Comment on lines
+19
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 관리자 API 보안 설정 누락 관리자 전용 REST API이지만 인증/인가 메커니즘이 없습니다. Spring Security나 커스텀 인터셉터를 통해 보안을 강화해야 합니다. 다음과 같은 보안 설정을 추가하세요: @RestController
@RequiredArgsConstructor
@Validated
@RequestMapping("/api/v1/admin/wayble-zones")
@PreAuthorize("hasRole('ADMIN')") // Spring Security 사용 시
public class AdminWaybleZoneController {🤖 Prompt for AI Agents |
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| private final AdminWaybleZoneService adminWaybleZoneService; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| @GetMapping() | ||||||||||||||||||||||
| public CommonResponse<List<AdminWaybleZoneThumbnailDto>> findByCondition( | ||||||||||||||||||||||
| @RequestParam(name = "page", defaultValue = "0") int page, | ||||||||||||||||||||||
| @RequestParam(name = "size", defaultValue = "20") int size) | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| return CommonResponse.success(adminWaybleZoneService.findWaybleZonesByPage(page, size)); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| @GetMapping("/count") | ||||||||||||||||||||||
| public CommonResponse<Long> findAllWaybleZoneCount() { | ||||||||||||||||||||||
| return CommonResponse.success(adminWaybleZoneService.getTotalWaybleZoneCounts()); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| @GetMapping("/{waybleZoneId}") | ||||||||||||||||||||||
| public CommonResponse<Optional<AdminWaybleZoneDetailDto>> findWaybleZoneById(@PathVariable("waybleZoneId") long waybleZoneId) { | ||||||||||||||||||||||
| return CommonResponse.success(adminWaybleZoneService.findWaybleZoneById(waybleZoneId)); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
Comment on lines
+40
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Optional을 API 응답으로 직접 반환하지 마세요 REST API에서 Optional을 직접 반환하는 것은 권장되지 않습니다. 값이 없을 때 적절한 HTTP 상태 코드를 반환하도록 수정하세요. @GetMapping("/{waybleZoneId}")
-public CommonResponse<Optional<AdminWaybleZoneDetailDto>> findWaybleZoneById(@PathVariable("waybleZoneId") long waybleZoneId) {
- return CommonResponse.success(adminWaybleZoneService.findWaybleZoneById(waybleZoneId));
+public CommonResponse<AdminWaybleZoneDetailDto> findWaybleZoneById(@PathVariable("waybleZoneId") Long waybleZoneId) {
+ return adminWaybleZoneService.findWaybleZoneById(waybleZoneId)
+ .map(CommonResponse::success)
+ .orElseThrow(() -> new ApplicationException(AdminErrorCase.WAYBLE_ZONE_NOT_FOUND));
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| @PostMapping | ||||||||||||||||||||||
| public CommonResponse<Long> createWaybleZone(@Valid @RequestBody AdminWaybleZoneCreateDto createDto) { | ||||||||||||||||||||||
| Long waybleZoneId = adminWaybleZoneService.createWaybleZone(createDto); | ||||||||||||||||||||||
| return CommonResponse.success(waybleZoneId); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| @PutMapping("/{waybleZoneId}") | ||||||||||||||||||||||
| public CommonResponse<Long> updateWaybleZone(@PathVariable("waybleZoneId") Long waybleZoneId, | ||||||||||||||||||||||
| @Valid @RequestBody AdminWaybleZoneUpdateDto updateDto) { | ||||||||||||||||||||||
| // DTO의 ID와 URL의 ID가 일치하는지 확인 | ||||||||||||||||||||||
| AdminWaybleZoneUpdateDto validatedDto = new AdminWaybleZoneUpdateDto( | ||||||||||||||||||||||
| waybleZoneId, | ||||||||||||||||||||||
| updateDto.zoneName(), | ||||||||||||||||||||||
| updateDto.contactNumber(), | ||||||||||||||||||||||
| updateDto.zoneType(), | ||||||||||||||||||||||
| updateDto.state(), | ||||||||||||||||||||||
| updateDto.city(), | ||||||||||||||||||||||
| updateDto.district(), | ||||||||||||||||||||||
| updateDto.streetAddress(), | ||||||||||||||||||||||
| updateDto.detailAddress(), | ||||||||||||||||||||||
| updateDto.latitude(), | ||||||||||||||||||||||
| updateDto.longitude(), | ||||||||||||||||||||||
| updateDto.mainImageUrl() | ||||||||||||||||||||||
| ); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Long updatedWaybleZoneId = adminWaybleZoneService.updateWaybleZone(validatedDto); | ||||||||||||||||||||||
| return CommonResponse.success(updatedWaybleZoneId); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
평문 비밀번호 비교는 보안상 위험합니다.
평문으로 비밀번호를 비교하고 있습니다. 프로덕션 환경에서는 해시된 비밀번호를 사용해야 합니다.
다음과 같이 개선하세요:
그리고
PasswordEncoder의존성을 추가하세요:🤖 Prompt for AI Agents