From 1e6eb9f248d37a880713dd4e41e33a2f6ff180ee Mon Sep 17 00:00:00 2001
From: “mitao” <“2763622819@qq.com”>
Date: 星期四, 26 三月 2026 18:12:35 +0800
Subject: [PATCH] feat:系统与安全:系统设置、APP版本设置、推送配置、Banner配置、健康科普、活动通知

---
 backend/src/main/java/com/tcm/training/admin/upload/UploadController.java |   69 +++++++++++++++++++++++++++++++---
 1 files changed, 63 insertions(+), 6 deletions(-)

diff --git a/backend/src/main/java/com/tcm/training/admin/upload/UploadController.java b/backend/src/main/java/com/tcm/training/admin/upload/UploadController.java
index fdf149b..2c37f64 100644
--- a/backend/src/main/java/com/tcm/training/admin/upload/UploadController.java
+++ b/backend/src/main/java/com/tcm/training/admin/upload/UploadController.java
@@ -7,6 +7,7 @@
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.multipart.MultipartFile;
+import org.springframework.util.StringUtils;
 
 import java.io.IOException;
 import java.util.Arrays;
@@ -19,12 +20,24 @@
 
     private static final long MAX_SIZE = 5 * 1024 * 1024L; // 5MB
     private static final long VIDEO_MAX_SIZE = 200 * 1024 * 1024L; // 200MB
+    private static final long APP_PACKAGE_MAX_SIZE = 500 * 1024 * 1024L; // 500MB
     private static final Set<String> ALLOWED_TYPES = new HashSet<>(
             Arrays.asList("image/jpeg", "image/png", "image/gif", "image/webp")
     );
     private static final Set<String> ALLOWED_VIDEO_TYPES = new HashSet<>(
             Arrays.asList("video/mp4", "video/quicktime", "video/x-msvideo", "video/x-matroska", "video/webm")
     );
+    private static final Set<String> ALLOWED_APP_PACKAGE_TYPES = new HashSet<>(
+            Arrays.asList(
+                    "application/vnd.android.package-archive",
+                    "application/octet-stream",
+                    "application/x-itunes-ipa",
+                    "application/zip",
+                    "application/x-zip-compressed"
+            )
+    );
+    private static final String PLATFORM_ANDROID = "ANDROID";
+    private static final String PLATFORM_IOS = "IOS";
 
     private final ObsUploadService obsUploadService;
     private final UploadProgressService uploadProgressService;
@@ -52,6 +65,16 @@
         return ApiResponse.ok(storeFile(file, VIDEO_MAX_SIZE, ALLOWED_VIDEO_TYPES, "仅支持 MP4、MOV、AVI、MKV、WebM 格式", "video", uploadTaskId));
     }
 
+    @PostMapping("/upload/app-package")
+    public ApiResponse<UploadResult> uploadAppPackage(
+            @RequestParam("file") MultipartFile file,
+            @RequestParam("platform") String platform) throws IOException {
+        String normalizedPlatform = normalizePlatform(platform);
+        validateAppPackage(file, normalizedPlatform);
+        UploadFileResult uploadResult = obsUploadService.upload(file, "app-package/" + normalizedPlatform.toLowerCase(), null);
+        return ApiResponse.ok(new UploadResult(uploadResult.getUrl(), uploadResult.getFileName()));
+    }
+
     @GetMapping("/upload/video/progress")
     public ApiResponse<UploadProgressInfo> uploadVideoProgress(
             @RequestParam("taskId") String taskId) {
@@ -64,12 +87,7 @@
                                    String typeMessage,
                                    String bizPath,
                                    String uploadTaskId) throws IOException {
-        if (file == null || file.isEmpty()) {
-            throw new IllegalArgumentException("请选择要上传的文件");
-        }
-        if (file.getSize() > maxSize) {
-            throw new IllegalArgumentException("文件大小超出限制");
-        }
+        validateFileSize(file, maxSize);
         String contentType = file.getContentType();
         if (contentType == null || !allowedTypes.contains(contentType)) {
             throw new IllegalArgumentException(typeMessage);
@@ -78,6 +96,45 @@
         return new UploadResult(uploadResult.getUrl(), uploadResult.getFileName());
     }
 
+    private void validateFileSize(MultipartFile file, long maxSize) {
+        if (file == null || file.isEmpty()) {
+            throw new IllegalArgumentException("请选择要上传的文件");
+        }
+        if (file.getSize() > maxSize) {
+            throw new IllegalArgumentException("文件大小超出限制");
+        }
+    }
+
+    private void validateAppPackage(MultipartFile file, String platform) {
+        validateFileSize(file, APP_PACKAGE_MAX_SIZE);
+        String originalName = file.getOriginalFilename();
+        String extension = "";
+        if (StringUtils.hasText(originalName) && originalName.contains(".")) {
+            extension = originalName.substring(originalName.lastIndexOf(".") + 1).toLowerCase();
+        }
+        if (PLATFORM_ANDROID.equals(platform) && !"apk".equals(extension)) {
+            throw new IllegalArgumentException("安卓安装包仅支持 APK 格式");
+        }
+        if (PLATFORM_IOS.equals(platform) && !"ipa".equals(extension)) {
+            throw new IllegalArgumentException("iOS 安装包仅支持 IPA 格式");
+        }
+        String contentType = file.getContentType();
+        if (StringUtils.hasText(contentType) && !ALLOWED_APP_PACKAGE_TYPES.contains(contentType)) {
+            throw new IllegalArgumentException("安装包格式不合法");
+        }
+    }
+
+    private String normalizePlatform(String platform) {
+        if (!StringUtils.hasText(platform)) {
+            throw new IllegalArgumentException("平台类型不能为空");
+        }
+        String normalized = platform.trim().toUpperCase();
+        if (PLATFORM_ANDROID.equals(normalized) || PLATFORM_IOS.equals(normalized)) {
+            return normalized;
+        }
+        throw new IllegalArgumentException("平台类型不合法");
+    }
+
     public static class UploadResult {
         private final String url;
         private final String fileName;

--
Gitblit v1.7.1