-
Notifications
You must be signed in to change notification settings - Fork 0
[FEAT] 운영자용 서비스내에 모든 푸드트럭 조회 API 구현 #56
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 5 commits
6f4ac28
686bf32
68f1181
20c1f10
26d494c
e151918
8cd4535
b844f94
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 |
|---|---|---|
| @@ -1,11 +1,13 @@ | ||
| package konkuk.chacall.domain.user.application; | ||
|
|
||
| import konkuk.chacall.domain.foodtruck.presentation.dto.response.FoodTruckResponse; | ||
|
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. 🧩 Analysis chain사용되지 않는 import인지 확인하세요.
다음 스크립트로 사용 여부를 확인하세요: 🏁 Script executed: #!/bin/bash
# FoodTruckResponse가 UserService.java에서 실제로 사용되는지 확인
rg -n "FoodTruckResponse" src/main/java/konkuk/chacall/domain/user/application/UserService.javaLength of output: 85 사용되지 않는 import 제거 필요 검증 결과 🤖 Prompt for AI Agents |
||
| import konkuk.chacall.domain.user.application.admin.AdminService; | ||
| import konkuk.chacall.domain.user.application.validator.AdminValidator; | ||
| import konkuk.chacall.domain.user.presentation.dto.request.ApproveFoodTruckStatusRequest; | ||
| import konkuk.chacall.domain.user.domain.model.User; | ||
| import konkuk.chacall.domain.user.domain.repository.UserRepository; | ||
| import konkuk.chacall.domain.user.presentation.dto.request.UpdateUserInfoRequest; | ||
| import konkuk.chacall.domain.user.presentation.dto.response.FoodTruckForAdminResponse; | ||
| import konkuk.chacall.domain.user.presentation.dto.response.UserResponse; | ||
| import konkuk.chacall.global.common.exception.EntityNotFoundException; | ||
| import konkuk.chacall.global.common.exception.code.ErrorCode; | ||
|
|
@@ -17,6 +19,8 @@ | |
| import org.springframework.stereotype.Service; | ||
| import org.springframework.transaction.annotation.Transactional; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| @Service | ||
| @RequiredArgsConstructor | ||
| @Transactional(readOnly = true) | ||
|
|
@@ -62,4 +66,10 @@ public ImageResponse createUserImagePresignedUrl(Long userId, ImageRequest reque | |
| KeyUtils::buildUserProfileImageKey | ||
| ); | ||
| } | ||
|
|
||
| public List<FoodTruckForAdminResponse> getAllFoodTrucks(Long userId, String status) { | ||
| adminValidator.validateAdmin(userId); | ||
|
|
||
| return adminService.getAllFoodTrucks(status); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,18 +1,27 @@ | ||||||||||||||||||||||||||
| package konkuk.chacall.domain.user.application.admin; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| import konkuk.chacall.domain.foodtruck.domain.model.FoodTruck; | ||||||||||||||||||||||||||
| import konkuk.chacall.domain.foodtruck.domain.model.FoodTruckDocument; | ||||||||||||||||||||||||||
| import konkuk.chacall.domain.foodtruck.domain.repository.FoodTruckDocumentRepository; | ||||||||||||||||||||||||||
| import konkuk.chacall.domain.foodtruck.domain.repository.FoodTruckRepository; | ||||||||||||||||||||||||||
| import konkuk.chacall.domain.foodtruck.domain.value.FoodTruckStatus; | ||||||||||||||||||||||||||
| import konkuk.chacall.domain.user.presentation.dto.request.ApproveFoodTruckStatusRequest; | ||||||||||||||||||||||||||
| import konkuk.chacall.domain.user.presentation.dto.response.FoodTruckForAdminResponse; | ||||||||||||||||||||||||||
| import konkuk.chacall.global.common.exception.EntityNotFoundException; | ||||||||||||||||||||||||||
| import konkuk.chacall.global.common.exception.code.ErrorCode; | ||||||||||||||||||||||||||
| import lombok.RequiredArgsConstructor; | ||||||||||||||||||||||||||
| import org.springframework.stereotype.Service; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| import java.util.List; | ||||||||||||||||||||||||||
| import java.util.Map; | ||||||||||||||||||||||||||
| import java.util.stream.Collectors; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| @Service | ||||||||||||||||||||||||||
| @RequiredArgsConstructor | ||||||||||||||||||||||||||
| public class AdminService { | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| private final FoodTruckRepository foodTruckRepository; | ||||||||||||||||||||||||||
| private final FoodTruckDocumentRepository foodTruckDocumentRepository; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| public void approveFoodTruckStatus(Long foodTruckId, ApproveFoodTruckStatusRequest request) { | ||||||||||||||||||||||||||
| FoodTruck foodTruck = foodTruckRepository.findById(foodTruckId) | ||||||||||||||||||||||||||
|
|
@@ -21,4 +30,34 @@ public void approveFoodTruckStatus(Long foodTruckId, ApproveFoodTruckStatusReque | |||||||||||||||||||||||||
| foodTruck.approveFoodTruck(request.status()); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| public List<FoodTruckForAdminResponse> getAllFoodTrucks(String status) { | ||||||||||||||||||||||||||
| List<FoodTruck> foodTruckList; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if(status != null && !status.isEmpty()) { | ||||||||||||||||||||||||||
| FoodTruckStatus foodTruckStatus = FoodTruckStatus.from(status); | ||||||||||||||||||||||||||
| foodTruckList = foodTruckRepository.findAllByFoodTruckStatus(foodTruckStatus); | ||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||
| foodTruckList = foodTruckRepository.findAll(); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
Comment on lines
+36
to
+41
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 | 🟠 Major 문자열 검증 로직을 개선하세요.
-if(status != null && !status.isEmpty()) {
+if(status != null && !status.isBlank()) {
FoodTruckStatus foodTruckStatus = FoodTruckStatus.from(status);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Map<Long, List<FoodTruckDocument>> documentMap = foodTruckDocumentRepository.findAllInFoodTruckIds( | ||||||||||||||||||||||||||
| foodTruckList.stream() | ||||||||||||||||||||||||||
| .map(FoodTruck::getFoodTruckId) | ||||||||||||||||||||||||||
| .toList() | ||||||||||||||||||||||||||
| ).stream() | ||||||||||||||||||||||||||
| .collect( | ||||||||||||||||||||||||||
| Collectors.groupingBy(document -> document.getFoodTruck().getFoodTruckId()) | ||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||
|
Comment on lines
+43
to
+50
Contributor
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. stream 으로 처리를 깔끔하게 잘 하신 것 같습니다! 좋아요 다만 한가지 궁금한 점은, 지금 특정 푸드트럭에 대한 푸드트럭 서류를 조회하기 위해 별도의 쿼리를 날리는 상황이라고 이해되는데, 현재 운영자 - 푸드트럭 조회 로직에서는 페이징이 구현이 되어있지 않은 상황인 것 같습니다. 만약 페이징이 존재한다면 FoodTruck <-> FoodTruckDocument 가 OneToMany 관계를 갖고 있어서 fetch join 이 불가하지만, 현재처럼 페이징이 존재하지 않는다면 fetch join 이 가능할 것 같아서 여쭤봅니당
Contributor
Author
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. 상균님 말씀이 FoodTruck 엔티티에 OneToMany로 그게 맞다면, 그렇게 진행하는게 더 좋을 것 같긴하네요!
Contributor
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. 아 지금 양방향이 안걸려있구나.. 그럼 좀 애매하긴 하네요.
Contributor
Author
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. 네넵 그러면 일단 현재 코드 유지하고 머지하겠습니다~ |
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| return foodTruckList.stream() | ||||||||||||||||||||||||||
| .map(foodTruck -> FoodTruckForAdminResponse.from( | ||||||||||||||||||||||||||
| foodTruck, | ||||||||||||||||||||||||||
| documentMap.getOrDefault(foodTruck.getFoodTruckId(), List.of()).stream() | ||||||||||||||||||||||||||
| .map(FoodTruckDocument::getDocumentUrl) | ||||||||||||||||||||||||||
| .collect(Collectors.toList()) | ||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
| .collect(Collectors.toList()); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,7 @@ | |
| import konkuk.chacall.domain.user.presentation.dto.request.ApproveFoodTruckStatusRequest; | ||
| import konkuk.chacall.domain.user.application.UserService; | ||
| import konkuk.chacall.domain.user.presentation.dto.request.UpdateUserInfoRequest; | ||
| import konkuk.chacall.domain.user.presentation.dto.response.FoodTruckForAdminResponse; | ||
| import konkuk.chacall.domain.user.presentation.dto.response.UserResponse; | ||
| import konkuk.chacall.global.common.annotation.ExceptionDescription; | ||
| import konkuk.chacall.global.common.annotation.UserId; | ||
|
|
@@ -17,6 +18,8 @@ | |
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.web.bind.annotation.*; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| @Tag(name = "User API", description = "전체 사용자(마이페이지) 관련 API") | ||
| @RestController | ||
| @RequestMapping("/users") | ||
|
|
@@ -53,7 +56,7 @@ public BaseResponse<Void> updateUserInfo( | |
| } | ||
|
|
||
| @Operation( | ||
| summary = "푸드트럭 승인 상태 변경", | ||
| summary = "[운영자용] 푸드트럭 승인 상태 변경", | ||
| description = "운영자 - 푸드트럭 승인 상태를 변경합니다." | ||
| ) | ||
| @ExceptionDescription(SwaggerResponseDescription.APPROVE_FOOD_TRUCK_STATUS) | ||
|
|
@@ -79,4 +82,18 @@ public BaseResponse<ImageResponse> getProfilePresignedUrl( | |
| ) { | ||
| return BaseResponse.ok(userService.createUserImagePresignedUrl(userId, request)); | ||
| } | ||
|
|
||
| @Operation( | ||
| summary = "[운영자용] 서비스내에 모든 푸드트럭 조회", | ||
| description = "운영자 - 서비스내에 모든 푸드트럭을 조회합니다." | ||
| ) | ||
| @ExceptionDescription(SwaggerResponseDescription.GET_ALL_FOOD_TRUCKS) | ||
| @GetMapping("/admin/food-trucks") | ||
| public BaseResponse<List<FoodTruckForAdminResponse>> getAllFoodTrucks ( | ||
| @Parameter(hidden = true) @UserId final Long userId, | ||
| @Parameter(description = "푸드트럭 승인 상태 필터링 (APPROVED, PENDING, REJECTED) / 안 보낼 경우 전체", example = "APPROVED") | ||
|
||
| @RequestParam(required = false) final String status | ||
| ) { | ||
| return BaseResponse.ok(userService.getAllFoodTrucks(userId, status)); | ||
| } | ||
buzz0331 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,29 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||
| package konkuk.chacall.domain.user.presentation.dto.response; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import konkuk.chacall.domain.foodtruck.domain.model.FoodTruck; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.List; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| public record FoodTruckForAdminResponse( | ||||||||||||||||||||||||||||||||||||||||||||||||
| @Schema(description = "푸드트럭 ID", example = "1") | ||||||||||||||||||||||||||||||||||||||||||||||||
| Long foodTruckId, | ||||||||||||||||||||||||||||||||||||||||||||||||
| @Schema(description = "푸드트럭 이름", example = "차콜 푸드트럭") | ||||||||||||||||||||||||||||||||||||||||||||||||
| String foodTruckName, | ||||||||||||||||||||||||||||||||||||||||||||||||
| @Schema(description = "푸드트럭 사장님 이름", example = "홍길동") | ||||||||||||||||||||||||||||||||||||||||||||||||
| String ownerName, | ||||||||||||||||||||||||||||||||||||||||||||||||
| @Schema(description = "푸드트럭 상태", example = "승인 대기") | ||||||||||||||||||||||||||||||||||||||||||||||||
| String foodTruckStatus, | ||||||||||||||||||||||||||||||||||||||||||||||||
| @Schema(description = "푸드트럭 서류 URL 목록 (사업자 등록증 포함)", example = "[\"https://cdn.chacall.com/foodtrucks/osori/doc1.jpg\", \"https://cdn.chacall.com/foodtrucks/osori/doc2.jpg\", \"https://cdn.chacall.com/foodtrucks/osori/doc3.jpg\", \"https://cdn.chacall.com/foodtrucks/osori/doc4.jpg\", \"https://cdn.chacall.com/foodtrucks/osori/doc5.jpg\"]") | ||||||||||||||||||||||||||||||||||||||||||||||||
| List<String> foodTruckDocumentUrls | ||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| public static FoodTruckForAdminResponse from(FoodTruck foodTruck, List<String> foodTruckDocumentUrls) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return new FoodTruckForAdminResponse( | ||||||||||||||||||||||||||||||||||||||||||||||||
| foodTruck.getFoodTruckId(), | ||||||||||||||||||||||||||||||||||||||||||||||||
| foodTruck.getFoodTruckInfo().getName(), | ||||||||||||||||||||||||||||||||||||||||||||||||
| foodTruck.getOwner().getName(), | ||||||||||||||||||||||||||||||||||||||||||||||||
| foodTruck.getFoodTruckStatus().getDescription(), | ||||||||||||||||||||||||||||||||||||||||||||||||
| foodTruckDocumentUrls | ||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+20
to
+28
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. 🧩 Analysis chain팩토리 메서드에 null 안전성 검증을 추가하세요. 현재 구현은 여러 지점에서
관리자 승인 워크플로우에서 사용되는 코드이므로 런타임 안정성이 중요합니다. 다음과 같이 방어적 null 체크를 추가하는 것을 권장합니다: public static FoodTruckForAdminResponse from(FoodTruck foodTruck, List<String> foodTruckDocumentUrls) {
+ if (foodTruck == null) {
+ throw new IllegalArgumentException("foodTruck cannot be null");
+ }
+ if (foodTruck.getFoodTruckInfo() == null || foodTruck.getOwner() == null || foodTruck.getFoodTruckStatus() == null) {
+ throw new IllegalStateException("FoodTruck has null required fields");
+ }
return new FoodTruckForAdminResponse(
foodTruck.getFoodTruckId(),
foodTruck.getFoodTruckInfo().getName(),
foodTruck.getOwner().getName(),
foodTruck.getFoodTruckStatus().getDescription(),
- foodTruckDocumentUrls
+ foodTruckDocumentUrls == null ? List.of() : foodTruckDocumentUrls
);
}또는, 도메인 모델이 null을 반환하지 않는다는 것이 보장된다면, 다음 스크립트로 🏁 Script executed: #!/bin/bash
# FoodTruck 엔티티와 관련 메서드의 null 안전성 확인
# FoodTruck 엔티티에서 @NotNull/@NonNull 어노테이션 확인
echo "=== FoodTruck 엔티티의 null 안전성 어노테이션 확인 ==="
fd -e java -x rg -n -A 2 -B 2 'class FoodTruck' {} \; | head -30
# getFoodTruckInfo, getOwner, getFoodTruckStatus 메서드 시그니처 확인
echo -e "\n=== getter 메서드 시그니처 확인 ==="
ast-grep --pattern $'getFoodTruckInfo() {
$$$
}'
ast-grep --pattern $'getOwner() {
$$$
}'
ast-grep --pattern $'getFoodTruckStatus() {
$$$
}'Length of output: 940 Length of output: 1112 🏁 Script executed: #!/bin/bash
# Find FoodTruck.java without type filter issues
echo "=== FoodTruck.java 파일 찾기 ==="
find . -name "FoodTruck.java" 2>/dev/null
# Show content of FoodTruck entity
echo -e "\n=== FoodTruck 엔티티 정의 ==="
find . -name "FoodTruck.java" -exec cat {} \; 2>/dev/null
# Find FoodTruckInfo entity
echo -e "\n=== FoodTruckInfo 엔티티 정의 ==="
find . -name "FoodTruckInfo.java" -exec cat {} \; 2>/dev/null
# Search for Owner entity
echo -e "\n=== Owner 엔티티 찾기 ==="
find . -path "*/domain/owner/domain/*.java" -name "*.java" | grep -i "owner" | head -5
find . -path "*/domain/owner/domain/*.java" -exec grep -l "class.*Owner.*Entity\|@Entity.*class.*Owner" {} \; 2>/dev/null | head -3Length of output: 8365 팩토리 메서드에 null 안전성 검증을 추가하세요. 도메인 모델 분석 결과 다음 지점에서
관리자 승인 워크플로우의 중요한 메서드이므로 방어적 검증이 필수입니다. 다음과 같이 수정하세요: public static FoodTruckForAdminResponse from(FoodTruck foodTruck, List<String> foodTruckDocumentUrls) {
+ Objects.requireNonNull(foodTruck, "foodTruck cannot be null");
+ Objects.requireNonNull(foodTruck.getFoodTruckInfo(), "foodTruckInfo cannot be null");
+ Objects.requireNonNull(foodTruck.getOwner(), "owner cannot be null");
+ Objects.requireNonNull(foodTruck.getFoodTruckStatus(), "foodTruckStatus cannot be null");
+
return new FoodTruckForAdminResponse(
foodTruck.getFoodTruckId(),
foodTruck.getFoodTruckInfo().getName(),
foodTruck.getOwner().getName(),
foodTruck.getFoodTruckStatus().getDescription(),
- foodTruckDocumentUrls
+ foodTruckDocumentUrls != null ? foodTruckDocumentUrls : Collections.emptyList()
);
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.