197 lines
6.3 KiB
Go
197 lines
6.3 KiB
Go
package repository
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"yingce/backend/internal/model"
|
|
)
|
|
|
|
type AdminResourceFilter struct {
|
|
Kind string
|
|
Status string
|
|
Provider string
|
|
UserQuery string
|
|
Keyword string
|
|
Limit int
|
|
Offset int
|
|
}
|
|
|
|
type ResourceKindStat struct {
|
|
Kind string `json:"kind" gorm:"column:kind"`
|
|
Count int64 `json:"count" gorm:"column:count"`
|
|
LogicalBytes int64 `json:"logicalBytes" gorm:"column:logical_bytes"`
|
|
PhysicalBytes int64 `json:"physicalBytes" gorm:"column:physical_bytes"`
|
|
}
|
|
|
|
type ResourceProviderStat struct {
|
|
Provider string `json:"provider" gorm:"column:provider"`
|
|
Count int64 `json:"count" gorm:"column:count"`
|
|
LogicalBytes int64 `json:"logicalBytes" gorm:"column:logical_bytes"`
|
|
PhysicalBytes int64 `json:"physicalBytes" gorm:"column:physical_bytes"`
|
|
}
|
|
|
|
type ResourceStorageSummary struct {
|
|
ResourceCount int64 `json:"resourceCount" gorm:"column:resource_count"`
|
|
ReadyCount int64 `json:"readyCount" gorm:"column:ready_count"`
|
|
LogicalBytes int64 `json:"logicalBytes" gorm:"column:logical_bytes"`
|
|
PhysicalBytes int64 `json:"physicalBytes" gorm:"column:physical_bytes"`
|
|
LocalBytes int64 `json:"localBytes" gorm:"column:local_bytes"`
|
|
RemoteBytes int64 `json:"remoteBytes" gorm:"column:remote_bytes"`
|
|
}
|
|
|
|
func (r *Repository) AdminResources(filter AdminResourceFilter) ([]model.Resource, int64, error) {
|
|
var resources []model.Resource
|
|
var total int64
|
|
query := r.db.Model(&model.Resource{})
|
|
if filter.Kind != "" {
|
|
query = query.Where("kind = ?", filter.Kind)
|
|
}
|
|
if filter.Status != "" {
|
|
query = query.Where("status = ?", filter.Status)
|
|
}
|
|
if filter.Provider != "" {
|
|
query = query.Where(resourceProviderExpression()+" = ?", filter.Provider)
|
|
}
|
|
if filter.UserQuery != "" {
|
|
pattern := "%" + strings.ToLower(filter.UserQuery) + "%"
|
|
matchingUsers := r.db.Model(&model.User{}).Select("id").Where(
|
|
"lower(id) LIKE ? OR lower(username) LIKE ? OR lower(display_name) LIKE ? OR lower(email) LIKE ?",
|
|
pattern, pattern, pattern, pattern,
|
|
)
|
|
// 保留对已删除用户资源的 ID 搜索,同时支持按现存用户的账号信息筛选。
|
|
query = query.Where("(lower(user_id) LIKE ? OR user_id IN (?))", pattern, matchingUsers)
|
|
}
|
|
if filter.Keyword != "" {
|
|
pattern := "%" + strings.ToLower(filter.Keyword) + "%"
|
|
query = query.Where("lower(id) LIKE ? OR lower(object_key) LIKE ?", pattern, pattern)
|
|
}
|
|
if err := query.Count(&total).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
if err := query.Order("created_at desc, id desc").Limit(filter.Limit).Offset(filter.Offset).Find(&resources).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
return resources, total, nil
|
|
}
|
|
|
|
func (r *Repository) UsersByIDs(ids []string) (map[string]model.User, error) {
|
|
if len(ids) == 0 {
|
|
return map[string]model.User{}, nil
|
|
}
|
|
var users []model.User
|
|
if err := r.db.Select("id", "username", "display_name", "email").Where("id IN ?", ids).Find(&users).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
result := make(map[string]model.User, len(users))
|
|
for _, user := range users {
|
|
result[user.ID] = user
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (r *Repository) ResourceStorageSummary() (ResourceStorageSummary, error) {
|
|
var summary ResourceStorageSummary
|
|
err := r.db.Model(&model.Resource{}).Select(`
|
|
COUNT(*) AS resource_count,
|
|
COALESCE(SUM(CASE WHEN status = 'ready' THEN 1 ELSE 0 END), 0) AS ready_count,
|
|
COALESCE(SUM(size), 0) AS logical_bytes
|
|
`).Scan(&summary).Error
|
|
if err != nil {
|
|
return summary, err
|
|
}
|
|
physicalBytes, err := r.resourcePhysicalBytes("")
|
|
if err != nil {
|
|
return summary, err
|
|
}
|
|
localBytes, err := r.resourcePhysicalBytes(resourceProviderExpression()+" = ?", "local")
|
|
if err != nil {
|
|
return summary, err
|
|
}
|
|
summary.PhysicalBytes = physicalBytes
|
|
summary.LocalBytes = localBytes
|
|
summary.RemoteBytes = physicalBytes - localBytes
|
|
return summary, nil
|
|
}
|
|
|
|
func (r *Repository) resourcePhysicalBytes(where string, args ...any) (int64, error) {
|
|
query := r.db.Model(&model.Resource{}).
|
|
Select("MAX(size) AS size").
|
|
Where("status = ?", model.ResourceStatusReady)
|
|
if where != "" {
|
|
query = query.Where(where, args...)
|
|
}
|
|
query = query.Group(resourceProviderExpression() + ", endpoint, bucket, object_key")
|
|
var total struct {
|
|
Bytes int64 `gorm:"column:bytes"`
|
|
}
|
|
err := r.db.Table("(?) AS physical_resources", query).
|
|
Select("COALESCE(SUM(size), 0) AS bytes").
|
|
Scan(&total).Error
|
|
return total.Bytes, err
|
|
}
|
|
|
|
func (r *Repository) ResourceKindStats() ([]ResourceKindStat, error) {
|
|
var stats []ResourceKindStat
|
|
err := r.db.Model(&model.Resource{}).
|
|
Select("kind, COUNT(*) AS count, COALESCE(SUM(size), 0) AS logical_bytes").
|
|
Group("kind").
|
|
Order("logical_bytes desc, kind asc").
|
|
Scan(&stats).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
physical, err := r.resourcePhysicalStats("kind")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for index := range stats {
|
|
stats[index].PhysicalBytes = physical[stats[index].Kind]
|
|
}
|
|
return stats, err
|
|
}
|
|
|
|
func (r *Repository) ResourceProviderStats() ([]ResourceProviderStat, error) {
|
|
var stats []ResourceProviderStat
|
|
provider := resourceProviderExpression()
|
|
err := r.db.Model(&model.Resource{}).
|
|
Select(provider + " AS provider, COUNT(*) AS count, COALESCE(SUM(size), 0) AS logical_bytes").
|
|
Group(provider).
|
|
Order("logical_bytes desc, provider asc").
|
|
Scan(&stats).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
physical, err := r.resourcePhysicalStats(provider)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for index := range stats {
|
|
stats[index].PhysicalBytes = physical[stats[index].Provider]
|
|
}
|
|
return stats, err
|
|
}
|
|
|
|
func (r *Repository) resourcePhysicalStats(dimensionExpression string) (map[string]int64, error) {
|
|
query := r.db.Model(&model.Resource{}).
|
|
Select(dimensionExpression+" AS dimension, MAX(size) AS size").
|
|
Where("status = ?", model.ResourceStatusReady).
|
|
Group(dimensionExpression + ", " + resourceProviderExpression() + ", endpoint, bucket, object_key")
|
|
var rows []struct {
|
|
Dimension string `gorm:"column:dimension"`
|
|
Bytes int64 `gorm:"column:bytes"`
|
|
}
|
|
err := r.db.Table("(?) AS physical_resources", query).
|
|
Select("dimension, COALESCE(SUM(size), 0) AS bytes").
|
|
Group("dimension").
|
|
Scan(&rows).Error
|
|
result := make(map[string]int64, len(rows))
|
|
for _, row := range rows {
|
|
result[row.Dimension] = row.Bytes
|
|
}
|
|
return result, err
|
|
}
|
|
|
|
func resourceProviderExpression() string {
|
|
return "COALESCE(NULLIF(provider, ''), 'local')"
|
|
}
|