// Package models 提供CMS数据模型和数据库操作 // 包含文章、分类、标签、评论等实体的数据结构和CRUD操作 package models import ( "fmt" "git.apinb.com/bsm-apps/cms/internal/impl" pb "git.apinb.com/bsm-apps/cms/pb" "git.apinb.com/bsm-sdk/core/errcode" "git.apinb.com/bsm-sdk/core/types" "git.apinb.com/bsm-sdk/core/utils" "gorm.io/gorm" ) // 数据库表名常量 const ( postTable = "cms_post" // 文章表 accessoryTable = "cms_accessory" // 附件表 postRelateCategoryTable = "cms_relate" // 关联表 ) // InitData 初始化基础数据 // 创建系统必需的默认数据,如根分类等 func InitData() { var cnt int64 = 0 // 检查是否存在根分类,如果不存在则创建 err := impl.DBService.Model(&CmsCategory{}).Where("identity=?", "_RootCategory").Count(&cnt).Error if cnt == 0 || err != nil { data := &CmsCategory{ Title: "根目录", ParentId: 0, // 0表示顶级分类 Std_Identity: types.Std_Identity{Identity: "_RootCategory"}, } impl.DBService.Create(data) } } // AddPost 添加文章及其关联数据 // 使用事务确保数据一致性,同时创建文章、分类关联、标签关联和附件信息 // 参数: // - post: 文章数据模型 // - accessory: 附件数据列表 // - category: 分类标识列表 // - tags: 标签标识列表 // // 返回: // - error: 错误信息 func AddPost(post *CmsPost, accessory []*pb.AccessoryItem, category, tags []string) (err error) { var ( accessoryPath = make([]CmsAccessory, 0) // 文章附件数据 categoryData = make([]CmsRelateCategory, 0) // 文章分类关联数据 TagsData = make([]CmsRelateTags, 0) // 文章标签关联数据 ) // 构建分类关联数据 for _, va := range category { categoryData = append(categoryData, CmsRelateCategory{ Identity: utils.UUID(), PostIdentity: post.Identity, CategoryIdentity: va, }) } // 构建标签关联数据 for _, val := range tags { TagsData = append(TagsData, CmsRelateTags{ Identity: utils.ULID(), PostIdentity: post.Identity, TagsIdentity: val, }) } // 使用事务确保数据一致性 return impl.DBService.Transaction(func(tx *gorm.DB) error { // 创建文章记录 if err = tx.Create(post).Error; err != nil { return err } // 创建附件关联记录 if len(accessory) != 0 { for _, v := range accessory { accessoryPath = append(accessoryPath, CmsAccessory{ Std_Identity: types.Std_Identity{Identity: utils.UUID()}, PostId: post.ID, PostIdentity: post.Identity, FilePath: v.FilePath, Title: v.Title, }) } if err := tx.Create(&accessoryPath).Error; err != nil { return err } } // 创建分类关联记录 if len(category) != 0 { if err := tx.Create(&categoryData).Error; err != nil { return err } } // 创建标签关联记录 if len(tags) != 0 { if err := tx.Create(&TagsData).Error; err != nil { return err } } return nil }) } // PostList 文章列表 func PostList(page, size int64, categoryIdentity, keyword string, userType int64) (list []*CmsPost, cnt int64, err error) { tx := impl.DBService.Debug().Model(&CmsPost{}) if categoryIdentity != "" { tx = tx.Joins("left join cms_relate_category crc on crc.post_identity = cms_post.identity ") tx = tx.Where("crc.category_identity = ?", categoryIdentity) } if keyword != "" { tx = tx.Where("cms_post.title like ?", "%"+keyword+"%") } if userType != 0 { tx = tx.Where("cms_post.types = ?", userType) } if err = tx.Count(&cnt).Order("cms_post.created_at desc").Limit(int(size)).Offset(int((page - 1) * size)).Error; err != nil { return nil, 0, errcode.ErrDB } err = tx.Preload("Accessories").Preload("Categories.Category").Preload("Tags.Tags").Find(&list).Error return } // ModifyPost 修改文章 func ModifyPost(identity string, post *CmsPost, accessory, category, tags []string) (err error) { var ( accessoryPath = make([]CmsAccessory, 0) categoryData = make([]CmsRelateCategory, 0) tagsData = make([]CmsRelateTags, 0) ) for _, v := range category { categoryData = append(categoryData, CmsRelateCategory{ Identity: utils.UUID(), PostIdentity: identity, CategoryIdentity: v, }) } for _, val := range tags { tagsData = append(tagsData, CmsRelateTags{ Identity: utils.ULID(), PostIdentity: identity, TagsIdentity: val, }) } for _, v := range accessory { accessoryPath = append(accessoryPath, CmsAccessory{ Std_Identity: types.Std_Identity{Identity: utils.UUID()}, PostId: post.ID, PostIdentity: identity, FilePath: v, }) } return impl.DBService.Transaction(func(tx *gorm.DB) error { if err = tx.Where("identity = ?", identity).Updates(post).Error; err != nil { return err } // 删除旧的附件、分类和标签关联 if err := tx.Where("post_identity = ?", identity).Delete(&CmsAccessory{}).Error; err != nil { return err } if err := tx.Where("post_identity = ?", identity).Delete(&CmsRelateCategory{}).Error; err != nil { return err } if err := tx.Where("post_identity = ?", identity).Delete(&CmsRelateTags{}).Error; err != nil { return err } // 创建新的分类关联 if len(category) != 0 { if err := tx.Create(&categoryData).Error; err != nil { return err } } // 创建新的标签关联 if len(tags) != 0 { if err := tx.Create(&tagsData).Error; err != nil { return err } } // 创建新的附件关联 if len(accessory) != 0 { if err := tx.Create(&accessoryPath).Error; err != nil { return err } } return nil }) } // DeletePost 删除文章 func DeletePost(identity, authorIdentity string) (err error) { return impl.DBService.Transaction(func(tx *gorm.DB) error { if err = tx.Where("identity = ?", identity).Delete(&CmsPost{}).Error; err != nil { return err } if err = tx.Where("post_identity = ?", identity).Delete(&CmsRelateCategory{}).Error; err != nil { return err } if err = tx.Where("post_identity = ?", identity).Delete(&CmsRelateTags{}).Error; err != nil { return err } if err = tx.Where("post_identity = ?", identity).Delete(&CmsAccessory{}).Error; err != nil { return err } return nil }) } // GetPost 根据指定字段获取文章详情 // 查询成功后会更新文章的点击量 // 参数: // - key: 查询字段名(如"identity"、"key"等) // - val: 查询字段值 // // 返回: // - *CmsPost: 文章详情 // - error: 错误信息 func GetPost(key, val string) (*CmsPost, error) { var post = &CmsPost{} // 使用事务确保查询和更新操作的原子性 return post, impl.DBService.Transaction(func(tx *gorm.DB) error { // 查询文章详情,预加载关联数据 if err := tx.Model(&CmsPost{}).Preload("Accessories").Preload("Categories").Preload("Tags"). Where(key+" = ?", val).First(post).Error; err != nil { return err } // 更新文章点击量,只有在查询成功后才更新 if err := tx.Model(&CmsPost{}).Where(key+" = ?", val). UpdateColumn("hits", gorm.Expr("hits + 1")).Error; err != nil { return err } return nil }) } // IncrOrDescPostField 增加或减少文章字段值 // 用于更新文章的点赞、踩赞、评论等计数字段 // 参数: // - identity: 文章唯一标识 // - column: 要更新的字段名 // - desc: true表示减少,false表示增加 // // 返回: // - error: 错误信息 func IncrOrDescPostField(identity, column string, desc bool) (err error) { expr := "%s + ?" if desc { expr = "%s - ?" } if err := impl.DBService.Model(&CmsPost{}).Where("identity = ?", identity).UpdateColumn(column, gorm.Expr(fmt.Sprintf(expr, column), 1)).Error; err != nil { return err } return nil } func AddComment(comment *CmsComment, authorIdentity string) (err error) { err = impl.DBService.Transaction(func(tx *gorm.DB) error { if err := tx.Create(comment).Error; err != nil { return err } // 添加评论,更新文章评论量 err := tx.Model(&CmsPost{}).Where("identity = ?", comment.PostIdentity).UpdateColumn("comment_hits", gorm.Expr("comment_hits + ?", 1)).Error if err != nil { return err } if comment.ParentId != 0 { // 更新被评论的那条评论的计数 err = tx.Model(&CmsComment{}).Where("id = ?", comment.ParentId).UpdateColumn("comment_hits", gorm.Expr("comment_hits + ?", 1)).Error if err != nil { return err } } return nil }) return } func ModifyComment(comment *CmsComment) (err error) { return impl.DBService.Where("identity = ?", comment.Identity).Updates(comment).Error } func DeleteComment(identity string) (err error) { // get comment comment := new(CmsComment) err = impl.DBService.Where("identity = ?", identity).First(comment).Error if err != nil { return err } err = impl.DBService.Transaction(func(tx *gorm.DB) error { // 删除评论,更新文章评论量 if err := tx.Where("identity = ?", identity).Delete(&CmsComment{}).Error; err != nil { return err } if err := tx.Model(&CmsPost{}).Where("identity = ?", comment.PostIdentity). UpdateColumn("comment_hits", gorm.Expr("comment_hits - ?", 1)).Error; err != nil { return err } if comment.ParentId != 0 { // 更新被评论的那条评论的计数 if err := tx.Model(&CmsComment{}).Where("id = ?", comment.ParentId). UpdateColumn("comment_hits", gorm.Expr("comment_hits - ?", 1)).Error; err != nil { return err } } // 删除子级评论 err = tx.Where("parent_id = ?", comment.ID).Delete(&CmsComment{}).Error if err != nil { return err } return nil }) return } // IncrOrDescCommentField 增加或减少评论字段值 // 用于更新评论的点赞、踩赞等计数字段 // 参数: // - identity: 评论唯一标识 // - column: 要更新的字段名 // - desc: true表示减少,false表示增加 // // 返回: // - error: 错误信息 // // TODO: 记录点赞对象避免重复点赞 func IncrOrDescCommentField(identity, column string, desc bool) (err error) { expr := "%s + ?" if desc { expr = "%s - ?" } if err = impl.DBService.Model(&CmsComment{}).Where("identity = ?", identity).UpdateColumn(column, gorm.Expr(fmt.Sprintf(expr, column), 1)).Error; err != nil { return err } return nil } // CategoryList 文章分类列表 func CategoryList() (list []CmsCategory, cnt int64, err error) { result := impl.DBService.Preload("Children").Find(&list) if result.Error != nil { err = result.Error return } cnt = result.RowsAffected return } // DeleteCategory 删除分类 func DeleteCategory(identity string) (err error) { var ( cnt int64 = 0 category CmsCategory ) err = impl.DBService.Model(&CmsCategory{}).Where("identity = ?", identity).Find(&category).Error if err != nil { return err } impl.DBService.Model(&CmsCategory{}).Where("parent_id = ?", category.ID).Count(&cnt) if cnt != 0 { return errcode.NewError(500, "ErrCategoryHasSubcategories") } err = impl.DBService.Where("identity = ?", identity).Delete(&CmsCategory{}).Error if err != nil { return err } return }