فهرست منبع

update some api

huangguangrong 9 ماه پیش
والد
کامیت
cbf2fd1fb3

+ 98 - 95
api/slow_wild.api

@@ -41,21 +41,21 @@ type (
 	LoginReq {
 		Phone     string `json:"phone"`
 		Password  string `json:"password"`
-		Code      string `json:"code"`
+		Code      string `json:"code,optional"`
 		LoginType int32  `json:"login_type"`
-		LoginIp   string `json:"login_ip"`
+		LoginIp   string `json:"login_ip,optional"`
 	}
 	RegisterReq {
 		Phone    string `json:"phone"`
-		Nickname string `json:"nickname"`
+		Nickname string `json:"nickname,optional"`
 		Password string `json:"password"`
-		Avatar   string `json:"avatar"`
-		Sex      int32  `json:"sex"`
-		Username string `json:"username"`
-		Code     string `json:"code"`
+		Avatar   string `json:"avatar,optional"`
+		Sex      int32  `json:"sex,default=1"`
+		Username string `json:"username,optional"`
+		Code     string `json:"code,optional"`
 	}
 	GetUserInfoReq {
-		QueryUserId int64 `json:"query_user_id"`
+		QueryUserId int64 `form:"query_user_id"`
 	}
 	GetUserInfoResp {
 		Id       int64  `json:"id"`
@@ -65,9 +65,9 @@ type (
 		IsFollow bool   `json:"is_follow"`
 	}
 	FindUserReq {
-		Username string  `json:"username"`
-		Phone    string  `json:"phone"`
-		Ids      []int64 `json:"ids"`
+		Username string  `json:"username,optional"`
+		Phone    string  `json:"phone,optional"`
+		Ids      []int64 `json:"ids,optional"`
 	}
 	FindUserResp {
 		UserList []UserInfo `json:"user_list"`
@@ -85,25 +85,25 @@ type (
 		Success bool `json:"success"`
 	}
 	GetFollowingReq {
-		PageSize    int32 `json:"page_size"`
-		Page        int32 `json:"page"`
-		QueryUserId int64 `json:"query_user_id"`
+		PageSize    int32 `form:"page_size,default=10"`
+		Page        int32 `form:"page,default=1"`
+		QueryUserId int64 `form:"query_user_id"`
 	}
 	GetFollowingRes {
 		List  []FollowUserInfo `json:"list"`
 		Total int64            `json:"total"`
 	}
 	GetFollowReq {
-		PageSize    int32 `json:"page_size"`
-		Page        int32 `json:"page"`
-		QueryUserId int64 `json:"query_user_id"`
+		PageSize    int32 `form:"page_size,default=10"`
+		Page        int32 `form:"page,default=1"`
+		QueryUserId int64 `form:"query_user_id"`
 	}
 	GetFollowRes {
 		List  []FollowUserInfo `json:"list"`
 		Total int64            `json:"total"`
 	}
 	GetUserProfileReq {
-		UserId int64 `json:"user_id"`
+		UserId int64 `form:"user_id"`
 	}
 	GetUserProfileRes {
 		Id                 int64  `json:"id"`
@@ -122,10 +122,9 @@ type (
 		Sex                int32  `json:"sex"`
 	}
 	SearchUsernameReq {
-		Keyword  string `json:"keyword"`
-		Page     int32  `json:"page"`
-		PageSize int32  `json:"page_size"`
-		UserId   int64  `json:"user_id"`
+		Keyword  string `form:"keyword"`
+		Page     int32  `form:"page,default=1"`
+		PageSize int32  `form:"page_size,default=10"`
 	}
 	SearchUsernameItem {
 		Nickname string `json:"nickname"`
@@ -145,24 +144,22 @@ type (
 	CreatePostReq {
 		Title      string      `json:"title"`
 		Content    string      `json:"content"`
-		Tags       []CreateTag `json:"tags"`
-		AtUserIds  []int64     `json:"at_user_ids"`
+		Tags       []CreateTag `json:"tags,optional"`
+		AtUserIds  []int64     `json:"at_user_ids,optional"`
 		PostType   int32       `json:"post_type"`
 		Visibility int32       `json:"visibility"`
 		Images     []string    `json:"images"`
-		VideoCover string      `json:"video_cover"`
-		VideoUrl   string      `json:"video_url"`
-		Ip         string      `json:"ip"`
-		IpLoc      string      `json:"ip_loc"`
+		VideoCover string      `json:"video_cover,optional"`
+		VideoUrl   string      `json:"video_url,optional"`
 	}
 	CreatePostRsp {
 		PostId int64 `json:"post_id"`
 	}
 	GetTagListReq {
-		Page     int32  `json:"page"`
-		PageSize int32  `json:"page_size"`
-		TagName  string `json:"tag_name"`
-		SortBy   int32  `json:"sort_by"`
+		Page     int32  `form:"page,default=1"`
+		PageSize int32  `form:"page_size,default=10"`
+		TagName  string `form:"tag_name"`
+		SortBy   int32  `form:"sort_by"`
 	}
 	TagItem {
 		Name      string   `json:"name"`
@@ -176,7 +173,7 @@ type (
 		Total   int32     `json:"total"`
 	}
 	GetTagReq {
-		TagId int64 `json:"tag_id"`
+		TagId int64 `form:"tag_id"`
 	}
 	GetTagRsp {
 		Name        string   `json:"name"`
@@ -189,11 +186,11 @@ type (
 		IsFollowed  bool     `json:"is_followed"`
 	}
 	GetPostListReq {
-		Page       int32  `json:"page"`
-		PageSize   int32  `json:"page_size"`
-		SearchType int32  `json:"search_type"`
-		SortType   int32  `json:"sort_type"`
-		Keyword    string `json:"keyword"`
+		Page       int32  `form:"page,default=1"`
+		PageSize   int32  `form:"page_size,default=10"`
+		SearchType int32  `form:"search_type,optional"`
+		SortType   int32  `form:"sort_type,optional"`
+		Keyword    string `form:"keyword,optional"`
 	}
 	GetPostListItem {
 		Id              int64     `json:"id"`
@@ -222,7 +219,7 @@ type (
 		Total int64             `json:"total"`
 	}
 	GetPostReq {
-		PostId int64 `json:"post_id"`
+		PostId int64 `form:"post_id"`
 	}
 	GetPostRsp {
 		Id              int64      `json:"id"`
@@ -270,10 +267,8 @@ type (
 	PostCommentReq {
 		PostId   int64  `json:"post_id"`
 		Content  string `json:"content"`
-		Image    string `json:"image"`
-		AtUserId int64  `json:"at_user_id"`
-		Ip       string `json:"ip"`
-		IpLoc    string `json:"ip_loc"`
+		Image    string `json:"image,optional"`
+		AtUserId int64  `json:"at_user_id,optional"`
 	}
 	PostCommentRsp {
 		CommentId int64 `json:"comment_id"`
@@ -281,20 +276,18 @@ type (
 	PostReplyReq {
 		PostId    int64  `json:"post_id"`
 		Content   string `json:"content"`
-		Image     string `json:"image"`
-		AtUserId  int64  `json:"at_user_id"`
-		Ip        string `json:"ip"`
-		IpLoc     string `json:"ip_loc"`
+		Image     string `json:"image,optional"`
+		AtUserId  int64  `json:"at_user_id,optional"`
 		CommentId int64  `json:"comment_id"`
-		ToReplyId int64  `json:"to_reply_id"`
+		ToReplyId int64  `json:"to_reply_id,optional"`
 	}
 	PostReplyRsp {
 		ReplyId int64 `json:"reply_id"`
 	}
 	GetPostCommentListReq {
-		Page     int32 `json:"page"`
-		PageSize int32 `json:"page_size"`
-		PostId   int64 `json:"post_id"`
+		Page     int32 `form:"page,default=1"`
+		PageSize int32 `form:"page_size,default=10"`
+		PostId   int64 `form:"post_id"`
 	}
 	RepliesItem {
 		Id          int64      `json:"id"`
@@ -329,9 +322,9 @@ type (
 		Total int64         `json:"total"`
 	}
 	GetReplyListReq {
-		Page      int32 `json:"page"`
-		PageSize  int32 `json:"page_size"`
-		CommentId int64 `json:"comment_id"`
+		Page      int32 `from:"page,default=1"`
+		PageSize  int32 `from:"page_size,default=10"`
+		CommentId int64 `from:"comment_id"`
 	}
 	GetReplyListRsp {
 		List  []RepliesItem `json:"list"`
@@ -358,27 +351,27 @@ type (
 		Success bool `json:"success"`
 	}
 	GetUserPostListReq {
-		Page        int64 `json:"page"`
-		PageSize    int64 `json:"page_size"`
-		QueryUserId int64 `json:"query_user_id"`
+		Page        int64 `from:"page,default=1"`
+		PageSize    int64 `from:"page_size,default=10"`
+		QueryUserId int64 `from:"query_user_id"`
 	}
 	GetUserPostListRsp {
 		List  []GetPostListItem `json:"list"`
 		Total int64             `json:"total"`
 	}
 	GetUserPostCollectionListReq {
-		Page        int64 `json:"page"`
-		PageSize    int64 `json:"page_size"`
-		QueryUserId int64 `json:"query_user_id"`
+		Page        int64 `from:"page,default=1"`
+		PageSize    int64 `from:"page_size,default=10"`
+		QueryUserId int64 `from:"query_user_id"`
 	}
 	GetUserPostCollectionListRsp {
 		List  []GetPostListItem `json:"list"`
 		Total int64             `json:"total"`
 	}
 	GetUserPostLikeListReq {
-		Page        int64 `json:"page"`
-		PageSize    int64 `json:"page_size"`
-		QueryUserId int64 `json:"query_user_id"`
+		Page        int64 `from:"page,default=1"`
+		PageSize    int64 `from:"page_size,default=10"`
+		QueryUserId int64 `from:"query_user_id"`
 	}
 	GetUserPostLikeListRsp {
 		List  []GetPostListItem `json:"list"`
@@ -387,137 +380,147 @@ type (
 )
 
 @server (
-	perfix: /v1
+	prefix: /v1/api
 )
 service slowwild {
 	@doc "登录方法"
 	@handler login
-	post /api/user/login (LoginReq) returns (BaseReturnData)
+	post /user/login (LoginReq) returns (BaseReturnData)
 
 	@doc "用户注册"
 	@handler register
-	post /api/user/register (RegisterReq) returns (BaseReturnData)
+	post /user/register (RegisterReq) returns (BaseReturnData)
 }
 
 @server (
-	perfix: /v1
+	prefix: /v1/api
 	jwt:    Auth // 开启 jwt 认证
 )
 service slowwild {
 	@doc "获取用户信息"
 	@handler getUserInfo
-	get /api/user/info (GetUserInfoReq) returns (BaseReturnData)
+	get /user/info (GetUserInfoReq) returns (BaseReturnData)
 
 	@doc "查询用户"
 	@handler findUser
-	post /api/user/find (FindUserReq) returns (BaseReturnData)
+	post /user/find (FindUserReq) returns (BaseReturnData)
 
 	@doc "关注用户"
 	@handler followUser
-	post /api/user/follow (FollowUserReq) returns (BaseReturnData)
+	post /user/follow (FollowUserReq) returns (BaseReturnData)
 
 	@doc "取消关注用户"
 	@handler unfollowUser
-	post /api/user/unfollow (UnFollowUserReq) returns (BaseReturnData)
+	post /user/unfollow (UnFollowUserReq) returns (BaseReturnData)
 
 	@doc "获取粉丝"
 	@handler getFans
-	get /api/user/fans (GetFollowingReq) returns (BaseReturnData)
+	get /user/fans (GetFollowingReq) returns (BaseReturnData)
 
 	@doc "获取关注列表"
 	@handler getFollows
-	get /api/user/follows (GetFollowReq) returns (BaseReturnData)
+	get /user/follows (GetFollowReq) returns (BaseReturnData)
 
 	@doc "获取用户信息"
 	@handler getUserProfile
-	get /api/user/profile (GetUserProfileReq) returns (BaseReturnData)
+	get /user/profile (GetUserProfileReq) returns (BaseReturnData)
 
 	@doc "搜索用户"
 	@handler searchUsername
-	get /api/user/search (SearchUsernameReq) returns (BaseReturnData)
+	get /user/search (SearchUsernameReq) returns (BaseReturnData)
 }
 
 @server (
-	perfix: /v1
+	prefix: /v1/api
 	jwt:    Auth
 )
 service slowwild {
 	@doc "创建帖子"
 	@handler createPost
-	post /api/post/create (CreatePostReq) returns (BaseReturnData)
+	post /post/create (CreatePostReq) returns (BaseReturnData)
 
 	@doc "帖子点赞"
 	@handler postUpvote
-	post /api/post/upvote (PostUpvoteReq) returns (BaseReturnData)
+	post /post/upvote (PostUpvoteReq) returns (BaseReturnData)
 
 	@doc "帖子收藏"
 	@handler postCollection
-	post /api/post/collection (PostCollectionReq) returns (BaseReturnData)
+	post /post/collection (PostCollectionReq) returns (BaseReturnData)
 
 	@doc "帖子分享"
 	@handler postShare
-	post /api/post/share (PostShareReq) returns (BaseReturnData)
+	post /post/share (PostShareReq) returns (BaseReturnData)
 
 	@doc "发布评论"
 	@handler postComment
-	post /api/post/comment (PostCommentReq) returns (BaseReturnData)
+	post /post/comment (PostCommentReq) returns (BaseReturnData)
 
 	@doc "回复评论"
 	@handler postReply
-	post /api/post/reply (PostReplyReq) returns (BaseReturnData)
+	post /post/reply (PostReplyReq) returns (BaseReturnData)
 
 	@doc "评论点赞"
 	@handler postCommentUpvote
-	post /api/post/comment/upvote (PostCommentUpvoteReq) returns (BaseReturnData)
+	post /post/comment/upvote (PostCommentUpvoteReq) returns (BaseReturnData)
 
 	@doc "帖子删除"
 	@handler postDelete
-	post /api/post/delete (PostDeleteReq) returns (BaseReturnData)
+	post /post/delete (PostDeleteReq) returns (BaseReturnData)
 
 	@doc "评论删除"
 	@handler commentDelete
-	post /api/comment/delete (CommentDeleteReq) returns (BaseReturnData)
+	post /comment/delete (CommentDeleteReq) returns (BaseReturnData)
 }
 
 @server (
-	perfix:     /v1
+	prefix:     /v1/api
 	middleware: OptionalJwtMiddleware
 )
 service slowwild {
 	@doc "获取话题列表"
 	@handler getTagList
-	get /api/tag/list (GetTagListReq) returns (BaseReturnData)
+	get /tag/list (GetTagListReq) returns (BaseReturnData)
 
 	@doc "获取话题详情"
 	@handler getTag
-	get /api/tag (GetTagReq) returns (BaseReturnData)
+	get /tag (GetTagReq) returns (BaseReturnData)
 
 	@doc "获取帖子列表"
 	@handler getPostList
-	get /api/post/list (GetPostListReq) returns (BaseReturnData)
+	get /post/list (GetPostListReq) returns (BaseReturnData)
 
 	@doc "获取帖子详情"
 	@handler getPost
-	get /api/post (GetPostReq) returns (BaseReturnData)
+	get /post (GetPostReq) returns (BaseReturnData)
 
 	@doc "获取评论列表"
 	@handler getPostCommentList
-	get /api/post/comment/list (GetPostCommentListReq) returns (BaseReturnData)
+	get /post/comment/list (GetPostCommentListReq) returns (BaseReturnData)
 
 	@doc "获取回复列表"
 	@handler getReplyList
-	get /api/post/reply/list (GetReplyListReq) returns (BaseReturnData)
+	get /post/reply/list (GetReplyListReq) returns (BaseReturnData)
 
 	@doc "获取用户发布的帖子列表"
 	@handler getUserPostList
-	get /api/user/post/list (GetUserPostListReq) returns (BaseReturnData)
+	get /user/post/list (GetUserPostListReq) returns (BaseReturnData)
 
 	@doc "获取用户收藏的帖子列表"
 	@handler getUserPostCollectionList
-	get /api/user/post/collection/list (GetUserPostCollectionListReq) returns (BaseReturnData)
+	get /user/post/collection/list (GetUserPostCollectionListReq) returns (BaseReturnData)
 
 	@doc "获取用户点赞的帖子列表"
 	@handler getUserPostLikeList
-	get /api/user/post/like/list (GetUserPostLikeListReq) returns (BaseReturnData)
+	get /user/post/like/list (GetUserPostLikeListReq) returns (BaseReturnData)
+}
+
+@server (
+	prefix: /v1/api
+	jwt:    Auth
+)
+service slowwild {
+	@doc "上传文件"
+	@handler attachmentUpload
+	post /attachment/upload returns (BaseReturnData)
 }
 

+ 13 - 2
apps/etc/slowwild.yaml

@@ -11,6 +11,17 @@ SlowwildRpc:
     Key: slowwildserver
 
 Auth:
-  AccessSecret: "bejson822834"
+  AccessSecret: "your-secret-key"
   AccessExpire: 3600
-
+# 腾讯云cos的配置
+TencentCos:
+  # 公用桶的地址
+  BucketUrl: "https://paopao-im-1258668793.cos.ap-guangzhou.myqcloud.com"
+  # 私有桶的地址
+  PrivateBucketUrl: "https://paopao-im-private-1258668793.cos.ap-guangzhou.myqcloud.com"
+  # 桶的地址
+  ServiceUrl: "https://cos.ap-guangzhou.myqcloud.com"
+  # 加密参数
+  SecretId: AKIDJSQAtlW5TtPm9KJFJcr0zbLHFJJGLQVT
+  # 加密参数
+  SecretKey: ticQ5kT7Y5W0eBct3jKycMIE8zswEa9s

+ 9 - 0
apps/internal/config/config.go

@@ -12,4 +12,13 @@ type Config struct {
 		AccessExpire int64
 	}
 	SlowwildRpc zrpc.RpcClientConf
+
+	TencentCos struct {
+		// 访问 bucket, object 相关 API 的基础 URL(不包含 path 部分): https://examplebucket-1250000000.cos.<Region>.myqcloud.com
+		BucketUrl        string
+		PrivateBucketUrl string
+		SecretId         string
+		SecretKey        string
+		ServiceUrl       string
+	}
 }

+ 24 - 0
apps/internal/handler/attachmentuploadhandler.go

@@ -0,0 +1,24 @@
+package handler
+
+import (
+	"net/http"
+	"slow_wild_api/apps/internal/logic"
+	"slow_wild_api/apps/internal/response"
+	"slow_wild_api/apps/internal/svc"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+)
+
+func attachmentUploadHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		file, header, errFile := r.FormFile("file")
+		if errFile != nil {
+			httpx.ErrorCtx(r.Context(), w, errFile)
+			return
+		}
+		defer file.Close()
+		l := logic.NewAttachmentUploadLogic(r.Context(), svcCtx)
+		resp, err := l.AttachmentUpload(file, header)
+		response.Response(w, resp, err)
+	}
+}

+ 3 - 2
apps/internal/handler/createposthandler.go

@@ -6,6 +6,7 @@ import (
 	"slow_wild_api/apps/internal/response"
 	"slow_wild_api/apps/internal/svc"
 	"slow_wild_api/apps/internal/types"
+	"slow_wild_api/apps/internal/utils"
 
 	"github.com/zeromicro/go-zero/rest/httpx"
 )
@@ -18,9 +19,9 @@ func createPostHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
 			httpx.ErrorCtx(r.Context(), w, err)
 			return
 		}
-
+		ip := utils.GetRealIP(r)
 		l := logic.NewCreatePostLogic(r.Context(), svcCtx)
-		resp, err := l.CreatePost(&req)
+		resp, err := l.CreatePost(&req, ip, "")
 		response.Response(w, resp, err)
 	}
 }

+ 3 - 2
apps/internal/handler/postcommenthandler.go

@@ -6,6 +6,7 @@ import (
 	"slow_wild_api/apps/internal/response"
 	"slow_wild_api/apps/internal/svc"
 	"slow_wild_api/apps/internal/types"
+	"slow_wild_api/apps/internal/utils"
 
 	"github.com/zeromicro/go-zero/rest/httpx"
 )
@@ -18,9 +19,9 @@ func postCommentHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
 			httpx.ErrorCtx(r.Context(), w, err)
 			return
 		}
-
+		ip := utils.GetRealIP(r)
 		l := logic.NewPostCommentLogic(r.Context(), svcCtx)
-		resp, err := l.PostComment(&req)
+		resp, err := l.PostComment(&req, ip, "")
 		response.Response(w, resp, err)
 	}
 }

+ 3 - 2
apps/internal/handler/postreplyhandler.go

@@ -6,6 +6,7 @@ import (
 	"slow_wild_api/apps/internal/response"
 	"slow_wild_api/apps/internal/svc"
 	"slow_wild_api/apps/internal/types"
+	"slow_wild_api/apps/internal/utils"
 
 	"github.com/zeromicro/go-zero/rest/httpx"
 )
@@ -18,9 +19,9 @@ func postReplyHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
 			httpx.ErrorCtx(r.Context(), w, err)
 			return
 		}
-
+		ip := utils.GetRealIP(r)
 		l := logic.NewPostReplyLogic(r.Context(), svcCtx)
-		resp, err := l.PostReply(&req)
+		resp, err := l.PostReply(&req, ip, "")
 		response.Response(w, resp, err)
 	}
 }

+ 44 - 56
apps/internal/handler/routes.go

@@ -13,132 +13,116 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
 	server.AddRoutes(
 		[]rest.Route{
 			{
-				// 登录方法
 				Method:  http.MethodPost,
-				Path:    "/api/user/login",
+				Path:    "/user/login",
 				Handler: loginHandler(serverCtx),
 			},
 			{
-				// 用户注册
 				Method:  http.MethodPost,
-				Path:    "/api/user/register",
+				Path:    "/user/register",
 				Handler: registerHandler(serverCtx),
 			},
 		},
+		rest.WithPrefix("/v1/api"),
 	)
 
 	server.AddRoutes(
 		[]rest.Route{
 			{
-				// 获取粉丝
 				Method:  http.MethodGet,
-				Path:    "/api/user/fans",
+				Path:    "/user/fans",
 				Handler: getFansHandler(serverCtx),
 			},
 			{
-				// 查询用户
 				Method:  http.MethodPost,
-				Path:    "/api/user/find",
+				Path:    "/user/find",
 				Handler: findUserHandler(serverCtx),
 			},
 			{
-				// 关注用户
 				Method:  http.MethodPost,
-				Path:    "/api/user/follow",
+				Path:    "/user/follow",
 				Handler: followUserHandler(serverCtx),
 			},
 			{
-				// 获取关注列表
 				Method:  http.MethodGet,
-				Path:    "/api/user/follows",
+				Path:    "/user/follows",
 				Handler: getFollowsHandler(serverCtx),
 			},
 			{
-				// 获取用户信息
 				Method:  http.MethodGet,
-				Path:    "/api/user/info",
+				Path:    "/user/info",
 				Handler: getUserInfoHandler(serverCtx),
 			},
 			{
-				// 获取用户信息
 				Method:  http.MethodGet,
-				Path:    "/api/user/profile",
+				Path:    "/user/profile",
 				Handler: getUserProfileHandler(serverCtx),
 			},
 			{
-				// 搜索用户
 				Method:  http.MethodGet,
-				Path:    "/api/user/search",
+				Path:    "/user/search",
 				Handler: searchUsernameHandler(serverCtx),
 			},
 			{
-				// 取消关注用户
 				Method:  http.MethodPost,
-				Path:    "/api/user/unfollow",
+				Path:    "/user/unfollow",
 				Handler: unfollowUserHandler(serverCtx),
 			},
 		},
 		rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
+		rest.WithPrefix("/v1/api"),
 	)
 
 	server.AddRoutes(
 		[]rest.Route{
 			{
-				// 评论删除
 				Method:  http.MethodPost,
-				Path:    "/api/comment/delete",
+				Path:    "/comment/delete",
 				Handler: commentDeleteHandler(serverCtx),
 			},
 			{
-				// 帖子收藏
 				Method:  http.MethodPost,
-				Path:    "/api/post/collection",
+				Path:    "/post/collection",
 				Handler: postCollectionHandler(serverCtx),
 			},
 			{
-				// 发布评论
 				Method:  http.MethodPost,
-				Path:    "/api/post/comment",
+				Path:    "/post/comment",
 				Handler: postCommentHandler(serverCtx),
 			},
 			{
-				// 评论点赞
 				Method:  http.MethodPost,
-				Path:    "/api/post/comment/upvote",
+				Path:    "/post/comment/upvote",
 				Handler: postCommentUpvoteHandler(serverCtx),
 			},
 			{
-				// 创建帖子
 				Method:  http.MethodPost,
-				Path:    "/api/post/create",
+				Path:    "/post/create",
 				Handler: createPostHandler(serverCtx),
 			},
 			{
-				// 帖子删除
 				Method:  http.MethodPost,
-				Path:    "/api/post/delete",
+				Path:    "/post/delete",
 				Handler: postDeleteHandler(serverCtx),
 			},
 			{
-				// 回复评论
 				Method:  http.MethodPost,
-				Path:    "/api/post/reply",
+				Path:    "/post/reply",
 				Handler: postReplyHandler(serverCtx),
 			},
 			{
-				// 帖子分享
 				Method:  http.MethodPost,
-				Path:    "/api/post/share",
+				Path:    "/post/share",
 				Handler: postShareHandler(serverCtx),
 			},
 			{
-				// 帖子点赞
 				Method:  http.MethodPost,
-				Path:    "/api/post/upvote",
+				Path:    "/post/upvote",
 				Handler: postUpvoteHandler(serverCtx),
 			},
 		},
 		rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
+		rest.WithPrefix("/v1/api"),
 	)
 
 	server.AddRoutes(
@@ -146,60 +130,64 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
 			[]rest.Middleware{serverCtx.OptionalJwtMiddleware},
 			[]rest.Route{
 				{
-					// 获取帖子详情
 					Method:  http.MethodGet,
-					Path:    "/api/post",
+					Path:    "/post",
 					Handler: getPostHandler(serverCtx),
 				},
 				{
-					// 获取评论列表
 					Method:  http.MethodGet,
-					Path:    "/api/post/comment/list",
+					Path:    "/post/comment/list",
 					Handler: getPostCommentListHandler(serverCtx),
 				},
 				{
-					// 获取帖子列表
 					Method:  http.MethodGet,
-					Path:    "/api/post/list",
+					Path:    "/post/list",
 					Handler: getPostListHandler(serverCtx),
 				},
 				{
-					// 获取回复列表
 					Method:  http.MethodGet,
-					Path:    "/api/post/reply/list",
+					Path:    "/post/reply/list",
 					Handler: getReplyListHandler(serverCtx),
 				},
 				{
-					// 获取话题详情
 					Method:  http.MethodGet,
-					Path:    "/api/tag",
+					Path:    "/tag",
 					Handler: getTagHandler(serverCtx),
 				},
 				{
-					// 获取话题列表
 					Method:  http.MethodGet,
-					Path:    "/api/tag/list",
+					Path:    "/tag/list",
 					Handler: getTagListHandler(serverCtx),
 				},
 				{
-					// 获取用户收藏的帖子列表
 					Method:  http.MethodGet,
-					Path:    "/api/user/post/collection/list",
+					Path:    "/user/post/collection/list",
 					Handler: getUserPostCollectionListHandler(serverCtx),
 				},
 				{
-					// 获取用户点赞的帖子列表
 					Method:  http.MethodGet,
-					Path:    "/api/user/post/like/list",
+					Path:    "/user/post/like/list",
 					Handler: getUserPostLikeListHandler(serverCtx),
 				},
 				{
-					// 获取用户发布的帖子列表
 					Method:  http.MethodGet,
-					Path:    "/api/user/post/list",
+					Path:    "/user/post/list",
 					Handler: getUserPostListHandler(serverCtx),
 				},
 			}...,
 		),
+		rest.WithPrefix("/v1/api"),
+	)
+
+	server.AddRoutes(
+		[]rest.Route{
+			{
+				Method:  http.MethodPost,
+				Path:    "/attachment/upload",
+				Handler: attachmentUploadHandler(serverCtx),
+			},
+		},
+		rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
+		rest.WithPrefix("/v1/api"),
 	)
 }

+ 61 - 0
apps/internal/logic/attachmentuploadlogic.go

@@ -0,0 +1,61 @@
+package logic
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"io"
+	"mime/multipart"
+	"strings"
+	"time"
+
+	"slow_wild_api/apps/internal/svc"
+	"slow_wild_api/apps/internal/types"
+	"slow_wild_api/apps/internal/utils"
+
+	"github.com/tencentyun/cos-go-sdk-v5"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type AttachmentUploadLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+func NewAttachmentUploadLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AttachmentUploadLogic {
+	return &AttachmentUploadLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx,
+	}
+}
+
+func (l *AttachmentUploadLogic) AttachmentUpload(fileIO multipart.File, header *multipart.FileHeader) (resp *types.BaseReturnData, err error) {
+	resp = &types.BaseReturnData{}
+	contentData, err := io.ReadAll(fileIO)
+	if err != nil {
+		return
+	}
+	// 读取文件的流,用文件流加密成MD5, 如果文件不更改的话MD5不变,减少cos使用
+	fileName := utils.Str2MD5(contentData, "paopao-im-aghbslsghslk346300basbdja2313241")
+	oldFileName := header.Filename
+	splits := strings.Split(oldFileName, ".")
+	if len(splits) < 2 {
+		err = errors.New("文件上传失败")
+		return
+	}
+	fileName += "." + splits[len(splits)-1]
+	year := time.Now().Year()
+	month := time.Now().Month()
+	path := fmt.Sprintf("%d/%d/%s", year, month, fileName)
+	objectAclOption := &cos.ObjectPutOptions{}
+	_, err = l.svcCtx.TencentCos.Object.Put(l.ctx, path, strings.NewReader(string(contentData)), objectAclOption)
+	if err != nil {
+		return
+	}
+	resp.Data = map[string]string{
+		"url": fmt.Sprintf("%s/%s", l.svcCtx.TencentCos.BaseURL.BucketURL, path),
+	}
+	return
+}

+ 4 - 3
apps/internal/logic/createpostlogic.go

@@ -25,7 +25,7 @@ func NewCreatePostLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Create
 	}
 }
 
-func (l *CreatePostLogic) CreatePost(req *types.CreatePostReq) (resp *types.BaseReturnData, err error) {
+func (l *CreatePostLogic) CreatePost(req *types.CreatePostReq, ip, ipLoc string) (resp *types.BaseReturnData, err error) {
 	resp = &types.BaseReturnData{}
 	var tags []*slowwildserver.CreateTag
 	if len(req.Tags) > 0 {
@@ -36,6 +36,7 @@ func (l *CreatePostLogic) CreatePost(req *types.CreatePostReq) (resp *types.Base
 			})
 		}
 	}
+
 	rpcResp, err := l.svcCtx.SlowWildPb.CreatePost(l.ctx, &slowwildserver.CreatePostReq{
 		Title:      req.Title,
 		Content:    req.Content,
@@ -46,8 +47,8 @@ func (l *CreatePostLogic) CreatePost(req *types.CreatePostReq) (resp *types.Base
 		Images:     req.Images,
 		VideoCover: req.VideoCover,
 		VideoUrl:   req.VideoUrl,
-		Ip:         req.Ip,
-		IpLoc:      req.IpLoc,
+		Ip:         ip,
+		IpLoc:      ipLoc,
 	})
 	if err != nil {
 		return nil, err

+ 3 - 3
apps/internal/logic/postcommentlogic.go

@@ -25,15 +25,15 @@ func NewPostCommentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PostC
 	}
 }
 
-func (l *PostCommentLogic) PostComment(req *types.PostCommentReq) (resp *types.BaseReturnData, err error) {
+func (l *PostCommentLogic) PostComment(req *types.PostCommentReq, ip, ipLoc string) (resp *types.BaseReturnData, err error) {
 	resp = &types.BaseReturnData{}
 	rpcResp, err := l.svcCtx.SlowWildPb.PostComment(l.ctx, &slowwildserver.PostCommentReq{
 		PostId:   req.PostId,
 		Content:  req.Content,
 		Image:    req.Image,
 		AtUserId: req.AtUserId,
-		Ip:       req.Ip,
-		IpLoc:    req.IpLoc,
+		Ip:       ip,
+		IpLoc:    ipLoc,
 	})
 	if err != nil {
 		return nil, err

+ 3 - 3
apps/internal/logic/postreplylogic.go

@@ -25,15 +25,15 @@ func NewPostReplyLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PostRep
 	}
 }
 
-func (l *PostReplyLogic) PostReply(req *types.PostReplyReq) (resp *types.BaseReturnData, err error) {
+func (l *PostReplyLogic) PostReply(req *types.PostReplyReq, ip, ipLoc string) (resp *types.BaseReturnData, err error) {
 	resp = &types.BaseReturnData{}
 	rpcResp, err := l.svcCtx.SlowWildPb.PostReply(l.ctx, &slowwildserver.PostReplyReq{
 		CommentId: req.CommentId,
 		Content:   req.Content,
 		Image:     req.Image,
 		AtUserId:  req.AtUserId,
-		Ip:        req.Ip,
-		IpLoc:     req.IpLoc,
+		Ip:        ip,
+		IpLoc:     ipLoc,
 	})
 	if err != nil {
 		return nil, err

+ 3 - 1
apps/internal/middleware/optionaljwtmiddleware.go

@@ -35,11 +35,14 @@ func (m *OptionalJwtMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 		// 尝试从请求头中获取 JWT 令牌
 		tokenString := r.Header.Get("Authorization")
+		fmt.Println("token: ", tokenString)
 		ctx := r.Context()
 		if len(tokenString) > 0 {
 			// 如果提供了 JWT 令牌,则验证它
 			tokenValue := strings.Split(tokenString, " ")
 
+			fmt.Println(tokenValue)
+
 			if len(tokenValue) == 2 {
 				parsedToken, err := jwt.Parse(tokenValue[1], func(token *jwt.Token) (interface{}, error) {
 					if _, ok := token.Method.(*jwt.SigningMethodHMAC); ok {
@@ -59,7 +62,6 @@ func (m *OptionalJwtMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
 								ctx = context.WithValue(ctx, k, v)
 							}
 						}
-						fmt.Println("vvvvvvvv: ")
 					}
 				}
 			}

+ 13 - 4
apps/internal/response/response.go

@@ -2,8 +2,10 @@ package response
 
 import (
 	"net/http"
+	"slow_wild_api/apps/internal/types"
 
 	"github.com/zeromicro/go-zero/rest/httpx"
+	"google.golang.org/grpc/status"
 )
 
 type Body struct {
@@ -12,14 +14,21 @@ type Body struct {
 	Data interface{} `json:"data,omitempty"`
 }
 
-func Response(w http.ResponseWriter, resp interface{}, err error) {
+func Response(w http.ResponseWriter, resp *types.BaseReturnData, err error) {
 	var body Body
 	if err != nil {
-		body.Code = -1
-		body.Msg = err.Error()
+		// 处理 RPC 错误
+		if e, ok := status.FromError(err); ok {
+			body.Code = int(e.Code())
+			body.Msg = e.Message()
+		} else {
+			body.Code = -1
+			body.Msg = err.Error()
+		}
 	} else {
+		body.Code = 0
 		body.Msg = "OK"
-		body.Data = resp
+		body.Data = resp.Data
 	}
 	httpx.OkJson(w, body)
 }

+ 17 - 0
apps/internal/svc/servicecontext.go

@@ -3,6 +3,8 @@ package svc
 import (
 	"context"
 	"fmt"
+	"net/http"
+	"net/url"
 	"slow_wild_api/apps/internal/config"
 	"slow_wild_api/apps/internal/middleware"
 
@@ -10,6 +12,7 @@ import (
 	"google.golang.org/grpc/metadata"
 
 	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserverclient"
+	"github.com/tencentyun/cos-go-sdk-v5"
 	"github.com/zeromicro/go-zero/rest"
 	"github.com/zeromicro/go-zero/zrpc"
 )
@@ -18,6 +21,7 @@ type ServiceContext struct {
 	Config                config.Config
 	SlowWildPb            slowwildserverclient.SlowWildServer
 	OptionalJwtMiddleware rest.Middleware
+	TencentCos            *cos.Client
 }
 
 // 创建 gRPC 拦截器
@@ -37,6 +41,18 @@ func userAuthInterceptor() grpc.UnaryClientInterceptor {
 	}
 }
 
+func getTencentCosClient(bucketUrl string, secretId, secretKey string) *cos.Client {
+	u, _ := url.Parse(bucketUrl)
+	b := &cos.BaseURL{BucketURL: u}
+	client := cos.NewClient(b, &http.Client{
+		Transport: &cos.AuthorizationTransport{
+			SecretID:  secretId,
+			SecretKey: secretKey,
+		},
+	})
+	return client
+}
+
 func NewServiceContext(c config.Config) *ServiceContext {
 	// 创建带有拦截器的 gRPC 客户端
 	client := zrpc.MustNewClient(c.SlowwildRpc,
@@ -46,5 +62,6 @@ func NewServiceContext(c config.Config) *ServiceContext {
 		Config:                c,
 		SlowWildPb:            slowwildserverclient.NewSlowWildServer(client),
 		OptionalJwtMiddleware: middleware.NewOptionalJwtMiddleware(c.Auth.AccessSecret).Handle,
+		TencentCos:            getTencentCosClient(c.TencentCos.BucketUrl, c.TencentCos.SecretId, c.TencentCos.SecretKey),
 	}
 }

+ 56 - 63
apps/internal/types/types.go

@@ -33,15 +33,13 @@ type CommentItem struct {
 type CreatePostReq struct {
 	Title      string      `json:"title"`
 	Content    string      `json:"content"`
-	Tags       []CreateTag `json:"tags"`
-	AtUserIds  []int64     `json:"at_user_ids"`
+	Tags       []CreateTag `json:"tags,optional"`
+	AtUserIds  []int64     `json:"at_user_ids,optional"`
 	PostType   int32       `json:"post_type"`
 	Visibility int32       `json:"visibility"`
 	Images     []string    `json:"images"`
-	VideoCover string      `json:"video_cover"`
-	VideoUrl   string      `json:"video_url"`
-	Ip         string      `json:"ip"`
-	IpLoc      string      `json:"ip_loc"`
+	VideoCover string      `json:"video_cover,optional"`
+	VideoUrl   string      `json:"video_url,optional"`
 }
 
 type CreatePostRsp struct {
@@ -54,9 +52,9 @@ type CreateTag struct {
 }
 
 type FindUserReq struct {
-	Username string  `json:"username"`
-	Phone    string  `json:"phone"`
-	Ids      []int64 `json:"ids"`
+	Username string  `json:"username,optional"`
+	Phone    string  `json:"phone,optional"`
+	Ids      []int64 `json:"ids,optional"`
 }
 
 type FindUserResp struct {
@@ -77,9 +75,9 @@ type FollowUserRes struct {
 }
 
 type GetFollowReq struct {
-	PageSize    int32 `json:"page_size"`
-	Page        int32 `json:"page"`
-	QueryUserId int64 `json:"query_user_id"`
+	PageSize    int32 `form:"page_size,default=10"`
+	Page        int32 `form:"page,default=1"`
+	QueryUserId int64 `form:"query_user_id"`
 }
 
 type GetFollowRes struct {
@@ -88,9 +86,9 @@ type GetFollowRes struct {
 }
 
 type GetFollowingReq struct {
-	PageSize    int32 `json:"page_size"`
-	Page        int32 `json:"page"`
-	QueryUserId int64 `json:"query_user_id"`
+	PageSize    int32 `form:"page_size,default=10"`
+	Page        int32 `form:"page,default=1"`
+	QueryUserId int64 `form:"query_user_id"`
 }
 
 type GetFollowingRes struct {
@@ -99,9 +97,9 @@ type GetFollowingRes struct {
 }
 
 type GetPostCommentListReq struct {
-	Page     int32 `json:"page"`
-	PageSize int32 `json:"page_size"`
-	PostId   int64 `json:"post_id"`
+	Page     int32 `form:"page,default=1"`
+	PageSize int32 `form:"page_size,default=10"`
+	PostId   int64 `form:"post_id"`
 }
 
 type GetPostCommentListRsp struct {
@@ -133,11 +131,11 @@ type GetPostListItem struct {
 }
 
 type GetPostListReq struct {
-	Page       int32  `json:"page"`
-	PageSize   int32  `json:"page_size"`
-	SearchType int32  `json:"search_type"`
-	SortType   int32  `json:"sort_type"`
-	Keyword    string `json:"keyword"`
+	Page       int32  `form:"page,default=1"`
+	PageSize   int32  `form:"page_size,default=10"`
+	SearchType int32  `form:"search_type,optional"`
+	SortType   int32  `form:"sort_type,optional"`
+	Keyword    string `form:"keyword,optional"`
 }
 
 type GetPostListRsp struct {
@@ -146,7 +144,7 @@ type GetPostListRsp struct {
 }
 
 type GetPostReq struct {
-	PostId int64 `json:"post_id"`
+	PostId int64 `form:"post_id"`
 }
 
 type GetPostRsp struct {
@@ -176,9 +174,9 @@ type GetPostRsp struct {
 }
 
 type GetReplyListReq struct {
-	Page      int32 `json:"page"`
-	PageSize  int32 `json:"page_size"`
-	CommentId int64 `json:"comment_id"`
+	Page      int32 `from:"page,default=1"`
+	PageSize  int32 `from:"page_size,default=10"`
+	CommentId int64 `from:"comment_id"`
 }
 
 type GetReplyListRsp struct {
@@ -187,10 +185,10 @@ type GetReplyListRsp struct {
 }
 
 type GetTagListReq struct {
-	Page     int32  `json:"page"`
-	PageSize int32  `json:"page_size"`
-	TagName  string `json:"tag_name"`
-	SortBy   int32  `json:"sort_by"`
+	Page     int32  `form:"page,default=1"`
+	PageSize int32  `form:"page_size,default=10"`
+	TagName  string `form:"tag_name"`
+	SortBy   int32  `form:"sort_by"`
 }
 
 type GetTagListRsp struct {
@@ -199,7 +197,7 @@ type GetTagListRsp struct {
 }
 
 type GetTagReq struct {
-	TagId int64 `json:"tag_id"`
+	TagId int64 `form:"tag_id"`
 }
 
 type GetTagRsp struct {
@@ -214,7 +212,7 @@ type GetTagRsp struct {
 }
 
 type GetUserInfoReq struct {
-	QueryUserId int64 `json:"query_user_id"`
+	QueryUserId int64 `form:"query_user_id"`
 }
 
 type GetUserInfoResp struct {
@@ -226,9 +224,9 @@ type GetUserInfoResp struct {
 }
 
 type GetUserPostCollectionListReq struct {
-	Page        int64 `json:"page"`
-	PageSize    int64 `json:"page_size"`
-	QueryUserId int64 `json:"query_user_id"`
+	Page        int64 `from:"page,default=1"`
+	PageSize    int64 `from:"page_size,default=10"`
+	QueryUserId int64 `from:"query_user_id"`
 }
 
 type GetUserPostCollectionListRsp struct {
@@ -237,9 +235,9 @@ type GetUserPostCollectionListRsp struct {
 }
 
 type GetUserPostLikeListReq struct {
-	Page        int64 `json:"page"`
-	PageSize    int64 `json:"page_size"`
-	QueryUserId int64 `json:"query_user_id"`
+	Page        int64 `from:"page,default=1"`
+	PageSize    int64 `from:"page_size,default=10"`
+	QueryUserId int64 `from:"query_user_id"`
 }
 
 type GetUserPostLikeListRsp struct {
@@ -248,9 +246,9 @@ type GetUserPostLikeListRsp struct {
 }
 
 type GetUserPostListReq struct {
-	Page        int64 `json:"page"`
-	PageSize    int64 `json:"page_size"`
-	QueryUserId int64 `json:"query_user_id"`
+	Page        int64 `from:"page,default=1"`
+	PageSize    int64 `from:"page_size,default=10"`
+	QueryUserId int64 `from:"query_user_id"`
 }
 
 type GetUserPostListRsp struct {
@@ -259,7 +257,7 @@ type GetUserPostListRsp struct {
 }
 
 type GetUserProfileReq struct {
-	UserId int64 `json:"user_id"`
+	UserId int64 `form:"user_id"`
 }
 
 type GetUserProfileRes struct {
@@ -287,9 +285,9 @@ type LoginAndRegisterRsp struct {
 type LoginReq struct {
 	Phone     string `json:"phone"`
 	Password  string `json:"password"`
-	Code      string `json:"code"`
+	Code      string `json:"code,optional"`
 	LoginType int32  `json:"login_type"`
-	LoginIp   string `json:"login_ip"`
+	LoginIp   string `json:"login_ip,optional"`
 }
 
 type PostCollectionReq struct {
@@ -303,10 +301,8 @@ type PostCollectionRsp struct {
 type PostCommentReq struct {
 	PostId   int64  `json:"post_id"`
 	Content  string `json:"content"`
-	Image    string `json:"image"`
-	AtUserId int64  `json:"at_user_id"`
-	Ip       string `json:"ip"`
-	IpLoc    string `json:"ip_loc"`
+	Image    string `json:"image,optional"`
+	AtUserId int64  `json:"at_user_id,optional"`
 }
 
 type PostCommentRsp struct {
@@ -333,12 +329,10 @@ type PostDeleteRsp struct {
 type PostReplyReq struct {
 	PostId    int64  `json:"post_id"`
 	Content   string `json:"content"`
-	Image     string `json:"image"`
-	AtUserId  int64  `json:"at_user_id"`
-	Ip        string `json:"ip"`
-	IpLoc     string `json:"ip_loc"`
+	Image     string `json:"image,optional"`
+	AtUserId  int64  `json:"at_user_id,optional"`
 	CommentId int64  `json:"comment_id"`
-	ToReplyId int64  `json:"to_reply_id"`
+	ToReplyId int64  `json:"to_reply_id,optional"`
 }
 
 type PostReplyRsp struct {
@@ -363,12 +357,12 @@ type PostUpvoteRsp struct {
 
 type RegisterReq struct {
 	Phone    string `json:"phone"`
-	Nickname string `json:"nickname"`
+	Nickname string `json:"nickname,optional"`
 	Password string `json:"password"`
-	Avatar   string `json:"avatar"`
-	Sex      int32  `json:"sex"`
-	Username string `json:"username"`
-	Code     string `json:"code"`
+	Avatar   string `json:"avatar,optional"`
+	Sex      int32  `json:"sex,default=1"`
+	Username string `json:"username,optional"`
+	Code     string `json:"code,optional"`
 }
 
 type RepliesItem struct {
@@ -394,10 +388,9 @@ type SearchUsernameItem struct {
 }
 
 type SearchUsernameReq struct {
-	Keyword  string `json:"keyword"`
-	Page     int32  `json:"page"`
-	PageSize int32  `json:"page_size"`
-	UserId   int64  `json:"user_id"`
+	Keyword  string `form:"keyword"`
+	Page     int32  `form:"page,default=1"`
+	PageSize int32  `form:"page_size,default=10"`
 }
 
 type SearchUsernameRes struct {

+ 12 - 0
apps/internal/utils/tool.go

@@ -0,0 +1,12 @@
+package utils
+
+import (
+	"crypto/md5"
+	"encoding/hex"
+)
+
+func Str2MD5(data []byte, salt string) string {
+	m := md5.New()
+	m.Write(data)
+	return hex.EncodeToString(m.Sum([]byte(salt)))
+}

+ 8 - 0
go.mod

@@ -10,6 +10,13 @@ require (
 	google.golang.org/grpc v1.70.0
 )
 
+require (
+	github.com/clbanning/mxj v1.8.4 // indirect
+	github.com/google/go-querystring v1.0.0 // indirect
+	github.com/mitchellh/mapstructure v1.4.3 // indirect
+	github.com/mozillazg/go-httpheader v0.2.1 // indirect
+)
+
 require (
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/cenkalti/backoff/v4 v4.3.0 // indirect
@@ -50,6 +57,7 @@ require (
 	github.com/prometheus/procfs v0.15.1 // indirect
 	github.com/redis/go-redis/v9 v9.7.1 // indirect
 	github.com/spaolacci/murmur3 v1.1.0 // indirect
+	github.com/tencentyun/cos-go-sdk-v5 v0.7.62
 	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/v3 v3.5.15 // indirect

+ 15 - 0
go.sum

@@ -1,5 +1,6 @@
 git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.1.8 h1:BW3DLwhDNATa45Jyzp18LCbXPNGEcz9k3v9mct81ayE=
 git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.1.8/go.mod h1:RvtFTWaCnJcB8iy/clOqBoimd7UxSx5+KB96G+bdq5c=
+github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
 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/miniredis/v2 v2.34.0 h1:mBFWMaJSNL9RwdGRyEDoAAv8OQc5UlEhLDQggTglU/0=
@@ -16,6 +17,8 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3
 github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
 github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
 github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I=
+github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
 github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
 github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
 github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
@@ -50,6 +53,7 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
 github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
 github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
 github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
 github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
 github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
@@ -59,11 +63,14 @@ github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYu
 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
+github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
 github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
 github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
@@ -94,11 +101,15 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
 github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
+github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
 github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/mozillazg/go-httpheader v0.2.1 h1:geV7TrjbL8KXSyvghnFm+NyTux/hxwueTSrwhe88TQQ=
+github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4=
@@ -144,6 +155,10 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
 github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
 github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.563/go.mod h1:uom4Nvi9W+Qkom0exYiJ9VWJjXwyxtPYTkKkaLMlfE0=
+github.com/tencentyun/cos-go-sdk-v5 v0.7.62 h1:7SZVCc31rkvMxod8nwvG1Ko0N5npT39/s3NhpHBvs70=
+github.com/tencentyun/cos-go-sdk-v5 v0.7.62/go.mod h1:8+hG+mQMuRP/OIS9d83syAvXvrMj9HhkND6Q1fLghw0=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=