2026-04-28 21:05:06 +08:00
|
|
|
package com.xuqm.update.controller;
|
|
|
|
|
|
|
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
|
|
import com.xuqm.common.model.ApiResponse;
|
|
|
|
|
import com.xuqm.update.entity.AppVersionEntity;
|
|
|
|
|
import com.xuqm.update.entity.RnBundleEntity;
|
|
|
|
|
import com.xuqm.update.model.UnifiedReleaseManifest;
|
|
|
|
|
import com.xuqm.update.model.UnifiedReleaseResult;
|
|
|
|
|
import com.xuqm.update.repository.AppVersionRepository;
|
|
|
|
|
import com.xuqm.update.repository.RnBundleRepository;
|
|
|
|
|
import com.xuqm.update.service.UpdateAssetService;
|
|
|
|
|
import jakarta.servlet.http.HttpServletRequest;
|
|
|
|
|
import org.springframework.http.ResponseEntity;
|
|
|
|
|
import org.springframework.web.bind.annotation.PostMapping;
|
|
|
|
|
import org.springframework.web.bind.annotation.RequestMapping;
|
|
|
|
|
import org.springframework.web.bind.annotation.RequestParam;
|
|
|
|
|
import org.springframework.web.bind.annotation.RestController;
|
|
|
|
|
import org.springframework.web.multipart.MultipartFile;
|
|
|
|
|
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
|
|
|
|
|
|
|
|
|
import java.time.LocalDateTime;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.UUID;
|
|
|
|
|
|
|
|
|
|
@RestController
|
|
|
|
|
@RequestMapping("/api/v1/updates")
|
|
|
|
|
public class UnifiedReleaseController {
|
|
|
|
|
|
|
|
|
|
private final ObjectMapper objectMapper;
|
|
|
|
|
private final AppVersionRepository appVersionRepository;
|
|
|
|
|
private final RnBundleRepository rnBundleRepository;
|
|
|
|
|
private final UpdateAssetService updateAssetService;
|
|
|
|
|
|
|
|
|
|
public UnifiedReleaseController(
|
|
|
|
|
ObjectMapper objectMapper,
|
|
|
|
|
AppVersionRepository appVersionRepository,
|
|
|
|
|
RnBundleRepository rnBundleRepository,
|
|
|
|
|
UpdateAssetService updateAssetService) {
|
|
|
|
|
this.objectMapper = objectMapper;
|
|
|
|
|
this.appVersionRepository = appVersionRepository;
|
|
|
|
|
this.rnBundleRepository = rnBundleRepository;
|
|
|
|
|
this.updateAssetService = updateAssetService;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@PostMapping("/unified/upload")
|
|
|
|
|
public ResponseEntity<ApiResponse<UnifiedReleaseResult>> upload(
|
|
|
|
|
@RequestParam String appId,
|
|
|
|
|
@RequestParam String manifest,
|
|
|
|
|
HttpServletRequest request) throws Exception {
|
|
|
|
|
|
|
|
|
|
if (!(request instanceof MultipartHttpServletRequest multipartRequest)) {
|
|
|
|
|
throw new IllegalArgumentException("multipart request required");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UnifiedReleaseManifest unifiedReleaseManifest =
|
|
|
|
|
objectMapper.readValue(manifest, UnifiedReleaseManifest.class);
|
|
|
|
|
|
|
|
|
|
List<AppVersionEntity> appVersions = new ArrayList<>();
|
|
|
|
|
for (UnifiedReleaseManifest.AppUploadItem item : safeList(unifiedReleaseManifest.appVersions())) {
|
|
|
|
|
MultipartFile file = multipartRequest.getFile(item.fileKey());
|
2026-04-29 19:08:13 +08:00
|
|
|
if (item.platform() == AppVersionEntity.Platform.ANDROID && file == null) {
|
|
|
|
|
throw new IllegalArgumentException("Android app version requires an uploaded package file");
|
|
|
|
|
}
|
2026-04-28 21:05:06 +08:00
|
|
|
AppVersionEntity entity = new AppVersionEntity();
|
|
|
|
|
entity.setId(UUID.randomUUID().toString());
|
|
|
|
|
entity.setAppId(appId);
|
|
|
|
|
entity.setPlatform(item.platform());
|
|
|
|
|
entity.setVersionName(item.versionName());
|
|
|
|
|
entity.setVersionCode(item.versionCode());
|
|
|
|
|
entity.setChangeLog(item.changeLog());
|
|
|
|
|
entity.setForceUpdate(item.forceUpdate());
|
2026-04-29 15:46:40 +08:00
|
|
|
entity.setPackageName(item.packageName());
|
2026-04-28 21:05:06 +08:00
|
|
|
entity.setAppStoreUrl(item.appStoreUrl());
|
|
|
|
|
entity.setMarketUrl(item.marketUrl());
|
|
|
|
|
entity.setPublishStatus(AppVersionEntity.PublishStatus.DRAFT);
|
|
|
|
|
entity.setCreatedAt(LocalDateTime.now());
|
2026-04-29 19:08:13 +08:00
|
|
|
entity.setDownloadUrl(item.platform() == AppVersionEntity.Platform.ANDROID && file != null
|
|
|
|
|
? updateAssetService.storeAppPackage(file)
|
|
|
|
|
: null);
|
2026-04-29 15:46:40 +08:00
|
|
|
if (item.publishImmediately()) {
|
|
|
|
|
entity.setPublishStatus(AppVersionEntity.PublishStatus.PUBLISHED);
|
|
|
|
|
entity.setGrayEnabled(false);
|
|
|
|
|
entity.setGrayPercent(0);
|
|
|
|
|
}
|
2026-04-28 21:05:06 +08:00
|
|
|
appVersions.add(appVersionRepository.save(entity));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<RnBundleEntity> rnBundles = new ArrayList<>();
|
|
|
|
|
for (UnifiedReleaseManifest.RnBundleUploadItem item : safeList(unifiedReleaseManifest.rnBundles())) {
|
|
|
|
|
MultipartFile file = multipartRequest.getFile(item.fileKey());
|
|
|
|
|
UpdateAssetService.StoredRnBundle stored = updateAssetService.storeRnBundle(
|
|
|
|
|
appId,
|
|
|
|
|
item.platform().name(),
|
|
|
|
|
item.moduleId(),
|
|
|
|
|
file);
|
|
|
|
|
|
|
|
|
|
RnBundleEntity entity = new RnBundleEntity();
|
|
|
|
|
entity.setId(UUID.randomUUID().toString());
|
|
|
|
|
entity.setAppId(appId);
|
|
|
|
|
entity.setModuleId(item.moduleId());
|
|
|
|
|
entity.setPlatform(item.platform());
|
|
|
|
|
entity.setVersion(item.version());
|
|
|
|
|
entity.setBundleUrl(stored.bundlePath());
|
|
|
|
|
entity.setMd5(stored.md5());
|
|
|
|
|
entity.setMinCommonVersion(item.minCommonVersion());
|
2026-04-29 15:46:40 +08:00
|
|
|
entity.setPackageName(item.packageName());
|
2026-04-28 21:05:06 +08:00
|
|
|
entity.setNote(item.note());
|
|
|
|
|
entity.setPublishStatus(RnBundleEntity.PublishStatus.DRAFT);
|
|
|
|
|
entity.setCreatedAt(LocalDateTime.now());
|
|
|
|
|
rnBundles.add(rnBundleRepository.save(entity));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ResponseEntity.ok(ApiResponse.success(new UnifiedReleaseResult(appVersions, rnBundles)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static <T> List<T> safeList(List<T> input) {
|
|
|
|
|
return input == null ? List.of() : input;
|
|
|
|
|
}
|
|
|
|
|
}
|