| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567 |
- package model
- import (
- "context"
- "encoding/json"
- "fmt"
- "time"
- "git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
- "github.com/go-redis/redis/v8"
- "gorm.io/gorm"
- )
- const (
- // 缓存key前缀
- prefixPostKey = "post:%d" // 帖子信息
- prefixPostContentKey = "post:content:%d" // 帖子内容
- prefixUserLikeKey = "user:like:%d:%d" // 用户点赞状态
- prefixUserCollectKey = "user:collect:%d:%d" // 用户收藏状态
- prefixPostListKey = "post:list:%d:%d:%d:%s" // 帖子列表 (searchType:sortType:page:keyword)
- cacheExpiry = 24 * time.Hour // 缓存过期时间
- )
- // PPost 冒泡/文章
- type Post struct {
- *Model
- UserId int64 `json:"user_id" gorm:"user_id"` // 用户ID
- PostType int32 `json:"post_type" gorm:"post_type"` // 帖子类型0-普通图文1-视频
- CommentCount int64 `json:"comment_count" gorm:"comment_count"` // 评论数
- CollectionCount int64 `json:"collection_count" gorm:"collection_count"` // 收藏数
- UpvoteCount int64 `json:"upvote_count" gorm:"upvote_count"` // 点赞数
- ShareCount int64 `json:"share_count" gorm:"share_count"` // 分享数
- Visibility int8 `json:"visibility" gorm:"visibility"` // 可见性: 0私密 1好友可见 2关注可见 4公开
- IsTop int8 `json:"is_top" gorm:"is_top"` // 是否置顶
- IsEssence int8 `json:"is_essence" gorm:"is_essence"` // 是否精华
- LatestRepliedOn int64 `json:"latest_replied_on" gorm:"latest_replied_on"` // 最新回复时间
- Tags string `json:"tags" gorm:"tags"` // 标签
- Ip string `json:"ip" gorm:"ip"` // IP地址
- IpLoc string `json:"ip_loc" gorm:"ip_loc"` // IP城市地址
- WithUserIds string `json:"with_user_ids" gorm:"with_user_ids"` // 被艾特的用户
- HotNum int64 `json:"hot_num" gorm:"hot_num"` // 热度值
- }
- // TableName 表名称
- func (*Post) TableName() string {
- return "p_post"
- }
- // PostModel 帖子模型
- type PostModel struct {
- conn *gorm.DB
- redis *redis.Client
- }
- func NewPostModel(conn *gorm.DB, rdb *redis.Client) *PostModel {
- return &PostModel{
- conn: conn,
- redis: rdb,
- }
- }
- // Create 创建帖子
- func (m *PostModel) Create(ctx context.Context, post *Post) error {
- return m.conn.WithContext(ctx).Create(post).Error
- }
- // PostContentModel 帖子内容模型
- type PostContentModel struct {
- conn *gorm.DB
- }
- func NewPostContentModel(conn *gorm.DB) *PostContentModel {
- return &PostContentModel{
- conn: conn,
- }
- }
- // Create 创建帖子内容
- func (m *PostContentModel) Create(ctx context.Context, content *PostContent) error {
- return m.conn.WithContext(ctx).Create(content).Error
- }
- // GetPostList 根据条件获取帖子列表
- func (m *PostModel) GetPostList(ctx context.Context, req *slowwildserver.GetPostListReq) ([]*Post, error) {
- // 构建缓存key
- cacheKey := fmt.Sprintf(prefixPostListKey, req.SearchType, req.SortType, req.Page, req.Keyword)
- // 尝试从缓存获取
- var posts []*Post
- data, err := m.redis.Get(ctx, cacheKey).Bytes()
- if err == nil {
- if err := json.Unmarshal(data, &posts); err == nil {
- return posts, nil
- }
- }
- var normalPosts []*Post
- // 先查询置顶帖子
- topQuery := m.conn.WithContext(ctx).Model(&Post{}).Where("is_top = ?", 1)
- normalQuery := m.conn.WithContext(ctx).Model(&Post{}).Where("is_top = ?", 0)
- // 根据搜索类型构建查询条件
- switch req.SearchType {
- case 1: // 搜索内容/标题
- subQuery := fmt.Sprintf("%%%s%%", req.Keyword)
- topQuery = topQuery.Joins("LEFT JOIN p_post_content ON p_post.id = p_post_content.post_id").
- Where("p_post_content.content LIKE ? OR p_post_content.title LIKE ?", subQuery, subQuery)
- normalQuery = normalQuery.Joins("LEFT JOIN p_post_content ON p_post.id = p_post_content.post_id").
- Where("p_post_content.content LIKE ? OR p_post_content.title LIKE ?", subQuery, subQuery)
- case 2: // 话题搜索
- topQuery = topQuery.Joins("JOIN p_tag_with_post ON p_tag_with_post.post_id = p_post.id").
- Where("p_tag_with_post.tag_id = ?", req.Keyword)
- normalQuery = normalQuery.Joins("JOIN p_tag_with_post ON p_tag_with_post.post_id = p_post.id").
- Where("p_tag_with_post.tag_id = ?", req.Keyword)
- }
- // 根据排序类型构建排序条件
- switch req.SortType {
- case 1: // 热度排序
- topQuery = topQuery.Order("hot_num DESC")
- normalQuery = normalQuery.Order("hot_num DESC")
- default: // 默认创建时间排序
- topQuery = topQuery.Order("created_on DESC")
- normalQuery = normalQuery.Order("created_on DESC")
- }
- // 查询置顶帖子
- if err := topQuery.Find(&posts).Error; err != nil {
- return nil, err
- }
- // 查询普通帖子
- offset := (req.Page - 1) * req.PageSize
- limit := int(req.PageSize) - len(posts)
- if limit > 0 {
- if err := normalQuery.Offset(int(offset)).Limit(limit).Find(&normalPosts).Error; err != nil {
- return nil, err
- }
- posts = append(posts, normalPosts...)
- }
- // 缓存查询结果
- if data, err := json.Marshal(posts); err == nil {
- // 设置较短的过期时间,因为列表数据变化较频繁
- m.redis.Set(ctx, cacheKey, data, 5*time.Minute)
- }
- return posts, nil
- }
- // GetPostContents 批量获取帖子内容(带缓存)
- func (m *PostModel) GetPostContents(ctx context.Context, postIds []int64) (map[int64]*PostContent, error) {
- contentMap := make(map[int64]*PostContent)
- var missedIds []int64
- // 先从Redis获取
- for _, id := range postIds {
- key := fmt.Sprintf(prefixPostContentKey, id)
- data, err := m.redis.Get(ctx, key).Bytes()
- if err == nil {
- var content PostContent
- if err := json.Unmarshal(data, &content); err == nil {
- contentMap[id] = &content
- continue
- }
- }
- missedIds = append(missedIds, id)
- }
- // 查询未命中的内容
- if len(missedIds) > 0 {
- var contents []*PostContent
- err := m.conn.WithContext(ctx).Where("post_id IN ?", missedIds).Find(&contents).Error
- if err != nil {
- return nil, err
- }
- // 写入缓存并添加到结果
- for _, content := range contents {
- key := fmt.Sprintf(prefixPostContentKey, content.PostId)
- if data, err := json.Marshal(content); err == nil {
- m.redis.Set(ctx, key, data, cacheExpiry)
- }
- contentMap[content.PostId] = content
- }
- }
- return contentMap, nil
- }
- // IsPostLikedByUser 检查用户是否点赞了帖子(带缓存)
- func (m *PostModel) IsPostLikedByUser(ctx context.Context, userId, postId int64) (bool, error) {
- key := fmt.Sprintf(prefixUserLikeKey, userId, postId)
- exists, err := m.redis.Exists(ctx, key).Result()
- if err == nil && exists == 1 {
- return m.redis.Get(ctx, key).Bool()
- }
- var count int64
- err = m.conn.WithContext(ctx).
- Model(&PostAction{}).
- Where("user_id = ? AND post_id = ? and type = 0", userId, postId).
- Count(&count).Error
- if err != nil {
- return false, err
- }
- liked := count > 0
- m.redis.Set(ctx, key, liked, cacheExpiry)
- return liked, nil
- }
- // IsPostCollectedByUser 检查用户是否收藏了帖子(带缓存)
- func (m *PostModel) IsPostCollectedByUser(ctx context.Context, userId, postId int64) (bool, error) {
- key := fmt.Sprintf(prefixUserCollectKey, userId, postId)
- exists, err := m.redis.Exists(ctx, key).Result()
- if err == nil && exists == 1 {
- return m.redis.Get(ctx, key).Bool()
- }
- var count int64
- err = m.conn.WithContext(ctx).
- Model(&PostAction{}).
- Where("user_id = ? AND post_id = ? and type = 1", userId, postId).
- Count(&count).Error
- if err != nil {
- return false, err
- }
- collected := count > 0
- m.redis.Set(ctx, key, collected, cacheExpiry)
- return collected, nil
- }
- // 在需要更新帖子时,清除相关缓存
- func (m *PostModel) ClearListCache(ctx context.Context) error {
- // 使用通配符匹配所有列表缓存
- pattern := "post:list:*"
- keys, err := m.redis.Keys(ctx, pattern).Result()
- if err != nil {
- return err
- }
- if len(keys) > 0 {
- return m.redis.Del(ctx, keys...).Err()
- }
- return nil
- }
- // UpdatePost 更新帖子时清除缓存
- func (m *PostModel) UpdatePost(ctx context.Context, post *Post) error {
- err := m.conn.WithContext(ctx).Save(post).Error
- if err != nil {
- return err
- }
- // 清除帖子相关的所有缓存
- m.redis.Del(ctx, fmt.Sprintf(prefixPostKey, post.ID))
- m.ClearListCache(ctx)
- return nil
- }
- // GetPostDetail 获取帖子详情(带缓存)
- func (m *PostModel) GetPostDetail(ctx context.Context, postId int64) (*Post, error) {
- // 尝试从缓存获取
- key := fmt.Sprintf(prefixPostKey, postId)
- data, err := m.redis.Get(ctx, key).Bytes()
- if err == nil {
- var post Post
- if err := json.Unmarshal(data, &post); err == nil {
- return &post, nil
- }
- }
- // 从数据库获取
- var post Post
- err = m.conn.WithContext(ctx).First(&post, postId).Error
- if err != nil {
- return nil, err
- }
- // 写入缓存
- if data, err := json.Marshal(post); err == nil {
- m.redis.Set(ctx, key, data, cacheExpiry)
- }
- return &post, nil
- }
- // GetPostContent 获取帖子内容(带缓存)
- func (m *PostModel) GetPostContent(ctx context.Context, postId int64) (*PostContent, error) {
- // 尝试从缓存获取
- key := fmt.Sprintf(prefixPostContentKey, postId)
- data, err := m.redis.Get(ctx, key).Bytes()
- if err == nil {
- var content PostContent
- if err := json.Unmarshal(data, &content); err == nil {
- return &content, nil
- }
- }
- // 从数据库获取
- var content PostContent
- err = m.conn.WithContext(ctx).Where("post_id = ?", postId).First(&content).Error
- if err != nil {
- return nil, err
- }
- // 写入缓存
- if data, err := json.Marshal(content); err == nil {
- m.redis.Set(ctx, key, data, cacheExpiry)
- }
- return &content, nil
- }
- // IncrementCollectionCount 增加收藏数
- func (m *PostModel) IncrementCollectionCount(ctx context.Context, postId int64, increment int) error {
- err := m.conn.WithContext(ctx).Model(&Post{}).
- Where("id = ?", postId).
- UpdateColumn("collection_count", gorm.Expr("collection_count + ?", increment)).Error
- if err != nil {
- return err
- }
- // 删除相关缓存
- key := fmt.Sprintf(prefixPostKey, postId)
- m.redis.Del(ctx, key)
- m.ClearListCache(ctx)
- return nil
- }
- // IncrementCommentCount 增加评论数
- func (m *PostModel) IncrementCommentCount(ctx context.Context, postId int64, increment int) error {
- err := m.conn.WithContext(ctx).Model(&Post{}).
- Where("id = ?", postId).
- UpdateColumn("comment_count", gorm.Expr("comment_count + ?", increment)).Error
- if err != nil {
- return err
- }
- // 删除相关缓存
- key := fmt.Sprintf(prefixPostKey, postId)
- m.redis.Del(ctx, key)
- m.ClearListCache(ctx)
- return nil
- }
- // IncrementShareCount 增加分享数
- func (m *PostModel) IncrementShareCount(ctx context.Context, postId int64) error {
- err := m.conn.WithContext(ctx).Model(&Post{}).
- Where("id = ?", postId).
- UpdateColumn("share_count", gorm.Expr("share_count + ?", 1)).Error
- if err != nil {
- return err
- }
- // 删除相关缓存
- key := fmt.Sprintf(prefixPostKey, postId)
- m.redis.Del(ctx, key)
- m.ClearListCache(ctx)
- return nil
- }
- // Delete 删除帖子
- func (m *PostModel) Delete(ctx context.Context, postId int64) error {
- return m.conn.WithContext(ctx).Model(&Post{}).
- Where("id = ?", postId).
- Update("is_del", 1).Error
- }
- // ClearCache 清除帖子相关的所有缓存
- func (m *PostModel) ClearCache(ctx context.Context, postId int64) {
- // 删除帖子详情缓存
- key := fmt.Sprintf(prefixPostKey, postId)
- m.redis.Del(ctx, key)
- // 删除帖子内容缓存
- key = fmt.Sprintf(prefixPostContentKey, postId)
- m.redis.Del(ctx, key)
- // 删除帖子列表缓存
- m.ClearListCache(ctx)
- }
- // IncrementUpvoteCount 增加点赞数
- func (m *PostModel) IncrementUpvoteCount(ctx context.Context, postId int64, increment int) error {
- err := m.conn.WithContext(ctx).Model(&Post{}).
- Where("id = ?", postId).
- UpdateColumn("upvote_count", gorm.Expr("upvote_count + ?", increment)).Error
- if err != nil {
- return err
- }
- // 删除相关缓存
- key := fmt.Sprintf(prefixPostKey, postId)
- m.redis.Del(ctx, key)
- m.ClearListCache(ctx)
- return nil
- }
- // GetUserCollectionPosts 获取用户收藏的帖子列表(带缓存)
- func (m *PostModel) GetUserCollectionPosts(ctx context.Context, userId int64, page, pageSize int) ([]*Post, int64, error) {
- // 尝试从缓存获取
- key := fmt.Sprintf("user:collection:%d:%d:%d", userId, page, pageSize)
- data, err := m.redis.Get(ctx, key).Bytes()
- if err == nil {
- var cacheData struct {
- Posts []*Post
- Total int64
- }
- if err := json.Unmarshal(data, &cacheData); err == nil {
- return cacheData.Posts, cacheData.Total, nil
- }
- }
- // 从数据库获取
- var posts []*Post
- var total int64
- // 获取收藏总数
- err = m.conn.Model(&PostAction{}).
- Where("user_id = ? AND action_type = ? AND is_del = 0", userId, 1). // 1表示收藏动作
- Count(&total).Error
- if err != nil {
- return nil, 0, err
- }
- // 获取收藏的帖子列表
- offset := (page - 1) * pageSize
- err = m.conn.Table("p_post_action pa").
- Select("p.*").
- Joins("LEFT JOIN p_post p ON p.id = pa.post_id").
- Where("pa.user_id = ? AND pa.action_type = ? AND pa.is_del = 0 AND p.is_del = 0", userId, 1).
- Order("pa.created_on DESC").
- Offset(offset).
- Limit(pageSize).
- Find(&posts).Error
- if err != nil {
- return nil, 0, err
- }
- // 写入缓存
- cacheData := struct {
- Posts []*Post
- Total int64
- }{
- Posts: posts,
- Total: total,
- }
- if data, err := json.Marshal(cacheData); err == nil {
- m.redis.Set(ctx, key, data, 5*time.Minute)
- }
- return posts, total, nil
- }
- // GetUserLikePosts 获取用户点赞的帖子列表(带缓存)
- func (m *PostModel) GetUserLikePosts(ctx context.Context, userId int64, page, pageSize int) ([]*Post, int64, error) {
- // 尝试从缓存获取
- key := fmt.Sprintf("user:like:%d:%d:%d", userId, page, pageSize)
- data, err := m.redis.Get(ctx, key).Bytes()
- if err == nil {
- var cacheData struct {
- Posts []*Post
- Total int64
- }
- if err := json.Unmarshal(data, &cacheData); err == nil {
- return cacheData.Posts, cacheData.Total, nil
- }
- }
- // 从数据库获取
- var posts []*Post
- var total int64
- // 获取点赞总数
- err = m.conn.Model(&PostAction{}).
- Where("user_id = ? AND action_type = ? AND is_del = 0", userId, 0). // 0表示点赞动作
- Count(&total).Error
- if err != nil {
- return nil, 0, err
- }
- // 获取点赞的帖子列表
- offset := (page - 1) * pageSize
- err = m.conn.Table("p_post_action pa").
- Select("p.*").
- Joins("LEFT JOIN p_post p ON p.id = pa.post_id").
- Where("pa.user_id = ? AND pa.action_type = ? AND pa.is_del = 0 AND p.is_del = 0", userId, 0).
- Order("pa.created_on DESC").
- Offset(offset).
- Limit(pageSize).
- Find(&posts).Error
- if err != nil {
- return nil, 0, err
- }
- // 写入缓存
- cacheData := struct {
- Posts []*Post
- Total int64
- }{
- Posts: posts,
- Total: total,
- }
- if data, err := json.Marshal(cacheData); err == nil {
- m.redis.Set(ctx, key, data, 5*time.Minute)
- }
- return posts, total, nil
- }
- // GetUserPosts 获取用户发布的帖子列表(带缓存)
- func (m *PostModel) GetUserPosts(ctx context.Context, userId int64, page, pageSize int) ([]*Post, int64, error) {
- // 尝试从缓存获取
- key := fmt.Sprintf("user:posts:%d:%d:%d", userId, page, pageSize)
- data, err := m.redis.Get(ctx, key).Bytes()
- if err == nil {
- var cacheData struct {
- Posts []*Post
- Total int64
- }
- if err := json.Unmarshal(data, &cacheData); err == nil {
- return cacheData.Posts, cacheData.Total, nil
- }
- }
- // 从数据库获取
- var posts []*Post
- var total int64
- // 获取发布帖子总数
- err = m.conn.Model(&Post{}).
- Where("user_id = ? AND is_del = 0", userId).
- Count(&total).Error
- if err != nil {
- return nil, 0, err
- }
- // 获取发布的帖子列表
- offset := (page - 1) * pageSize
- err = m.conn.Where("user_id = ? AND is_del = 0", userId).
- Order("created_on DESC").
- Offset(offset).
- Limit(pageSize).
- Find(&posts).Error
- if err != nil {
- return nil, 0, err
- }
- // 写入缓存
- cacheData := struct {
- Posts []*Post
- Total int64
- }{
- Posts: posts,
- Total: total,
- }
- if data, err := json.Marshal(cacheData); err == nil {
- m.redis.Set(ctx, key, data, 5*time.Minute)
- }
- return posts, total, nil
- }
|