Browse Source

帖子相关

huangguangrong 9 months ago
parent
commit
f2fba44925
39 changed files with 4192 additions and 34 deletions
  1. 5 0
      etc/slowwild.yaml
  2. 3 1
      go.mod
  3. 22 0
      go.sum
  4. 5 0
      internal/config/config.go
  5. 13 0
      internal/errorx/errcode.go
  6. 107 0
      internal/logic/commentdeletelogic.go
  7. 208 0
      internal/logic/createpostlogic.go
  8. 187 0
      internal/logic/getpostcommentlistlogic.go
  9. 181 0
      internal/logic/getpostlistlogic.go
  10. 162 0
      internal/logic/getpostlogic.go
  11. 128 0
      internal/logic/getreplylistlogic.go
  12. 94 0
      internal/logic/gettaglistlogic.go
  13. 83 0
      internal/logic/gettaglogic.go
  14. 135 0
      internal/logic/getuserpostcollectionlistlogic.go
  15. 134 0
      internal/logic/getuserpostlikelistlogic.go
  16. 134 0
      internal/logic/getuserpostlistlogic.go
  17. 111 0
      internal/logic/postcollectionlogic.go
  18. 105 0
      internal/logic/postcommentlogic.go
  19. 109 0
      internal/logic/postcommentupvotelogic.go
  20. 73 0
      internal/logic/postdeletelogic.go
  21. 127 0
      internal/logic/postreplylogic.go
  22. 97 0
      internal/logic/postsharelogic.go
  23. 115 0
      internal/logic/postupvotelogic.go
  24. 287 0
      internal/model/comment_model.go
  25. 14 0
      internal/model/comment_upvote_model.go
  26. 78 0
      internal/model/message_model.go
  27. 130 0
      internal/model/post_action_model.go
  28. 20 0
      internal/model/post_content_model.go
  29. 567 0
      internal/model/post_model.go
  30. 89 0
      internal/model/reply_model.go
  31. 193 0
      internal/model/tag_model.go
  32. 13 0
      internal/model/tag_with_post_model.go
  33. 2 2
      internal/model/user_follow.go
  34. 17 0
      internal/model/user_follow_tag_model.go
  35. 47 2
      internal/model/user_model.go
  36. 110 1
      internal/server/slowwildserverserver.go
  37. 43 6
      internal/svc/servicecontext.go
  38. 34 0
      internal/utils/html.go
  39. 210 22
      slowwildserverclient/slowwildserver.go

+ 5 - 0
etc/slowwild.yaml

@@ -12,3 +12,8 @@ Etcd:
 
 
 DB:
 DB:
   DataSource: root:easy-chat@tcp(121.11.99.220:13306)/slow_wild?charset=utf8mb4&parseTime=True&loc=Local
   DataSource: root:easy-chat@tcp(121.11.99.220:13306)/slow_wild?charset=utf8mb4&parseTime=True&loc=Local
+
+RedisConf:
+  Host: 121.11.99.220:16379
+  Password: ""
+  DB: 0

+ 3 - 1
go.mod

@@ -3,7 +3,7 @@ module slowwild
 go 1.22.0
 go 1.22.0
 
 
 require (
 require (
-	git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.0.7
+	git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.1.6
 	github.com/zeromicro/go-zero v1.8.0
 	github.com/zeromicro/go-zero v1.8.0
 	google.golang.org/grpc v1.70.0
 	google.golang.org/grpc v1.70.0
 )
 )
@@ -24,6 +24,7 @@ require (
 	github.com/go-openapi/jsonpointer v0.19.6 // indirect
 	github.com/go-openapi/jsonpointer v0.19.6 // indirect
 	github.com/go-openapi/jsonreference v0.20.2 // indirect
 	github.com/go-openapi/jsonreference v0.20.2 // indirect
 	github.com/go-openapi/swag v0.22.4 // indirect
 	github.com/go-openapi/swag v0.22.4 // indirect
+	github.com/go-redis/redis/v8 v8.11.5
 	github.com/go-sql-driver/mysql v1.8.1 // indirect
 	github.com/go-sql-driver/mysql v1.8.1 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang-jwt/jwt/v4 v4.5.1
 	github.com/golang-jwt/jwt/v4 v4.5.1
@@ -53,6 +54,7 @@ require (
 	github.com/prometheus/procfs v0.15.1 // indirect
 	github.com/prometheus/procfs v0.15.1 // indirect
 	github.com/redis/go-redis/v9 v9.7.0 // indirect
 	github.com/redis/go-redis/v9 v9.7.0 // indirect
 	github.com/spaolacci/murmur3 v1.1.0 // indirect
 	github.com/spaolacci/murmur3 v1.1.0 // indirect
+	github.com/spf13/cast v1.7.1
 	go.etcd.io/etcd/api/v3 v3.5.15 // indirect
 	go.etcd.io/etcd/api/v3 v3.5.15 // indirect
 	go.etcd.io/etcd/client/pkg/v3 v3.5.15 // indirect
 	go.etcd.io/etcd/client/pkg/v3 v3.5.15 // indirect
 	go.etcd.io/etcd/client/v3 v3.5.15 // indirect
 	go.etcd.io/etcd/client/v3 v3.5.15 // indirect

+ 22 - 0
go.sum

@@ -10,6 +10,24 @@ git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.0.6 h1:5dwVwAFgpj83f3xcR1c
 git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.0.6/go.mod h1:v8AzHCelFBbIkoY+gR4WIEuc6mG5okJ12IXbjvGJWHk=
 git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.0.6/go.mod h1:v8AzHCelFBbIkoY+gR4WIEuc6mG5okJ12IXbjvGJWHk=
 git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.0.7 h1:FvDzmIojdaAYzMOOq880nmSd1ZVLRGHYVSTE91gX3+E=
 git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.0.7 h1:FvDzmIojdaAYzMOOq880nmSd1ZVLRGHYVSTE91gX3+E=
 git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.0.7/go.mod h1:v8AzHCelFBbIkoY+gR4WIEuc6mG5okJ12IXbjvGJWHk=
 git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.0.7/go.mod h1:v8AzHCelFBbIkoY+gR4WIEuc6mG5okJ12IXbjvGJWHk=
+git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.0.8 h1:zcT09BfbwkF99vSDONUv1nvTUl0/jngBia43U/M1Wag=
+git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.0.8/go.mod h1:v8AzHCelFBbIkoY+gR4WIEuc6mG5okJ12IXbjvGJWHk=
+git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.0.9 h1:Kd/W4EYfhHUTuRt7ezyGXDxjElAbT3IxfoUf5GkpvjQ=
+git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.0.9/go.mod h1:v8AzHCelFBbIkoY+gR4WIEuc6mG5okJ12IXbjvGJWHk=
+git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.1.0 h1:hS0ZCTkHLc21tNrg1gtDQBIWg6UhjtbPIt3a81K46BU=
+git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.1.0/go.mod h1:v8AzHCelFBbIkoY+gR4WIEuc6mG5okJ12IXbjvGJWHk=
+git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.1.1 h1:AUi6OUCAhWQE7tD1MU9ICa92MgoxqYAVwJYAYLxnZ2s=
+git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.1.1/go.mod h1:v8AzHCelFBbIkoY+gR4WIEuc6mG5okJ12IXbjvGJWHk=
+git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.1.2 h1:ma3UX4X3s15JOOL89AcNz3o7wqw/WdEoz14yuAjQ/Bs=
+git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.1.2/go.mod h1:v8AzHCelFBbIkoY+gR4WIEuc6mG5okJ12IXbjvGJWHk=
+git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.1.3 h1:371fgzGGYkAMmGnVME8bSjELpDqDIApFSLdz9I0srVg=
+git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.1.3/go.mod h1:v8AzHCelFBbIkoY+gR4WIEuc6mG5okJ12IXbjvGJWHk=
+git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.1.4 h1:aVOMpIyr0TOiSyRqMeIfINptZRD9iaKQltepdkD++N8=
+git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.1.4/go.mod h1:v8AzHCelFBbIkoY+gR4WIEuc6mG5okJ12IXbjvGJWHk=
+git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.1.5 h1:sk2kkLJQoO8UUKP0qQ33s/xvo7kkWDdcNBjTWF1A/QE=
+git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.1.5/go.mod h1:v8AzHCelFBbIkoY+gR4WIEuc6mG5okJ12IXbjvGJWHk=
+git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.1.6 h1:AZtJwYwmLnq/pDYtPd4vm2TU1UGFZHPJevhQEvjcnbM=
+git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.1.6/go.mod h1:v8AzHCelFBbIkoY+gR4WIEuc6mG5okJ12IXbjvGJWHk=
 github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE=
 github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE=
 github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
 github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
 github.com/alicebob/miniredis/v2 v2.34.0 h1:mBFWMaJSNL9RwdGRyEDoAAv8OQc5UlEhLDQggTglU/0=
 github.com/alicebob/miniredis/v2 v2.34.0 h1:mBFWMaJSNL9RwdGRyEDoAAv8OQc5UlEhLDQggTglU/0=
@@ -53,6 +71,8 @@ github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En
 github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
 github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
 github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
 github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
 github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
 github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
+github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
+github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
 github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
 github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
 github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
 github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
 github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
 github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
@@ -149,6 +169,8 @@ github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjR
 github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
 github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
 github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
 github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
 github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
+github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=

+ 5 - 0
internal/config/config.go

@@ -7,4 +7,9 @@ type Config struct {
 	DB struct {
 	DB struct {
 		DataSource string
 		DataSource string
 	}
 	}
+	RedisConf struct {
+		Host     string
+		Password string
+		DB       int
+	}
 }
 }

+ 13 - 0
internal/errorx/errcode.go

@@ -29,4 +29,17 @@ var (
 	ErrUnFollowFailed  = NewCodeError(10009, "取消关注失败")
 	ErrUnFollowFailed  = NewCodeError(10009, "取消关注失败")
 	ErrAlreadyFollowed = NewCodeError(10010, "已经关注该用户")
 	ErrAlreadyFollowed = NewCodeError(10010, "已经关注该用户")
 	ErrFollowFailed    = NewCodeError(10011, "关注用户失败")
 	ErrFollowFailed    = NewCodeError(10011, "关注用户失败")
+
+	// 帖子相关错误码 (20000-20099)
+	ErrInvalidPostType     = NewCodeError(20001, "帖子类型不合法")
+	ErrCreatePostFailed    = NewCodeError(20002, "发布帖子失败")
+	ErrPostQueryFailed     = NewCodeError(20003, "查询帖子失败")
+	ErrCommentQueryFailed  = NewCodeError(20004, "查询评论列表失败")
+	ErrInvalidOperation    = NewCodeError(20005, "无效的操作")
+	ErrCommentFailed       = NewCodeError(20006, "发布评论失败")
+	ErrCommentUpvoteFailed = NewCodeError(20007, "评论点赞失败")
+	ErrAlreadyShared       = NewCodeError(20011, "已经分享过该帖子")
+
+	// 话题相关错误码 (30000-30099)
+	ErrTagQueryFailed = NewCodeError(30001, "查询话题失败")
 )
 )

+ 107 - 0
internal/logic/commentdeletelogic.go

@@ -0,0 +1,107 @@
+package logic
+
+import (
+	"context"
+	"slowwild/internal/errorx"
+	"slowwild/internal/model"
+	"slowwild/internal/svc"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+
+	"github.com/zeromicro/go-zero/core/logx"
+	"gorm.io/gorm"
+)
+
+type CommentDeleteLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewCommentDeleteLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CommentDeleteLogic {
+	return &CommentDeleteLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 删除回复/评论
+func (l *CommentDeleteLogic) CommentDelete(in *slowwildserver.CommentDeleteReq) (*slowwildserver.CommentDeleteRsp, error) {
+	if in.UserId <= 0 || in.CommentId <= 0 {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	// 开启事务
+	err := l.svcCtx.UserModel.Transaction(l.ctx, func(tx *gorm.DB) error {
+		postModel := model.NewPostModel(tx, l.svcCtx.Redis)
+		commentModel := model.NewCommentModel(tx, l.svcCtx.Redis)
+		replyModel := model.NewReplyModel(tx, l.svcCtx.Redis)
+
+		if in.CommentType == 0 { // 删除评论
+			// 获取评论信息
+			comment, err := commentModel.GetComment(l.ctx, in.CommentId)
+			if err != nil {
+				return err
+			}
+
+			// 检查是否是自己的评论
+			if comment.UserId != in.UserId {
+				return errorx.NewCodeError(20012, "无权删除该评论")
+			}
+
+			// 删除评论
+			if err := commentModel.Delete(l.ctx, in.CommentId); err != nil {
+				return err
+			}
+
+			// 减少帖子评论数(包括评论的回复数)
+			totalCount := comment.ReplyCount + 1 // 评论本身+回复数
+			if err := postModel.IncrementCommentCount(l.ctx, comment.PostId, int(-totalCount)); err != nil {
+				return err
+			}
+
+			// 删除评论相关缓存
+			commentModel.ClearCommentListCache(l.ctx, comment.PostId)
+
+		} else { // 删除回复
+			// 获取回复信息
+			reply, err := replyModel.GetReply(l.ctx, in.CommentId)
+			if err != nil {
+				return err
+			}
+
+			// 检查是否是自己的回复
+			if reply.UserId != in.UserId {
+				return errorx.NewCodeError(20012, "无权删除该回复")
+			}
+
+			// 删除回复
+			if err := replyModel.Delete(l.ctx, in.CommentId); err != nil {
+				return err
+			}
+
+			// 减少评论的回复数
+			if err := commentModel.IncrementReplyCount(l.ctx, reply.CommentId, -1); err != nil {
+				return err
+			}
+
+			// 减少帖子评论数
+			if err := postModel.IncrementCommentCount(l.ctx, reply.PostId, -1); err != nil {
+				return err
+			}
+
+			// 删除回复相关缓存
+			replyModel.ClearReplyListCache(l.ctx, reply.CommentId)
+		}
+
+		return nil
+	})
+
+	if err != nil {
+		l.Logger.Errorf("删除评论/回复失败: %v", err)
+		return nil, err
+	}
+
+	return &slowwildserver.CommentDeleteRsp{}, nil
+}

+ 208 - 0
internal/logic/createpostlogic.go

@@ -0,0 +1,208 @@
+package logic
+
+import (
+	"context"
+	"slowwild/internal/svc"
+	"strings"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+
+	"slowwild/internal/errorx"
+	"slowwild/internal/model"
+	"time"
+
+	"slowwild/internal/utils"
+
+	"github.com/spf13/cast"
+	"github.com/zeromicro/go-zero/core/logx"
+	"gorm.io/gorm"
+)
+
+type CreatePostLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewCreatePostLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreatePostLogic {
+	return &CreatePostLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 发布帖子
+func (l *CreatePostLogic) CreatePost(in *slowwildserver.CreatePostReq) (*slowwildserver.CreatePostRsp, error) {
+	if in.UserId <= 0 || in.Content == "" {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	// 检查帖子类型是否合法
+	if in.Type != 0 && in.Type != 1 {
+		return nil, errorx.NewCodeError(20001, "帖子类型不合法")
+	}
+
+	// 开启事务
+	err := l.svcCtx.UserModel.Transaction(l.ctx, func(tx *gorm.DB) error {
+		postModel := model.NewPostModel(tx, l.svcCtx.Redis)
+		postContentModel := model.NewPostContentModel(tx)
+		userModel := model.NewUserModel(tx)
+		tagModel := model.NewTagModel(tx, l.svcCtx.Redis)
+		messageModel := model.NewMessageModel(tx)
+
+		// 提取话题名称
+		var tagNames []string
+		for _, tag := range in.Tags {
+			tagNames = append(tagNames, tag.Name)
+		}
+
+		// 检查话题是否存在
+		existingTags, err := tagModel.FindByNames(l.ctx, tagNames)
+		if err != nil {
+			return err
+		}
+
+		// 找出不存在的话题名称
+		existingTagMap := make(map[string]bool)
+		for _, tag := range existingTags {
+			existingTagMap[tag.Name] = true
+		}
+
+		// 需要新建的话题
+		var newTags []*model.Tag
+		for _, tag := range in.Tags {
+			if !existingTagMap[tag.Name] {
+				newTags = append(newTags, &model.Tag{
+					Model: &model.Model{
+						CreatedOn:  time.Now().Unix(),
+						ModifiedOn: time.Now().Unix(),
+					},
+					UserId: in.UserId,
+					Name:   tag.Name,
+					HotNum: 0,
+				})
+			}
+		}
+
+		// 创建新话题
+		if len(newTags) > 0 {
+			if err = tagModel.BatchCreate(l.ctx, newTags); err != nil {
+				return err
+			}
+		}
+
+		// 获取所有话题的ID
+		var tagIds []int64
+		for _, tag := range existingTags {
+			tagIds = append(tagIds, tag.ID)
+		}
+		for _, tag := range newTags {
+			tagIds = append(tagIds, tag.ID)
+		}
+
+		// 判断@用户是否重复,重复只发一次
+		existUserMap := make(map[string]bool)
+		if len(in.AtUserIds) > 0 {
+			for _, id := range in.AtUserIds {
+				existUserMap[cast.ToString(id)] = true
+			}
+		}
+		atUserIds := make([]string, 0, len(existUserMap))
+		if len(existUserMap) > 0 {
+			for id := range existUserMap {
+				atUserIds = append(atUserIds, id)
+			}
+		}
+
+		// 创建帖子
+		post := &model.Post{
+			Model: &model.Model{
+				CreatedOn:  time.Now().Unix(),
+				ModifiedOn: time.Now().Unix(),
+			},
+			UserId:          in.UserId,
+			PostType:        in.Type,
+			Visibility:      int8(in.Visibility),
+			Tags:            strings.Join(tagNames, ","),
+			Ip:              in.Ip,
+			IpLoc:           in.IpLoc,
+			WithUserIds:     strings.Join(atUserIds, ","),
+			CommentCount:    0,
+			CollectionCount: 0,
+			UpvoteCount:     0,
+			ShareCount:      0,
+			IsTop:           0,
+			IsEssence:       0,
+			HotNum:          0,
+		}
+
+		// 创建帖子基本信息
+		if err := postModel.Create(l.ctx, post); err != nil {
+			return err
+		}
+
+		// 处理内容,生成摘要
+		plainContent := utils.StripHTML(in.Content)
+		contentSummary := utils.TruncateText(plainContent, 1000)
+
+		// 创建帖子内容
+		postContent := &model.PostContent{
+			Model: &model.Model{
+				CreatedOn:  time.Now().Unix(),
+				ModifiedOn: time.Now().Unix(),
+			},
+			PostId:         post.ID,
+			UserId:         in.UserId,
+			Title:          in.Title,
+			Content:        in.Content,     // 保存原始内容
+			ContentSummary: contentSummary, // 保存处理后的摘要
+			PostVideoCover: in.VideoCover,
+			PostCovers:     strings.Join(in.Images, ","),
+			PostVideoUrl:   in.VideoUrl,
+			Sort:           0,
+		}
+
+		if err := postContentModel.Create(l.ctx, postContent); err != nil {
+			return err
+		}
+
+		// 创建帖子和话题的关联关系
+		if err := tagModel.CreateTagWithPost(l.ctx, post.ID, tagIds); err != nil {
+			return err
+		}
+
+		// 检查被艾特的用户是否存在
+		if len(atUserIds) > 0 {
+			atUsers, err := userModel.FindByIds(l.ctx, in.AtUserIds)
+			if err != nil {
+				return err
+			}
+
+			// 发送站内信给被艾特的用户
+			for _, user := range atUsers {
+				err = messageModel.SendNotification(l.ctx, in.UserId, user.ID, 1, "你被艾特了", "你被艾特在一条动态中", post.ID, 0, 0)
+				if err != nil {
+					return err
+				}
+			}
+		}
+
+		// 增加用户的帖子数
+		if err := userModel.IncrementTweetCount(l.ctx, in.UserId); err != nil {
+			return err
+		}
+
+		// 删除缓存
+		postModel.ClearListCache(l.ctx)
+
+		return nil
+	})
+
+	if err != nil {
+		l.Logger.Errorf("发布帖子失败: %v", err)
+		return nil, errorx.NewCodeError(20002, "发布帖子失败")
+	}
+
+	return &slowwildserver.CreatePostRsp{}, nil
+}

+ 187 - 0
internal/logic/getpostcommentlistlogic.go

@@ -0,0 +1,187 @@
+package logic
+
+import (
+	"context"
+	"slowwild/internal/errorx"
+	"slowwild/internal/model"
+	"slowwild/internal/svc"
+	"strconv"
+	"strings"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetPostCommentListLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewGetPostCommentListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetPostCommentListLogic {
+	return &GetPostCommentListLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 获取评论列表
+func (l *GetPostCommentListLogic) GetPostCommentList(in *slowwildserver.GetPostCommentListReq) (*slowwildserver.GetPostCommentListRsp, error) {
+	if in.PostId <= 0 || in.Page <= 0 || in.PageSize <= 0 {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	// 获取评论列表
+	comments, err := l.svcCtx.CommentModel.GetCommentList(l.ctx, in.PostId, int(in.Page), int(in.PageSize))
+	if err != nil {
+		l.Logger.Errorf("获取评论列表失败: %v", err)
+		return nil, errorx.ErrCommentQueryFailed
+	}
+
+	// 收集所有用户ID(评论用户和被@用户)
+	userIds := make(map[int64]bool)
+	for _, comment := range comments {
+		userIds[comment.UserId] = true
+		if comment.WithUserIds != "" {
+			for _, idStr := range strings.Split(comment.WithUserIds, ",") {
+				if userId, err := strconv.ParseInt(idStr, 10, 64); err == nil {
+					userIds[userId] = true
+				}
+			}
+		}
+	}
+
+	// 批量获取用户信息
+	var userIdList []int64
+	for userId := range userIds {
+		userIdList = append(userIdList, userId)
+	}
+	users, err := l.svcCtx.UserModel.FindByIds(l.ctx, userIdList)
+	if err != nil {
+		l.Logger.Errorf("批量获取用户信息失败: %v", err)
+		return nil, errorx.ErrUserQueryFailed
+	}
+
+	// 构建用户信息map
+	userMap := make(map[int64]*model.User)
+	for _, user := range users {
+		userMap[user.ID] = user
+	}
+
+	// 构建响应
+	resp := &slowwildserver.GetPostCommentListRsp{
+		List: make([]*slowwildserver.CommentItem, 0, len(comments)),
+	}
+
+	for _, comment := range comments {
+		// 获取评论用户信息
+		user := userMap[comment.UserId]
+		if user == nil {
+			continue
+		}
+
+		// 获取@用户信息
+		var withUsers []*slowwildserver.UserInfo
+		if comment.WithUserIds != "" {
+			for _, idStr := range strings.Split(comment.WithUserIds, ",") {
+				if userId, err := strconv.ParseInt(idStr, 10, 64); err == nil {
+					if atUser := userMap[userId]; atUser != nil {
+						withUsers = append(withUsers, &slowwildserver.UserInfo{
+							Id:       atUser.ID,
+							Nickname: atUser.Nickname,
+							Avatar:   atUser.Avatar,
+							Sex:      int32(atUser.Sex),
+						})
+					}
+				}
+			}
+		}
+
+		// 获取最新的3条回复
+		replies, err := l.svcCtx.CommentModel.GetLatestReplies(l.ctx, comment.ID, 3)
+		if err != nil {
+			l.Logger.Errorf("获取评论回复失败: %v", err)
+			continue
+		}
+
+		// 获取评论点赞状态
+		isUpvoted, err := l.svcCtx.CommentModel.IsCommentUpvotedByUser(l.ctx, in.UserId, comment.ID)
+		if err != nil {
+			l.Logger.Errorf("获取评论点赞状态失败: %v", err)
+			continue
+		}
+
+		// 构建回复列表
+		var replyList []*slowwildserver.RepliesItem
+		for _, reply := range replies {
+			replyUser := userMap[reply.UserId]
+			if replyUser == nil {
+				continue
+			}
+
+			// 获取回复中的@用户信息
+			var replyWithUsers []*slowwildserver.UserInfo
+			if reply.WithUserIds != "" {
+				for _, idStr := range strings.Split(reply.WithUserIds, ",") {
+					if userId, err := strconv.ParseInt(idStr, 10, 64); err == nil {
+						if atUser := userMap[userId]; atUser != nil {
+							replyWithUsers = append(replyWithUsers, &slowwildserver.UserInfo{
+								Id:       atUser.ID,
+								Nickname: atUser.Nickname,
+								Avatar:   atUser.Avatar,
+								Sex:      int32(atUser.Sex),
+							})
+						}
+					}
+				}
+			}
+
+			// 获取回复点赞状态
+			isReplyUpvoted, err := l.svcCtx.CommentModel.IsCommentUpvotedByUser(l.ctx, in.UserId, reply.ID)
+			if err != nil {
+				l.Logger.Errorf("获取回复点赞状态失败: %v", err)
+				continue
+			}
+
+			replyList = append(replyList, &slowwildserver.RepliesItem{
+				Id: reply.ID,
+				User: &slowwildserver.UserInfo{
+					Id:       replyUser.ID,
+					Nickname: replyUser.Nickname,
+					Avatar:   replyUser.Avatar,
+					Sex:      int32(replyUser.Sex),
+				},
+				Content:     reply.Content,
+				WithUser:    replyWithUsers,
+				UpvoteCount: reply.ThumbsUpCount,
+				IpLoc:       reply.IpLoc,
+				CreatedOn:   reply.CreatedOn,
+				IsMine:      in.UserId == reply.UserId,
+				IsLiked:     isReplyUpvoted,
+			})
+		}
+
+		resp.List = append(resp.List, &slowwildserver.CommentItem{
+			Id: comment.ID,
+			User: &slowwildserver.UserInfo{
+				Id:       user.ID,
+				Nickname: user.Nickname,
+				Avatar:   user.Avatar,
+				Sex:      int32(user.Sex),
+			},
+			Content:     comment.Content,
+			WithUser:    withUsers,
+			ReplyCount:  comment.ReplyCount,
+			UpvoteCount: comment.ThumbsUpCount,
+			IpLoc:       comment.IpLoc,
+			CreatedOn:   comment.CreatedOn,
+			IsMine:      in.UserId == comment.UserId,
+			ReplyItem:   replyList,
+			IsLiked:     isUpvoted,
+		})
+	}
+
+	return resp, nil
+}

+ 181 - 0
internal/logic/getpostlistlogic.go

@@ -0,0 +1,181 @@
+package logic
+
+import (
+	"context"
+	"slowwild/internal/errorx"
+	"slowwild/internal/model"
+	"slowwild/internal/svc"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+
+	"strconv"
+	"strings"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetPostListLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewGetPostListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetPostListLogic {
+	return &GetPostListLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 获取帖子列表
+func (l *GetPostListLogic) GetPostList(in *slowwildserver.GetPostListReq) (*slowwildserver.GetPostListRsp, error) {
+	if in.Page <= 0 || in.PageSize <= 0 {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	posts, err := l.svcCtx.PostModel.GetPostList(l.ctx, in)
+	if err != nil {
+		l.Logger.Errorf("获取帖子列表失败: %v", err)
+		return nil, errorx.ErrPostQueryFailed
+	}
+
+	// 构建响应
+	resp := &slowwildserver.GetPostListRsp{
+		List: make([]*slowwildserver.GetPostListItem, 0, len(posts)),
+	}
+
+	// 收集所有用户ID
+	userIds := make([]int64, 0, len(posts))
+	for _, post := range posts {
+		userIds = append(userIds, post.UserId)
+	}
+
+	// 批量获取用户信息
+	users, err := l.svcCtx.UserModel.FindByIds(l.ctx, userIds)
+	if err != nil {
+		l.Logger.Errorf("批量获取用户信息失败: %v", err)
+		return nil, errorx.ErrUserQueryFailed
+	}
+
+	// 构建用户信息map
+	userMap := make(map[int64]*model.User)
+	for _, user := range users {
+		userMap[user.ID] = user
+	}
+
+	// 获取所有话题ID
+	var allTagIds []int64
+	for _, post := range posts {
+		if post.Tags != "" {
+			tagStrs := strings.Split(post.Tags, ",")
+			for _, tagStr := range tagStrs {
+				tagId, err := strconv.ParseInt(tagStr, 10, 64)
+				if err != nil {
+					continue
+				}
+				allTagIds = append(allTagIds, tagId)
+			}
+		}
+	}
+
+	// 批量获取话题信息
+	tags, err := l.svcCtx.TagModel.FindByIds(l.ctx, allTagIds)
+	if err != nil {
+		l.Logger.Errorf("批量获取话题信息失败: %v", err)
+		return nil, errorx.ErrPostQueryFailed
+	}
+
+	// 构建话题信息map
+	tagMap := make(map[int64]*model.Tag)
+	for _, tag := range tags {
+		tagMap[tag.ID] = tag
+	}
+
+	// 收集所有帖子ID
+	postIds := make([]int64, 0, len(posts))
+	for _, post := range posts {
+		postIds = append(postIds, post.ID)
+	}
+
+	// 批量获取帖子内容
+	postContents, err := l.svcCtx.PostModel.GetPostContents(l.ctx, postIds)
+	if err != nil {
+		l.Logger.Errorf("批量获取帖子内容失败: %v", err)
+		return nil, errorx.ErrPostQueryFailed
+	}
+
+	for _, post := range posts {
+		// 查询用户状态
+		isLiked, err := l.svcCtx.UserModel.IsPostLikedByUser(l.ctx, in.UserId, post.ID)
+		if err != nil {
+			l.Logger.Errorf("查询用户点赞状态失败: %v", err)
+			return nil, errorx.ErrPostQueryFailed
+		}
+
+		isCollected, err := l.svcCtx.UserModel.IsPostCollectedByUser(l.ctx, in.UserId, post.ID)
+		if err != nil {
+			l.Logger.Errorf("查询用户收藏状态失败: %v", err)
+			return nil, errorx.ErrPostQueryFailed
+		}
+
+		// 获取话题信息
+		var tagItems []*slowwildserver.TagItem
+		if post.Tags != "" {
+			tagStrs := strings.Split(post.Tags, ",")
+			for _, tagStr := range tagStrs {
+				tagId, err := strconv.ParseInt(tagStr, 10, 64)
+				if err != nil {
+					continue
+				}
+				if tag, ok := tagMap[tagId]; ok {
+					tagItems = append(tagItems, &slowwildserver.TagItem{
+						Id:   tag.ID,
+						Name: tag.Name,
+					})
+				}
+			}
+		}
+
+		// 获取用户信息
+		user := userMap[post.UserId]
+		if user == nil {
+			continue
+		}
+
+		// 获取帖子内容
+		content := postContents[post.ID]
+		if content == nil {
+			continue
+		}
+
+		resp.List = append(resp.List, &slowwildserver.GetPostListItem{
+			Id: post.ID,
+			User: &slowwildserver.UserInfo{
+				Id:       user.ID,
+				Nickname: user.Nickname,
+				Avatar:   user.Avatar,
+				Sex:      int32(user.Sex),
+			},
+			PostType:        post.PostType,
+			Title:           content.Title,
+			ContentSummary:  content.ContentSummary,
+			Images:          strings.Split(content.PostCovers, ","),
+			VideoUrl:        content.PostVideoUrl,
+			VideoCover:      content.PostVideoCover,
+			CommentCount:    post.CommentCount,
+			CollectionCount: post.CollectionCount,
+			UpvoteCount:     post.UpvoteCount,
+			ShareCount:      post.ShareCount,
+			Tags:            tagItems,
+			IpLoc:           post.IpLoc,
+			HotNum:          post.HotNum,
+			CreatedOn:       post.CreatedOn,
+			IsLiked:         isLiked,
+			IsCollected:     isCollected,
+			IsMine:          in.UserId == post.UserId,
+		})
+	}
+
+	return resp, nil
+}

+ 162 - 0
internal/logic/getpostlogic.go

@@ -0,0 +1,162 @@
+package logic
+
+import (
+	"context"
+	"slowwild/internal/errorx"
+	"slowwild/internal/svc"
+	"strconv"
+	"strings"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetPostLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewGetPostLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetPostLogic {
+	return &GetPostLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 获取动态详情
+func (l *GetPostLogic) GetPost(in *slowwildserver.GetPostReq) (*slowwildserver.GetPostRsp, error) {
+	if in.PostId <= 0 {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	// 获取帖子基本信息
+	post, err := l.svcCtx.PostModel.GetPostDetail(l.ctx, in.PostId)
+	if err != nil {
+		l.Logger.Errorf("获取帖子详情失败: %v", err)
+		return nil, errorx.ErrPostQueryFailed
+	}
+
+	// 获取帖子内容
+	content, err := l.svcCtx.PostModel.GetPostContent(l.ctx, in.PostId)
+	if err != nil {
+		l.Logger.Errorf("获取帖子内容失败: %v", err)
+		return nil, errorx.ErrPostQueryFailed
+	}
+
+	// 获取用户信息
+	user, err := l.svcCtx.UserModel.FindOne(l.ctx, post.UserId)
+	if err != nil {
+		l.Logger.Errorf("获取用户信息失败: %v", err)
+		return nil, errorx.ErrUserQueryFailed
+	}
+
+	// 获取话题信息
+	var tagItems []*slowwildserver.TagItem
+	if post.Tags != "" {
+		tagStrs := strings.Split(post.Tags, ",")
+		var tagIds []int64
+		for _, tagStr := range tagStrs {
+			tagId, err := strconv.ParseInt(tagStr, 10, 64)
+			if err != nil {
+				continue
+			}
+			tagIds = append(tagIds, tagId)
+		}
+
+		tags, err := l.svcCtx.TagModel.FindByIds(l.ctx, tagIds)
+		if err != nil {
+			l.Logger.Errorf("获取话题信息失败: %v", err)
+			return nil, errorx.ErrPostQueryFailed
+		}
+
+		for _, tag := range tags {
+			tagItems = append(tagItems, &slowwildserver.TagItem{
+				Id:   tag.ID,
+				Name: tag.Name,
+			})
+		}
+	}
+
+	// 获取用户交互状态
+	isLiked, err := l.svcCtx.UserModel.IsPostLikedByUser(l.ctx, in.UserId, post.ID)
+	if err != nil {
+		l.Logger.Errorf("查询用户点赞状态失败: %v", err)
+		return nil, errorx.ErrPostQueryFailed
+	}
+
+	isCollected, err := l.svcCtx.UserModel.IsPostCollectedByUser(l.ctx, in.UserId, post.ID)
+	if err != nil {
+		l.Logger.Errorf("查询用户收藏状态失败: %v", err)
+		return nil, errorx.ErrPostQueryFailed
+	}
+
+	// 获取@用户信息
+	var atUserInfos []*slowwildserver.UserInfo
+	if post.WithUserIds != "" {
+		// 解析用户ID列表
+		atUserIdStrs := strings.Split(post.WithUserIds, ",")
+		var atUserIds []int64
+		for _, idStr := range atUserIdStrs {
+			userId, err := strconv.ParseInt(idStr, 10, 64)
+			if err != nil {
+				continue
+			}
+			atUserIds = append(atUserIds, userId)
+		}
+
+		// 批量获取用户信息
+		if len(atUserIds) > 0 {
+			atUsers, err := l.svcCtx.UserModel.FindByIds(l.ctx, atUserIds)
+			if err != nil {
+				l.Logger.Errorf("获取@用户信息失败: %v", err)
+				return nil, errorx.ErrUserQueryFailed
+			}
+
+			// 构建用户信息列表
+			for _, atUser := range atUsers {
+				atUserInfos = append(atUserInfos, &slowwildserver.UserInfo{
+					Id:       atUser.ID,
+					Nickname: atUser.Nickname,
+					Avatar:   atUser.Avatar,
+					Sex:      int32(atUser.Sex),
+				})
+			}
+		}
+	}
+
+	// 构建响应
+	resp := &slowwildserver.GetPostRsp{
+		Id: post.ID,
+		User: &slowwildserver.UserInfo{
+			Id:       user.ID,
+			Nickname: user.Nickname,
+			Avatar:   user.Avatar,
+			Sex:      int32(user.Sex),
+		},
+		PostType:        post.PostType,
+		Title:           content.Title,
+		Content:         content.Content,
+		ContentSummary:  content.ContentSummary,
+		Images:          strings.Split(content.PostCovers, ","),
+		VideoUrl:        content.PostVideoUrl,
+		VideoCover:      content.PostVideoCover,
+		CommentCount:    post.CommentCount,
+		CollectionCount: post.CollectionCount,
+		UpvoteCount:     post.UpvoteCount,
+		ShareCount:      post.ShareCount,
+		Tags:            tagItems,
+		IpLoc:           post.IpLoc,
+		HotNum:          post.HotNum,
+		CreatedOn:       post.CreatedOn,
+		IsLiked:         isLiked,
+		IsCollected:     isCollected,
+		IsMine:          in.UserId == post.UserId,
+		LatestRepliedOn: post.LatestRepliedOn,
+		WithUser:        atUserInfos,
+	}
+
+	return resp, nil
+}

+ 128 - 0
internal/logic/getreplylistlogic.go

@@ -0,0 +1,128 @@
+package logic
+
+import (
+	"context"
+	"slowwild/internal/errorx"
+	"slowwild/internal/model"
+	"slowwild/internal/svc"
+	"strconv"
+	"strings"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetReplyListLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewGetReplyListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetReplyListLogic {
+	return &GetReplyListLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 获取回复列表
+func (l *GetReplyListLogic) GetReplyList(in *slowwildserver.GetReplyListReq) (*slowwildserver.GetReplyListRsp, error) {
+	if in.CommentId <= 0 || in.Page <= 0 || in.PageSize <= 0 {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	// 获取回复列表
+	replies, err := l.svcCtx.CommentModel.GetReplyList(l.ctx, in.CommentId, int(in.Page), int(in.PageSize))
+	if err != nil {
+		l.Logger.Errorf("获取回复列表失败: %v", err)
+		return nil, errorx.ErrCommentQueryFailed
+	}
+
+	// 收集所有用户ID(回复用户和被@用户)
+	userIds := make(map[int64]bool)
+	for _, reply := range replies {
+		userIds[reply.UserId] = true
+		if reply.WithUserIds != "" {
+			for _, idStr := range strings.Split(reply.WithUserIds, ",") {
+				if userId, err := strconv.ParseInt(idStr, 10, 64); err == nil {
+					userIds[userId] = true
+				}
+			}
+		}
+	}
+
+	// 批量获取用户信息
+	var userIdList []int64
+	for userId := range userIds {
+		userIdList = append(userIdList, userId)
+	}
+	users, err := l.svcCtx.UserModel.FindByIds(l.ctx, userIdList)
+	if err != nil {
+		l.Logger.Errorf("批量获取用户信息失败: %v", err)
+		return nil, errorx.ErrUserQueryFailed
+	}
+
+	// 构建用户信息map
+	userMap := make(map[int64]*model.User)
+	for _, user := range users {
+		userMap[user.ID] = user
+	}
+
+	// 构建响应
+	resp := &slowwildserver.GetReplyListRsp{
+		List: make([]*slowwildserver.RepliesItem, 0, len(replies)),
+	}
+
+	for _, reply := range replies {
+		// 获取回复用户信息
+		user := userMap[reply.UserId]
+		if user == nil {
+			continue
+		}
+
+		// 获取@用户信息
+		var withUsers []*slowwildserver.UserInfo
+		if reply.WithUserIds != "" {
+			for _, idStr := range strings.Split(reply.WithUserIds, ",") {
+				if userId, err := strconv.ParseInt(idStr, 10, 64); err == nil {
+					if atUser := userMap[userId]; atUser != nil {
+						withUsers = append(withUsers, &slowwildserver.UserInfo{
+							Id:       atUser.ID,
+							Nickname: atUser.Nickname,
+							Avatar:   atUser.Avatar,
+							Sex:      int32(atUser.Sex),
+						})
+					}
+				}
+			}
+		}
+
+		// 获取回复点赞状态
+		isUpvoted, err := l.svcCtx.CommentModel.IsCommentUpvotedByUser(l.ctx, in.UserId, reply.ID)
+		if err != nil {
+			l.Logger.Errorf("获取回复点赞状态失败: %v", err)
+			continue
+		}
+
+		resp.List = append(resp.List, &slowwildserver.RepliesItem{
+			Id: reply.ID,
+			User: &slowwildserver.UserInfo{
+				Id:       user.ID,
+				Nickname: user.Nickname,
+				Avatar:   user.Avatar,
+				Sex:      int32(user.Sex),
+			},
+			Content:     reply.Content,
+			WithUser:    withUsers,
+			UpvoteCount: reply.ThumbsUpCount,
+			IpLoc:       reply.IpLoc,
+			CreatedOn:   reply.CreatedOn,
+			IsMine:      in.UserId == reply.UserId,
+			IsLiked:     isUpvoted,
+		})
+	}
+
+	return resp, nil
+}

+ 94 - 0
internal/logic/gettaglistlogic.go

@@ -0,0 +1,94 @@
+package logic
+
+import (
+	"context"
+	"slowwild/internal/errorx"
+	"slowwild/internal/model"
+	"slowwild/internal/svc"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetTagListLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewGetTagListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetTagListLogic {
+	return &GetTagListLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 获取话题列表
+func (l *GetTagListLogic) GetTagList(in *slowwildserver.GetTagListReq) (*slowwildserver.GetTagListRsp, error) {
+	if in.Page <= 0 || in.PageSize <= 0 {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	// 获取话题列表
+	tags, total, err := l.svcCtx.TagModel.GetTagList(l.ctx, int(in.Page), int(in.PageSize), in.SortBy, in.TagName)
+	if err != nil {
+		l.Logger.Errorf("获取话题列表失败: %v", err)
+		return nil, errorx.ErrTagQueryFailed
+	}
+
+	// 收集所有创建者ID
+	userIds := make(map[int64]bool)
+	for _, tag := range tags {
+		userIds[tag.UserId] = true
+	}
+
+	// 批量获取用户信息
+	var userIdList []int64
+	for userId := range userIds {
+		userIdList = append(userIdList, userId)
+	}
+	users, err := l.svcCtx.UserModel.FindByIds(l.ctx, userIdList)
+	if err != nil {
+		l.Logger.Errorf("批量获取用户信息失败: %v", err)
+		return nil, errorx.ErrUserQueryFailed
+	}
+
+	// 构建用户信息map
+	userMap := make(map[int64]*model.User)
+	for _, user := range users {
+		userMap[user.ID] = user
+	}
+
+	// 构建响应
+	resp := &slowwildserver.GetTagListRsp{
+		Total:   int32(total),
+		TagList: make([]*slowwildserver.TagItem, 0, len(tags)),
+	}
+
+	for _, tag := range tags {
+
+		// 获取创建者信息
+		user := userMap[tag.UserId]
+		var userInfo *slowwildserver.UserInfo
+		if user != nil {
+			userInfo = &slowwildserver.UserInfo{
+				Id:       user.ID,
+				Nickname: user.Nickname,
+				Avatar:   user.Avatar,
+				Sex:      int32(user.Sex),
+			}
+		}
+
+		resp.TagList = append(resp.TagList, &slowwildserver.TagItem{
+			Id:        tag.ID,
+			Name:      tag.Name,
+			HotNum:    tag.HotNum,
+			CreatedOn: tag.CreatedOn,
+			User:      userInfo,
+		})
+	}
+
+	return resp, nil
+}

+ 83 - 0
internal/logic/gettaglogic.go

@@ -0,0 +1,83 @@
+package logic
+
+import (
+	"context"
+	"slowwild/internal/errorx"
+	"slowwild/internal/svc"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetTagLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewGetTagLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetTagLogic {
+	return &GetTagLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 获取话题详情
+func (l *GetTagLogic) GetTag(in *slowwildserver.GetTagReq) (*slowwildserver.GetTagRsp, error) {
+	if in.TagId <= 0 {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	// 获取话题基本信息
+	tag, err := l.svcCtx.TagModel.GetTag(l.ctx, int64(in.TagId))
+	if err != nil {
+		l.Logger.Errorf("获取话题信息失败: %v", err)
+		return nil, errorx.ErrTagQueryFailed
+	}
+
+	// 查询用户信息
+	userInfo, err := l.svcCtx.UserModel.FindOne(l.ctx, tag.UserId)
+	if err != nil {
+		l.Logger.Errorf("获取用户信息失败: %v", err)
+		return nil, errorx.ErrUserNotFound
+	}
+
+	// 获取话题统计信息
+	postCount, followCount, err := l.svcCtx.TagModel.GetTagStats(l.ctx, int64(in.TagId))
+	if err != nil {
+		l.Logger.Errorf("获取话题统计信息失败: %v", err)
+		return nil, errorx.ErrTagQueryFailed
+	}
+
+	// 获取用户关注状态
+	isFollowed := false
+	if in.UserId > 0 {
+		isFollowed, err = l.svcCtx.TagModel.IsTagFollowedByUser(l.ctx, in.UserId, int64(in.TagId))
+		if err != nil {
+			l.Logger.Errorf("获取话题关注状态失败: %v", err)
+			return nil, errorx.ErrTagQueryFailed
+		}
+	}
+
+	// 构建响应
+	resp := &slowwildserver.GetTagRsp{
+
+		Id:          tag.ID,
+		Name:        tag.Name,
+		HotNum:      tag.HotNum,
+		PostCount:   postCount,
+		FollowCount: followCount,
+		IsFollowed:  isFollowed,
+		CreatedOn:   tag.CreatedOn,
+		User: &slowwildserver.UserInfo{
+			Id:       userInfo.ID,
+			Nickname: userInfo.Username,
+			Avatar:   userInfo.Avatar,
+			Sex:      int32(userInfo.Sex),
+		},
+	}
+
+	return resp, nil
+}

+ 135 - 0
internal/logic/getuserpostcollectionlistlogic.go

@@ -0,0 +1,135 @@
+package logic
+
+import (
+	"context"
+	"slowwild/internal/errorx"
+	"slowwild/internal/model"
+	"slowwild/internal/svc"
+	"strconv"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+
+	"strings"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetUserPostCollectionListLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewGetUserPostCollectionListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserPostCollectionListLogic {
+	return &GetUserPostCollectionListLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 获取用户收藏的帖子
+func (l *GetUserPostCollectionListLogic) GetUserPostCollectionList(in *slowwildserver.GetUserPostCollectionListReq) (*slowwildserver.GetUserPostCollectionListRsp, error) {
+	if in.UserId <= 0 || in.QueryUserId <= 0 || in.Page <= 0 || in.PageSize <= 0 {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	// 获取收藏的帖子列表
+	posts, total, err := l.svcCtx.PostModel.GetUserCollectionPosts(l.ctx, int64(in.QueryUserId), int(in.Page), int(in.PageSize))
+	if err != nil {
+		l.Logger.Errorf("获取用户收藏帖子列表失败: %v", err)
+		return nil, errorx.ErrPostQueryFailed
+	}
+
+	// 获取所有话题ID
+	var allTagIds []int64
+	for _, post := range posts {
+		if post.Tags != "" {
+			tagStrs := strings.Split(post.Tags, ",")
+			for _, tagStr := range tagStrs {
+				tagId, err := strconv.ParseInt(tagStr, 10, 64)
+				if err != nil {
+					continue
+				}
+				allTagIds = append(allTagIds, tagId)
+			}
+		}
+	}
+
+	// 批量获取话题信息
+	tags, err := l.svcCtx.TagModel.FindByIds(l.ctx, allTagIds)
+	if err != nil {
+		l.Logger.Errorf("批量获取话题信息失败: %v", err)
+		return nil, errorx.ErrPostQueryFailed
+	}
+
+	// 构建话题信息map
+	tagMap := make(map[int64]*model.Tag)
+	for _, tag := range tags {
+		tagMap[tag.ID] = tag
+	}
+
+	// 构建返回数据
+	var postList []*slowwildserver.GetPostListItem
+	for _, post := range posts {
+		// 获取帖子内容
+		content, err := l.svcCtx.PostModel.GetPostContent(l.ctx, post.ID)
+		if err != nil {
+			l.Logger.Errorf("获取帖子内容失败: %v", err)
+			continue
+		}
+
+		// 检查当前用户是否点赞过该帖子
+		isLiked, err := l.svcCtx.PostActionModel.CheckPostAction(l.ctx, int64(in.UserId), post.ID, 0)
+		if err != nil {
+			l.Logger.Errorf("检查点赞状态失败: %v", err)
+		}
+
+		// 检查当前用户是否收藏过该帖子
+		isCollected, err := l.svcCtx.PostActionModel.CheckPostAction(l.ctx, int64(in.UserId), post.ID, 1)
+		if err != nil {
+			l.Logger.Errorf("检查收藏状态失败: %v", err)
+		}
+
+		// 获取话题信息
+		var tagItems []*slowwildserver.TagItem
+		if post.Tags != "" {
+			tagStrs := strings.Split(post.Tags, ",")
+			for _, tagStr := range tagStrs {
+				tagId, err := strconv.ParseInt(tagStr, 10, 64)
+				if err != nil {
+					continue
+				}
+				if tag, ok := tagMap[tagId]; ok {
+					tagItems = append(tagItems, &slowwildserver.TagItem{
+						Id:   tag.ID,
+						Name: tag.Name,
+					})
+				}
+			}
+		}
+
+		postList = append(postList, &slowwildserver.GetPostListItem{
+			Id:              post.ID,
+			PostType:        post.PostType,
+			Title:           content.Title,
+			ContentSummary:  content.ContentSummary,
+			Images:          strings.Split(content.PostCovers, ","),
+			VideoUrl:        content.PostVideoUrl,
+			VideoCover:      content.PostVideoCover,
+			CommentCount:    post.CommentCount,
+			CollectionCount: post.CollectionCount,
+			UpvoteCount:     post.UpvoteCount,
+			ShareCount:      post.ShareCount,
+			CreatedOn:       post.CreatedOn,
+			IsLiked:         isLiked,
+			IsCollected:     isCollected,
+			Tags:            tagItems,
+		})
+	}
+
+	return &slowwildserver.GetUserPostCollectionListRsp{
+		Total: total,
+		List:  postList,
+	}, nil
+}

+ 134 - 0
internal/logic/getuserpostlikelistlogic.go

@@ -0,0 +1,134 @@
+package logic
+
+import (
+	"context"
+	"slowwild/internal/errorx"
+	"slowwild/internal/model"
+	"slowwild/internal/svc"
+	"strconv"
+	"strings"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetUserPostLikeListLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewGetUserPostLikeListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserPostLikeListLogic {
+	return &GetUserPostLikeListLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 获取用户点赞过的帖子
+func (l *GetUserPostLikeListLogic) GetUserPostLikeList(in *slowwildserver.GetUserPostLikeListReq) (*slowwildserver.GetUserPostLikeListRsp, error) {
+	if in.UserId <= 0 || in.QueryUserId <= 0 || in.Page <= 0 || in.PageSize <= 0 {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	// 获取点赞的帖子列表
+	posts, total, err := l.svcCtx.PostModel.GetUserLikePosts(l.ctx, int64(in.QueryUserId), int(in.Page), int(in.PageSize))
+	if err != nil {
+		l.Logger.Errorf("获取用户点赞帖子列表失败: %v", err)
+		return nil, errorx.ErrPostQueryFailed
+	}
+
+	// 获取所有话题ID
+	var allTagIds []int64
+	for _, post := range posts {
+		if post.Tags != "" {
+			tagStrs := strings.Split(post.Tags, ",")
+			for _, tagStr := range tagStrs {
+				tagId, err := strconv.ParseInt(tagStr, 10, 64)
+				if err != nil {
+					continue
+				}
+				allTagIds = append(allTagIds, tagId)
+			}
+		}
+	}
+
+	// 批量获取话题信息
+	tags, err := l.svcCtx.TagModel.FindByIds(l.ctx, allTagIds)
+	if err != nil {
+		l.Logger.Errorf("批量获取话题信息失败: %v", err)
+		return nil, errorx.ErrPostQueryFailed
+	}
+
+	// 构建话题信息map
+	tagMap := make(map[int64]*model.Tag)
+	for _, tag := range tags {
+		tagMap[tag.ID] = tag
+	}
+
+	// 构建返回数据
+	var postList []*slowwildserver.GetPostListItem
+	for _, post := range posts {
+		// 获取帖子内容
+		content, err := l.svcCtx.PostModel.GetPostContent(l.ctx, post.ID)
+		if err != nil {
+			l.Logger.Errorf("获取帖子内容失败: %v", err)
+			continue
+		}
+
+		// 检查当前用户是否点赞过该帖子
+		isLiked, err := l.svcCtx.PostActionModel.CheckPostAction(l.ctx, int64(in.UserId), post.ID, 0)
+		if err != nil {
+			l.Logger.Errorf("检查点赞状态失败: %v", err)
+		}
+
+		// 检查当前用户是否收藏过该帖子
+		isCollected, err := l.svcCtx.PostActionModel.CheckPostAction(l.ctx, int64(in.UserId), post.ID, 1)
+		if err != nil {
+			l.Logger.Errorf("检查收藏状态失败: %v", err)
+		}
+
+		// 获取话题信息
+		var tagItems []*slowwildserver.TagItem
+		if post.Tags != "" {
+			tagStrs := strings.Split(post.Tags, ",")
+			for _, tagStr := range tagStrs {
+				tagId, err := strconv.ParseInt(tagStr, 10, 64)
+				if err != nil {
+					continue
+				}
+				if tag, ok := tagMap[tagId]; ok {
+					tagItems = append(tagItems, &slowwildserver.TagItem{
+						Id:   tag.ID,
+						Name: tag.Name,
+					})
+				}
+			}
+		}
+
+		postList = append(postList, &slowwildserver.GetPostListItem{
+			Id:              post.ID,
+			PostType:        post.PostType,
+			Title:           content.Title,
+			ContentSummary:  content.ContentSummary,
+			Images:          strings.Split(content.PostCovers, ","),
+			VideoUrl:        content.PostVideoUrl,
+			VideoCover:      content.PostVideoCover,
+			CommentCount:    post.CommentCount,
+			CollectionCount: post.CollectionCount,
+			UpvoteCount:     post.UpvoteCount,
+			ShareCount:      post.ShareCount,
+			CreatedOn:       post.CreatedOn,
+			IsLiked:         isLiked,
+			IsCollected:     isCollected,
+			Tags:            tagItems,
+		})
+	}
+
+	return &slowwildserver.GetUserPostLikeListRsp{
+		Total: total,
+		List:  postList,
+	}, nil
+}

+ 134 - 0
internal/logic/getuserpostlistlogic.go

@@ -0,0 +1,134 @@
+package logic
+
+import (
+	"context"
+	"slowwild/internal/errorx"
+	"slowwild/internal/model"
+	"slowwild/internal/svc"
+	"strconv"
+	"strings"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetUserPostListLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewGetUserPostListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserPostListLogic {
+	return &GetUserPostListLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 获取用户发布的帖子
+func (l *GetUserPostListLogic) GetUserPostList(in *slowwildserver.GetUserPostListReq) (*slowwildserver.GetUserPostListRsp, error) {
+	if in.UserId <= 0 || in.QueryUserId <= 0 || in.Page <= 0 || in.PageSize <= 0 {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	// 获取发布的帖子列表
+	posts, total, err := l.svcCtx.PostModel.GetUserPosts(l.ctx, int64(in.QueryUserId), int(in.Page), int(in.PageSize))
+	if err != nil {
+		l.Logger.Errorf("获取用户发布帖子列表失败: %v", err)
+		return nil, errorx.ErrPostQueryFailed
+	}
+
+	// 获取所有话题ID
+	var allTagIds []int64
+	for _, post := range posts {
+		if post.Tags != "" {
+			tagStrs := strings.Split(post.Tags, ",")
+			for _, tagStr := range tagStrs {
+				tagId, err := strconv.ParseInt(tagStr, 10, 64)
+				if err != nil {
+					continue
+				}
+				allTagIds = append(allTagIds, tagId)
+			}
+		}
+	}
+
+	// 批量获取话题信息
+	tags, err := l.svcCtx.TagModel.FindByIds(l.ctx, allTagIds)
+	if err != nil {
+		l.Logger.Errorf("批量获取话题信息失败: %v", err)
+		return nil, errorx.ErrPostQueryFailed
+	}
+
+	// 构建话题信息map
+	tagMap := make(map[int64]*model.Tag)
+	for _, tag := range tags {
+		tagMap[tag.ID] = tag
+	}
+
+	// 构建返回数据
+	var postList []*slowwildserver.GetPostListItem
+	for _, post := range posts {
+		// 获取帖子内容
+		content, err := l.svcCtx.PostModel.GetPostContent(l.ctx, post.ID)
+		if err != nil {
+			l.Logger.Errorf("获取帖子内容失败: %v", err)
+			continue
+		}
+
+		// 检查当前用户是否点赞过该帖子
+		isLiked, err := l.svcCtx.PostActionModel.CheckPostAction(l.ctx, int64(in.UserId), post.ID, 0)
+		if err != nil {
+			l.Logger.Errorf("检查点赞状态失败: %v", err)
+		}
+
+		// 检查当前用户是否收藏过该帖子
+		isCollected, err := l.svcCtx.PostActionModel.CheckPostAction(l.ctx, int64(in.UserId), post.ID, 1)
+		if err != nil {
+			l.Logger.Errorf("检查收藏状态失败: %v", err)
+		}
+
+		// 获取话题信息
+		var tagItems []*slowwildserver.TagItem
+		if post.Tags != "" {
+			tagStrs := strings.Split(post.Tags, ",")
+			for _, tagStr := range tagStrs {
+				tagId, err := strconv.ParseInt(tagStr, 10, 64)
+				if err != nil {
+					continue
+				}
+				if tag, ok := tagMap[tagId]; ok {
+					tagItems = append(tagItems, &slowwildserver.TagItem{
+						Id:   tag.ID,
+						Name: tag.Name,
+					})
+				}
+			}
+		}
+
+		postList = append(postList, &slowwildserver.GetPostListItem{
+			Id:              post.ID,
+			PostType:        post.PostType,
+			Title:           content.Title,
+			ContentSummary:  content.ContentSummary,
+			Images:          strings.Split(content.PostCovers, ","),
+			VideoUrl:        content.PostVideoUrl,
+			VideoCover:      content.PostVideoCover,
+			CommentCount:    post.CommentCount,
+			CollectionCount: post.CollectionCount,
+			UpvoteCount:     post.UpvoteCount,
+			ShareCount:      post.ShareCount,
+			CreatedOn:       post.CreatedOn,
+			IsLiked:         isLiked,
+			IsCollected:     isCollected,
+			Tags:            tagItems,
+		})
+	}
+
+	return &slowwildserver.GetUserPostListRsp{
+		Total: total,
+		List:  postList,
+	}, nil
+}

+ 111 - 0
internal/logic/postcollectionlogic.go

@@ -0,0 +1,111 @@
+package logic
+
+import (
+	"context"
+	"slowwild/internal/errorx"
+	"slowwild/internal/model"
+	"slowwild/internal/svc"
+	"time"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+
+	"github.com/zeromicro/go-zero/core/logx"
+	"gorm.io/gorm"
+)
+
+type PostCollectionLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewPostCollectionLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PostCollectionLogic {
+	return &PostCollectionLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 收藏帖子
+func (l *PostCollectionLogic) PostCollection(in *slowwildserver.PostCollectionReq) (*slowwildserver.PostCollectionRsp, error) {
+	if in.UserId <= 0 || in.PostId <= 0 {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	// 开启事务
+	err := l.svcCtx.UserModel.Transaction(l.ctx, func(tx *gorm.DB) error {
+		postModel := model.NewPostModel(tx, l.svcCtx.Redis)
+		userModel := model.NewUserModel(tx)
+		postActionModel := model.NewPostActionModel(tx, l.svcCtx.Redis)
+
+		// 检查帖子是否存在
+		post, err := postModel.GetPostDetail(l.ctx, in.PostId)
+		if err != nil {
+			return err
+		}
+
+		// 检查是否已经收藏
+		isCollected, err := userModel.IsPostCollectedByUser(l.ctx, in.UserId, in.PostId)
+		if err != nil {
+			return err
+		}
+
+		// 创建或更新收藏记录
+		postAction := &model.PostAction{
+			Model: &model.Model{
+				CreatedOn:  time.Now().Unix(),
+				ModifiedOn: time.Now().Unix(),
+			},
+			PostId:     in.PostId,
+			UserId:     in.UserId,
+			ActionType: 1, // 1表示收藏
+		}
+
+		if !isCollected {
+			// 收藏操作
+			if err := postActionModel.CreatePostAction(l.ctx, postAction); err != nil {
+				return err
+			}
+			// 增加收藏数
+			if err := postModel.IncrementCollectionCount(l.ctx, in.PostId, 1); err != nil {
+				return err
+			}
+
+			// 如果不是自己的帖子,发送消息通知
+			if post.UserId != in.UserId {
+				messageModel := model.NewMessageModel(tx)
+				err = messageModel.SendNotification(l.ctx, in.UserId, post.UserId, 2,
+					"收藏通知",
+					"有人收藏了你的动态",
+					in.PostId, 0, 0)
+				if err != nil {
+					return err
+				}
+			}
+		} else {
+			// 取消收藏操作
+			postAction.IsDel = 1
+			postAction.DeletedOn = time.Now().Unix()
+			if err := postActionModel.DeletePostAction(l.ctx, postAction); err != nil {
+				return err
+			}
+			// 减少收藏数
+			if err := postModel.IncrementCollectionCount(l.ctx, in.PostId, -1); err != nil {
+				return err
+			}
+		}
+
+		return nil
+	})
+
+	if err != nil {
+		if err == errorx.ErrInvalidOperation {
+			return nil, err
+		}
+		l.Logger.Errorf("收藏帖子失败: %v", err)
+		return nil, errorx.NewCodeError(20005, "收藏操作失败")
+	}
+
+	return &slowwildserver.PostCollectionRsp{}, nil
+}

+ 105 - 0
internal/logic/postcommentlogic.go

@@ -0,0 +1,105 @@
+package logic
+
+import (
+	"context"
+	"slowwild/internal/errorx"
+	"slowwild/internal/model"
+	"slowwild/internal/svc"
+	"time"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+	"github.com/spf13/cast"
+	"github.com/zeromicro/go-zero/core/logx"
+	"gorm.io/gorm"
+)
+
+type PostCommentLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewPostCommentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PostCommentLogic {
+	return &PostCommentLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 发布评论
+func (l *PostCommentLogic) PostComment(in *slowwildserver.PostCommentReq) (*slowwildserver.PostCommentRsp, error) {
+	if in.UserId <= 0 || in.PostId <= 0 || in.Content == "" {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	// 开启事务
+	err := l.svcCtx.UserModel.Transaction(l.ctx, func(tx *gorm.DB) error {
+		postModel := model.NewPostModel(tx, l.svcCtx.Redis)
+		commentModel := model.NewCommentModel(tx, l.svcCtx.Redis)
+		messageModel := model.NewMessageModel(tx)
+
+		// 检查帖子是否存在
+		post, err := postModel.GetPostDetail(l.ctx, in.PostId)
+		if err != nil {
+			return err
+		}
+
+		// 构建评论数据
+		comment := &model.Comment{
+			Model: &model.Model{
+				CreatedOn:  time.Now().Unix(),
+				ModifiedOn: time.Now().Unix(),
+			},
+			PostId:      in.PostId,
+			UserId:      in.UserId,
+			Content:     in.Content,
+			Image:       in.Image,
+			WithUserIds: cast.ToString(in.AtUserId),
+			Ip:          in.Ip,
+			IpLoc:       in.IpLoc,
+		}
+
+		// 创建评论
+		if err := commentModel.Create(l.ctx, comment); err != nil {
+			return err
+		}
+
+		// 增加帖子评论数
+		if err := postModel.IncrementCommentCount(l.ctx, in.PostId, 1); err != nil {
+			return err
+		}
+
+		// 如果不是评论自己的帖子,发送消息通知
+		if post.UserId != in.UserId {
+			err = messageModel.SendNotification(l.ctx, in.UserId, post.UserId, 3,
+				"评论通知",
+				"有人评论了你的动态",
+				in.PostId, comment.ID, 0)
+			if err != nil {
+				return err
+			}
+		}
+
+		// 如果有@用户,给被@的用户发送消息通知
+		if in.AtUserId > 0 {
+			err = messageModel.SendNotification(l.ctx, in.UserId, in.AtUserId, 4,
+				"@通知",
+				"有人在评论中@了你",
+				in.PostId, comment.ID, 0)
+			if err != nil {
+				return err
+			}
+
+		}
+
+		return nil
+	})
+
+	if err != nil {
+		l.Logger.Errorf("发布评论失败: %v", err)
+		return nil, errorx.NewCodeError(20006, "发布评论失败")
+	}
+
+	return &slowwildserver.PostCommentRsp{}, nil
+}

+ 109 - 0
internal/logic/postcommentupvotelogic.go

@@ -0,0 +1,109 @@
+package logic
+
+import (
+	"context"
+	"slowwild/internal/errorx"
+	"slowwild/internal/model"
+	"slowwild/internal/svc"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+
+	"time"
+
+	"github.com/zeromicro/go-zero/core/logx"
+	"gorm.io/gorm"
+)
+
+type PostCommentUpvoteLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewPostCommentUpvoteLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PostCommentUpvoteLogic {
+	return &PostCommentUpvoteLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 评论点赞
+func (l *PostCommentUpvoteLogic) PostCommentUpvote(in *slowwildserver.PostCommentUpvoteReq) (*slowwildserver.PostCommentUpvoteRsp, error) {
+	if in.UserId <= 0 || in.CommentId <= 0 {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	// 开启事务
+	err := l.svcCtx.UserModel.Transaction(l.ctx, func(tx *gorm.DB) error {
+		commentModel := model.NewCommentModel(tx, l.svcCtx.Redis)
+		messageModel := model.NewMessageModel(tx)
+
+		// 获取评论/回复信息
+		comment, err := commentModel.GetComment(l.ctx, in.CommentId)
+		if err != nil {
+			return err
+		}
+
+		// 检查是否已经点赞
+		isUpvoted, err := commentModel.IsCommentUpvotedByUser(l.ctx, in.UserId, in.CommentId)
+		if err != nil {
+			return err
+		}
+
+		if !isUpvoted {
+			// 创建点赞记录
+			upvote := &model.CommentUpvote{
+				Model: &model.Model{
+					CreatedOn:  time.Now().Unix(),
+					ModifiedOn: time.Now().Unix(),
+				},
+				UserId:     in.UserId,
+				CommentId:  in.CommentId,
+				ActionType: in.CommentType, // 0-评论点赞 1-回复点赞
+			}
+			if err := commentModel.CreateCommentUpvote(l.ctx, upvote); err != nil {
+				return err
+			}
+
+			// 增加点赞数
+			if err := commentModel.IncrementUpvoteCount(l.ctx, in.CommentId, 1); err != nil {
+				return err
+			}
+
+			// 如果不是给自己点赞,发送消息通知
+			if comment.UserId != in.UserId {
+				notificationType := 3 // 评论通知
+				if in.CommentType == 1 {
+					notificationType = 4 // 回复通知
+				}
+				err = messageModel.SendNotification(l.ctx, in.UserId, comment.UserId, int8(notificationType),
+					"点赞通知",
+					"有人点赞了你的评论",
+					comment.PostId, in.CommentId, 0)
+				if err != nil {
+					return err
+				}
+			}
+		} else {
+			// 取消点赞
+			if err := commentModel.DeleteCommentUpvote(l.ctx, in.UserId, in.CommentId); err != nil {
+				return err
+			}
+
+			// 减少点赞数
+			if err := commentModel.IncrementUpvoteCount(l.ctx, in.CommentId, -1); err != nil {
+				return err
+			}
+		}
+
+		return nil
+	})
+
+	if err != nil {
+		l.Logger.Errorf("评论点赞失败: %v", err)
+		return nil, errorx.NewCodeError(20007, "评论点赞失败")
+	}
+
+	return &slowwildserver.PostCommentUpvoteRsp{}, nil
+}

+ 73 - 0
internal/logic/postdeletelogic.go

@@ -0,0 +1,73 @@
+package logic
+
+import (
+	"context"
+	"slowwild/internal/errorx"
+	"slowwild/internal/model"
+	"slowwild/internal/svc"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+
+	"github.com/zeromicro/go-zero/core/logx"
+	"gorm.io/gorm"
+)
+
+type PostDeleteLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewPostDeleteLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PostDeleteLogic {
+	return &PostDeleteLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 删除帖子
+func (l *PostDeleteLogic) PostDelete(in *slowwildserver.PostDeleteReq) (*slowwildserver.PostDeleteRsp, error) {
+	if in.UserId <= 0 || in.PostId <= 0 {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	// 开启事务
+	err := l.svcCtx.UserModel.Transaction(l.ctx, func(tx *gorm.DB) error {
+		postModel := model.NewPostModel(tx, l.svcCtx.Redis)
+		userModel := model.NewUserModel(tx)
+
+		// 获取帖子信息
+		post, err := postModel.GetPostDetail(l.ctx, in.PostId)
+		if err != nil {
+			return err
+		}
+
+		// 检查是否是自己的帖子
+		if post.UserId != in.UserId {
+			return errorx.NewCodeError(20008, "无权删除该帖子")
+		}
+
+		// 删除帖子
+		if err := postModel.Delete(l.ctx, in.PostId); err != nil {
+			return err
+		}
+
+		// 减少用户的帖子数
+		if err := userModel.DecrementTweetCount(l.ctx, in.UserId); err != nil {
+			return err
+		}
+
+		// 删除相关缓存
+		postModel.ClearCache(l.ctx, in.PostId)
+
+		return nil
+	})
+
+	if err != nil {
+		l.Logger.Errorf("删除帖子失败: %v", err)
+		return nil, errorx.NewCodeError(20009, "删除帖子失败")
+	}
+
+	return &slowwildserver.PostDeleteRsp{}, nil
+}

+ 127 - 0
internal/logic/postreplylogic.go

@@ -0,0 +1,127 @@
+package logic
+
+import (
+	"context"
+	"slowwild/internal/errorx"
+	"slowwild/internal/model"
+	"slowwild/internal/svc"
+	"time"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+
+	"github.com/spf13/cast"
+	"github.com/zeromicro/go-zero/core/logx"
+	"gorm.io/gorm"
+)
+
+type PostReplyLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewPostReplyLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PostReplyLogic {
+	return &PostReplyLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 发布回复
+func (l *PostReplyLogic) PostReply(in *slowwildserver.PostReplyReq) (*slowwildserver.PostReplyRsp, error) {
+	if in.UserId <= 0 || in.PostId <= 0 || in.CommentId <= 0 || in.Content == "" {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	// 开启事务
+	err := l.svcCtx.UserModel.Transaction(l.ctx, func(tx *gorm.DB) error {
+		postModel := model.NewPostModel(tx, l.svcCtx.Redis)
+		commentModel := model.NewCommentModel(tx, l.svcCtx.Redis)
+		replyModel := model.NewReplyModel(tx, l.svcCtx.Redis)
+		messageModel := model.NewMessageModel(tx)
+
+		// 检查评论是否存在
+		parentComment, err := commentModel.GetComment(l.ctx, in.CommentId)
+		if err != nil {
+			return err
+		}
+
+		// 构建回复数据
+		reply := &model.CommentReply{
+			Model: &model.Model{
+				CreatedOn:  time.Now().Unix(),
+				ModifiedOn: time.Now().Unix(),
+			},
+			PostId:      in.PostId,
+			UserId:      in.UserId,
+			CommentId:   in.CommentId,
+			Content:     in.Content,
+			Image:       in.Image,
+			WithUserIds: cast.ToString(in.AtUserId),
+			Ip:          in.Ip,
+			IpLoc:       in.IpLoc,
+			ReplyId:     in.ToReplyId,
+		}
+
+		// 创建回复
+		if err := replyModel.Create(l.ctx, reply); err != nil {
+			return err
+		}
+
+		// 增加父评论的回复数
+		if err := commentModel.IncrementReplyCount(l.ctx, in.CommentId, 1); err != nil {
+			return err
+		}
+
+		// 增加帖子评论数
+		if err := postModel.IncrementCommentCount(l.ctx, in.PostId, 1); err != nil {
+			return err
+		}
+
+		// 如果是回复的回复,需要通知被回复的用户
+		if in.ToReplyId > 0 {
+			parentReply, err := replyModel.GetReply(l.ctx, in.ToReplyId)
+			if err != nil {
+				return err
+			}
+			if parentReply.UserId != in.UserId {
+				err = messageModel.SendNotification(l.ctx, in.UserId, parentReply.UserId, 3,
+					"回复通知",
+					"有人回复了你的回复",
+					in.PostId, in.CommentId, reply.ID)
+				if err != nil {
+					return err
+				}
+			}
+		} else if parentComment.UserId != in.UserId { // 如果是回复评论,需要通知评论作者
+			err = messageModel.SendNotification(l.ctx, in.UserId, parentComment.UserId, 3,
+				"回复通知",
+				"有人回复了你的评论",
+				in.PostId, in.CommentId, reply.ID)
+			if err != nil {
+				return err
+			}
+		}
+
+		// 如果有@用户,给被@的用户发送消息通知
+		if in.AtUserId > 0 && in.AtUserId != in.UserId {
+			err = messageModel.SendNotification(l.ctx, in.UserId, in.AtUserId, 4,
+				"@通知",
+				"有人在回复中@了你",
+				in.PostId, in.CommentId, reply.ID)
+			if err != nil {
+				return err
+			}
+		}
+
+		return nil
+	})
+
+	if err != nil {
+		l.Logger.Errorf("发布回复失败: %v", err)
+		return nil, errorx.ErrCommentFailed
+	}
+
+	return &slowwildserver.PostReplyRsp{}, nil
+}

+ 97 - 0
internal/logic/postsharelogic.go

@@ -0,0 +1,97 @@
+package logic
+
+import (
+	"context"
+	"slowwild/internal/errorx"
+	"slowwild/internal/model"
+	"slowwild/internal/svc"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+
+	"time"
+
+	"github.com/zeromicro/go-zero/core/logx"
+	"gorm.io/gorm"
+)
+
+type PostShareLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewPostShareLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PostShareLogic {
+	return &PostShareLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 分享帖子
+func (l *PostShareLogic) PostShare(in *slowwildserver.PostShareReq) (*slowwildserver.PostShareRsp, error) {
+	if in.UserId <= 0 || in.PostId <= 0 {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	// 开启事务
+	err := l.svcCtx.UserModel.Transaction(l.ctx, func(tx *gorm.DB) error {
+		postModel := model.NewPostModel(tx, l.svcCtx.Redis)
+		postActionModel := model.NewPostActionModel(tx, l.svcCtx.Redis)
+		messageModel := model.NewMessageModel(tx)
+
+		// 获取帖子信息
+		post, err := postModel.GetPostDetail(l.ctx, in.PostId)
+		if err != nil {
+			return err
+		}
+
+		// 检查是否已经分享过
+		isShared, err := postActionModel.CheckPostAction(l.ctx, in.UserId, in.PostId, 2) // 2表示分享动作
+		if err != nil {
+			return err
+		}
+		if isShared {
+			return errorx.NewCodeError(20011, "已经分享过该帖子")
+		}
+
+		// 创建分享记录
+		postAction := &model.PostAction{
+			Model: &model.Model{
+				CreatedOn:  time.Now().Unix(),
+				ModifiedOn: time.Now().Unix(),
+			},
+			PostId:     in.PostId,
+			UserId:     in.UserId,
+			ActionType: 2, // 2表示分享
+		}
+		if err := postActionModel.CreatePostAction(l.ctx, postAction); err != nil {
+			return err
+		}
+
+		// 增加分享数
+		if err := postModel.IncrementShareCount(l.ctx, in.PostId); err != nil {
+			return err
+		}
+
+		// 如果不是分享自己的帖子,发送消息通知
+		if post.UserId != in.UserId {
+			err = messageModel.SendNotification(l.ctx, in.UserId, post.UserId, 5,
+				"分享通知",
+				"有人分享了你的动态",
+				in.PostId, 0, 0)
+			if err != nil {
+				return err
+			}
+		}
+
+		return nil
+	})
+
+	if err != nil {
+		l.Logger.Errorf("分享帖子失败: %v", err)
+		return nil, err
+	}
+
+	return &slowwildserver.PostShareRsp{}, nil
+}

+ 115 - 0
internal/logic/postupvotelogic.go

@@ -0,0 +1,115 @@
+package logic
+
+import (
+	"context"
+	"slowwild/internal/errorx"
+	"slowwild/internal/model"
+	"slowwild/internal/svc"
+	"time"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+
+	"github.com/zeromicro/go-zero/core/logx"
+	"gorm.io/gorm"
+)
+
+type PostUpvoteLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewPostUpvoteLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PostUpvoteLogic {
+	return &PostUpvoteLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 点赞帖子
+func (l *PostUpvoteLogic) PostUpvote(in *slowwildserver.PostUpvoteReq) (*slowwildserver.PostUpvoteRsp, error) {
+	if in.UserId <= 0 || in.PostId <= 0 {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	// 开启事务
+	err := l.svcCtx.UserModel.Transaction(l.ctx, func(tx *gorm.DB) error {
+		postModel := model.NewPostModel(tx, l.svcCtx.Redis)
+		postActionModel := model.NewPostActionModel(tx, l.svcCtx.Redis)
+		messageModel := model.NewMessageModel(tx)
+
+		// 获取帖子信息
+		post, err := postModel.GetPostDetail(l.ctx, in.PostId)
+		if err != nil {
+			return err
+		}
+
+		// 检查是否已经点赞过
+		isLiked, err := postActionModel.CheckPostAction(l.ctx, in.UserId, in.PostId, 0) // 0表示点赞动作
+		if err != nil {
+			return err
+		}
+
+		if !isLiked {
+			// 创建点赞记录
+			postAction := &model.PostAction{
+				Model: &model.Model{
+					CreatedOn:  time.Now().Unix(),
+					ModifiedOn: time.Now().Unix(),
+				},
+				PostId:     in.PostId,
+				UserId:     in.UserId,
+				ActionType: 0, // 0表示点赞
+			}
+			if err := postActionModel.CreatePostAction(l.ctx, postAction); err != nil {
+				return err
+			}
+
+			// 增加点赞数
+			if err := postModel.IncrementUpvoteCount(l.ctx, in.PostId, 1); err != nil {
+				return err
+			}
+
+			// 如果不是点赞自己的帖子,发送消息通知
+			if post.UserId != in.UserId {
+				err = messageModel.SendNotification(l.ctx, in.UserId, post.UserId, 6,
+					"点赞通知",
+					"有人点赞了你的动态",
+					in.PostId, 0, 0)
+				if err != nil {
+					return err
+				}
+			}
+		} else {
+			// 取消点赞
+			postAction := &model.PostAction{
+				Model: &model.Model{
+					ModifiedOn: time.Now().Unix(),
+					DeletedOn:  time.Now().Unix(),
+					IsDel:      1,
+				},
+				PostId:     in.PostId,
+				UserId:     in.UserId,
+				ActionType: 0,
+			}
+			if err := postActionModel.DeletePostAction(l.ctx, postAction); err != nil {
+				return err
+			}
+
+			// 减少点赞数
+			if err := postModel.IncrementUpvoteCount(l.ctx, in.PostId, -1); err != nil {
+				return err
+			}
+		}
+
+		return nil
+	})
+
+	if err != nil {
+		l.Logger.Errorf("帖子点赞失败: %v", err)
+		return nil, err
+	}
+
+	return &slowwildserver.PostUpvoteRsp{}, nil
+}

+ 287 - 0
internal/model/comment_model.go

@@ -0,0 +1,287 @@
+package model
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"time"
+
+	"github.com/go-redis/redis/v8"
+	"gorm.io/gorm"
+)
+
+const (
+	prefixCommentListKey   = "comment:list:%d:%d:%d" // postId:page:pageSize
+	prefixCommentKey       = "comment:%d"            // commentId
+	prefixReplyListKey     = "reply:list:%d"         // commentId
+	prefixCommentUpvoteKey = "comment:upvote:%d:%d"  // userId:commentId
+)
+
+// PComment 评论
+type Comment struct {
+	*Model
+	PostId        int64  `json:"post_id" gorm:"post_id"`                 // POST ID
+	UserId        int64  `json:"user_id" gorm:"user_id"`                 // 用户ID
+	Ip            string `json:"ip" gorm:"ip"`                           // IP地址
+	IpLoc         string `json:"ip_loc" gorm:"ip_loc"`                   // IP城市地址
+	Content       string `json:"content" gorm:"content"`                 // 评论内容
+	Image         string `json:"image" gorm:"image"`                     // 评论图片
+	WithUserIds   string `json:"with_user_ids" gorm:"with_user_ids"`     // 被艾特的用户
+	IsEssence     int8   `json:"is_essence" gorm:"is_essence"`           // 是否精选
+	ReplyCount    int64  `json:"reply_count" gorm:"reply_count"`         // 回复数
+	ThumbsUpCount int64  `json:"thumbs_up_count" gorm:"thumbs_up_count"` // 点赞数
+}
+
+// TableName 表名称
+func (*Comment) TableName() string {
+	return "p_comment"
+}
+
+type CommentModel struct {
+	conn  *gorm.DB
+	redis *redis.Client
+}
+
+func NewCommentModel(conn *gorm.DB, rdb *redis.Client) *CommentModel {
+	return &CommentModel{
+		conn:  conn,
+		redis: rdb,
+	}
+}
+
+// GetCommentList 获取评论列表(带缓存)
+func (m *CommentModel) GetCommentList(ctx context.Context, postId int64, page, pageSize int) ([]*Comment, error) {
+	// 尝试从缓存获取
+	key := fmt.Sprintf(prefixCommentListKey, postId, page, pageSize)
+	data, err := m.redis.Get(ctx, key).Bytes()
+	if err == nil {
+		var comments []*Comment
+		if err := json.Unmarshal(data, &comments); err == nil {
+			return comments, nil
+		}
+	}
+
+	// 从数据库获取
+	var comments []*Comment
+	offset := (page - 1) * pageSize
+	err = m.conn.WithContext(ctx).
+		Where("post_id = ? AND parent_id = 0", postId).
+		Order("created_on DESC").
+		Offset(offset).
+		Limit(pageSize).
+		Find(&comments).Error
+	if err != nil {
+		return nil, err
+	}
+
+	// 写入缓存
+	if data, err := json.Marshal(comments); err == nil {
+		m.redis.Set(ctx, key, data, 5*time.Minute) // 评论列表缓存时间较短
+	}
+
+	return comments, nil
+}
+
+// GetLatestReplies 获取最新的N条回复(带缓存)
+func (m *CommentModel) GetLatestReplies(ctx context.Context, commentId int64, limit int) ([]*Comment, error) {
+	// 尝试从缓存获取
+	key := fmt.Sprintf(prefixReplyListKey, commentId)
+	data, err := m.redis.Get(ctx, key).Bytes()
+	if err == nil {
+		var replies []*Comment
+		if err := json.Unmarshal(data, &replies); err == nil {
+			return replies, nil
+		}
+	}
+
+	// 从数据库获取
+	var replies []*Comment
+	err = m.conn.WithContext(ctx).
+		Where("parent_id = ?", commentId).
+		Order("created_on DESC").
+		Limit(limit).
+		Find(&replies).Error
+	if err != nil {
+		return nil, err
+	}
+
+	// 写入缓存
+	if data, err := json.Marshal(replies); err == nil {
+		m.redis.Set(ctx, key, data, 5*time.Minute)
+	}
+
+	return replies, nil
+}
+
+// IsCommentUpvotedByUser 检查用户是否点赞了评论/回复(带缓存)
+func (m *CommentModel) IsCommentUpvotedByUser(ctx context.Context, userId, commentId int64) (bool, error) {
+	// 尝试从缓存获取
+	key := fmt.Sprintf(prefixCommentUpvoteKey, userId, commentId)
+	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(&CommentUpvote{}).
+		Where("user_id = ? AND comment_id = ? AND is_del = 0", userId, commentId).
+		Count(&count).Error
+	if err != nil {
+		return false, err
+	}
+
+	// 写入缓存
+	isUpvoted := count > 0
+	m.redis.Set(ctx, key, isUpvoted, cacheExpiry)
+	return isUpvoted, nil
+}
+
+// GetReplyList 获取回复列表(带缓存)
+func (m *CommentModel) GetReplyList(ctx context.Context, commentId int64, page, pageSize int) ([]*Comment, error) {
+	// 尝试从缓存获取
+	key := fmt.Sprintf(prefixReplyListKey+":%d:%d", commentId, page, pageSize)
+	data, err := m.redis.Get(ctx, key).Bytes()
+	if err == nil {
+		var replies []*Comment
+		if err := json.Unmarshal(data, &replies); err == nil {
+			return replies, nil
+		}
+	}
+
+	// 从数据库获取
+	var replies []*Comment
+	offset := (page - 1) * pageSize
+	err = m.conn.WithContext(ctx).
+		Where("parent_id = ?", commentId).
+		Order("created_on DESC").
+		Offset(offset).
+		Limit(pageSize).
+		Find(&replies).Error
+	if err != nil {
+		return nil, err
+	}
+
+	// 写入缓存
+	if data, err := json.Marshal(replies); err == nil {
+		m.redis.Set(ctx, key, data, 5*time.Minute) // 回复列表缓存时间较短
+	}
+
+	return replies, nil
+}
+
+// Create 创建评论
+func (m *CommentModel) Create(ctx context.Context, comment *Comment) error {
+	err := m.conn.WithContext(ctx).Create(comment).Error
+	if err != nil {
+		return err
+	}
+
+	// 删除相关缓存
+	m.ClearCommentListCache(ctx, comment.PostId)
+	return nil
+}
+
+// ClearCommentListCache 清除评论列表缓存
+func (m *CommentModel) ClearCommentListCache(ctx context.Context, postId int64) error {
+	// 清除评论列表缓存
+	pattern := fmt.Sprintf("comment:list:%d*", postId)
+	keys, err := m.redis.Keys(ctx, pattern).Result()
+	if err != nil {
+		return err
+	}
+	if len(keys) > 0 {
+		m.redis.Del(ctx, keys...)
+	}
+
+	// 清除回复列表缓存
+	pattern = fmt.Sprintf(prefixReplyListKey+"*", postId)
+	keys, err = m.redis.Keys(ctx, pattern).Result()
+	if err != nil {
+		return err
+	}
+	if len(keys) > 0 {
+		m.redis.Del(ctx, keys...)
+	}
+
+	return nil
+}
+
+// IncrementUpvoteCount 增加点赞数
+func (m *CommentModel) IncrementUpvoteCount(ctx context.Context, commentId int64, increment int) error {
+	err := m.conn.WithContext(ctx).Model(&Comment{}).
+		Where("id = ?", commentId).
+		UpdateColumn("thumbs_up_count", gorm.Expr("thumbs_up_count + ?", increment)).Error
+	if err != nil {
+		return err
+	}
+
+	// 删除评论缓存
+	key := fmt.Sprintf(prefixCommentKey, commentId)
+	m.redis.Del(ctx, key)
+	return nil
+}
+
+// CreateCommentUpvote 创建评论点赞记录
+func (m *CommentModel) CreateCommentUpvote(ctx context.Context, upvote *CommentUpvote) error {
+	err := m.conn.WithContext(ctx).Create(upvote).Error
+	if err != nil {
+		return err
+	}
+
+	// 删除点赞状态缓存
+	key := fmt.Sprintf(prefixCommentUpvoteKey, upvote.UserId, upvote.CommentId)
+	m.redis.Del(ctx, key)
+	return nil
+}
+
+// DeleteCommentUpvote 删除评论点赞记录
+func (m *CommentModel) DeleteCommentUpvote(ctx context.Context, userId, commentId int64) error {
+	err := m.conn.WithContext(ctx).Model(&CommentUpvote{}).
+		Where("user_id = ? AND comment_id = ?", userId, commentId).
+		Update("is_del", 1).Error
+	if err != nil {
+		return err
+	}
+
+	// 删除点赞状态缓存
+	key := fmt.Sprintf(prefixCommentUpvoteKey, userId, commentId)
+	m.redis.Del(ctx, key)
+	return nil
+}
+
+// 获取评论
+func (m *CommentModel) GetComment(ctx context.Context, commentId int64) (*Comment, error) {
+	var comment Comment
+	err := m.conn.WithContext(ctx).Where("id = ?", commentId).First(&comment).Error
+	if err != nil {
+		return nil, err
+	}
+	return &comment, nil
+}
+
+// IncrementReplyCount 增加回复数
+func (m *CommentModel) IncrementReplyCount(ctx context.Context, commentId int64, increment int) error {
+	err := m.conn.WithContext(ctx).Model(&Comment{}).
+		Where("id = ?", commentId).
+		UpdateColumn("reply_count", gorm.Expr("reply_count + ?", increment)).Error
+	if err != nil {
+		return err
+	}
+
+	// 删除评论缓存
+	key := fmt.Sprintf(prefixCommentKey, commentId)
+	m.redis.Del(ctx, key)
+	return nil
+}
+
+// Delete 删除评论
+func (m *CommentModel) Delete(ctx context.Context, commentId int64) error {
+	return m.conn.WithContext(ctx).Model(&Comment{}).
+		Where("id = ?", commentId).
+		Updates(map[string]interface{}{
+			"is_del":      1,
+			"deleted_on":  time.Now().Unix(),
+			"modified_on": time.Now().Unix(),
+		}).Error
+}

+ 14 - 0
internal/model/comment_upvote_model.go

@@ -0,0 +1,14 @@
+package model
+
+// CommentUpvote 评论/回复点赞
+type CommentUpvote struct {
+	*Model
+	CommentId  int64 `json:"comment_id" gorm:"comment_id"`   // comment ID
+	UserId     int64 `json:"user_id" gorm:"user_id"`         // 用户ID
+	ActionType int32 `json:"action_type" gorm:"action_type"` // 对操作的类型0-评论1-回复
+}
+
+// TableName 表名称
+func (*CommentUpvote) TableName() string {
+	return "p_comment_upvote"
+}

+ 78 - 0
internal/model/message_model.go

@@ -0,0 +1,78 @@
+package model
+
+import (
+	"context"
+	"time"
+
+	"gorm.io/gorm"
+)
+
+// PMessage 消息通知
+type Message struct {
+	ID             int64  `json:"id" gorm:"id"`                             // 消息通知ID
+	SenderUserId   int64  `json:"sender_user_id" gorm:"sender_user_id"`     // 发送方用户ID
+	ReceiverUserId int64  `json:"receiver_user_id" gorm:"receiver_user_id"` // 接收方用户ID
+	Type           int8   `json:"type" gorm:"type"`                         // 通知类型,1动态,2评论,3回复,4私信,99系统通知
+	Brief          string `json:"brief" gorm:"brief"`                       // 摘要说明
+	Content        string `json:"content" gorm:"content"`                   // 详细内容
+	PostId         int64  `json:"post_id" gorm:"post_id"`                   // 动态ID
+	CommentId      int64  `json:"comment_id" gorm:"comment_id"`             // 评论ID
+	ReplyId        int64  `json:"reply_id" gorm:"reply_id"`                 // 回复ID
+	IsRead         int8   `json:"is_read" gorm:"is_read"`                   // 是否已读
+	CreatedOn      int64  `json:"created_on" gorm:"created_on"`             // 创建时间
+	ModifiedOn     int64  `json:"modified_on" gorm:"modified_on"`           // 修改时间
+	DeletedOn      int64  `json:"deleted_on" gorm:"deleted_on"`             // 删除时间
+	IsDel          int8   `json:"is_del" gorm:"is_del"`                     // 是否删除 0 为未删除、1 为已删除
+}
+
+// TableName 表名称
+func (*Message) TableName() string {
+	return "p_message"
+}
+
+// MessageModel 消息模型
+type MessageModel struct {
+	conn *gorm.DB
+}
+
+func NewMessageModel(conn *gorm.DB) *MessageModel {
+	return &MessageModel{
+		conn: conn,
+	}
+}
+
+// SendMessage 发送站内信
+func (m *MessageModel) SendMessage(ctx context.Context, senderId, receiverId int64, postId int64, content string) error {
+	message := &Message{
+		SenderUserId:   senderId,
+		ReceiverUserId: receiverId,
+		Type:           1, // 假设1表示动态通知
+		Brief:          "你被艾特了",
+		Content:        content,
+		PostId:         postId,
+		IsRead:         0,
+		CreatedOn:      time.Now().Unix(),
+		ModifiedOn:     time.Now().Unix(),
+		IsDel:          0,
+	}
+	return m.conn.WithContext(ctx).Create(message).Error
+}
+
+// SendNotification 发送站内信
+func (m *MessageModel) SendNotification(ctx context.Context, senderId, receiverId int64, messageType int8, brief, content string, postId, commentId, replyId int64) error {
+	message := &Message{
+		SenderUserId:   senderId,
+		ReceiverUserId: receiverId,
+		Type:           messageType,
+		Brief:          brief,
+		Content:        content,
+		PostId:         postId,
+		CommentId:      commentId,
+		ReplyId:        replyId,
+		IsRead:         0,
+		CreatedOn:      time.Now().Unix(),
+		ModifiedOn:     time.Now().Unix(),
+		IsDel:          0,
+	}
+	return m.conn.WithContext(ctx).Create(message).Error
+}

+ 130 - 0
internal/model/post_action_model.go

@@ -0,0 +1,130 @@
+package model
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/go-redis/redis/v8"
+	"gorm.io/gorm"
+)
+
+// PPostAction 冒泡/文章动作
+type PostAction struct {
+	*Model
+	PostId     int64 `json:"post_id" gorm:"post_id"`         // POST ID
+	UserId     int64 `json:"user_id" gorm:"user_id"`         // 用户ID
+	ActionType int8  `json:"action_type" gorm:"action_type"` // 对帖子的动作0-点赞1-收藏2-分享
+}
+
+// TableName 表名称
+func (*PostAction) TableName() string {
+	return "p_post_action"
+}
+
+// MessageModel 消息模型
+type PostActionModel struct {
+	conn  *gorm.DB
+	redis *redis.Client
+}
+
+func NewPostActionModel(conn *gorm.DB, rConn *redis.Client) *PostActionModel {
+	return &PostActionModel{
+		conn:  conn,
+		redis: rConn,
+	}
+}
+
+func (p *PostActionModel) CreatePostAction(c context.Context, data *PostAction) error {
+	return p.conn.WithContext(c).Create(data).Error
+}
+
+func (p *PostActionModel) DeletePostAction(c context.Context, data *PostAction) error {
+	return p.conn.WithContext(c).Updates(data).Error
+}
+
+// CheckPostAction 检查用户是否对帖子执行过某个动作
+func (p *PostActionModel) CheckPostAction(ctx context.Context, userId, postId int64, actionType int8) (bool, error) {
+	var count int64
+	err := p.conn.WithContext(ctx).Model(&PostAction{}).
+		Where("user_id = ? AND post_id = ? AND action_type = ? AND is_del = 0", userId, postId, actionType).
+		Count(&count).Error
+	if err != nil {
+		return false, err
+	}
+	return count > 0, nil
+}
+
+const (
+	prefixUserCollectionKey = "user:collection:%d:%d:%d" // userId:page:pageSize
+)
+
+// GetUserCollectionPosts 获取用户收藏的帖子列表(带缓存)
+func (p *PostActionModel) GetUserCollectionPosts(ctx context.Context, userId int64, page, pageSize int) ([]*Post, int64, error) {
+	// 尝试从缓存获取
+	key := fmt.Sprintf(prefixUserCollectionKey, userId, page, pageSize)
+	data, err := p.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 = p.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 = p.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 {
+		p.redis.Set(ctx, key, data, 5*time.Minute)
+	}
+
+	return posts, total, nil
+}
+
+// ClearUserCollectionCache 清除用户收藏列表缓存
+func (p *PostActionModel) ClearUserCollectionCache(ctx context.Context, userId int64) {
+	pattern := fmt.Sprintf(prefixUserCollectionKey[:strings.LastIndex(prefixUserCollectionKey, ":")]+"*", userId)
+	keys, err := p.redis.Keys(ctx, pattern).Result()
+	if err != nil {
+		return
+	}
+	if len(keys) > 0 {
+		p.redis.Del(ctx, keys...)
+	}
+}

+ 20 - 0
internal/model/post_content_model.go

@@ -0,0 +1,20 @@
+package model
+
+// PPostContent 冒泡/文章内容
+type PostContent struct {
+	*Model
+	PostId         int64  `json:"post_id" gorm:"post_id"`                   // POST ID
+	UserId         int64  `json:"user_id" gorm:"user_id"`                   // 用户ID
+	Title          string `json:"title" gorm:"title"`                       // 帖子标题
+	Content        string `json:"content" gorm:"content"`                   // 内容
+	ContentSummary string `json:"content_summary" gorm:"content_summary"`   // 内容简要
+	PostVideoCover string `json:"post_video_cover" gorm:"post_video_cover"` // 帖子视频封面
+	PostCovers     string `json:"post_covers" gorm:"post_covers"`           // 帖子图片地址
+	PostVideoUrl   string `json:"post_video_url" gorm:"post_video_url"`     // 帖子视频地址
+	Sort           int64  `json:"sort" gorm:"sort"`                         // 排序,越小越靠前
+}
+
+// TableName 表名称
+func (*PostContent) TableName() string {
+	return "p_post_content"
+}

+ 567 - 0
internal/model/post_model.go

@@ -0,0 +1,567 @@
+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
+}

+ 89 - 0
internal/model/reply_model.go

@@ -0,0 +1,89 @@
+package model
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"github.com/go-redis/redis/v8"
+	"gorm.io/gorm"
+)
+
+// CommentReply 评论回复
+type CommentReply struct {
+	*Model
+	PostId        int64  `json:"post_id" gorm:"post_id"`                 // POST ID
+	UserId        int64  `json:"user_id" gorm:"user_id"`                 // 用户ID
+	CommentId     int64  `json:"comment_id" gorm:"comment_id"`           // 评论ID
+	Ip            string `json:"ip" gorm:"ip"`                           // IP地址
+	IpLoc         string `json:"ip_loc" gorm:"ip_loc"`                   // IP城市地址
+	Content       string `json:"content" gorm:"content"`                 // 评论内容
+	Image         string `json:"image" gorm:"image"`                     // 评论图片
+	WithUserIds   string `json:"with_user_ids" gorm:"with_user_ids"`     // 被艾特的用户
+	IsEssence     int8   `json:"is_essence" gorm:"is_essence"`           // 是否精选
+	ReplyCount    int64  `json:"reply_count" gorm:"reply_count"`         // 回复数
+	ThumbsUpCount int64  `json:"thumbs_up_count" gorm:"thumbs_up_count"` // 点赞数
+	ReplyId       int64  `json:"reply_id" gorm:"reply_id"`               // 回复的id
+}
+
+// TableName 表名称
+func (*CommentReply) TableName() string {
+	return "p_comment_reply"
+}
+
+type ReplyModel struct {
+	conn  *gorm.DB
+	redis *redis.Client
+}
+
+func NewReplyModel(conn *gorm.DB, rdb *redis.Client) ReplyModel {
+	return ReplyModel{
+		conn:  conn,
+		redis: rdb,
+	}
+}
+
+// Create 创建回复
+func (m *ReplyModel) Create(ctx context.Context, reply *CommentReply) error {
+	err := m.conn.WithContext(ctx).Create(reply).Error
+	if err != nil {
+		return err
+	}
+
+	// 删除回复列表缓存
+	m.ClearReplyListCache(ctx, reply.CommentId)
+	return nil
+}
+
+// ClearReplyListCache 清除回复列表缓存
+func (m *ReplyModel) ClearReplyListCache(ctx context.Context, commentId int64) {
+	pattern := fmt.Sprintf("reply:list:%d*", commentId)
+	keys, err := m.redis.Keys(ctx, pattern).Result()
+	if err != nil {
+		return
+	}
+	if len(keys) > 0 {
+		m.redis.Del(ctx, keys...)
+	}
+}
+
+// GetReply 获取回复详情
+func (m *ReplyModel) GetReply(ctx context.Context, replyId int64) (*CommentReply, error) {
+	var reply CommentReply
+	err := m.conn.WithContext(ctx).Where("id = ?", replyId).First(&reply).Error
+	if err != nil {
+		return nil, err
+	}
+	return &reply, nil
+}
+
+// Delete 删除回复
+func (m *ReplyModel) Delete(ctx context.Context, replyId int64) error {
+	return m.conn.WithContext(ctx).Model(&CommentReply{}).
+		Where("id = ?", replyId).
+		Updates(map[string]interface{}{
+			"is_del":      1,
+			"deleted_on":  time.Now().Unix(),
+			"modified_on": time.Now().Unix(),
+		}).Error
+}

+ 193 - 0
internal/model/tag_model.go

@@ -0,0 +1,193 @@
+package model
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"time"
+
+	"github.com/go-redis/redis/v8"
+	"gorm.io/gorm"
+)
+
+// PTag 话题表
+type Tag struct {
+	*Model
+	UserId int64  `json:"user_id" gorm:"user_id"` // 用户id
+	Name   string `json:"name" gorm:"name"`       // 标题昵称
+	HotNum int64  `json:"hot_num" gorm:"hot_num"` // 热度值
+}
+
+// TableName 表名称
+func (*Tag) TableName() string {
+	return "p_tag"
+}
+
+type TagModel struct {
+	conn  *gorm.DB
+	redis *redis.Client
+}
+
+func NewTagModel(conn *gorm.DB, rdb *redis.Client) *TagModel {
+	return &TagModel{
+		conn:  conn,
+		redis: rdb,
+	}
+}
+
+// FindByIds 批量查询话题
+func (m *TagModel) FindByIds(ctx context.Context, ids []int64) ([]*Tag, error) {
+	var tags []*Tag
+	err := m.conn.WithContext(ctx).Where("id IN ?", ids).Find(&tags).Error
+	return tags, err
+}
+
+// BatchCreate 批量创建话题
+func (m *TagModel) BatchCreate(ctx context.Context, tags []*Tag) error {
+	return m.conn.WithContext(ctx).Create(&tags).Error
+}
+
+// CreateTagWithPost 创建话题与帖子的关联
+func (m *TagModel) CreateTagWithPost(ctx context.Context, postId int64, tagIds []int64) error {
+	var relations []*TagWithPost
+	for _, tagId := range tagIds {
+		relations = append(relations, &TagWithPost{
+			Model: &Model{
+				CreatedOn:  time.Now().Unix(),
+				ModifiedOn: time.Now().Unix(),
+			},
+			PostId: postId,
+			TagId:  tagId,
+		})
+	}
+	return m.conn.WithContext(ctx).Create(&relations).Error
+}
+
+// FindByNames 批量查询话题
+func (m *TagModel) FindByNames(ctx context.Context, names []string) ([]*Tag, error) {
+	var tags []*Tag
+	err := m.conn.WithContext(ctx).Where("name IN ?", names).Find(&tags).Error
+	return tags, err
+}
+
+const (
+	prefixTagKey     = "tag:%d"               // tagId
+	prefixTagListKey = "tag:list:%d:%d:%d:%s" // page:pageSize:orderBy:keyword
+)
+
+// GetTag 获取话题详情(带缓存)
+func (m *TagModel) GetTag(ctx context.Context, tagId int64) (*Tag, error) {
+	// 尝试从缓存获取
+	key := fmt.Sprintf(prefixTagKey, tagId)
+	data, err := m.redis.Get(ctx, key).Bytes()
+	if err == nil {
+		var tag Tag
+		if err := json.Unmarshal(data, &tag); err == nil {
+			return &tag, nil
+		}
+	}
+
+	// 从数据库获取
+	var tag Tag
+	err = m.conn.WithContext(ctx).First(&tag, tagId).Error
+	if err != nil {
+		return nil, err
+	}
+
+	// 写入缓存
+	if data, err := json.Marshal(tag); err == nil {
+		m.redis.Set(ctx, key, data, cacheExpiry)
+	}
+
+	return &tag, nil
+}
+
+// GetTagStats 获取话题统计信息
+func (m *TagModel) GetTagStats(ctx context.Context, tagId int64) (postCount, followCount int64, err error) {
+	// 获取帖子数
+	err = m.conn.WithContext(ctx).Model(&Post{}).
+		Where("FIND_IN_SET(?, tags)", tagId).
+		Count(&postCount).Error
+	if err != nil {
+		return 0, 0, err
+	}
+
+	// 获取关注数
+	err = m.conn.WithContext(ctx).Model(&UserFollow{}).
+		Where("tag_id = ? AND is_del = 0", tagId).
+		Count(&followCount).Error
+	if err != nil {
+		return 0, 0, err
+	}
+
+	return postCount, followCount, nil
+}
+
+// IsTagFollowedByUser 检查用户是否关注了话题
+func (m *TagModel) IsTagFollowedByUser(ctx context.Context, userId, tagId int64) (bool, error) {
+	var count int64
+	err := m.conn.WithContext(ctx).Model(&UserFollow{}).
+		Where("user_id = ? AND tag_id = ? AND is_del = 0", userId, tagId).
+		Count(&count).Error
+	return count > 0, err
+}
+
+// GetTagList 获取话题列表(带缓存)
+func (m *TagModel) GetTagList(ctx context.Context, page, pageSize int, orderBy int32, keyword string) ([]*Tag, int64, error) {
+	// 尝试从缓存获取
+	key := fmt.Sprintf(prefixTagListKey, page, pageSize, orderBy, keyword)
+	data, err := m.redis.Get(ctx, key).Bytes()
+	if err == nil {
+		var result struct {
+			Tags  []*Tag
+			Total int64
+		}
+		if err := json.Unmarshal(data, &result); err == nil {
+			return result.Tags, result.Total, nil
+		}
+	}
+
+	// 构建查询
+	query := m.conn.WithContext(ctx).Model(&Tag{})
+	if keyword != "" {
+		query = query.Where("name LIKE ?", "%"+keyword+"%")
+	}
+
+	// 获取总数
+	var total int64
+	err = query.Count(&total).Error
+	if err != nil {
+		return nil, 0, err
+	}
+
+	// 获取列表
+	var tags []*Tag
+	offset := (page - 1) * pageSize
+
+	// 根据排序方式查询
+	switch orderBy {
+	case 1:
+		query = query.Order("hot_num DESC")
+	default:
+		query = query.Order("created_on DESC")
+	}
+
+	err = query.Offset(offset).Limit(pageSize).Find(&tags).Error
+	if err != nil {
+		return nil, 0, err
+	}
+
+	// 写入缓存
+	result := struct {
+		Tags  []*Tag
+		Total int64
+	}{
+		Tags:  tags,
+		Total: total,
+	}
+	if data, err := json.Marshal(result); err == nil {
+		m.redis.Set(ctx, key, data, 5*time.Minute) // 列表缓存时间较短
+	}
+
+	return tags, total, nil
+}

+ 13 - 0
internal/model/tag_with_post_model.go

@@ -0,0 +1,13 @@
+package model
+
+// PTagWithPost 话题关联帖子表
+type TagWithPost struct {
+	*Model
+	PostId int64 `json:"post_id" gorm:"post_id"` // 帖子id
+	TagId  int64 `json:"tag_id" gorm:"tag_id"`   // 话题id
+}
+
+// TableName 表名称
+func (*TagWithPost) TableName() string {
+	return "p_tag_with_post"
+}

+ 2 - 2
internal/model/user_follow.go

@@ -20,8 +20,8 @@ type UserFollowModel struct {
 	conn *gorm.DB
 	conn *gorm.DB
 }
 }
 
 
-func NewUserFollowModel(conn *gorm.DB) UserFollowModel {
-	return UserFollowModel{
+func NewUserFollowModel(conn *gorm.DB) *UserFollowModel {
+	return &UserFollowModel{
 		conn: conn,
 		conn: conn,
 	}
 	}
 }
 }

+ 17 - 0
internal/model/user_follow_tag_model.go

@@ -0,0 +1,17 @@
+package model
+
+// PUserFollowTag 用户关注标签
+type UserFollowTag struct {
+	ID         int64 `json:"id" gorm:"id"`
+	UserId     int64 `json:"user_id" gorm:"user_id"`         // 用户id
+	TagId      int64 `json:"tag_id" gorm:"tag_id"`           // 被关注的标签id
+	CreatedOn  int64 `json:"created_on" gorm:"created_on"`   // 创建时间
+	ModifiedOn int64 `json:"modified_on" gorm:"modified_on"` // 修改时间
+	DeletedOn  int64 `json:"deleted_on" gorm:"deleted_on"`   // 删除时间
+	IsDel      int8  `json:"is_del" gorm:"is_del"`           // 是否删除 0 为未删除、1 为已删除
+}
+
+// TableName 表名称
+func (*UserFollowTag) TableName() string {
+	return "p_user_follow_tag"
+}

+ 47 - 2
internal/model/user_model.go

@@ -90,6 +90,15 @@ func (m *UserModel) UpdateLoginIP(ctx context.Context, userId int64, ip string)
 		Update("last_login_ip", ip).Error
 		Update("last_login_ip", ip).Error
 }
 }
 
 
+// FindOne 获取一条用户信息
+func (m *UserModel) FindOne(ctx context.Context, userId int64) (*User, error) {
+	var userInfo *User
+	err := m.conn.WithContext(ctx).Model(&User{}).
+		Where("id = ?", userId).
+		First(&userInfo).Error
+	return userInfo, err
+}
+
 // Create 创建用户
 // Create 创建用户
 func (m *UserModel) Create(ctx context.Context, user *User) error {
 func (m *UserModel) Create(ctx context.Context, user *User) error {
 	return m.conn.WithContext(ctx).Create(user).Error
 	return m.conn.WithContext(ctx).Create(user).Error
@@ -145,19 +154,55 @@ func (m *UserModel) IncrementFollowCount(ctx context.Context, userId int64) erro
 		UpdateColumn("follow_count", gorm.Expr("follow_count + ?", 1)).Error
 		UpdateColumn("follow_count", gorm.Expr("follow_count + ?", 1)).Error
 }
 }
 
 
+// IncrementTweetCount 增加帖子数
+func (m *UserModel) IncrementTweetCount(ctx context.Context, userId int64) error {
+	return m.conn.WithContext(ctx).
+		Model(&User{}).
+		Where("id = ?", userId).
+		UpdateColumn("tweet_count", gorm.Expr("tweet_count + ?", 1)).Error
+}
+
+// DecrementTweetCount 减少帖子数
+func (m *UserModel) DecrementTweetCount(ctx context.Context, userId int64) error {
+	return m.conn.WithContext(ctx).
+		Model(&User{}).
+		Where("id = ?", userId).
+		UpdateColumn("tweet_count", gorm.Expr("tweet_count - ?", 1)).Error
+}
+
 // Transaction 执行数据库事务
 // Transaction 执行数据库事务
 func (m *UserModel) Transaction(ctx context.Context, fn func(tx *gorm.DB) error) error {
 func (m *UserModel) Transaction(ctx context.Context, fn func(tx *gorm.DB) error) error {
 	return m.conn.WithContext(ctx).Transaction(fn)
 	return m.conn.WithContext(ctx).Transaction(fn)
 }
 }
 
 
+// IsPostLikedByUser 检查用户是否点赞了帖子
+func (m *UserModel) IsPostLikedByUser(ctx context.Context, userId, postId int64) (bool, error) {
+	var count int64
+	err := m.conn.WithContext(ctx).
+		Model(&PostAction{}).
+		Where("user_id = ? AND post_id = ? and type = 0", userId, postId).
+		Count(&count).Error
+	return count > 0, err
+}
+
+// IsPostCollectedByUser 检查用户是否收藏了帖子
+func (m *UserModel) IsPostCollectedByUser(ctx context.Context, userId, postId int64) (bool, error) {
+	var count int64
+	err := m.conn.WithContext(ctx).
+		Model(&PostAction{}).
+		Where("user_id = ? AND post_id = ? and type = 1", userId, postId).
+		Count(&count).Error
+	return count > 0, err
+}
+
 type UserModel struct {
 type UserModel struct {
 	conn *gorm.DB
 	conn *gorm.DB
 }
 }
 
 
 var ErrNotFound = gorm.ErrRecordNotFound
 var ErrNotFound = gorm.ErrRecordNotFound
 
 
-func NewUserModel(conn *gorm.DB) UserModel {
-	return UserModel{
+func NewUserModel(conn *gorm.DB) *UserModel {
+	return &UserModel{
 		conn: conn,
 		conn: conn,
 	}
 	}
 }
 }

+ 110 - 1
internal/server/slowwildserverserver.go

@@ -5,9 +5,10 @@ package server
 
 
 import (
 import (
 	"context"
 	"context"
+	"slowwild/internal/svc"
 	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
 	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
 	"slowwild/internal/logic"
 	"slowwild/internal/logic"
-	"slowwild/internal/svc"
+	
 )
 )
 
 
 type SlowWildServerServer struct {
 type SlowWildServerServer struct {
@@ -80,3 +81,111 @@ func (s *SlowWildServerServer) SearchUsername(ctx context.Context, in *slowwilds
 	l := logic.NewSearchUsernameLogic(ctx, s.svcCtx)
 	l := logic.NewSearchUsernameLogic(ctx, s.svcCtx)
 	return l.SearchUsername(in)
 	return l.SearchUsername(in)
 }
 }
+
+// 发布帖子
+func (s *SlowWildServerServer) CreatePost(ctx context.Context, in *slowwildserver.CreatePostReq) (*slowwildserver.CreatePostRsp, error) {
+	l := logic.NewCreatePostLogic(ctx, s.svcCtx)
+	return l.CreatePost(in)
+}
+
+// 获取话题列表
+func (s *SlowWildServerServer) GetTagList(ctx context.Context, in *slowwildserver.GetTagListReq) (*slowwildserver.GetTagListRsp, error) {
+	l := logic.NewGetTagListLogic(ctx, s.svcCtx)
+	return l.GetTagList(in)
+}
+
+// 获取话题详情
+func (s *SlowWildServerServer) GetTag(ctx context.Context, in *slowwildserver.GetTagReq) (*slowwildserver.GetTagRsp, error) {
+	l := logic.NewGetTagLogic(ctx, s.svcCtx)
+	return l.GetTag(in)
+}
+
+// 获取帖子列表
+func (s *SlowWildServerServer) GetPostList(ctx context.Context, in *slowwildserver.GetPostListReq) (*slowwildserver.GetPostListRsp, error) {
+	l := logic.NewGetPostListLogic(ctx, s.svcCtx)
+	return l.GetPostList(in)
+}
+
+// 获取帖子详情
+func (s *SlowWildServerServer) GetPost(ctx context.Context, in *slowwildserver.GetPostReq) (*slowwildserver.GetPostRsp, error) {
+	l := logic.NewGetPostLogic(ctx, s.svcCtx)
+	return l.GetPost(in)
+}
+
+// 点赞帖子
+func (s *SlowWildServerServer) PostUpvote(ctx context.Context, in *slowwildserver.PostUpvoteReq) (*slowwildserver.PostUpvoteRsp, error) {
+	l := logic.NewPostUpvoteLogic(ctx, s.svcCtx)
+	return l.PostUpvote(in)
+}
+
+// 分享帖子
+func (s *SlowWildServerServer) PostShare(ctx context.Context, in *slowwildserver.PostShareReq) (*slowwildserver.PostShareRsp, error) {
+	l := logic.NewPostShareLogic(ctx, s.svcCtx)
+	return l.PostShare(in)
+}
+
+// 收藏帖子
+func (s *SlowWildServerServer) PostCollection(ctx context.Context, in *slowwildserver.PostCollectionReq) (*slowwildserver.PostCollectionRsp, error) {
+	l := logic.NewPostCollectionLogic(ctx, s.svcCtx)
+	return l.PostCollection(in)
+}
+
+// 发布评论
+func (s *SlowWildServerServer) PostComment(ctx context.Context, in *slowwildserver.PostCommentReq) (*slowwildserver.PostCommentRsp, error) {
+	l := logic.NewPostCommentLogic(ctx, s.svcCtx)
+	return l.PostComment(in)
+}
+
+// 发布回复
+func (s *SlowWildServerServer) PostReply(ctx context.Context, in *slowwildserver.PostReplyReq) (*slowwildserver.PostReplyRsp, error) {
+	l := logic.NewPostReplyLogic(ctx, s.svcCtx)
+	return l.PostReply(in)
+}
+
+// 获取评论列表
+func (s *SlowWildServerServer) GetPostCommentList(ctx context.Context, in *slowwildserver.GetPostCommentListReq) (*slowwildserver.GetPostCommentListRsp, error) {
+	l := logic.NewGetPostCommentListLogic(ctx, s.svcCtx)
+	return l.GetPostCommentList(in)
+}
+
+// 获取回复列表
+func (s *SlowWildServerServer) GetReplyList(ctx context.Context, in *slowwildserver.GetReplyListReq) (*slowwildserver.GetReplyListRsp, error) {
+	l := logic.NewGetReplyListLogic(ctx, s.svcCtx)
+	return l.GetReplyList(in)
+}
+
+// 评论点赞
+func (s *SlowWildServerServer) PostCommentUpvote(ctx context.Context, in *slowwildserver.PostCommentUpvoteReq) (*slowwildserver.PostCommentUpvoteRsp, error) {
+	l := logic.NewPostCommentUpvoteLogic(ctx, s.svcCtx)
+	return l.PostCommentUpvote(in)
+}
+
+// 删除帖子
+func (s *SlowWildServerServer) PostDelete(ctx context.Context, in *slowwildserver.PostDeleteReq) (*slowwildserver.PostDeleteRsp, error) {
+	l := logic.NewPostDeleteLogic(ctx, s.svcCtx)
+	return l.PostDelete(in)
+}
+
+// 删除回复/评论
+func (s *SlowWildServerServer) CommentDelete(ctx context.Context, in *slowwildserver.CommentDeleteReq) (*slowwildserver.CommentDeleteRsp, error) {
+	l := logic.NewCommentDeleteLogic(ctx, s.svcCtx)
+	return l.CommentDelete(in)
+}
+
+// 获取用户发布的帖子
+func (s *SlowWildServerServer) GetUserPostList(ctx context.Context, in *slowwildserver.GetUserPostListReq) (*slowwildserver.GetUserPostListRsp, error) {
+	l := logic.NewGetUserPostListLogic(ctx, s.svcCtx)
+	return l.GetUserPostList(in)
+}
+
+// 获取用户收藏的帖子
+func (s *SlowWildServerServer) GetUserPostCollectionList(ctx context.Context, in *slowwildserver.GetUserPostCollectionListReq) (*slowwildserver.GetUserPostCollectionListRsp, error) {
+	l := logic.NewGetUserPostCollectionListLogic(ctx, s.svcCtx)
+	return l.GetUserPostCollectionList(in)
+}
+
+// 获取用户点赞过的帖子
+func (s *SlowWildServerServer) GetUserPostLikeList(ctx context.Context, in *slowwildserver.GetUserPostLikeListReq) (*slowwildserver.GetUserPostLikeListRsp, error) {
+	l := logic.NewGetUserPostLikeListLogic(ctx, s.svcCtx)
+	return l.GetUserPostLikeList(in)
+}

+ 43 - 6
internal/svc/servicecontext.go

@@ -1,21 +1,51 @@
 package svc
 package svc
 
 
 import (
 import (
+	"context"
+	"fmt"
 	"slowwild/internal/config"
 	"slowwild/internal/config"
 	"slowwild/internal/model"
 	"slowwild/internal/model"
+	"time"
 
 
+	"github.com/go-redis/redis/v8"
 	"gorm.io/driver/mysql"
 	"gorm.io/driver/mysql"
 	"gorm.io/gorm"
 	"gorm.io/gorm"
 	"gorm.io/gorm/schema"
 	"gorm.io/gorm/schema"
 )
 )
 
 
 type ServiceContext struct {
 type ServiceContext struct {
-	Config          config.Config
-	UserModel       model.UserModel
-	UserFollowModel model.UserFollowModel
+	Config           config.Config
+	Redis            *redis.Client
+	UserModel        *model.UserModel
+	UserFollowModel  *model.UserFollowModel
+	PostModel        *model.PostModel
+	PostContentModel *model.PostContentModel
+	TagModel         *model.TagModel
+	MessageModel     *model.MessageModel
+	CommentModel     *model.CommentModel
+	PostActionModel  *model.PostActionModel
 }
 }
 
 
 func NewServiceContext(c config.Config) *ServiceContext {
 func NewServiceContext(c config.Config) *ServiceContext {
+	// 初始化Redis客户端
+	rdb := redis.NewClient(&redis.Options{
+		Addr:         c.RedisConf.Host,
+		Password:     c.RedisConf.Password,
+		DB:           c.RedisConf.DB,
+		PoolSize:     100,             // 连接池大小
+		MinIdleConns: 10,              // 最小空闲连接数
+		ReadTimeout:  2 * time.Second, // 读取超时
+		WriteTimeout: 2 * time.Second, // 写入超时
+		DialTimeout:  2 * time.Second, // 连接超时
+	})
+
+	// 测试Redis连接
+	ctx := context.Background()
+	_, err := rdb.Ping(ctx).Result()
+	if err != nil {
+		panic(fmt.Sprintf("Redis连接失败: %v", err))
+	}
+
 	db, err := gorm.Open(mysql.Open(c.DB.DataSource), &gorm.Config{
 	db, err := gorm.Open(mysql.Open(c.DB.DataSource), &gorm.Config{
 		NamingStrategy: schema.NamingStrategy{
 		NamingStrategy: schema.NamingStrategy{
 			TablePrefix:   "p_",
 			TablePrefix:   "p_",
@@ -27,8 +57,15 @@ func NewServiceContext(c config.Config) *ServiceContext {
 	}
 	}
 
 
 	return &ServiceContext{
 	return &ServiceContext{
-		Config:          c,
-		UserModel:       model.NewUserModel(db),
-		UserFollowModel: model.NewUserFollowModel(db),
+		Config:           c,
+		Redis:            rdb,
+		UserModel:        model.NewUserModel(db),
+		UserFollowModel:  model.NewUserFollowModel(db),
+		PostModel:        model.NewPostModel(db, rdb),
+		PostContentModel: model.NewPostContentModel(db),
+		TagModel:         model.NewTagModel(db, rdb),
+		MessageModel:     model.NewMessageModel(db),
+		CommentModel:     model.NewCommentModel(db, rdb),
+		PostActionModel:  model.NewPostActionModel(db, rdb),
 	}
 	}
 }
 }

+ 34 - 0
internal/utils/html.go

@@ -0,0 +1,34 @@
+package utils
+
+import (
+	"regexp"
+	"strings"
+)
+
+// StripHTML 去除HTML标签,保留纯文本
+func StripHTML(html string) string {
+	// 移除HTML标签
+	re := regexp.MustCompile(`<[^>]*>`)
+	text := re.ReplaceAllString(html, "")
+
+	// 替换多个空格为一个空格
+	text = regexp.MustCompile(`\s+`).ReplaceAllString(text, " ")
+
+	// 移除特殊字符
+	text = strings.ReplaceAll(text, "&nbsp;", " ")
+	text = strings.ReplaceAll(text, "&lt;", "<")
+	text = strings.ReplaceAll(text, "&gt;", ">")
+	text = strings.ReplaceAll(text, "&amp;", "&")
+	text = strings.ReplaceAll(text, "&quot;", "\"")
+
+	return strings.TrimSpace(text)
+}
+
+// TruncateText 截取指定长度的文本
+func TruncateText(text string, length int) string {
+	runeText := []rune(text)
+	if len(runeText) <= length {
+		return text
+	}
+	return string(runeText[:length])
+}

+ 210 - 22
slowwildserverclient/slowwildserver.go

@@ -5,33 +5,77 @@ package slowwildserverclient
 
 
 import (
 import (
 	"context"
 	"context"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+
 	"github.com/zeromicro/go-zero/zrpc"
 	"github.com/zeromicro/go-zero/zrpc"
 	"google.golang.org/grpc"
 	"google.golang.org/grpc"
-	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
 )
 )
 
 
 type (
 type (
-	FindUserReq         = slowwildserver.FindUserReq
-	FindUserResp        = slowwildserver.FindUserResp
-	FollowUserReq       = slowwildserver.FollowUserReq
-	FollowUserRes       = slowwildserver.FollowUserRes
-	GetFollowReq        = slowwildserver.GetFollowReq
-	GetFollowRes        = slowwildserver.GetFollowRes
-	GetFollowingReq     = slowwildserver.GetFollowingReq
-	GetFollowingRes     = slowwildserver.GetFollowingRes
-	GetUserInfoReq      = slowwildserver.GetUserInfoReq
-	GetUserInfoResp     = slowwildserver.GetUserInfoResp
-	GetUserProfileReq   = slowwildserver.GetUserProfileReq
-	GetUserProfileRes   = slowwildserver.GetUserProfileRes
-	LoginAndRegisterRsp = slowwildserver.LoginAndRegisterRsp
-	LoginReq            = slowwildserver.LoginReq
-	RegisterReq         = slowwildserver.RegisterReq
-	SearchUsernameItem  = slowwildserver.SearchUsernameItem
-	SearchUsernameReq   = slowwildserver.SearchUsernameReq
-	SearchUsernameRes   = slowwildserver.SearchUsernameRes
-	UnFollowUserReq     = slowwildserver.UnFollowUserReq
-	UnFollowUserRes     = slowwildserver.UnFollowUserRes
-	UserEntity          = slowwildserver.UserEntity
+	CommentItem              = slowwildserver.CommentItem
+	CreatePostReq            = slowwildserver.CreatePostReq
+	CreatePostRsp            = slowwildserver.CreatePostRsp
+	FindUserReq              = slowwildserver.FindUserReq
+	FindUserResp             = slowwildserver.FindUserResp
+	FollowUserInfo           = slowwildserver.FollowUserInfo
+	FollowUserReq            = slowwildserver.FollowUserReq
+	FollowUserRes            = slowwildserver.FollowUserRes
+	GetFollowReq             = slowwildserver.GetFollowReq
+	GetFollowRes             = slowwildserver.GetFollowRes
+	GetFollowingReq          = slowwildserver.GetFollowingReq
+	GetFollowingRes          = slowwildserver.GetFollowingRes
+	GetPostCommentListReq    = slowwildserver.GetPostCommentListReq
+	GetPostCommentListRsp    = slowwildserver.GetPostCommentListRsp
+	GetPostListItem          = slowwildserver.GetPostListItem
+	GetPostListReq           = slowwildserver.GetPostListReq
+	GetPostListRsp           = slowwildserver.GetPostListRsp
+	GetPostReq               = slowwildserver.GetPostReq
+	GetPostRsp               = slowwildserver.GetPostRsp
+	GetReplyListReq          = slowwildserver.GetReplyListReq
+	GetReplyListRsp          = slowwildserver.GetReplyListRsp
+	GetTagListReq            = slowwildserver.GetTagListReq
+	GetTagListRsp            = slowwildserver.GetTagListRsp
+	GetTagReq                = slowwildserver.GetTagReq
+	GetTagRsp                = slowwildserver.GetTagRsp
+	GetUserInfoReq           = slowwildserver.GetUserInfoReq
+	GetUserInfoResp          = slowwildserver.GetUserInfoResp
+	GetUserProfileReq        = slowwildserver.GetUserProfileReq
+	GetUserProfileRes        = slowwildserver.GetUserProfileRes
+	LoginAndRegisterRsp      = slowwildserver.LoginAndRegisterRsp
+	LoginReq                 = slowwildserver.LoginReq
+	PostCollectionReq        = slowwildserver.PostCollectionReq
+	PostCollectionRsp        = slowwildserver.PostCollectionRsp
+	PostCommentReq           = slowwildserver.PostCommentReq
+	PostCommentRsp           = slowwildserver.PostCommentRsp
+	PostCommentUpvoteReq     = slowwildserver.PostCommentUpvoteReq
+	PostCommentUpvoteRsp     = slowwildserver.PostCommentUpvoteRsp
+	PostReplyReq             = slowwildserver.PostReplyReq
+	PostReplyRsp             = slowwildserver.PostReplyRsp
+	PostShareReq             = slowwildserver.PostShareReq
+	PostShareRsp             = slowwildserver.PostShareRsp
+	PostUpvoteReq            = slowwildserver.PostUpvoteReq
+	PostUpvoteRsp            = slowwildserver.PostUpvoteRsp
+	RegisterReq              = slowwildserver.RegisterReq
+	RepliesItem              = slowwildserver.RepliesItem
+	SearchUsernameItem       = slowwildserver.SearchUsernameItem
+	SearchUsernameReq        = slowwildserver.SearchUsernameReq
+	SearchUsernameRes        = slowwildserver.SearchUsernameRes
+	TagItem                  = slowwildserver.TagItem
+	UnFollowUserReq          = slowwildserver.UnFollowUserReq
+	UnFollowUserRes          = slowwildserver.UnFollowUserRes
+	UserEntity               = slowwildserver.UserEntity
+	UserInfo                 = slowwildserver.UserInfo
+	PostDeleteReq         = slowwildserver.PostDeleteReq
+	PostDeleteRsp         = slowwildserver.PostDeleteRsp
+	CommentDeleteReq      = slowwildserver.CommentDeleteReq
+	CommentDeleteRsp      = slowwildserver.CommentDeleteRsp
+	GetUserPostCollectionListReq = slowwildserver.GetUserPostCollectionListReq
+	GetUserPostCollectionListRsp = slowwildserver.GetUserPostCollectionListRsp
+	GetUserPostLikeListReq       = slowwildserver.GetUserPostLikeListReq
+	GetUserPostLikeListRsp       = slowwildserver.GetUserPostLikeListRsp
+	GetUserPostListReq           = slowwildserver.GetUserPostListReq
+	GetUserPostListRsp           = slowwildserver.GetUserPostListRsp
 
 
 	SlowWildServer interface {
 	SlowWildServer interface {
 		// 用户登录
 		// 用户登录
@@ -54,6 +98,42 @@ type (
 		GetUserProfile(ctx context.Context, in *GetUserProfileReq, opts ...grpc.CallOption) (*GetUserProfileRes, error)
 		GetUserProfile(ctx context.Context, in *GetUserProfileReq, opts ...grpc.CallOption) (*GetUserProfileRes, error)
 		// 搜索用户名称
 		// 搜索用户名称
 		SearchUsername(ctx context.Context, in *SearchUsernameReq, opts ...grpc.CallOption) (*SearchUsernameRes, error)
 		SearchUsername(ctx context.Context, in *SearchUsernameReq, opts ...grpc.CallOption) (*SearchUsernameRes, error)
+		// 发布帖子
+		CreatePost(ctx context.Context, in *CreatePostReq, opts ...grpc.CallOption) (*CreatePostRsp, error)
+		// 获取话题列表
+		GetTagList(ctx context.Context, in *GetTagListReq, opts ...grpc.CallOption) (*GetTagListRsp, error)
+		// 获取话题详情
+		GetTag(ctx context.Context, in *GetTagReq, opts ...grpc.CallOption) (*GetTagRsp, error)
+		// 获取帖子列表
+		GetPostList(ctx context.Context, in *GetPostListReq, opts ...grpc.CallOption) (*GetPostListRsp, error)
+		// 获取帖子详情
+		GetPost(ctx context.Context, in *GetPostReq, opts ...grpc.CallOption) (*GetPostRsp, error)
+		// 点赞帖子
+		PostUpvote(ctx context.Context, in *PostUpvoteReq, opts ...grpc.CallOption) (*PostUpvoteRsp, error)
+		// 分享帖子
+		PostShare(ctx context.Context, in *PostShareReq, opts ...grpc.CallOption) (*PostShareRsp, error)
+		// 收藏帖子
+		PostCollection(ctx context.Context, in *PostCollectionReq, opts ...grpc.CallOption) (*PostCollectionRsp, error)
+		// 发布评论
+		PostComment(ctx context.Context, in *PostCommentReq, opts ...grpc.CallOption) (*PostCommentRsp, error)
+		// 发布回复
+		PostReply(ctx context.Context, in *PostReplyReq, opts ...grpc.CallOption) (*PostReplyRsp, error)
+		// 获取评论列表
+		GetPostCommentList(ctx context.Context, in *GetPostCommentListReq, opts ...grpc.CallOption) (*GetPostCommentListRsp, error)
+		// 获取回复列表
+		GetReplyList(ctx context.Context, in *GetReplyListReq, opts ...grpc.CallOption) (*GetReplyListRsp, error)
+		// 评论点赞
+		PostCommentUpvote(ctx context.Context, in *PostCommentUpvoteReq, opts ...grpc.CallOption) (*PostCommentUpvoteRsp, error)
+		// 删除帖子
+		PostDelete(ctx context.Context, in *PostDeleteReq, opts ...grpc.CallOption) (*PostDeleteRsp, error)
+		// 删除回复/评论
+		CommentDelete(ctx context.Context, in *CommentDeleteReq, opts ...grpc.CallOption) (*CommentDeleteRsp, error)
+		// 获取用户发布的帖子
+		GetUserPostList(ctx context.Context, in *GetUserPostListReq, opts ...grpc.CallOption) (*GetUserPostListRsp, error)
+		// 获取用户收藏的帖子
+		GetUserPostCollectionList(ctx context.Context, in *GetUserPostCollectionListReq, opts ...grpc.CallOption) (*GetUserPostCollectionListRsp, error)
+		// 获取用户点赞过的帖子
+		GetUserPostLikeList(ctx context.Context, in *GetUserPostLikeListReq, opts ...grpc.CallOption) (*GetUserPostLikeListRsp, error)
 	}
 	}
 
 
 	defaultSlowWildServer struct {
 	defaultSlowWildServer struct {
@@ -126,3 +206,111 @@ func (m *defaultSlowWildServer) SearchUsername(ctx context.Context, in *SearchUs
 	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
 	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
 	return client.SearchUsername(ctx, in, opts...)
 	return client.SearchUsername(ctx, in, opts...)
 }
 }
+
+// 发布帖子
+func (m *defaultSlowWildServer) CreatePost(ctx context.Context, in *CreatePostReq, opts ...grpc.CallOption) (*CreatePostRsp, error) {
+	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
+	return client.CreatePost(ctx, in, opts...)
+}
+
+// 获取话题列表
+func (m *defaultSlowWildServer) GetTagList(ctx context.Context, in *GetTagListReq, opts ...grpc.CallOption) (*GetTagListRsp, error) {
+	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
+	return client.GetTagList(ctx, in, opts...)
+}
+
+// 获取话题详情
+func (m *defaultSlowWildServer) GetTag(ctx context.Context, in *GetTagReq, opts ...grpc.CallOption) (*GetTagRsp, error) {
+	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
+	return client.GetTag(ctx, in, opts...)
+}
+
+// 获取帖子列表
+func (m *defaultSlowWildServer) GetPostList(ctx context.Context, in *GetPostListReq, opts ...grpc.CallOption) (*GetPostListRsp, error) {
+	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
+	return client.GetPostList(ctx, in, opts...)
+}
+
+// 获取帖子详情
+func (m *defaultSlowWildServer) GetPost(ctx context.Context, in *GetPostReq, opts ...grpc.CallOption) (*GetPostRsp, error) {
+	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
+	return client.GetPost(ctx, in, opts...)
+}
+
+// 点赞帖子
+func (m *defaultSlowWildServer) PostUpvote(ctx context.Context, in *PostUpvoteReq, opts ...grpc.CallOption) (*PostUpvoteRsp, error) {
+	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
+	return client.PostUpvote(ctx, in, opts...)
+}
+
+// 分享帖子
+func (m *defaultSlowWildServer) PostShare(ctx context.Context, in *PostShareReq, opts ...grpc.CallOption) (*PostShareRsp, error) {
+	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
+	return client.PostShare(ctx, in, opts...)
+}
+
+// 收藏帖子
+func (m *defaultSlowWildServer) PostCollection(ctx context.Context, in *PostCollectionReq, opts ...grpc.CallOption) (*PostCollectionRsp, error) {
+	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
+	return client.PostCollection(ctx, in, opts...)
+}
+
+// 发布评论
+func (m *defaultSlowWildServer) PostComment(ctx context.Context, in *PostCommentReq, opts ...grpc.CallOption) (*PostCommentRsp, error) {
+	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
+	return client.PostComment(ctx, in, opts...)
+}
+
+// 发布回复
+func (m *defaultSlowWildServer) PostReply(ctx context.Context, in *PostReplyReq, opts ...grpc.CallOption) (*PostReplyRsp, error) {
+	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
+	return client.PostReply(ctx, in, opts...)
+}
+
+// 获取评论列表
+func (m *defaultSlowWildServer) GetPostCommentList(ctx context.Context, in *GetPostCommentListReq, opts ...grpc.CallOption) (*GetPostCommentListRsp, error) {
+	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
+	return client.GetPostCommentList(ctx, in, opts...)
+}
+
+// 获取回复列表
+func (m *defaultSlowWildServer) GetReplyList(ctx context.Context, in *GetReplyListReq, opts ...grpc.CallOption) (*GetReplyListRsp, error) {
+	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
+	return client.GetReplyList(ctx, in, opts...)
+}
+
+// 评论点赞
+func (m *defaultSlowWildServer) PostCommentUpvote(ctx context.Context, in *PostCommentUpvoteReq, opts ...grpc.CallOption) (*PostCommentUpvoteRsp, error) {
+	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
+	return client.PostCommentUpvote(ctx, in, opts...)
+}
+
+// 删除帖子
+func (m *defaultSlowWildServer) PostDelete(ctx context.Context, in *PostDeleteReq, opts ...grpc.CallOption) (*PostDeleteRsp, error) {
+	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
+	return client.PostDelete(ctx, in, opts...)
+}
+
+// 删除回复/评论
+func (m *defaultSlowWildServer) CommentDelete(ctx context.Context, in *CommentDeleteReq, opts ...grpc.CallOption) (*CommentDeleteRsp, error) {
+	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
+	return client.CommentDelete(ctx, in, opts...)
+}
+
+// 获取用户发布的帖子
+func (m *defaultSlowWildServer) GetUserPostList(ctx context.Context, in *GetUserPostListReq, opts ...grpc.CallOption) (*GetUserPostListRsp, error) {
+	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
+	return client.GetUserPostList(ctx, in, opts...)
+}
+
+// 获取用户收藏的帖子
+func (m *defaultSlowWildServer) GetUserPostCollectionList(ctx context.Context, in *GetUserPostCollectionListReq, opts ...grpc.CallOption) (*GetUserPostCollectionListRsp, error) {
+	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
+	return client.GetUserPostCollectionList(ctx, in, opts...)
+}
+
+// 获取用户点赞过的帖子
+func (m *defaultSlowWildServer) GetUserPostLikeList(ctx context.Context, in *GetUserPostLikeListReq, opts ...grpc.CallOption) (*GetUserPostLikeListRsp, error) {
+	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
+	return client.GetUserPostLikeList(ctx, in, opts...)
+}