From c9c8eac734e07d75138100e3e43aa141c90fc1ca Mon Sep 17 00:00:00 2001 From: nyne Date: Sat, 15 Nov 2025 16:00:26 +0800 Subject: [PATCH] Add charactor api. --- server/api/resource.go | 2 +- server/dao/resource.go | 79 ++++++++++++++++++++++++++++++-------- server/model/charactor.go | 44 +++++++++++++++++++++ server/model/resource.go | 43 ++++++++++++--------- server/service/resource.go | 46 +++++++++++++++++----- 5 files changed, 170 insertions(+), 44 deletions(-) create mode 100644 server/model/charactor.go diff --git a/server/api/resource.go b/server/api/resource.go index f58bfdf..f89a2ad 100644 --- a/server/api/resource.go +++ b/server/api/resource.go @@ -239,7 +239,7 @@ func handleUpdateResource(c fiber.Ctx) error { if !ok { return model.NewUnAuthorizedError("You must be logged in to update a resource") } - err = service.EditResource(uid, uint(id), ¶ms) + err = service.UpdateResource(uid, uint(id), ¶ms) if err != nil { return err } diff --git a/server/dao/resource.go b/server/dao/resource.go index 7ee5d24..dba944f 100644 --- a/server/dao/resource.go +++ b/server/dao/resource.go @@ -16,10 +16,18 @@ import ( func CreateResource(r model.Resource) (model.Resource, error) { err := db.Transaction(func(tx *gorm.DB) error { r.ModifiedTime = time.Now() + charactors := r.Charactors + r.Charactors = nil err := tx.Create(&r).Error if err != nil { return err } + for _, c := range charactors { + c.ResourceID = r.ID + if err := tx.Create(&c).Error; err != nil { + return err + } + } if err := tx.Model(&model.User{}).Where("id = ?", r.UserID).Update("resources_count", gorm.Expr("resources_count + ?", 1)).Error; err != nil { return err } @@ -42,6 +50,7 @@ func GetResourceByID(id uint) (model.Resource, error) { Preload("Files"). Preload("Files.User"). Preload("Files.Storage"). + Preload("Charactors"). First(&r, id).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return model.Resource{}, model.NewNotFoundError("Resource not found") @@ -99,22 +108,60 @@ func GetResourceList(page, pageSize int, sort model.RSort) ([]model.Resource, in func UpdateResource(r model.Resource) error { // Update a resource in the database - images := r.Images - tags := r.Tags - r.Images = nil - r.Tags = nil - r.Files = nil - r.ModifiedTime = time.Now() - if err := db.Save(&r).Error; err != nil { - return err - } - if err := db.Model(&r).Association("Images").Replace(images); err != nil { - return err - } - if err := db.Model(&r).Association("Tags").Replace(tags); err != nil { - return err - } - return nil + return db.Transaction(func(tx *gorm.DB) error { + images := r.Images + tags := r.Tags + charactors := r.Charactors + r.Charactors = nil + r.Images = nil + r.Tags = nil + r.Files = nil + r.ModifiedTime = time.Now() + oldCharactors := []model.Charactor{} + if err := db.Model(&model.Charactor{}).Where("resource_id = ?", r.ID).Find(&oldCharactors).Error; err != nil { + return err + } + if err := db.Save(&r).Error; err != nil { + return err + } + if err := db.Model(&r).Association("Images").Replace(images); err != nil { + return err + } + if err := db.Model(&r).Association("Tags").Replace(tags); err != nil { + return err + } + for _, c := range oldCharactors { + shouldDelete := true + for _, nc := range charactors { + if c.ID == nc.ID { + shouldDelete = false + break + } + } + if shouldDelete { + if err := tx.Delete(&c).Error; err != nil { + return err + } + } + } + for _, c := range charactors { + shouldAdd := true + for _, oc := range oldCharactors { + if c.Equal(&oc) { + shouldAdd = false + break + } + } + if shouldAdd { + c.ID = 0 + c.ResourceID = r.ID + if err := tx.Create(&c).Error; err != nil { + return err + } + } + } + return nil + }) } func DeleteResource(id uint) error { diff --git a/server/model/charactor.go b/server/model/charactor.go new file mode 100644 index 0000000..84db719 --- /dev/null +++ b/server/model/charactor.go @@ -0,0 +1,44 @@ +package model + +type Charactor struct { + ID uint `gorm:"primaryKey;autoIncrement"` + Name string `gorm:"type:varchar(100);not null"` + Alias []string `gorm:"serializer:json"` + CV string `gorm:"type:varchar(100)"` + ImageID uint + ResourceID uint + Image *Image `gorm:"foreignKey:ImageID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"` +} + +type CharactorView struct { + Id uint `json:"id"` + Name string `json:"name"` + Alias []string `json:"alias"` + CV string `json:"cv"` + Image uint `json:"image"` +} + +func (c *Charactor) ToView() *CharactorView { + return &CharactorView{ + Id: c.ID, + Name: c.Name, + Alias: c.Alias, + CV: c.CV, + Image: c.ImageID, + } +} + +func (c *Charactor) Equal(other *Charactor) bool { + if c.Name != other.Name || c.CV != other.CV || c.ImageID != other.ImageID { + return false + } + if len(c.Alias) != len(other.Alias) { + return false + } + for i := range c.Alias { + if c.Alias[i] != other.Alias[i] { + return false + } + } + return true +} diff --git a/server/model/resource.go b/server/model/resource.go index d6fbcc4..bb8dd0b 100644 --- a/server/model/resource.go +++ b/server/model/resource.go @@ -21,8 +21,9 @@ type Resource struct { Downloads uint Comments uint ModifiedTime time.Time - Gallery []uint `gorm:"serializer:json"` - GalleryNsfw []uint `gorm:"serializer:json"` + Gallery []uint `gorm:"serializer:json"` + GalleryNsfw []uint `gorm:"serializer:json"` + Charactors []Charactor `gorm:"foreignKey:ResourceID"` } type Link struct { @@ -40,22 +41,23 @@ type ResourceView struct { } type ResourceDetailView struct { - ID uint `json:"id"` - Title string `json:"title"` - AlternativeTitles []string `json:"alternativeTitles"` - Links []Link `json:"links"` - Article string `json:"article"` - CreatedAt time.Time `json:"createdAt"` - Tags []TagView `json:"tags"` - Images []ImageView `json:"images"` - Files []FileView `json:"files"` - Author UserView `json:"author"` - Views uint `json:"views"` - Downloads uint `json:"downloads"` - Comments uint `json:"comments"` - Related []ResourceView `json:"related"` - Gallery []uint `json:"gallery"` - GalleryNsfw []uint `json:"galleryNsfw"` + ID uint `json:"id"` + Title string `json:"title"` + AlternativeTitles []string `json:"alternativeTitles"` + Links []Link `json:"links"` + Article string `json:"article"` + CreatedAt time.Time `json:"createdAt"` + Tags []TagView `json:"tags"` + Images []ImageView `json:"images"` + Files []FileView `json:"files"` + Author UserView `json:"author"` + Views uint `json:"views"` + Downloads uint `json:"downloads"` + Comments uint `json:"comments"` + Related []ResourceView `json:"related"` + Gallery []uint `json:"gallery"` + GalleryNsfw []uint `json:"galleryNsfw"` + Charactors []CharactorView `json:"charactors"` } func (r *Resource) ToView() ResourceView { @@ -94,6 +96,10 @@ func (r *Resource) ToDetailView() ResourceDetailView { for i, file := range r.Files { files[i] = *file.ToView() } + charactors := make([]CharactorView, len(r.Charactors)) + for i, charactor := range r.Charactors { + charactors[i] = *charactor.ToView() + } return ResourceDetailView{ ID: r.ID, Title: r.Title, @@ -110,5 +116,6 @@ func (r *Resource) ToDetailView() ResourceDetailView { Comments: r.Comments, Gallery: r.Gallery, GalleryNsfw: r.GalleryNsfw, + Charactors: charactors, } } diff --git a/server/service/resource.go b/server/service/resource.go index 1317ce3..ca8600a 100644 --- a/server/service/resource.go +++ b/server/service/resource.go @@ -22,14 +22,22 @@ const ( ) type ResourceParams struct { - Title string `json:"title" binding:"required"` - AlternativeTitles []string `json:"alternative_titles"` - Links []model.Link `json:"links"` - Tags []uint `json:"tags"` - Article string `json:"article"` - Images []uint `json:"images"` - Gallery []uint `json:"gallery"` - GalleryNsfw []uint `json:"gallery_nsfw"` + Title string `json:"title" binding:"required"` + AlternativeTitles []string `json:"alternative_titles"` + Links []model.Link `json:"links"` + Tags []uint `json:"tags"` + Article string `json:"article"` + Images []uint `json:"images"` + Gallery []uint `json:"gallery"` + GalleryNsfw []uint `json:"gallery_nsfw"` + Charactors []CharactorParams `json:"charactors"` +} + +type CharactorParams struct { + Name string `json:"name" binding:"required"` + Alias []string `json:"alias"` + CV string `json:"cv"` + Image uint `json:"image"` } func CreateResource(uid uint, params *ResourceParams) (uint, error) { @@ -69,6 +77,15 @@ func CreateResource(uid uint, params *ResourceParams) (uint, error) { nsfw = append(nsfw, id) } } + charactors := make([]model.Charactor, len(params.Charactors)) + for i, c := range params.Charactors { + charactors[i] = model.Charactor{ + Name: c.Name, + Alias: c.Alias, + CV: c.CV, + ImageID: c.Image, + } + } r := model.Resource{ Title: params.Title, AlternativeTitles: params.AlternativeTitles, @@ -79,6 +96,7 @@ func CreateResource(uid uint, params *ResourceParams) (uint, error) { UserID: uid, Gallery: gallery, GalleryNsfw: nsfw, + Charactors: charactors, } if r, err = dao.CreateResource(r); err != nil { return 0, err @@ -451,7 +469,7 @@ func GetResourcesWithUser(username string, page int) ([]model.ResourceView, int, return views, totalPages, nil } -func EditResource(uid, rid uint, params *ResourceParams) error { +func UpdateResource(uid, rid uint, params *ResourceParams) error { isAdmin, err := checkUserCanUpload(uid) if err != nil { log.Error("checkUserCanUpload error: ", err) @@ -477,6 +495,15 @@ func EditResource(uid, rid uint, params *ResourceParams) error { nsfw = append(nsfw, id) } } + charactors := make([]model.Charactor, len(params.Charactors)) + for i, c := range params.Charactors { + charactors[i] = model.Charactor{ + Name: c.Name, + Alias: c.Alias, + CV: c.CV, + ImageID: c.Image, + } + } r.Title = params.Title r.AlternativeTitles = params.AlternativeTitles @@ -484,6 +511,7 @@ func EditResource(uid, rid uint, params *ResourceParams) error { r.Links = params.Links r.Gallery = gallery r.GalleryNsfw = nsfw + r.Charactors = charactors images := make([]model.Image, len(params.Images)) for i, id := range params.Images {