package db import ( "fmt" "time" "github.com/samber/lo" jcstypes "gitlink.org.cn/cloudream/jcs-pub/common/types" "gorm.io/gorm" ) type PackageDB struct { *DB } func (db *DB) Package() *PackageDB { return &PackageDB{DB: db} } func (db *PackageDB) GetByID(ctx SQLContext, packageID jcstypes.PackageID) (jcstypes.Package, error) { var ret jcstypes.Package err := ctx.Table("Package").Where("PackageID = ?", packageID).First(&ret).Error return ret, err } func (db *PackageDB) GetByName(ctx SQLContext, bucketID jcstypes.BucketID, name string) (jcstypes.Package, error) { var ret jcstypes.Package err := ctx.Table("Package").Where("BucketID = ? AND Name = ?", bucketID, name).First(&ret).Error return ret, err } func (db *PackageDB) GetDetail(ctx SQLContext, packageID jcstypes.PackageID) (jcstypes.PackageDetail, error) { var pkg jcstypes.Package err := ctx.Table("Package").Where("PackageID = ?", packageID).First(&pkg).Error if err != nil { return jcstypes.PackageDetail{}, err } var ret struct { ObjectCount int64 TotalSize int64 } err = ctx.Table("Object"). Select("COUNT(*) as ObjectCount, SUM(Size) as TotalSize"). Where("PackageID = ?", packageID). First(&ret). Error if err != nil { return jcstypes.PackageDetail{}, err } return jcstypes.PackageDetail{ Package: pkg, ObjectCount: ret.ObjectCount, TotalSize: ret.TotalSize, }, nil } func (db *PackageDB) BatchGetDetailPaged(ctx SQLContext, lastPkgID jcstypes.PackageID, count int) ([]jcstypes.PackageDetail, error) { var pkgs []jcstypes.Package err := ctx.Table("Package"). Where("PackageID > ?", lastPkgID). Order("PackageID ASC"). Limit(count). Find(&pkgs).Error if err != nil { return nil, err } if len(pkgs) == 0 { return nil, nil } minPkgID := pkgs[0].PackageID maxPkgID := pkgs[len(pkgs)-1].PackageID type detail struct { ObjectCount int64 TotalSize int64 } var details []detail err = ctx. Table("Package"). Select("COUNT(ObjectID) as ObjectCount, SUM(Size) as TotalSize"). Joins("left join Object o on Package.PackageID = o.PackageID"). Where("Package.PackageID >= ? AND Package.PackageID <= ?", minPkgID, maxPkgID). Group("Package.PackageID"). Order("Package.PackageID ASC"). Find(&details).Error if err != nil { return nil, err } ret := make([]jcstypes.PackageDetail, len(pkgs)) for i := range pkgs { ret[i] = jcstypes.PackageDetail{ Package: pkgs[i], ObjectCount: details[i].ObjectCount, TotalSize: details[i].TotalSize, } } return ret, nil } func (db *PackageDB) BatchTestPackageID(ctx SQLContext, pkgIDs []jcstypes.PackageID) (map[jcstypes.PackageID]bool, error) { if len(pkgIDs) == 0 { return make(map[jcstypes.PackageID]bool), nil } var avaiIDs []jcstypes.PackageID err := ctx.Table("Package"). Select("PackageID"). Where("PackageID IN ?", pkgIDs). Find(&avaiIDs).Error if err != nil { return nil, err } avaiIDMap := make(map[jcstypes.PackageID]bool) for _, pkgID := range avaiIDs { avaiIDMap[pkgID] = true } return avaiIDMap, nil } func (*PackageDB) BatchGetAllPackageIDs(ctx SQLContext, start int, count int) ([]jcstypes.PackageID, error) { var ret []jcstypes.PackageID err := ctx.Table("Package").Select("PackageID").Limit(count).Offset(start).Find(&ret).Error return ret, err } func (db *PackageDB) GetBucketPackages(ctx SQLContext, bucketID jcstypes.BucketID) ([]jcstypes.Package, error) { var ret []jcstypes.Package err := ctx.Table("Package"). Select("Package.*"). Where("BucketID = ?", bucketID). Find(&ret).Error return ret, err } func (db *PackageDB) GetBucketPackagesByName(ctx SQLContext, bucketName string) ([]jcstypes.Package, error) { var ret []jcstypes.Package err := ctx.Table("Package"). Select("Package.*"). Joins("JOIN Bucket ON Package.BucketID = Bucket.BucketID"). Where("Bucket.Name = ?", bucketName). Find(&ret).Error return ret, err } // 在指定名称的Bucket中查找指定名称的Package func (*PackageDB) GetByFullName(ctx SQLContext, bucketName string, packageName string) (jcstypes.Package, error) { var ret jcstypes.Package err := ctx.Table("Package"). Select("Package.*"). Joins("JOIN Bucket ON Package.BucketID = Bucket.BucketID"). Where("Package.Name = ? AND Bucket.Name = ?", packageName, bucketName). First(&ret).Error return ret, err } func (db *PackageDB) Create(ctx SQLContext, bucketID jcstypes.BucketID, name string, createTime time.Time) (jcstypes.Package, error) { var packageID int64 err := ctx.Table("Package"). Select("PackageID"). Where("Name = ? AND BucketID = ?", name, bucketID). Scan(&packageID).Error if err != nil { return jcstypes.Package{}, err } if packageID != 0 { return jcstypes.Package{}, gorm.ErrDuplicatedKey } newPackage := jcstypes.Package{Name: name, BucketID: bucketID, CreateTime: createTime} if err := ctx.Create(&newPackage).Error; err != nil { return jcstypes.Package{}, fmt.Errorf("insert package failed, err: %w", err) } return newPackage, nil } func (*PackageDB) Delete(ctx SQLContext, packageID jcstypes.PackageID) error { err := ctx.Delete(&jcstypes.Package{}, "PackageID = ?", packageID).Error return err } // 删除与Package相关的所有数据 func (db *PackageDB) DeleteComplete(ctx SQLContext, packageID jcstypes.PackageID) error { if err := db.Package().Delete(ctx, packageID); err != nil { return fmt.Errorf("delete package state: %w", err) } if err := db.ObjectAccessStat().DeleteInPackage(ctx, packageID); err != nil { return fmt.Errorf("delete from object access stat: %w", err) } if err := db.ObjectBlock().DeleteInPackage(ctx, packageID); err != nil { return fmt.Errorf("delete from object block failed, err: %w", err) } if err := db.PinnedObject().DeleteInPackage(ctx, packageID); err != nil { return fmt.Errorf("deleting pinned objects in package: %w", err) } if err := db.Object().DeleteInPackage(ctx, packageID); err != nil { return fmt.Errorf("deleting objects in package: %w", err) } if err := db.PackageAccessStat().DeleteByPackageID(ctx, packageID); err != nil { return fmt.Errorf("deleting package access stat: %w", err) } return nil } func (*PackageDB) ChangeState(ctx SQLContext, packageID jcstypes.PackageID, state string) error { err := ctx.Exec("UPDATE Package SET State = ? WHERE PackageID = ?", state, packageID).Error return err } // 返回ErrRecordNotFound表示没有找到指定名称的Bucket,nil表示找到了 func (*PackageDB) HasPackageIn(ctx SQLContext, bucketID jcstypes.BucketID) error { var pkg jcstypes.Package return ctx.Table("Package").Where("BucketID = ?", bucketID).First(&pkg).Error } func (*PackageDB) Move(ctx SQLContext, packageID jcstypes.PackageID, newBktID jcstypes.BucketID, newName string) error { err := ctx.Table("Package").Where("PackageID = ?", packageID).Update("BucketID", newBktID).Update("Name", newName).Error return err } type AddAccessStatEntry struct { ObjectID jcstypes.ObjectID `json:"objectID"` PackageID jcstypes.PackageID `json:"packageID"` UserSpaceID jcstypes.UserSpaceID `json:"userSpaceID"` Counter float64 `json:"counter"` } func (db *PackageDB) BatchAddPackageAccessStat(ctx SQLContext, entries []AddAccessStatEntry) error { pkgIDs := make(map[jcstypes.PackageID]bool) objIDs := make(map[jcstypes.ObjectID]bool) for _, e := range entries { pkgIDs[e.PackageID] = true objIDs[e.ObjectID] = true } avaiPkgIDs, err := db.Package().BatchTestPackageID(ctx, lo.Keys(pkgIDs)) if err != nil { return fmt.Errorf("batch test package id: %w", err) } avaiObjIDs, err := db.Object().BatchTestObjectID(ctx, lo.Keys(objIDs)) if err != nil { return fmt.Errorf("batch test object id: %w", err) } var willAdds []AddAccessStatEntry for _, e := range entries { if avaiPkgIDs[e.PackageID] && avaiObjIDs[e.ObjectID] { willAdds = append(willAdds, e) } } if len(willAdds) > 0 { err := db.PackageAccessStat().BatchAddCounter(ctx, willAdds) if err != nil { return fmt.Errorf("batch add package access stat counter: %w", err) } err = db.ObjectAccessStat().BatchAddCounter(ctx, willAdds) if err != nil { return fmt.Errorf("batch add object access stat counter: %w", err) } } return nil } // 尝试创建指定名称的Bucket和Package,如果Bucket不存在,则创建Bucket,如果Package已存在,则直接返回已有的Package func (db *PackageDB) TryCreateAll(ctx SQLContext, bktName string, pkgName string) (jcstypes.Package, error) { bkt, err := db.Bucket().GetByName(ctx, bktName) if err == gorm.ErrRecordNotFound { bkt, err = db.Bucket().Create(ctx, bktName, time.Now()) if err != nil { return jcstypes.Package{}, fmt.Errorf("create bucket: %w", err) } } else if err != nil { return jcstypes.Package{}, fmt.Errorf("get bucket by name: %w", err) } pkg, err := db.GetByName(ctx, bkt.BucketID, pkgName) if err == nil { return pkg, nil } if err != gorm.ErrRecordNotFound { return jcstypes.Package{}, fmt.Errorf("get package by name: %w", err) } pkg, err = db.Create(ctx, bkt.BucketID, pkgName, time.Now()) if err != nil { return jcstypes.Package{}, fmt.Errorf("create package: %w", err) } return pkg, nil }