Files
full/module/social/feed/internal/models/query.go
2026-09-22 21:15:34 +08:00

273 lines
7.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package models
import (
"errors"
"bsm/full/module/social/feed/internal/impl"
"git.apinb.com/bsm-sdk/core/errcode"
"gorm.io/gorm"
)
// maxActionCounter 互动计数上限,避免计数被无条件刷高
const maxActionCounter int64 = 1000000
func InitData() error {
return nil
}
func CreatePost(data *FeedPost) error {
err := impl.DBService.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(&data).Error; err != nil {
return err
}
if err := tx.Create(&data.Attachs).Error; err != nil {
return err
}
if err := tx.Create(&data.Tags).Error; err != nil {
return err
}
return nil
})
return err
}
// ChargePost 修改动态仅允许作者本人修改is_open=false 需真正写入,附件变更需真正落库
func ChargePost(data *FeedPost, passportIdentity string) (affected int64, err error) {
err = impl.DBService.Transaction(func(tx *gorm.DB) error {
// 用 map 更新,避免 GORM 结构体更新忽略 is_open=false 这类零值
res := tx.Model(&FeedPost{}).
Where("identity = ? AND passport_identity = ?", data.Identity, passportIdentity).
Updates(map[string]any{"content": data.Content, "is_open": data.IsOpen})
if res.Error != nil {
return res.Error
}
affected = res.RowsAffected
if affected == 0 {
// 动态不存在或非本人发布,直接结束,不再处理附件
return nil
}
// 附件变更真正落库:请求携带附件时整体替换原附件
if len(data.Attachs) > 0 {
if err := tx.Where("post_identity = ?", data.Identity).Delete(&FeedRelateAttach{}).Error; err != nil {
return err
}
if err := tx.Create(&data.Attachs).Error; err != nil {
return err
}
}
return nil
})
return
}
// DeletePost 删除动态,仅允许作者本人删除
func DeletePost(identity, passportIdentity string) (affected int64, err error) {
res := impl.DBService.Where("identity = ? AND passport_identity = ?", identity, passportIdentity).Delete(&FeedPost{})
return res.RowsAffected, res.Error
}
// 点赞/踩操作
func LikeAction(action_op, action_type, identity string) (err error) {
var (
column string
tx = impl.DBService
)
// 显式绑定目标表,避免 action_type 取任意值时目标表不确定
switch action_type {
case "post":
tx = tx.Model(&FeedPost{})
case "comment":
tx = tx.Model(&FeedComment{})
default:
return errcode.ErrInvalidArgument
}
switch action_op {
case "ilike":
column = "cnt_like"
case "unlike":
column = "cnt_unlike"
default:
return errcode.ErrInvalidArgument
}
// 条件更新:目标必须存在且计数未达上限,避免计数被无限刷高
res := tx.Where("identity = ? AND "+column+" < ?", identity, maxActionCounter).
UpdateColumn(column, gorm.Expr(column+" + ?", 1))
if res.Error != nil {
return res.Error
}
if res.RowsAffected == 0 {
return errcode.ErrRecordNotFound
}
return nil
}
// PostList 动态列表,仅返回公开动态或调用者本人发布的动态
func PostList(page, size int64, tag, viewerIdentity string) (list []*FeedPost, cnt int64, err error) {
list = make([]*FeedPost, 0)
tx := impl.DBService.Model(&FeedPost{}).
Where("status <> ?", -1).
Where("is_open = ? OR passport_identity = ?", true, viewerIdentity)
if tag != "" {
tx = tx.Joins("join feed_relate_tags on feed_relate_tags.post_identity = feed_post.identity").
Where("feed_relate_tags.key = ?", tag)
}
// 使句柄可重复使用,统计与列表查询互不影响
tx = tx.Session(&gorm.Session{})
if err = tx.Count(&cnt).Error; err != nil {
return nil, 0, err
}
err = tx.Order("created_at desc").
Limit(int(size)).
Offset(int((page - 1) * size)).
Find(&list).Error
if err != nil {
return nil, 0, err
}
// 标签与附件是 gorm:"-" 字段GORM 不会加载,需二次查询回填
if err = fillRelates(list); err != nil {
return nil, 0, err
}
return list, cnt, nil
}
// fillRelates 按 post_identity 批量回填动态的标签与附件
func fillRelates(list []*FeedPost) error {
if len(list) == 0 {
return nil
}
identities := make([]string, 0, len(list))
for _, item := range list {
identities = append(identities, item.Identity)
}
var attachs []FeedRelateAttach
if err := impl.DBService.Where("post_identity IN ?", identities).Find(&attachs).Error; err != nil {
return err
}
var relates []FeedRelateTags
if err := impl.DBService.Where("post_identity IN ?", identities).Find(&relates).Error; err != nil {
return err
}
// 标签名称存于 feed_tags 字典,按 key 批量查出后回填
contents := map[string]string{}
if len(relates) > 0 {
keys := make([]string, 0, len(relates))
for _, relate := range relates {
keys = append(keys, relate.Key)
}
var dict []FeedTags
if err := impl.DBService.Where("key IN ?", keys).Find(&dict).Error; err != nil {
return err
}
for _, tag := range dict {
contents[tag.Key] = tag.Content
}
}
attachMap := map[string][]FeedRelateAttach{}
for _, attach := range attachs {
attachMap[attach.PostIdentity] = append(attachMap[attach.PostIdentity], attach)
}
tagMap := map[string][]FeedRelateTags{}
for _, relate := range relates {
relate.Content = contents[relate.Key]
tagMap[relate.PostIdentity] = append(tagMap[relate.PostIdentity], relate)
}
for _, item := range list {
item.Attachs = attachMap[item.Identity]
item.Tags = tagMap[item.Identity]
}
return nil
}
func AddTags(data *[]FeedTags) (err error) {
err = impl.DBService.Create(data).Error
if err != nil {
return err
}
return err
}
// ModifyTags 按标签 key 定位并保存feed_tags 表没有 identity 列
func ModifyTags(data *FeedTags) (err error) {
err = impl.DBService.Where("key = ?", data.Key).Save(data).Error
if err != nil {
return err
}
return err
}
// DeleteTags 按标签 key 删除feed_tags 表没有 identity 列
func DeleteTags(key string) (err error) {
err = impl.DBService.Where("key = ?", key).Delete(&FeedTags{}).Error
if err != nil {
return err
}
return err
}
func TagsList() (list []*FeedTags, cnt int64, err error) {
tags := make([]*FeedTags, 0)
tx := impl.DBService.Order("created_at desc")
err = tx.Find(&tags).Count(&cnt).Error
return tags, cnt, err
}
func AddComment(comment *FeedComment, authorIdentity string) (err error) {
err = impl.DBService.Transaction(func(tx *gorm.DB) error {
// 记录评论作者,供后续归属校验使用
comment.PassportIdentity = authorIdentity
// 添加评论,更新文章评论量
err := tx.Create(comment).Table("feed_post").Where("identity = ?", comment.PostIdentity).UpdateColumn("cnt_comment", gorm.Expr("cnt_comment + ?", 1)).Error
if err != nil {
return err
}
if comment.ParentIdentity != "" {
//更新被评论的评论数
err = tx.Where("identity = ?", comment.ParentIdentity).UpdateColumn("cnt_comment", gorm.Expr("cnt_comment + ?", 1)).Error
if err != nil {
return err
}
}
return nil
})
return
}
// DeleteComment 删除评论,仅允许评论作者本人删除,并同步递减所属动态的评论数
func DeleteComment(identity, passportIdentity string) (affected int64, err error) {
err = impl.DBService.Transaction(func(tx *gorm.DB) error {
var comment FeedComment
// 只有作者本人的评论才允许删除
if err := tx.Where("identity = ? AND passport_identity = ?", identity, passportIdentity).First(&comment).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil
}
return err
}
res := tx.Delete(&FeedComment{}, "identity = ?", identity)
if res.Error != nil {
return res.Error
}
affected = res.RowsAffected
// 同步递减所属动态的评论数,并做下限保护避免出现负数
if comment.PostIdentity != "" {
if err := tx.Model(&FeedPost{}).
Where("identity = ? AND cnt_comment > 0", comment.PostIdentity).
UpdateColumn("cnt_comment", gorm.Expr("cnt_comment - ?", 1)).Error; err != nil {
return err
}
}
return nil
})
return
}