289 lines
8.4 KiB
Go
289 lines
8.4 KiB
Go
package repository
|
|
|
|
import (
|
|
"seeyon-filesystem/config"
|
|
"seeyon-filesystem/model"
|
|
)
|
|
|
|
// FileRepository 文件数据访问
|
|
type FileRepository struct{}
|
|
|
|
// NewFileRepository 创建文件Repository实例
|
|
func NewFileRepository() *FileRepository {
|
|
return &FileRepository{}
|
|
}
|
|
|
|
// Create 创建文件记录
|
|
func (r *FileRepository) Create(file *model.File) error {
|
|
return config.DB.Create(file).Error
|
|
}
|
|
|
|
// FindByID 根据ID查找文件
|
|
func (r *FileRepository) FindByID(id uint64) (*model.File, error) {
|
|
var file model.File
|
|
err := config.DB.First(&file, id).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &file, nil
|
|
}
|
|
|
|
// FindByMD5 根据MD5查找文件(用于秒传)
|
|
func (r *FileRepository) FindByMD5(md5 string) (*model.File, error) {
|
|
var file model.File
|
|
err := config.DB.Where("md5 = ? AND status = 1", md5).First(&file).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &file, nil
|
|
}
|
|
|
|
// ListByFolder 查询文件夹下的文件列表(基于权限)
|
|
// 使用 LEFT JOIN 一次性查出, 避免子查询
|
|
func (r *FileRepository) ListByFolder(folderID uint64, userID uint64, roleIDs []uint64) ([]model.File, error) {
|
|
var files []model.File
|
|
|
|
// 构建权限条件: 自己的 OR 直接授权 OR 角色授权
|
|
permCond := "f.owner_id = ?"
|
|
args := []interface{}{userID}
|
|
|
|
// 直接授权
|
|
permCond += " OR fp_user.user_id = ?"
|
|
args = append(args, userID)
|
|
|
|
// 角色授权
|
|
if len(roleIDs) > 0 {
|
|
permCond += " OR fp_role.role_id IN ?"
|
|
args = append(args, roleIDs)
|
|
}
|
|
|
|
query := config.DB.Table("fs_file f").
|
|
Select("DISTINCT f.*").
|
|
Joins("LEFT JOIN fs_file_permission fp_user ON fp_user.file_id = f.id AND fp_user.user_id = ?", userID)
|
|
|
|
if len(roleIDs) > 0 {
|
|
query = query.Joins("LEFT JOIN fs_file_permission fp_role ON fp_role.file_id = f.id AND fp_role.role_id IN ?", roleIDs)
|
|
}
|
|
|
|
query = query.Where("f.folder_id = ? AND f.status = 1 AND ("+permCond+")", append([]interface{}{folderID}, args...)...).
|
|
Order("f.name ASC")
|
|
|
|
err := query.Find(&files).Error
|
|
return files, err
|
|
}
|
|
|
|
// ListByOwner 查询用户的所有文件(仅自己的)
|
|
func (r *FileRepository) ListByOwner(ownerID uint64) ([]model.File, error) {
|
|
var files []model.File
|
|
err := config.DB.Where("owner_id = ? AND status = 1", ownerID).
|
|
Order("created_at DESC").Find(&files).Error
|
|
return files, err
|
|
}
|
|
|
|
// SearchByName 按文件名搜索(基于权限, 使用JOIN优化)
|
|
func (r *FileRepository) SearchByName(userID uint64, roleIDs []uint64, keyword string) ([]model.File, error) {
|
|
var files []model.File
|
|
|
|
// 权限条件
|
|
permCond := "f.owner_id = ? OR fp_user.user_id = ?"
|
|
args := []interface{}{userID, userID}
|
|
if len(roleIDs) > 0 {
|
|
permCond += " OR fp_role.role_id IN ?"
|
|
args = append(args, roleIDs)
|
|
}
|
|
|
|
query := config.DB.Table("fs_file f").
|
|
Select("DISTINCT f.*").
|
|
Joins("LEFT JOIN fs_file_permission fp_user ON fp_user.file_id = f.id AND fp_user.user_id = ?", userID)
|
|
|
|
if len(roleIDs) > 0 {
|
|
query = query.Joins("LEFT JOIN fs_file_permission fp_role ON fp_role.file_id = f.id AND fp_role.role_id IN ?", roleIDs)
|
|
}
|
|
|
|
query = query.Where("f.status = 1 AND ("+permCond+")", args...)
|
|
|
|
// 多关键词: 用|分隔, OR匹配
|
|
if keyword != "" {
|
|
keywords := splitKeywords(keyword)
|
|
if len(keywords) > 1 {
|
|
conditions := make([]string, 0, len(keywords))
|
|
args := make([]interface{}, 0, len(keywords))
|
|
for _, kw := range keywords {
|
|
conditions = append(conditions, "name LIKE ?")
|
|
args = append(args, "%"+kw+"%")
|
|
}
|
|
query = query.Where("("+joinStrings(conditions, " OR ")+")", args...)
|
|
} else {
|
|
query = query.Where("name LIKE ?", "%"+keywords[0]+"%")
|
|
}
|
|
}
|
|
|
|
err := query.Order("created_at DESC").Limit(200).Find(&files).Error
|
|
return files, err
|
|
}
|
|
|
|
// SearchByType 按文件类型搜索
|
|
func (r *FileRepository) SearchByType(ownerID uint64, extensions []string) ([]model.File, error) {
|
|
var files []model.File
|
|
err := config.DB.Where("owner_id = ? AND status = 1 AND extension IN ?", ownerID, extensions).
|
|
Order("created_at DESC").Limit(200).Find(&files).Error
|
|
return files, err
|
|
}
|
|
|
|
// SearchByPolicy 按存储策略搜索(管理员用)
|
|
func (r *FileRepository) SearchByPolicy(policyID uint64) ([]model.File, error) {
|
|
var files []model.File
|
|
err := config.DB.Where("storage_policy_id = ? AND status = 1", policyID).
|
|
Order("created_at DESC").Limit(500).Find(&files).Error
|
|
return files, err
|
|
}
|
|
|
|
func splitKeywords(s string) []string {
|
|
var result []string
|
|
for _, kw := range splitByChar(s, '|') {
|
|
kw = trimSpace(kw)
|
|
if kw != "" {
|
|
result = append(result, kw)
|
|
}
|
|
}
|
|
if len(result) == 0 {
|
|
result = []string{s}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func splitByChar(s string, sep byte) []string {
|
|
var result []string
|
|
start := 0
|
|
for i := 0; i < len(s); i++ {
|
|
if s[i] == sep {
|
|
result = append(result, s[start:i])
|
|
start = i + 1
|
|
}
|
|
}
|
|
result = append(result, s[start:])
|
|
return result
|
|
}
|
|
|
|
func trimSpace(s string) string {
|
|
start, end := 0, len(s)
|
|
for start < end && s[start] == ' ' {
|
|
start++
|
|
}
|
|
for end > start && s[end-1] == ' ' {
|
|
end--
|
|
}
|
|
return s[start:end]
|
|
}
|
|
|
|
func joinStrings(strs []string, sep string) string {
|
|
if len(strs) == 0 {
|
|
return ""
|
|
}
|
|
result := strs[0]
|
|
for i := 1; i < len(strs); i++ {
|
|
result += sep + strs[i]
|
|
}
|
|
return result
|
|
}
|
|
|
|
// ListTrashed 查询回收站文件
|
|
func (r *FileRepository) ListTrashed(ownerID uint64) ([]model.File, error) {
|
|
var files []model.File
|
|
err := config.DB.Where("owner_id = ? AND status = 0", ownerID).
|
|
Order("updated_at DESC").Find(&files).Error
|
|
return files, err
|
|
}
|
|
|
|
// Update 更新文件信息
|
|
func (r *FileRepository) Update(file *model.File) error {
|
|
return config.DB.Save(file).Error
|
|
}
|
|
|
|
// SoftDelete 软删除(移入回收站)
|
|
func (r *FileRepository) SoftDelete(id uint64) error {
|
|
return config.DB.Model(&model.File{}).Where("id = ?", id).Update("status", 0).Error
|
|
}
|
|
|
|
// Restore 从回收站恢复
|
|
func (r *FileRepository) Restore(id uint64) error {
|
|
return config.DB.Model(&model.File{}).Where("id = ?", id).Update("status", 1).Error
|
|
}
|
|
|
|
// HardDelete 永久删除
|
|
func (r *FileRepository) HardDelete(id uint64) error {
|
|
return config.DB.Unscoped().Delete(&model.File{}, id).Error
|
|
}
|
|
|
|
// IncrementDownloadCount 增加下载次数
|
|
func (r *FileRepository) IncrementDownloadCount(id uint64) error {
|
|
return config.DB.Model(&model.File{}).Where("id = ?", id).
|
|
Update("download_count", config.DB.Raw("download_count + 1")).Error
|
|
}
|
|
|
|
// CountByOwner 统计用户文件数
|
|
func (r *FileRepository) CountByOwner(ownerID uint64) (int64, error) {
|
|
var count int64
|
|
err := config.DB.Model(&model.File{}).Where("owner_id = ? AND status = 1", ownerID).Count(&count).Error
|
|
return count, err
|
|
}
|
|
|
|
// SumSizeByOwner 统计用户文件总大小
|
|
func (r *FileRepository) SumSizeByOwner(ownerID uint64) (int64, error) {
|
|
var total int64
|
|
err := config.DB.Model(&model.File{}).Where("owner_id = ? AND status = 1", ownerID).
|
|
Select("COALESCE(SUM(size), 0)").Scan(&total).Error
|
|
return total, err
|
|
}
|
|
|
|
// PolicyStats 存储策略统计
|
|
type PolicyStats struct {
|
|
PolicyID uint64 `json:"policy_id"`
|
|
PolicyName string `json:"policy_name"`
|
|
PolicyType string `json:"policy_type"`
|
|
FileCount int64 `json:"file_count"`
|
|
TotalSize int64 `json:"total_size"`
|
|
UserCount int64 `json:"user_count"`
|
|
}
|
|
|
|
// GetPolicyStats 获取所有存储策略的使用统计
|
|
func (r *FileRepository) GetPolicyStats() ([]PolicyStats, error) {
|
|
var stats []PolicyStats
|
|
err := config.DB.Raw(`
|
|
SELECT
|
|
f.storage_policy_id as policy_id,
|
|
COALESCE(sp.name, '未知') as policy_name,
|
|
COALESCE(sp.policy_type, '') as policy_type,
|
|
COUNT(*) as file_count,
|
|
COALESCE(SUM(f.size), 0) as total_size,
|
|
COUNT(DISTINCT f.owner_id) as user_count
|
|
FROM fs_file f
|
|
LEFT JOIN fs_storage_policy sp ON sp.id = f.storage_policy_id
|
|
WHERE f.status = 1
|
|
GROUP BY f.storage_policy_id, sp.name, sp.policy_type
|
|
ORDER BY total_size DESC
|
|
`).Scan(&stats).Error
|
|
return stats, err
|
|
}
|
|
|
|
// GetGlobalStats 获取全局统计
|
|
func (r *FileRepository) GetGlobalStats() (map[string]interface{}, error) {
|
|
var totalFiles int64
|
|
var totalSize int64
|
|
var totalUsers int64
|
|
|
|
config.DB.Model(&model.File{}).Where("status = 1").Count(&totalFiles)
|
|
config.DB.Model(&model.File{}).Where("status = 1").Select("COALESCE(SUM(size), 0)").Scan(&totalSize)
|
|
config.DB.Model(&model.File{}).Where("status = 1").Distinct("owner_id").Count(&totalUsers)
|
|
|
|
var totalFolders int64
|
|
config.DB.Model(&model.Folder{}).Where("status = 1").Count(&totalFolders)
|
|
|
|
return map[string]interface{}{
|
|
"total_files": totalFiles,
|
|
"total_size": totalSize,
|
|
"total_folders": totalFolders,
|
|
"total_users": totalUsers,
|
|
}, nil
|
|
}
|