Repository: Team-Festimate/festimate-server
Pull request #106: [refactor] #105 RESTful URI 구조로 통일 및 Admin API 분리
Author: login: 2hyunjinn
Labels: name: refactor
description: This doesn't seem right
Created: 2025-05-05T12:43:59Z | Merged: 2025-05-05T12:58:45Z

## 📌 PR 제목
[refactor] #105 RESTful URI 구조로 통일 및 Admin API 분리

## 📌 PR 내용
- 전체 컨트롤러의 URI를 RESTful하게 통일하고, admin 전용 API를 별도 컨트롤러로 분리했습니다.

## 🛠 작업 내용
- [x]  /{festivalId}/participants/me 형태로 URI 구조 일관성 확보
- [x] Admin 전용 API를 AdminController로 분리
- [x] PointController 내 URI 일관성 확보
- [x] 중복되는 URI 패턴 정리 및 명확한 리소스 명명 적용

## 🔍 관련 이슈
Closes #105 

## 📸 스크린샷 (Optional)
<img width="823" alt="image" src="https://github.com/user-attachments/assets/4fb6c6d6-65a7-488d-b728-46927e43a18c" />

## 📚 레퍼런스 (Optional)
N/A

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Introduced new admin endpoints for managing festivals, participants, and points, including creating festivals, retrieving festival details, searching participants by nickname, recharging points, and viewing participant point history.

- **Refactor**
  - Updated and reorganized endpoint paths for participant and point operations to provide a more consistent and hierarchical URL structure.
  - Moved several data transfer objects to new package locations for improved organization.

- **Bug Fixes**
  - Removed admin-related endpoints from non-admin controllers to prevent unauthorized access and improve endpoint clarity.

- **Style**
  - Updated endpoint naming for verification to enhance clarity and consistency.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Linked issue #105: [refactor] RESTful URI 구조로 통일 및 Admin API 분리
## 🎯 어떤 기능인가요?

> 추가하려는 기능에 대해 간결하게 설명해주세요
> 참가자 관련 API의 URI 일관성이 부족한 문제를 해결하고자 합니다.

## 💡 해결 방법
현재 일부 엔드포인트는 /me, 일부는 /participants 또는 /entry 등으로 표현이 다르며, RESTful한 표현 방식과 일관성을 맞추는 리팩토링이 필요합니다.

## 🚀 추가 정보
N/A

Diff:
diff --git a/src/main/java/org/festimate/team/api/admin/AdminController.java b/src/main/java/org/festimate/team/api/admin/AdminController.java
new file mode 100644
index 0000000..299fe2c
--- /dev/null
+++ b/src/main/java/org/festimate/team/api/admin/AdminController.java
@@ -0,0 +1,89 @@
+package org.festimate.team.api.admin;
+
+import lombok.RequiredArgsConstructor;
+import org.festimate.team.api.admin.dto.*;
+import org.festimate.team.api.facade.FestivalFacade;
+import org.festimate.team.api.facade.ParticipantFacade;
+import org.festimate.team.api.facade.PointFacade;
+import org.festimate.team.api.point.dto.PointHistoryResponse;
+import org.festimate.team.api.point.dto.RechargePointRequest;
+import org.festimate.team.global.response.ApiResponse;
+import org.festimate.team.global.response.ResponseBuilder;
+import org.festimate.team.infra.jwt.JwtService;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/v1/admin/festivals")
+@RequiredArgsConstructor
+public class AdminController {
+    private final JwtService jwtService;
+    private final FestivalFacade festivalFacade;
+    private final ParticipantFacade participantFacade;
+    private final PointFacade pointFacade;
+
+    @PostMapping()
+    public ResponseEntity<ApiResponse<FestivalResponse>> createFestival(
+            @RequestHeader("Authorization") String accessToken,
+            @RequestBody FestivalRequest request
+    ) {
+        Long userId = jwtService.parseTokenAndGetUserId(accessToken);
+        FestivalResponse response = festivalFacade.createFestival(userId, request);
+        return ResponseBuilder.created(response);
+    }
+
+    @GetMapping()
+    public ResponseEntity<ApiResponse<List<AdminFestivalResponse>>> getAllFestivals(
+            @RequestHeader("Authorization") String accessToken
+    ) {
+        Long userId = jwtService.parseTokenAndGetUserId(accessToken);
+        List<AdminFestivalResponse> response = festivalFacade.getAllFestivals(userId);
+        return ResponseBuilder.ok(response);
+    }
+
+    @GetMapping("/{festivalId}")
+    public ResponseEntity<ApiResponse<AdminFestivalDetailResponse>> getFestivalDetail(
+            @RequestHeader("Authorization") String accessToken,
+            @PathVariable Long festivalId
+    ) {
+        Long userId = jwtService.parseTokenAndGetUserId(accessToken);
+        AdminFestivalDetailResponse response = festivalFacade.getFestivalDetail(userId, festivalId);
+        return ResponseBuilder.ok(response);
+    }
+
+    @GetMapping("/{festivalId}/participants/search")
+    public ResponseEntity<ApiResponse<List<SearchParticipantResponse>>> getParticipantByNickname(
+            @RequestHeader("Authorization") String accessToken,
+            @PathVariable("festivalId") Long festivalId,
+            @RequestParam("nickname") String nickname
+    ) {
+        Long userId = jwtService.parseTokenAndGetUserId(accessToken);
+
+        List<SearchParticipantResponse> response = participantFacade.getParticipantByNickname(userId, festivalId, nickname);
+        return ResponseBuilder.ok(response);
+    }
+
+    @PostMapping("/{festivalId}/points")
+    public ResponseEntity<ApiResponse<Void>> rechargePoints(
+            @RequestHeader("Authorization") String accessToken,
+            @PathVariable("festivalId") Long festivalId,
+            @RequestBody RechargePointRequest request
+    ) {
+        Long userId = jwtService.parseTokenAndGetUserId(accessToken);
+        pointFacade.rechargePoints(userId, festivalId, request);
+        return ResponseBuilder.ok(null);
+    }
+
+    @GetMapping("/{festivalId}/participants/{participantId}/points")
+    public ResponseEntity<ApiResponse<PointHistoryResponse>> getParticipantPointHistory(
+            @RequestHeader("Authorization") String accessToken,
+            @PathVariable("festivalId") Long festivalId,
+            @PathVariable("participantId") Long participantId
+    ) {
+        Long userId = jwtService.parseTokenAndGetUserId(accessToken);
+        PointHistoryResponse response = pointFacade.getParticipantPointHistory(userId, festivalId, participantId);
+        return ResponseBuilder.ok(response);
+    }
+}
diff --git a/src/main/java/org/festimate/team/api/festival/dto/AdminFestivalDetailResponse.java b/src/main/java/org/festimate/team/api/admin/dto/AdminFestivalDetailResponse.java
similarity index 93%
rename from src/main/java/org/festimate/team/api/festival/dto/AdminFestivalDetailResponse.java
rename to src/main/java/org/festimate/team/api/admin/dto/AdminFestivalDetailResponse.java
index 75afed2..e3d32c7 100644
--- a/src/main/java/org/festimate/team/api/festival/dto/AdminFestivalDetailResponse.java
+++ b/src/main/java/org/festimate/team/api/admin/dto/AdminFestivalDetailResponse.java
@@ -1,4 +1,4 @@
-package org.festimate.team.api.festival.dto;
+package org.festimate.team.api.admin.dto;
 
 import org.festimate.team.domain.festival.entity.Festival;
 import org.festimate.team.domain.festival.entity.FestivalStatus;
diff --git a/src/main/java/org/festimate/team/api/festival/dto/AdminFestivalResponse.java b/src/main/java/org/festimate/team/api/admin/dto/AdminFestivalResponse.java
similarity index 92%
rename from src/main/java/org/festimate/team/api/festival/dto/AdminFestivalResponse.java
rename to src/main/java/org/festimate/team/api/admin/dto/AdminFestivalResponse.java
index 213c51e..6021516 100644
--- a/src/main/java/org/festimate/team/api/festival/dto/AdminFestivalResponse.java
+++ b/src/main/java/org/festimate/team/api/admin/dto/AdminFestivalResponse.java
@@ -1,4 +1,4 @@
-package org.festimate.team.api.festival.dto;
+package org.festimate.team.api.admin.dto;
 
 import org.festimate.team.domain.festival.entity.Festival;
 import org.festimate.team.domain.festival.entity.FestivalStatus;
diff --git a/src/main/java/org/festimate/team/api/festival/dto/FestivalRequest.java b/src/main/java/org/festimate/team/api/admin/dto/FestivalRequest.java
similarity index 84%
rename from src/main/java/org/festimate/team/api/festival/dto/FestivalRequest.java
rename to src/main/java/org/festimate/team/api/admin/dto/FestivalRequest.java
index 409fc22..d8b54bf 100644
--- a/src/main/java/org/festimate/team/api/festival/dto/FestivalRequest.java
+++ b/src/main/java/org/festimate/team/api/admin/dto/FestivalRequest.java
@@ -1,4 +1,4 @@
-package org.festimate.team.api.festival.dto;
+package org.festimate.team.api.admin.dto;
 
 import java.time.LocalDate;
 import java.time.LocalDateTime;
diff --git a/src/main/java/org/festimate/team/api/festival/dto/FestivalResponse.java b/src/main/java/org/festimate/team/api/admin/dto/FestivalResponse.java
similarity index 83%
rename from src/main/java/org/festimate/team/api/festival/dto/FestivalResponse.java
rename to src/main/java/org/festimate/team/api/admin/dto/FestivalResponse.java
index 227e09d..408374c 100644
--- a/src/main/java/org/festimate/team/api/festival/dto/FestivalResponse.java
+++ b/src/main/java/org/festimate/team/api/admin/dto/FestivalResponse.java
@@ -1,4 +1,4 @@
-package org.festimate.team.api.festival.dto;
+package org.festimate.team.api.admin.dto;
 
 public record FestivalResponse(
         Long festivalId,
diff --git a/src/main/java/org/festimate/team/api/participant/dto/SearchParticipantResponse.java b/src/main/java/org/festimate/team/api/admin/dto/SearchParticipantResponse.java
similarity index 94%
rename from src/main/java/org/festimate/team/api/participant/dto/SearchParticipantResponse.java
rename to src/main/java/org/festimate/team/api/admin/dto/SearchParticipantResponse.java
index 8883bdb..3e975b1 100644
--- a/src/main/java/org/festimate/team/api/participant/dto/SearchParticipantResponse.java
+++ b/src/main/java/org/festimate/team/api/admin/dto/SearchParticipantResponse.java
@@ -1,4 +1,4 @@
-package org.festimate.team.api.participant.dto;
+package org.festimate.team.api.admin.dto;
 
 import org.festimate.team.domain.participant.entity.Participant;
 
diff --git a/src/main/java/org/festimate/team/api/facade/FestivalFacade.java b/src/main/java/org/festimate/team/api/facade/FestivalFacade.java
index f61c218..3e0834c 100644
--- a/src/main/java/org/festimate/team/api/facade/FestivalFacade.java
+++ b/src/main/java/org/festimate/team/api/facade/FestivalFacade.java
@@ -1,7 +1,13 @@
 package org.festimate.team.api.facade;
 
 import lombok.RequiredArgsConstructor;
-import org.festimate.team.api.festival.dto.*;
+import org.festimate.team.api.admin.dto.AdminFestivalDetailResponse;
+import org.festimate.team.api.admin.dto.AdminFestivalResponse;
+import org.festimate.team.api.admin.dto.FestivalRequest;
+import org.festimate.team.api.admin.dto.FestivalResponse;
+import org.festimate.team.api.festival.dto.FestivalInfoResponse;
+import org.festimate.team.api.festival.dto.FestivalVerifyRequest;
+import org.festimate.team.api.festival.dto.FestivalVerifyResponse;
 import org.festimate.team.api.user.dto.UserFestivalResponse;
 import org.festimate.team.domain.festival.entity.Festival;
 import org.festimate.team.domain.festival.service.FestivalService;
diff --git a/src/main/java/org/festimate/team/api/facade/ParticipantFacade.java b/src/main/java/org/festimate/team/api/facade/ParticipantFacade.java
index b9bced5..f4d910a 100644
--- a/src/main/java/org/festimate/team/api/facade/ParticipantFacade.java
+++ b/src/main/java/org/festimate/team/api/facade/ParticipantFacade.java
@@ -1,6 +1,7 @@
 package org.festimate.team.api.facade;
 
 import lombok.RequiredArgsConstructor;
+import org.festimate.team.api.admin.dto.SearchParticipantResponse;
 import org.festimate.team.api.participant.dto.*;
 import org.festimate.team.domain.festival.entity.Festival;
 import org.festimate.team.domain.festival.service.FestivalService;
diff --git a/src/main/java/org/festimate/team/api/festival/FestivalController.java b/src/main/java/org/festimate/team/api/festival/FestivalController.java
index ace19a9..0b7278c 100644
--- a/src/main/java/org/festimate/team/api/festival/FestivalController.java
+++ b/src/main/java/org/festimate/team/api/festival/FestivalController.java
@@ -9,8 +9,6 @@
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.*;
 
-import java.util.List;
-
 @RestController
 @RequestMapping("/v1/festivals")
 @RequiredArgsConstructor
@@ -18,7 +16,7 @@ public class FestivalController {
     private final JwtService jwtService;
     private final FestivalFacade festivalFacade;
 
-    @PostMapping("/verify-code")
+    @PostMapping("/verify")
     public ResponseEntity<ApiResponse<FestivalVerifyResponse>> verifyFestival(
             @RequestHeader("Authorization") String accessToken,
             @RequestBody FestivalVerifyRequest request
@@ -37,33 +35,4 @@ public ResponseEntity<ApiResponse<FestivalInfoResponse>> getFestivalInfo(
         FestivalInfoResponse response = festivalFacade.getFestivalInfo(userId, festivalId);
         return ResponseBuilder.ok(response);
     }
-
-    @PostMapping("/admin/festival")
-    public ResponseEntity<ApiResponse<FestivalResponse>> createFestival(
-            @RequestHeader("Authorization") String accessToken,
-            @RequestBody FestivalRequest request
-    ) {
-        Long userId = jwtService.parseTokenAndGetUserId(accessToken);
-        FestivalResponse response = festivalFacade.createFestival(userId, request);
-        return ResponseBuilder.created(response);
-    }
-
-    @GetMapping("/admin/festivals")
-    public ResponseEntity<ApiResponse<List<AdminFestivalResponse>>> getAllFestivals(
-            @RequestHeader("Authorization") String accessToken
-    ) {
-        Long userId = jwtService.parseTokenAndGetUserId(accessToken);
-        List<AdminFestivalResponse> response = festivalFacade.getAllFestivals(userId);
-        return ResponseBuilder.ok(response);
-    }
-
-    @GetMapping("/admin/festivals/{festivalId}")
-    public ResponseEntity<ApiResponse<AdminFestivalDetailResponse>> getFestivalDetail(
-            @RequestHeader("Authorization") String accessToken,
-            @PathVariable Long festivalId
-    ) {
-        Long userId = jwtService.parseTokenAndGetUserId(accessToken);
-        AdminFestivalDetailResponse response = festivalFacade.getFestivalDetail(userId, festivalId);
-        return ResponseBuilder.ok(response);
-    }
 }
diff --git a/src/main/java/org/festimate/team/api/participant/ParticipantController.java b/src/main/java/org/festimate/team/api/participant/ParticipantController.java
index 7ff3af2..473f5cc 100644
--- a/src/main/java/org/festimate/team/api/participant/ParticipantController.java
+++ b/src/main/java/org/festimate/team/api/participant/ParticipantController.java
@@ -10,10 +10,8 @@
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.*;
 
-import java.util.List;
-
 @RestController
-@RequestMapping("/v1")
+@RequestMapping("/v1/festivals")
 @RequiredArgsConstructor
 public class ParticipantController {
 
@@ -21,18 +19,18 @@ public class ParticipantController {
     private final ParticipantService participantService;
     private final ParticipantFacade participantFacade;
 
-    @PostMapping("/participants/type")
+    @PostMapping("/{festivalId}/participants/type")
     public ResponseEntity<ApiResponse<TypeResponse>> getFestivalType(
             @RequestHeader("Authorization") String accessToken,
+            @PathVariable Long festivalId,
             @RequestBody TypeRequest request
     ) {
         jwtService.parseTokenAndGetUserId(accessToken);
-
         TypeResponse response = participantService.getTypeResult(request);
         return ResponseBuilder.ok(response);
     }
 
-    @PostMapping("/festivals/{festivalId}/entry")
+    @GetMapping("/{festivalId}/participants/me")
     public ResponseEntity<ApiResponse<EntryResponse>> entryFestival(
             @RequestHeader("Authorization") String accessToken,
             @PathVariable("festivalId") Long festivalId
@@ -43,7 +41,7 @@ public ResponseEntity<ApiResponse<EntryResponse>> entryFestival(
         return ResponseBuilder.ok(response);
     }
 
-    @PostMapping("/festivals/{festivalId}/participants")
+    @PostMapping("/{festivalId}/participants")
     public ResponseEntity<ApiResponse<EntryResponse>> createParticipant(
             @RequestHeader("Authorization") String accessToken,
             @PathVariable("festivalId") Long festivalId,
@@ -54,7 +52,7 @@ public ResponseEntity<ApiResponse<EntryResponse>> createParticipant(
         return ResponseBuilder.created(response);
     }
 
-    @GetMapping("/festivals/{festivalId}/me")
+    @GetMapping("/{festivalId}/participants/me/profile")
     public ResponseEntity<ApiResponse<ProfileResponse>> getMyProfile(
             @RequestHeader("Authorization") String accessToken,
             @PathVariable("festivalId") Long festivalId
@@ -64,7 +62,7 @@ public ResponseEntity<ApiResponse<ProfileResponse>> getMyProfile(
         return ResponseBuilder.ok(response);
     }
 
-    @GetMapping("/festivals/{festivalId}/me/summary")
+    @GetMapping("/{festivalId}/participants/me/summary")
     public ResponseEntity<ApiResponse<MainUserInfoResponse>> getParticipantAndPoint(
             @RequestHeader("Authorization") String accessToken,
             @PathVariable("festivalId") Long festivalId
@@ -75,7 +73,7 @@ public ResponseEntity<ApiResponse<MainUserInfoResponse>> getParticipantAndPoint(
         return ResponseBuilder.ok(response);
     }
 
-    @GetMapping("/festivals/{festivalId}/me/type")
+    @GetMapping("/{festivalId}/participants/me/type")
     public ResponseEntity<ApiResponse<DetailProfileResponse>> getParticipantType(
             @RequestHeader("Authorization") String accessToken,
             @PathVariable("festivalId") Long festivalId
@@ -86,7 +84,7 @@ public ResponseEntity<ApiResponse<DetailProfileResponse>> getParticipantType(
         return ResponseBuilder.ok(response);
     }
 
-    @PatchMapping("/festivals/{festivalId}/me/message")
+    @PatchMapping("/{festivalId}/participants/me/message")
     public ResponseEntity<ApiResponse<Void>> modifyMyMessage(
             @RequestHeader("Authorization") String accessToken,
             @PathVariable("festivalId") Long festivalId,
@@ -97,16 +95,4 @@ public ResponseEntity<ApiResponse<Void>> modifyMyMessage(
 
         return ResponseBuilder.created(null);
     }
-
-    @GetMapping("/admin/festivals/{festivalId}/participants/search")
-    public ResponseEntity<ApiResponse<List<SearchParticipantResponse>>> getParticipantByNickname(
-            @RequestHeader("Authorization") String accessToken,
-            @PathVariable("festivalId") Long festivalId,
-            @RequestParam("nickname") String nickname
-    ) {
-        Long userId = jwtService.parseTokenAndGetUserId(accessToken);
-
-        List<SearchParticipantResponse> response = participantFacade.getParticipantByNickname(userId, festivalId, nickname);
-        return ResponseBuilder.ok(response);
-    }
 }
diff --git a/src/main/java/org/festimate/team/api/point/PointController.java b/src/main/java/org/festimate/team/api/point/PointController.java
index 569d978..2ee3c39 100644
--- a/src/main/java/org/festimate/team/api/point/PointController.java
+++ b/src/main/java/org/festimate/team/api/point/PointController.java
@@ -3,7 +3,6 @@
 import lombok.RequiredArgsConstructor;
 import org.festimate.team.api.facade.PointFacade;
 import org.festimate.team.api.point.dto.PointHistoryResponse;
-import org.festimate.team.api.point.dto.RechargePointRequest;
 import org.festimate.team.global.response.ApiResponse;
 import org.festimate.team.global.response.ResponseBuilder;
 import org.festimate.team.infra.jwt.JwtService;
@@ -18,7 +17,7 @@ public class PointController {
     private final JwtService jwtService;
     private final PointFacade pointFacade;
 
-    @GetMapping("/festivals/{festivalId}/me/points")
+    @GetMapping("/festivals/{festivalId}/participants/me/points")
     public ResponseEntity<ApiResponse<PointHistoryResponse>> getMyPointHistory(
             @RequestHeader("Authorization") String accessToken,
             @PathVariable("festivalId") Long festivalId
@@ -27,26 +26,4 @@ public ResponseEntity<ApiResponse<PointHistoryResponse>> getMyPointHistory(
         PointHistoryResponse response = pointFacade.getMyPointHistory(userId, festivalId);
         return ResponseBuilder.ok(response);
     }
-
-    @PostMapping("/admin/festival/{festivalId}/points")
-    public ResponseEntity<ApiResponse<Void>> rechargePoints(
-            @RequestHeader("Authorization") String accessToken,
-            @PathVariable("festivalId") Long festivalId,
-            @RequestBody RechargePointRequest request
-    ) {
-        Long userId = jwtService.parseTokenAndGetUserId(accessToken);
-        pointFacade.rechargePoints(userId, festivalId, request);
-        return ResponseBuilder.created(null);
-    }
-
-    @GetMapping("/admin/festivals/{festivalId}/participants/{participantId}/points")
-    public ResponseEntity<ApiResponse<PointHistoryResponse>> getParticipantPointHistory(
-            @RequestHeader("Authorization") String accessToken,
-            @PathVariable("festivalId") Long festivalId,
-            @PathVariable("participantId") Long participantId
-    ) {
-        Long userId = jwtService.parseTokenAndGetUserId(accessToken);
-        PointHistoryResponse response = pointFacade.getParticipantPointHistory(userId, festivalId, participantId);
-        return ResponseBuilder.ok(response);
-    }
 }
\ No newline at end of file
diff --git a/src/main/java/org/festimate/team/domain/festival/service/FestivalService.java b/src/main/java/org/festimate/team/domain/festival/service/FestivalService.java
index 1a7c73f..7d04c93 100644
--- a/src/main/java/org/festimate/team/domain/festival/service/FestivalService.java
+++ b/src/main/java/org/festimate/team/domain/festival/service/FestivalService.java
@@ -1,6 +1,6 @@
 package org.festimate.team.domain.festival.service;
 
-import org.festimate.team.api.festival.dto.FestivalRequest;
+import org.festimate.team.api.admin.dto.FestivalRequest;
 import org.festimate.team.domain.festival.entity.Festival;
 import org.festimate.team.domain.user.entity.User;
 
diff --git a/src/main/java/org/festimate/team/domain/festival/service/impl/FestivalServiceImpl.java b/src/main/java/org/festimate/team/domain/festival/service/impl/FestivalServiceImpl.java
index bff2914..51661e5 100644
--- a/src/main/java/org/festimate/team/domain/festival/service/impl/FestivalServiceImpl.java
+++ b/src/main/java/org/festimate/team/domain/festival/service/impl/FestivalServiceImpl.java
@@ -4,7 +4,7 @@
 import lombok.extern.slf4j.Slf4j;
 import org.festimate.team.global.response.ResponseError;
 import org.festimate.team.global.exception.FestimateException;
-import org.festimate.team.api.festival.dto.FestivalRequest;
+import org.festimate.team.api.admin.dto.FestivalRequest;
 import org.festimate.team.domain.festival.entity.Category;
 import org.festimate.team.domain.festival.entity.Festival;
 import org.festimate.team.domain.festival.repository.FestivalRepository;
diff --git a/src/test/java/org/festimate/team/api/facade/FestivalFacadeTest.java b/src/test/java/org/festimate/team/api/facade/FestivalFacadeTest.java
index a5cb0c9..8738e9d 100644
--- a/src/test/java/org/festimate/team/api/facade/FestivalFacadeTest.java
+++ b/src/test/java/org/festimate/team/api/facade/FestivalFacadeTest.java
@@ -1,6 +1,6 @@
 package org.festimate.team.api.facade;
 
-import org.festimate.team.api.festival.dto.FestivalRequest;
+import org.festimate.team.api.admin.dto.FestivalRequest;
 import org.festimate.team.api.festival.dto.FestivalVerifyRequest;
 import org.festimate.team.common.mock.MockFactory;
 import org.festimate.team.domain.festival.entity.Festival;


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>🧹 Nitpick comments (4)</summary><blockquote>

<details>
<summary>src/main/java/org/festimate/team/api/festival/FestivalController.java (1)</summary><blockquote>

`24-25`: **Consider storing and passing the userId to the facade.**

The method parses the user ID from the token but doesn't store or pass it to the facade. For consistency with other methods, consider passing the user ID to the facade method.

```diff
-        jwtService.parseTokenAndGetUserId(accessToken);
-        FestivalVerifyResponse response = festivalFacade.verifyFestival(request);
+        Long userId = jwtService.parseTokenAndGetUserId(accessToken);
+        FestivalVerifyResponse response = festivalFacade.verifyFestival(userId, request);
```

</blockquote></details>
<details>
<summary>src/main/java/org/festimate/team/api/admin/AdminController.java (1)</summary><blockquote>

`56-66`: **Consistent error handling should be implemented.**

While the controller properly delegates to facade methods, there's no explicit error handling. Consider implementing global exception handling or adding try-catch blocks to handle potential errors from the facade layer.

</blockquote></details>
<details>
<summary>src/main/java/org/festimate/team/api/participant/ParticipantController.java (2)</summary><blockquote>

`87-97`: **Consider response status for the modify endpoint.**

The `modifyMyMessage` endpoint returns `ResponseBuilder.created(null)` which suggests resource creation, but this method is performing an update. Consider using `ResponseBuilder.ok(null)` to better reflect the HTTP semantics.

```diff
-        return ResponseBuilder.created(null);
+        return ResponseBuilder.ok(null);
```

---

`22-31`: **Inconsistent parameter naming style.**

In the `getFestivalType` method, the `festivalId` parameter is defined as `@PathVariable Long festivalId`, whereas in other methods it's defined with the explicit name, e.g., `@PathVariable("festivalId") Long festivalId`. Consider standardizing this approach throughout the controller for better consistency.

```diff
-            @PathVariable Long festivalId,
+            @PathVariable("festivalId") Long festivalId,
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 5a022be9cdfc221207144590bada5dfc6774ca17 and 56ed78e5b1bf082d7796c7480be74cb8116c2154.

</details>

<details>
<summary>📒 Files selected for processing (14)</summary>

* `src/main/java/org/festimate/team/api/admin/AdminController.java` (1 hunks)
* `src/main/java/org/festimate/team/api/admin/dto/AdminFestivalDetailResponse.java` (1 hunks)
* `src/main/java/org/festimate/team/api/admin/dto/AdminFestivalResponse.java` (1 hunks)
* `src/main/java/org/festimate/team/api/admin/dto/FestivalRequest.java` (1 hunks)
* `src/main/java/org/festimate/team/api/admin/dto/FestivalResponse.java` (1 hunks)
* `src/main/java/org/festimate/team/api/admin/dto/SearchParticipantResponse.java` (1 hunks)
* `src/main/java/org/festimate/team/api/facade/FestivalFacade.java` (1 hunks)
* `src/main/java/org/festimate/team/api/facade/ParticipantFacade.java` (1 hunks)
* `src/main/java/org/festimate/team/api/festival/FestivalController.java` (1 hunks)
* `src/main/java/org/festimate/team/api/participant/ParticipantController.java` (6 hunks)
* `src/main/java/org/festimate/team/api/point/PointController.java` (1 hunks)
* `src/main/java/org/festimate/team/domain/festival/service/FestivalService.java` (1 hunks)
* `src/main/java/org/festimate/team/domain/festival/service/impl/FestivalServiceImpl.java` (1 hunks)
* `src/test/java/org/festimate/team/api/facade/FestivalFacadeTest.java` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (2)</summary>

<details>
<summary>src/main/java/org/festimate/team/api/admin/AdminController.java (1)</summary><blockquote>

<details>
<summary>src/main/java/org/festimate/team/global/response/ResponseBuilder.java (1)</summary>

* `ResponseBuilder` (5-19)

</details>

</blockquote></details>
<details>
<summary>src/main/java/org/festimate/team/api/participant/ParticipantController.java (1)</summary><blockquote>

<details>
<summary>src/main/java/org/festimate/team/global/response/ResponseBuilder.java (1)</summary>

* `ResponseBuilder` (5-19)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>🔇 Additional comments (17)</summary><blockquote>

<details>
<summary>src/main/java/org/festimate/team/api/admin/dto/AdminFestivalResponse.java (1)</summary>

`1-1`: **Package Declaration Updated for Admin Context**

The package has been changed to `org.festimate.team.api.admin.dto` to reflect the new AdminController boundary. Confirm that the directory path (`.../api/admin/dto/`) matches this declaration and that all references to this class in facades and controllers are updated.

</details>
<details>
<summary>src/main/java/org/festimate/team/api/admin/dto/SearchParticipantResponse.java (1)</summary>

`1-1`: **Package Declaration Updated for Admin Participant Search**

The package change to `org.festimate.team.api.admin.dto` correctly isolates admin-specific DTOs. Ensure the `ParticipantController` no longer references this DTO and that the `AdminController` imports it from the new location.

</details>
<details>
<summary>src/main/java/org/festimate/team/api/facade/ParticipantFacade.java (1)</summary>

`4-4`: **Import Updated to Admin DTO for Participant Search**

The import for `SearchParticipantResponse` now points to the admin DTO package, aligning with the new `AdminController`. Verify that no stale imports of the old package remain and that any controller or service using this DTO references the updated path.

</details>
<details>
<summary>src/main/java/org/festimate/team/domain/festival/service/impl/FestivalServiceImpl.java (1)</summary>

`7-7`: **Import Updated to Admin DTO for FestivalRequest**

The `FestivalRequest` import now references the admin-specific DTO package, matching the shift of festival management endpoints to `AdminController`. Confirm that validation and mapping logic consume the correct DTO and that the original non-admin endpoints no longer rely on this class.

</details>
<details>
<summary>src/main/java/org/festimate/team/domain/festival/service/FestivalService.java (1)</summary>

`3-3`: **Interface Signature Adjusted to Admin FestivalRequest**

The service interface now accepts `FestivalRequest` from `org.festimate.team.api.admin.dto`. Ensure downstream implementations and callers (including facades and controllers) are updated to use the admin DTO and that no legacy imports remain.

</details>
<details>
<summary>src/test/java/org/festimate/team/api/facade/FestivalFacadeTest.java (1)</summary>

`3-3`: **Correct import for relocated `FestivalRequest`.**  
The import has been updated to `org.festimate.team.api.admin.dto.FestivalRequest` to reflect the DTO move under the admin package.

</details>
<details>
<summary>src/main/java/org/festimate/team/api/admin/dto/FestivalRequest.java (1)</summary>

`1-1`: **Package declaration updated for admin scope.**  
Moving `FestivalRequest` into `org.festimate.team.api.admin.dto` aligns with separating admin APIs into their own controller.

</details>
<details>
<summary>src/main/java/org/festimate/team/api/admin/dto/AdminFestivalDetailResponse.java (1)</summary>

`1-1`: **Package declaration aligned with admin DTOs.**  
`AdminFestivalDetailResponse` has been moved to the `admin.dto` package, matching the new AdminController structure.

</details>
<details>
<summary>src/main/java/org/festimate/team/api/admin/dto/FestivalResponse.java (1)</summary>

`1-1`: **Admin DTO package placement confirmed.**  
`FestivalResponse` now resides under `org.festimate.team.api.admin.dto` as part of the admin API refactoring.

</details>
<details>
<summary>src/main/java/org/festimate/team/api/facade/FestivalFacade.java (1)</summary>

`4-10`: **Explicit DTO imports for improved clarity.**  
Replacing the wildcard import with explicit imports from `admin.dto` and `festival.dto` ensures that it's clear which controllers use which DTOs, supporting the separation of admin and public APIs.

</details>
<details>
<summary>src/main/java/org/festimate/team/api/point/PointController.java (2)</summary>

`20-20`: **Good refactoring to RESTful URI structure.**

The endpoint path now follows a more consistent RESTful pattern with nested resources. The change from `/festivals/{festivalId}/me/points` to `/festivals/{festivalId}/participants/me/points` clearly identifies the resource hierarchy.

---

`1-28`: **Excellent job isolating admin endpoints.**

The controller now focuses solely on user-specific point operations, with admin functionality properly moved to a dedicated AdminController. This separation of concerns follows good API design principles.

</details>
<details>
<summary>src/main/java/org/festimate/team/api/festival/FestivalController.java (1)</summary>

`19-19`: **Good simplification of the endpoint name.**

Changing from `/verify-code` to `/verify` creates a cleaner, more concise URI that still clearly communicates the endpoint's purpose.

</details>
<details>
<summary>src/main/java/org/festimate/team/api/admin/AdminController.java (2)</summary>

`1-89`: **Well-structured admin controller with consolidated endpoints.**

The new AdminController successfully centralizes all administrative operations for festivals, participants, and points under a single, dedicated controller. The URI structure is consistent, following REST best practices with clear resource hierarchies.

---

`19-19`: **Good choice of base request mapping.**

Using `/v1/admin/festivals` as the base path clearly separates admin APIs from regular user APIs and establishes festivals as the primary resource.

</details>
<details>
<summary>src/main/java/org/festimate/team/api/participant/ParticipantController.java (2)</summary>

`14-14`: **Good refactoring of base request mapping.**

Changing from `/v1` to `/v1/festivals` provides a clearer resource hierarchy and better aligns with RESTful URI conventions.

---

`33-42`: **Appropriate change from POST to GET for resource retrieval.**

The `entryFestival` endpoint now correctly uses GET instead of POST, following HTTP method semantics since it retrieves a participant's entry status rather than creating a resource.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-05T12:48:27Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-05T12:55:51Z

Review comments:
diff_hunk: @@ -0,0 +1,89 @@
+package org.festimate.team.api.admin;
+
+import lombok.RequiredArgsConstructor;
+import org.festimate.team.api.admin.dto.*;
+import org.festimate.team.api.facade.FestivalFacade;
+import org.festimate.team.api.facade.ParticipantFacade;
+import org.festimate.team.api.facade.PointFacade;
+import org.festimate.team.api.point.dto.PointHistoryResponse;
+import org.festimate.team.api.point.dto.RechargePointRequest;
+import org.festimate.team.global.response.ApiResponse;
+import org.festimate.team.global.response.ResponseBuilder;
+import org.festimate.team.infra.jwt.JwtService;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/v1/admin/festivals")
+@RequiredArgsConstructor
+public class AdminController {
+    private final JwtService jwtService;
+    private final FestivalFacade festivalFacade;
+    private final ParticipantFacade participantFacade;
+    private final PointFacade pointFacade;
+
+    @PostMapping()
+    public ResponseEntity<ApiResponse<FestivalResponse>> createFestival(
+            @RequestHeader("Authorization") String accessToken,
+            @RequestBody FestivalRequest request
+    ) {
+        Long userId = jwtService.parseTokenAndGetUserId(accessToken);
+        FestivalResponse response = festivalFacade.createFestival(userId, request);
+        return ResponseBuilder.created(response);
+    }
+
+    @GetMapping()
+    public ResponseEntity<ApiResponse<List<AdminFestivalResponse>>> getAllFestivals(
+            @RequestHeader("Authorization") String accessToken
+    ) {
+        Long userId = jwtService.parseTokenAndGetUserId(accessToken);
+        List<AdminFestivalResponse> response = festivalFacade.getAllFestivals(userId);
+        return ResponseBuilder.ok(response);
+    }
+
+    @GetMapping("/{festivalId}")
+    public ResponseEntity<ApiResponse<AdminFestivalDetailResponse>> getFestivalDetail(
+            @RequestHeader("Authorization") String accessToken,
+            @PathVariable Long festivalId
+    ) {
+        Long userId = jwtService.parseTokenAndGetUserId(accessToken);
+        AdminFestivalDetailResponse response = festivalFacade.getFestivalDetail(userId, festivalId);
+        return ResponseBuilder.ok(response);
+    }
+
+    @GetMapping("/{festivalId}/participants/search")
+    public ResponseEntity<ApiResponse<List<SearchParticipantResponse>>> getParticipantByNickname(
+            @RequestHeader("Authorization") String accessToken,
+            @PathVariable("festivalId") Long festivalId,
+            @RequestParam("nickname") String nickname
+    ) {
+        Long userId = jwtService.parseTokenAndGetUserId(accessToken);
+
+        List<SearchParticipantResponse> response = participantFacade.getParticipantByNickname(userId, festivalId, nickname);
+        return ResponseBuilder.ok(response);
+    }
+
+    @PostMapping("/{festivalId}/points")
+    public ResponseEntity<ApiResponse<Void>> rechargePoints(
+            @RequestHeader("Authorization") String accessToken,
+            @PathVariable("festivalId") Long festivalId,
+            @RequestBody RechargePointRequest request
+    ) {
+        Long userId = jwtService.parseTokenAndGetUserId(accessToken);
+        pointFacade.rechargePoints(userId, festivalId, request);
+        return ResponseBuilder.created(null);
+    }
path: src/main/java/org/festimate/team/api/admin/AdminController.java
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Check if created() is the most appropriate response for rechargePoints.**

The method uses `ResponseBuilder.created(null)` for the recharge points endpoint, but since this is modifying an existing resource rather than creating a new one, consider if `ResponseBuilder.ok(null)` would be more semantically appropriate.

---

🌐 Web query:

```web
What is the most appropriate HTTP status code for an API endpoint that modifies an existing resource?
```

💡 Result:

For API endpoints that modify existing resources, there are several appropriate HTTP status codes you can return, with the most suitable ones being in the 2xx success range.

## Most Appropriate Status Codes for Resource Modification

When modifying an existing resource via PUT or PATCH methods, the following status codes are most appropriate:

**200 OK**
This is the most commonly used and appropriate status code for most update operations[1][6]. It indicates that the request has succeeded and typically includes the updated resource representation in the response body[6]. This allows the client to see the results of their modification.

**204 No Content**
This is appropriate for updates that don't need to return data to the client[1][4]. For example, when just saving a currently edited document and there's no need to send back the updated resource. The server has fulfilled the request but does not need to return a response body[6].

**202 Accepted**
This status code is suitable if the update operation is performed asynchronously[1]. It indicates that the request has been received and accepted for processing, but the processing hasn't been completed yet. It should ideally include a URL to access the updated resource or to check if the update has succeeded, possibly with an estimation of how long the update will take[1].

## Differences Between Update Methods

The appropriate status code may also depend on which HTTP method you use for updates:

- **PUT**: Requires the client to send an entire representation of a resource to update it (replacing the old one with the new one)[1]
- **PATCH**: Requires the client to send only the parts of the resource that need updating (add, update, or delete specific parts)[1]

## Other Considerations

For specific update scenarios, other status codes might be appropriate:

- **409 Conflict**: When the update would conflict with the current state of the resource[5]
- **412 Precondition Failed**: When conditions specified in request headers are not met[2]

In RESTful API design, using the correct status code makes your API more intuitive and self-documenting for clients consuming your service.

Citations:

- 1: https://www.moesif.com/blog/technical/api-design/Which-HTTP-Status-Code-To-Use-For-Every-CRUD-App/
- 2: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
- 3: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
- 4: https://laracasts.com/discuss/channels/tips/http-response-status-code-for-update-and-delete-method
- 5: https://www.googlecloudcommunity.com/gc/Apigee/Need-suggestions-on-HTTP-Status-Code-for-trying-to-create-an/m-p/59464
- 6: https://restfulapi.net/http-status-codes/
- 7: https://umbraco.com/knowledge-base/http-status-codes/
- 8: https://codemia.io/knowledge-hub/path/http_response_code_for_post_when_resource_already_exists
- 9: https://www.atatus.com/blog/http-status-code-to-use-for-crud-operation/

---



</details>

**Use 200 OK (or 204 No Content) for updates, not 201 Created**

The `rechargePoints` endpoint is modifying an existing resource, so returning 201 Created is misleading. For update operations you should return:

- 200 OK (with an optional response body)  
- 204 No Content (when there’s nothing to return)

Please update in `src/main/java/org/festimate/team/api/admin/AdminController.java` (lines 68–77):

```diff
-    return ResponseBuilder.created(null);
+    // Option A: indicate success with no body
+    return ResponseBuilder.noContent();
+
+    // Option B: indicate success and return an empty body
+    // return ResponseBuilder.ok(null);
```

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-05T12:48:26Z
author_association: NONE
diff_hunk: @@ -0,0 +1,89 @@
+package org.festimate.team.api.admin;
+
+import lombok.RequiredArgsConstructor;
+import org.festimate.team.api.admin.dto.*;
+import org.festimate.team.api.facade.FestivalFacade;
+import org.festimate.team.api.facade.ParticipantFacade;
+import org.festimate.team.api.facade.PointFacade;
+import org.festimate.team.api.point.dto.PointHistoryResponse;
+import org.festimate.team.api.point.dto.RechargePointRequest;
+import org.festimate.team.global.response.ApiResponse;
+import org.festimate.team.global.response.ResponseBuilder;
+import org.festimate.team.infra.jwt.JwtService;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/v1/admin/festivals")
+@RequiredArgsConstructor
+public class AdminController {
+    private final JwtService jwtService;
+    private final FestivalFacade festivalFacade;
+    private final ParticipantFacade participantFacade;
+    private final PointFacade pointFacade;
+
+    @PostMapping()
+    public ResponseEntity<ApiResponse<FestivalResponse>> createFestival(
+            @RequestHeader("Authorization") String accessToken,
+            @RequestBody FestivalRequest request
+    ) {
+        Long userId = jwtService.parseTokenAndGetUserId(accessToken);
+        FestivalResponse response = festivalFacade.createFestival(userId, request);
+        return ResponseBuilder.created(response);
+    }
+
+    @GetMapping()
+    public ResponseEntity<ApiResponse<List<AdminFestivalResponse>>> getAllFestivals(
+            @RequestHeader("Authorization") String accessToken
+    ) {
+        Long userId = jwtService.parseTokenAndGetUserId(accessToken);
+        List<AdminFestivalResponse> response = festivalFacade.getAllFestivals(userId);
+        return ResponseBuilder.ok(response);
+    }
+
+    @GetMapping("/{festivalId}")
+    public ResponseEntity<ApiResponse<AdminFestivalDetailResponse>> getFestivalDetail(
+            @RequestHeader("Authorization") String accessToken,
+            @PathVariable Long festivalId
+    ) {
+        Long userId = jwtService.parseTokenAndGetUserId(accessToken);
+        AdminFestivalDetailResponse response = festivalFacade.getFestivalDetail(userId, festivalId);
+        return ResponseBuilder.ok(response);
+    }
+
+    @GetMapping("/{festivalId}/participants/search")
+    public ResponseEntity<ApiResponse<List<SearchParticipantResponse>>> getParticipantByNickname(
+            @RequestHeader("Authorization") String accessToken,
+            @PathVariable("festivalId") Long festivalId,
+            @RequestParam("nickname") String nickname
+    ) {
+        Long userId = jwtService.parseTokenAndGetUserId(accessToken);
+
+        List<SearchParticipantResponse> response = participantFacade.getParticipantByNickname(userId, festivalId, nickname);
+        return ResponseBuilder.ok(response);
+    }
+
+    @PostMapping("/{festivalId}/points")
+    public ResponseEntity<ApiResponse<Void>> rechargePoints(
+            @RequestHeader("Authorization") String accessToken,
+            @PathVariable("festivalId") Long festivalId,
+            @RequestBody RechargePointRequest request
+    ) {
+        Long userId = jwtService.parseTokenAndGetUserId(accessToken);
+        pointFacade.rechargePoints(userId, festivalId, request);
+        return ResponseBuilder.created(null);
+    }
path: src/main/java/org/festimate/team/api/admin/AdminController.java
user: login: coderabbitai[bot]
body: @2hyunjinn Yes, 200 OK is indeed better than 201 Created in this case, even though you're using a POST method.

The HTTP status codes have specific semantic meanings:
- 201 Created: Should be used only when a new resource has been created and is now accessible at a specific URL
- 200 OK: Indicates the request succeeded for general updates/modifications
- 204 No Content: Similar to 200 OK but used when there's no response body

Since your endpoint is updating existing data (recharging points for an existing participant), 200 OK or 204 No Content would be more semantically correct, even with a POST method. The HTTP method (POST) and the status code (200/201) serve different purposes - POST indicates the operation type while the status code indicates the result.

If your operation is creating a historical transaction record internally, you could argue for 201, but the primary client-facing effect is updating an existing balance, so 200 OK is more appropriate.

(･ ･) ♡


<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@2hyunjinn Yes, 200 OK is indeed better than 201 Created in this case, even though you're using a POST method.

The HTTP status codes have specific semantic meanings:
- 201 Created: Should be used only when a new resource has been created and is now accessible at a specific URL
- 200 OK: Indicates the request succeeded for general updates/modifications
- 204 No Content: Similar to 200 OK but used when there's no response body

Since your endpoint is updating existing data (recharging points for an existing participant), 200 OK or 204 No Content would be more semantically correct, even with a POST method. The HTTP method (POST) and the status code (200/201) serve different purposes - POST indicates the operation type while the status code indicates the result.

If your operation is creating a historical transaction record internally, you could argue for 201, but the primary client-facing effect is updating an existing balance, so 200 OK is more appropriate.

(･ ･) ♡</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-05T12:55:51Z
author_association: NONE

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-05-05T12:44:05Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

This update introduces a new `AdminController` dedicated to administrative endpoints for managing festivals, participants, and points, moving related logic out of the previous controllers. The package structure for several DTOs is reorganized, relocating them under an admin-specific package. Participant and point-related endpoints are refactored for more RESTful and consistent URI patterns, with participant endpoints now nested under `/v1/festivals/{festivalId}/participants` and admin endpoints under `/v1/admin/festivals`. The `FestivalController` and `PointController` have admin-specific methods removed, and related imports are updated throughout the codebase.

## Changes

| File(s)                                                                                         | Change Summary                                                                                                  |
|------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------|
| `src/main/java/org/festimate/team/api/admin/AdminController.java`                              | New controller for admin endpoints: festival creation, listing, detail, participant search, point recharge/history. |
| `src/main/java/org/festimate/team/api/admin/dto/AdminFestivalDetailResponse.java`,<br>`AdminFestivalResponse.java`,<br>`FestivalRequest.java`,<br>`FestivalResponse.java`,<br>`SearchParticipantResponse.java` | Moved DTOs from festival/participant packages to `admin.dto` package; no logic changes.                         |
| `src/main/java/org/festimate/team/api/facade/FestivalFacade.java`,<br>`ParticipantFacade.java` | Updated imports to use DTOs from new `admin.dto` package.                                                       |
| `src/main/java/org/festimate/team/api/festival/FestivalController.java`                        | Removed admin endpoints; renamed `/verify-code` to `/verify`; updated JWT parsing logic.                        |
| `src/main/java/org/festimate/team/api/participant/ParticipantController.java`                  | Refactored all endpoints to `/v1/festivals/{festivalId}/participants`; removed admin participant search endpoint.|
| `src/main/java/org/festimate/team/api/point/PointController.java`                              | Removed admin endpoints; updated user point history endpoint path.                                              |
| `src/main/java/org/festimate/team/domain/festival/service/FestivalService.java`,<br>`impl/FestivalServiceImpl.java` | Updated imports for `FestivalRequest` to new package.                                                           |
| `src/test/java/org/festimate/team/api/facade/FestivalFacadeTest.java`                          | Updated import for `FestivalRequest` to new package.                                                            |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Admin
    participant AdminController
    participant JwtService
    participant FestivalFacade
    participant ParticipantFacade
    participant PointFacade

    Admin->>AdminController: POST /v1/admin/festivals (createFestival)
    AdminController->>JwtService: parseTokenAndGetUserId(token)
    AdminController->>FestivalFacade: createFestival(userId, request)
    FestivalFacade-->>AdminController: FestivalResponse
    AdminController-->>Admin: ApiResponse<FestivalResponse>

    Admin->>AdminController: GET /v1/admin/festivals (getAllFestivals)
    AdminController->>JwtService: parseTokenAndGetUserId(token)
    AdminController->>FestivalFacade: getAllFestivals(userId)
    FestivalFacade-->>AdminController: List<AdminFestivalResponse>
    AdminController-->>Admin: ApiResponse<List<AdminFestivalResponse>>

    Admin->>AdminController: POST /v1/admin/festivals/{festivalId}/points (rechargePoints)
    AdminController->>JwtService: parseTokenAndGetUserId(token)
    AdminController->>PointFacade: rechargePoints(userId, festivalId, request)
    PointFacade-->>AdminController: void
    AdminController-->>Admin: ApiResponse<Void>
```

## Assessment against linked issues

| Objective                                                                                 | Addressed | Explanation                                                                                 |
|-------------------------------------------------------------------------------------------|-----------|---------------------------------------------------------------------------------------------|
| Refactor participant-related API URIs for RESTful consistency (#105)                      | ✅        |                                                                                             |
| Move admin endpoints to a dedicated AdminController and package (#105)                    | ✅        |                                                                                             |
| Remove admin logic from non-admin controllers and update imports accordingly (#105)       | ✅        |                                                                                             |

## Possibly related PRs

- Team-Festimate/festimate-server#101: Refactors the `FestivalFacade` and controllers by moving admin logic into a dedicated admin controller, aligning with the new `AdminController` and DTO reorganizations in this PR.

## Poem

> In the warren of code, a new path appears,  
> Admins now gather where logic is clear.  
> Endpoints align, RESTful and neat,  
> DTOs hop to a new package retreat.  
> Controllers refactored, confusion undone—  
> This rabbit applauds: refactoring won!  
> 🐇✨

<!-- walkthrough_end -->


---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 56ed78e5b1bf082d7796c7480be74cb8116c2154 and 05e3780785fb10046185fc36a650d172220b4025.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `src/main/java/org/festimate/team/api/admin/AdminController.java` (1 hunks)

</details>

<details>
<summary>✅ Files skipped from review due to trivial changes (1)</summary>

* src/main/java/org/festimate/team/api/admin/AdminController.java

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (1)</summary>

* GitHub Check: test

</details>

</details>
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=Team-Festimate/festimate-server&utm_content=106):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-05T12:46:53Z
body: ## Test Results
40 tests   40 ✅  1s ⏱️
10 suites   0 💤
10 files     0 ❌

Results for commit 05e37807.

[test-results]:data:application/gzip;base64,H4sIANm1GGgC/12MyQ6DIBRFf8Ww7uKBDKY/0yBC8lKVhmFl+u8FO6Dd3XNucjbicLaRXDsKl47EjKnRlINO6NfKBcuT6sfhC7eYjfkzd3wU04TTOJ+EDcGHjwl5/RXrPgXfovV2PuR2PtaMXxZMBQgI26sB1CDcSAG4pGWZXmopYKKKMQYjBybI8wXzN3Y/AQEAAA==

:recycle: This comment has been updated with latest results.
author_association: NONE