2026-08-09 10:43:45 +08:00
|
|
|
|
// Package models 提供CMS数据模型和数据库操作
|
|
|
|
|
|
// 包含文章、分类、标签、评论等实体的数据结构和CRUD操作
|
|
|
|
|
|
package models
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"fmt"
|
|
|
|
|
|
|
2026-08-09 12:41:08 +08:00
|
|
|
|
"bsm/full/module/base/cms/internal/impl"
|
|
|
|
|
|
pb "bsm/full/module/base/cms/pb"
|
2026-08-09 10:43:45 +08:00
|
|
|
|
"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
|
|
|
|
|
|
}
|