Files
2026-03-05 09:12:19 +08:00

435 lines
12 KiB
Go

package plant
import (
"sundynix-go/global"
"sundynix-go/model/plant"
plantReq "sundynix-go/model/plant/request"
"sundynix-go/model/system"
"sundynix-go/utils/wechat"
"gorm.io/gorm"
"go.uber.org/zap"
)
type PostService struct{}
var PostServiceApp = new(PostService)
// PublishPost 发表帖子
func (s *PostService) PublishPost(req plantReq.CreatePost, userId string) error {
var postId string
var ossList []*system.Oss
err := global.DB.Transaction(func(tx *gorm.DB) error {
//1.验证oss是否存在
err := tx.Where("id in ?", req.OssIds).Find(&ossList).Error
if err != nil {
return err
}
//2.保存帖子
post := plant.Post{
Title: req.Title,
UserId: userId,
Content: req.Content,
Location: req.Location,
}
err = tx.Create(&post).Error
if err != nil {
return err
}
postId = post.Id
//3.处理图片关系
if len(ossList) > 0 {
var relations []map[string]interface{}
for _, oss := range ossList {
relations = append(relations, map[string]interface{}{
"post_id": post.Id,
"oss_id": oss.Id,
})
}
err = tx.Table("sundynix_post_oss").Create(relations).Error
if err != nil {
return err
}
}
return nil
})
if err != nil {
return err
}
// 4.异步内容安全检查
// 4.异步内容安全检查
go func(pid, uid, title, content string, imgs []*system.Oss) {
defer func() {
if r := recover(); r != nil {
global.Logger.Error("异步内容安全检查Panic", zap.Any("panic", r))
}
}()
// 获取用户 openid
var user system.User
if err := global.DB.Where("id = ?", uid).First(&user).Error; err != nil {
global.Logger.Error("获取用户失败,跳过安全检查", zap.String("userId", uid), zap.Error(err))
return
}
openid := user.MiniOpenId
if openid == "" {
global.Logger.Warn("用户无MiniOpenId,跳过安全检查", zap.String("userId", uid))
return
}
// 文本检查
fullContent := title + "\n" + content
isSafe := wechat.MsgSecCheck(fullContent, openid)
// 文本违规 -> 直接拒绝
if !isSafe {
if err := global.DB.Model(&plant.Post{}).Where("id = ?", pid).Update("has_reviewed", 2).Error; err != nil {
global.Logger.Error("更新帖子违规状态失败", zap.String("postId", pid), zap.Error(err))
}
global.Logger.Info("帖子内容违规,已标记", zap.String("postId", pid))
return // 文本违规,无需再检查图片
}
if err := global.DB.Model(&plant.Post{}).Where("id = ?", pid).Update("has_reviewed", 1).Error; err != nil {
global.Logger.Error("更新帖子违规状态失败", zap.String("postId", pid), zap.Error(err))
}
return // 文本违规,无需再检查图片
// 文本通过,判断是否有图片
//if len(imgs) == 0 {
// // 无图且文本通过 -> 直接通过
// if err := global.DB.Model(&plant.Post{}).Where("id = ?", pid).Update("has_reviewed", 1).Error; err != nil {
// global.Logger.Error("更新帖子通过状态失败", zap.String("postId", pid), zap.Error(err))
// }
// return
//}
// 有图,文本通过 -> 保持状态为0(待审核),等待图片回调
// todo 图片检查
//for _, oss := range imgs {
// // 假设 oss.Url 是完整链接
// traceId, err := wechat.MediaCheckAsync(oss.Url, 2, openid)
// if err != nil {
// global.Logger.Error("图片安全检查请求失败", zap.String("url", oss.Url), zap.Error(err))
// continue
// }
// global.Logger.Info("图片安全检查请求成功", zap.String("traceId", traceId))
//
// // 保存检测记录
// checkResult := plant.MediaCheckResult{
// TraceId: traceId,
// PostId: pid,
// OssId: oss.Id,
// UserId: uid,
// Status: 0, // 检测中
// Type: 2, // 图片
// }
// if err := global.DB.Create(&checkResult).Error; err != nil {
// global.Logger.Error("保存媒体检测记录失败", zap.Error(err))
// }
//}
}(postId, userId, req.Title, req.Content, ossList)
return nil
}
// PostPage 帖子列表
func (s *PostService) PostPage(req plantReq.PostPage, userId string) (list interface{}, total int64, err error) {
limit := req.PageSize
offset := req.PageSize * (req.Current - 1)
db := global.DB.Model(&plant.Post{}).
Preload("ImgList", func(db *gorm.DB) *gorm.DB {
return db.Order("created_at desc")
}).
Preload("Publisher", func(db *gorm.DB) *gorm.DB {
return db.Preload("Avatar")
}).
Preload("CommentList", func(db *gorm.DB) *gorm.DB {
return db.Preload("Commentator", func(db *gorm.DB) *gorm.DB {
return db.Preload("Avatar")
})
}).Preload("LikeList", func(db *gorm.DB) *gorm.DB {
return db.Preload("Liker")
})
var posts []plant.Post
if req.Title != "" {
db = db.Where("title like ?", "%"+req.Title+"%")
}
if req.HasReviewed != nil {
db = db.Where("has_reviewed = ?", *req.HasReviewed)
}
//todo 审核帖子
err = db.Count(&total).Error
if err != nil {
return
}
err = db.Limit(limit).Offset(offset).Order("created_at desc").Find(&posts).Error
// 优化 N+1 查询
var postIds []string
for _, v := range posts {
postIds = append(postIds, v.Id)
}
// 批量查询当前用户点赞的记录
var postLikeList []*plant.PostLike
err = global.DB.Where("user_id = ? and post_id in ?", userId, postIds).Find(&postLikeList).Error
if err != nil {
return
}
//批量查询当前用户的收藏
var stars []*plant.UserStar
err = global.DB.Where("user_id = ? and post_id in ?", userId, postIds).Find(&stars).Error
if err != nil {
return
}
// 构建id映射
likesMap := make(map[string]bool)
for _, v := range postLikeList {
likesMap[v.PostId] = true
}
starsMap := make(map[string]bool)
for _, v := range stars {
starsMap[v.PostId] = true
}
// 是否点赞
for i := range posts {
if likesMap[posts[i].Id] {
posts[i].HasLiked = 1
} else {
posts[i].HasLiked = 0
}
if starsMap[posts[i].Id] {
posts[i].HasStar = 1
} else {
posts[i].HasStar = 0
}
}
return posts, total, err
}
// MyPost 我的帖子
func (s *PostService) MyPost(req plantReq.PostPage, userId string) (list interface{}, total int64, err error) {
limit := req.PageSize
offset := req.PageSize * (req.Current - 1)
db := global.DB.Model(&plant.Post{}).
Preload("ImgList", func(db *gorm.DB) *gorm.DB {
return db.Order("created_at desc")
}).
Preload("Publisher", func(db *gorm.DB) *gorm.DB {
return db.Preload("Avatar")
}).
Preload("LikeList", func(db *gorm.DB) *gorm.DB {
return db.Preload("Liker", func(db *gorm.DB) *gorm.DB {
return db.Preload("Avatar")
})
}).
Preload("CommentList", func(db *gorm.DB) *gorm.DB {
return db.Preload("Commentator", func(db *gorm.DB) *gorm.DB {
return db.Preload("Avatar")
})
})
var posts []plant.Post
db = db.Where("user_id = ?", userId)
if req.Title != "" {
db = db.Where("title like ?", "%"+req.Title+"%")
}
//todo 审核帖子
err = db.Count(&total).Error
if err != nil {
return
}
err = db.Limit(limit).Offset(offset).Order("created_at desc").Find(&posts).Error
// 优化 N+1 查询
var postIds []string
for _, v := range posts {
postIds = append(postIds, v.Id)
}
// 批量查询当前用户点赞的记录
var postLikeList []*plant.PostLike
err = global.DB.Where("user_id = ? and post_id in ?", userId, postIds).Find(&postLikeList).Error
if err != nil {
return
}
// 构建id映射
likesMap := make(map[string]bool)
for _, v := range postLikeList {
likesMap[v.PostId] = true
}
// 是否点赞
for i := range posts {
if likesMap[posts[i].Id] {
posts[i].HasLiked = 1
} else {
posts[i].HasLiked = 0
}
}
return posts, total, err
}
// LikePost 点赞帖或取消赞
func (s *PostService) LikePost(userId, postId, class string) error {
// class = 1点赞
if class == "1" {
return global.DB.Transaction(func(tx *gorm.DB) error {
var post plant.Post
err := tx.Where("id = ?", postId).First(&post).Error
if err != nil {
return err
}
//1.更新点赞数
err = tx.Model(&post).Update("like_count", post.LikeCount+1).Error
if err != nil {
return err
}
//2.添加点赞记录
like := plant.PostLike{
UserId: userId,
PostId: postId,
}
return tx.Create(&like).Error
})
} else if class == "2" {
return global.DB.Transaction(func(tx *gorm.DB) error {
var like plant.PostLike
err := tx.Where("post_id = ? and user_id = ?", postId, userId).First(&like).Error
if err != nil {
return err
}
//1.更新点赞数
var post plant.Post
err = tx.Where("id = ?", postId).First(&post).Error
if err != nil {
return err
}
err = tx.Model(&post).
Where("like_count > 0"). // 只有大于 0 才会执行减法
Update("like_count", post.LikeCount-1).Error
if err != nil {
return err
}
//2.删除点赞记录
return tx.Unscoped().Delete(&like).Error
})
}
return nil
}
// CommentPost 评论帖子
func (s *PostService) CommentPost(req plantReq.CreateComment, userId string) error {
return global.DB.Transaction(func(tx *gorm.DB) error {
//1.为帖子评论数量+1
var post plant.Post
err := global.DB.Where("id = ?", req.PostId).First(&post).Error
if err != nil {
return err
}
//1.更新评论数
err = tx.Model(&post).Update("comment_count", post.CommentCount+1).Error
if err != nil {
return err
}
//2.添加评论
comment := plant.PostComment{
PostId: req.PostId,
UserId: userId,
Content: req.Content,
}
return tx.Create(&comment).Error
})
}
// DeletePost 删除帖子
func (s *PostService) DeletePost(ids []string) error {
return global.DB.Transaction(func(tx *gorm.DB) error {
var imgIds []string
tx.Table("sundynix_post_oss").Where("post_id IN ?", ids).Pluck("oss_id", &imgIds)
// 2. 清理中间表记录 (解开多对多关系)
// 使用 Exec 直接操作中间表比循环 Clear 快得多
if err := tx.Exec("DELETE FROM sundynix_post_oss WHERE post_id IN ?", ids).Error; err != nil {
return err
}
// 3. 物理删除图片记录本身
if len(imgIds) > 0 {
if err := tx.Unscoped().Where("id IN ?", imgIds).Delete(&system.Oss{}).Error; err != nil {
return err
}
}
// 4. 批量删除点赞 (PostLike)
if err := tx.Unscoped().Where("post_id IN ?", ids).Delete(&plant.PostLike{}).Error; err != nil {
return err
}
// 5. 批量删除评论 (PostComment)
if err := tx.Unscoped().Where("post_id IN ?", ids).Delete(&plant.PostComment{}).Error; err != nil {
return err
}
// 6. 最后删除主表 Post
if err := tx.Unscoped().Where("id IN ?", ids).Delete(&plant.Post{}).Error; err != nil {
return err
}
return nil
})
}
// StarPost 收藏帖子
func (s *PostService) StarPost(userId string, postId string, class string) error {
if class == "1" {
return global.DB.Transaction(func(tx *gorm.DB) error {
var post plant.Post
err := tx.Where("id = ?", postId).First(&post).Error
if err != nil {
return err
}
//1.更新点赞数
err = tx.Model(&post).Update("star_count", post.LikeCount+1).Error
if err != nil {
return err
}
//2.添加到我的收藏
star := plant.UserStar{
UserId: userId,
Type: 2,
PostId: postId,
}
return tx.Create(&star).Error
})
} else if class == "2" {
return global.DB.Transaction(func(tx *gorm.DB) error {
var star plant.UserStar
err := tx.Where("post_id = ? and user_id = ?", postId, userId).First(&star).Error
if err != nil {
return err
}
//1.更新收藏数
var post plant.Post
err = tx.Where("id = ?", postId).First(&post).Error
if err != nil {
return err
}
err = tx.Model(&post).
Where("star_count > ?", 0). // 只有大于 0 才会执行减法
Update("star_count", gorm.Expr("star_count - 1")).
Error
if err != nil {
return err
}
//2.删除收藏
return tx.Unscoped().Delete(&star).Error
})
}
return nil
}