package com.kamco.cd.kamcoback.trainingdata; import com.kamco.cd.kamcoback.code.dto.CommonCodeDto; import com.kamco.cd.kamcoback.config.api.ApiResponseDto; import com.kamco.cd.kamcoback.config.api.ApiResponseDto.ResponseObj; import com.kamco.cd.kamcoback.trainingdata.dto.TrainingDataReviewDto; import com.kamco.cd.kamcoback.trainingdata.dto.TrainingDataReviewDto.ReviewGeometryInfo; import com.kamco.cd.kamcoback.trainingdata.dto.TrainingDataReviewDto.ReviewListDto; import com.kamco.cd.kamcoback.trainingdata.service.TrainingDataReviewService; import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @Tag(name = "라벨링 툴 > 검수자", description = "라벨링 툴 > 검수자 API") @RestController @RequiredArgsConstructor @RequestMapping("/api/training-data/review") public class TrainingDataReviewApiController { private final TrainingDataReviewService trainingDataReviewService; @Operation(summary = "목록 조회", description = "검수 할당 목록 조회") @ApiResponses( value = { @ApiResponse( responseCode = "200", description = "조회 성공", content = @Content( mediaType = "application/json", schema = @Schema(implementation = CommonCodeDto.Basic.class))), @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @GetMapping public ApiResponseDto> findReviewAssignedList( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "20") int size, @RequestParam(defaultValue = "01022223333") String userId) { TrainingDataReviewDto.searchReq searchReq = new TrainingDataReviewDto.searchReq(page, size, ""); return ApiResponseDto.ok(trainingDataReviewService.findReviewAssignedList(searchReq, userId)); } @Hidden @Operation(summary = "상세 Geometry 조회", description = "검수 할당 상세 Geometry 조회") @ApiResponses( value = { @ApiResponse( responseCode = "200", description = "조회 성공", content = @Content( mediaType = "application/json", schema = @Schema(implementation = CommonCodeDto.Basic.class))), @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @GetMapping("/geom-info") public ApiResponseDto findReviewAssignedGeom( @RequestParam(defaultValue = "4f9ebc8b-6635-4177-b42f-7efc9c7b4c02") String operatorUid) { return ApiResponseDto.ok(trainingDataReviewService.findReviewAssignedGeom(operatorUid)); } @Operation(summary = "검수 결과 저장", description = "검수 결과 저장") @ApiResponses( value = { @ApiResponse( responseCode = "200", description = "조회 성공", content = @Content( mediaType = "application/json", schema = @Schema(implementation = CommonCodeDto.Basic.class))), @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @PostMapping public ApiResponseDto saveReviewFeature( @RequestBody TrainingDataReviewDto.GeoFeatureRequest request) { return ApiResponseDto.okObject(trainingDataReviewService.saveReviewFeature(request)); } @Operation(summary = "작업 통계 조회", description = "검수자의 작업 현황 통계를 조회합니다. (전체/미작업/Today 건수)") @ApiResponses( value = { @ApiResponse( responseCode = "200", description = "조회 성공", content = @Content( mediaType = "application/json", schema = @Schema(implementation = TrainingDataReviewDto.SummaryRes.class))), @ApiResponse(responseCode = "400", description = "잘못된 요청", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @GetMapping("/summary") public ApiResponseDto getSummary( @io.swagger.v3.oas.annotations.Parameter( description = "검수자 사번", required = true, example = "01022223333") @RequestParam String userId) { try { System.out.println("[Controller] getSummary called with userId: " + userId); TrainingDataReviewDto.SummaryRes result = trainingDataReviewService.getSummary(userId); System.out.println("[Controller] getSummary result: " + result); return ApiResponseDto.ok(result); } catch (Exception e) { System.err.println("[Controller] getSummary ERROR: " + e.getMessage()); e.printStackTrace(); // 예외 발생 시에도 빈 통계 반환 return ApiResponseDto.ok( TrainingDataReviewDto.SummaryRes.builder() .totalCnt(0L) .undoneCnt(0L) .todayCnt(0L) .build()); } } @Operation(summary = "변화탐지정보 및 실태조사결과 조회", description = "선택한 작업의 변화탐지정보 및 실태조사결과를 조회합니다.") @ApiResponses( value = { @ApiResponse( responseCode = "200", description = "조회 성공", content = @Content( mediaType = "application/json", schema = @Schema(implementation = TrainingDataReviewDto.DetailRes.class))), @ApiResponse(responseCode = "400", description = "잘못된 요청", content = @Content), @ApiResponse(responseCode = "404", description = "데이터를 찾을 수 없음", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @GetMapping("/detail") public ApiResponseDto getDetail( @io.swagger.v3.oas.annotations.Parameter( description = "검수 작업 ID (UUID)", required = true, example = "93c56be8-0246-4b22-b976-2476549733cc") @RequestParam java.util.UUID operatorUid) { return ApiResponseDto.ok(trainingDataReviewService.getDetail(operatorUid)); } @Operation(summary = "검수자 목록 기본정보제공", description = "검수자 목록 기본정보제공") @ApiResponses( value = { @ApiResponse( responseCode = "200", description = "조회 성공", content = @Content( mediaType = "application/json", schema = @Schema(implementation = TrainingDataReviewDto.DetailRes.class))), @ApiResponse(responseCode = "400", description = "잘못된 요청", content = @Content), @ApiResponse(responseCode = "404", description = "데이터를 찾을 수 없음", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @GetMapping("/default-page") public ApiResponseDto getDefaultPagingNumber( @Parameter(description = "사번", example = "01022223333") @RequestParam String userId, @Parameter(description = "페이징 사이즈", example = "20") @RequestParam(defaultValue = "20") Integer size, @Parameter(description = "개별 UUID", example = "79bcdbbe-6ed4-4caa-b4a4-22f3cf2f9d25") @RequestParam(required = false) String operatorUid) { return ApiResponseDto.ok( trainingDataReviewService.getDefaultPagingNumber(userId, size, operatorUid)); } @Operation( summary = "새로운 polygon(들) 추가 저장", description = "탐지결과 외 새로운 polygon을 추가로 저장합니다. 단일 또는 여러 개를 저장할 수 있습니다.") @ApiResponses( value = { @ApiResponse( responseCode = "200", description = "저장 성공", content = @Content( mediaType = "application/json", schema = @Schema(implementation = ResponseObj.class))), @ApiResponse(responseCode = "400", description = "잘못된 요청", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @PostMapping("/new-polygon") public ApiResponseDto saveNewPolygon( @io.swagger.v3.oas.annotations.parameters.RequestBody( description = "새로운 polygon 저장 요청", required = true, content = @Content( mediaType = "application/json", schema = @Schema(implementation = TrainingDataReviewDto.NewPolygonRequest.class), examples = { @io.swagger.v3.oas.annotations.media.ExampleObject( name = "1개 polygon 저장", value = """ { "operatorUid": "93c56be8-0246-4b22-b976-2476549733cc", "analUid": 53, "mapSheetNum": "35905086", "compareYyyy": 2023, "targetYyyy": 2024, "features": [ { "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [ [ [126.663, 34.588], [126.662, 34.587], [126.664, 34.589], [126.663, 34.588] ] ] }, "properties": { "beforeClass": "WASTE", "afterClass": "LAND" } } ] } """), @io.swagger.v3.oas.annotations.media.ExampleObject( name = "3개 polygon 저장", value = """ { "operatorUid": "93c56be8-0246-4b22-b976-2476549733cc", "analUid": 53, "mapSheetNum": "35905086", "compareYyyy": 2023, "targetYyyy": 2024, "features": [ { "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [ [ [126.663, 34.588], [126.662, 34.587], [126.664, 34.589], [126.663, 34.588] ] ] }, "properties": { "beforeClass": "WASTE", "afterClass": "LAND" } }, { "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [ [ [126.665, 34.590], [126.664, 34.589], [126.666, 34.591], [126.665, 34.590] ] ] }, "properties": { "beforeClass": "FOREST", "afterClass": "BUILDING" } }, { "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [ [ [126.667, 34.592], [126.666, 34.591], [126.668, 34.593], [126.667, 34.592] ] ] }, "properties": { "beforeClass": "FARMLAND", "afterClass": "SOLAR_PANEL" } } ] } """) })) @RequestBody TrainingDataReviewDto.NewPolygonRequest request) { return ApiResponseDto.okObject(trainingDataReviewService.saveNewPolygon(request)); } @Operation(summary = "COG 이미지 URL 조회", description = "변화 전/후 COG 이미지 URL을 함께 조회합니다") @ApiResponses( value = { @ApiResponse( responseCode = "200", description = "조회 성공", content = @Content( mediaType = "application/json", schema = @Schema(implementation = TrainingDataReviewDto.CogImageResponse.class))), @ApiResponse(responseCode = "404", description = "이미지를 찾을 수 없음", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @GetMapping("/cog-image") public ApiResponseDto getCogImageUrl( @Parameter(description = "도엽번호", required = true, example = "35905086") @RequestParam String mapSheetNum, @Parameter(description = "변화 전 년도", required = true, example = "2023") @RequestParam Integer beforeYear, @Parameter(description = "변화 후 년도", required = true, example = "2024") @RequestParam Integer afterYear) { return ApiResponseDto.ok( trainingDataReviewService.getCogImageUrl(mapSheetNum, beforeYear, afterYear)); } }