221 lines
6.3 KiB
Go
221 lines
6.3 KiB
Go
package repository
|
|
|
|
import (
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"filestoragesystem/internal/model"
|
|
)
|
|
|
|
// FileListFilter 文件列表过滤条件
|
|
type FileListFilter struct {
|
|
UserID uint
|
|
ProjectID uint
|
|
Filename string // 模糊匹配
|
|
Prefix string // 虚拟目录前缀
|
|
Trashed bool // 查询回收站
|
|
Visibility int8 // 0为不过滤
|
|
Page int
|
|
PageSize int
|
|
}
|
|
|
|
// FileRepo 文件数据访问
|
|
type FileRepo struct{ DB *gorm.DB }
|
|
|
|
func (r *FileRepo) Create(f *model.File) error { return r.DB.Create(f).Error }
|
|
|
|
func (r *FileRepo) FindByID(id uint) (*model.File, error) {
|
|
var f model.File
|
|
err := r.DB.First(&f, id).Error
|
|
return &f, err
|
|
}
|
|
|
|
// List 分页查询文件
|
|
func (r *FileRepo) List(filter FileListFilter) ([]model.File, int64, error) {
|
|
q := r.DB.Model(&model.File{})
|
|
if filter.UserID > 0 {
|
|
q = q.Where("user_id = ?", filter.UserID)
|
|
}
|
|
if filter.ProjectID > 0 {
|
|
q = q.Where("project_id = ?", filter.ProjectID)
|
|
}
|
|
if filter.Filename != "" {
|
|
q = q.Where("filename LIKE ?", "%"+filter.Filename+"%")
|
|
}
|
|
if filter.Prefix != "" {
|
|
q = q.Where("(filename = ? OR filename LIKE ?)", filter.Prefix, filter.Prefix+"/%")
|
|
}
|
|
if filter.Trashed {
|
|
q = q.Where("status = 0")
|
|
} else {
|
|
q = q.Where("status = 1")
|
|
}
|
|
if filter.Visibility > 0 {
|
|
q = q.Where("visibility = ?", filter.Visibility)
|
|
}
|
|
var total int64
|
|
if err := q.Count(&total).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
var files []model.File
|
|
err := q.Preload("Project").Order("id DESC").
|
|
Offset((filter.Page - 1) * filter.PageSize).Limit(filter.PageSize).Find(&files).Error
|
|
return files, total, err
|
|
}
|
|
|
|
func (r *FileRepo) Update(f *model.File) error { return r.DB.Save(f).Error }
|
|
|
|
// SoftDelete 软删除(进入回收站)
|
|
func (r *FileRepo) SoftDelete(id uint) error {
|
|
now := time.Now()
|
|
return r.DB.Model(&model.File{}).Where("id = ?", id).
|
|
Updates(map[string]interface{}{"status": 0, "deleted_at": now}).Error
|
|
}
|
|
|
|
// Restore 从回收站恢复
|
|
func (r *FileRepo) Restore(id uint) error {
|
|
return r.DB.Model(&model.File{}).Where("id = ?", id).
|
|
Updates(map[string]interface{}{"status": 1, "deleted_at": nil}).Error
|
|
}
|
|
|
|
func (r *FileRepo) Delete(id uint) error { return r.DB.Delete(&model.File{}, id).Error }
|
|
|
|
// CountByStoredPath 统计引用同一物理文件的记录数(用于去重删除)
|
|
func (r *FileRepo) CountByStoredPath(storedPath string) (int64, error) {
|
|
var n int64
|
|
err := r.DB.Model(&model.File{}).Where("stored_path = ?", storedPath).Count(&n).Error
|
|
return n, err
|
|
}
|
|
|
|
// FindByMD5AndPath 基于MD5查找可复用的物理文件记录
|
|
func (r *FileRepo) FindByMD5AndPath(md5 string) (*model.File, error) {
|
|
var f model.File
|
|
err := r.DB.Where("md5 = ? AND stored_path != ''", md5).Order("id ASC").First(&f).Error
|
|
return &f, err
|
|
}
|
|
|
|
// ListByProject 项目下所有文件(用于项目删除级联)
|
|
func (r *FileRepo) ListByProject(projectID uint) ([]model.File, error) {
|
|
var files []model.File
|
|
err := r.DB.Where("project_id = ?", projectID).Find(&files).Error
|
|
return files, err
|
|
}
|
|
|
|
// ListByUser 用户所有文件(用于用户删除级联)
|
|
func (r *FileRepo) ListByUser(userID uint) ([]model.File, error) {
|
|
var files []model.File
|
|
err := r.DB.Where("user_id = ?", userID).Find(&files).Error
|
|
return files, err
|
|
}
|
|
|
|
// StatsByProject 项目文件统计
|
|
func (r *FileRepo) StatsByProject(projectID uint) (count int64, size int64, err error) {
|
|
var res struct {
|
|
Count int64 `json:"count"`
|
|
Size int64 `json:"size"`
|
|
}
|
|
err = r.DB.Model(&model.File{}).
|
|
Where("project_id = ? AND status = 1", projectID).
|
|
Select("COUNT(*) as count, COALESCE(SUM(size),0) as size").
|
|
Scan(&res).Error
|
|
return res.Count, res.Size, err
|
|
}
|
|
|
|
// StatsByUser 用户文件统计
|
|
func (r *FileRepo) StatsByUser(userID uint) (count int64, size int64, err error) {
|
|
var res struct {
|
|
Count int64 `json:"count"`
|
|
Size int64 `json:"size"`
|
|
}
|
|
err = r.DB.Model(&model.File{}).
|
|
Where("user_id = ? AND status = 1", userID).
|
|
Select("COUNT(*) as count, COALESCE(SUM(size),0) as size").
|
|
Scan(&res).Error
|
|
return res.Count, res.Size, err
|
|
}
|
|
|
|
// IncrDownloadCount 下载计数
|
|
func (r *FileRepo) IncrDownloadCount(id uint) error {
|
|
return r.DB.Model(&model.File{}).Where("id = ?", id).
|
|
UpdateColumn("download_count", gorm.Expr("download_count + 1")).Error
|
|
}
|
|
|
|
// ---- 临时链接 ----
|
|
|
|
// TempLinkRepo 临时链接数据访问
|
|
type TempLinkRepo struct{ DB *gorm.DB }
|
|
|
|
func (r *TempLinkRepo) Create(l *model.TempLink) error { return r.DB.Create(l).Error }
|
|
|
|
func (r *TempLinkRepo) FindByToken(token string) (*model.TempLink, error) {
|
|
var l model.TempLink
|
|
err := r.DB.Where("token = ?", token).First(&l).Error
|
|
return &l, err
|
|
}
|
|
|
|
func (r *TempLinkRepo) IncrUsed(id uint) error {
|
|
return r.DB.Model(&model.TempLink{}).Where("id = ?", id).
|
|
UpdateColumn("used_count", gorm.Expr("used_count + 1")).Error
|
|
}
|
|
|
|
func (r *TempLinkRepo) DeleteByFile(fileID uint) error {
|
|
return r.DB.Where("file_id = ?", fileID).Delete(&model.TempLink{}).Error
|
|
}
|
|
|
|
// ---- 流量日志 ----
|
|
|
|
// TrafficRepo 流量日志数据访问
|
|
type TrafficRepo struct{ DB *gorm.DB }
|
|
|
|
func (r *TrafficRepo) Create(t *model.TrafficLog) error { return r.DB.Create(t).Error }
|
|
|
|
// DailyStats 按天/类型聚合流量
|
|
func (r *TrafficRepo) DailyStats(userID uint, days int) ([]map[string]interface{}, error) {
|
|
var rows []map[string]interface{}
|
|
err := r.DB.Model(&model.TrafficLog{}).
|
|
Select("date(created_at) as date, type, COALESCE(SUM(size),0) as size, COUNT(*) as count").
|
|
Where("user_id = ? AND created_at >= date('now', ?)", userID, "-"+itoa(days)+" day").
|
|
Group("date(created_at), type").
|
|
Order("date DESC").
|
|
Find(&rows).Error
|
|
return rows, err
|
|
}
|
|
|
|
// SumByUser 汇总用户上传/下载流量
|
|
func (r *TrafficRepo) SumByUser(userID uint, days int) (upload, download int64, err error) {
|
|
var res []struct {
|
|
Type int64 `json:"type"`
|
|
Size int64 `json:"size"`
|
|
}
|
|
q := r.DB.Model(&model.TrafficLog{}).
|
|
Select("type, COALESCE(SUM(size),0) as size").
|
|
Where("user_id = ?", userID)
|
|
if days > 0 {
|
|
q = q.Where("created_at >= date('now', ?)", "-"+itoa(days)+" day")
|
|
}
|
|
err = q.Group("type").Scan(&res).Error
|
|
for _, r := range res {
|
|
if r.Type == 1 {
|
|
upload = r.Size
|
|
} else if r.Type == 2 {
|
|
download = r.Size
|
|
}
|
|
}
|
|
return upload, download, err
|
|
}
|
|
|
|
func itoa(n int) string {
|
|
if n == 0 {
|
|
return "0"
|
|
}
|
|
var buf [12]byte
|
|
i := len(buf)
|
|
for n > 0 {
|
|
i--
|
|
buf[i] = byte('0' + n%10)
|
|
n /= 10
|
|
}
|
|
return string(buf[i:])
|
|
}
|