diff --git a/src/main/java/konkuk/chacall/domain/foodtruck/domain/repository/FoodTruckDocumentRepository.java b/src/main/java/konkuk/chacall/domain/foodtruck/domain/repository/FoodTruckDocumentRepository.java index 44d77d8b..97f49b0c 100644 --- a/src/main/java/konkuk/chacall/domain/foodtruck/domain/repository/FoodTruckDocumentRepository.java +++ b/src/main/java/konkuk/chacall/domain/foodtruck/domain/repository/FoodTruckDocumentRepository.java @@ -2,6 +2,16 @@ import konkuk.chacall.domain.foodtruck.domain.model.FoodTruckDocument; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.Collection; +import java.util.List; public interface FoodTruckDocumentRepository extends JpaRepository { + @Query(""" + SELECT ftd + FROM FoodTruckDocument ftd + WHERE ftd.foodTruck.foodTruckId IN :foodTruckIds + """) + List findAllInFoodTruckIds(List foodTruckIds); } diff --git a/src/main/java/konkuk/chacall/domain/foodtruck/domain/repository/FoodTruckRepository.java b/src/main/java/konkuk/chacall/domain/foodtruck/domain/repository/FoodTruckRepository.java index 740d120f..a197eb7f 100644 --- a/src/main/java/konkuk/chacall/domain/foodtruck/domain/repository/FoodTruckRepository.java +++ b/src/main/java/konkuk/chacall/domain/foodtruck/domain/repository/FoodTruckRepository.java @@ -5,10 +5,12 @@ import konkuk.chacall.domain.foodtruck.domain.value.FoodTruckStatus; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; +import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; +import java.util.List; import java.util.Optional; public interface FoodTruckRepository extends JpaRepository, FoodTruckSearchRepository { @@ -46,4 +48,9 @@ boolean existsByFoodTruckIdAndOwnerIdAndFoodTruckStatus( boolean existsByFoodTruckInfo_Name(String name); + @EntityGraph(attributePaths = {"owner"}) + List findAllByFoodTruckStatus(FoodTruckStatus foodTruckStatus); + + @EntityGraph(attributePaths = {"owner"}) + List findAll(); } diff --git a/src/main/java/konkuk/chacall/domain/foodtruck/domain/value/FoodTruckStatus.java b/src/main/java/konkuk/chacall/domain/foodtruck/domain/value/FoodTruckStatus.java index 56876318..c8da52e7 100644 --- a/src/main/java/konkuk/chacall/domain/foodtruck/domain/value/FoodTruckStatus.java +++ b/src/main/java/konkuk/chacall/domain/foodtruck/domain/value/FoodTruckStatus.java @@ -1,5 +1,8 @@ package konkuk.chacall.domain.foodtruck.domain.value; +import com.fasterxml.jackson.annotation.JsonCreator; +import konkuk.chacall.global.common.exception.DomainRuleException; +import konkuk.chacall.global.common.exception.code.ErrorCode; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -11,4 +14,13 @@ public enum FoodTruckStatus { REJECTED("승인 거부"); // 관리자가 승인을 거부한 상태 private final String description; + + public static FoodTruckStatus from(String status) { + for (FoodTruckStatus foodTruckStatus : FoodTruckStatus.values()) { + if (foodTruckStatus.getDescription().equals(status)) { + return foodTruckStatus; + } + } + throw new DomainRuleException(ErrorCode.FOOD_TRUCK_STATUS_MISMATCH); + } } \ No newline at end of file diff --git a/src/main/java/konkuk/chacall/domain/user/application/UserService.java b/src/main/java/konkuk/chacall/domain/user/application/UserService.java index 3e8852ed..c8ba1a8b 100644 --- a/src/main/java/konkuk/chacall/domain/user/application/UserService.java +++ b/src/main/java/konkuk/chacall/domain/user/application/UserService.java @@ -1,11 +1,13 @@ package konkuk.chacall.domain.user.application; +import konkuk.chacall.domain.foodtruck.presentation.dto.response.FoodTruckResponse; 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 getAllFoodTrucks(Long userId, String status) { + adminValidator.validateAdmin(userId); + + return adminService.getAllFoodTrucks(status); + } } diff --git a/src/main/java/konkuk/chacall/domain/user/application/admin/AdminService.java b/src/main/java/konkuk/chacall/domain/user/application/admin/AdminService.java index 93b996dc..e292ec06 100644 --- a/src/main/java/konkuk/chacall/domain/user/application/admin/AdminService.java +++ b/src/main/java/konkuk/chacall/domain/user/application/admin/AdminService.java @@ -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 getAllFoodTrucks(String status) { + List foodTruckList; + + if(status != null && !status.isEmpty()) { + FoodTruckStatus foodTruckStatus = FoodTruckStatus.from(status); + foodTruckList = foodTruckRepository.findAllByFoodTruckStatus(foodTruckStatus); + } else { + foodTruckList = foodTruckRepository.findAll(); + } + + Map> documentMap = foodTruckDocumentRepository.findAllInFoodTruckIds( + foodTruckList.stream() + .map(FoodTruck::getFoodTruckId) + .toList() + ).stream() + .collect( + Collectors.groupingBy(document -> document.getFoodTruck().getFoodTruckId()) + ); + + + return foodTruckList.stream() + .map(foodTruck -> FoodTruckForAdminResponse.from( + foodTruck, + documentMap.getOrDefault(foodTruck.getFoodTruckId(), List.of()).stream() + .map(FoodTruckDocument::getDocumentUrl) + .collect(Collectors.toList()) + ) + ) + .collect(Collectors.toList()); + } } diff --git a/src/main/java/konkuk/chacall/domain/user/presentation/UserController.java b/src/main/java/konkuk/chacall/domain/user/presentation/UserController.java index fedfdac3..4b705b16 100644 --- a/src/main/java/konkuk/chacall/domain/user/presentation/UserController.java +++ b/src/main/java/konkuk/chacall/domain/user/presentation/UserController.java @@ -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 updateUserInfo( } @Operation( - summary = "푸드트럭 승인 상태 변경", + summary = "[운영자용] 푸드트럭 승인 상태 변경", description = "운영자 - 푸드트럭 승인 상태를 변경합니다." ) @ExceptionDescription(SwaggerResponseDescription.APPROVE_FOOD_TRUCK_STATUS) @@ -79,4 +82,18 @@ public BaseResponse getProfilePresignedUrl( ) { return BaseResponse.ok(userService.createUserImagePresignedUrl(userId, request)); } + + @Operation( + summary = "[운영자용] 서비스내에 모든 푸드트럭 조회", + description = "운영자 - 서비스내에 모든 푸드트럭을 조회합니다." + ) + @ExceptionDescription(SwaggerResponseDescription.GET_ALL_FOOD_TRUCKS) + @GetMapping("/admin/food-trucks") + public BaseResponse> getAllFoodTrucks ( + @Parameter(hidden = true) @UserId final Long userId, + @Parameter(description = "푸드트럭 승인 상태 필터링 (승인 대기, 승인 완료, 승인 거부)", example = "승인 대기") + @RequestParam(required = false) final String status + ) { + return BaseResponse.ok(userService.getAllFoodTrucks(userId, status)); + } } diff --git a/src/main/java/konkuk/chacall/domain/user/presentation/dto/response/FoodTruckForAdminResponse.java b/src/main/java/konkuk/chacall/domain/user/presentation/dto/response/FoodTruckForAdminResponse.java new file mode 100644 index 00000000..a5ea269b --- /dev/null +++ b/src/main/java/konkuk/chacall/domain/user/presentation/dto/response/FoodTruckForAdminResponse.java @@ -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 foodTruckDocumentUrls +) { + public static FoodTruckForAdminResponse from(FoodTruck foodTruck, List foodTruckDocumentUrls) { + return new FoodTruckForAdminResponse( + foodTruck.getFoodTruckId(), + foodTruck.getFoodTruckInfo().getName(), + foodTruck.getOwner().getName(), + foodTruck.getFoodTruckStatus().getDescription(), + foodTruckDocumentUrls + ); + } +} diff --git a/src/main/java/konkuk/chacall/global/common/exception/code/ErrorCode.java b/src/main/java/konkuk/chacall/global/common/exception/code/ErrorCode.java index cee2d804..b29ff399 100644 --- a/src/main/java/konkuk/chacall/global/common/exception/code/ErrorCode.java +++ b/src/main/java/konkuk/chacall/global/common/exception/code/ErrorCode.java @@ -83,7 +83,8 @@ public enum ErrorCode implements ResponseCode { AVAILABLE_QUANTITY_MISMATCH(HttpStatus.BAD_REQUEST, 110007, "제조 가능 수량 값이 올바르지 않습니다."), NEED_ELECTRICITY_MISMATCH(HttpStatus.BAD_REQUEST, 110008, "전기 사용 여부 값이 올바르지 않습니다."), PAYMENT_METHOD_MISMATCH(HttpStatus.BAD_REQUEST, 110009, "결제 수단 값이 올바르지 않습니다."), - FOOD_TRUCK_NOT_VIEWABLE(HttpStatus.FORBIDDEN, 110010, "노출 가능한 상태의 푸드트럭이 아닙니다."), + FOOD_TRUCK_STATUS_MISMATCH(HttpStatus.BAD_REQUEST, 110010, "푸드트럭 상태 값이 올바르지 않습니다."), + FOOD_TRUCK_NOT_VIEWABLE(HttpStatus.FORBIDDEN, 110011, "노출 가능한 상태의 푸드트럭이 아닙니다."), /** * Image diff --git a/src/main/java/konkuk/chacall/global/common/swagger/SwaggerResponseDescription.java b/src/main/java/konkuk/chacall/global/common/swagger/SwaggerResponseDescription.java index eb65e196..437f441f 100644 --- a/src/main/java/konkuk/chacall/global/common/swagger/SwaggerResponseDescription.java +++ b/src/main/java/konkuk/chacall/global/common/swagger/SwaggerResponseDescription.java @@ -259,6 +259,11 @@ public enum SwaggerResponseDescription { USER_FORBIDDEN, FOOD_TRUCK_NOT_FOUND ))), + GET_ALL_FOOD_TRUCKS(new LinkedHashSet<>(Set.of( + USER_NOT_FOUND, + USER_FORBIDDEN, + FOOD_TRUCK_STATUS_MISMATCH + ))), // Default DEFAULT(new LinkedHashSet<>())