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.payload.request.BaseSearchRequest; import kr.re.etri.autoflow.repository.TrainingScriptAttachmentRepository; import kr.re.etri.autoflow.service.TrainingScriptAttachmentService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springdoc.core.annotations.ParameterObject; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.domain.Page; 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.util.*; @RestController @RequestMapping("/api/trainingscript") @RequiredArgsConstructor @Slf4j @Tag(name = "트레이닝 스크립트", description = "트레이닝 스크립트 MinIO 버킷/파일 관리 API") public class TrainingScriptAttachmentController { private final MinioClient minioClient; private final TrainingScriptAttachmentRepository trainingScriptAttachmentRepository; private final TrainingScriptAttachmentService trainingScriptAttachmentService; @Value("${minio.bucket}") private String bucketName; @Value("${minio.endpoint}") private String minioEndpoint; @Operation(summary = "전체 트레이닝 스크립트 목록 조회") @GetMapping public ResponseEntity> getAllTrainingScripts() { return ResponseEntity.ok(trainingScriptAttachmentService.findAll()); } @Operation(summary = "ID로 트레이닝 스크립트 조회") @GetMapping("/{id}") public ResponseEntity getTrainingScriptById( @Parameter(description = "조회할 트레이닝 스크립트 ID", required = true, in = ParameterIn.PATH) @PathVariable("id") Long id) { return trainingScriptAttachmentService.findById(id) .map(ResponseEntity::ok) .orElse(ResponseEntity.notFound().build()); } @Operation(summary = "검색 및 페이지네이션 트레이닝 스크립트 목록 조회") @GetMapping("/search") public ResponseEntity> searchTrainingScripts( @ParameterObject @ModelAttribute BaseSearchRequest request) { Page page = trainingScriptAttachmentService.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 (trainingScriptAttachmentService.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 = "업로드할 파일") @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 ) { 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에 저장 TrainingScriptAttachmentEntity attachment = TrainingScriptAttachmentEntity.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) .build(); TrainingScriptAttachmentEntity saved = trainingScriptAttachmentRepository.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(); } } }