refactor: simplify to maintain only one latest full package per app
- Remove per-version full package storage - Remove RegenerateDependentVersions logic - Only generate full package for the latest version - Simplify package_util.go Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -8,56 +8,44 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"verification-platform-backend/internal/database"
|
|
||||||
"verification-platform-backend/internal/model"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// MergePatchWithFullPackage 合并补丁包和全量包,生成新的全量包
|
// MergePatchWithFullPackage merges a patch with a full package to create a new full package
|
||||||
func MergePatchWithFullPackage(fullPackagePath, patchPath string, appID, userID uint) (string, int64, string, error) {
|
func MergePatchWithFullPackage(fullPackagePath, patchPath string, appID uint) (string, int64, string, error) {
|
||||||
// 读取全量包
|
|
||||||
fullPackageFile := filepath.Join(".", fullPackagePath)
|
fullPackageFile := filepath.Join(".", fullPackagePath)
|
||||||
patchFile := filepath.Join(".", patchPath)
|
patchFile := filepath.Join(".", patchPath)
|
||||||
|
|
||||||
// 创建临时目录
|
|
||||||
tempDir := filepath.Join(os.TempDir(), fmt.Sprintf("merge_%d_%d", appID, time.Now().Unix()))
|
tempDir := filepath.Join(os.TempDir(), fmt.Sprintf("merge_%d_%d", appID, time.Now().Unix()))
|
||||||
if err := os.MkdirAll(tempDir, 0755); err != nil {
|
if err := os.MkdirAll(tempDir, 0755); err != nil {
|
||||||
return "", 0, "", fmt.Errorf("create temp dir failed: %v", err)
|
return "", 0, "", fmt.Errorf("create temp dir failed: %v", err)
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(tempDir)
|
defer os.RemoveAll(tempDir)
|
||||||
|
|
||||||
// 解压全量包
|
|
||||||
extractDir := filepath.Join(tempDir, "extracted")
|
extractDir := filepath.Join(tempDir, "extracted")
|
||||||
if err := os.MkdirAll(extractDir, 0755); err != nil {
|
if err := os.MkdirAll(extractDir, 0755); err != nil {
|
||||||
return "", 0, "", fmt.Errorf("create extract dir failed: %v", err)
|
return "", 0, "", fmt.Errorf("create extract dir failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解压全量包
|
|
||||||
if err := extractZip(fullPackageFile, extractDir); err != nil {
|
if err := extractZip(fullPackageFile, extractDir); err != nil {
|
||||||
return "", 0, "", fmt.Errorf("extract full package failed: %v", err)
|
return "", 0, "", fmt.Errorf("extract full package failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解压补丁包(覆盖同名文件)
|
|
||||||
if err := extractZip(patchFile, extractDir); err != nil {
|
if err := extractZip(patchFile, extractDir); err != nil {
|
||||||
return "", 0, "", fmt.Errorf("extract patch failed: %v", err)
|
return "", 0, "", fmt.Errorf("extract patch failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建新的全量包
|
|
||||||
timestamp := time.Now().Unix()
|
timestamp := time.Now().Unix()
|
||||||
newFileName := fmt.Sprintf("full_%d_%d_%d.zip", userID, appID, timestamp)
|
newFileName := fmt.Sprintf("full_%d_%d.zip", appID, timestamp)
|
||||||
newFilePath := filepath.Join("uploads", "versions", newFileName)
|
newFilePath := filepath.Join("uploads", "versions", newFileName)
|
||||||
|
|
||||||
// 确保目录存在
|
|
||||||
if err := os.MkdirAll(filepath.Dir(newFilePath), 0755); err != nil {
|
if err := os.MkdirAll(filepath.Dir(newFilePath), 0755); err != nil {
|
||||||
return "", 0, "", fmt.Errorf("create dir failed: %v", err)
|
return "", 0, "", fmt.Errorf("create dir failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 打包新的全量包
|
|
||||||
if err := createZipFromDir(extractDir, newFilePath); err != nil {
|
if err := createZipFromDir(extractDir, newFilePath); err != nil {
|
||||||
return "", 0, "", fmt.Errorf("create new full package failed: %v", err)
|
return "", 0, "", fmt.Errorf("create new full package failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取文件大小和哈希
|
|
||||||
fileInfo, err := os.Stat(newFilePath)
|
fileInfo, err := os.Stat(newFilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 0, "", fmt.Errorf("stat new file failed: %v", err)
|
return "", 0, "", fmt.Errorf("stat new file failed: %v", err)
|
||||||
@@ -68,104 +56,11 @@ func MergePatchWithFullPackage(fullPackagePath, patchPath string, appID, userID
|
|||||||
return "", 0, "", fmt.Errorf("calculate hash failed: %v", err)
|
return "", 0, "", fmt.Errorf("calculate hash failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[INFO] Created merged full package: %s, size: %d\n", newFilePath, fileInfo.Size())
|
log.Printf("[INFO] Created merged full package: %s, size: %d", newFilePath, fileInfo.Size())
|
||||||
|
|
||||||
return "/" + filepath.ToSlash(newFilePath), fileInfo.Size(), hash, nil
|
return "/" + filepath.ToSlash(newFilePath), fileInfo.Size(), hash, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegenerateFullPackage 重新生成指定版本的完整包
|
|
||||||
// 用于版本删除后的回退
|
|
||||||
func RegenerateFullPackage(versionID uint) error {
|
|
||||||
var version model.Version
|
|
||||||
if err := database.DB.First(&version, versionID).Error; err != nil {
|
|
||||||
return fmt.Errorf("version not found: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 全量包:完整包就是自己
|
|
||||||
if version.UpdateType == "full" {
|
|
||||||
version.FullPackagePath = version.FilePath
|
|
||||||
version.FullPackageSize = version.FileSize
|
|
||||||
version.FullPackageHash = version.FileHash
|
|
||||||
} else if version.UpdateType == "patch" {
|
|
||||||
// 补丁包:需要找到新的基础版本
|
|
||||||
if version.BaseVersionID == nil {
|
|
||||||
return fmt.Errorf("patch version has no base version")
|
|
||||||
}
|
|
||||||
|
|
||||||
var baseVersion model.Version
|
|
||||||
if err := database.DB.First(&baseVersion, *version.BaseVersionID).Error; err != nil {
|
|
||||||
return fmt.Errorf("base version not found: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 确保基础版本有完整包
|
|
||||||
if baseVersion.FullPackagePath == "" {
|
|
||||||
if err := RegenerateFullPackage(baseVersion.ID); err != nil {
|
|
||||||
return fmt.Errorf("failed to regenerate base version: %v", err)
|
|
||||||
}
|
|
||||||
// 重新加载基础版本
|
|
||||||
database.DB.First(&baseVersion, *version.BaseVersionID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 合并生成新的完整包
|
|
||||||
fullPkgPath, fullPkgSize, fullPkgHash, err := MergePatchWithFullPackage(
|
|
||||||
baseVersion.FullPackagePath,
|
|
||||||
version.FilePath,
|
|
||||||
version.ApplicationID,
|
|
||||||
0, // userID 不重要,用于生成文件名
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to merge: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
version.FullPackagePath = fullPkgPath
|
|
||||||
version.FullPackageSize = fullPkgSize
|
|
||||||
version.FullPackageHash = fullPkgHash
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存更新
|
|
||||||
return database.DB.Model(&version).Updates(map[string]interface{}{
|
|
||||||
"full_package_path": version.FullPackagePath,
|
|
||||||
"full_package_size": version.FullPackageSize,
|
|
||||||
"full_package_hash": version.FullPackageHash,
|
|
||||||
}).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegenerateDependentVersions 重新生成所有依赖指定版本的后续版本
|
|
||||||
// 在删除版本时调用
|
|
||||||
func RegenerateDependentVersions(deletedVersionID uint) error {
|
|
||||||
// 找到所有以被删除版本为基础版本的版本
|
|
||||||
var dependentVersions []model.Version
|
|
||||||
if err := database.DB.Where("base_version_id = ?", deletedVersionID).Find(&dependentVersions).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, dv := range dependentVersions {
|
|
||||||
log.Printf("[INFO] Regenerating dependent version %d after deletion of %d\n", dv.ID, deletedVersionID)
|
|
||||||
|
|
||||||
// 重新找到该应用最新的全量包作为新的基础
|
|
||||||
var newBaseVersion model.Version
|
|
||||||
if err := database.DB.Where("application_id = ? AND update_type = ? AND id != ? AND id < ?",
|
|
||||||
dv.ApplicationID, "full", dv.ID, dv.ID).
|
|
||||||
Order("id DESC").First(&newBaseVersion).Error; err == nil {
|
|
||||||
// 更新基础版本ID
|
|
||||||
dv.BaseVersionID = &newBaseVersion.ID
|
|
||||||
database.DB.Model(&dv).Update("base_version_id", newBaseVersion.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重新生成完整包
|
|
||||||
if err := RegenerateFullPackage(dv.ID); err != nil {
|
|
||||||
log.Printf("[ERROR] Failed to regenerate version %d: %v\n", dv.ID, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// 递归处理依赖这个版本的后续版本
|
|
||||||
RegenerateDependentVersions(dv.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// extractZip 解压zip文件到指定目录
|
|
||||||
func extractZip(zipPath, destDir string) error {
|
func extractZip(zipPath, destDir string) error {
|
||||||
r, err := zip.OpenReader(zipPath)
|
r, err := zip.OpenReader(zipPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -181,7 +76,6 @@ func extractZip(zipPath, destDir string) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确保父目录存在
|
|
||||||
os.MkdirAll(filepath.Dir(destPath), 0755)
|
os.MkdirAll(filepath.Dir(destPath), 0755)
|
||||||
|
|
||||||
destFile, err := os.OpenFile(destPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
|
destFile, err := os.OpenFile(destPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
|
||||||
@@ -207,7 +101,6 @@ func extractZip(zipPath, destDir string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// createZipFromDir 将目录打包成zip
|
|
||||||
func createZipFromDir(srcDir, zipPath string) error {
|
func createZipFromDir(srcDir, zipPath string) error {
|
||||||
zipFile, err := os.Create(zipPath)
|
zipFile, err := os.Create(zipPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -354,7 +354,6 @@ func handleCreateVersionGlobal(c *gin.Context) {
|
|||||||
lastFullVersion.FullPackagePath,
|
lastFullVersion.FullPackagePath,
|
||||||
req.FilePath,
|
req.FilePath,
|
||||||
req.ApplicationID,
|
req.ApplicationID,
|
||||||
userID,
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[ERROR] Failed to merge patch with full package: %v\n", err)
|
log.Printf("[ERROR] Failed to merge patch with full package: %v\n", err)
|
||||||
@@ -571,11 +570,6 @@ func handleBatchDeleteVersions(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除后重新生成依赖这些版本的后续版本
|
|
||||||
for _, id := range req.IDs {
|
|
||||||
RegenerateDependentVersions(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
response.Success(c, nil)
|
response.Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user