JCS-pub/client/internal/db/package.go

312 lines
9.0 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.

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表示没有找到指定名称的Bucketnil表示找到了
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
}