在線升級功能API設計與實現
下面我將設計一個基于Spring Boot的在線升級功能API,包括數據庫表設計、API接口和詳細實現代碼。
- 數據庫設計
版本信息表 (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 '升級記錄表'; - 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} - 刪除版本
-
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> {
OptionalfindTopByAppTypeOrderByVersionCodeDesc(String appType); Optional
findByVersionCodeAndAppType(Integer versionCode, String appType); List
findByAppTypeOrderByVersionCodeDesc(String appType);
}
UpgradeRecordRepository.java
JAVA
public interface UpgradeRecordRepository extends JpaRepository<UpgradeRecord, Long> {
OptionalfindByDeviceIdAndAppType(String deviceId, String appType);
}
3.4 Service層
UpgradeService.java
JAVA
@Service
@RequiredArgsConstructor
public class UpgradeService {
private final AppVersionRepository versionRepository;
private final UpgradeRecordRepository recordRepository;/**
-
檢查是否有新版本
*/
public OptionalcheckNewVersion(String appType, Integer currentVersion) {
// 獲取最新版本
OptionallatestVersionOpt = 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) {
OptionalversionOpt = 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) {
OptionalrecordOpt = 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 ResponseEntityrecordDownload(
@RequestParam Integer versionCode,
@RequestParam String deviceId,
@RequestParam(required = false) Long userId) {upgradeService.recordDownloadStart(versionCode, deviceId, userId); return ResponseEntity.ok().build();}
@PostMapping("/status")
public ResponseEntityupdateStatus(
@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) {
Listversions = versionRepository.findByAppTypeOrderByVersionCodeDesc(appType);
return ResponseEntity.ok(versions);
}@PostMapping
public ResponseEntitycreateVersion(@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 ResponseEntityupdateVersion(
@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 ResponseEntitydeleteVersion(@PathVariable Long id) {
versionRepository.deleteById(id);
return ResponseEntity.noContent().build();
}
}
3.7 異常處理
GlobalExceptionHandler.java
JAVA
@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntityhandleResourceNotFound(ResourceNotFoundException ex) {
ErrorResponse response = new ErrorResponse("NOT_FOUND", ex.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response);
}@ExceptionHandler(BusinessException.class)
public ResponseEntityhandleBusinessException(BusinessException ex) {
ErrorResponse response = new ErrorResponse("BUSINESS_ERROR", ex.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
}@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntityhandleValidationException(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;
}
} -
-
安全考慮
在實際部署時,應該考慮以下安全措施:
管理后臺API應添加權限控制,可以使用Spring Security
文件下載URL應使用簽名或時效性驗證
文件MD5校驗防止文件被篡改
敏感操作應記錄日志
5. 擴展功能
可以根據需求擴展以下功能:
分渠道升級控制
灰度發布功能
升級統計分析
CDN加速下載
斷點續傳支持
這個實現提供了一個完整的在線升級功能API,包括版本管理、升級檢查、下載統計和狀態上報等功能??梢愿鶕嶋H需求進行調整和擴展。

浙公網安備 33010602011771號