Files
yunzerwebsiteallinone/go/cmd/migrate_storage/main.go
T

121 lines
3.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 存量文件迁移到分层目录(文件存储分层改造 S9)
//
// 用法(在 go/ 目录下执行):
//
// go run ./cmd/migrate_storage # 默认 dry-run,只打印计划,不改动任何数据
// go run ./cmd/migrate_storage --apply # 真正执行迁移
// go run ./cmd/migrate_storage --tid=234573 # 只迁移指定租户
// go run ./cmd/migrate_storage --limit=10 # 只处理前 N 条(调试用)
//
// 迁移原理:
// - 七牛云:BucketManager.Move 同 bucket 内服务端改名,不重新上传、不消耗流量、与文件大小无关
// - 本地 :os.Rename 同盘改名,不搬数据
//
// 幂等:目标 key 已存在时跳过(Skipped),可重复执行。
//
// 注意:老数据没有「个人/共享」归属信息(tuid 全为 NULL、uid 是上传者),
//
// 因此老数据统一按「租户共享」迁移,个人目录只对新上传的文件生效。
package main
import (
"flag"
"fmt"
"os"
"server/models"
"server/services"
)
func main() {
apply := flag.Bool("apply", false, "真正执行迁移;不传则只打印计划(dry-run)")
tid := flag.Uint64("tid", 0, "只迁移指定租户,0 表示全部")
limit := flag.Int("limit", 0, "只处理前 N 条,0 表示不限制")
flag.Parse()
// 注意:需在 go/ 目录下运行,beego 会自动加载 conf/app.conf
models.Init("")
if models.Orm == nil {
fmt.Println("数据库未初始化")
os.Exit(1)
}
models.EnsureSystemFileStorageColumns()
storageSvc, err := services.GetStorageService()
if err != nil {
fmt.Println("获取存储服务失败:", err)
os.Exit(1)
}
fmt.Printf("当前存储类型: %s\n", storageSvc.Type())
qs := models.Orm.QueryTable(new(models.SystemFile)).Filter("delete_time__isnull", true)
if *tid > 0 {
qs = qs.Filter("tid", *tid)
}
if *limit > 0 {
qs = qs.Limit(*limit)
}
var files []models.SystemFile
if _, err := qs.OrderBy("id").All(&files); err != nil {
fmt.Println("读取文件列表失败:", err)
os.Exit(1)
}
fmt.Printf("待处理文件数: %d(模式: %s)\n\n", len(files), modeName(*apply))
planned, skipped, failed := 0, 0, 0
for i := range files {
f := files[i]
oldKey := f.ObjectKey
if oldKey == "" {
oldKey = services.KeyFromSrc(f.Src, storageSvc)
}
if oldKey == "" {
fmt.Printf("[跳过] id=%d 无法解析原路径: %s\n", f.ID, f.Src)
skipped++
continue
}
newKey := services.TargetKey(&f)
if oldKey == newKey {
skipped++
continue
}
planned++
fmt.Printf("[%s] id=%d tid=%d\n 老: %s\n 新: %s\n", modeName(*apply), f.ID, f.Tid, oldKey, newKey)
if !*apply {
continue
}
if err := storageSvc.Move(oldKey, newKey); err != nil {
fmt.Printf(" !! 迁移失败: %v\n", err)
failed++
continue
}
newSrc := storageSvc.GetPublicURL(newKey)
if _, err := models.Orm.QueryTable(new(models.SystemFile)).
Filter("id", f.ID).
Update(map[string]interface{}{
"src": newSrc,
"object_key": newKey,
"storage": storageSvc.Type(),
}); err != nil {
fmt.Printf(" !! 更新数据库失败(文件已移动,请重跑本脚本): %v\n", err)
failed++
}
}
fmt.Printf("\n==== 汇总 ====\n计划迁移: %d\n跳过: %d\n失败: %d\n", planned, skipped, failed)
if !*apply {
fmt.Println("当前为 dry-run,未改动任何数据。确认无误后加 --apply 执行。")
}
}
func modeName(apply bool) string {
if apply {
return "APPLY"
}
return "DRY-RUN"
}