XuqmGroup-Server/update-service/src/main/java/com/xuqm/update/controller/RnBundleController.java
XuqmGroup d7f5fd02c2 docs(server): 添加服务器信息记录和联调接口文档
- 创建信息记录文档,包含项目管理要求、产物范围、Git仓库、制品仓库信息
- 添加服务器部署信息,包括应用服务器、MySQL/Redis服务器、Jenkins服务配置
- 记录邮件服务、DNS/HTTPS证书配置及安全备注
- 创建API联调文档,包含线上入口、ID约定、初始化管理员账号信息
- 添加统一响应格式、常见错误码、鉴权规则说明
- 提供核心接口清单,涵盖tenant-service、im-service、push-service等服务
- 补充curl示例,包含运营平台登录、IM登录、会话管理等操作示例
- 实现会话控制器,支持置顶、免打扰、标记已读、草稿等功能
- 添加全局异常处理器,统一处理业务异常和参数校验错误
- 创建IM管理控制器,提供用户管理、好友请求、黑名单等管理功能
2026-04-29 12:33:25 +08:00

170 行
7.7 KiB
Java

package com.xuqm.update.controller;
import com.xuqm.common.model.ApiResponse;
import com.xuqm.update.entity.RnBundleEntity;
import com.xuqm.update.model.RnBundleInspectResult;
import com.xuqm.update.repository.RnBundleRepository;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import com.xuqm.update.service.UpdateAssetService;
@RestController
@RequestMapping("/api/v1/rn")
public class RnBundleController {
private final RnBundleRepository bundleRepository;
private final UpdateAssetService updateAssetService;
@Value("${update.base-url:https://update.dev.xuqinmin.com}")
private String baseUrl;
public RnBundleController(RnBundleRepository bundleRepository, UpdateAssetService updateAssetService) {
this.bundleRepository = bundleRepository;
this.updateAssetService = updateAssetService;
}
@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(),
"downloadUrl", resolvePublicBaseUrl() + "/api/v1/rn/files/" + appId + "/" + platform.toLowerCase() + "/" + moduleId,
"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(required = false) String moduleId,
@RequestParam(required = false) RnBundleEntity.Platform platform,
@RequestParam(required = false) String version,
@RequestParam(required = false) String minCommonVersion,
@RequestParam(required = false) String note,
@RequestParam MultipartFile bundle) throws Exception {
RnBundleInspectResult inspected = updateAssetService.inspectRnBundle(bundle);
String resolvedModuleId = hasText(moduleId) ? moduleId : inspected.moduleId();
String resolvedVersion = hasText(version) ? version : inspected.version();
String resolvedMinCommonVersion = hasText(minCommonVersion) ? minCommonVersion : inspected.minCommonVersion();
RnBundleEntity.Platform resolvedPlatform = platform != null ? platform : parsePlatform(inspected.platform());
if (!hasText(resolvedModuleId) || !hasText(resolvedVersion) || resolvedPlatform == null) {
throw new IllegalArgumentException("moduleId, version and platform are required or must be readable from the bundle name");
}
UpdateAssetService.StoredRnBundle stored = updateAssetService.storeRnBundle(
appId, resolvedPlatform.name(), resolvedModuleId, bundle);
RnBundleEntity entity = new RnBundleEntity();
entity.setId(UUID.randomUUID().toString());
entity.setAppId(appId);
entity.setModuleId(resolvedModuleId);
entity.setPlatform(resolvedPlatform);
entity.setVersion(resolvedVersion);
entity.setBundleUrl(stored.bundlePath());
entity.setMd5(stored.md5());
entity.setMinCommonVersion(resolvedMinCommonVersion);
entity.setNote(note);
entity.setPublishStatus(RnBundleEntity.PublishStatus.DRAFT);
entity.setCreatedAt(LocalDateTime.now());
return ResponseEntity.ok(ApiResponse.success(bundleRepository.save(entity)));
}
@PostMapping("/inspect")
public ResponseEntity<ApiResponse<RnBundleInspectResult>> inspect(@RequestParam(required = false) MultipartFile bundle) throws Exception {
return ResponseEntity.ok(ApiResponse.success(updateAssetService.inspectRnBundle(bundle)));
}
@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));
}
@PostMapping("/{id}/publish")
public ResponseEntity<ApiResponse<RnBundleEntity>> publish(@PathVariable String id) {
RnBundleEntity entity = bundleRepository.findById(id).orElseThrow();
entity.setPublishStatus(RnBundleEntity.PublishStatus.PUBLISHED);
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);
return ResponseEntity.ok(ApiResponse.success(bundleRepository.save(entity)));
}
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;
}
private boolean hasText(String value) {
return value != null && !value.isBlank();
}
private RnBundleEntity.Platform parsePlatform(String platform) {
if (!hasText(platform)) {
return null;
}
try {
return RnBundleEntity.Platform.valueOf(platform.toUpperCase());
} catch (Exception e) {
return null;
}
}
}