package models import ( "strings" "time" "gorm.io/gorm" ) // Tag represents a blog post tag with multi-language support. type Tag struct { ID uint `gorm:"primarykey" json:"id"` NameZh string `gorm:"size:50;not null" json:"name_zh"` NameEn string `gorm:"size:50;not null" json:"name_en"` Slug string `gorm:"size:100;uniqueIndex;not null" json:"slug"` Count int `gorm:"default:0" json:"count"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } // TableName overrides the default GORM table name. func (Tag) TableName() string { return "tags" } // Name returns the tag name for the specified language. func (t *Tag) Name(lang string) string { if lang == "zh" { return t.NameZh } return t.NameEn } // generateTagSlug creates a URL-friendly slug from tag name. func generateTagSlug(name string) string { slug := strings.ToLower(strings.TrimSpace(name)) slug = strings.ReplaceAll(slug, " ", "-") slug = strings.ReplaceAll(slug, "_", "-") return slug } // FindOrCreateTag finds a tag by name or creates it if it doesn't exist. // If both nameZh and nameEn are provided, it uses them; otherwise uses the same name for both languages. func FindOrCreateTag(db *gorm.DB, nameZh, nameEn string) (*Tag, error) { nameZh = strings.TrimSpace(nameZh) nameEn = strings.TrimSpace(nameEn) // If only one name is provided, use it for both languages if nameZh == "" && nameEn != "" { nameZh = nameEn } else if nameEn == "" && nameZh != "" { nameEn = nameZh } if nameZh == "" { return nil, nil } slug := generateTagSlug(nameZh) var tag Tag err := db.Where("slug = ?", slug).First(&tag).Error if err == nil { return &tag, nil } if err != gorm.ErrRecordNotFound { return nil, err } // Create new tag tag = Tag{ NameZh: nameZh, NameEn: nameEn, Slug: slug, Count: 0, } if err := db.Create(&tag).Error; err != nil { return nil, err } return &tag, nil } // GetAllTags returns all tags ordered by count descending. func GetAllTags(db *gorm.DB) ([]Tag, error) { var tags []Tag err := db.Order("count DESC, name_zh ASC").Find(&tags).Error return tags, err } // GetTagBySlug returns a tag by its slug. func GetTagBySlug(db *gorm.DB, slug string) (*Tag, error) { var tag Tag err := db.Where("slug = ?", slug).First(&tag).Error if err != nil { return nil, err } return &tag, nil } // UpdateTagCount recalculates the article count for a tag. func UpdateTagCount(db *gorm.DB, tagID uint) error { var count int64 db.Table("article_tags").Where("tag_id = ?", tagID).Count(&count) return db.Model(&Tag{}).Where("id = ?", tagID).Update("count", count).Error } // UpdateAllTagCounts recalculates article counts for all tags. func UpdateAllTagCounts(db *gorm.DB) error { // Get all tags var tags []Tag if err := db.Find(&tags).Error; err != nil { return err } // Update count for each tag for _, tag := range tags { if err := UpdateTagCount(db, tag.ID); err != nil { return err } } return nil }