在线升级功能API设计与实现

下面我将设计一个基于Spring Boot的在线升级功能API,包括数据库表设计、API接口和详细实现代码。

  1. 数据库设计
    版本信息表 (app_version)
    SQL
    CREATE TABLE app_version (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    version_code INT NOT NULL COMMENT '版本号(数字)',
    version_name VARCHAR(50) NOT NULL COMMENT '版本名称(如v1.0.0)',
    app_type VARCHAR(50) NOT NULL COMMENT '应用类型(如android/ios/web)',
    download_url VARCHAR(255) NOT NULL COMMENT '下载地址',
    upgrade_desc TEXT COMMENT '升级描述',
    file_size BIGINT COMMENT '文件大小(字节)',
    file_md5 VARCHAR(32) COMMENT '文件MD5值',
    is_force TINYINT(1) DEFAULT 0 COMMENT '是否强制升级(0-否 1-是)',
    min_support_version INT COMMENT '最低支持版本(低于此版本必须升级)',
    publish_time DATETIME COMMENT '发布时间',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY uk_version (version_code, app_type)
    ) COMMENT '应用版本信息表';
    升级记录表 (upgrade_record)
    SQL
    CREATE TABLE upgrade_record (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id BIGINT COMMENT '用户ID',
    device_id VARCHAR(100) COMMENT '设备ID',
    app_type VARCHAR(50) NOT NULL COMMENT '应用类型',
    current_version INT NOT NULL COMMENT '当前版本号',
    target_version INT NOT NULL COMMENT '目标版本号',
    upgrade_status TINYINT DEFAULT 0 COMMENT '升级状态(0-开始 1-下载中 2-下载完成 3-安装中 4-成功 5-失败)',
    download_progress INT DEFAULT 0 COMMENT '下载进度(0-100)',
    ip_address VARCHAR(50) COMMENT 'IP地址',
    network_type VARCHAR(20) COMMENT '网络类型(WIFI/4G等)',
    error_msg VARCHAR(255) COMMENT '错误信息',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_device (device_id, app_type)
    ) COMMENT '升级记录表';
  2. API接口设计
    2.1 版本检查接口
    TEXT
    GET /api/upgrade/check
    参数:
  • appType: 应用类型(android/ios/web)
  • currentVersion: 当前版本号
    响应:
  • 有新版本返回版本信息
  • 无新版本返回204状态码
    2.2 下载统计接口
    TEXT
    POST /api/upgrade/download
    参数:
  • versionCode: 版本号
  • deviceId: 设备ID
  • userId: 用户ID(可选)
    响应:
  • 200状态码表示记录成功
    2.3 升级状态上报接口
    TEXT
    POST /api/upgrade/status
    参数:
  • versionCode: 版本号
  • deviceId: 设备ID
  • status: 升级状态
  • progress: 进度(可选)
  • errorMsg: 错误信息(可选)
    响应:
  • 200状态码表示记录成功
    2.4 版本管理接口(后台)
    TEXT
    GET /api/admin/versions - 获取版本列表
    POST /api/admin/versions - 创建新版本
    PUT /api/admin/versions/{id} - 更新版本信息
    DELETE /api/admin/versions/{id} - 删除版本
  1. Spring Boot实现
    3.1 项目结构
    TEXT
    src/main/java/com/example/upgrade/
    ├── config/ # 配置类
    ├── controller/ # 控制器
    │ ├── api/ # 客户端API
    │ └── admin/ # 管理后台API
    ├── entity/ # 实体类
    ├── repository/ # 数据访问层
    ├── service/ # 业务逻辑层
    ├── dto/ # 数据传输对象
    ├── exception/ # 异常处理
    └── UpgradeApplication.java
    3.2 实体类
    AppVersion.java
    JAVA
    @Entity
    @Table(name = "app_version")
    @Data
    public class AppVersion {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private Integer versionCode;

    @Column(nullable = false, length = 50)
    private String versionName;

    @Column(nullable = false, length = 50)
    private String appType;

    @Column(nullable = false, length = 255)
    private String downloadUrl;

    @Column(columnDefinition = "TEXT")
    private String upgradeDesc;

    private Long fileSize;

    @Column(length = 32)
    private String fileMd5;

    @Column(columnDefinition = "TINYINT(1) DEFAULT 0")
    private Boolean isForce = false;

    private Integer minSupportVersion;

    private LocalDateTime publishTime;

    @CreationTimestamp
    private LocalDateTime createTime;

    @UpdateTimestamp
    private LocalDateTime updateTime;
    }
    UpgradeRecord.java
    JAVA
    @Entity
    @Table(name = "upgrade_record")
    @Data
    public class UpgradeRecord {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private Long userId;

    @Column(length = 100)
    private String deviceId;

    @Column(nullable = false, length = 50)
    private String appType;

    @Column(nullable = false)
    private Integer currentVersion;

    @Column(nullable = false)
    private Integer targetVersion;

    @Column(columnDefinition = "TINYINT DEFAULT 0")
    private Integer upgradeStatus;

    @Column(columnDefinition = "INT DEFAULT 0")
    private Integer downloadProgress;

    @Column(length = 50)
    private String ipAddress;

    @Column(length = 20)
    private String networkType;

    @Column(length = 255)
    private String errorMsg;

    @CreationTimestamp
    private LocalDateTime createTime;

    @UpdateTimestamp
    private LocalDateTime updateTime;
    }
    3.3 Repository接口
    AppVersionRepository.java
    JAVA
    public interface AppVersionRepository extends JpaRepository<AppVersion, Long> {
    Optional findTopByAppTypeOrderByVersionCodeDesc(String appType);

    Optional findByVersionCodeAndAppType(Integer versionCode, String appType);

    List findByAppTypeOrderByVersionCodeDesc(String appType);
    }
    UpgradeRecordRepository.java
    JAVA
    public interface UpgradeRecordRepository extends JpaRepository<UpgradeRecord, Long> {
    Optional findByDeviceIdAndAppType(String deviceId, String appType);
    }
    3.4 Service层
    UpgradeService.java
    JAVA
    @Service
    @RequiredArgsConstructor
    public class UpgradeService {
    private final AppVersionRepository versionRepository;
    private final UpgradeRecordRepository recordRepository;

    /**

    • 检查是否有新版本
      */
      public Optional checkNewVersion(String appType, Integer currentVersion) {
      // 获取最新版本
      Optional latestVersionOpt = versionRepository.findTopByAppTypeOrderByVersionCodeDesc(appType);
      if (latestVersionOpt.isEmpty()) {
      return Optional.empty();
      }

      AppVersion latestVersion = latestVersionOpt.get();
      // 如果当前版本小于最新版本
      if (currentVersion < latestVersion.getVersionCode()) {
      VersionCheckDTO dto = new VersionCheckDTO();
      dto.setHasUpdate(true);
      dto.setForceUpdate(latestVersion.getIsForce() ||
      (latestVersion.getMinSupportVersion() != null &&
      currentVersion < latestVersion.getMinSupportVersion()));
      dto.setVersionInfo(convertToVersionInfo(latestVersion));
      return Optional.of(dto);
      }

      return Optional.empty();
      }

    /**

    • 记录下载开始
      */
      public void recordDownloadStart(Integer versionCode, String deviceId, Long userId) {
      Optional versionOpt = versionRepository.findById(versionCode.longValue());
      if (versionOpt.isEmpty()) {
      throw new ResourceNotFoundException("Version not found");
      }

      AppVersion version = versionOpt.get();
      UpgradeRecord record = new UpgradeRecord();
      record.setDeviceId(deviceId);
      record.setUserId(userId);
      record.setAppType(version.getAppType());
      record.setCurrentVersion(versionCode);
      record.setTargetVersion(version.getVersionCode());
      record.setUpgradeStatus(0); // 开始下载
      recordRepository.save(record);
      }

    /**

    • 更新升级状态
      */
      public void updateUpgradeStatus(String deviceId, String appType, Integer status,
      Integer progress, String errorMsg) {
      Optional recordOpt = recordRepository.findByDeviceIdAndAppType(deviceId, appType);
      if (recordOpt.isPresent()) {
      UpgradeRecord record = recordOpt.get();
      record.setUpgradeStatus(status);
      if (progress != null) {
      record.setDownloadProgress(progress);
      }
      if (errorMsg != null) {
      record.setErrorMsg(errorMsg);
      }
      recordRepository.save(record);
      }
      }

    private VersionInfoDTO convertToVersionInfo(AppVersion version) {
    VersionInfoDTO dto = new VersionInfoDTO();
    dto.setVersionCode(version.getVersionCode());
    dto.setVersionName(version.getVersionName());
    dto.setDownloadUrl(version.getDownloadUrl());
    dto.setUpgradeDesc(version.getUpgradeDesc());
    dto.setFileSize(version.getFileSize());
    dto.setFileMd5(version.getFileMd5());
    dto.setForce(version.getIsForce());
    dto.setPublishTime(version.getPublishTime());
    return dto;
    }
    }
    3.5 DTO类
    VersionCheckDTO.java
    JAVA
    @Data
    public class VersionCheckDTO {
    private boolean hasUpdate;
    private boolean forceUpdate;
    private VersionInfoDTO versionInfo;
    }
    VersionInfoDTO.java
    JAVA
    @Data
    public class VersionInfoDTO {
    private Integer versionCode;
    private String versionName;
    private String downloadUrl;
    private String upgradeDesc;
    private Long fileSize;
    private String fileMd5;
    private Boolean force;
    private LocalDateTime publishTime;
    }
    UpgradeStatusDTO.java
    JAVA
    @Data
    public class UpgradeStatusDTO {
    @NotBlank
    private String deviceId;

    @NotBlank
    private String appType;

    @NotNull
    @Min(0) @Max(5)
    private Integer status;

    @Min(0) @Max(100)
    private Integer progress;

    private String errorMsg;
    }
    3.6 Controller层
    UpgradeApiController.java (客户端API)
    JAVA
    @RestController
    @RequestMapping("/api/upgrade")
    @RequiredArgsConstructor
    public class UpgradeApiController {
    private final UpgradeService upgradeService;

    @GetMapping("/check")
    public ResponseEntity<?> checkNewVersion(
    @RequestParam String appType,
    @RequestParam Integer currentVersion) {

     Optional<VersionCheckDTO> result = upgradeService.checkNewVersion(appType, currentVersion);
     return result.map(ResponseEntity::ok)
             .orElseGet(() -> ResponseEntity.noContent().build());
    

    }

    @PostMapping("/download")
    public ResponseEntity recordDownload(
    @RequestParam Integer versionCode,
    @RequestParam String deviceId,
    @RequestParam(required = false) Long userId) {

     upgradeService.recordDownloadStart(versionCode, deviceId, userId);
     return ResponseEntity.ok().build();
    

    }

    @PostMapping("/status")
    public ResponseEntity updateStatus(
    @RequestBody @Valid UpgradeStatusDTO statusDTO) {

     upgradeService.updateUpgradeStatus(
             statusDTO.getDeviceId(),
             statusDTO.getAppType(),
             statusDTO.getStatus(),
             statusDTO.getProgress(),
             statusDTO.getErrorMsg());
     
     return ResponseEntity.ok().build();
    

    }
    }
    VersionAdminController.java (管理后台API)
    JAVA
    @RestController
    @RequestMapping("/api/admin/versions")
    @RequiredArgsConstructor
    public class VersionAdminController {
    private final AppVersionRepository versionRepository;

    @GetMapping
    public ResponseEntity<List> listVersions(@RequestParam String appType) {
    List versions = versionRepository.findByAppTypeOrderByVersionCodeDesc(appType);
    return ResponseEntity.ok(versions);
    }

    @PostMapping
    public ResponseEntity createVersion(@RequestBody @Valid AppVersion version) {
    // 检查版本号是否已存在
    if (versionRepository.existsByVersionCodeAndAppType(version.getVersionCode(), version.getAppType())) {
    throw new BusinessException("版本号已存在");
    }

     version.setPublishTime(LocalDateTime.now());
     AppVersion saved = versionRepository.save(version);
     return ResponseEntity.status(HttpStatus.CREATED).body(saved);
    

    }

    @PutMapping("/{id}")
    public ResponseEntity updateVersion(
    @PathVariable Long id,
    @RequestBody @Valid AppVersion version) {

     AppVersion existing = versionRepository.findById(id)
             .orElseThrow(() -> new ResourceNotFoundException("版本不存在"));
     
     // 更新字段
     existing.setVersionName(version.getVersionName());
     existing.setDownloadUrl(version.getDownloadUrl());
     existing.setUpgradeDesc(version.getUpgradeDesc());
     existing.setFileSize(version.getFileSize());
     existing.setFileMd5(version.getFileMd5());
     existing.setIsForce(version.getIsForce());
     existing.setMinSupportVersion(version.getMinSupportVersion());
     
     AppVersion updated = versionRepository.save(existing);
     return ResponseEntity.ok(updated);
    

    }

    @DeleteMapping("/{id}")
    public ResponseEntity deleteVersion(@PathVariable Long id) {
    versionRepository.deleteById(id);
    return ResponseEntity.noContent().build();
    }
    }
    3.7 异常处理
    GlobalExceptionHandler.java
    JAVA
    @RestControllerAdvice
    public class GlobalExceptionHandler {

    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity handleResourceNotFound(ResourceNotFoundException ex) {
    ErrorResponse response = new ErrorResponse("NOT_FOUND", ex.getMessage());
    return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response);
    }

    @ExceptionHandler(BusinessException.class)
    public ResponseEntity handleBusinessException(BusinessException ex) {
    ErrorResponse response = new ErrorResponse("BUSINESS_ERROR", ex.getMessage());
    return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity handleValidationException(MethodArgumentNotValidException ex) {
    String message = ex.getBindingResult().getAllErrors().stream()
    .map(DefaultMessageSourceResolvable::getDefaultMessage)
    .collect(Collectors.joining(", "));
    ErrorResponse response = new ErrorResponse("VALIDATION_ERROR", message);
    return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
    }

    @Data
    @AllArgsConstructor
    static class ErrorResponse {
    private String code;
    private String message;
    }
    }

  2. 安全考虑
    在实际部署时,应该考虑以下安全措施:

管理后台API应添加权限控制,可以使用Spring Security
文件下载URL应使用签名或时效性验证
文件MD5校验防止文件被篡改
敏感操作应记录日志
5. 扩展功能
可以根据需求扩展以下功能:

分渠道升级控制
灰度发布功能
升级统计分析
CDN加速下载
断点续传支持
这个实现提供了一个完整的在线升级功能API,包括版本管理、升级检查、下载统计和状态上报等功能。可以根据实际需求进行调整和扩展。

posted @ 2025-06-25 23:48  升鲜宝供应链管理系统  阅读(80)  评论(0)    收藏  举报