package com.kamco.cd.kamcoback.inference; import com.kamco.cd.kamcoback.common.exception.CustomApiException; import com.kamco.cd.kamcoback.config.api.ApiResponseDto; import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceServerStatusDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceStatusDetailDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.ResultList; import com.kamco.cd.kamcoback.inference.service.InferenceResultService; import com.kamco.cd.kamcoback.log.dto.AuditLogDto; import com.kamco.cd.kamcoback.log.dto.AuditLogDto.DownloadReq; import com.kamco.cd.kamcoback.log.dto.AuditLogDto.searchReq; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.MngYyyyDto; import com.kamco.cd.kamcoback.mapsheet.service.MapSheetMngService; import com.kamco.cd.kamcoback.model.dto.ModelMngDto; import com.kamco.cd.kamcoback.model.service.ModelMngService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.ArraySchema; 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 jakarta.validation.Valid; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.time.LocalDate; import java.util.List; import java.util.UUID; import lombok.RequiredArgsConstructor; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import org.springframework.data.domain.Page; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; 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; import org.springframework.web.util.UriUtils; @Tag(name = "추론관리", description = "추론관리 API") @RequestMapping("/api/inference") @RequiredArgsConstructor @RestController public class InferenceResultApiController { private final InferenceResultService inferenceResultService; private final MapSheetMngService mapSheetMngService; private final ModelMngService modelMngService; @Operation(summary = "추론관리 목록", description = "어드민 홈 > 추론관리 > 추론관리 > 추론관리 목록") @ApiResponses( value = { @ApiResponse( responseCode = "200", description = "검색 성공", content = @Content( mediaType = "application/json", schema = @Schema(implementation = Page.class))), @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @GetMapping("/list") public ApiResponseDto> getInferenceResultList( @Parameter(description = "국유인 반영 여부", example = "Y") @RequestParam(required = false) String applyYn, @Parameter(description = "반영일", example = "2025-01-01") @RequestParam(required = false) LocalDate strtDttm, @Parameter(description = "반영일", example = "2026-01-01") @RequestParam(required = false) LocalDate endDttm, @Parameter(description = "제목", example = "변화탐지") @RequestParam(required = false) String title, @Parameter(description = "페이지 번호 (0부터 시작)", example = "0") @RequestParam(defaultValue = "0") int page, @Parameter(description = "페이지 크기", example = "20") @RequestParam(defaultValue = "20") int size) { InferenceResultDto.SearchListReq req = new InferenceResultDto.SearchListReq(applyYn, strtDttm, endDttm, title, page, size); Page analResList = inferenceResultService.getInferenceResultList(req); return ApiResponseDto.ok(analResList); } @Operation(summary = "추론 진행 여부 확인", description = "어드민 홈 > 추론관리 > 추론관리 > 추론관리 목록") @ApiResponses( value = { @ApiResponse( responseCode = "200", description = "검색 성공", content = @Content( mediaType = "application/json", schema = @Schema( description = "진행 여부 (UUID 있으면 진행중)", type = "UUID", example = "44709877-2e27-4fc5-bacb-8e0328c69b64"))), @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @GetMapping("/processing-yn") public ApiResponseDto getProcessing() { return ApiResponseDto.ok(inferenceResultService.getProcessing()); } @Operation(summary = "년도 목록 조회", description = "어드민 홈 > 추론관리 > 추론목록 > 변화탐지 실행 정보 입력 > 년도 목록 조회") @ApiResponses( value = { @ApiResponse( responseCode = "200", description = "조회 성공", content = @Content( mediaType = "application/json", array = @ArraySchema(schema = @Schema(implementation = Integer.class)))), @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @GetMapping("/years") public ApiResponseDto> findMapSheetMngDoneYyyyList() { return ApiResponseDto.ok(mapSheetMngService.findMapSheetMngDoneYyyyList()); } @Operation(summary = "변화탐지 실행 정보 입력", description = "어드민 홈 > 추론관리 > 추론목록 > 변화탐지 실행 정보 입력") @ApiResponses( value = { @ApiResponse( responseCode = "201", description = "변화탐지 실행 정보 생성 성공", content = @Content( mediaType = "application/json", schema = @Schema(description = "저장 uuid", implementation = UUID.class))), @ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @PostMapping("/reg") public ApiResponseDto saveInferenceInfo( @io.swagger.v3.oas.annotations.parameters.RequestBody( description = "변화탐지 실행 정보 저장 요청 정보", required = true) @RequestBody @Valid InferenceResultDto.RegReq req) { UUID uuid = inferenceResultService.saveInferenceInfo(req); return ApiResponseDto.ok(uuid); } @Operation(summary = "추론 종료", description = "추론 종료") @ApiResponses( value = { @ApiResponse( responseCode = "201", description = "종료 성공", content = @Content( mediaType = "application/json", schema = @Schema(implementation = Page.class))), @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @DeleteMapping("/end") public ApiResponseDto getInferenceGeomList() { inferenceResultService.deleteInferenceEnd(); return null; } @Operation(summary = "분석 모델 선택 조회", description = "변화탐지 실행 정보 입력 모델선택 팝업 ") @ApiResponses( value = { @ApiResponse( responseCode = "200", description = "분석 모델 조회 성공", content = @Content( mediaType = "application/json", schema = @Schema(description = "분석 모델", implementation = Page.class))), @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @GetMapping("/model") public ApiResponseDto> saveInferenceInfo( @Parameter(description = "모델 생성일 시작", example = "2025-12-01") @RequestParam(required = false) LocalDate strtDttm, @Parameter(description = "모델 생성일 종료", example = "2026-01-09") @RequestParam(required = false) LocalDate endDttm, @Parameter(description = "키워드 (모델버전)", example = "M1.H1.E28") @RequestParam(required = false) String searchVal, @Parameter(description = "타입", example = "M1") @RequestParam(required = false) String modelType, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "20") int size) { ModelMngDto.searchReq searchReq = new ModelMngDto.searchReq(page, size, null); Page result = modelMngService.findModelMgmtList(searchReq, strtDttm, endDttm, modelType, searchVal); return ApiResponseDto.ok(result); } // @ApiResponses( // value = { // @ApiResponse( // responseCode = "200", // description = "검색 성공", // content = // @Content( // mediaType = "application/json", // schema = @Schema(implementation = InferenceDetailDto.AnalResSummary.class))), // @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content), // @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) // }) // @GetMapping("/summary/{id}") // public ApiResponseDto getInferenceResultSummary( // @Parameter(description = "목록 id", example = "53") @PathVariable Long id) { // return ApiResponseDto.ok(inferenceResultService.getInferenceResultSummary(id)); // } // // @Operation(summary = "추론관리 분석결과 상세", description = "분석결과 상제 정보 Summary, DashBoard") // @ApiResponses( // value = { // @ApiResponse( // responseCode = "200", // description = "검색 성공", // content = // @Content( // mediaType = "application/json", // schema = @Schema(implementation = InferenceDetailDto.Detail.class))), // @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content), // @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) // }) // @GetMapping("/detail/{id}") // public ApiResponseDto getInferenceDetail( // @Parameter(description = "목록 id", example = "53") @PathVariable Long id) { // return ApiResponseDto.ok(inferenceResultService.getDetail(id)); // } // // @Operation(summary = "추론관리 분석결과 상세 목록", description = "추론관리 분석결과 상세 목록 geojson 데이터 조회") // @ApiResponses( // value = { // @ApiResponse( // responseCode = "200", // description = "검색 성공", // content = // @Content( // mediaType = "application/json", // schema = @Schema(implementation = Page.class))), // @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content), // @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) // }) // @GetMapping("/geom/{id}") // public ApiResponseDto> getInferenceResultGeomList( // @Parameter(description = "분석결과 id", example = "53") @PathVariable Long id, // @Parameter(description = "기준년도 분류", example = "land") @RequestParam(required = false) // String targetClass, // @Parameter(description = "비교년도 분류", example = "waste") @RequestParam(required = false) // String compareClass, // @Parameter(description = "5000:1 도협번호 37801011,37801012") @RequestParam(required = false) // List mapSheetNum, // @Parameter(description = "페이지 번호 (0부터 시작)", example = "0") @RequestParam(defaultValue = "0") // int page, // @Parameter(description = "페이지 크기", example = "20") @RequestParam(defaultValue = "20") // int size, // @Parameter(description = "정렬 조건 (형식: 필드명,방향)", example = "name,asc") // @RequestParam(required = false) // String sort) { // InferenceDetailDto.SearchGeoReq searchGeoReq = // new InferenceDetailDto.SearchGeoReq( // targetClass, compareClass, mapSheetNum, page, size, sort); // Page geomList = // inferenceResultService.getInferenceResultGeomList(id, searchGeoReq); // return ApiResponseDto.ok(geomList); // } @Operation(summary = "추론관리 추론진행 서버 현황", description = "추론관리 추론진행 서버 현황") @ApiResponses( value = { @ApiResponse( responseCode = "200", description = "검색 성공", content = @Content( mediaType = "application/json", schema = @Schema(implementation = Page.class))), @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @GetMapping("/serverStatus") public ApiResponseDto> getInferenceServerStatusList() { return ApiResponseDto.ok(inferenceResultService.getInferenceServerStatusList()); } @Operation(summary = "추론관리 진행현황 상세", description = "어드민 홈 > 추론관리 > 추론관리 > 진행현황 상세") @ApiResponses( value = { @ApiResponse( responseCode = "200", description = "검색 성공", content = @Content( mediaType = "application/json", schema = @Schema(implementation = InferenceStatusDetailDto.class))), @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @GetMapping("/status/{uuid}") public ApiResponseDto getInferenceStatus( @io.swagger.v3.oas.annotations.parameters.RequestBody( description = "추론 진행현황 정보", required = true) @PathVariable UUID uuid) { return ApiResponseDto.ok(inferenceResultService.getInferenceStatus(uuid)); } @Operation(summary = "추론결과 기본정보", description = "추론결과 기본정보") @ApiResponses( value = { @ApiResponse( responseCode = "200", description = "검색 성공", content = @Content( mediaType = "application/json", schema = @Schema(implementation = InferenceDetailDto.AnalResSummary.class))), @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @GetMapping("/infer-result-info") public ApiResponseDto getInferenceResultInfo( @Parameter(description = "회차 uuid", example = "f30e8817-9625-4fff-ba43-c1e6ed2067c4") @RequestParam UUID uuid) { return ApiResponseDto.ok(inferenceResultService.getInferenceResultInfo(uuid)); } @Operation(summary = "추론결과 분류별 탐지 건수", description = "추론결과 분류별 탐지 건수") @ApiResponses( value = { @ApiResponse( responseCode = "200", description = "검색 성공", content = @Content( mediaType = "application/json", schema = @Schema(implementation = InferenceDetailDto.AnalResSummary.class))), @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @GetMapping("/infer-class-count") public ApiResponseDto> getInferenceClassCountList( @Parameter(description = "회차 uuid", example = "242750c5-a627-429b-950a-dce5a87c1c01") @RequestParam UUID uuid) { return ApiResponseDto.ok(inferenceResultService.getInferenceClassCountList(uuid)); } @Operation(summary = "추론관리 분석결과 상세 목록", description = "추론관리 분석결과 상세 목록 geojson 데이터 조회") @ApiResponses( value = { @ApiResponse( responseCode = "200", description = "검색 성공", content = @Content( mediaType = "application/json", schema = @Schema(implementation = Page.class))), @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @GetMapping("/geom-list") public ApiResponseDto> getInferenceGeomList( @Parameter(description = "회차 uuid", example = "242750c5-a627-429b-950a-dce5a87c1c01") @RequestParam(required = true) UUID uuid, @Parameter(description = "기준년도 분류", example = "land") @RequestParam(required = false) String targetClass, @Parameter(description = "비교년도 분류", example = "waste") @RequestParam(required = false) String compareClass, @Parameter(description = "5000:1 도엽번호 37801011,37801012") @RequestParam(required = false) List mapSheetNum, @Parameter(description = "페이지 번호 (0부터 시작)", example = "0") @RequestParam(defaultValue = "0") int page, @Parameter(description = "페이지 크기", example = "20") @RequestParam(defaultValue = "20") int size, @Parameter(description = "정렬 조건 (형식: 필드명,방향)", example = "name,asc") @RequestParam(required = false) String sort) { InferenceDetailDto.SearchGeoReq searchGeoReq = new InferenceDetailDto.SearchGeoReq( targetClass, compareClass, mapSheetNum, page, size, sort); Page geomList = inferenceResultService.getInferenceGeomList(uuid, searchGeoReq); return ApiResponseDto.ok(geomList); } @Operation(summary = "shp 파일 다운로드", description = "추론관리 분석결과 shp 파일 다운로드") @ApiResponses( value = { @ApiResponse( responseCode = "200", description = "shp zip파일 다운로드", content = @Content( mediaType = "application/octet-stream", schema = @Schema(type = "string", format = "binary"))), @ApiResponse(responseCode = "404", description = "파일 없음", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @GetMapping(value = "/download/{uuid}", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) public ResponseEntity downloadShp( @Parameter(description = "uuid", example = "0192efc6-9ec2-43ee-9a90-5b73e763c09f") @PathVariable UUID uuid) throws IOException { String path; try { path = String.valueOf(inferenceResultService.shpDownloadPath(uuid)); } catch (CustomApiException e) { // 데이터 없음 등 404 return ResponseEntity.status(e.getStatus()).build(); } Path zipPath = Path.of(path); FileSystemResource resource = new FileSystemResource(zipPath); if (!resource.exists() || !resource.isReadable()) { return ResponseEntity.notFound().build(); } String filename = zipPath.getFileName().toString(); String encodedFilename = UriUtils.encode(filename, StandardCharsets.UTF_8); return ResponseEntity.ok() .contentType(MediaType.APPLICATION_OCTET_STREAM) .header( HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"; filename*=UTF-8''" + encodedFilename) .contentLength(resource.contentLength()) .body((Resource) resource); } @Operation(summary = "shp 파일 다운로드 이력", description = "추론관리 분석결과 shp 파일 다운로드 이") @GetMapping(value = "/download-audit/{uuid}") @ApiResponses( value = { @ApiResponse( responseCode = "200", description = "검색 성공", content = @Content( mediaType = "application/json", schema = @Schema(implementation = Page.class))), @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) public ApiResponseDto> downloadAudit( @Parameter(description = "UUID", example = "0192efc6-9ec2-43ee-9a90-5b73e763c09f") @PathVariable UUID uuid, @Parameter(description = "구분-NAME(이름), EMPLOYEE_NO(사번)", example = "NAME") @RequestParam(required = false) String type, @Parameter(description = "다운로드일 시작", example = "2025-01-01") @RequestParam(required = false) LocalDate strtDttm, @Parameter(description = "다운로드일 종료", example = "2026-01-01") @RequestParam(required = false) LocalDate endDttm, @Parameter(description = "키워드", example = "관리자") @RequestParam(required = false) String searchValue, @Parameter(description = "페이지 번호 (0부터 시작)", example = "0") @RequestParam(defaultValue = "0") int page, @Parameter(description = "페이지 크기", example = "20") @RequestParam(defaultValue = "20") int size) { AuditLogDto.searchReq searchReq = new searchReq(); searchReq.setPage(page); searchReq.setSize(size); DownloadReq downloadReq = new DownloadReq(); downloadReq.setUuid(uuid); downloadReq.setStartDate(strtDttm); downloadReq.setEndDate(endDttm); downloadReq.setType(type); downloadReq.setSearchValue(searchValue); downloadReq.setMenuId("22"); downloadReq.setRequestUri("/api/inference/download-audit"); return ApiResponseDto.ok(inferenceResultService.getDownloadAudit(searchReq, downloadReq)); } }