package com.goldenchart.controller; import com.goldenchart.dto.VerificationIssueImageDto; import com.goldenchart.service.VerificationIssueImageService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.core.io.Resource; import org.springframework.http.HttpHeaders; 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.IOException; import java.util.List; import java.util.Map; @RestController @RequestMapping("/verification-issues/{issueId}/images") @RequiredArgsConstructor @Slf4j public class VerificationIssueImageController { private final VerificationIssueImageService service; @GetMapping public ResponseEntity> list(@PathVariable Long issueId) { return ResponseEntity.ok(service.listByIssue(issueId)); } @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity upload( @PathVariable Long issueId, @RequestParam("files") List files) { try { return ResponseEntity.ok(service.upload(issueId, files)); } catch (IllegalArgumentException e) { log.warn("[VerificationImage] upload rejected issueId={}: {}", issueId, e.getMessage()); return ResponseEntity.badRequest().body(Map.of("message", toUserMessage(e.getMessage()))); } catch (IOException e) { log.error("[VerificationImage] upload io error issueId={}", issueId, e); return ResponseEntity.internalServerError() .body(Map.of("message", "이미지 저장에 실패했습니다. 잠시 후 다시 시도하세요.")); } } private static String toUserMessage(String code) { if (code == null) return "업로드할 수 없습니다."; return switch (code) { case "unsupported image type" -> "지원하지 않는 이미지 형식입니다. (jpeg, png, gif, webp, bmp)"; case "file too large" -> "이미지 크기는 파일당 5MB 이하여야 합니다."; case "no files", "no valid image files" -> "업로드할 이미지가 없습니다."; default -> code.startsWith("max images exceeded") ? "이슈당 첨부 이미지는 최대 30장까지입니다." : code.startsWith("issue not found") ? "검증 이슈를 찾을 수 없습니다." : code; }; } @GetMapping("/{imageId}/file") public ResponseEntity file( @PathVariable Long issueId, @PathVariable Long imageId) { return service.loadFile(issueId, imageId) .map(resource -> { String type = service.contentType(issueId, imageId).orElse(MediaType.APPLICATION_OCTET_STREAM_VALUE); return ResponseEntity.ok() .contentType(MediaType.parseMediaType(type)) .header(HttpHeaders.CACHE_CONTROL, "private, max-age=3600") .body(resource); }) .orElse(ResponseEntity.notFound().build()); } @DeleteMapping("/{imageId}") public ResponseEntity delete( @PathVariable Long issueId, @PathVariable Long imageId) { try { if (!service.delete(issueId, imageId)) { return ResponseEntity.notFound().build(); } return ResponseEntity.noContent().build(); } catch (IOException e) { return ResponseEntity.internalServerError().build(); } } }