package data import ( "context" "errors" "time" "kra/internal/biz" "gorm.io/gorm" ) type mediaPO struct { ID uint `gorm:"primaryKey"` CreatedAt time.Time UpdatedAt time.Time DeletedAt gorm.DeletedAt `gorm:"index"` Name string CategoryID int `gorm:"column:class_id"` URL string Tag string Key string `gorm:"index"` Size int64 Mime string MD5 string `gorm:"index;column:md5"` UserID uint `gorm:"index"` } func (mediaPO) TableName() string { return "media_file_upload_and_downloads" } type categoryPO struct { ID uint `gorm:"primaryKey"` CreatedAt time.Time UpdatedAt time.Time DeletedAt gorm.DeletedAt `gorm:"index"` Name string ParentID uint `gorm:"column:pid"` } func (categoryPO) TableName() string { return "media_attachment_category" } type mediaRepo struct{ data *Data } func NewMediaRepo(data *Data) biz.MediaRepo { return &mediaRepo{data: data} } func mediaFromPO(v mediaPO) *biz.MediaFile { return &biz.MediaFile{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, Name: v.Name, CategoryID: v.CategoryID, URL: v.URL, Tag: v.Tag, Key: v.Key, Size: v.Size, Mime: v.Mime, MD5: v.MD5, UserID: v.UserID} } func mediaToPO(v *biz.MediaFile) mediaPO { return mediaPO{ID: v.ID, Name: v.Name, CategoryID: v.CategoryID, URL: v.URL, Tag: v.Tag, Key: v.Key, Size: v.Size, Mime: v.Mime, MD5: v.MD5, UserID: v.UserID} } func (r *mediaRepo) CreateMedia(ctx context.Context, v *biz.MediaFile) error { po := mediaToPO(v) if err := r.data.gormDB.WithContext(ctx).Create(&po).Error; err != nil { return err } v.ID = po.ID v.CreatedAt = po.CreatedAt return nil } func (r *mediaRepo) FindMedia(ctx context.Context, id uint) (*biz.MediaFile, error) { var po mediaPO if err := r.data.gormDB.WithContext(ctx).First(&po, id).Error; err != nil { return nil, err } return mediaFromPO(po), nil } func (r *mediaRepo) FindMediaByHash(ctx context.Context, userID uint, hash string) (*biz.MediaFile, error) { var po mediaPO if err := r.data.gormDB.WithContext(ctx).Where("user_id = ? AND md5 = ?", userID, hash).First(&po).Error; err != nil { return nil, err } return mediaFromPO(po), nil } func (r *mediaRepo) ListMedia(ctx context.Context, filter biz.MediaFilter) ([]*biz.MediaFile, int64, error) { if filter.Page < 1 { filter.Page = 1 } if filter.PageSize < 1 { filter.PageSize = 10 } db := r.data.gormDB.WithContext(ctx).Model(&mediaPO{}) if filter.Keyword != "" { db = db.Where("name LIKE ?", "%"+filter.Keyword+"%") } if filter.CategoryID > 0 { db = db.Where("class_id = ?", filter.CategoryID) } if filter.Tag != "" { db = db.Where("tag = ?", filter.Tag) } if filter.UserID > 0 { db = db.Where("user_id = ?", filter.UserID) } if !filter.StartCreatedAt.IsZero() { db = db.Where("created_at >= ?", filter.StartCreatedAt) } if !filter.EndCreatedAt.IsZero() { db = db.Where("created_at < ?", filter.EndCreatedAt) } var total int64 if err := db.Count(&total).Error; err != nil { return nil, 0, err } var pos []mediaPO orderKey := filter.OrderKey if orderKey != "id" && orderKey != "size" && orderKey != "created_at" && orderKey != "name" { orderKey = "id" } if filter.Desc { orderKey += " desc" } if err := applyPagination(db.Order(orderKey), filter.Page, filter.PageSize, 100).Find(&pos).Error; err != nil { return nil, 0, err } out := make([]*biz.MediaFile, 0, len(pos)) for _, po := range pos { out = append(out, mediaFromPO(po)) } return out, total, nil } func (r *mediaRepo) UpdateMediaName(ctx context.Context, id uint, name string) error { return r.data.gormDB.WithContext(ctx).Model(&mediaPO{}).Where("id = ?", id).Update("name", name).Error } func (r *mediaRepo) DeleteMedia(ctx context.Context, id uint) error { return r.data.gormDB.WithContext(ctx).Unscoped().Delete(&mediaPO{}, id).Error } func (r *mediaRepo) MediaKeyReferences(ctx context.Context, key string) (int64, error) { var count int64 err := r.data.gormDB.WithContext(ctx).Model(&mediaPO{}).Where(map[string]any{"key": key}).Count(&count).Error return count, err } func (r *mediaRepo) CreateMediaBatch(ctx context.Context, items []*biz.MediaFile) error { pos := make([]mediaPO, 0, len(items)) for _, v := range items { pos = append(pos, mediaToPO(v)) } if len(pos) == 0 { return nil } return r.data.gormDB.WithContext(ctx).Create(&pos).Error } func (r *mediaRepo) SaveCategory(ctx context.Context, v *biz.AttachmentCategory) error { db := r.data.gormDB.WithContext(ctx) var count int64 query := db.Model(&categoryPO{}).Where("name = ? AND pid = ?", v.Name, v.ParentID) if err := query.Count(&count).Error; err != nil { return err } if count > 0 { return errors.New("分类名称已存在") } if v.ID == 0 { po := categoryPO{Name: v.Name, ParentID: v.ParentID} if err := db.Create(&po).Error; err != nil { return err } v.ID = po.ID return nil } return db.Model(&categoryPO{}).Where("id = ?", v.ID).Updates(map[string]any{"name": v.Name, "pid": v.ParentID}).Error } func (r *mediaRepo) DeleteCategory(ctx context.Context, id uint) error { var count int64 if err := r.data.gormDB.WithContext(ctx).Model(&categoryPO{}).Where("pid = ?", id).Count(&count).Error; err != nil { return err } if count > 0 { return errors.New("请先删除子级") } return r.data.gormDB.WithContext(ctx).Unscoped().Delete(&categoryPO{}, id).Error } func (r *mediaRepo) ListCategories(ctx context.Context) ([]*biz.AttachmentCategory, error) { var pos []categoryPO if err := r.data.gormDB.WithContext(ctx).Order("id").Find(&pos).Error; err != nil { return nil, err } byID := map[uint]*biz.AttachmentCategory{} for _, po := range pos { byID[po.ID] = &biz.AttachmentCategory{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, Name: po.Name, ParentID: po.ParentID, Children: []*biz.AttachmentCategory{}} } roots := []*biz.AttachmentCategory{} for _, po := range pos { v := byID[po.ID] if parent := byID[po.ParentID]; parent != nil { parent.Children = append(parent.Children, v) } else { roots = append(roots, v) } } return roots, nil }