From 0cf2f393854c154efbf73052fd64d93711688341 Mon Sep 17 00:00:00 2001 From: shinae1023 Date: Mon, 11 May 2026 18:46:19 +0900 Subject: [PATCH] =?UTF-8?q?[Feat]=20=EC=A7=81=EB=AC=B4=20=EB=B6=84?= =?UTF-8?q?=EB=A5=98=20API=20(#22)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ClassificationController.java | 115 ++++++++++++++++++ .../ClassificationLowerResponseDto.java | 8 ++ .../response/ClassificationResponseDto.java | 4 + .../service/ClassificationService.java | 79 ++++++++++++ .../apiPayload/code/GeneralErrorCode.java | 3 + 5 files changed, 209 insertions(+) create mode 100644 src/main/java/com/jobdri/jobdri_api/domain/classification/controller/ClassificationController.java create mode 100644 src/main/java/com/jobdri/jobdri_api/domain/classification/dto/response/ClassificationLowerResponseDto.java create mode 100644 src/main/java/com/jobdri/jobdri_api/domain/classification/dto/response/ClassificationResponseDto.java create mode 100644 src/main/java/com/jobdri/jobdri_api/domain/classification/service/ClassificationService.java diff --git a/src/main/java/com/jobdri/jobdri_api/domain/classification/controller/ClassificationController.java b/src/main/java/com/jobdri/jobdri_api/domain/classification/controller/ClassificationController.java new file mode 100644 index 0000000..acabef0 --- /dev/null +++ b/src/main/java/com/jobdri/jobdri_api/domain/classification/controller/ClassificationController.java @@ -0,0 +1,115 @@ +package com.jobdri.jobdri_api.domain.classification.controller; + +import com.jobdri.jobdri_api.domain.classification.dto.response.ClassificationLowerResponseDto; +import com.jobdri.jobdri_api.domain.classification.dto.response.ClassificationResponseDto; +import com.jobdri.jobdri_api.domain.classification.service.ClassificationService; +import com.jobdri.jobdri_api.global.apiPayload.ApiResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/classifications") +@Tag(name = "Classification", description = "직무 분류 조회 API") +public class ClassificationController { + + private final ClassificationService classificationService; + + @Operation( + summary = "대분류 전체 조회", + description = "등록된 모든 대분류(Big Classification)를 전체 조회합니다." + ) + @ApiResponses(value = { + @io.swagger.v3.oas.annotations.responses.ApiResponse( + responseCode = "200", + description = "대분류 조회 성공", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = ApiResponse.class), + examples = @ExampleObject(value = "{\"isSuccess\":true,\"code\":\"COMMON2000\",\"message\":\"대분류 조회에 성공했습니다.\",\"result\":[{\"classificationId\":1,\"classificationName\":\"개발\"},{\"classificationId\":2,\"classificationName\":\"디자인\"}],\"error\":null}") + ) + ) + }) + @GetMapping + public ApiResponse> getBigClassifications() { + return ApiResponse.onSuccess( + "대분류 조회에 성공했습니다.", + classificationService.getBigClassifications() + ); + } + + @Operation( + summary = "대분류 기준 중분류 조회", + description = "대분류 ID를 기준으로 연결된 모든 중분류(Middle Classification)를 조회합니다." + ) + @ApiResponses(value = { + @io.swagger.v3.oas.annotations.responses.ApiResponse( + responseCode = "200", + description = "중분류 조회 성공", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = ApiResponse.class), + examples = @ExampleObject(value = "{\"isSuccess\":true,\"code\":\"COMMON2000\",\"message\":\"중분류 조회에 성공했습니다.\",\"result\":{\"upperClassId\":1,\"upperClassName\":\"개발\",\"lowerClassifications\":[{\"classificationId\":10,\"classificationName\":\"백엔드\"},{\"classificationId\":11,\"classificationName\":\"프론트엔드\"}]},\"error\":null}") + ) + ), + @io.swagger.v3.oas.annotations.responses.ApiResponse( + responseCode = "404", + description = "대분류를 찾을 수 없음", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = ApiResponse.class), + examples = @ExampleObject(value = "{\"isSuccess\":false,\"code\":\"CLASSIFICATION_4041\",\"message\":\"분류를 찾을 수 없습니다.\",\"result\":null,\"error\":\"해당 대분류를 찾을 수 없습니다. bigId=999\"}") + ) + ) + }) + @GetMapping("/{bigId}/middles") + public ApiResponse getMiddleClassifications(@PathVariable Long bigId) { + return ApiResponse.onSuccess( + "중분류 조회에 성공했습니다.", + classificationService.getMiddleClassifications(bigId) + ); + } + + @Operation( + summary = "중분류 기준 소분류 조회", + description = "중분류 ID를 기준으로 연결된 모든 소분류(Detail Classification)를 조회합니다." + ) + @ApiResponses(value = { + @io.swagger.v3.oas.annotations.responses.ApiResponse( + responseCode = "200", + description = "소분류 조회 성공", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = ApiResponse.class), + examples = @ExampleObject(value = "{\"isSuccess\":true,\"code\":\"COMMON2000\",\"message\":\"소분류 조회에 성공했습니다.\",\"result\":{\"upperClassId\":10,\"upperClassName\":\"백엔드\",\"lowerClassifications\":[{\"classificationId\":100,\"classificationName\":\"Java/Spring\"},{\"classificationId\":101,\"classificationName\":\"Node.js\"}]},\"error\":null}") + ) + ), + @io.swagger.v3.oas.annotations.responses.ApiResponse( + responseCode = "404", + description = "중분류를 찾을 수 없음", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = ApiResponse.class), + examples = @ExampleObject(value = "{\"isSuccess\":false,\"code\":\"CLASSIFICATION_4041\",\"message\":\"분류를 찾을 수 없습니다.\",\"result\":null,\"error\":\"해당 중분류를 찾을 수 없습니다. middleId=999\"}") + ) + ) + }) + @GetMapping("/middles/{middleId}/details") + public ApiResponse getDetailClassifications(@PathVariable Long middleId) { + return ApiResponse.onSuccess( + "소분류 조회에 성공했습니다.", + classificationService.getDetailClassifications(middleId) + ); + } +} diff --git a/src/main/java/com/jobdri/jobdri_api/domain/classification/dto/response/ClassificationLowerResponseDto.java b/src/main/java/com/jobdri/jobdri_api/domain/classification/dto/response/ClassificationLowerResponseDto.java new file mode 100644 index 0000000..ff527e4 --- /dev/null +++ b/src/main/java/com/jobdri/jobdri_api/domain/classification/dto/response/ClassificationLowerResponseDto.java @@ -0,0 +1,8 @@ +package com.jobdri.jobdri_api.domain.classification.dto.response; + +import java.util.List; + +public record ClassificationLowerResponseDto(Long upperClassId, + String upperClassName, + List lowerClassifications) { +} diff --git a/src/main/java/com/jobdri/jobdri_api/domain/classification/dto/response/ClassificationResponseDto.java b/src/main/java/com/jobdri/jobdri_api/domain/classification/dto/response/ClassificationResponseDto.java new file mode 100644 index 0000000..4dd601b --- /dev/null +++ b/src/main/java/com/jobdri/jobdri_api/domain/classification/dto/response/ClassificationResponseDto.java @@ -0,0 +1,4 @@ +package com.jobdri.jobdri_api.domain.classification.dto.response; + +public record ClassificationResponseDto(Long classificationId, String classificationName) { +} diff --git a/src/main/java/com/jobdri/jobdri_api/domain/classification/service/ClassificationService.java b/src/main/java/com/jobdri/jobdri_api/domain/classification/service/ClassificationService.java new file mode 100644 index 0000000..79fa380 --- /dev/null +++ b/src/main/java/com/jobdri/jobdri_api/domain/classification/service/ClassificationService.java @@ -0,0 +1,79 @@ +package com.jobdri.jobdri_api.domain.classification.service; + +import com.jobdri.jobdri_api.domain.classification.dto.response.ClassificationLowerResponseDto; +import com.jobdri.jobdri_api.domain.classification.dto.response.ClassificationResponseDto; +import com.jobdri.jobdri_api.domain.classification.entity.Classification; +import com.jobdri.jobdri_api.domain.classification.entity.MiddleClassification; +import com.jobdri.jobdri_api.domain.classification.repository.ClassificationRepository; +import com.jobdri.jobdri_api.domain.classification.repository.DetailClassificationRepository; +import com.jobdri.jobdri_api.domain.classification.repository.MiddleClassificationRepository; +import com.jobdri.jobdri_api.global.apiPayload.code.GeneralErrorCode; +import com.jobdri.jobdri_api.global.apiPayload.exception.GeneralException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class ClassificationService { + + private final ClassificationRepository classificationRepository; + private final DetailClassificationRepository detailClassificationRepository; + private final MiddleClassificationRepository middleClassificationRepository; + + public List getBigClassifications() { + return classificationRepository.findAll().stream() + .map(classification -> new ClassificationResponseDto( + classification.getId(), + classification.getBigName() + )) + .toList(); + } + + public ClassificationLowerResponseDto getMiddleClassifications(Long bigId) { + Classification classification = classificationRepository.findById(bigId) + .orElseThrow(() -> new GeneralException( + GeneralErrorCode.CLASSIFICATION_NOT_FOUND, + "해당 대분류를 찾을 수 없습니다. bigId=" + bigId + )); + + List middleClassifications = middleClassificationRepository + .findAllByClassificationId(bigId).stream() + .map(middleClassification -> new ClassificationResponseDto( + middleClassification.getId(), + middleClassification.getMiddleName() + )) + .toList(); + + return new ClassificationLowerResponseDto( + classification.getId(), + classification.getBigName(), + middleClassifications + ); + } + + public ClassificationLowerResponseDto getDetailClassifications(Long middleId) { + MiddleClassification middleClassification = middleClassificationRepository.findById(middleId) + .orElseThrow(() -> new GeneralException( + GeneralErrorCode.CLASSIFICATION_NOT_FOUND, + "해당 중분류를 찾을 수 없습니다. middleId=" + middleId + )); + + List detailClassifications = detailClassificationRepository + .findAllByMiddleClassificationId(middleId).stream() + .map(detailClassification -> new ClassificationResponseDto( + detailClassification.getId(), + detailClassification.getDetailName() + )) + .toList(); + + return new ClassificationLowerResponseDto( + middleClassification.getId(), + middleClassification.getMiddleName(), + detailClassifications + ); + } +} diff --git a/src/main/java/com/jobdri/jobdri_api/global/apiPayload/code/GeneralErrorCode.java b/src/main/java/com/jobdri/jobdri_api/global/apiPayload/code/GeneralErrorCode.java index 81c8f19..8bb0bad 100644 --- a/src/main/java/com/jobdri/jobdri_api/global/apiPayload/code/GeneralErrorCode.java +++ b/src/main/java/com/jobdri/jobdri_api/global/apiPayload/code/GeneralErrorCode.java @@ -29,6 +29,9 @@ public enum GeneralErrorCode implements BaseErrorCode { INVALID_PARAMETER(HttpStatus.BAD_REQUEST, "REQ_4002", "파라미터 형식이 잘못되었습니다."), UNSUPPORTED_CONTENT_TYPE(HttpStatus.UNSUPPORTED_MEDIA_TYPE, "REQ_4151", "지원하지 않는 Content-Type입니다."), + // 분류 에러 + CLASSIFICATION_NOT_FOUND(HttpStatus.NOT_FOUND, "CLASSIFICATION_4041", "분류를 찾을 수 없습니다."), + // 유저 에러 USER_NOT_FOUND(HttpStatus.NOT_FOUND, "USER_4041", "유저를 찾을 수 없습니다.");