comment_model.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. package model
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "time"
  7. "github.com/go-redis/redis/v8"
  8. "gorm.io/gorm"
  9. )
  10. const (
  11. prefixCommentListKey = "comment:list:%d:%d:%d" // postId:page:pageSize
  12. prefixCommentKey = "comment:%d" // commentId
  13. prefixReplyListKey = "reply:list:%d" // commentId
  14. prefixCommentUpvoteKey = "comment:upvote:%d:%d" // userId:commentId
  15. )
  16. // PComment 评论
  17. type Comment struct {
  18. *Model
  19. PostId int64 `json:"post_id" gorm:"post_id"` // POST ID
  20. UserId int64 `json:"user_id" gorm:"user_id"` // 用户ID
  21. Ip string `json:"ip" gorm:"ip"` // IP地址
  22. IpLoc string `json:"ip_loc" gorm:"ip_loc"` // IP城市地址
  23. Content string `json:"content" gorm:"content"` // 评论内容
  24. Image string `json:"image" gorm:"image"` // 评论图片
  25. WithUserIds string `json:"with_user_ids" gorm:"with_user_ids"` // 被艾特的用户
  26. IsEssence int8 `json:"is_essence" gorm:"is_essence"` // 是否精选
  27. ReplyCount int64 `json:"reply_count" gorm:"reply_count"` // 回复数
  28. ThumbsUpCount int64 `json:"thumbs_up_count" gorm:"thumbs_up_count"` // 点赞数
  29. }
  30. // TableName 表名称
  31. func (*Comment) TableName() string {
  32. return "p_comment"
  33. }
  34. type CommentModel struct {
  35. conn *gorm.DB
  36. redis *redis.Client
  37. }
  38. func NewCommentModel(conn *gorm.DB, rdb *redis.Client) *CommentModel {
  39. return &CommentModel{
  40. conn: conn,
  41. redis: rdb,
  42. }
  43. }
  44. // GetCommentList 获取评论列表(带缓存)
  45. func (m *CommentModel) GetCommentList(ctx context.Context, postId int64, page, pageSize int) ([]*Comment, error) {
  46. // 尝试从缓存获取
  47. key := fmt.Sprintf(prefixCommentListKey, postId, page, pageSize)
  48. data, err := m.redis.Get(ctx, key).Bytes()
  49. if err == nil {
  50. var comments []*Comment
  51. if err := json.Unmarshal(data, &comments); err == nil {
  52. return comments, nil
  53. }
  54. }
  55. // 从数据库获取
  56. var comments []*Comment
  57. offset := (page - 1) * pageSize
  58. err = m.conn.WithContext(ctx).
  59. Where("post_id = ? AND parent_id = 0", postId).
  60. Order("created_on DESC").
  61. Offset(offset).
  62. Limit(pageSize).
  63. Find(&comments).Error
  64. if err != nil {
  65. return nil, err
  66. }
  67. // 写入缓存
  68. if data, err := json.Marshal(comments); err == nil {
  69. m.redis.Set(ctx, key, data, 5*time.Minute) // 评论列表缓存时间较短
  70. }
  71. return comments, nil
  72. }
  73. // GetLatestReplies 获取最新的N条回复(带缓存)
  74. func (m *CommentModel) GetLatestReplies(ctx context.Context, commentId int64, limit int) ([]*Comment, error) {
  75. // 尝试从缓存获取
  76. key := fmt.Sprintf(prefixReplyListKey, commentId)
  77. data, err := m.redis.Get(ctx, key).Bytes()
  78. if err == nil {
  79. var replies []*Comment
  80. if err := json.Unmarshal(data, &replies); err == nil {
  81. return replies, nil
  82. }
  83. }
  84. // 从数据库获取
  85. var replies []*Comment
  86. err = m.conn.WithContext(ctx).
  87. Where("parent_id = ?", commentId).
  88. Order("created_on DESC").
  89. Limit(limit).
  90. Find(&replies).Error
  91. if err != nil {
  92. return nil, err
  93. }
  94. // 写入缓存
  95. if data, err := json.Marshal(replies); err == nil {
  96. m.redis.Set(ctx, key, data, 5*time.Minute)
  97. }
  98. return replies, nil
  99. }
  100. // IsCommentUpvotedByUser 检查用户是否点赞了评论/回复(带缓存)
  101. func (m *CommentModel) IsCommentUpvotedByUser(ctx context.Context, userId, commentId int64) (bool, error) {
  102. // 尝试从缓存获取
  103. key := fmt.Sprintf(prefixCommentUpvoteKey, userId, commentId)
  104. exists, err := m.redis.Exists(ctx, key).Result()
  105. if err == nil && exists == 1 {
  106. return m.redis.Get(ctx, key).Bool()
  107. }
  108. // 从数据库获取
  109. var count int64
  110. err = m.conn.WithContext(ctx).Model(&CommentUpvote{}).
  111. Where("user_id = ? AND comment_id = ? AND is_del = 0", userId, commentId).
  112. Count(&count).Error
  113. if err != nil {
  114. return false, err
  115. }
  116. // 写入缓存
  117. isUpvoted := count > 0
  118. m.redis.Set(ctx, key, isUpvoted, cacheExpiry)
  119. return isUpvoted, nil
  120. }
  121. // GetReplyList 获取回复列表(带缓存)
  122. func (m *CommentModel) GetReplyList(ctx context.Context, commentId int64, page, pageSize int) ([]*Comment, error) {
  123. // 尝试从缓存获取
  124. key := fmt.Sprintf(prefixReplyListKey+":%d:%d", commentId, page, pageSize)
  125. data, err := m.redis.Get(ctx, key).Bytes()
  126. if err == nil {
  127. var replies []*Comment
  128. if err := json.Unmarshal(data, &replies); err == nil {
  129. return replies, nil
  130. }
  131. }
  132. // 从数据库获取
  133. var replies []*Comment
  134. offset := (page - 1) * pageSize
  135. err = m.conn.WithContext(ctx).
  136. Where("parent_id = ?", commentId).
  137. Order("created_on DESC").
  138. Offset(offset).
  139. Limit(pageSize).
  140. Find(&replies).Error
  141. if err != nil {
  142. return nil, err
  143. }
  144. // 写入缓存
  145. if data, err := json.Marshal(replies); err == nil {
  146. m.redis.Set(ctx, key, data, 5*time.Minute) // 回复列表缓存时间较短
  147. }
  148. return replies, nil
  149. }
  150. // Create 创建评论
  151. func (m *CommentModel) Create(ctx context.Context, comment *Comment) error {
  152. err := m.conn.WithContext(ctx).Create(comment).Error
  153. if err != nil {
  154. return err
  155. }
  156. // 删除相关缓存
  157. m.ClearCommentListCache(ctx, comment.PostId)
  158. return nil
  159. }
  160. // ClearCommentListCache 清除评论列表缓存
  161. func (m *CommentModel) ClearCommentListCache(ctx context.Context, postId int64) error {
  162. // 清除评论列表缓存
  163. pattern := fmt.Sprintf("comment:list:%d*", postId)
  164. keys, err := m.redis.Keys(ctx, pattern).Result()
  165. if err != nil {
  166. return err
  167. }
  168. if len(keys) > 0 {
  169. m.redis.Del(ctx, keys...)
  170. }
  171. // 清除回复列表缓存
  172. pattern = fmt.Sprintf(prefixReplyListKey+"*", postId)
  173. keys, err = m.redis.Keys(ctx, pattern).Result()
  174. if err != nil {
  175. return err
  176. }
  177. if len(keys) > 0 {
  178. m.redis.Del(ctx, keys...)
  179. }
  180. return nil
  181. }
  182. // IncrementUpvoteCount 增加点赞数
  183. func (m *CommentModel) IncrementUpvoteCount(ctx context.Context, commentId int64, increment int) error {
  184. err := m.conn.WithContext(ctx).Model(&Comment{}).
  185. Where("id = ?", commentId).
  186. UpdateColumn("thumbs_up_count", gorm.Expr("thumbs_up_count + ?", increment)).Error
  187. if err != nil {
  188. return err
  189. }
  190. // 删除评论缓存
  191. key := fmt.Sprintf(prefixCommentKey, commentId)
  192. m.redis.Del(ctx, key)
  193. return nil
  194. }
  195. // CreateCommentUpvote 创建评论点赞记录
  196. func (m *CommentModel) CreateCommentUpvote(ctx context.Context, upvote *CommentUpvote) error {
  197. err := m.conn.WithContext(ctx).Create(upvote).Error
  198. if err != nil {
  199. return err
  200. }
  201. // 删除点赞状态缓存
  202. key := fmt.Sprintf(prefixCommentUpvoteKey, upvote.UserId, upvote.CommentId)
  203. m.redis.Del(ctx, key)
  204. return nil
  205. }
  206. // DeleteCommentUpvote 删除评论点赞记录
  207. func (m *CommentModel) DeleteCommentUpvote(ctx context.Context, userId, commentId int64) error {
  208. err := m.conn.WithContext(ctx).Model(&CommentUpvote{}).
  209. Where("user_id = ? AND comment_id = ?", userId, commentId).
  210. Update("is_del", 1).Error
  211. if err != nil {
  212. return err
  213. }
  214. // 删除点赞状态缓存
  215. key := fmt.Sprintf(prefixCommentUpvoteKey, userId, commentId)
  216. m.redis.Del(ctx, key)
  217. return nil
  218. }
  219. // 获取评论
  220. func (m *CommentModel) GetComment(ctx context.Context, commentId int64) (*Comment, error) {
  221. var comment Comment
  222. err := m.conn.WithContext(ctx).Where("id = ?", commentId).First(&comment).Error
  223. if err != nil {
  224. return nil, err
  225. }
  226. return &comment, nil
  227. }
  228. // IncrementReplyCount 增加回复数
  229. func (m *CommentModel) IncrementReplyCount(ctx context.Context, commentId int64, increment int) error {
  230. err := m.conn.WithContext(ctx).Model(&Comment{}).
  231. Where("id = ?", commentId).
  232. UpdateColumn("reply_count", gorm.Expr("reply_count + ?", increment)).Error
  233. if err != nil {
  234. return err
  235. }
  236. // 删除评论缓存
  237. key := fmt.Sprintf(prefixCommentKey, commentId)
  238. m.redis.Del(ctx, key)
  239. return nil
  240. }
  241. // Delete 删除评论
  242. func (m *CommentModel) Delete(ctx context.Context, commentId int64) error {
  243. return m.conn.WithContext(ctx).Model(&Comment{}).
  244. Where("id = ?", commentId).
  245. Updates(map[string]interface{}{
  246. "is_del": 1,
  247. "deleted_on": time.Now().Unix(),
  248. "modified_on": time.Now().Unix(),
  249. }).Error
  250. }