2026-04-21 22:07:29 +08:00
|
|
|
package com.xuqm.update.controller;
|
|
|
|
|
|
|
|
|
|
import com.xuqm.common.model.ApiResponse;
|
|
|
|
|
import com.xuqm.update.entity.RnBundleEntity;
|
|
|
|
|
import com.xuqm.update.repository.RnBundleRepository;
|
|
|
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
|
|
|
import org.springframework.http.ResponseEntity;
|
2026-04-24 16:16:33 +08:00
|
|
|
import org.springframework.web.bind.annotation.*;
|
2026-04-21 22:07:29 +08:00
|
|
|
import org.springframework.web.multipart.MultipartFile;
|
|
|
|
|
|
|
|
|
|
import java.time.LocalDateTime;
|
2026-04-24 16:16:33 +08:00
|
|
|
import java.util.List;
|
2026-04-21 22:07:29 +08:00
|
|
|
import java.util.Map;
|
|
|
|
|
import java.util.Optional;
|
|
|
|
|
import java.util.UUID;
|
2026-04-28 21:05:06 +08:00
|
|
|
import com.xuqm.update.service.UpdateAssetService;
|
2026-04-21 22:07:29 +08:00
|
|
|
|
|
|
|
|
@RestController
|
|
|
|
|
@RequestMapping("/api/v1/rn")
|
|
|
|
|
public class RnBundleController {
|
|
|
|
|
|
|
|
|
|
private final RnBundleRepository bundleRepository;
|
2026-04-28 21:05:06 +08:00
|
|
|
private final UpdateAssetService updateAssetService;
|
2026-04-21 22:07:29 +08:00
|
|
|
|
2026-04-27 19:23:11 +08:00
|
|
|
@Value("${update.base-url:https://update.dev.xuqinmin.com}")
|
2026-04-21 22:07:29 +08:00
|
|
|
private String baseUrl;
|
|
|
|
|
|
2026-04-28 21:05:06 +08:00
|
|
|
public RnBundleController(RnBundleRepository bundleRepository, UpdateAssetService updateAssetService) {
|
2026-04-21 22:07:29 +08:00
|
|
|
this.bundleRepository = bundleRepository;
|
2026-04-28 21:05:06 +08:00
|
|
|
this.updateAssetService = updateAssetService;
|
2026-04-21 22:07:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@GetMapping("/update/check")
|
|
|
|
|
public ResponseEntity<ApiResponse<Map<String, Object>>> checkUpdate(
|
|
|
|
|
@RequestParam String appId,
|
|
|
|
|
@RequestParam String moduleId,
|
|
|
|
|
@RequestParam String platform,
|
|
|
|
|
@RequestParam String currentVersion) {
|
|
|
|
|
|
|
|
|
|
RnBundleEntity.Platform p = RnBundleEntity.Platform.valueOf(platform.toUpperCase());
|
|
|
|
|
Optional<RnBundleEntity> latest = bundleRepository
|
|
|
|
|
.findTopByAppIdAndModuleIdAndPlatformAndPublishStatusOrderByCreatedAtDesc(
|
|
|
|
|
appId, moduleId, p, RnBundleEntity.PublishStatus.PUBLISHED);
|
|
|
|
|
|
|
|
|
|
if (latest.isEmpty()) {
|
|
|
|
|
return ResponseEntity.ok(ApiResponse.success(Map.of("needsUpdate", false)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RnBundleEntity b = latest.get();
|
|
|
|
|
boolean needsUpdate = !b.getVersion().equals(currentVersion);
|
|
|
|
|
return ResponseEntity.ok(ApiResponse.success(Map.of(
|
|
|
|
|
"needsUpdate", needsUpdate,
|
|
|
|
|
"latestVersion", b.getVersion(),
|
2026-04-24 10:42:11 +08:00
|
|
|
"downloadUrl", resolvePublicBaseUrl() + "/api/v1/rn/files/" + appId + "/" + platform.toLowerCase() + "/" + moduleId,
|
2026-04-21 22:07:29 +08:00
|
|
|
"md5", b.getMd5(),
|
|
|
|
|
"minCommonVersion", b.getMinCommonVersion() != null ? b.getMinCommonVersion() : "0.0.0",
|
|
|
|
|
"note", b.getNote() != null ? b.getNote() : ""
|
|
|
|
|
)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@PostMapping("/upload")
|
|
|
|
|
public ResponseEntity<ApiResponse<RnBundleEntity>> upload(
|
|
|
|
|
@RequestParam String appId,
|
|
|
|
|
@RequestParam String moduleId,
|
|
|
|
|
@RequestParam RnBundleEntity.Platform platform,
|
|
|
|
|
@RequestParam String version,
|
|
|
|
|
@RequestParam(required = false) String minCommonVersion,
|
|
|
|
|
@RequestParam(required = false) String note,
|
|
|
|
|
@RequestParam MultipartFile bundle) throws Exception {
|
2026-04-28 21:05:06 +08:00
|
|
|
UpdateAssetService.StoredRnBundle stored = updateAssetService.storeRnBundle(
|
|
|
|
|
appId, platform.name(), moduleId, bundle);
|
2026-04-21 22:07:29 +08:00
|
|
|
|
|
|
|
|
RnBundleEntity entity = new RnBundleEntity();
|
|
|
|
|
entity.setId(UUID.randomUUID().toString());
|
|
|
|
|
entity.setAppId(appId);
|
|
|
|
|
entity.setModuleId(moduleId);
|
|
|
|
|
entity.setPlatform(platform);
|
|
|
|
|
entity.setVersion(version);
|
2026-04-28 21:05:06 +08:00
|
|
|
entity.setBundleUrl(stored.bundlePath());
|
|
|
|
|
entity.setMd5(stored.md5());
|
2026-04-21 22:07:29 +08:00
|
|
|
entity.setMinCommonVersion(minCommonVersion);
|
|
|
|
|
entity.setNote(note);
|
|
|
|
|
entity.setPublishStatus(RnBundleEntity.PublishStatus.DRAFT);
|
|
|
|
|
entity.setCreatedAt(LocalDateTime.now());
|
|
|
|
|
return ResponseEntity.ok(ApiResponse.success(bundleRepository.save(entity)));
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-24 16:16:33 +08:00
|
|
|
@GetMapping("/list")
|
|
|
|
|
public ResponseEntity<ApiResponse<List<RnBundleEntity>>> list(
|
|
|
|
|
@RequestParam String appId,
|
|
|
|
|
@RequestParam(required = false) String moduleId,
|
|
|
|
|
@RequestParam(required = false) String platform) {
|
|
|
|
|
List<RnBundleEntity> result;
|
|
|
|
|
if (moduleId != null && platform != null) {
|
|
|
|
|
RnBundleEntity.Platform p = RnBundleEntity.Platform.valueOf(platform.toUpperCase());
|
|
|
|
|
result = bundleRepository.findByAppIdAndModuleIdAndPlatformOrderByCreatedAtDesc(appId, moduleId, p);
|
|
|
|
|
} else if (moduleId != null) {
|
|
|
|
|
result = bundleRepository.findByAppIdAndModuleIdOrderByCreatedAtDesc(appId, moduleId);
|
|
|
|
|
} else {
|
|
|
|
|
result = bundleRepository.findByAppIdOrderByCreatedAtDesc(appId);
|
|
|
|
|
}
|
|
|
|
|
return ResponseEntity.ok(ApiResponse.success(result));
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-21 22:07:29 +08:00
|
|
|
@PostMapping("/{id}/publish")
|
|
|
|
|
public ResponseEntity<ApiResponse<RnBundleEntity>> publish(@PathVariable String id) {
|
|
|
|
|
RnBundleEntity entity = bundleRepository.findById(id).orElseThrow();
|
|
|
|
|
entity.setPublishStatus(RnBundleEntity.PublishStatus.PUBLISHED);
|
2026-04-24 16:16:33 +08:00
|
|
|
entity.setGrayEnabled(false);
|
|
|
|
|
entity.setGrayPercent(0);
|
|
|
|
|
return ResponseEntity.ok(ApiResponse.success(bundleRepository.save(entity)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@PostMapping("/{id}/unpublish")
|
|
|
|
|
public ResponseEntity<ApiResponse<RnBundleEntity>> unpublish(@PathVariable String id) {
|
|
|
|
|
RnBundleEntity entity = bundleRepository.findById(id).orElseThrow();
|
|
|
|
|
entity.setPublishStatus(RnBundleEntity.PublishStatus.DEPRECATED);
|
|
|
|
|
return ResponseEntity.ok(ApiResponse.success(bundleRepository.save(entity)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@PostMapping("/{id}/gray")
|
|
|
|
|
public ResponseEntity<ApiResponse<RnBundleEntity>> gray(
|
|
|
|
|
@PathVariable String id,
|
|
|
|
|
@RequestBody Map<String, Object> body) {
|
|
|
|
|
RnBundleEntity entity = bundleRepository.findById(id).orElseThrow();
|
|
|
|
|
entity.setGrayEnabled(Boolean.TRUE.equals(body.get("enabled")));
|
|
|
|
|
entity.setGrayPercent(body.get("percent") instanceof Number n ? n.intValue() : 0);
|
|
|
|
|
entity.setPublishStatus(RnBundleEntity.PublishStatus.PUBLISHED);
|
2026-04-21 22:07:29 +08:00
|
|
|
return ResponseEntity.ok(ApiResponse.success(bundleRepository.save(entity)));
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-24 10:42:11 +08:00
|
|
|
private String resolvePublicBaseUrl() {
|
|
|
|
|
String normalized = baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl;
|
|
|
|
|
String suffix = "/api/v1/updates";
|
|
|
|
|
if (normalized.endsWith(suffix)) {
|
|
|
|
|
return normalized.substring(0, normalized.length() - suffix.length());
|
|
|
|
|
}
|
|
|
|
|
return normalized;
|
|
|
|
|
}
|
2026-04-21 22:07:29 +08:00
|
|
|
}
|