feat: add application API documentation and C++ SDK
- Add complete API documentation for application integration (docs/API_DOCUMENT.md) - Fix check-update API to use version ID comparison instead of string comparison - Add validation: client version must exist in server version list - Add support for patch/incremental updates with base_version check - Add C++ SDK with HTTP client, JSON parser, and crypto support - Add simple_test.cpp for standalone testing on Windows Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -48,6 +48,8 @@ func handleAppGetInfo(c *gin.Context) {
|
||||
|
||||
func handleAppCheckUpdate(c *gin.Context) {
|
||||
appKey := c.Param("appKey")
|
||||
clientVersion := c.Query("version")
|
||||
|
||||
var app model.Application
|
||||
if err := database.DB.Where("app_key = ?", appKey).First(&app).Error; err != nil {
|
||||
response.Error(c, 404, "应用不存在")
|
||||
@@ -59,14 +61,21 @@ func handleAppCheckUpdate(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
clientVersion := c.Query("version")
|
||||
// 查询该应用的所有版本,按ID降序(创建顺序)
|
||||
var allVersions []model.Version
|
||||
if err := database.DB.Where("application_id = ? AND status = ?", app.ID, "active").
|
||||
Order("id DESC").
|
||||
Preload("Files").
|
||||
Find(&allVersions).Error; err != nil {
|
||||
response.Error(c, 500, "获取版本列表失败")
|
||||
return
|
||||
}
|
||||
|
||||
var latestVersion model.Version
|
||||
err := database.DB.Where("application_id = ? AND status = ?", app.ID, "active").Order("created_at DESC").Preload("Files").First(&latestVersion).Error
|
||||
|
||||
if err != nil {
|
||||
// 没有任何版本
|
||||
if len(allVersions) == 0 {
|
||||
response.Success(c, gin.H{
|
||||
"has_update": false,
|
||||
"current_version": clientVersion,
|
||||
"latest_version": "",
|
||||
"download_url": "",
|
||||
"update_notes": "",
|
||||
@@ -77,14 +86,82 @@ func handleAppCheckUpdate(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
hasUpdate := clientVersion != latestVersion.Version
|
||||
if latestVersion.UpdateStrategy == "forced" {
|
||||
hasUpdate = true
|
||||
// 客户端必须提供版本号
|
||||
if clientVersion == "" {
|
||||
response.Error(c, 400, "请提供客户端版本号")
|
||||
return
|
||||
}
|
||||
|
||||
files := make([]gin.H, len(latestVersion.Files))
|
||||
// 找到客户端当前版本对应的版本记录
|
||||
var clientVersionRecord *model.Version
|
||||
for i := range allVersions {
|
||||
if allVersions[i].Version == clientVersion {
|
||||
clientVersionRecord = &allVersions[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 客户端版本不存在于服务器版本列表中,返回错误
|
||||
if clientVersionRecord == nil {
|
||||
response.Error(c, 404, "客户端版本不存在")
|
||||
return
|
||||
}
|
||||
|
||||
// 判断客户端版本状态:检查是否有更高ID的强制更新版本
|
||||
isSuperseded := false
|
||||
for i := range allVersions {
|
||||
v := &allVersions[i]
|
||||
// 存在ID更高(创建时间更晚)的强制更新版本
|
||||
if v.ID > clientVersionRecord.ID && v.UpdateStrategy == "forced" {
|
||||
isSuperseded = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 如果被取代(有强制更新),必须更新
|
||||
// 找到最新的强制更新版本
|
||||
var latestVersion *model.Version
|
||||
var updateStrategy string
|
||||
|
||||
if isSuperseded {
|
||||
// 找到ID最高的强制更新版本
|
||||
for i := range allVersions {
|
||||
v := &allVersions[i]
|
||||
if v.ID > clientVersionRecord.ID && v.UpdateStrategy == "forced" {
|
||||
if latestVersion == nil || v.ID > latestVersion.ID {
|
||||
latestVersion = v
|
||||
}
|
||||
}
|
||||
}
|
||||
updateStrategy = "forced"
|
||||
} else {
|
||||
// 没有强制更新,检查是否有更高ID的版本(普通更新)
|
||||
// 最新版本就是ID最高的版本(已经是按ID降序排列,第一个就是最新)
|
||||
latestVersion = &allVersions[0]
|
||||
|
||||
// 只有当最新版本ID大于客户端版本ID时才需要更新
|
||||
if latestVersion.ID > clientVersionRecord.ID {
|
||||
updateStrategy = latestVersion.UpdateStrategy // optional 或 forced
|
||||
} else {
|
||||
// 已经是最新版本
|
||||
response.Success(c, gin.H{
|
||||
"has_update": false,
|
||||
"current_version": clientVersion,
|
||||
"latest_version": clientVersionRecord.Version,
|
||||
"download_url": "",
|
||||
"update_notes": "",
|
||||
"update_strategy": "",
|
||||
"update_method": "",
|
||||
"files": []interface{}{},
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 构建文件列表
|
||||
files := make([]map[string]interface{}, len(latestVersion.Files))
|
||||
for i, f := range latestVersion.Files {
|
||||
files[i] = gin.H{
|
||||
files[i] = map[string]interface{}{
|
||||
"file_path": f.FilePath,
|
||||
"file_name": f.FileName,
|
||||
"file_size": f.FileSize,
|
||||
@@ -94,20 +171,42 @@ func handleAppCheckUpdate(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"has_update": hasUpdate,
|
||||
// 构建返回结果
|
||||
result := map[string]interface{}{
|
||||
"has_update": true,
|
||||
"current_version": clientVersion,
|
||||
"latest_version": latestVersion.Version,
|
||||
"download_url": latestVersion.FilePath,
|
||||
"file_size": latestVersion.FileSize,
|
||||
"file_hash": latestVersion.FileHash,
|
||||
"entry_file": latestVersion.EntryFile,
|
||||
"update_notes": latestVersion.Description,
|
||||
"update_strategy": latestVersion.UpdateStrategy,
|
||||
"update_strategy": updateStrategy,
|
||||
"update_type": latestVersion.UpdateType,
|
||||
"update_method": latestVersion.UpdateMethod,
|
||||
"changelog": latestVersion.Changelog,
|
||||
"files": files,
|
||||
})
|
||||
}
|
||||
|
||||
// 检查是否有适合客户端版本的增量更新
|
||||
// 查找以客户端版本为基础版本的 patch 更新
|
||||
var patchVersion model.Version
|
||||
err := database.DB.Where("application_id = ? AND update_type = ? AND base_version_id = ? AND status = ?",
|
||||
app.ID, "patch", clientVersionRecord.ID, "active").
|
||||
Order("id DESC").
|
||||
Preload("Files").
|
||||
First(&patchVersion).Error
|
||||
|
||||
if err == nil {
|
||||
// 找到了增量更新
|
||||
result["is_patch"] = true
|
||||
result["base_version"] = clientVersion
|
||||
result["patch_url"] = patchVersion.FilePath
|
||||
result["patch_size"] = patchVersion.FileSize
|
||||
result["patch_hash"] = patchVersion.FileHash
|
||||
}
|
||||
|
||||
response.Success(c, result)
|
||||
}
|
||||
|
||||
func handleAppGetAnnouncements(c *gin.Context) {
|
||||
|
||||
Reference in New Issue
Block a user