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;