diff --git a/src/main/java/kr/re/etri/autoflow/controllers/DatasetController.java b/src/main/java/kr/re/etri/autoflow/controllers/DatasetController.java new file mode 100644 index 0000000..ac3fc00 --- /dev/null +++ b/src/main/java/kr/re/etri/autoflow/controllers/DatasetController.java @@ -0,0 +1,134 @@ +package kr.re.etri.autoflow.controllers; + +import io.minio.*; +import io.minio.messages.Item; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import kr.re.etri.autoflow.entity.DatasetAttachmentEntity; +import kr.re.etri.autoflow.repository.DatasetAttachmentRepository; +import kr.re.etri.autoflow.service.DataGroupService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.InputStream; +import java.time.LocalDateTime; +import java.util.*; + +@RestController +@RequestMapping("/api/dataset") +@RequiredArgsConstructor +@Slf4j +@Tag(name = "데이터셋 업로드", description = "MinIO 버킷/파일 관리 API") +public class DatasetController { + private final MinioClient minioClient; + private final DatasetAttachmentRepository datasetAttachmentRepository; + private final DataGroupService dataGroupService; + + + @Value("${minio.bucket}") + private String bucketName; + + @Value("${minio.endpoint}") + private String minioEndpoint; + + @Operation(summary = "파일 업로드", description = "MultipartFile을 MinIO 버킷에 업로드하고 DB에 기록합니다.") + @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public ResponseEntity> uploadFile( + @Parameter(description = "업로드할 파일") + @RequestPart("file") MultipartFile file, + @Parameter(description = "저장 경로 (선택)") + @RequestPart(value = "path", required = false) String path, + @RequestParam(value = "title", required = false, defaultValue = "배터리 퍼센트 데이터 셋") String title, + @RequestParam(value = "description", required = false, defaultValue = "배터리 퍼센트 데이터 모음") String description, + @RequestParam(value = "version", required = false, defaultValue = "1") Integer version, + @RequestParam(value = "regUserId") String regUserId, + @RequestParam(value = "refId") Long refId + ) { + try (InputStream is = file.getInputStream()) { + String storedName = UUID.randomUUID() + "-" + file.getOriginalFilename(); + String objectName = (path == null || path.isEmpty()) + ? storedName + : path + "/" + storedName; + + // MinIO 업로드 + minioClient.putObject( + PutObjectArgs.builder() + .bucket(bucketName) + .object(objectName) + .stream(is, is.available(), -1) + .contentType(file.getContentType()) + .build() + ); + + // DB에 저장 + DatasetAttachmentEntity attachment = DatasetAttachmentEntity.builder() + .originalName(file.getOriginalFilename()) + .storedName(storedName) + .contentType(file.getContentType()) + .size(file.getSize()) + .storagePath(objectName) + .title(title != null ? title : file.getOriginalFilename()) + .version(version) + .description(description) + .regUserId(regUserId) + .refId(refId) + .build(); + + DatasetAttachmentEntity saved = datasetAttachmentRepository.save(attachment); + + // MinIO URL 생성 + String minioUrl = String.format("%s/%s/%s", minioEndpoint, bucketName, objectName); + + Map response = new HashMap<>(); + response.put("attachment", saved); + response.put("minioUrl", minioUrl); + + return ResponseEntity.ok(response); + + } catch (Exception e) { + log.error("파일 업로드 실패", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + } + + @Operation(summary = "데이터셋 삭제") + @DeleteMapping("/{id}") + public ResponseEntity deleteDataset( + @Parameter(description = "삭제할 데이터셋 ID", required = true, in = ParameterIn.PATH) + @PathVariable("id") Long id) { + try { + // 1. DB에서 데이터셋 조회 + Optional optional = datasetAttachmentRepository.findById(id); + if (optional.isEmpty()) { + return ResponseEntity.notFound().build(); + } + DatasetAttachmentEntity attachment = optional.get(); + + // 2. MinIO에서 실제 파일 삭제 + minioClient.removeObject( + RemoveObjectArgs.builder() + .bucket(bucketName) + .object(attachment.getStoragePath()) + .build() + ); + + // 3. DB 레코드 삭제 + datasetAttachmentRepository.deleteById(id); + + return ResponseEntity.noContent().build(); + + } catch (Exception e) { + log.error("데이터셋 삭제 실패", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + } +} \ No newline at end of file diff --git a/src/main/java/kr/re/etri/autoflow/controllers/MinIOController.java b/src/main/java/kr/re/etri/autoflow/controllers/MinIOController.java index 0bde85d..1336942 100644 --- a/src/main/java/kr/re/etri/autoflow/controllers/MinIOController.java +++ b/src/main/java/kr/re/etri/autoflow/controllers/MinIOController.java @@ -25,7 +25,7 @@ import java.util.*; @RequestMapping("/api/minio") @RequiredArgsConstructor @Slf4j -@Tag(name = "MinIO 업로드 관련", description = "MinIO 버킷/파일 관리 API") +@Tag(name = "MinIO 스토리지 관련", description = "MinIO 버킷/파일 관리 API") public class MinIOController { private final MinioClient minioClient; private final DatasetAttachmentRepository datasetAttachmentRepository; @@ -195,8 +195,6 @@ public class MinIOController { return ResponseEntity.ok(savedAttachments); } - - @Operation(summary = "파일 다운로드", description = "MinIO에서 파일을 다운로드합니다.") @GetMapping("/download") public ResponseEntity downloadFile(@RequestParam String objectName) { diff --git a/src/main/java/kr/re/etri/autoflow/controllers/TrainingScriptController.java b/src/main/java/kr/re/etri/autoflow/controllers/TrainingScriptController.java index 7b92f18..c8c2a41 100644 --- a/src/main/java/kr/re/etri/autoflow/controllers/TrainingScriptController.java +++ b/src/main/java/kr/re/etri/autoflow/controllers/TrainingScriptController.java @@ -3,9 +3,13 @@ package kr.re.etri.autoflow.controllers; import io.minio.*; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.tags.Tag; import kr.re.etri.autoflow.entity.TrainingScriptAttachmentEntity; +import kr.re.etri.autoflow.entity.TrainingScriptAttachmentEntity; +import kr.re.etri.autoflow.entity.WorkflowEntity; import kr.re.etri.autoflow.payload.request.BaseSearchRequest; +import kr.re.etri.autoflow.payload.request.ProjectRequest; import kr.re.etri.autoflow.repository.TrainingScriptAttachmentRepository; import kr.re.etri.autoflow.service.TrainingScriptService; import lombok.RequiredArgsConstructor; @@ -41,7 +45,46 @@ public class TrainingScriptController { @Value("${minio.endpoint}") private String minioEndpoint; - @Operation(summary = "파일 업로드", description = "MultipartFile을 MinIO 버킷에 업로드하고 DB에 기록합니다.") + + + @Operation(summary = "전체 트레이닝 스크립트 목록 조회") + @GetMapping + public ResponseEntity> getAllTrainingScripts() { + return ResponseEntity.ok(trainingScriptService.findAll()); + } + + @Operation(summary = "ID로 트레이닝 스크립트 조회") + @GetMapping("/{id}") + public ResponseEntity getTrainingScriptById( + @Parameter(description = "조회할 트레이닝 스크립트 ID", required = true, in = ParameterIn.PATH) + @PathVariable("id") Long id) { + + return trainingScriptService.findById(id) + .map(ResponseEntity::ok) + .orElse(ResponseEntity.notFound().build()); + } + + @Operation(summary = "검색 및 페이지네이션 트레이닝 스크립트 목록 조회") + @GetMapping("/search") + public ResponseEntity> searchTrainingScripts( + @ParameterObject @ModelAttribute BaseSearchRequest request) { + Page page = trainingScriptService.search(request); + return ResponseEntity.ok(page); + } + + @Operation(summary = "트레이닝 스크립트 삭제") + @DeleteMapping("/{id}") + public ResponseEntity deleteTrainingScript( + @Parameter(description = "삭제할 트레이닝 스크립트 ID", required = true, in = ParameterIn.PATH) + @PathVariable("id") Long id) { + if (trainingScriptService.delete(id)) { + return ResponseEntity.noContent().build(); + } + return ResponseEntity.notFound().build(); + } + + + @Operation(summary = "파일 업로드 및 트레이닝 스크립트 생성", description = "MultipartFile을 MinIO 버킷에 업로드하고 DB에 기록합니다.") @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity> uploadFile( @Parameter(description = "업로드할 파일") @@ -98,12 +141,4 @@ public class TrainingScriptController { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } } - - @Operation(summary = "검색 및 페이지네이션 프로젝트 목록 조회") - @GetMapping("/search") - public ResponseEntity> searchProjects( - @ParameterObject @ModelAttribute BaseSearchRequest request) { - Page page = trainingScriptService.search(request); - return ResponseEntity.ok(page); - } } \ No newline at end of file