Files
infinite-canvas/repository/asset.go
T
HouYunFei 472bf8b732 first commit
Co-Authored-By: Codex <noreply@openai.com>
2026-05-19 19:50:36 +08:00

133 lines
3.3 KiB
Go

package repository
import (
"errors"
"github.com/basketikun/infinite-canvas/model"
"gorm.io/gorm"
)
// ListAssets 按查询条件返回素材分页列表。
func ListAssets(q model.Query) ([]model.Asset, int64, error) {
db, err := DB()
if err != nil {
return nil, 0, err
}
q.Normalize()
tx := applyAssetFilters(db.Model(&model.Asset{}), q)
var total int64
if err := tx.Count(&total).Error; err != nil {
return nil, 0, err
}
var items []model.Asset
err = tx.Order("updated_at desc").Offset(q.Offset()).Limit(q.PageSize).Find(&items).Error
return items, total, err
}
// ListAssetTags 返回当前素材查询条件下的全部标签。
func ListAssetTags(q model.Query) ([]string, error) {
db, err := DB()
if err != nil {
return nil, err
}
q.Normalize()
q.Tags = nil
tx := applyAssetFilters(db.Model(&model.Asset{}), q)
var items []model.Asset
if err := tx.Select("tags").Find(&items).Error; err != nil {
return nil, err
}
return assetTagsFromItems(items), nil
}
// SaveAsset 保存素材,并在更新时保留原创建时间。
func SaveAsset(item model.Asset) (model.Asset, error) {
db, err := DB()
if err != nil {
return item, err
}
if saved, ok, err := findAsset(db, item.ID); err != nil {
return item, err
} else if ok && item.CreatedAt == "" {
item.CreatedAt = saved.CreatedAt
}
return item, db.Save(&item).Error
}
// DeleteAsset 删除指定素材。
func DeleteAsset(id string) error {
db, err := DB()
if err != nil {
return err
}
return db.Delete(&model.Asset{}, "id = ?", id).Error
}
// applyAssetFilters 应用素材列表的搜索条件。
func applyAssetFilters(tx *gorm.DB, q model.Query) *gorm.DB {
if q.Keyword != "" {
like := "%" + q.Keyword + "%"
tx = tx.Where("title LIKE ? OR description LIKE ? OR content LIKE ?", like, like, like)
}
if isActiveAssetOption(q.Type) {
tx = tx.Where("type = ?", q.Type)
}
return applyAssetTagsFilter(tx, q.Tags)
}
// findAsset 根据 ID 查询素材。
func findAsset(db *gorm.DB, id string) (model.Asset, bool, error) {
item := model.Asset{}
err := db.Where("id = ?", id).First(&item).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return model.Asset{}, false, nil
}
return item, err == nil, err
}
// applyAssetTagsFilter 应用 JSON 标签条件。
func applyAssetTagsFilter(tx *gorm.DB, tags []string) *gorm.DB {
if len(tags) == 0 {
return tx
}
condition := tx.Session(&gorm.Session{NewDB: true})
for _, tag := range tags {
condition = condition.Or(assetJSONTagsContains(tx), tag)
}
return tx.Where(condition)
}
func assetTagsFromItems(items []model.Asset) []string {
seen := map[string]bool{}
tags := []string{}
for _, item := range items {
for _, tag := range item.Tags {
if tag != "" && !seen[tag] {
seen[tag] = true
tags = append(tags, tag)
}
}
}
return tags
}
// assetJSONTagsContains 返回素材 tags 的 JSON 包含条件。
func assetJSONTagsContains(tx *gorm.DB) string {
switch tx.Dialector.Name() {
case "mysql":
return "JSON_CONTAINS(tags, JSON_QUOTE(?))"
case "postgres":
return "jsonb_exists(tags::jsonb, ?)"
default:
return "EXISTS (SELECT 1 FROM json_each(tags) WHERE value = ?)"
}
}
// isActiveAssetOption 判断素材筛选项有效状态。
func isActiveAssetOption(value string) bool {
return value != "" && value != "全部" && value != "all"
}