390 lines
11 KiB
Go
390 lines
11 KiB
Go
// Package models 提供CMS数据模型和数据库操作
|
||
// 包含文章、分类、标签、评论等实体的数据结构和CRUD操作
|
||
package models
|
||
|
||
import (
|
||
"fmt"
|
||
|
||
"bsm/full/module/base/cms/internal/impl"
|
||
pb "bsm/full/module/base/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
|
||
}
|