Add user file statistic.

This commit is contained in:
2025-07-09 17:17:54 +08:00
parent b568b234c4
commit b1d395eac6
4 changed files with 61 additions and 36 deletions

View File

@@ -5,7 +5,8 @@ export interface User {
avatar_path: string; avatar_path: string;
is_admin: boolean; is_admin: boolean;
can_upload: boolean; can_upload: boolean;
uploads_count: number; resources_count: number;
files_count: number;
comments_count: number; comments_count: number;
bio: string; bio: string;
} }

View File

@@ -89,7 +89,7 @@ function UserCard({ user }: { user: User }) {
<p> <p>
<span className="text-sm font-bold mr-1"> <span className="text-sm font-bold mr-1">
{" "} {" "}
{user.uploads_count} {user.resources_count}
</span> </span>
<span className="text-sm">Resources</span> <span className="text-sm">Resources</span>
<span className="mx-2"></span> <span className="mx-2"></span>

View File

@@ -77,6 +77,7 @@ func CreateFile(filename string, description string, resourceID uint, storageID
if storageID == nil && redirectUrl == "" { if storageID == nil && redirectUrl == "" {
return nil, errors.New("storageID and redirectUrl cannot be both empty") return nil, errors.New("storageID and redirectUrl cannot be both empty")
} }
f := &model.File{ f := &model.File{
UUID: uuid.NewString(), UUID: uuid.NewString(),
Filename: filename, Filename: filename,
@@ -88,9 +89,23 @@ func CreateFile(filename string, description string, resourceID uint, storageID
Size: size, Size: size,
UserID: userID, UserID: userID,
} }
if err := db.Create(f).Error; err != nil {
err := db.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(f).Error; err != nil {
return err
}
err := tx.Model(&model.User{}).Where("id = ?", userID).
UpdateColumn("FilesCount", gorm.Expr("FilesCount + ?", 1)).Error
if err != nil {
return err
}
return nil
})
if err != nil {
return nil, err return nil, err
} }
return f, nil return f, nil
} }
@@ -108,14 +123,19 @@ func GetFile(id string) (*model.File, error) {
func DeleteFile(id string) error { func DeleteFile(id string) error {
f := &model.File{} f := &model.File{}
if err := db.Where("uuid = ?", id).First(f).Error; err != nil { if err := db.Where("uuid = ?", id).First(f).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { return err
return model.NewNotFoundError("file not found") }
if err := db.Transaction(func(tx *gorm.DB) error {
if err := tx.Delete(f).Error; err != nil {
return err
} }
return tx.Model(&model.User{}).Where("id = ?", f.UserID).
UpdateColumn("FilesCount", gorm.Expr("FilesCount - ?", 1)).Error
}); err != nil {
return err return err
} }
if err := db.Delete(f).Error; err != nil {
return err
}
return nil return nil
} }

View File

@@ -2,33 +2,36 @@ package model
import ( import (
"fmt" "fmt"
"gorm.io/gorm"
"time" "time"
"gorm.io/gorm"
) )
type User struct { type User struct {
gorm.Model gorm.Model
Username string `gorm:"uniqueIndex;not null"` Username string `gorm:"uniqueIndex;not null"`
PasswordHash []byte PasswordHash []byte
IsAdmin bool IsAdmin bool
CanUpload bool CanUpload bool
AvatarVersion int AvatarVersion int
UploadsCount int ResourcesCount int
CommentsCount int FilesCount int
Resources []Resource `gorm:"foreignKey:UserID"` CommentsCount int
Bio string Resources []Resource `gorm:"foreignKey:UserID"`
Bio string
} }
type UserView struct { type UserView struct {
ID uint `json:"id"` ID uint `json:"id"`
Username string `json:"username"` Username string `json:"username"`
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
AvatarPath string `json:"avatar_path"` AvatarPath string `json:"avatar_path"`
IsAdmin bool `json:"is_admin"` IsAdmin bool `json:"is_admin"`
CanUpload bool `json:"can_upload"` CanUpload bool `json:"can_upload"`
UploadsCount int `json:"uploads_count"` ResourcesCount int `json:"resources_count"`
CommentsCount int `json:"comments_count"` FilesCount int `json:"files_count"`
Bio string `json:"bio"` CommentsCount int `json:"comments_count"`
Bio string `json:"bio"`
} }
type UserViewWithToken struct { type UserViewWithToken struct {
@@ -38,15 +41,16 @@ type UserViewWithToken struct {
func (u User) ToView() UserView { func (u User) ToView() UserView {
return UserView{ return UserView{
ID: u.ID, ID: u.ID,
Username: u.Username, Username: u.Username,
CreatedAt: u.CreatedAt, CreatedAt: u.CreatedAt,
AvatarPath: fmt.Sprintf("/api/user/avatar/%d?v=%d", u.ID, u.AvatarVersion), AvatarPath: fmt.Sprintf("/api/user/avatar/%d?v=%d", u.ID, u.AvatarVersion),
IsAdmin: u.IsAdmin, IsAdmin: u.IsAdmin,
CanUpload: u.CanUpload || u.IsAdmin, CanUpload: u.CanUpload || u.IsAdmin,
UploadsCount: u.UploadsCount, ResourcesCount: u.ResourcesCount,
CommentsCount: u.CommentsCount, FilesCount: u.FilesCount,
Bio: u.Bio, CommentsCount: u.CommentsCount,
Bio: u.Bio,
} }
} }