在线升级功能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,包括版本管理、升级检查、下载统计和状态上报等功能。可以根据实际需求进行调整和扩展。

浙公网安备 33010602011771号