From 2f81beb0d888b4eed66d707ceef5e90d31b2fd0c Mon Sep 17 00:00:00 2001 From: waner Date: Wed, 8 Apr 2026 11:10:26 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8D=87=E7=BA=A7=E4=BB=BB=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/application.yml.example | 4 +++ .../exception/GlobalExceptionHandler.java | 7 ++++ .../upgrade/controller/UpgradeController.java | 7 ++-- .../upgrade/executor/UpgradeTaskRunner.java | 15 ++++++--- src/main/resources/application.yml | 7 +++- .../exception/GlobalExceptionHandlerTest.java | 27 ++++++++++++++++ .../MultipartUploadConfigurationTest.java | 32 +++++++++++++++++++ .../executor/UpgradeTaskRunnerTest.java | 26 +++++++++++++++ 8 files changed, 115 insertions(+), 10 deletions(-) create mode 100644 src/test/java/com/cisd/tms/common/exception/GlobalExceptionHandlerTest.java create mode 100644 src/test/java/com/cisd/tms/config/MultipartUploadConfigurationTest.java diff --git a/config/application.yml.example b/config/application.yml.example index 43f9abe..7e2c3c9 100644 --- a/config/application.yml.example +++ b/config/application.yml.example @@ -1,6 +1,10 @@ spring: application: name: tms-framework + servlet: + multipart: + max-file-size: 104857600 + max-request-size: 104857600 server: port: 8080 diff --git a/src/main/java/com/cisd/tms/common/exception/GlobalExceptionHandler.java b/src/main/java/com/cisd/tms/common/exception/GlobalExceptionHandler.java index 5d6e4b7..162310f 100644 --- a/src/main/java/com/cisd/tms/common/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/cisd/tms/common/exception/GlobalExceptionHandler.java @@ -11,6 +11,7 @@ import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.multipart.MaxUploadSizeExceededException; @RestControllerAdvice public class GlobalExceptionHandler { @@ -45,6 +46,12 @@ public class GlobalExceptionHandler { return ApiResponse.fail(ErrorCode.VALIDATE_FAILED.getCode(), ex.getMessage()).withPath(request.getRequestURI()); } + @ExceptionHandler(MaxUploadSizeExceededException.class) + public ApiResponse handleMaxUploadSizeExceeded(MaxUploadSizeExceededException ex, HttpServletRequest request) { + return ApiResponse.fail(ErrorCode.VALIDATE_FAILED.getCode(), "upload file size exceeds limit") + .withPath(request.getRequestURI()); + } + @ExceptionHandler(PcieCryptoException.class) public ApiResponse handleCryptoCardException(PcieCryptoException ex, HttpServletRequest request) { return ApiResponse.fail(ErrorCode.CRYPTO_CARD_ERROR.getCode(), ex.getMessage()) diff --git a/src/main/java/com/cisd/tms/modules/upgrade/controller/UpgradeController.java b/src/main/java/com/cisd/tms/modules/upgrade/controller/UpgradeController.java index ea7b9e6..c95a677 100644 --- a/src/main/java/com/cisd/tms/modules/upgrade/controller/UpgradeController.java +++ b/src/main/java/com/cisd/tms/modules/upgrade/controller/UpgradeController.java @@ -15,6 +15,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -26,17 +27,13 @@ import org.springframework.web.multipart.MultipartFile; @RestController @RequestMapping("/api/v1") +@RequiredArgsConstructor @Tag(name = "离线升级管理", description = "离线升级包上传、预检、任务创建、执行和升级记录查询接口") public class UpgradeController { private final UpgradeService upgradeService; private final FileService fileService; - public UpgradeController(UpgradeService upgradeService, FileService fileService) { - this.upgradeService = upgradeService; - this.fileService = fileService; - } - @PostMapping("/upgrade-packages") @Operation(summary = "上传离线升级包", description = "上传离线升级包,返回 fileId 供升级预检和任务创建使用。") public ApiResponse uploadPackage( diff --git a/src/main/java/com/cisd/tms/modules/upgrade/executor/UpgradeTaskRunner.java b/src/main/java/com/cisd/tms/modules/upgrade/executor/UpgradeTaskRunner.java index 9dbbd9f..649acc5 100644 --- a/src/main/java/com/cisd/tms/modules/upgrade/executor/UpgradeTaskRunner.java +++ b/src/main/java/com/cisd/tms/modules/upgrade/executor/UpgradeTaskRunner.java @@ -98,8 +98,9 @@ public class UpgradeTaskRunner { } DeviceSoftwareVersionEntity versionEntity = new DeviceSoftwareVersionEntity(); - versionEntity.setComponentCode(task.getTaskType()); - versionEntity.setComponentName(componentName(task.getTaskType())); + String componentCode = componentCode(task.getTaskType()); + versionEntity.setComponentCode(componentCode); + versionEntity.setComponentName(componentName(componentCode)); versionEntity.setCurrentVersion(task.getTargetVersion()); versionEntity.setSourceType("UPGRADE"); versionEntity.setDetectedAt(LocalDateTime.now()); @@ -211,12 +212,18 @@ public class UpgradeTaskRunner { private String componentName(String componentCode) { return switch (trim(componentCode)) { case "TMS" -> "设备管理软件"; - case "RECEIVER" -> "标准收发器"; - case "FIRMWARE" -> "固件"; + case "APP" -> "标准收发器"; default -> componentCode; }; } + private String componentCode(String taskType) { + return switch (trim(taskType)) { + case "RECEIVER" -> "APP"; + default -> trim(taskType); + }; + } + private String trim(String value) { return value == null ? "" : value.trim(); } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index def075e..b834c8d 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -2,6 +2,11 @@ spring: application: # Spring 应用名;用于日志、注册中心标识等。 name: tms-framework + servlet: + multipart: + # Spring/Tomcat multipart 解析上限;必须不小于业务上传上限,否则请求会在进入 Controller 前被拒绝。 + max-file-size: ${TMS_FILE_STORAGE_MAX_FILE_SIZE_BYTES:104857600} + max-request-size: ${TMS_FILE_STORAGE_MAX_FILE_SIZE_BYTES:104857600} profiles: # 默认激活环境;可通过 --spring.profiles.active=prod 覆盖。 active: dev @@ -173,7 +178,7 @@ tms: # 升级任务统一日志目录。 log-dir: ${TMS_UPGRADE_LOG_DIR:/home/tms/tmp/tms-upgrade-logs} # 升级包验签使用的公钥 PEM 文件路径;为空时升级预检会拒绝通过。 - signature-public-key-pem-path: ${TMS_UPGRADE_SIGNATURE_PUBLIC_KEY_PEM_PATH:} + signature-public-key-pem-path: ${TMS_UPGRADE_SIGNATURE_PUBLIC_KEY_PEM_PATH:/home/tms/cert/public_key.pem} mk: init-identify: # SDFE_InitIdentify 旧 PIN(Base64 编码);主密钥初始化接口会读取该值。 diff --git a/src/test/java/com/cisd/tms/common/exception/GlobalExceptionHandlerTest.java b/src/test/java/com/cisd/tms/common/exception/GlobalExceptionHandlerTest.java new file mode 100644 index 0000000..fd47f10 --- /dev/null +++ b/src/test/java/com/cisd/tms/common/exception/GlobalExceptionHandlerTest.java @@ -0,0 +1,27 @@ +package com.cisd.tms.common.exception; + +import com.cisd.tms.common.api.ApiResponse; +import com.cisd.tms.common.enums.ErrorCode; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.web.multipart.MaxUploadSizeExceededException; + +class GlobalExceptionHandlerTest { + + @Test + void shouldHandleMultipartMaxUploadSizeExceededAsValidationFailure() { + GlobalExceptionHandler handler = new GlobalExceptionHandler(); + MockHttpServletRequest request = new MockHttpServletRequest("POST", "/api/v1/upgrade-packages"); + + ApiResponse response = handler.handleMaxUploadSizeExceeded( + new MaxUploadSizeExceededException(104857600), + request + ); + + Assertions.assertFalse(response.isSuccess()); + Assertions.assertEquals(ErrorCode.VALIDATE_FAILED.getCode(), response.getCode()); + Assertions.assertEquals("upload file size exceeds limit", response.getMsg()); + Assertions.assertEquals("/api/v1/upgrade-packages", response.getPath()); + } +} diff --git a/src/test/java/com/cisd/tms/config/MultipartUploadConfigurationTest.java b/src/test/java/com/cisd/tms/config/MultipartUploadConfigurationTest.java new file mode 100644 index 0000000..e202a99 --- /dev/null +++ b/src/test/java/com/cisd/tms/config/MultipartUploadConfigurationTest.java @@ -0,0 +1,32 @@ +package com.cisd.tms.config; + +import java.nio.file.Path; +import java.util.List; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.boot.env.YamlPropertySourceLoader; +import org.springframework.core.env.PropertySource; +import org.springframework.core.io.FileSystemResource; + +class MultipartUploadConfigurationTest { + + @Test + void shouldConfigureSpringMultipartLimitsForLargeUpgradePackages() throws Exception { + FileSystemResource resource = new FileSystemResource(Path.of("src/main/resources/application.yml")); + List> propertySources = new YamlPropertySourceLoader().load("application", resource); + + Object maxFileSize = findProperty(propertySources, "spring.servlet.multipart.max-file-size"); + Object maxRequestSize = findProperty(propertySources, "spring.servlet.multipart.max-request-size"); + + Assertions.assertEquals("${TMS_FILE_STORAGE_MAX_FILE_SIZE_BYTES:104857600}", maxFileSize); + Assertions.assertEquals("${TMS_FILE_STORAGE_MAX_FILE_SIZE_BYTES:104857600}", maxRequestSize); + } + + private Object findProperty(List> propertySources, String name) { + return propertySources.stream() + .filter(source -> source.containsProperty(name)) + .findFirst() + .map(source -> source.getProperty(name)) + .orElse(null); + } +} diff --git a/src/test/java/com/cisd/tms/modules/upgrade/executor/UpgradeTaskRunnerTest.java b/src/test/java/com/cisd/tms/modules/upgrade/executor/UpgradeTaskRunnerTest.java index ce2fca1..92283a6 100644 --- a/src/test/java/com/cisd/tms/modules/upgrade/executor/UpgradeTaskRunnerTest.java +++ b/src/test/java/com/cisd/tms/modules/upgrade/executor/UpgradeTaskRunnerTest.java @@ -56,6 +56,32 @@ class UpgradeTaskRunnerTest { Assertions.assertTrue(log.indexOf("execute") < log.indexOf("verify")); } + @Test + void shouldUpdateAppSoftwareVersionWhenReceiverUpgradeSucceeds() throws Exception { + Path tempDir = Files.createTempDirectory("upgrade-runner-test"); + FileRecordEntity fileRecord = buildPackage(tempDir, false, "#!/bin/sh\necho execute\nexit 0\n", false); + InMemoryFileRecordRepository fileRepository = new InMemoryFileRecordRepository(); + fileRepository.save(fileRecord); + InMemoryUpgradeTaskRepository taskRepository = new InMemoryUpgradeTaskRepository(); + InMemoryDeviceSoftwareVersionRepository versionRepository = new InMemoryDeviceSoftwareVersionRepository(); + + UpgradeTaskRunner runner = new UpgradeTaskRunner( + taskRepository, + fileRepository, + versionRepository, + stagingService(tempDir.resolve("staging"), tempDir.resolve("logs")), + properties(tempDir.resolve("staging"), tempDir.resolve("logs")), + objectMapper + ); + + runner.run(task("UPG-001-R", fileRecord.getFileId(), "RECEIVER", "V1.4.0")); + + Assertions.assertEquals("SUCCESS", taskRepository.status); + Assertions.assertNull(versionRepository.store.get("RECEIVER")); + Assertions.assertEquals("V1.4.0", versionRepository.store.get("APP").getCurrentVersion()); + Assertions.assertEquals("标准收发器", versionRepository.store.get("APP").getComponentName()); + } + @Test void shouldMarkTaskFailedWhenExecuteScriptFails() throws Exception { Path tempDir = Files.createTempDirectory("upgrade-runner-test");