diff --git a/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/contracts/ContractController.java b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/contracts/ContractController.java index 43e5636..b18338e 100644 --- a/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/contracts/ContractController.java +++ b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/contracts/ContractController.java @@ -16,10 +16,13 @@ import jakarta.validation.Valid; import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; import java.io.File; +import java.io.IOException; import java.time.OffsetDateTime; import java.util.List; import java.util.Map; +import java.util.Set; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.DeleteMapping; @@ -34,6 +37,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.server.ResponseStatusException; @RestController @RequestMapping("/api/v1/contracts") @@ -50,6 +54,16 @@ public class ContractController { this.contractStatusTransitionService = contractStatusTransitionService; } + private static final long MAX_FILE_SIZE = 50 * 1024 * 1024; // 50MB + private static final Set ALLOWED_CONTENT_TYPES = Set.of( + MediaType.APPLICATION_PDF_VALUE, + "image/jpeg", "image/png", "image/tiff", + "application/msword", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "application/vnd.ms-excel", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + ); + @GetMapping public PageResponse list( @RequestParam(value = "page", defaultValue = "0") @Min(0) int page, @@ -113,28 +127,44 @@ public class ContractController { public ResponseEntity> uploadAttachment( @PathVariable Long id, @RequestParam("file") MultipartFile file) { - try { - String uploadDir = System.getProperty("user.dir") + "/uploads/contracts/" + id + "/"; - File dir = new File(uploadDir); - if (!dir.exists()) dir.mkdirs(); - - String fileName = System.currentTimeMillis() + "_" + file.getOriginalFilename(); - File dest = new File(uploadDir + fileName); - file.transferTo(dest); - - PlatformContractAttachment attachment = new PlatformContractAttachment(); - attachment.setContractId(id); - attachment.setFileName(file.getOriginalFilename()); - attachment.setFilePath(dest.getAbsolutePath()); - attachment.setFileSize(file.getSize()); - attachment.setContentType(file.getContentType()); - attachment.setCreatedAt(OffsetDateTime.now()); - attachmentMapper.insert(attachment); - - return ResponseEntity.ok(Map.of("id", attachment.getId(), "fileName", attachment.getFileName())); - } catch (Exception e) { - return ResponseEntity.status(500).body(Map.of("error", e.getMessage())); + if (file.isEmpty()) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "上传文件为空"); } + if (file.getSize() > MAX_FILE_SIZE) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "文件大小超过限制(最大 50MB)"); + } + String contentType = file.getContentType(); + if (contentType == null || !ALLOWED_CONTENT_TYPES.contains(contentType)) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, + "不支持的文件类型: " + (contentType != null ? contentType : "未知")); + } + + String uploadDir = System.getProperty("user.dir") + "/uploads/contracts/" + id + "/"; + File dir = new File(uploadDir); + if (!dir.exists()) dir.mkdirs(); + + String originalName = file.getOriginalFilename(); + String ext = originalName != null && originalName.contains(".") + ? originalName.substring(originalName.lastIndexOf('.')) + : ""; + String storedName = java.util.UUID.randomUUID().toString() + ext; + File dest = new File(uploadDir + storedName); + try { + file.transferTo(dest); + } catch (IOException e) { + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "文件存储失败"); + } + + PlatformContractAttachment attachment = new PlatformContractAttachment(); + attachment.setContractId(id); + attachment.setFileName(originalName); + attachment.setFilePath(dest.getAbsolutePath()); + attachment.setFileSize(file.getSize()); + attachment.setContentType(contentType); + attachment.setCreatedAt(OffsetDateTime.now()); + attachmentMapper.insert(attachment); + + return ResponseEntity.ok(Map.of("id", attachment.getId(), "fileName", attachment.getFileName())); } @GetMapping("/{id}/attachments") diff --git a/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/license/LicenseController.java b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/license/LicenseController.java index 78faa8c..1d06cc2 100644 --- a/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/license/LicenseController.java +++ b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/license/LicenseController.java @@ -22,7 +22,7 @@ public class LicenseController { try { return ResponseEntity.ok(licenseService.create(request)); } catch (Exception e) { - return ResponseEntity.internalServerError().body(Map.of("error", e.getMessage())); + throw new RuntimeException(e); } }