├── .gitignore ├── LICENSE ├── README.md ├── cmd ├── api │ ├── handlers │ │ ├── comment.go │ │ ├── favorite.go │ │ ├── feed.go │ │ ├── handler.go │ │ ├── publish.go │ │ ├── relation.go │ │ └── user.go │ ├── main.go │ ├── rpc │ │ ├── comment.go │ │ ├── favorite.go │ │ ├── feed.go │ │ ├── init.go │ │ ├── publish.go │ │ ├── relation.go │ │ └── user.go │ └── run.sh ├── comment │ ├── build.sh │ ├── handler.go │ ├── main.go │ ├── script │ │ └── bootstrap.sh │ └── service │ │ ├── comment_list.go │ │ ├── comment_list_test.go │ │ ├── create_comment.go │ │ ├── create_comment_test.go │ │ ├── delete_comment.go │ │ └── delete_comment_test.go ├── favorite │ ├── build.sh │ ├── handler.go │ ├── main.go │ ├── script │ │ └── bootstrap.sh │ └── service │ │ ├── favorite_action.go │ │ ├── favorite_action_test.go │ │ ├── favorite_list.go │ │ └── favorite_list_test.go ├── feed │ ├── build.sh │ ├── handler.go │ ├── main.go │ ├── script │ │ └── bootstrap.sh │ └── service │ │ ├── feed.go │ │ └── feed_test.go ├── publish │ ├── build.sh │ ├── handler.go │ ├── main.go │ ├── script │ │ └── bootstrap.sh │ └── service │ │ ├── publish.go │ │ ├── publish_list.go │ │ ├── publish_list_test.go │ │ └── publish_test.go ├── relation │ ├── build.sh │ ├── handler.go │ ├── main.go │ ├── script │ │ └── bootstrap.sh │ └── service │ │ ├── follow_list.go │ │ ├── follow_list_test.go │ │ ├── follower_list.go │ │ ├── follower_list_test.go │ │ ├── relation_action_test.go │ │ └── relaton_action.go └── user │ ├── build.sh │ ├── handler.go │ ├── main.go │ ├── script │ └── bootstrap.sh │ └── service │ ├── check_user.go │ ├── check_user_test.go │ ├── register_user.go │ ├── register_user_test.go │ ├── user_info.go │ └── user_info_test.go ├── dal ├── db │ ├── comment.go │ ├── favorite.go │ ├── feed.go │ ├── init.go │ ├── publish.go │ ├── relation.go │ └── user.go ├── init.go └── pack │ ├── comment.go │ ├── favorite.go │ ├── feed.go │ ├── publish.go │ ├── relation.go │ ├── resp.go │ └── user.go ├── docker-compose.yml ├── go.mod ├── go.sum ├── idl ├── comment.proto ├── favorite.proto ├── feed.proto ├── publish.proto ├── relation.proto └── user.proto ├── kitex_gen ├── comment │ ├── comment.pb.go │ └── commentservice │ │ ├── client.go │ │ ├── commentservice.go │ │ ├── invoker.go │ │ └── server.go ├── favorite │ ├── favorite.pb.go │ └── favoriteservice │ │ ├── client.go │ │ ├── favoriteservice.go │ │ ├── invoker.go │ │ └── server.go ├── feed │ ├── feed.pb.go │ └── feedservice │ │ ├── client.go │ │ ├── feedservice.go │ │ ├── invoker.go │ │ └── server.go ├── publish │ ├── publish.pb.go │ └── publishservice │ │ ├── client.go │ │ ├── invoker.go │ │ ├── publishservice.go │ │ └── server.go ├── relation │ ├── relation.pb.go │ └── relationservice │ │ ├── client.go │ │ ├── invoker.go │ │ ├── relationservice.go │ │ └── server.go └── user │ ├── user.pb.go │ └── userservice │ ├── client.go │ ├── invoker.go │ ├── server.go │ └── userservice.go ├── pkg ├── bound │ └── cpu.go ├── constants │ └── constant.go ├── errno │ ├── code.go │ ├── errno.go │ └── errors.go ├── jwt │ ├── jwt.go │ └── jwt_test.go ├── middleware │ ├── client.go │ ├── common.go │ └── server.go ├── oss │ ├── init.go │ └── utils.go └── tracer │ └── tracer.go └── public ├── girl.mp4 ├── jaeger展示图.jpg ├── 微服务数据库图.jpg └── 服务调用关系截图.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | output 2 | [0-9]*.mp4 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 XiaoHuang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 极简版抖音服务端 2 | 3 | [![Go](https://img.shields.io/github/go-mod/go-version/gohugoio/hugo)](https://go.dev/) 4 | [![Kitex](https://img.shields.io/badge/Kitex-v0.3.0-green)](https://github.com/cloudwego/kitex) 5 | [![Gin](https://img.shields.io/badge/Gin-v1.7.7-brightgreen)](https://github.com/gin-gonic/gin) 6 | [![Gorm](https://img.shields.io/badge/Gorm-v1.23.5-blue)](https://gorm.io/) 7 | [![MySQL](https://img.shields.io/badge/MySQL-v1.3.3-red)](https://github.com/go-gorm/mysql) 8 | [![OSS](https://img.shields.io/badge/OSS-v2.2.4-orange)](https://github.com/aliyun/aliyun-oss-go-sdk) 9 | [![ETCD](https://img.shields.io/badge/ETCD-v0.0.0-lightgrey)](https://github.com/kitex-contrib/registry-etcd) 10 | [![Jaeger](https://img.shields.io/badge/Jaeger-v2.30.0-blue)](https://github.com/jaegertracing/jaeger-client-go) 11 | [![JWT](https://img.shields.io/badge/JWT-v3.2.2-green)](https://github.com/golang-jwt/jwt) 12 | [![ffmpeg](https://img.shields.io/badge/ffmpeg-v0.4.1-orange)](https://github.com/u2takey/ffmpeg-go) 13 | 14 | ## 一、介绍 15 | 16 | - 1.基于RPC框架**Kitex**、HTTP框架**Gin**、ORM框架**GORM**的极简版抖音服务端项目 17 | - 2.代码采用api层、service层、dal层三层结构 18 | - 3.使用Kitex构建RPC微服务,Gin构建HTTP服务 19 | - 4.GORM操作MySQL数据库,防止SQL注入,使用事务保证数据一致性,完整性 20 | - 5.使用**ETCD**进行服务注册、服务发现,**Jarger**进行链路追踪 21 | - 6.使用**MySQL**数据库进行数据存储,并建立索引 22 | - 7.使用**OSS**进行视频对象存储,分片上传视频 23 | - 8.使用**JWT**鉴权,**MD5**密码加密,**ffmpeg**获取视频第一帧当作视频封面 24 | - 9.进行了**单元测试**,api**自动化测试**,[Apifox接口文档](https://www.apifox.cn/apidoc/shared-2a880467-5d93-4621-b152-a27bc722058c) 25 | - 10.[演示视频及客户端apk](https://bytedancecampus1.feishu.cn/docx/doxcn8sH7FS6BWFPzG4GaTf187b) 26 | 27 | | 服务名 | 用途 | 框架 | 协议 | 注册中心 | 链路追踪 | 数据存储 | 日志 | 28 | | -------- | ---------------------------------- | --------------------- | ---------- | -------- | -------- | ------------- | ------ | 29 | | api | http接口,通过RPC客户端调用RPC服务 | `gin` `kitex` | `http` | `etcd` | `jaeger` | | `klog` | 30 | | feed | 视频流RPC微服务 | `kitex` `gorm` | `protobuf` | `etcd` | `jaeger` | `mysql` | `klog` | 31 | | publish | 视频上传RPC微服务 | `kitex` `gorm` `ffmpeg`| `protobuf` | `etcd` | `jaeger` | `mysql` `oss` | `klog` | 32 | | user | 用户RPC微服务 | `kitex` `gorm` `jwt` | `protobuf` | `etcd` | `jaeger` | `mysql` | `klog` | 33 | | favorite | 点赞RPC微服务 | `kitex` `gorm` | `protobuf` | `etcd` | `jaeger` | `mysql` | `klog` | 34 | | comment | 评论RPC微服务 | `kitex` `gorm` | `protobuf` | `etcd` | `jaeger` | `mysql` | `klog` | 35 | | relation | 关注RPC微服务 | `kitex` `gorm` | `protobuf` | `etcd` | `jaeger` | `mysql` | `klog` | 36 | 37 | ## 二、调用关系图 38 | 39 | 40 | 41 | ## 三、数据库ER图 42 | 43 | 44 | 45 | ## 四、文件目录结构 46 | 47 | | 目录| 说明 | 48 | | --- | ---- | 49 | | [cmd/api](https://github.com/chenmengangzhi29/douyin/tree/main/cmd/api) | api层代码,处理外部http请求,通过rpc客户端发起rpc请求| 50 | | [cmd/feed](https://github.com/chenmengangzhi29/douyin/tree/main/cmd/feed) | 视频流微服务,包含获取视频流功能,处理api层rpc请求,调用dal层处理数据 | 51 | | [cmd/publish](https://github.com/chenmengangzhi29/douyin/tree/main/cmd/publish) | publish微服务,包含视频上传、视频列表等功能,处理api层rpc请求,调用dal层处理数据 | 52 | | [cmd/user](https://github.com/chenmengangzhi29/douyin/tree/main/cmd/user) | user微服务,包含用户注册、用户登录、用户信息等功能,处理api层rpc请求,调用dal层处理数据 | 53 | | [cmd/favorite](https://github.com/chenmengangzhi29/douyin/tree/main/cmd/favorite) | favorite微服务,包含点赞、取消点赞、点赞列表等功能,处理api层rpc请求,调用dal层处理数据 | 54 | | [cmd/comment](https://github.com/chenmengangzhi29/douyin/tree/main/cmd/comment) | comment微服务,包含增加评论,删除评论,评论列表等功能,处理api层rpc请求,调用dal层处理数据 | 55 | | [cmd/relation](https://github.com/chenmengangzhi29/douyin/tree/main/cmd/relation) | relation微服务,包含关注、取消关注、关注列表等功能,处理api层rpc请求,调用dal层处理数据 | 56 | | [dal/db](https://github.com/chenmengangzhi29/douyin/tree/main/dal/db) | 使用gorm进行底层数据库操作 | 57 | | [dal/pack](https://github.com/chenmengangzhi29/douyin/tree/main/dal/pack) | 封装gorm结构数据为protobuf结构数据 | 58 | | [idl](https://github.com/chenmengangzhi29/douyin/tree/main/idl) | proto IDL文件 | 59 | | [kitex_gen](https://github.com/chenmengangzhi29/douyin/tree/main/kitex_gen) | kitex框架生成的IDL内容相关代码 | 60 | | [pkg/bound](https://github.com/chenmengangzhi29/douyin/tree/main/pkg/bound) | 限制CPU的相关代码 | 61 | | [pkg/constants](https://github.com/chenmengangzhi29/douyin/tree/main/pkg/constants) | 项目中的配置及常量代码 | 62 | | [pkg/errno](https://github.com/chenmengangzhi29/douyin/tree/main/pkg/errno) | 错误码的代码封装 | 63 | | [pkg/jwt](https://github.com/chenmengangzhi29/douyin/tree/main/pkg/jwt) | jwt鉴权的代码封装 | 64 | | [pkg/middleware](https://github.com/chenmengangzhi29/douyin/tree/main/pkg/middleware) | rpc的中间件 | 65 | | [pkg/oss](https://github.com/chenmengangzhi29/douyin/tree/main/pkg/oss) | oss操作的相关代码 | 66 | | [pkg/tracer](https://github.com/chenmengangzhi29/douyin/tree/main/pkg/tracer) | 初始化jaeger | 67 | | [public](https://github.com/chenmengangzhi29/douyin/tree/main/public) | 存放本地视频 | 68 | 69 | ## 五、代码运行 70 | 71 | ### 1.更改配置 72 | 更改 constants/constant.go 中的地址配置 73 | 74 | ### 2.建立基础依赖 75 | ```shell 76 | docker-compose up 77 | ``` 78 | 79 | ### 3.运行feed微服务 80 | ```shell 81 | cd cmd/feed 82 | sh build.sh 83 | sh output/bootstrap.sh 84 | ``` 85 | 86 | ### 4.运行publish微服务 87 | ```shell 88 | cd cmd/publish 89 | sh build.sh 90 | sh output/bootstrap.sh 91 | ``` 92 | 93 | ### 5.运行user微服务 94 | ```shell 95 | cd cmd/user 96 | sh build.sh 97 | sh output/bootstrap.sh 98 | ``` 99 | 100 | ### 6.运行favorite微服务 101 | ```shell 102 | cd cmd/favorite 103 | sh build.sh 104 | sh output/bootstrap.sh 105 | ``` 106 | 107 | ### 7.运行comment微服务 108 | ```shell 109 | cd cmd/comment 110 | sh build.sh 111 | sh output/bootstrap.sh 112 | ``` 113 | 114 | ### 8.运行relation微服务 115 | ```shell 116 | cd cmd/relation 117 | sh build.sh 118 | sh output/bootstrap.sh 119 | ``` 120 | 121 | ### 9.运行api微服务 122 | ```shell 123 | cd cmd/api 124 | chmod +x run.sh 125 | ./run.sh 126 | ``` 127 | 128 | ### 10.Jaeger链路追踪 129 | 130 | 在浏览器上查看`http://127.0.0.1:16686/` 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /cmd/api/handlers/comment.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "context" 5 | "strconv" 6 | "unicode/utf8" 7 | 8 | "github.com/chenmengangzhi29/douyin/cmd/api/rpc" 9 | "github.com/chenmengangzhi29/douyin/kitex_gen/comment" 10 | "github.com/chenmengangzhi29/douyin/pkg/constants" 11 | "github.com/chenmengangzhi29/douyin/pkg/errno" 12 | "github.com/gin-gonic/gin" 13 | ) 14 | 15 | // CommentAction implement adding and deleting comments 16 | func CommentAction(c *gin.Context) { 17 | token := c.Query("token") 18 | videoIdStr := c.Query("video_id") 19 | actionTypeStr := c.Query("action_type") 20 | 21 | videoId, err := strconv.ParseInt(videoIdStr, 10, 64) 22 | if err != nil { 23 | SendResponse(c, errno.ParamParseErr) 24 | return 25 | } 26 | 27 | actionType, err := strconv.ParseInt(actionTypeStr, 10, 64) 28 | if err != nil { 29 | SendResponse(c, errno.ParamParseErr) 30 | return 31 | } 32 | 33 | if actionType == constants.AddComment { 34 | commentText := c.Query("comment_text") 35 | 36 | if len := utf8.RuneCountInString(commentText); len > 20 { 37 | SendResponse(c, errno.CommentTextErr) 38 | return 39 | } 40 | 41 | req := &comment.CreateCommentRequest{Token: token, VideoId: videoId, CommentText: commentText} 42 | comment, err := rpc.CreateComment(context.Background(), req) 43 | if err != nil { 44 | SendResponse(c, errno.ConvertErr(err)) 45 | return 46 | } 47 | SendCommentActionResponse(c, errno.Success, comment) 48 | 49 | } else if actionType == constants.DelComment { 50 | commentIdStr := c.Query("comment_id") 51 | 52 | commentId, err := strconv.ParseInt(commentIdStr, 10, 64) 53 | if err != nil { 54 | SendResponse(c, errno.ParamParseErr) 55 | } 56 | 57 | req := &comment.DeleteCommentRequest{Token: token, VideoId: videoId, CommentId: commentId} 58 | comment, err := rpc.DeleteComment(context.Background(), req) 59 | if err != nil { 60 | SendResponse(c, errno.ConvertErr(err)) 61 | return 62 | } 63 | SendCommentActionResponse(c, errno.Success, comment) 64 | 65 | } else { 66 | SendResponse(c, errno.ParamErr) 67 | } 68 | } 69 | 70 | //CommentList get comment list info 71 | func CommentList(c *gin.Context) { 72 | token := c.Query("token") 73 | videoIdStr := c.Query("video_id") 74 | 75 | videoId, err := strconv.ParseInt(videoIdStr, 10, 64) 76 | if err != nil { 77 | SendResponse(c, errno.ParamParseErr) 78 | } 79 | 80 | req := &comment.CommentListRequest{Token: token, VideoId: videoId} 81 | commentList, err := rpc.CommentList(context.Background(), req) 82 | if err != nil { 83 | SendResponse(c, errno.ConvertErr(err)) 84 | return 85 | } 86 | 87 | SendCommentListResponse(c, errno.Success, commentList) 88 | } 89 | -------------------------------------------------------------------------------- /cmd/api/handlers/favorite.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "context" 5 | "strconv" 6 | 7 | "github.com/chenmengangzhi29/douyin/cmd/api/rpc" 8 | "github.com/chenmengangzhi29/douyin/kitex_gen/favorite" 9 | "github.com/chenmengangzhi29/douyin/pkg/errno" 10 | "github.com/gin-gonic/gin" 11 | ) 12 | 13 | // FavoriteAction implement like and unlike operations 14 | func FavoriteAction(c *gin.Context) { 15 | token := c.Query("token") 16 | videoIdStr := c.Query("video_id") 17 | actionTypeStr := c.Query("action_type") 18 | 19 | videoId, err := strconv.ParseInt(videoIdStr, 10, 64) 20 | if err != nil { 21 | SendResponse(c, errno.ParamParseErr) 22 | return 23 | } 24 | 25 | actionType, err := strconv.ParseInt(actionTypeStr, 10, 64) 26 | if err != nil { 27 | SendResponse(c, errno.ParamParseErr) 28 | return 29 | } 30 | 31 | err = rpc.FavoriteAction(context.Background(), &favorite.FavoriteActionRequest{ 32 | Token: token, VideoId: videoId, ActionType: int32(actionType), 33 | }) 34 | if err != nil { 35 | SendResponse(c, errno.ConvertErr(err)) 36 | return 37 | } 38 | 39 | SendResponse(c, errno.Success) 40 | } 41 | 42 | //FavoriteList get favorite list info 43 | func FavoriteList(c *gin.Context) { 44 | userIdStr := c.Query("user_id") 45 | token := c.Query("token") 46 | 47 | userId, err := strconv.ParseInt(userIdStr, 10, 64) 48 | if err != nil { 49 | SendResponse(c, errno.ParamParseErr) 50 | return 51 | } 52 | 53 | videoList, err := rpc.FavoriteList(context.Background(), &favorite.FavoriteListRequest{ 54 | Token: token, UserId: userId, 55 | }) 56 | if err != nil { 57 | SendResponse(c, errno.ConvertErr(err)) 58 | return 59 | } 60 | 61 | SendFavoriteListResponse(c, errno.Success, videoList) 62 | } 63 | -------------------------------------------------------------------------------- /cmd/api/handlers/feed.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "context" 5 | "strconv" 6 | "time" 7 | 8 | "github.com/chenmengangzhi29/douyin/cmd/api/rpc" 9 | "github.com/chenmengangzhi29/douyin/kitex_gen/feed" 10 | "github.com/chenmengangzhi29/douyin/pkg/errno" 11 | "github.com/gin-gonic/gin" 12 | ) 13 | 14 | //Feed get video feed data 15 | func Feed(c *gin.Context) { 16 | 17 | token := c.DefaultQuery("token", "") 18 | defaultTime := time.Now().UnixMilli() 19 | defaultTimeStr := strconv.Itoa(int(defaultTime)) 20 | latestTimeStr := c.DefaultQuery("latest_time", defaultTimeStr) 21 | 22 | //处理传入参数 23 | latestTime, err := strconv.ParseInt(latestTimeStr, 10, 64) 24 | if err != nil { 25 | SendResponse(c, err) 26 | return 27 | } 28 | 29 | req := &feed.FeedRequest{LatestTime: latestTime, Token: token} 30 | video, nextTime, err := rpc.Feed(context.Background(), req) 31 | if err != nil { 32 | SendResponse(c, err) 33 | return 34 | } 35 | SendFeedResponse(c, errno.Success, video, nextTime) 36 | } 37 | -------------------------------------------------------------------------------- /cmd/api/handlers/handler.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/chenmengangzhi29/douyin/pkg/errno" 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | type UserLoginParam struct { 11 | Username string `form:"username" json:"username" binding:"required"` 12 | Password string `form:"password" json:"password" binding:"required"` 13 | } 14 | 15 | type Response struct { 16 | StatusCode int32 `json:"status_code"` 17 | StatusMsg string `json:"status_msg"` 18 | } 19 | 20 | // SendResponse pack response 21 | func SendResponse(c *gin.Context, err error) { 22 | Err := errno.ConvertErr(err) 23 | c.JSON(http.StatusOK, Response{ 24 | StatusCode: Err.ErrCode, 25 | StatusMsg: Err.ErrMsg, 26 | }) 27 | } 28 | 29 | type FeedResponse struct { 30 | StatusCode int32 `json:"status_code"` 31 | StatusMsg string `json:"status_msg"` 32 | VideoList interface{} `json:"video_list,omitempty"` 33 | NextTime int64 `json:"next_time,omitempty"` 34 | } 35 | 36 | // SendFeedResponse pack feed response 37 | func SendFeedResponse(c *gin.Context, err error, videoList interface{}, nextTime int64) { 38 | Err := errno.ConvertErr(err) 39 | c.JSON(http.StatusOK, FeedResponse{ 40 | StatusCode: Err.ErrCode, 41 | StatusMsg: Err.ErrMsg, 42 | VideoList: videoList, 43 | NextTime: nextTime, 44 | }) 45 | } 46 | 47 | type VideoListResponse struct { 48 | StatusCode int32 `json:"status_code"` 49 | StatusMsg string `json:"status_msg"` 50 | VideoList interface{} `json:"video_list,omitempty"` 51 | } 52 | 53 | // SendVideoListResponse pack video list response 54 | func SendVideoListResponse(c *gin.Context, err error, videoList interface{}) { 55 | Err := errno.ConvertErr(err) 56 | c.JSON(http.StatusOK, VideoListResponse{ 57 | StatusCode: Err.ErrCode, 58 | StatusMsg: Err.ErrMsg, 59 | VideoList: videoList, 60 | }) 61 | } 62 | 63 | type UserResponse struct { 64 | StatusCode int32 `json:"status_code"` 65 | StatusMsg string `json:"status_msg"` 66 | UserId int64 `json:"user_id,omitempty"` 67 | Token string `json:"token,omitempty"` 68 | } 69 | 70 | // SendUserResponse pack user response 71 | func SendUserResponse(c *gin.Context, err error, userId int64, token string) { 72 | Err := errno.ConvertErr(err) 73 | c.JSON(http.StatusOK, UserResponse{ 74 | StatusCode: Err.ErrCode, 75 | StatusMsg: Err.ErrMsg, 76 | UserId: userId, 77 | Token: token, 78 | }) 79 | } 80 | 81 | type UserInfoResponse struct { 82 | StatusCode int32 `json:"status_code"` 83 | StatusMsg string `json:"status_msg"` 84 | User interface{} `json:"user"` 85 | } 86 | 87 | // SendUserInfoResponse pack user info response 88 | func SendUserInfoResponse(c *gin.Context, err error, user interface{}) { 89 | Err := errno.ConvertErr(err) 90 | c.JSON(http.StatusOK, UserInfoResponse{ 91 | StatusCode: Err.ErrCode, 92 | StatusMsg: Err.ErrMsg, 93 | User: user, 94 | }) 95 | } 96 | 97 | type FavoriteListResponse struct { 98 | StatusCode int32 `json:"status_code"` 99 | StatusMsg string `json:"status_msg"` 100 | VideoList interface{} `json:"video_list,omitempty"` 101 | } 102 | 103 | // SendFavoriteListResponse pack favorite list response 104 | func SendFavoriteListResponse(c *gin.Context, err error, videoList interface{}) { 105 | Err := errno.ConvertErr(err) 106 | c.JSON(http.StatusOK, FavoriteListResponse{ 107 | StatusCode: Err.ErrCode, 108 | StatusMsg: Err.ErrMsg, 109 | VideoList: videoList, 110 | }) 111 | } 112 | 113 | type CommentActionResponse struct { 114 | StatusCode int32 `json:"status_code"` 115 | StatusMsg string `json:"status_msg"` 116 | Comment interface{} `json:"comment,omitempty"` 117 | } 118 | 119 | // SendCommentActionResponse pack comment action response 120 | func SendCommentActionResponse(c *gin.Context, err error, comment interface{}) { 121 | Err := errno.ConvertErr(err) 122 | c.JSON(http.StatusOK, CommentActionResponse{ 123 | StatusCode: Err.ErrCode, 124 | StatusMsg: Err.ErrMsg, 125 | Comment: comment, 126 | }) 127 | } 128 | 129 | type CommentListResponse struct { 130 | StatusCode int32 `json:"status_code"` 131 | StatusMsg string `json:"status_msg"` 132 | CommentList interface{} `json:"comment_list,omitempty"` 133 | } 134 | 135 | // SendCommentListResponse pack comment list response 136 | func SendCommentListResponse(c *gin.Context, err error, commentList interface{}) { 137 | Err := errno.ConvertErr(err) 138 | c.JSON(http.StatusOK, CommentListResponse{ 139 | StatusCode: Err.ErrCode, 140 | StatusMsg: Err.ErrMsg, 141 | CommentList: commentList, 142 | }) 143 | } 144 | 145 | type RelationListResponse struct { 146 | StatusCode int32 `json:"status_code"` 147 | StatusMsg string `json:"status_msg,omitempty"` 148 | UserList interface{} `json:"user_list,omitempty"` 149 | } 150 | 151 | // SendRelationListResponse pack relation list response 152 | func SendRelationListResponse(c *gin.Context, err error, userList interface{}) { 153 | Err := errno.ConvertErr(err) 154 | c.JSON(http.StatusOK, RelationListResponse{ 155 | StatusCode: Err.ErrCode, 156 | StatusMsg: Err.ErrMsg, 157 | UserList: userList, 158 | }) 159 | } 160 | -------------------------------------------------------------------------------- /cmd/api/handlers/publish.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "io" 7 | "strconv" 8 | 9 | "github.com/chenmengangzhi29/douyin/cmd/api/rpc" 10 | "github.com/chenmengangzhi29/douyin/kitex_gen/publish" 11 | "github.com/chenmengangzhi29/douyin/pkg/errno" 12 | "github.com/gin-gonic/gin" 13 | ) 14 | 15 | //Publish upload video datas 16 | func Publish(c *gin.Context) { 17 | title := c.PostForm("title") 18 | token := c.PostForm("token") 19 | 20 | data, _, err := c.Request.FormFile("data") 21 | if err != nil { 22 | SendResponse(c, errno.ConvertErr(err)) 23 | return 24 | } 25 | defer data.Close() 26 | 27 | if length := len(title); length <= 0 || length > 128 { 28 | SendResponse(c, errno.ParamErr) 29 | return 30 | } 31 | 32 | //处理视频数据 33 | buf := bytes.NewBuffer(nil) 34 | if _, err := io.Copy(buf, data); err != nil { 35 | SendResponse(c, errno.VideoDataCopyErr) 36 | return 37 | } 38 | video := buf.Bytes() 39 | 40 | err = rpc.PublishVideoData(context.Background(), &publish.PublishActionRequest{ 41 | Token: token, 42 | Title: title, Data: video, 43 | }) 44 | if err != nil { 45 | SendResponse(c, errno.ConvertErr(err)) 46 | return 47 | } 48 | 49 | SendResponse(c, errno.Success) 50 | } 51 | 52 | //PublishList get publish list 53 | func PublishList(c *gin.Context) { 54 | token := c.Query("token") 55 | userIdStr := c.Query("user_id") 56 | 57 | userId, err := strconv.ParseInt(userIdStr, 10, 64) 58 | if err != nil { 59 | SendResponse(c, errno.ParamParseErr) 60 | } 61 | 62 | videoList, err := rpc.QueryVideoList(context.Background(), &publish.PublishListRequest{ 63 | Token: token, 64 | UserId: userId, 65 | }) 66 | if err != nil { 67 | SendResponse(c, errno.ConvertErr(err)) 68 | return 69 | } 70 | 71 | SendVideoListResponse(c, errno.Success, videoList) 72 | } 73 | -------------------------------------------------------------------------------- /cmd/api/handlers/relation.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "context" 5 | "strconv" 6 | 7 | "github.com/chenmengangzhi29/douyin/cmd/api/rpc" 8 | "github.com/chenmengangzhi29/douyin/kitex_gen/relation" 9 | "github.com/chenmengangzhi29/douyin/pkg/constants" 10 | "github.com/chenmengangzhi29/douyin/pkg/errno" 11 | "github.com/gin-gonic/gin" 12 | ) 13 | 14 | //RelationAction implement follow and unfollow actions 15 | func RelationAction(c *gin.Context) { 16 | token := c.Query("token") 17 | toUserIdStr := c.Query("to_user_id") 18 | actionTypeStr := c.Query("action_type") 19 | 20 | if len(token) == 0 { 21 | SendResponse(c, errno.ParamErr) 22 | return 23 | } 24 | 25 | toUserId, err := strconv.ParseInt(toUserIdStr, 10, 64) 26 | if err != nil { 27 | SendResponse(c, errno.ParamParseErr) 28 | return 29 | } 30 | 31 | actionType, err := strconv.ParseInt(actionTypeStr, 10, 64) 32 | if err != nil { 33 | SendResponse(c, errno.ParamParseErr) 34 | return 35 | } 36 | if actionType != constants.Follow && actionType != constants.UnFollow { 37 | SendResponse(c, errno.ActionTypeErr) 38 | return 39 | } 40 | 41 | req := &relation.RelationActionRequest{Token: token, ToUserId: toUserId, ActionType: int32(actionType)} 42 | err = rpc.RelationAction(context.Background(), req) 43 | if err != nil { 44 | SendResponse(c, err) 45 | return 46 | } 47 | SendResponse(c, errno.Success) 48 | } 49 | 50 | // Followlist get user follow list info 51 | func FollowList(c *gin.Context) { 52 | token := c.Query("token") 53 | userIdStr := c.Query("user_id") 54 | 55 | userId, err := strconv.ParseInt(userIdStr, 10, 64) 56 | if err != nil { 57 | SendResponse(c, errno.ParamParseErr) 58 | return 59 | } 60 | 61 | req := &relation.FollowListRequest{Token: token, UserId: userId} 62 | userList, err := rpc.FollowList(context.Background(), req) 63 | if err != nil { 64 | SendResponse(c, err) 65 | return 66 | } 67 | SendRelationListResponse(c, errno.Success, userList) 68 | } 69 | 70 | // FollowerList get user follower list info 71 | func FollowerList(c *gin.Context) { 72 | token := c.Query("token") 73 | userIdStr := c.Query("user_id") 74 | 75 | userId, err := strconv.ParseInt(userIdStr, 10, 64) 76 | if err != nil { 77 | SendResponse(c, errno.ParamParseErr) 78 | return 79 | } 80 | 81 | req := &relation.FollowerListRequest{Token: token, UserId: userId} 82 | userList, err := rpc.FollowerList(context.Background(), req) 83 | if err != nil { 84 | SendResponse(c, err) 85 | return 86 | } 87 | SendRelationListResponse(c, errno.Success, userList) 88 | } 89 | -------------------------------------------------------------------------------- /cmd/api/handlers/user.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "context" 5 | "strconv" 6 | 7 | "github.com/chenmengangzhi29/douyin/cmd/api/rpc" 8 | "github.com/chenmengangzhi29/douyin/kitex_gen/user" 9 | "github.com/chenmengangzhi29/douyin/pkg/errno" 10 | "github.com/gin-gonic/gin" 11 | ) 12 | 13 | // Register register uesr info 14 | func Register(c *gin.Context) { 15 | username := c.Query("username") 16 | password := c.Query("password") 17 | 18 | if len := len(username); len <= 0 || len > 32 { 19 | SendResponse(c, errno.UserNameValidationErr) 20 | return 21 | } 22 | 23 | if len := len(password); len <= 0 || len > 32 { 24 | SendResponse(c, errno.PasswordValidationErr) 25 | return 26 | } 27 | 28 | userId, token, err := rpc.RegisterUser(context.Background(), &user.RegisterUserRequest{ 29 | Username: username, 30 | Password: password, 31 | }) 32 | if err != nil { 33 | SendResponse(c, errno.ConvertErr(err)) 34 | return 35 | } 36 | SendUserResponse(c, errno.Success, userId, token) 37 | } 38 | 39 | // UserInfo get user info 40 | func UserInfo(c *gin.Context) { 41 | userIdStr := c.Query("user_id") 42 | token := c.DefaultQuery("token", "") 43 | 44 | userId, err := strconv.ParseInt(userIdStr, 10, 64) 45 | if err != nil { 46 | SendResponse(c, errno.ParamParseErr) 47 | return 48 | } 49 | 50 | user, err := rpc.UserInfo(context.Background(), &user.UserInfoRequest{ 51 | UserId: userId, 52 | Token: token, 53 | }) 54 | if err != nil { 55 | SendResponse(c, errno.ConvertErr(err)) 56 | } 57 | SendUserInfoResponse(c, errno.Success, user) 58 | } 59 | -------------------------------------------------------------------------------- /cmd/api/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | "time" 7 | 8 | jwt "github.com/appleboy/gin-jwt/v2" 9 | "github.com/chenmengangzhi29/douyin/cmd/api/handlers" 10 | "github.com/chenmengangzhi29/douyin/cmd/api/rpc" 11 | "github.com/chenmengangzhi29/douyin/kitex_gen/user" 12 | "github.com/chenmengangzhi29/douyin/pkg/constants" 13 | "github.com/chenmengangzhi29/douyin/pkg/errno" 14 | JWT "github.com/chenmengangzhi29/douyin/pkg/jwt" 15 | "github.com/chenmengangzhi29/douyin/pkg/tracer" 16 | "github.com/cloudwego/kitex/pkg/klog" 17 | "github.com/gin-gonic/gin" 18 | ) 19 | 20 | func Init() { 21 | tracer.InitJaeger(constants.ApiServiceName) 22 | rpc.InitRPC() 23 | } 24 | 25 | func main() { 26 | Init() 27 | r := gin.New() 28 | authMiddleware, err := jwt.New(&jwt.GinJWTMiddleware{ 29 | Key: []byte(constants.SecretKey), 30 | Timeout: time.Hour, 31 | MaxRefresh: time.Hour, 32 | PayloadFunc: func(data interface{}) jwt.MapClaims { 33 | if v, ok := data.(int64); ok { 34 | return jwt.MapClaims{ 35 | constants.IdentiryKey: v, 36 | } 37 | } 38 | return jwt.MapClaims{} 39 | }, 40 | Authenticator: func(c *gin.Context) (interface{}, error) { 41 | var loginVar handlers.UserLoginParam 42 | if err := c.ShouldBind(&loginVar); err != nil { 43 | return "", jwt.ErrMissingLoginValues 44 | } 45 | if len(loginVar.Username) == 0 || len(loginVar.Password) == 0 { 46 | return "", jwt.ErrMissingLoginValues 47 | } 48 | return rpc.CheckUser(context.Background(), &user.CheckUserRequest{Username: loginVar.Username, Password: loginVar.Password}) 49 | }, 50 | LoginResponse: func(c *gin.Context, code int, message string, time time.Time) { 51 | Jwt := JWT.NewJWT([]byte(constants.SecretKey)) 52 | userId, err := Jwt.CheckToken(message) 53 | if err != nil { 54 | panic(err) 55 | } 56 | handlers.SendUserResponse(c, errno.Success, userId, message) 57 | }, 58 | TokenLookup: "header: Authorization, query: token, cookie: jwt", 59 | TokenHeadName: "Bearer", 60 | TimeFunc: time.Now, 61 | }) 62 | 63 | if err != nil { 64 | klog.Fatal("JWT Error:" + err.Error()) 65 | } 66 | 67 | douyin := r.Group("/douyin") 68 | douyin.GET("/feed/", handlers.Feed) 69 | 70 | publish := douyin.Group("/publish") 71 | publish.POST("/action/", handlers.Publish) 72 | publish.GET("/list/", handlers.PublishList) 73 | 74 | user := douyin.Group("/user") 75 | user.GET("/", handlers.UserInfo) 76 | user.POST("/register/", handlers.Register) 77 | user.POST("/login/", authMiddleware.LoginHandler) 78 | 79 | favorite := douyin.Group("/favorite") 80 | favorite.POST("/action/", handlers.FavoriteAction) 81 | favorite.GET("/list/", handlers.FavoriteList) 82 | 83 | comment := douyin.Group("/comment") 84 | comment.POST("/action/", handlers.CommentAction) 85 | comment.GET("/list/", handlers.CommentList) 86 | 87 | relation := douyin.Group("/relation") 88 | relation.POST("/action/", handlers.RelationAction) 89 | relation.GET("/follow/list/", handlers.FollowList) 90 | relation.GET("/follower/list/", handlers.FollowerList) 91 | 92 | if err := http.ListenAndServe(constants.ApiAddress, r); err != nil { 93 | klog.Fatal(err) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /cmd/api/rpc/comment.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/chenmengangzhi29/douyin/kitex_gen/comment" 8 | "github.com/chenmengangzhi29/douyin/kitex_gen/comment/commentservice" 9 | "github.com/chenmengangzhi29/douyin/pkg/constants" 10 | "github.com/chenmengangzhi29/douyin/pkg/errno" 11 | "github.com/chenmengangzhi29/douyin/pkg/middleware" 12 | "github.com/cloudwego/kitex/client" 13 | "github.com/cloudwego/kitex/pkg/retry" 14 | etcd "github.com/kitex-contrib/registry-etcd" 15 | trace "github.com/kitex-contrib/tracer-opentracing" 16 | ) 17 | 18 | var commentClient commentservice.Client 19 | 20 | func initCommentRpc() { 21 | r, err := etcd.NewEtcdResolver([]string{constants.EtcdAddress}) 22 | if err != nil { 23 | panic(err) 24 | } 25 | 26 | c, err := commentservice.NewClient( 27 | constants.CommentServiceName, 28 | client.WithMiddleware(middleware.CommonMiddleware), 29 | client.WithInstanceMW(middleware.ClientMiddleware), 30 | client.WithMuxConnection(1), 31 | client.WithRPCTimeout(3*time.Second), 32 | client.WithConnectTimeout(50*time.Millisecond), 33 | client.WithFailureRetry(retry.NewFailurePolicy()), 34 | client.WithSuite(trace.NewDefaultClientSuite()), 35 | client.WithResolver(r), 36 | ) 37 | if err != nil { 38 | panic(err) 39 | } 40 | commentClient = c 41 | } 42 | 43 | // CreateComment add comment 44 | func CreateComment(ctx context.Context, req *comment.CreateCommentRequest) (*comment.Comment, error) { 45 | resp, err := commentClient.CreateComment(ctx, req) 46 | if err != nil { 47 | return nil, err 48 | } 49 | if resp.BaseResp.StatusCode != 0 { 50 | return nil, errno.NewErrNo(resp.BaseResp.StatusCode, resp.BaseResp.StatusMessage) 51 | } 52 | return resp.Comment, nil 53 | } 54 | 55 | // DeleteComment delete comment 56 | func DeleteComment(ctx context.Context, req *comment.DeleteCommentRequest) (*comment.Comment, error) { 57 | resp, err := commentClient.DeleteComment(ctx, req) 58 | if err != nil { 59 | return nil, err 60 | } 61 | if resp.BaseResp.StatusCode != 0 { 62 | return nil, errno.NewErrNo(resp.BaseResp.StatusCode, resp.BaseResp.StatusMessage) 63 | } 64 | return resp.Comment, nil 65 | } 66 | 67 | // CommentList get comment list info 68 | func CommentList(ctx context.Context, req *comment.CommentListRequest) ([]*comment.Comment, error) { 69 | resp, err := commentClient.CommentList(ctx, req) 70 | if err != nil { 71 | return nil, err 72 | } 73 | if resp.BaseResp.StatusCode != 0 { 74 | return nil, errno.NewErrNo(resp.BaseResp.StatusCode, resp.BaseResp.StatusMessage) 75 | } 76 | return resp.CommentList, nil 77 | } 78 | -------------------------------------------------------------------------------- /cmd/api/rpc/favorite.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/chenmengangzhi29/douyin/kitex_gen/favorite" 8 | "github.com/chenmengangzhi29/douyin/kitex_gen/favorite/favoriteservice" 9 | "github.com/chenmengangzhi29/douyin/pkg/constants" 10 | "github.com/chenmengangzhi29/douyin/pkg/errno" 11 | "github.com/chenmengangzhi29/douyin/pkg/middleware" 12 | "github.com/cloudwego/kitex/client" 13 | "github.com/cloudwego/kitex/pkg/retry" 14 | etcd "github.com/kitex-contrib/registry-etcd" 15 | trace "github.com/kitex-contrib/tracer-opentracing" 16 | ) 17 | 18 | var favoriteClient favoriteservice.Client 19 | 20 | func initFavoriteRpc() { 21 | r, err := etcd.NewEtcdResolver([]string{constants.EtcdAddress}) 22 | if err != nil { 23 | panic(err) 24 | } 25 | 26 | c, err := favoriteservice.NewClient( 27 | constants.FavoriteServiceName, 28 | client.WithMiddleware(middleware.CommonMiddleware), 29 | client.WithInstanceMW(middleware.ClientMiddleware), 30 | client.WithMuxConnection(1), 31 | client.WithRPCTimeout(3*time.Second), 32 | client.WithConnectTimeout(50*time.Millisecond), 33 | client.WithFailureRetry(retry.NewFailurePolicy()), 34 | client.WithSuite(trace.NewDefaultClientSuite()), 35 | client.WithResolver(r), 36 | ) 37 | if err != nil { 38 | panic(err) 39 | } 40 | favoriteClient = c 41 | } 42 | 43 | // FavoriteAction implement like and unlike operations 44 | func FavoriteAction(ctx context.Context, req *favorite.FavoriteActionRequest) error { 45 | resp, err := favoriteClient.FavoriteAction(ctx, req) 46 | if err != nil { 47 | return err 48 | } 49 | if resp.BaseResp.StatusCode != 0 { 50 | return errno.NewErrNo(resp.BaseResp.StatusCode, resp.BaseResp.StatusMessage) 51 | } 52 | return nil 53 | } 54 | 55 | // FavoriteList get favorite list info 56 | func FavoriteList(ctx context.Context, req *favorite.FavoriteListRequest) ([]*favorite.Video, error) { 57 | resp, err := favoriteClient.FavoriteList(ctx, req) 58 | if err != nil { 59 | return nil, err 60 | } 61 | if resp.BaseResp.StatusCode != 0 { 62 | return nil, errno.NewErrNo(resp.BaseResp.StatusCode, resp.BaseResp.StatusMessage) 63 | } 64 | return resp.VideoList, nil 65 | } 66 | -------------------------------------------------------------------------------- /cmd/api/rpc/feed.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/chenmengangzhi29/douyin/kitex_gen/feed" 8 | "github.com/chenmengangzhi29/douyin/kitex_gen/feed/feedservice" 9 | "github.com/chenmengangzhi29/douyin/pkg/constants" 10 | "github.com/chenmengangzhi29/douyin/pkg/errno" 11 | "github.com/chenmengangzhi29/douyin/pkg/middleware" 12 | "github.com/cloudwego/kitex/client" 13 | "github.com/cloudwego/kitex/pkg/retry" 14 | etcd "github.com/kitex-contrib/registry-etcd" 15 | trace "github.com/kitex-contrib/tracer-opentracing" 16 | ) 17 | 18 | var feedClient feedservice.Client 19 | 20 | func initFeedRpc() { 21 | r, err := etcd.NewEtcdResolver([]string{constants.EtcdAddress}) 22 | if err != nil { 23 | panic(err) 24 | } 25 | 26 | c, err := feedservice.NewClient( 27 | constants.FeedServiceName, 28 | client.WithMiddleware(middleware.CommonMiddleware), 29 | client.WithInstanceMW(middleware.ClientMiddleware), 30 | client.WithMuxConnection(1), 31 | client.WithRPCTimeout(3*time.Second), 32 | client.WithConnectTimeout(50*time.Millisecond), 33 | client.WithFailureRetry(retry.NewFailurePolicy()), 34 | client.WithSuite(trace.NewDefaultClientSuite()), 35 | client.WithResolver(r), 36 | ) 37 | if err != nil { 38 | panic(err) 39 | } 40 | feedClient = c 41 | } 42 | 43 | //Feed query list of video info 44 | func Feed(ctx context.Context, req *feed.FeedRequest) ([]*feed.Video, int64, error) { 45 | resp, err := feedClient.Feed(ctx, req) 46 | if err != nil { 47 | return nil, 0, err 48 | } 49 | if resp.BaseResp.StatusCode != 0 { 50 | return nil, 0, errno.NewErrNo(resp.BaseResp.StatusCode, resp.BaseResp.StatusMessage) 51 | } 52 | return resp.VideoList, resp.NextTime, nil 53 | } 54 | -------------------------------------------------------------------------------- /cmd/api/rpc/init.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | // InitRPC init rpc client 4 | func InitRPC() { 5 | initFeedRpc() 6 | initPublishRpc() 7 | initUserRpc() 8 | initFavoriteRpc() 9 | initCommentRpc() 10 | initRelationRpc() 11 | } 12 | -------------------------------------------------------------------------------- /cmd/api/rpc/publish.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/chenmengangzhi29/douyin/kitex_gen/publish" 8 | "github.com/chenmengangzhi29/douyin/kitex_gen/publish/publishservice" 9 | "github.com/chenmengangzhi29/douyin/pkg/constants" 10 | "github.com/chenmengangzhi29/douyin/pkg/errno" 11 | "github.com/chenmengangzhi29/douyin/pkg/middleware" 12 | "github.com/cloudwego/kitex/client" 13 | "github.com/cloudwego/kitex/pkg/retry" 14 | etcd "github.com/kitex-contrib/registry-etcd" 15 | trace "github.com/kitex-contrib/tracer-opentracing" 16 | ) 17 | 18 | var publishClient publishservice.Client 19 | 20 | func initPublishRpc() { 21 | r, err := etcd.NewEtcdResolver([]string{constants.EtcdAddress}) 22 | if err != nil { 23 | panic(err) 24 | } 25 | 26 | c, err := publishservice.NewClient( 27 | constants.PublishServiceName, 28 | client.WithInstanceMW(middleware.ClientMiddleware), 29 | client.WithMuxConnection(1), 30 | client.WithRPCTimeout(10*time.Second), 31 | client.WithConnectTimeout(10000*time.Millisecond), 32 | client.WithFailureRetry(retry.NewFailurePolicy()), 33 | client.WithSuite(trace.NewDefaultClientSuite()), 34 | client.WithResolver(r), 35 | ) 36 | if err != nil { 37 | panic(err) 38 | } 39 | publishClient = c 40 | } 41 | 42 | // PublishVideoData upload video data 43 | func PublishVideoData(ctx context.Context, req *publish.PublishActionRequest) error { 44 | resp, err := publishClient.PublishAction(ctx, req) 45 | if err != nil { 46 | return err 47 | } 48 | if resp.BaseResp.StatusCode != 0 { 49 | return errno.NewErrNo(resp.BaseResp.StatusCode, resp.BaseResp.StatusMessage) 50 | } 51 | return nil 52 | } 53 | 54 | // QueryVideoList get a list of video releases 55 | func QueryVideoList(ctx context.Context, req *publish.PublishListRequest) ([]*publish.Video, error) { 56 | resp, err := publishClient.PublishList(ctx, req) 57 | if err != nil { 58 | return nil, err 59 | } 60 | if resp.BaseResp.StatusCode != 0 { 61 | return nil, errno.NewErrNo(resp.BaseResp.StatusCode, resp.BaseResp.StatusMessage) 62 | } 63 | return resp.VideoList, nil 64 | } 65 | -------------------------------------------------------------------------------- /cmd/api/rpc/relation.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/chenmengangzhi29/douyin/kitex_gen/relation" 8 | "github.com/chenmengangzhi29/douyin/kitex_gen/relation/relationservice" 9 | "github.com/chenmengangzhi29/douyin/pkg/constants" 10 | "github.com/chenmengangzhi29/douyin/pkg/errno" 11 | "github.com/chenmengangzhi29/douyin/pkg/middleware" 12 | "github.com/cloudwego/kitex/client" 13 | "github.com/cloudwego/kitex/pkg/retry" 14 | etcd "github.com/kitex-contrib/registry-etcd" 15 | trace "github.com/kitex-contrib/tracer-opentracing" 16 | ) 17 | 18 | var relationClient relationservice.Client 19 | 20 | func initRelationRpc() { 21 | r, err := etcd.NewEtcdResolver([]string{constants.EtcdAddress}) 22 | if err != nil { 23 | panic(err) 24 | } 25 | 26 | c, err := relationservice.NewClient( 27 | constants.RelationServiceName, 28 | client.WithMiddleware(middleware.CommonMiddleware), 29 | client.WithInstanceMW(middleware.ClientMiddleware), 30 | client.WithMuxConnection(1), 31 | client.WithRPCTimeout(3*time.Second), 32 | client.WithConnectTimeout(50*time.Millisecond), 33 | client.WithFailureRetry(retry.NewFailurePolicy()), 34 | client.WithSuite(trace.NewDefaultClientSuite()), 35 | client.WithResolver(r), 36 | ) 37 | if err != nil { 38 | panic(err) 39 | } 40 | relationClient = c 41 | } 42 | 43 | // RelationAction implement follow and unfollow actions 44 | func RelationAction(ctx context.Context, req *relation.RelationActionRequest) error { 45 | resp, err := relationClient.RelationAction(ctx, req) 46 | if err != nil { 47 | return err 48 | } 49 | if resp.BaseResp.StatusCode != 0 { 50 | return errno.NewErrNo(resp.BaseResp.StatusCode, resp.BaseResp.StatusMessage) 51 | } 52 | return nil 53 | } 54 | 55 | // FollowList get user follow list info 56 | func FollowList(ctx context.Context, req *relation.FollowListRequest) ([]*relation.User, error) { 57 | resp, err := relationClient.FollowList(ctx, req) 58 | if err != nil { 59 | return nil, err 60 | } 61 | if resp.BaseResp.StatusCode != 0 { 62 | return nil, errno.NewErrNo(resp.BaseResp.StatusCode, resp.BaseResp.StatusMessage) 63 | } 64 | return resp.UserList, nil 65 | } 66 | 67 | // FollowerList get user follower list info 68 | func FollowerList(ctx context.Context, req *relation.FollowerListRequest) ([]*relation.User, error) { 69 | resp, err := relationClient.FollowerList(ctx, req) 70 | if err != nil { 71 | return nil, err 72 | } 73 | if resp.BaseResp.StatusCode != 0 { 74 | return nil, errno.NewErrNo(resp.BaseResp.StatusCode, resp.BaseResp.StatusMessage) 75 | } 76 | return resp.UserList, nil 77 | } 78 | -------------------------------------------------------------------------------- /cmd/api/rpc/user.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/chenmengangzhi29/douyin/kitex_gen/user" 8 | "github.com/chenmengangzhi29/douyin/kitex_gen/user/userservice" 9 | "github.com/chenmengangzhi29/douyin/pkg/constants" 10 | "github.com/chenmengangzhi29/douyin/pkg/errno" 11 | "github.com/chenmengangzhi29/douyin/pkg/middleware" 12 | "github.com/cloudwego/kitex/client" 13 | "github.com/cloudwego/kitex/pkg/retry" 14 | etcd "github.com/kitex-contrib/registry-etcd" 15 | trace "github.com/kitex-contrib/tracer-opentracing" 16 | ) 17 | 18 | var userClient userservice.Client 19 | 20 | func initUserRpc() { 21 | r, err := etcd.NewEtcdResolver([]string{constants.EtcdAddress}) 22 | if err != nil { 23 | panic(err) 24 | } 25 | 26 | c, err := userservice.NewClient( 27 | constants.UserServiceName, 28 | client.WithMiddleware(middleware.CommonMiddleware), 29 | client.WithInstanceMW(middleware.ClientMiddleware), 30 | client.WithMuxConnection(1), 31 | client.WithRPCTimeout(3*time.Second), 32 | client.WithConnectTimeout(50*time.Millisecond), 33 | client.WithFailureRetry(retry.NewFailurePolicy()), 34 | client.WithSuite(trace.NewDefaultClientSuite()), 35 | client.WithResolver(r), 36 | ) 37 | if err != nil { 38 | panic(err) 39 | } 40 | userClient = c 41 | } 42 | 43 | // CheckUser check user info 44 | func CheckUser(ctx context.Context, req *user.CheckUserRequest) (int64, error) { 45 | resp, err := userClient.CheckUser(ctx, req) 46 | if err != nil { 47 | return 0, err 48 | } 49 | if resp.BaseResp.StatusCode != 0 { 50 | return 0, errno.NewErrNo(resp.BaseResp.StatusCode, resp.BaseResp.StatusMessage) 51 | } 52 | return resp.UserId, nil 53 | } 54 | 55 | // RegisterUser register user info 56 | func RegisterUser(ctx context.Context, req *user.RegisterUserRequest) (int64, string, error) { 57 | resp, err := userClient.RegisterUser(ctx, req) 58 | if err != nil { 59 | return 0, "", err 60 | } 61 | if resp.BaseResp.StatusCode != 0 { 62 | return 0, "", errno.NewErrNo(resp.BaseResp.StatusCode, resp.BaseResp.StatusMessage) 63 | } 64 | return resp.UserId, resp.Token, nil 65 | } 66 | 67 | // UserInfo get user info 68 | func UserInfo(ctx context.Context, req *user.UserInfoRequest) (*user.User, error) { 69 | resp, err := userClient.UserInfo(ctx, req) 70 | if err != nil { 71 | return nil, err 72 | } 73 | if resp.BaseResp.StatusCode != 0 { 74 | return nil, errno.NewErrNo(resp.BaseResp.StatusCode, resp.BaseResp.StatusMessage) 75 | } 76 | return resp.User, nil 77 | } 78 | -------------------------------------------------------------------------------- /cmd/api/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | export JAEGER_BISABLED=false; 4 | export JAEGER_SANPLER_TYPE="const"; 5 | export JAEGER_SAMPLER_PARAM=1; 6 | export JAEGER_REPORTER_LOG_SPANS=true; 7 | export JAEGER_AGENT_HOST="127.0.0.1" 8 | export JAEGER_AGENT_PORT=6831 9 | go run ./main.go -------------------------------------------------------------------------------- /cmd/comment/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | RUN_NAME="comment" 3 | 4 | mkdir -p output/bin 5 | cp script/* output/ 6 | chmod +x output/bootstrap.sh 7 | 8 | if [ "$IS_SYSTEM_TEST_ENV" != "1" ]; then 9 | go build -o output/bin/${RUN_NAME} 10 | else 11 | go test -c -covermode=set -o output/bin/${RUN_NAME} -coverpkg=./... 12 | fi 13 | -------------------------------------------------------------------------------- /cmd/comment/handler.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "unicode/utf8" 6 | 7 | "github.com/chenmengangzhi29/douyin/cmd/comment/service" 8 | "github.com/chenmengangzhi29/douyin/dal/pack" 9 | "github.com/chenmengangzhi29/douyin/kitex_gen/comment" 10 | "github.com/chenmengangzhi29/douyin/pkg/errno" 11 | ) 12 | 13 | // CommentServiceImpl implements the last service interface defined in the IDL. 14 | type CommentServiceImpl struct{} 15 | 16 | // CreateComment implements the CommentServiceImpl interface. 17 | func (s *CommentServiceImpl) CreateComment(ctx context.Context, req *comment.CreateCommentRequest) (resp *comment.CreateCommentResponse, err error) { 18 | resp = new(comment.CreateCommentResponse) 19 | 20 | if len(req.Token) == 0 || req.VideoId == 0 || utf8.RuneCountInString(req.CommentText) > 20 { 21 | resp.BaseResp = pack.BuilCommentBaseResp(errno.ParamErr) 22 | return resp, nil 23 | } 24 | 25 | comment, err := service.NewCreateCommentService(ctx).CreateComment(req) 26 | if err != nil { 27 | resp.BaseResp = pack.BuilCommentBaseResp(err) 28 | return resp, nil 29 | } 30 | 31 | resp.BaseResp = pack.BuilCommentBaseResp(errno.Success) 32 | resp.Comment = comment 33 | return resp, nil 34 | } 35 | 36 | // DeleteComment implements the CommentServiceImpl interface. 37 | func (s *CommentServiceImpl) DeleteComment(ctx context.Context, req *comment.DeleteCommentRequest) (resp *comment.DeleteCommentResponse, err error) { 38 | resp = new(comment.DeleteCommentResponse) 39 | 40 | if len(req.Token) == 0 || req.VideoId == 0 || req.CommentId == 0 { 41 | resp.BaseResp = pack.BuilCommentBaseResp(errno.ParamErr) 42 | return resp, nil 43 | } 44 | 45 | comment, err := service.NewDeleteCommentService(ctx).DeleteComment(req) 46 | if err != nil { 47 | resp.BaseResp = pack.BuilCommentBaseResp(err) 48 | return resp, nil 49 | } 50 | 51 | resp.BaseResp = pack.BuilCommentBaseResp(errno.Success) 52 | resp.Comment = comment 53 | return resp, nil 54 | } 55 | 56 | // CommentList implements the CommentServiceImpl interface. 57 | func (s *CommentServiceImpl) CommentList(ctx context.Context, req *comment.CommentListRequest) (resp *comment.CommentListResponse, err error) { 58 | resp = new(comment.CommentListResponse) 59 | 60 | if req.VideoId == 0 { 61 | resp.BaseResp = pack.BuilCommentBaseResp(errno.ParamErr) 62 | return resp, nil 63 | } 64 | 65 | commentList, err := service.NewCommentListService(ctx).CommentList(req) 66 | if err != nil { 67 | resp.BaseResp = pack.BuilCommentBaseResp(err) 68 | return resp, nil 69 | } 70 | 71 | resp.BaseResp = pack.BuilCommentBaseResp(errno.Success) 72 | resp.CommentList = commentList 73 | return resp, nil 74 | } 75 | -------------------------------------------------------------------------------- /cmd/comment/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/chenmengangzhi29/douyin/dal" 7 | comment "github.com/chenmengangzhi29/douyin/kitex_gen/comment/commentservice" 8 | "github.com/chenmengangzhi29/douyin/pkg/bound" 9 | "github.com/chenmengangzhi29/douyin/pkg/constants" 10 | "github.com/chenmengangzhi29/douyin/pkg/middleware" 11 | tracer2 "github.com/chenmengangzhi29/douyin/pkg/tracer" 12 | "github.com/cloudwego/kitex/pkg/klog" 13 | "github.com/cloudwego/kitex/pkg/limit" 14 | "github.com/cloudwego/kitex/pkg/rpcinfo" 15 | "github.com/cloudwego/kitex/server" 16 | etcd "github.com/kitex-contrib/registry-etcd" 17 | trace "github.com/kitex-contrib/tracer-opentracing" 18 | ) 19 | 20 | func Init() { 21 | tracer2.InitJaeger(constants.CommentServiceName) 22 | dal.Init() 23 | } 24 | 25 | func main() { 26 | r, err := etcd.NewEtcdRegistry([]string{constants.EtcdAddress}) //r should not be reused. 27 | if err != nil { 28 | panic(err) 29 | } 30 | addr, err := net.ResolveTCPAddr("tcp", constants.CommentAddress) 31 | if err != nil { 32 | panic(err) 33 | } 34 | Init() 35 | svr := comment.NewServer(new(CommentServiceImpl), 36 | server.WithServerBasicInfo(&rpcinfo.EndpointBasicInfo{ServiceName: constants.CommentServiceName}), //server name 37 | server.WithMiddleware(middleware.CommonMiddleware), 38 | server.WithMiddleware(middleware.ServerMiddleware), 39 | server.WithServiceAddr(addr), //address 40 | server.WithLimit(&limit.Option{MaxConnections: 1000, MaxQPS: 100}), //limit 41 | server.WithMuxTransport(), //Multiplex 42 | server.WithSuite(trace.NewDefaultServerSuite()), //tracer 43 | server.WithBoundHandler(bound.NewCpuLimitHandler()), //BoundHandler 44 | server.WithRegistry(r), //registry 45 | ) 46 | err = svr.Run() 47 | if err != nil { 48 | klog.Fatal(err) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /cmd/comment/script/bootstrap.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | CURDIR=$(cd $(dirname $0); pwd) 3 | 4 | if [ "X$1" != "X" ]; then 5 | RUNTIME_ROOT=$1 6 | else 7 | RUNTIME_ROOT=${CURDIR} 8 | fi 9 | 10 | export KITEX_RUNTIME_ROOT=$RUNTIME_ROOT 11 | export KITEX_LOG_DIR="$RUNTIME_ROOT/log" 12 | 13 | if [ ! -d "$KITEX_LOG_DIR/app" ]; then 14 | mkdir -p "$KITEX_LOG_DIR/app" 15 | fi 16 | 17 | if [ ! -d "$KITEX_LOG_DIR/rpc" ]; then 18 | mkdir -p "$KITEX_LOG_DIR/rpc" 19 | fi 20 | 21 | exec "$CURDIR/bin/comment" 22 | -------------------------------------------------------------------------------- /cmd/comment/service/comment_list.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "github.com/chenmengangzhi29/douyin/dal/db" 8 | "github.com/chenmengangzhi29/douyin/dal/pack" 9 | "github.com/chenmengangzhi29/douyin/kitex_gen/comment" 10 | "github.com/chenmengangzhi29/douyin/pkg/constants" 11 | "github.com/chenmengangzhi29/douyin/pkg/jwt" 12 | ) 13 | 14 | type CommentListService struct { 15 | ctx context.Context 16 | } 17 | 18 | // NewCommentListService new CommentListService 19 | func NewCommentListService(ctx context.Context) *CommentListService { 20 | return &CommentListService{ctx: ctx} 21 | } 22 | 23 | // CommentList get comment list info 24 | func (s *CommentListService) CommentList(req *comment.CommentListRequest) ([]*comment.Comment, error) { 25 | //获取当前用户id号 26 | Jwt := jwt.NewJWT([]byte(constants.SecretKey)) 27 | currentId, _ := Jwt.CheckToken(req.Token) 28 | 29 | //验证视频Id是否存在 30 | videos, err := db.QueryVideoByVideoIds(s.ctx, []int64{req.VideoId}) 31 | if err != nil { 32 | return nil, err 33 | } 34 | if len(videos) == 0 { 35 | return nil, errors.New("video not exist") 36 | } 37 | 38 | //获取一系列评论信息 39 | comments, err := db.QueryCommentByVideoId(s.ctx, req.VideoId) 40 | if err != nil { 41 | return nil, err 42 | } 43 | 44 | //获取评论信息的用户id 45 | userIds := make([]int64, 0) 46 | for _, comment := range comments { 47 | userIds = append(userIds, comment.UserId) 48 | } 49 | 50 | //获取一系列用户信息 51 | users, err := db.QueryUserByIds(s.ctx, userIds) 52 | if err != nil { 53 | return nil, err 54 | } 55 | userMap := make(map[int64]*db.UserRaw) 56 | for _, user := range users { 57 | userMap[int64(user.ID)] = user 58 | } 59 | 60 | var relationMap map[int64]*db.RelationRaw 61 | if currentId == -1 { 62 | relationMap = nil 63 | } else { 64 | //获取一系列关注信息 65 | relationMap, err = db.QueryRelationByIds(s.ctx, currentId, userIds) 66 | if err != nil { 67 | return nil, err 68 | } 69 | } 70 | 71 | commentList := pack.CommentList(currentId, comments, userMap, relationMap) 72 | return commentList, nil 73 | } 74 | -------------------------------------------------------------------------------- /cmd/comment/service/comment_list_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/chenmengangzhi29/douyin/kitex_gen/comment" 8 | "github.com/cloudwego/kitex/pkg/klog" 9 | ) 10 | 11 | func TestCommentList(t *testing.T) { 12 | type args struct { 13 | token string 14 | videoId int64 15 | } 16 | tests := []struct { 17 | name string 18 | args args 19 | wantErr bool 20 | }{ 21 | { 22 | name: "测试评论列表的正常操作", 23 | args: args{ 24 | token: Token, 25 | videoId: 1, 26 | }, 27 | wantErr: false, 28 | }, 29 | { 30 | name: "测试评论列表的不正确视频id", 31 | args: args{ 32 | token: Token, 33 | videoId: 99999999, 34 | }, 35 | wantErr: true, 36 | }, 37 | } 38 | 39 | for _, tt := range tests { 40 | t.Run(tt.name, func(t *testing.T) { 41 | commentList, err := NewCommentListService(context.Background()).CommentList(&comment.CommentListRequest{Token: tt.args.token, VideoId: tt.args.videoId}) 42 | if (err != nil) != tt.wantErr { 43 | t.Errorf("CommentList() error = %v, wantErr %v", err, tt.wantErr) 44 | return 45 | } 46 | klog.Info(commentList) 47 | klog.Info(tt.name + " success") 48 | }) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /cmd/comment/service/create_comment.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "sync" 7 | 8 | "github.com/chenmengangzhi29/douyin/dal/db" 9 | "github.com/chenmengangzhi29/douyin/dal/pack" 10 | "github.com/chenmengangzhi29/douyin/kitex_gen/comment" 11 | "github.com/chenmengangzhi29/douyin/pkg/constants" 12 | "github.com/chenmengangzhi29/douyin/pkg/jwt" 13 | ) 14 | 15 | type CreateCommentService struct { 16 | ctx context.Context 17 | } 18 | 19 | // NewCreateCommentService new CreateCommentService 20 | func NewCreateCommentService(ctx context.Context) *CreateCommentService { 21 | return &CreateCommentService{ctx: ctx} 22 | } 23 | 24 | // CreateComment add comment 25 | func (s *CreateCommentService) CreateComment(req *comment.CreateCommentRequest) (*comment.Comment, error) { 26 | Jwt := jwt.NewJWT([]byte(constants.SecretKey)) 27 | currentId, err := Jwt.CheckToken(req.Token) 28 | if err != nil { 29 | return nil, err 30 | } 31 | 32 | videos, err := db.QueryVideoByVideoIds(s.ctx, []int64{req.VideoId}) 33 | if err != nil { 34 | return nil, err 35 | } 36 | if len(videos) == 0 { 37 | return nil, errors.New("video not exist") 38 | } 39 | 40 | commentRaw := &db.CommentRaw{ 41 | UserId: currentId, 42 | VideoId: req.VideoId, 43 | Contents: req.CommentText, 44 | } 45 | 46 | var wg sync.WaitGroup 47 | wg.Add(2) 48 | var user *db.UserRaw 49 | var commentErr, userErr error 50 | //创建评论记录并增加视频评论数 51 | go func() { 52 | defer wg.Done() 53 | err := db.CreateComment(s.ctx, commentRaw) 54 | if err != nil { 55 | commentErr = err 56 | return 57 | } 58 | }() 59 | //获取当前用户信息 60 | go func() { 61 | defer wg.Done() 62 | users, err := db.QueryUserByIds(s.ctx, []int64{currentId}) 63 | if err != nil { 64 | userErr = err 65 | return 66 | } 67 | user = users[0] 68 | }() 69 | wg.Wait() 70 | if commentErr != nil { 71 | return nil, commentErr 72 | } 73 | if userErr != nil { 74 | return nil, userErr 75 | } 76 | 77 | comment := pack.CommentInfo(commentRaw, user) 78 | return comment, nil 79 | 80 | } 81 | -------------------------------------------------------------------------------- /cmd/comment/service/create_comment_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "io" 7 | "os" 8 | "testing" 9 | 10 | "github.com/chenmengangzhi29/douyin/dal/db" 11 | "github.com/chenmengangzhi29/douyin/kitex_gen/comment" 12 | "github.com/chenmengangzhi29/douyin/pkg/constants" 13 | "github.com/chenmengangzhi29/douyin/pkg/jwt" 14 | "github.com/chenmengangzhi29/douyin/pkg/oss" 15 | "github.com/cloudwego/kitex/pkg/klog" 16 | ) 17 | 18 | var File []byte 19 | var Token string 20 | 21 | func TestMain(m *testing.M) { 22 | Jwt := jwt.NewJWT([]byte(constants.SecretKey)) 23 | token, err := Jwt.CreateToken(jwt.CustomClaims{ 24 | Id: int64(1), 25 | }) 26 | if err != nil { 27 | klog.Errorf("create token fail, %v", err.Error()) 28 | panic(err) 29 | } 30 | Token = token 31 | 32 | db.Init() 33 | oss.Init() 34 | 35 | path := oss.Path + "/public/girl.mp4" 36 | file, err := os.Open(path) 37 | if err != nil { 38 | klog.Errorf("open local file %v fail", path) 39 | panic(err) 40 | } 41 | defer file.Close() 42 | //处理视频数据 43 | buf := bytes.NewBuffer(nil) 44 | if _, err := io.Copy(buf, file); err != nil { 45 | panic(err) 46 | } 47 | File = buf.Bytes() 48 | 49 | m.Run() 50 | } 51 | 52 | func TestCreateComment(t *testing.T) { 53 | type args struct { 54 | token string 55 | videoId int64 56 | commentText string 57 | } 58 | tests := []struct { 59 | name string 60 | args args 61 | wantErr bool 62 | }{ 63 | { 64 | name: "测试创建评论的正常操作", 65 | args: args{ 66 | token: Token, 67 | videoId: 1, 68 | commentText: "hello world", 69 | }, 70 | wantErr: false, 71 | }, 72 | { 73 | name: "测试创建评论的不正确视频id", 74 | args: args{ 75 | token: Token, 76 | videoId: 99999999, 77 | commentText: "hello world", 78 | }, 79 | wantErr: true, 80 | }, 81 | } 82 | 83 | for _, tt := range tests { 84 | t.Run(tt.name, func(t *testing.T) { 85 | comment, err := NewCreateCommentService(context.Background()).CreateComment(&comment.CreateCommentRequest{Token: tt.args.token, VideoId: tt.args.videoId, CommentText: tt.args.commentText}) 86 | if (err != nil) != tt.wantErr { 87 | t.Errorf("CreateComment() error = %v, wantErr %v", err, tt.wantErr) 88 | return 89 | } 90 | klog.Info(comment) 91 | klog.Info(tt.name + " success") 92 | }) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /cmd/comment/service/delete_comment.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "sync" 7 | 8 | "github.com/chenmengangzhi29/douyin/dal/db" 9 | "github.com/chenmengangzhi29/douyin/dal/pack" 10 | "github.com/chenmengangzhi29/douyin/kitex_gen/comment" 11 | "github.com/chenmengangzhi29/douyin/pkg/constants" 12 | "github.com/chenmengangzhi29/douyin/pkg/jwt" 13 | ) 14 | 15 | type DeleteCommentService struct { 16 | ctx context.Context 17 | } 18 | 19 | // NewDeleteCommentService new DeleteCommentService 20 | func NewDeleteCommentService(ctx context.Context) *DeleteCommentService { 21 | return &DeleteCommentService{ctx: ctx} 22 | } 23 | 24 | // DeleteComment delete comment 25 | func (s *DeleteCommentService) DeleteComment(req *comment.DeleteCommentRequest) (*comment.Comment, error) { 26 | Jwt := jwt.NewJWT([]byte(constants.SecretKey)) 27 | currentId, err := Jwt.CheckToken(req.Token) 28 | if err != nil { 29 | return nil, err 30 | } 31 | 32 | videos, err := db.QueryVideoByVideoIds(s.ctx, []int64{req.VideoId}) 33 | if err != nil { 34 | return nil, err 35 | } 36 | if len(videos) == 0 { 37 | return nil, errors.New("videoId not exist") 38 | } 39 | comments, err := db.QueryCommentByCommentIds(s.ctx, []int64{req.CommentId}) 40 | if err != nil { 41 | return nil, err 42 | } 43 | if len(comments) == 0 { 44 | return nil, errors.New("commentId not exist") 45 | } 46 | 47 | var wg sync.WaitGroup 48 | wg.Add(2) 49 | var commentRaw *db.CommentRaw 50 | var userRaw *db.UserRaw 51 | var commentErr, userErr error 52 | //删除评论记录并减少视频评论数 53 | go func() { 54 | defer wg.Done() 55 | commentRaw, err = db.DeleteComment(s.ctx, req.CommentId) 56 | if err != nil { 57 | commentErr = err 58 | return 59 | } 60 | }() 61 | //获取用户信息 62 | go func() { 63 | defer wg.Done() 64 | users, err := db.QueryUserByIds(s.ctx, []int64{currentId}) 65 | if err != nil { 66 | userErr = err 67 | return 68 | } 69 | userRaw = users[0] 70 | }() 71 | wg.Wait() 72 | if commentErr != nil { 73 | return nil, commentErr 74 | } 75 | if userErr != nil { 76 | return nil, userErr 77 | } 78 | 79 | comment := pack.CommentInfo(commentRaw, userRaw) 80 | return comment, nil 81 | } 82 | -------------------------------------------------------------------------------- /cmd/comment/service/delete_comment_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/chenmengangzhi29/douyin/kitex_gen/comment" 8 | "github.com/cloudwego/kitex/pkg/klog" 9 | ) 10 | 11 | func TestDeleteComment(t *testing.T) { 12 | type args struct { 13 | token string 14 | videoId int64 15 | commentId int64 16 | } 17 | tests := []struct { 18 | name string 19 | args args 20 | wantErr bool 21 | }{ 22 | { 23 | name: "测试删除评论的正常操作", 24 | args: args{ 25 | token: Token, 26 | videoId: 1, 27 | commentId: 5, 28 | }, 29 | wantErr: false, 30 | }, 31 | { 32 | name: "测试删除评论的不正确视频id", 33 | args: args{ 34 | token: Token, 35 | videoId: 99999999, 36 | commentId: 1, 37 | }, 38 | wantErr: true, 39 | }, 40 | { 41 | name: "测试删除评论的不正确评论id", 42 | args: args{ 43 | token: Token, 44 | videoId: 1, 45 | commentId: 99999999, 46 | }, 47 | wantErr: true, 48 | }, 49 | } 50 | for _, tt := range tests { 51 | t.Run(tt.name, func(t *testing.T) { 52 | _, err := NewDeleteCommentService(context.Background()).DeleteComment(&comment.DeleteCommentRequest{Token: tt.args.token, VideoId: tt.args.videoId, CommentId: tt.args.commentId}) 53 | if (err != nil) != tt.wantErr { 54 | t.Errorf("DeleteComment() error = %v, wantErr %v", err, tt.wantErr) 55 | return 56 | } 57 | klog.Info(tt.name + " success") 58 | }) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /cmd/favorite/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | RUN_NAME="favorite" 3 | 4 | mkdir -p output/bin 5 | cp script/* output/ 6 | chmod +x output/bootstrap.sh 7 | 8 | if [ "$IS_SYSTEM_TEST_ENV" != "1" ]; then 9 | go build -o output/bin/${RUN_NAME} 10 | else 11 | go test -c -covermode=set -o output/bin/${RUN_NAME} -coverpkg=./... 12 | fi 13 | -------------------------------------------------------------------------------- /cmd/favorite/handler.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/chenmengangzhi29/douyin/cmd/favorite/service" 7 | "github.com/chenmengangzhi29/douyin/dal/pack" 8 | "github.com/chenmengangzhi29/douyin/kitex_gen/favorite" 9 | "github.com/chenmengangzhi29/douyin/pkg/errno" 10 | ) 11 | 12 | // FavoriteServiceImpl implements the last service interface defined in the IDL. 13 | type FavoriteServiceImpl struct{} 14 | 15 | // FavoriteAction implements the FavoriteServiceImpl interface. 16 | func (s *FavoriteServiceImpl) FavoriteAction(ctx context.Context, req *favorite.FavoriteActionRequest) (resp *favorite.FavoriteActionResponse, err error) { 17 | resp = new(favorite.FavoriteActionResponse) 18 | 19 | if len(req.Token) == 0 || req.VideoId == 0 || req.ActionType == 0 { 20 | resp.BaseResp = pack.BuildFavoriteBaseResp(errno.ParamErr) 21 | return resp, nil 22 | } 23 | 24 | err = service.NewFavoriteActionService(ctx).FavoriteAction(req) 25 | if err != nil { 26 | resp.BaseResp = pack.BuildFavoriteBaseResp(err) 27 | return resp, nil 28 | } 29 | resp.BaseResp = pack.BuildFavoriteBaseResp(errno.Success) 30 | return resp, nil 31 | } 32 | 33 | // FavoriteList implements the FavoriteServiceImpl interface. 34 | func (s *FavoriteServiceImpl) FavoriteList(ctx context.Context, req *favorite.FavoriteListRequest) (resp *favorite.FavoriteListResponse, err error) { 35 | resp = new(favorite.FavoriteListResponse) 36 | 37 | if req.UserId == 0 { 38 | resp.BaseResp = pack.BuildFavoriteBaseResp(errno.ParamErr) 39 | return resp, nil 40 | } 41 | 42 | videoList, err := service.NewFavoriteListService(ctx).FavoriteList(req) 43 | if err != nil { 44 | resp.BaseResp = pack.BuildFavoriteBaseResp(err) 45 | return resp, nil 46 | } 47 | 48 | resp.BaseResp = pack.BuildFavoriteBaseResp(errno.Success) 49 | resp.VideoList = videoList 50 | return resp, nil 51 | } 52 | -------------------------------------------------------------------------------- /cmd/favorite/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/chenmengangzhi29/douyin/dal" 7 | favorite "github.com/chenmengangzhi29/douyin/kitex_gen/favorite/favoriteservice" 8 | "github.com/chenmengangzhi29/douyin/pkg/bound" 9 | "github.com/chenmengangzhi29/douyin/pkg/constants" 10 | "github.com/chenmengangzhi29/douyin/pkg/middleware" 11 | tracer2 "github.com/chenmengangzhi29/douyin/pkg/tracer" 12 | "github.com/cloudwego/kitex/pkg/klog" 13 | "github.com/cloudwego/kitex/pkg/limit" 14 | "github.com/cloudwego/kitex/pkg/rpcinfo" 15 | "github.com/cloudwego/kitex/server" 16 | etcd "github.com/kitex-contrib/registry-etcd" 17 | trace "github.com/kitex-contrib/tracer-opentracing" 18 | ) 19 | 20 | func Init() { 21 | tracer2.InitJaeger(constants.FavoriteServiceName) 22 | dal.Init() 23 | } 24 | 25 | func main() { 26 | r, err := etcd.NewEtcdRegistry([]string{constants.EtcdAddress}) //r should not be reused. 27 | if err != nil { 28 | panic(err) 29 | } 30 | addr, err := net.ResolveTCPAddr("tcp", constants.FavoriteAddress) 31 | if err != nil { 32 | panic(err) 33 | } 34 | Init() 35 | svr := favorite.NewServer(new(FavoriteServiceImpl), 36 | server.WithServerBasicInfo(&rpcinfo.EndpointBasicInfo{ServiceName: constants.FavoriteServiceName}), //server name 37 | server.WithMiddleware(middleware.CommonMiddleware), 38 | server.WithMiddleware(middleware.ServerMiddleware), 39 | server.WithServiceAddr(addr), //address 40 | server.WithLimit(&limit.Option{MaxConnections: 1000, MaxQPS: 100}), //limit 41 | server.WithMuxTransport(), //Multiplex 42 | server.WithSuite(trace.NewDefaultServerSuite()), //tracer 43 | server.WithBoundHandler(bound.NewCpuLimitHandler()), //BoundHandler 44 | server.WithRegistry(r), //registry 45 | ) 46 | err = svr.Run() 47 | if err != nil { 48 | klog.Fatal(err) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /cmd/favorite/script/bootstrap.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | CURDIR=$(cd $(dirname $0); pwd) 3 | 4 | if [ "X$1" != "X" ]; then 5 | RUNTIME_ROOT=$1 6 | else 7 | RUNTIME_ROOT=${CURDIR} 8 | fi 9 | 10 | export KITEX_RUNTIME_ROOT=$RUNTIME_ROOT 11 | export KITEX_LOG_DIR="$RUNTIME_ROOT/log" 12 | 13 | if [ ! -d "$KITEX_LOG_DIR/app" ]; then 14 | mkdir -p "$KITEX_LOG_DIR/app" 15 | fi 16 | 17 | if [ ! -d "$KITEX_LOG_DIR/rpc" ]; then 18 | mkdir -p "$KITEX_LOG_DIR/rpc" 19 | fi 20 | 21 | exec "$CURDIR/bin/favorite" 22 | -------------------------------------------------------------------------------- /cmd/favorite/service/favorite_action.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "github.com/chenmengangzhi29/douyin/dal/db" 8 | "github.com/chenmengangzhi29/douyin/kitex_gen/favorite" 9 | "github.com/chenmengangzhi29/douyin/pkg/constants" 10 | "github.com/chenmengangzhi29/douyin/pkg/jwt" 11 | ) 12 | 13 | type FavoriteActionService struct { 14 | ctx context.Context 15 | } 16 | 17 | // NewFavoriteActionService new FavoriteActionService 18 | func NewFavoriteActionService(ctx context.Context) *FavoriteActionService { 19 | return &FavoriteActionService{ctx: ctx} 20 | } 21 | 22 | // FavoriteAction implement the like and unlike operations 23 | func (s *FavoriteActionService) FavoriteAction(req *favorite.FavoriteActionRequest) error { 24 | Jwt := jwt.NewJWT([]byte(constants.SecretKey)) 25 | claim, err := Jwt.ParseToken(req.Token) 26 | if err != nil { 27 | return err 28 | } 29 | currentId := claim.Id 30 | 31 | videos, err := db.QueryVideoByVideoIds(s.ctx, []int64{req.VideoId}) 32 | if err != nil { 33 | return err 34 | } 35 | if len(videos) == 0 { 36 | return errors.New("video not exist") 37 | } 38 | 39 | //若ActionType(操作类型)等于1,则向favorite表创建一条记录,同时向video表的目标video增加点赞数 40 | //若ActionType等于2,则向favorite表删除一条记录,同时向video表的目标video减少点赞数 41 | //若ActionType不等于1和2,则返回错误 42 | if req.ActionType == constants.Like { 43 | favorite := &db.FavoriteRaw{ 44 | UserId: currentId, 45 | VideoId: req.VideoId, 46 | } 47 | 48 | err := db.CreateFavorite(s.ctx, favorite, req.VideoId) 49 | if err != nil { 50 | return err 51 | } 52 | } 53 | if req.ActionType == constants.Unlike { 54 | err := db.DeleteFavorite(s.ctx, currentId, req.VideoId) 55 | if err != nil { 56 | return err 57 | } 58 | 59 | } 60 | if req.ActionType != constants.Like && req.ActionType != constants.Unlike { 61 | return errors.New("action type no equal 1 and 2") 62 | } 63 | return nil 64 | } 65 | -------------------------------------------------------------------------------- /cmd/favorite/service/favorite_action_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "io" 7 | "os" 8 | "testing" 9 | 10 | "github.com/chenmengangzhi29/douyin/dal/db" 11 | "github.com/chenmengangzhi29/douyin/kitex_gen/favorite" 12 | "github.com/chenmengangzhi29/douyin/pkg/constants" 13 | "github.com/chenmengangzhi29/douyin/pkg/jwt" 14 | "github.com/chenmengangzhi29/douyin/pkg/oss" 15 | "github.com/cloudwego/kitex/pkg/klog" 16 | ) 17 | 18 | var File []byte 19 | var Token string 20 | 21 | func TestMain(m *testing.M) { 22 | 23 | Jwt := jwt.NewJWT([]byte(constants.SecretKey)) 24 | token, err := Jwt.CreateToken(jwt.CustomClaims{ 25 | Id: int64(1), 26 | }) 27 | if err != nil { 28 | klog.Errorf("create token fail, %v", err.Error()) 29 | panic(err) 30 | } 31 | Token = token 32 | 33 | db.Init() 34 | oss.Init() 35 | 36 | path := oss.Path + "/public/girl.mp4" 37 | file, err := os.Open(path) 38 | if err != nil { 39 | klog.Errorf("open local file %v fail", path) 40 | panic(err) 41 | } 42 | defer file.Close() 43 | //处理视频数据 44 | buf := bytes.NewBuffer(nil) 45 | if _, err := io.Copy(buf, file); err != nil { 46 | panic(err) 47 | } 48 | File = buf.Bytes() 49 | 50 | m.Run() 51 | } 52 | 53 | func TestFavoriteActionData(t *testing.T) { 54 | type args struct { 55 | token string 56 | videoId int64 57 | actionType int64 58 | } 59 | tests := []struct { 60 | name string 61 | args args 62 | wantErr bool 63 | }{ 64 | { 65 | name: "测试点赞操作的actionType=1", 66 | args: args{ 67 | token: Token, 68 | videoId: 1, 69 | actionType: 1, 70 | }, 71 | wantErr: false, 72 | }, 73 | { 74 | name: "测试点赞操作的actionType=2", 75 | args: args{ 76 | token: Token, 77 | videoId: 1, 78 | actionType: 2, 79 | }, 80 | wantErr: false, 81 | }, 82 | { 83 | name: "测试点赞操作的actionType=3", 84 | args: args{ 85 | token: Token, 86 | videoId: 1, 87 | actionType: 3, 88 | }, 89 | wantErr: true, 90 | }, 91 | { 92 | name: "测试点赞列表的不存在视频id", 93 | args: args{ 94 | token: Token, 95 | videoId: 99999999, 96 | actionType: 1, 97 | }, 98 | wantErr: true, 99 | }, 100 | } 101 | 102 | for _, tt := range tests { 103 | t.Run(tt.name, func(t *testing.T) { 104 | err := NewFavoriteActionService(context.Background()).FavoriteAction(&favorite.FavoriteActionRequest{Token: tt.args.token, VideoId: tt.args.videoId, ActionType: int32(tt.args.actionType)}) 105 | if (err != nil) != tt.wantErr { 106 | t.Errorf("FavoriteAction() error = %v, wantErr %v", err, tt.wantErr) 107 | return 108 | } 109 | klog.Info(tt.name + " success") 110 | }) 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /cmd/favorite/service/favorite_list.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "sync" 7 | 8 | "github.com/chenmengangzhi29/douyin/dal/db" 9 | "github.com/chenmengangzhi29/douyin/dal/pack" 10 | "github.com/chenmengangzhi29/douyin/kitex_gen/favorite" 11 | "github.com/chenmengangzhi29/douyin/pkg/constants" 12 | "github.com/chenmengangzhi29/douyin/pkg/jwt" 13 | ) 14 | 15 | type FavoriteListService struct { 16 | ctx context.Context 17 | } 18 | 19 | // NewFavoriteListService new FavoriteListService 20 | func NewFavoriteListService(ctx context.Context) *FavoriteListService { 21 | return &FavoriteListService{ctx: ctx} 22 | } 23 | 24 | // FavoriteList get video information that users like 25 | func (s *FavoriteListService) FavoriteList(req *favorite.FavoriteListRequest) ([]*favorite.Video, error) { 26 | //获取用户id 27 | Jwt := jwt.NewJWT([]byte(constants.SecretKey)) 28 | currentId, _ := Jwt.CheckToken(req.Token) 29 | 30 | //检查用户是否存在 31 | user, err := db.QueryUserByIds(s.ctx, []int64{req.UserId}) 32 | if err != nil { 33 | return nil, err 34 | } 35 | if len(user) == 0 { 36 | return nil, errors.New("user not exist") 37 | } 38 | 39 | //获取目标用户的点赞视频id号 40 | videoIds, err := db.QueryFavoriteById(s.ctx, req.UserId) 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | //获取点赞视频的信息 46 | videoData, err := db.QueryVideoByVideoIds(s.ctx, videoIds) 47 | if err != nil { 48 | return nil, err 49 | } 50 | 51 | //获取点赞视频的用户id号 52 | userIds := make([]int64, 0) 53 | for _, video := range videoData { 54 | userIds = append(userIds, video.UserId) 55 | } 56 | 57 | //获取点赞视频的用户信息 58 | users, err := db.QueryUserByIds(s.ctx, userIds) 59 | if err != nil { 60 | return nil, err 61 | } 62 | userMap := make(map[int64]*db.UserRaw) 63 | for _, user := range users { 64 | userMap[int64(user.ID)] = user 65 | } 66 | 67 | var favoriteMap map[int64]*db.FavoriteRaw 68 | var relationMap map[int64]*db.RelationRaw 69 | //if user not logged in 70 | if currentId == -1 { 71 | favoriteMap = nil 72 | relationMap = nil 73 | } else { 74 | var wg sync.WaitGroup 75 | wg.Add(2) 76 | var favoriteErr, relationErr error 77 | //获取点赞信息 78 | go func() { 79 | defer wg.Done() 80 | favoriteMap, err = db.QueryFavoriteByIds(s.ctx, currentId, videoIds) 81 | if err != nil { 82 | favoriteErr = err 83 | return 84 | } 85 | }() 86 | //获取关注信息 87 | go func() { 88 | defer wg.Done() 89 | relationMap, err = db.QueryRelationByIds(s.ctx, currentId, userIds) 90 | if err != nil { 91 | relationErr = err 92 | return 93 | } 94 | }() 95 | wg.Wait() 96 | if favoriteErr != nil { 97 | return nil, favoriteErr 98 | } 99 | if relationErr != nil { 100 | return nil, relationErr 101 | } 102 | 103 | } 104 | 105 | videoList := pack.VideoList(currentId, videoData, userMap, favoriteMap, relationMap) 106 | return videoList, nil 107 | 108 | } 109 | -------------------------------------------------------------------------------- /cmd/favorite/service/favorite_list_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/chenmengangzhi29/douyin/kitex_gen/favorite" 8 | "github.com/cloudwego/kitex/pkg/klog" 9 | ) 10 | 11 | func TestFavoriteList(t *testing.T) { 12 | type args struct { 13 | userId int64 14 | token string 15 | } 16 | tests := []struct { 17 | name string 18 | args args 19 | wantErr bool 20 | }{ 21 | { 22 | name: "测试点赞列表的存在用户id", 23 | args: args{ 24 | token: Token, 25 | userId: 1, 26 | }, 27 | wantErr: false, 28 | }, 29 | { 30 | name: "测试点赞列表的不存在用户id", 31 | args: args{ 32 | token: Token, 33 | userId: 99999999, 34 | }, 35 | wantErr: true, 36 | }, 37 | } 38 | 39 | for _, tt := range tests { 40 | t.Run(tt.name, func(t *testing.T) { 41 | videoList, err := NewFavoriteListService(context.Background()).FavoriteList(&favorite.FavoriteListRequest{Token: tt.args.token, UserId: tt.args.userId}) 42 | if (err != nil) != tt.wantErr { 43 | t.Errorf("FavoriteList() error = %v, wantErr %v", err, tt.wantErr) 44 | return 45 | } 46 | klog.Info(tt.name + " success") 47 | klog.Info(videoList) 48 | }) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /cmd/feed/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | RUN_NAME="feed" 3 | 4 | mkdir -p output/bin 5 | cp script/* output/ 6 | chmod +x output/bootstrap.sh 7 | 8 | if [ "$IS_SYSTEM_TEST_ENV" != "1" ]; then 9 | go build -o output/bin/${RUN_NAME} 10 | else 11 | go test -c -covermode=set -o output/bin/${RUN_NAME} -coverpkg=./... 12 | fi 13 | -------------------------------------------------------------------------------- /cmd/feed/handler.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/chenmengangzhi29/douyin/cmd/feed/service" 7 | "github.com/chenmengangzhi29/douyin/dal/pack" 8 | "github.com/chenmengangzhi29/douyin/kitex_gen/feed" 9 | "github.com/chenmengangzhi29/douyin/pkg/errno" 10 | ) 11 | 12 | // FeedServiceImpl implements the last service interface defined in the IDL. 13 | type FeedServiceImpl struct{} 14 | 15 | // Feed implements the FeedServiceImpl interface. 16 | func (s *FeedServiceImpl) Feed(ctx context.Context, req *feed.FeedRequest) (resp *feed.FeedResponse, err error) { 17 | resp = new(feed.FeedResponse) 18 | 19 | if req.LatestTime <= 0 { 20 | resp.BaseResp = pack.BuildFeedBaseResp(errno.ParamErr) 21 | return resp, nil 22 | } 23 | 24 | videos, nextTime, err := service.NewFeedService(ctx).Feed(req) 25 | if err != nil { 26 | resp.BaseResp = pack.BuildFeedBaseResp(err) 27 | return resp, nil 28 | } 29 | resp.BaseResp = pack.BuildFeedBaseResp(errno.Success) 30 | resp.VideoList = videos 31 | resp.NextTime = nextTime 32 | return resp, nil 33 | } 34 | -------------------------------------------------------------------------------- /cmd/feed/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/chenmengangzhi29/douyin/dal" 7 | feed "github.com/chenmengangzhi29/douyin/kitex_gen/feed/feedservice" 8 | "github.com/chenmengangzhi29/douyin/pkg/bound" 9 | "github.com/chenmengangzhi29/douyin/pkg/constants" 10 | "github.com/chenmengangzhi29/douyin/pkg/middleware" 11 | tracer2 "github.com/chenmengangzhi29/douyin/pkg/tracer" 12 | "github.com/cloudwego/kitex/pkg/klog" 13 | "github.com/cloudwego/kitex/pkg/limit" 14 | "github.com/cloudwego/kitex/pkg/rpcinfo" 15 | "github.com/cloudwego/kitex/server" 16 | etcd "github.com/kitex-contrib/registry-etcd" 17 | trace "github.com/kitex-contrib/tracer-opentracing" 18 | ) 19 | 20 | func Init() { 21 | tracer2.InitJaeger(constants.FeedServiceName) 22 | dal.Init() 23 | } 24 | 25 | func main() { 26 | r, err := etcd.NewEtcdRegistry([]string{constants.EtcdAddress}) //r should not be reused. 27 | if err != nil { 28 | panic(err) 29 | } 30 | addr, err := net.ResolveTCPAddr("tcp", constants.FeedAddress) 31 | if err != nil { 32 | panic(err) 33 | } 34 | Init() 35 | svr := feed.NewServer(new(FeedServiceImpl), 36 | server.WithServerBasicInfo(&rpcinfo.EndpointBasicInfo{ServiceName: constants.FeedServiceName}), //server name 37 | server.WithMiddleware(middleware.CommonMiddleware), 38 | server.WithMiddleware(middleware.ServerMiddleware), 39 | server.WithServiceAddr(addr), //address 40 | server.WithLimit(&limit.Option{MaxConnections: 1000, MaxQPS: 100}), //limit 41 | server.WithMuxTransport(), //Multiplex 42 | server.WithSuite(trace.NewDefaultServerSuite()), //tracer 43 | server.WithBoundHandler(bound.NewCpuLimitHandler()), //BoundHandler 44 | server.WithRegistry(r), //registry 45 | ) 46 | err = svr.Run() 47 | if err != nil { 48 | klog.Fatal(err) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /cmd/feed/script/bootstrap.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | CURDIR=$(cd $(dirname $0); pwd) 3 | 4 | if [ "X$1" != "X" ]; then 5 | RUNTIME_ROOT=$1 6 | else 7 | RUNTIME_ROOT=${CURDIR} 8 | fi 9 | 10 | export KITEX_RUNTIME_ROOT=$RUNTIME_ROOT 11 | export KITEX_LOG_DIR="$RUNTIME_ROOT/log" 12 | 13 | if [ ! -d "$KITEX_LOG_DIR/app" ]; then 14 | mkdir -p "$KITEX_LOG_DIR/app" 15 | fi 16 | 17 | if [ ! -d "$KITEX_LOG_DIR/rpc" ]; then 18 | mkdir -p "$KITEX_LOG_DIR/rpc" 19 | fi 20 | 21 | exec "$CURDIR/bin/feed" 22 | -------------------------------------------------------------------------------- /cmd/feed/service/feed.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | 7 | "github.com/chenmengangzhi29/douyin/dal/db" 8 | "github.com/chenmengangzhi29/douyin/dal/pack" 9 | "github.com/chenmengangzhi29/douyin/kitex_gen/feed" 10 | "github.com/chenmengangzhi29/douyin/pkg/constants" 11 | "github.com/chenmengangzhi29/douyin/pkg/jwt" 12 | ) 13 | 14 | type FeedService struct { 15 | ctx context.Context 16 | } 17 | 18 | // NewFeedService new FeedService 19 | func NewFeedService(ctx context.Context) *FeedService { 20 | return &FeedService{ctx: ctx} 21 | } 22 | 23 | // Feed multiple get list of video info 24 | func (s *FeedService) Feed(req *feed.FeedRequest) ([]*feed.Video, int64, error) { 25 | Jwt := jwt.NewJWT([]byte(constants.SecretKey)) 26 | currentId, _ := Jwt.CheckToken(req.Token) 27 | 28 | //get video info 29 | videoData, err := db.QueryVideoByLatestTime(s.ctx, req.LatestTime) 30 | if err != nil { 31 | return nil, 0, err 32 | } 33 | 34 | //get video ids and user ids 35 | videoIds := make([]int64, 0) 36 | userIds := make([]int64, 0) 37 | for _, video := range videoData { 38 | videoIds = append(videoIds, int64(video.ID)) 39 | userIds = append(userIds, video.UserId) 40 | } 41 | 42 | //get user info 43 | users, err := db.QueryUserByIds(s.ctx, userIds) 44 | if err != nil { 45 | return nil, 0, err 46 | } 47 | userMap := make(map[int64]*db.UserRaw) 48 | for _, user := range users { 49 | userMap[int64(user.ID)] = user 50 | } 51 | 52 | var favoriteMap map[int64]*db.FavoriteRaw 53 | var relationMap map[int64]*db.RelationRaw 54 | //if user not logged in 55 | if currentId == -1 { 56 | favoriteMap = nil 57 | relationMap = nil 58 | } else { 59 | var wg sync.WaitGroup 60 | wg.Add(2) 61 | var favoriteErr, relationErr error 62 | //获取点赞信息 63 | go func() { 64 | defer wg.Done() 65 | favoriteMap, err = db.QueryFavoriteByIds(s.ctx, currentId, videoIds) 66 | if err != nil { 67 | favoriteErr = err 68 | return 69 | } 70 | }() 71 | //获取关注信息 72 | go func() { 73 | defer wg.Done() 74 | relationMap, err = db.QueryRelationByIds(s.ctx, currentId, userIds) 75 | if err != nil { 76 | relationErr = err 77 | return 78 | } 79 | }() 80 | wg.Wait() 81 | if favoriteErr != nil { 82 | return nil, 0, favoriteErr 83 | } 84 | if relationErr != nil { 85 | return nil, 0, relationErr 86 | } 87 | 88 | } 89 | 90 | videos, nextTime := pack.VideoInfo(currentId, videoData, userMap, favoriteMap, relationMap) 91 | return videos, nextTime, nil 92 | } 93 | -------------------------------------------------------------------------------- /cmd/feed/service/feed_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "testing" 7 | "time" 8 | 9 | "github.com/chenmengangzhi29/douyin/dal/db" 10 | "github.com/chenmengangzhi29/douyin/kitex_gen/feed" 11 | "github.com/chenmengangzhi29/douyin/pkg/constants" 12 | "github.com/chenmengangzhi29/douyin/pkg/jwt" 13 | "github.com/chenmengangzhi29/douyin/pkg/oss" 14 | "github.com/cloudwego/kitex/pkg/klog" 15 | ) 16 | 17 | var File *os.File 18 | var Token string 19 | 20 | func TestMain(m *testing.M) { 21 | 22 | Jwt := jwt.NewJWT([]byte(constants.SecretKey)) 23 | token, err := Jwt.CreateToken(jwt.CustomClaims{ 24 | Id: int64(1), 25 | }) 26 | if err != nil { 27 | klog.Errorf("create token fail, %v", err.Error()) 28 | panic(err) 29 | } 30 | Token = token 31 | 32 | db.Init() 33 | oss.Init() 34 | 35 | path := oss.Path + "/public/girl.mp4" 36 | file, err := os.Open(path) 37 | if err != nil { 38 | klog.Errorf("open local file %v fail", path) 39 | panic(err) 40 | } 41 | defer file.Close() 42 | File = file 43 | 44 | m.Run() 45 | } 46 | 47 | func TestFeed(t *testing.T) { 48 | type args struct { 49 | latestTime int64 50 | token string 51 | } 52 | tests := []struct { 53 | name string 54 | args args 55 | wantErr bool 56 | }{ 57 | { 58 | name: "测试视频流的默认token", 59 | args: args{ 60 | latestTime: time.Now().Unix(), 61 | token: "", 62 | }, 63 | wantErr: false, 64 | }, 65 | { 66 | name: "测试视频流的正常token", 67 | args: args{ 68 | latestTime: time.Now().Unix(), 69 | token: Token, 70 | }, 71 | wantErr: false, 72 | }, 73 | } 74 | 75 | for _, tt := range tests { 76 | t.Run(tt.name, func(t *testing.T) { 77 | _, _, err := NewFeedService(context.Background()).Feed(&feed.FeedRequest{LatestTime: tt.args.latestTime, Token: tt.args.token}) 78 | if (err != nil) != tt.wantErr { 79 | t.Errorf("Feed() error = %v, wantErr %v", err, tt.wantErr) 80 | return 81 | } 82 | klog.Info(tt.name + " success") 83 | }) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /cmd/publish/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | RUN_NAME="publish" 3 | 4 | mkdir -p output/bin 5 | cp script/* output/ 6 | chmod +x output/bootstrap.sh 7 | 8 | if [ "$IS_SYSTEM_TEST_ENV" != "1" ]; then 9 | go build -o output/bin/${RUN_NAME} 10 | else 11 | go test -c -covermode=set -o output/bin/${RUN_NAME} -coverpkg=./... 12 | fi 13 | -------------------------------------------------------------------------------- /cmd/publish/handler.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/chenmengangzhi29/douyin/cmd/publish/service" 7 | "github.com/chenmengangzhi29/douyin/dal/pack" 8 | "github.com/chenmengangzhi29/douyin/kitex_gen/publish" 9 | "github.com/chenmengangzhi29/douyin/pkg/errno" 10 | ) 11 | 12 | // PublishServiceImpl implements the last service interface defined in the IDL. 13 | type PublishServiceImpl struct{} 14 | 15 | // PublishAction implements the PublishServiceImpl interface. 16 | func (s *PublishServiceImpl) PublishAction(ctx context.Context, req *publish.PublishActionRequest) (resp *publish.PublishActionResponse, err error) { 17 | resp = new(publish.PublishActionResponse) 18 | 19 | if len(req.Token) == 0 || len(req.Title) == 0 || req.Data == nil { 20 | resp.BaseResp = pack.BuildPublishBaseResp(errno.ParamErr) 21 | return resp, nil 22 | } 23 | 24 | err = service.NewPublishService(ctx).Publish(req) 25 | if err != nil { 26 | resp.BaseResp = pack.BuildPublishBaseResp(err) 27 | return resp, nil 28 | } 29 | resp.BaseResp = pack.BuildPublishBaseResp(errno.Success) 30 | return resp, nil 31 | } 32 | 33 | // PublishList implements the PublishServiceImpl interface. 34 | func (s *PublishServiceImpl) PublishList(ctx context.Context, req *publish.PublishListRequest) (resp *publish.PublishListResponse, err error) { 35 | resp = new(publish.PublishListResponse) 36 | 37 | if req.UserId <= 0 { 38 | resp.BaseResp = pack.BuildPublishBaseResp(errno.ParamErr) 39 | return resp, nil 40 | } 41 | 42 | videoList, err := service.NewPublishListService(ctx).PublishList(req) 43 | if err != nil { 44 | resp.BaseResp = pack.BuildPublishBaseResp(err) 45 | return resp, nil 46 | } 47 | resp.BaseResp = pack.BuildPublishBaseResp(errno.Success) 48 | resp.VideoList = videoList 49 | return resp, nil 50 | } 51 | -------------------------------------------------------------------------------- /cmd/publish/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/chenmengangzhi29/douyin/dal" 7 | publish "github.com/chenmengangzhi29/douyin/kitex_gen/publish/publishservice" 8 | "github.com/chenmengangzhi29/douyin/pkg/bound" 9 | "github.com/chenmengangzhi29/douyin/pkg/constants" 10 | "github.com/chenmengangzhi29/douyin/pkg/middleware" 11 | "github.com/chenmengangzhi29/douyin/pkg/oss" 12 | tracer2 "github.com/chenmengangzhi29/douyin/pkg/tracer" 13 | "github.com/cloudwego/kitex/pkg/klog" 14 | "github.com/cloudwego/kitex/pkg/limit" 15 | "github.com/cloudwego/kitex/pkg/rpcinfo" 16 | "github.com/cloudwego/kitex/server" 17 | etcd "github.com/kitex-contrib/registry-etcd" 18 | trace "github.com/kitex-contrib/tracer-opentracing" 19 | ) 20 | 21 | func Init() { 22 | tracer2.InitJaeger(constants.PublishServiceName) 23 | dal.Init() 24 | oss.Init() 25 | } 26 | 27 | func main() { 28 | r, err := etcd.NewEtcdRegistry([]string{constants.EtcdAddress}) // r should not be reused 29 | if err != nil { 30 | panic(err) 31 | } 32 | addr, err := net.ResolveTCPAddr("tcp", constants.PublishAddress) 33 | if err != nil { 34 | panic(err) 35 | } 36 | Init() 37 | svr := publish.NewServer(new(PublishServiceImpl), 38 | server.WithServerBasicInfo(&rpcinfo.EndpointBasicInfo{ServiceName: constants.PublishServiceName}), //server name // middleWare 39 | server.WithMiddleware(middleware.ServerMiddleware), // middleWare 40 | server.WithServiceAddr(addr), //address 41 | server.WithLimit(&limit.Option{MaxConnections: 1000, MaxQPS: 100}), //limit 42 | server.WithMuxTransport(), //Multiplex 43 | server.WithSuite(trace.NewDefaultServerSuite()), //tracer 44 | server.WithBoundHandler(bound.NewCpuLimitHandler()), //BoundHandler 45 | server.WithRegistry(r), // registry 46 | ) 47 | err = svr.Run() 48 | if err != nil { 49 | klog.Fatal(err) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /cmd/publish/script/bootstrap.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | CURDIR=$(cd $(dirname $0); pwd) 3 | 4 | if [ "X$1" != "X" ]; then 5 | RUNTIME_ROOT=$1 6 | else 7 | RUNTIME_ROOT=${CURDIR} 8 | fi 9 | 10 | export KITEX_RUNTIME_ROOT=$RUNTIME_ROOT 11 | export KITEX_LOG_DIR="$RUNTIME_ROOT/log" 12 | 13 | if [ ! -d "$KITEX_LOG_DIR/app" ]; then 14 | mkdir -p "$KITEX_LOG_DIR/app" 15 | fi 16 | 17 | if [ ! -d "$KITEX_LOG_DIR/rpc" ]; then 18 | mkdir -p "$KITEX_LOG_DIR/rpc" 19 | fi 20 | 21 | exec "$CURDIR/bin/publish" 22 | -------------------------------------------------------------------------------- /cmd/publish/service/publish.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "fmt" 7 | "image/jpeg" 8 | "os" 9 | "strconv" 10 | "strings" 11 | "time" 12 | 13 | "github.com/chenmengangzhi29/douyin/dal/db" 14 | "github.com/chenmengangzhi29/douyin/kitex_gen/publish" 15 | "github.com/chenmengangzhi29/douyin/pkg/constants" 16 | "github.com/chenmengangzhi29/douyin/pkg/jwt" 17 | "github.com/chenmengangzhi29/douyin/pkg/oss" 18 | "github.com/cloudwego/kitex/pkg/klog" 19 | "github.com/disintegration/imaging" 20 | ffmpeg "github.com/u2takey/ffmpeg-go" 21 | ) 22 | 23 | type PublishService struct { 24 | ctx context.Context 25 | } 26 | 27 | // NewPublishService new PublishService 28 | func NewPublishService(ctx context.Context) *PublishService { 29 | return &PublishService{ctx: ctx} 30 | } 31 | 32 | // Publish upload video info 33 | func (s *PublishService) Publish(req *publish.PublishActionRequest) error { 34 | video := req.Data 35 | title := req.Title 36 | Jwt := jwt.NewJWT([]byte(constants.SecretKey)) 37 | currentId, err := Jwt.CheckToken(req.Token) 38 | if err != nil { 39 | return err 40 | } 41 | 42 | id := time.Now().Unix() 43 | fileName := strconv.Itoa(int(id)) + ".mp4" 44 | 45 | //将视频保存到本地文件夹 46 | filePath := oss.Path + "/public/" + fileName 47 | err = oss.PublishVideoToPublic(video, filePath) 48 | if err != nil { 49 | return err 50 | } 51 | 52 | //分片将视频流上传到oss 53 | objectKey := "video/" + fileName 54 | err = oss.PublishVideoToOss(objectKey, filePath) 55 | if err != nil { 56 | return err 57 | } 58 | 59 | //获取视频播放地址 60 | signedURL, err := oss.QueryOssVideoURL(objectKey) 61 | if err != nil { 62 | return err 63 | } 64 | videoUrl := strings.Split(signedURL, "?")[0] 65 | 66 | //获取封面 67 | coverName := strconv.Itoa(int(id)) + ".jpg" 68 | coverData, err := getSnapshot(filePath) 69 | if err != nil { 70 | return err 71 | } 72 | 73 | //上传封面到oss 74 | objectKey = "cover/" + coverName 75 | coverReader := bytes.NewReader(coverData) 76 | err = oss.PublishCoverToOss(objectKey, coverReader) 77 | if err != nil { 78 | return err 79 | } 80 | 81 | //获取封面链接 82 | signedURL, err = oss.QueryOssCoverURL(objectKey) 83 | if err != nil { 84 | return err 85 | } 86 | coverUrl := strings.Split(signedURL, "?")[0] 87 | 88 | //将视频信息上传到mysql 89 | videoRaw := &db.VideoRaw{ 90 | UserId: currentId, 91 | Title: title, 92 | PlayUrl: videoUrl, 93 | CoverUrl: coverUrl, 94 | FavoriteCount: 0, 95 | CommentCount: 0, 96 | UpdatedAt: time.Now(), 97 | } 98 | 99 | err = db.PublishVideoData(s.ctx, videoRaw) 100 | if err != nil { 101 | return err 102 | } 103 | return nil 104 | } 105 | 106 | func getSnapshot(filePath string) ([]byte, error) { 107 | buffer := bytes.NewBuffer(nil) 108 | err := ffmpeg.Input(filePath). 109 | Filter("select", ffmpeg.Args{fmt.Sprintf("gte(n,%d)", 1)}). 110 | Output("pipe:", ffmpeg.KwArgs{"vframes": 1, "format": "image2", "vcodec": "mjpeg"}). 111 | WithOutput(buffer, os.Stdout). 112 | Run() 113 | if err != nil { 114 | klog.Fatal("生成缩略图失败 ", err) 115 | return nil, err 116 | } 117 | 118 | img, err := imaging.Decode(buffer) 119 | if err != nil { 120 | klog.Fatal("生成缩略图失败 ", err) 121 | return nil, err 122 | } 123 | 124 | buf := new(bytes.Buffer) 125 | jpeg.Encode(buf, img, nil) 126 | 127 | return buf.Bytes(), err 128 | } 129 | -------------------------------------------------------------------------------- /cmd/publish/service/publish_list.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "sync" 7 | 8 | "github.com/chenmengangzhi29/douyin/dal/db" 9 | "github.com/chenmengangzhi29/douyin/dal/pack" 10 | "github.com/chenmengangzhi29/douyin/kitex_gen/publish" 11 | "github.com/chenmengangzhi29/douyin/pkg/constants" 12 | "github.com/chenmengangzhi29/douyin/pkg/jwt" 13 | ) 14 | 15 | type PublishListService struct { 16 | ctx context.Context 17 | } 18 | 19 | // NewPublishService new PublishService 20 | func NewPublishListService(ctx context.Context) *PublishListService { 21 | return &PublishListService{ctx: ctx} 22 | } 23 | 24 | // PublishList get publish list by userid 25 | func (s *PublishListService) PublishList(req *publish.PublishListRequest) ([]*publish.Video, error) { 26 | Jwt := jwt.NewJWT([]byte(constants.SecretKey)) 27 | currentId, _ := Jwt.CheckToken(req.Token) 28 | 29 | videoData, err := db.QueryVideoByUserId(s.ctx, req.UserId) 30 | if err != nil { 31 | return nil, err 32 | } 33 | 34 | videoIds := make([]int64, 0) 35 | userIds := []int64{req.UserId} 36 | for _, video := range videoData { 37 | videoIds = append(videoIds, int64(video.ID)) 38 | } 39 | 40 | users, err := db.QueryUserByIds(s.ctx, userIds) 41 | if err != nil { 42 | return nil, err 43 | } 44 | if len(users) == 0 { 45 | return nil, errors.New("user not exist") 46 | } 47 | userMap := make(map[int64]*db.UserRaw) 48 | for _, user := range users { 49 | userMap[int64(user.ID)] = user 50 | } 51 | 52 | var favoriteMap map[int64]*db.FavoriteRaw 53 | var relationMap map[int64]*db.RelationRaw 54 | if currentId == -1 { 55 | favoriteMap = nil 56 | relationMap = nil 57 | } else { 58 | var wg sync.WaitGroup 59 | wg.Add(2) 60 | var favoriteErr, relationErr error 61 | //获取点赞信息 62 | go func() { 63 | defer wg.Done() 64 | favoriteMap, err = db.QueryFavoriteByIds(s.ctx, currentId, videoIds) 65 | if err != nil { 66 | favoriteErr = err 67 | return 68 | } 69 | }() 70 | //获取关注信息 71 | go func() { 72 | defer wg.Done() 73 | relationMap, err = db.QueryRelationByIds(s.ctx, currentId, userIds) 74 | if err != nil { 75 | relationErr = err 76 | return 77 | } 78 | }() 79 | wg.Wait() 80 | if favoriteErr != nil { 81 | return nil, favoriteErr 82 | } 83 | if relationErr != nil { 84 | return nil, relationErr 85 | } 86 | } 87 | 88 | videoList := pack.PublishInfo(currentId, videoData, userMap, favoriteMap, relationMap) 89 | return videoList, nil 90 | } 91 | -------------------------------------------------------------------------------- /cmd/publish/service/publish_list_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/chenmengangzhi29/douyin/kitex_gen/publish" 8 | "github.com/cloudwego/kitex/pkg/klog" 9 | ) 10 | 11 | func TestPublishList(t *testing.T) { 12 | type args struct { 13 | token string 14 | userId int64 15 | } 16 | tests := []struct { 17 | name string 18 | args args 19 | wantErr bool 20 | }{ 21 | { 22 | name: "测试视频列表的默认token", 23 | args: args{ 24 | token: "", 25 | userId: 1, 26 | }, 27 | wantErr: false, 28 | }, 29 | { 30 | name: "测试视频列表的正常token", 31 | args: args{ 32 | token: Token, 33 | userId: 1, 34 | }, 35 | wantErr: false, 36 | }, 37 | { 38 | name: "测试视频列表的不正确用户id", 39 | args: args{ 40 | token: Token, 41 | userId: 99999999, 42 | }, 43 | wantErr: true, 44 | }, 45 | } 46 | 47 | for _, tt := range tests { 48 | t.Run(tt.name, func(t *testing.T) { 49 | videoList, err := NewPublishListService(context.Background()).PublishList(&publish.PublishListRequest{UserId: tt.args.userId, Token: tt.args.token}) 50 | if (err != nil) != tt.wantErr { 51 | t.Errorf("PublishList() error = %v, wantErr %v", err, tt.wantErr) 52 | return 53 | } 54 | klog.Info(tt.name + " success") 55 | klog.Info(videoList) 56 | }) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /cmd/publish/service/publish_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "io" 7 | "os" 8 | "testing" 9 | 10 | "github.com/chenmengangzhi29/douyin/dal/db" 11 | "github.com/chenmengangzhi29/douyin/kitex_gen/publish" 12 | "github.com/chenmengangzhi29/douyin/pkg/constants" 13 | "github.com/chenmengangzhi29/douyin/pkg/jwt" 14 | "github.com/chenmengangzhi29/douyin/pkg/oss" 15 | "github.com/cloudwego/kitex/pkg/klog" 16 | ) 17 | 18 | var File []byte 19 | var Token string 20 | 21 | func TestMain(m *testing.M) { 22 | 23 | Jwt := jwt.NewJWT([]byte(constants.SecretKey)) 24 | token, err := Jwt.CreateToken(jwt.CustomClaims{ 25 | Id: int64(1), 26 | }) 27 | if err != nil { 28 | klog.Errorf("create token fail, %v", err.Error()) 29 | panic(err) 30 | } 31 | Token = token 32 | 33 | db.Init() 34 | oss.Init() 35 | 36 | path := oss.Path + "/public/girl.mp4" 37 | file, err := os.Open(path) 38 | if err != nil { 39 | klog.Errorf("open local file %v fail", path) 40 | panic(err) 41 | } 42 | defer file.Close() 43 | //处理视频数据 44 | buf := bytes.NewBuffer(nil) 45 | if _, err := io.Copy(buf, file); err != nil { 46 | panic(err) 47 | } 48 | File = buf.Bytes() 49 | 50 | m.Run() 51 | } 52 | 53 | func TestPublish(t *testing.T) { 54 | type args struct { 55 | token string 56 | data []byte 57 | title string 58 | } 59 | tests := []struct { 60 | name string 61 | args args 62 | wantErr bool 63 | }{ 64 | { 65 | name: "测试上传视频的正常操作", 66 | args: args{ 67 | token: Token, 68 | data: File, 69 | title: "girl", 70 | }, 71 | wantErr: false, 72 | }, 73 | } 74 | 75 | for _, tt := range tests { 76 | t.Run(tt.name, func(t *testing.T) { 77 | err := NewPublishService(context.Background()).Publish(&publish.PublishActionRequest{Token: tt.args.token, Title: tt.args.title, Data: tt.args.data}) 78 | if (err != nil) != tt.wantErr { 79 | t.Errorf("%v fail, %v, wantErr %v", tt.name, err, tt.wantErr) 80 | return 81 | } 82 | klog.Info(tt.name + " success") 83 | }) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /cmd/relation/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | RUN_NAME="relation" 3 | 4 | mkdir -p output/bin 5 | cp script/* output/ 6 | chmod +x output/bootstrap.sh 7 | 8 | if [ "$IS_SYSTEM_TEST_ENV" != "1" ]; then 9 | go build -o output/bin/${RUN_NAME} 10 | else 11 | go test -c -covermode=set -o output/bin/${RUN_NAME} -coverpkg=./... 12 | fi 13 | -------------------------------------------------------------------------------- /cmd/relation/handler.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/chenmengangzhi29/douyin/cmd/relation/service" 7 | "github.com/chenmengangzhi29/douyin/dal/pack" 8 | "github.com/chenmengangzhi29/douyin/kitex_gen/relation" 9 | "github.com/chenmengangzhi29/douyin/pkg/errno" 10 | ) 11 | 12 | // RelationServiceImpl implements the last service interface defined in the IDL. 13 | type RelationServiceImpl struct{} 14 | 15 | // RelationAction implements the RelationServiceImpl interface. 16 | func (s *RelationServiceImpl) RelationAction(ctx context.Context, req *relation.RelationActionRequest) (resp *relation.RelationActionResponse, err error) { 17 | resp = new(relation.RelationActionResponse) 18 | 19 | if len(req.Token) == 0 || req.ToUserId == 0 || req.ActionType == 0 { 20 | resp.BaseResp = pack.BuilRelationBaseResp(errno.ParamErr) 21 | return resp, nil 22 | } 23 | 24 | err = service.NewRelationActionService(ctx).RelationAction(req) 25 | if err != nil { 26 | resp.BaseResp = pack.BuilRelationBaseResp(err) 27 | return resp, nil 28 | } 29 | resp.BaseResp = pack.BuilRelationBaseResp(errno.Success) 30 | return resp, nil 31 | } 32 | 33 | // FollowList implements the RelationServiceImpl interface. 34 | func (s *RelationServiceImpl) FollowList(ctx context.Context, req *relation.FollowListRequest) (resp *relation.FollowListResponse, err error) { 35 | resp = new(relation.FollowListResponse) 36 | 37 | if req.UserId == 0 { 38 | resp.BaseResp = pack.BuilRelationBaseResp(errno.ParamErr) 39 | return resp, nil 40 | } 41 | 42 | userList, err := service.NewFollowListService(ctx).FollowList(req) 43 | if err != nil { 44 | resp.BaseResp = pack.BuilRelationBaseResp(err) 45 | return resp, nil 46 | } 47 | resp.BaseResp = pack.BuilRelationBaseResp(errno.Success) 48 | resp.UserList = userList 49 | return resp, nil 50 | } 51 | 52 | // FollowerList implements the RelationServiceImpl interface. 53 | func (s *RelationServiceImpl) FollowerList(ctx context.Context, req *relation.FollowerListRequest) (resp *relation.FollowerListResponse, err error) { 54 | resp = new(relation.FollowerListResponse) 55 | 56 | if req.UserId == 0 { 57 | resp.BaseResp = pack.BuilRelationBaseResp(errno.ParamErr) 58 | return resp, nil 59 | } 60 | 61 | userList, err := service.NewFollowerListService(ctx).FollowerList(req) 62 | if err != nil { 63 | resp.BaseResp = pack.BuilRelationBaseResp(err) 64 | return resp, nil 65 | } 66 | resp.BaseResp = pack.BuilRelationBaseResp(errno.Success) 67 | resp.UserList = userList 68 | return resp, nil 69 | } 70 | -------------------------------------------------------------------------------- /cmd/relation/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/chenmengangzhi29/douyin/dal" 7 | relation "github.com/chenmengangzhi29/douyin/kitex_gen/relation/relationservice" 8 | "github.com/chenmengangzhi29/douyin/pkg/bound" 9 | "github.com/chenmengangzhi29/douyin/pkg/constants" 10 | "github.com/chenmengangzhi29/douyin/pkg/middleware" 11 | tracer2 "github.com/chenmengangzhi29/douyin/pkg/tracer" 12 | "github.com/cloudwego/kitex/pkg/klog" 13 | "github.com/cloudwego/kitex/pkg/limit" 14 | "github.com/cloudwego/kitex/pkg/rpcinfo" 15 | "github.com/cloudwego/kitex/server" 16 | etcd "github.com/kitex-contrib/registry-etcd" 17 | trace "github.com/kitex-contrib/tracer-opentracing" 18 | ) 19 | 20 | func Init() { 21 | tracer2.InitJaeger(constants.RelationServiceName) 22 | dal.Init() 23 | } 24 | 25 | func main() { 26 | r, err := etcd.NewEtcdRegistry([]string{constants.EtcdAddress}) // r should not be reused 27 | if err != nil { 28 | panic(err) 29 | } 30 | addr, err := net.ResolveTCPAddr("tcp", constants.RelationAddress) 31 | if err != nil { 32 | panic(err) 33 | } 34 | Init() 35 | svr := relation.NewServer(new(RelationServiceImpl), 36 | server.WithServerBasicInfo(&rpcinfo.EndpointBasicInfo{ServiceName: constants.RelationServiceName}), //server name 37 | server.WithMiddleware(middleware.CommonMiddleware), // middleWare 38 | server.WithMiddleware(middleware.ServerMiddleware), 39 | server.WithServiceAddr(addr), //address 40 | server.WithLimit(&limit.Option{MaxConnections: 1000, MaxQPS: 100}), //limit 41 | server.WithMuxTransport(), //Multiplex 42 | server.WithSuite(trace.NewDefaultServerSuite()), //tracer 43 | server.WithBoundHandler(bound.NewCpuLimitHandler()), //BoundHandler 44 | server.WithRegistry(r), // registry 45 | ) 46 | err = svr.Run() 47 | if err != nil { 48 | klog.Fatal(err) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /cmd/relation/script/bootstrap.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | CURDIR=$(cd $(dirname $0); pwd) 3 | 4 | if [ "X$1" != "X" ]; then 5 | RUNTIME_ROOT=$1 6 | else 7 | RUNTIME_ROOT=${CURDIR} 8 | fi 9 | 10 | export KITEX_RUNTIME_ROOT=$RUNTIME_ROOT 11 | export KITEX_LOG_DIR="$RUNTIME_ROOT/log" 12 | 13 | if [ ! -d "$KITEX_LOG_DIR/app" ]; then 14 | mkdir -p "$KITEX_LOG_DIR/app" 15 | fi 16 | 17 | if [ ! -d "$KITEX_LOG_DIR/rpc" ]; then 18 | mkdir -p "$KITEX_LOG_DIR/rpc" 19 | fi 20 | 21 | exec "$CURDIR/bin/relation" 22 | -------------------------------------------------------------------------------- /cmd/relation/service/follow_list.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "github.com/chenmengangzhi29/douyin/dal/db" 8 | "github.com/chenmengangzhi29/douyin/dal/pack" 9 | "github.com/chenmengangzhi29/douyin/kitex_gen/relation" 10 | "github.com/chenmengangzhi29/douyin/pkg/constants" 11 | "github.com/chenmengangzhi29/douyin/pkg/jwt" 12 | ) 13 | 14 | type FollowListService struct { 15 | ctx context.Context 16 | } 17 | 18 | // NewFollowListService new FollowListService 19 | func NewFollowListService(ctx context.Context) *FollowListService { 20 | return &FollowListService{ctx: ctx} 21 | } 22 | 23 | // FollowList get user follow list info 24 | func (s *FollowListService) FollowList(req *relation.FollowListRequest) ([]*relation.User, error) { 25 | Jwt := jwt.NewJWT([]byte(constants.SecretKey)) 26 | currentId, _ := Jwt.CheckToken(req.Token) 27 | 28 | user, err := db.QueryUserByIds(s.ctx, []int64{req.UserId}) 29 | if err != nil { 30 | return nil, err 31 | } 32 | if len(user) == 0 { 33 | return nil, errors.New("userId not exist") 34 | } 35 | 36 | //获取目标用户关注的用户id号 37 | relations, err := db.QueryFollowById(s.ctx, req.UserId) 38 | if err != nil { 39 | return nil, err 40 | } 41 | userIds := make([]int64, 0) 42 | for _, relation := range relations { 43 | userIds = append(userIds, relation.ToUserId) 44 | } 45 | 46 | //获取用户id号对应的用户信息 47 | users, err := db.QueryUserByIds(s.ctx, userIds) 48 | if err != nil { 49 | return nil, err 50 | } 51 | 52 | var relationMap map[int64]*db.RelationRaw 53 | if currentId == -1 { 54 | relationMap = nil 55 | } else { 56 | //获取当前用户和这些用户的关注信息 57 | relationMap, err = db.QueryRelationByIds(s.ctx, currentId, userIds) 58 | if err != nil { 59 | return nil, err 60 | } 61 | } 62 | userList := pack.UserList(currentId, users, relationMap) 63 | return userList, nil 64 | } 65 | -------------------------------------------------------------------------------- /cmd/relation/service/follow_list_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/chenmengangzhi29/douyin/kitex_gen/relation" 8 | "github.com/cloudwego/kitex/pkg/klog" 9 | ) 10 | 11 | func TestFollowList(t *testing.T) { 12 | type args struct { 13 | token string 14 | userId int64 15 | } 16 | tests := []struct { 17 | name string 18 | args args 19 | wantErr bool 20 | }{ 21 | { 22 | name: "测试关注列表的正常操作", 23 | args: args{ 24 | token: Token, 25 | userId: 1, 26 | }, 27 | wantErr: false, 28 | }, 29 | { 30 | name: "测试关注列表的错误用户id", 31 | args: args{ 32 | token: Token, 33 | userId: 99999999, 34 | }, 35 | wantErr: true, 36 | }, 37 | } 38 | 39 | for _, tt := range tests { 40 | t.Run(tt.name, func(t *testing.T) { 41 | _, err := NewFollowListService(context.Background()).FollowList(&relation.FollowListRequest{Token: tt.args.token, UserId: tt.args.userId}) 42 | if (err != nil) != tt.wantErr { 43 | t.Errorf("FollowList() error = %v, wantErr %v", err, tt.wantErr) 44 | return 45 | } 46 | klog.Info(tt.name + " success") 47 | }) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /cmd/relation/service/follower_list.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "github.com/chenmengangzhi29/douyin/dal/db" 8 | "github.com/chenmengangzhi29/douyin/dal/pack" 9 | "github.com/chenmengangzhi29/douyin/kitex_gen/relation" 10 | "github.com/chenmengangzhi29/douyin/pkg/constants" 11 | "github.com/chenmengangzhi29/douyin/pkg/jwt" 12 | ) 13 | 14 | type FollowerListService struct { 15 | ctx context.Context 16 | } 17 | 18 | // NewFollowerListService new FollowerListService 19 | func NewFollowerListService(ctx context.Context) *FollowerListService { 20 | return &FollowerListService{ctx: ctx} 21 | } 22 | 23 | // FollowerList get user follower list info 24 | func (s *FollowerListService) FollowerList(req *relation.FollowerListRequest) ([]*relation.User, error) { 25 | Jwt := jwt.NewJWT([]byte(constants.SecretKey)) 26 | currentId, _ := Jwt.CheckToken(req.Token) 27 | 28 | user, err := db.QueryUserByIds(s.ctx, []int64{req.UserId}) 29 | if err != nil { 30 | return nil, err 31 | } 32 | if len(user) == 0 { 33 | return nil, errors.New("userId not exist") 34 | } 35 | 36 | //查询目标用户的被关注记录 37 | relations, err := db.QueryFollowerById(s.ctx, req.UserId) 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | //获取这些记录的关注方id 43 | userIds := make([]int64, 0) 44 | for _, relation := range relations { 45 | userIds = append(userIds, relation.UserId) 46 | } 47 | 48 | //获取关注方的信息 49 | users, err := db.QueryUserByIds(s.ctx, userIds) 50 | if err != nil { 51 | return nil, err 52 | } 53 | 54 | var relationMap map[int64]*db.RelationRaw 55 | if currentId == -1 { 56 | relationMap = nil 57 | } else { 58 | //获取当前用户与关注方的关注记录 59 | relationMap, err = db.QueryRelationByIds(s.ctx, currentId, userIds) 60 | if err != nil { 61 | return nil, err 62 | } 63 | } 64 | 65 | userList := pack.UserList(currentId, users, relationMap) 66 | return userList, nil 67 | } 68 | -------------------------------------------------------------------------------- /cmd/relation/service/follower_list_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/chenmengangzhi29/douyin/kitex_gen/relation" 8 | "github.com/cloudwego/kitex/pkg/klog" 9 | ) 10 | 11 | func TestFollowerList(t *testing.T) { 12 | type args struct { 13 | token string 14 | userId int64 15 | } 16 | tests := []struct { 17 | name string 18 | args args 19 | wantErr bool 20 | }{ 21 | { 22 | name: "测试粉丝列表的正常操作", 23 | args: args{ 24 | token: Token, 25 | userId: 2, 26 | }, 27 | wantErr: false, 28 | }, 29 | { 30 | name: "测试粉丝列表的没有粉丝的用户", 31 | args: args{ 32 | token: Token, 33 | userId: 1, 34 | }, 35 | wantErr: false, 36 | }, 37 | { 38 | name: "测试粉丝列表的错误用户id", 39 | args: args{ 40 | token: Token, 41 | userId: 99999999, 42 | }, 43 | wantErr: true, 44 | }, 45 | } 46 | 47 | for _, tt := range tests { 48 | t.Run(tt.name, func(t *testing.T) { 49 | _, err := NewFollowerListService(context.Background()).FollowerList(&relation.FollowerListRequest{Token: tt.args.token, UserId: tt.args.userId}) 50 | if (err != nil) != tt.wantErr { 51 | t.Errorf("FollowerList() error = %v, wantErr %v", err, tt.wantErr) 52 | return 53 | } 54 | klog.Info(tt.name + " success") 55 | }) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /cmd/relation/service/relation_action_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "io" 7 | "os" 8 | "testing" 9 | 10 | "github.com/chenmengangzhi29/douyin/dal/db" 11 | "github.com/chenmengangzhi29/douyin/kitex_gen/relation" 12 | "github.com/chenmengangzhi29/douyin/pkg/constants" 13 | "github.com/chenmengangzhi29/douyin/pkg/jwt" 14 | "github.com/chenmengangzhi29/douyin/pkg/oss" 15 | "github.com/cloudwego/kitex/pkg/klog" 16 | ) 17 | 18 | var File []byte 19 | var Token string 20 | 21 | func TestMain(m *testing.M) { 22 | Jwt := jwt.NewJWT([]byte(constants.SecretKey)) 23 | token, err := Jwt.CreateToken(jwt.CustomClaims{ 24 | Id: int64(1), 25 | }) 26 | if err != nil { 27 | klog.Errorf("create token fail, %v", err.Error()) 28 | panic(err) 29 | } 30 | Token = token 31 | 32 | db.Init() 33 | oss.Init() 34 | 35 | path := oss.Path + "/public/girl.mp4" 36 | file, err := os.Open(path) 37 | if err != nil { 38 | klog.Errorf("open local file %v fail", path) 39 | panic(err) 40 | } 41 | defer file.Close() 42 | //处理视频数据 43 | buf := bytes.NewBuffer(nil) 44 | if _, err := io.Copy(buf, file); err != nil { 45 | panic(err) 46 | } 47 | File = buf.Bytes() 48 | 49 | m.Run() 50 | } 51 | 52 | func TestRelationAction(t *testing.T) { 53 | type args struct { 54 | token string 55 | toUserId int64 56 | actionType int64 57 | } 58 | tests := []struct { 59 | name string 60 | args args 61 | wantErr bool 62 | }{ 63 | { 64 | name: "测试关注操作的正常操作", 65 | args: args{ 66 | token: Token, 67 | toUserId: 2, 68 | actionType: 1, 69 | }, 70 | wantErr: false, 71 | }, 72 | { 73 | name: "测试取消关注的正常操作", 74 | args: args{ 75 | token: Token, 76 | toUserId: 2, 77 | actionType: 2, 78 | }, 79 | wantErr: false, 80 | }, 81 | { 82 | name: "测试关注操作的错误用户id", 83 | args: args{ 84 | token: Token, 85 | toUserId: 99999999, 86 | actionType: 1, 87 | }, 88 | wantErr: true, 89 | }, 90 | } 91 | 92 | for _, tt := range tests { 93 | t.Run(tt.name, func(t *testing.T) { 94 | err := NewRelationActionService(context.Background()).RelationAction(&relation.RelationActionRequest{Token: tt.args.token, ToUserId: tt.args.toUserId, ActionType: int32(tt.args.actionType)}) 95 | if (err != nil) != tt.wantErr { 96 | t.Errorf("RelationAction() error = %v, wantErr %v", err, tt.wantErr) 97 | return 98 | } 99 | klog.Info(tt.name + " success") 100 | }) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /cmd/relation/service/relaton_action.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "github.com/chenmengangzhi29/douyin/dal/db" 8 | "github.com/chenmengangzhi29/douyin/kitex_gen/relation" 9 | "github.com/chenmengangzhi29/douyin/pkg/constants" 10 | "github.com/chenmengangzhi29/douyin/pkg/jwt" 11 | ) 12 | 13 | type RelationActionService struct { 14 | ctx context.Context 15 | } 16 | 17 | // NewRelationActionService new RelationActionService 18 | func NewRelationActionService(ctx context.Context) *RelationActionService { 19 | return &RelationActionService{ctx: ctx} 20 | } 21 | 22 | // RelationAction implement follow and unfollow action 23 | //如果actionType等于1,表示当前用户关注其他用户, 24 | //当前用户的关注总数增加,其他用户的粉丝总数增加, 25 | //新建一条关注记录 26 | // 27 | //如果actionType等于2,表示当前用户取消关注其他用户 28 | //当前用户的关注总数减少,其他用户的粉丝总数减少, 29 | //删除该关注记录 30 | func (s *RelationActionService) RelationAction(req *relation.RelationActionRequest) error { 31 | Jwt := jwt.NewJWT([]byte(constants.SecretKey)) 32 | currentId, err := Jwt.CheckToken(req.Token) 33 | if err != nil { 34 | return err 35 | } 36 | 37 | users, err := db.QueryUserByIds(s.ctx, []int64{req.ToUserId}) 38 | if err != nil { 39 | return err 40 | } 41 | if len(users) == 0 { 42 | return errors.New("toUserId not exist") 43 | } 44 | 45 | if req.ActionType == constants.Follow { 46 | err := db.Create(s.ctx, currentId, req.ToUserId) 47 | if err != nil { 48 | return err 49 | } 50 | return nil 51 | } 52 | if req.ActionType == constants.UnFollow { 53 | err := db.Delete(s.ctx, currentId, req.ToUserId) 54 | if err != nil { 55 | return err 56 | } 57 | return nil 58 | } 59 | return errors.New("ActionType Err") 60 | } 61 | -------------------------------------------------------------------------------- /cmd/user/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | RUN_NAME="user" 3 | 4 | mkdir -p output/bin 5 | cp script/* output/ 6 | chmod +x output/bootstrap.sh 7 | 8 | if [ "$IS_SYSTEM_TEST_ENV" != "1" ]; then 9 | go build -o output/bin/${RUN_NAME} 10 | else 11 | go test -c -covermode=set -o output/bin/${RUN_NAME} -coverpkg=./... 12 | fi 13 | -------------------------------------------------------------------------------- /cmd/user/handler.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/chenmengangzhi29/douyin/cmd/user/service" 7 | "github.com/chenmengangzhi29/douyin/dal/pack" 8 | "github.com/chenmengangzhi29/douyin/kitex_gen/user" 9 | "github.com/chenmengangzhi29/douyin/pkg/errno" 10 | "github.com/chenmengangzhi29/douyin/pkg/jwt" 11 | ) 12 | 13 | // UserServiceImpl implements the last service interface defined in the IDL. 14 | type UserServiceImpl struct{} 15 | 16 | // CheckUser implements the UserServiceImpl interface. 17 | func (s *UserServiceImpl) CheckUser(ctx context.Context, req *user.CheckUserRequest) (resp *user.CheckUserResponse, err error) { 18 | resp = new(user.CheckUserResponse) 19 | 20 | if len(req.Username) == 0 || len(req.Password) == 0 { 21 | resp.BaseResp = pack.BuildUserBaseResp(errno.ParamErr) 22 | return resp, nil 23 | } 24 | 25 | uid, err := service.NewCheckUserService(ctx).CheckUser(req) 26 | if err != nil { 27 | resp.BaseResp = pack.BuildUserBaseResp(err) 28 | return resp, nil 29 | } 30 | resp.UserId = uid 31 | resp.BaseResp = pack.BuildUserBaseResp(errno.Success) 32 | return resp, nil 33 | } 34 | 35 | // RegisterUser implements the UserServiceImpl interface. 36 | func (s *UserServiceImpl) RegisterUser(ctx context.Context, req *user.RegisterUserRequest) (resp *user.RegisterUserResponse, err error) { 37 | resp = new(user.RegisterUserResponse) 38 | 39 | if len(req.Username) == 0 || len(req.Password) == 0 { 40 | resp.BaseResp = pack.BuildUserBaseResp(errno.ParamErr) 41 | } 42 | 43 | userId, err := service.NewRegisterUserService(ctx).RegisterUser(req) 44 | if err != nil { 45 | resp.BaseResp = pack.BuildUserBaseResp(err) 46 | return 47 | } 48 | 49 | token, err := Jwt.CreateToken(jwt.CustomClaims{ 50 | Id: int64(userId), 51 | }) 52 | if err != nil { 53 | resp.BaseResp = pack.BuildUserBaseResp(err) 54 | return 55 | } 56 | 57 | resp.BaseResp = pack.BuildUserBaseResp(errno.Success) 58 | resp.UserId = userId 59 | resp.Token = token 60 | return resp, nil 61 | } 62 | 63 | // UserInfo implements the UserServiceImpl interface. 64 | func (s *UserServiceImpl) UserInfo(ctx context.Context, req *user.UserInfoRequest) (resp *user.UserInfoResponse, err error) { 65 | resp = new(user.UserInfoResponse) 66 | 67 | if req.UserId == 0 { 68 | resp.BaseResp = pack.BuildUserBaseResp(errno.ParamErr) 69 | } 70 | 71 | user, err := service.NewUserInfoService(ctx).UserInfo(req) 72 | if err != nil { 73 | resp.BaseResp = pack.BuildUserBaseResp(err) 74 | return 75 | } 76 | 77 | resp.BaseResp = pack.BuildUserBaseResp(errno.Success) 78 | resp.User = user 79 | return resp, nil 80 | } 81 | -------------------------------------------------------------------------------- /cmd/user/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/chenmengangzhi29/douyin/dal" 7 | user "github.com/chenmengangzhi29/douyin/kitex_gen/user/userservice" 8 | "github.com/chenmengangzhi29/douyin/pkg/bound" 9 | "github.com/chenmengangzhi29/douyin/pkg/constants" 10 | "github.com/chenmengangzhi29/douyin/pkg/jwt" 11 | "github.com/chenmengangzhi29/douyin/pkg/middleware" 12 | tracer2 "github.com/chenmengangzhi29/douyin/pkg/tracer" 13 | "github.com/cloudwego/kitex/pkg/klog" 14 | "github.com/cloudwego/kitex/pkg/limit" 15 | "github.com/cloudwego/kitex/pkg/rpcinfo" 16 | "github.com/cloudwego/kitex/server" 17 | etcd "github.com/kitex-contrib/registry-etcd" 18 | trace "github.com/kitex-contrib/tracer-opentracing" 19 | ) 20 | 21 | var Jwt *jwt.JWT 22 | 23 | func Init() { 24 | tracer2.InitJaeger(constants.UserServiceName) 25 | dal.Init() 26 | Jwt = jwt.NewJWT([]byte(constants.SecretKey)) 27 | } 28 | 29 | func main() { 30 | r, err := etcd.NewEtcdRegistry([]string{constants.EtcdAddress}) //r should not be reused. 31 | if err != nil { 32 | panic(err) 33 | } 34 | addr, err := net.ResolveTCPAddr("tcp", constants.UserAddress) 35 | if err != nil { 36 | panic(err) 37 | } 38 | Init() 39 | svr := user.NewServer(new(UserServiceImpl), 40 | server.WithServerBasicInfo(&rpcinfo.EndpointBasicInfo{ServiceName: constants.UserServiceName}), //server name 41 | server.WithMiddleware(middleware.CommonMiddleware), 42 | server.WithMiddleware(middleware.ServerMiddleware), 43 | server.WithServiceAddr(addr), //address 44 | server.WithLimit(&limit.Option{MaxConnections: 1000, MaxQPS: 100}), //limit 45 | server.WithMuxTransport(), //Multiplex 46 | server.WithSuite(trace.NewDefaultServerSuite()), //tracer 47 | server.WithBoundHandler(bound.NewCpuLimitHandler()), //BoundHandler 48 | server.WithRegistry(r), //registry 49 | ) 50 | err = svr.Run() 51 | if err != nil { 52 | klog.Fatal(err) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /cmd/user/script/bootstrap.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | CURDIR=$(cd $(dirname $0); pwd) 3 | 4 | if [ "X$1" != "X" ]; then 5 | RUNTIME_ROOT=$1 6 | else 7 | RUNTIME_ROOT=${CURDIR} 8 | fi 9 | 10 | export KITEX_RUNTIME_ROOT=$RUNTIME_ROOT 11 | export KITEX_LOG_DIR="$RUNTIME_ROOT/log" 12 | 13 | if [ ! -d "$KITEX_LOG_DIR/app" ]; then 14 | mkdir -p "$KITEX_LOG_DIR/app" 15 | fi 16 | 17 | if [ ! -d "$KITEX_LOG_DIR/rpc" ]; then 18 | mkdir -p "$KITEX_LOG_DIR/rpc" 19 | fi 20 | 21 | exec "$CURDIR/bin/user" 22 | -------------------------------------------------------------------------------- /cmd/user/service/check_user.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "crypto/md5" 6 | "fmt" 7 | "io" 8 | 9 | "github.com/chenmengangzhi29/douyin/dal/db" 10 | "github.com/chenmengangzhi29/douyin/kitex_gen/user" 11 | "github.com/chenmengangzhi29/douyin/pkg/errno" 12 | ) 13 | 14 | type CheckUserService struct { 15 | ctx context.Context 16 | } 17 | 18 | // NewCheckUserService new CheckUserService 19 | func NewCheckUserService(ctx context.Context) *CheckUserService { 20 | return &CheckUserService{ 21 | ctx: ctx, 22 | } 23 | } 24 | 25 | // CheckUser check user info 26 | func (s *CheckUserService) CheckUser(req *user.CheckUserRequest) (int64, error) { 27 | h := md5.New() 28 | if _, err := io.WriteString(h, req.Password); err != nil { 29 | return 0, err 30 | } 31 | passWord := fmt.Sprintf("%x", h.Sum(nil)) 32 | 33 | userName := req.Username 34 | users, err := db.QueryUserByName(s.ctx, userName) 35 | if err != nil { 36 | return 0, err 37 | } 38 | if len(users) == 0 { 39 | return 0, errno.UserNotExistErr 40 | } 41 | u := users[0] 42 | if u.Password != passWord { 43 | return 0, errno.LoginErr 44 | } 45 | return int64(u.ID), nil 46 | } 47 | -------------------------------------------------------------------------------- /cmd/user/service/check_user_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/chenmengangzhi29/douyin/kitex_gen/user" 8 | "github.com/cloudwego/kitex/pkg/klog" 9 | ) 10 | 11 | func TestCheckUser(t *testing.T) { 12 | type args struct { 13 | username string 14 | password string 15 | } 16 | tests := []struct { 17 | name string 18 | args args 19 | wantErr bool 20 | }{ 21 | { 22 | name: "测试登陆存在的用户", 23 | args: args{ 24 | username: "xiaohuang", 25 | password: "xiaohuang", 26 | }, 27 | wantErr: false, 28 | }, 29 | { 30 | name: "测试登陆不存在的用户", 31 | args: args{ 32 | username: "UnExist", 33 | password: "UnExist", 34 | }, 35 | wantErr: true, 36 | }, 37 | } 38 | 39 | for _, tt := range tests { 40 | t.Run(tt.name, func(t *testing.T) { 41 | _, err := NewCheckUserService(context.Background()).CheckUser(&user.CheckUserRequest{Username: tt.args.username, Password: tt.args.password}) 42 | if (err != nil) != tt.wantErr { 43 | t.Errorf("CheckUser() error = %v, wantErr %v", err, tt.wantErr) 44 | return 45 | } 46 | klog.Info(tt.name + " success") 47 | }) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /cmd/user/service/register_user.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "crypto/md5" 6 | "fmt" 7 | "io" 8 | 9 | "github.com/chenmengangzhi29/douyin/dal/db" 10 | "github.com/chenmengangzhi29/douyin/kitex_gen/user" 11 | "github.com/chenmengangzhi29/douyin/pkg/errno" 12 | ) 13 | 14 | type RegisterUserService struct { 15 | ctx context.Context 16 | } 17 | 18 | // NewRegisterUserService new RegisterUserService 19 | func NewRegisterUserService(ctx context.Context) *RegisterUserService { 20 | return &RegisterUserService{ 21 | ctx: ctx, 22 | } 23 | } 24 | 25 | // RegisterUser register user info 26 | func (s *RegisterUserService) RegisterUser(req *user.RegisterUserRequest) (int64, error) { 27 | users, err := db.QueryUserByName(s.ctx, req.Username) 28 | if err != nil { 29 | return 0, err 30 | } 31 | if len(users) != 0 { 32 | return 0, errno.UserAlreadyExistErr 33 | } 34 | 35 | h := md5.New() 36 | if _, err = io.WriteString(h, req.Password); err != nil { 37 | return 0, err 38 | } 39 | password := fmt.Sprintf("%x", h.Sum(nil)) 40 | 41 | userId, err := db.UploadUserData(s.ctx, req.Username, password) 42 | if err != nil { 43 | return 0, err 44 | } 45 | 46 | return userId, nil 47 | 48 | } 49 | -------------------------------------------------------------------------------- /cmd/user/service/register_user_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "io" 7 | "os" 8 | "testing" 9 | 10 | "github.com/chenmengangzhi29/douyin/dal/db" 11 | "github.com/chenmengangzhi29/douyin/kitex_gen/user" 12 | "github.com/chenmengangzhi29/douyin/pkg/constants" 13 | "github.com/chenmengangzhi29/douyin/pkg/jwt" 14 | "github.com/chenmengangzhi29/douyin/pkg/oss" 15 | "github.com/cloudwego/kitex/pkg/klog" 16 | ) 17 | 18 | var File []byte 19 | var Token string 20 | 21 | func TestMain(m *testing.M) { 22 | 23 | Jwt := jwt.NewJWT([]byte(constants.SecretKey)) 24 | token, err := Jwt.CreateToken(jwt.CustomClaims{ 25 | Id: int64(1), 26 | }) 27 | if err != nil { 28 | klog.Errorf("create token fail, %v", err.Error()) 29 | panic(err) 30 | } 31 | Token = token 32 | 33 | db.Init() 34 | oss.Init() 35 | 36 | path := oss.Path + "/public/girl.mp4" 37 | file, err := os.Open(path) 38 | if err != nil { 39 | klog.Errorf("open local file %v fail", path) 40 | panic(err) 41 | } 42 | defer file.Close() 43 | //处理视频数据 44 | buf := bytes.NewBuffer(nil) 45 | if _, err := io.Copy(buf, file); err != nil { 46 | panic(err) 47 | } 48 | File = buf.Bytes() 49 | 50 | m.Run() 51 | } 52 | 53 | //测试用户注册 54 | func TestRegisterUser(t *testing.T) { 55 | type args struct { 56 | username string 57 | password string 58 | } 59 | tests := []struct { 60 | name string 61 | args args 62 | wantErr bool 63 | }{ 64 | { 65 | name: "测试注册不存在的用户", 66 | args: args{ 67 | username: "hhh", 68 | password: "hhh123", 69 | }, 70 | wantErr: false, 71 | }, 72 | } 73 | 74 | for _, tt := range tests { 75 | t.Run(tt.name, func(t *testing.T) { 76 | userId, err := NewRegisterUserService(context.Background()).RegisterUser(&user.RegisterUserRequest{Username: tt.args.username, Password: tt.args.password}) 77 | if (err != nil) != tt.wantErr { 78 | t.Errorf("RegisterUser() error = %v, wantErr %v", err, tt.wantErr) 79 | return 80 | } 81 | klog.Info(tt.name + " success") 82 | klog.Info(userId) 83 | }) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /cmd/user/service/user_info.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "github.com/chenmengangzhi29/douyin/dal/db" 8 | "github.com/chenmengangzhi29/douyin/dal/pack" 9 | "github.com/chenmengangzhi29/douyin/kitex_gen/user" 10 | "github.com/chenmengangzhi29/douyin/pkg/constants" 11 | "github.com/chenmengangzhi29/douyin/pkg/jwt" 12 | ) 13 | 14 | type UserInfoService struct { 15 | ctx context.Context 16 | } 17 | 18 | // NewUserInfoService new UserInfoService 19 | func NewUserInfoService(ctx context.Context) *UserInfoService { 20 | return &UserInfoService{ 21 | ctx: ctx, 22 | } 23 | } 24 | 25 | func (s *UserInfoService) UserInfo(req *user.UserInfoRequest) (*user.User, error) { 26 | Jwt := jwt.NewJWT([]byte(constants.SecretKey)) 27 | currentId, err := Jwt.CheckToken(req.Token) 28 | if err != nil { 29 | return nil, err 30 | } 31 | 32 | userIds := []int64{req.UserId} 33 | users, err := db.QueryUserByIds(s.ctx, userIds) 34 | if err != nil { 35 | return nil, err 36 | } 37 | if len(users) == 0 { 38 | return nil, errors.New("user not exist") 39 | } 40 | user := users[0] 41 | 42 | relationMap, err := db.QueryRelationByIds(s.ctx, currentId, userIds) 43 | if err != nil { 44 | return nil, err 45 | } 46 | 47 | var isFollow bool 48 | _, ok := relationMap[req.UserId] 49 | if ok { 50 | isFollow = true 51 | } else { 52 | isFollow = false 53 | } 54 | 55 | userInfo := pack.UserInfo(user, isFollow) 56 | return userInfo, nil 57 | } 58 | -------------------------------------------------------------------------------- /cmd/user/service/user_info_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/chenmengangzhi29/douyin/kitex_gen/user" 8 | "github.com/cloudwego/kitex/pkg/klog" 9 | ) 10 | 11 | //测试用户信息 12 | func TestUserInfo(t *testing.T) { 13 | type args struct { 14 | userId int64 15 | token string 16 | } 17 | tests := []struct { 18 | name string 19 | args args 20 | wantErr bool 21 | }{ 22 | { 23 | name: "测试获取存在用户的信息", 24 | args: args{ 25 | userId: 1, 26 | token: Token, 27 | }, 28 | wantErr: false, 29 | }, 30 | { 31 | name: "测试获取不存在用户的信息", 32 | args: args{ 33 | userId: 99999999, 34 | token: "JerryJerry123", 35 | }, 36 | wantErr: true, 37 | }, 38 | } 39 | 40 | for _, tt := range tests { 41 | t.Run(tt.name, func(t *testing.T) { 42 | user, err := NewUserInfoService(context.Background()).UserInfo(&user.UserInfoRequest{UserId: tt.args.userId, Token: tt.args.token}) 43 | if (err != nil) != tt.wantErr { 44 | t.Errorf("UserInfo() error = %v, wantErr %v", err, tt.wantErr) 45 | return 46 | } 47 | klog.Info(user) 48 | klog.Info(tt.name + " success") 49 | }) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /dal/db/comment.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cloudwego/kitex/pkg/klog" 7 | "gorm.io/gorm" 8 | ) 9 | 10 | // Comment Gorm Data Structures 11 | type CommentRaw struct { 12 | gorm.Model 13 | UserId int64 `gorm:"column:user_id;not null;index:idx_userid"` 14 | VideoId int64 `gorm:"column:video_id;not null;index:idx_videoid"` 15 | Contents string `grom:"column:contents;type:varchar(255);not null"` 16 | } 17 | 18 | func (CommentRaw) TableName() string { 19 | return "comment" 20 | } 21 | 22 | //通过一条评论创建一条评论记录并增加视频评论数 23 | func CreateComment(ctx context.Context, comment *CommentRaw) error { 24 | DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error { 25 | err := tx.Table("comment").Create(&comment).Error 26 | if err != nil { 27 | klog.Error("create comment fail " + err.Error()) 28 | return err 29 | } 30 | err = tx.Table("video").Where("id = ?", comment.VideoId).Update("comment_count", gorm.Expr("comment_count + ?", 1)).Error 31 | if err != nil { 32 | klog.Error("AddCommentCount error " + err.Error()) 33 | return err 34 | } 35 | err = tx.Table("comment").First(&comment).Error 36 | if err != nil { 37 | klog.Errorf("find comment %v fail, %v", comment, err.Error()) 38 | return err 39 | } 40 | return nil 41 | }) 42 | return nil 43 | } 44 | 45 | //通过评论id号删除一条评论并减少视频评论数,返回该评论 46 | func DeleteComment(ctx context.Context, commentId int64) (*CommentRaw, error) { 47 | var commentRaw *CommentRaw 48 | DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error { 49 | err := tx.Table("comment").Where("id = ?", commentId).First(&commentRaw).Error 50 | if err == gorm.ErrRecordNotFound { 51 | klog.Errorf("not find comment %v, %v", commentRaw, err.Error()) 52 | return err 53 | } 54 | if err != nil { 55 | klog.Errorf("find comment %v fail, %v", commentRaw, err.Error()) 56 | return err 57 | } 58 | err = tx.Table("comment").Where("id = ?", commentId).Delete(&CommentRaw{}).Error 59 | if err != nil { 60 | klog.Error("delete comment fail " + err.Error()) 61 | return err 62 | } 63 | err = tx.Table("video").Where("id = ?", commentRaw.VideoId).Update("comment_count", gorm.Expr("comment_count - ?", 1)).Error 64 | if err != nil { 65 | klog.Error("AddCommentCount error " + err.Error()) 66 | return err 67 | } 68 | return nil 69 | }) 70 | return commentRaw, nil 71 | } 72 | 73 | //通过评论id查询一组评论信息 74 | func QueryCommentByCommentIds(ctx context.Context, commentIds []int64) ([]*CommentRaw, error) { 75 | var comments []*CommentRaw 76 | err := DB.WithContext(ctx).Table("comment").Where("id In ?", commentIds).Find(&comments).Error 77 | if err != nil { 78 | klog.Error("query comment by comment id fail " + err.Error()) 79 | return nil, err 80 | } 81 | return comments, nil 82 | } 83 | 84 | //通过视频id号倒序返回一组评论信息 85 | func QueryCommentByVideoId(ctx context.Context, videoId int64) ([]*CommentRaw, error) { 86 | var comments []*CommentRaw 87 | err := DB.WithContext(ctx).Table("comment").Order("updated_at desc").Where("video_id = ?", videoId).Find(&comments).Error 88 | if err != nil { 89 | klog.Error("query comment by video id fail " + err.Error()) 90 | return nil, err 91 | } 92 | return comments, nil 93 | } 94 | -------------------------------------------------------------------------------- /dal/db/favorite.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cloudwego/kitex/pkg/klog" 7 | "gorm.io/gorm" 8 | ) 9 | 10 | // Favorite Gorm Data Structures 11 | type FavoriteRaw struct { 12 | gorm.Model 13 | UserId int64 `gorm:"column:user_id;not null;index:idx_userid"` 14 | VideoId int64 `gorm:"column:video_id;not null;index:idx_videoid"` 15 | } 16 | 17 | func (FavoriteRaw) TableName() string { 18 | return "favorite" 19 | } 20 | 21 | //根据当前用户id和视频id获取点赞信息 22 | func QueryFavoriteByIds(ctx context.Context, currentId int64, videoIds []int64) (map[int64]*FavoriteRaw, error) { 23 | var favorites []*FavoriteRaw 24 | err := DB.WithContext(ctx).Where("user_id = ? AND video_id IN ?", currentId, videoIds).Find(&favorites).Error 25 | if err != nil { 26 | klog.Error("quert favorite record fail " + err.Error()) 27 | return nil, err 28 | } 29 | favoriteMap := make(map[int64]*FavoriteRaw) 30 | for _, favorite := range favorites { 31 | favoriteMap[favorite.VideoId] = favorite 32 | } 33 | return favoriteMap, nil 34 | } 35 | 36 | // CreateFavorite add a record to the favorite table through a transaction, and add the number of video likes 37 | func CreateFavorite(ctx context.Context, favorite *FavoriteRaw, videoId int64) error { 38 | DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error { 39 | err := tx.Table("video").Where("id = ?", videoId).Update("favorite_count", gorm.Expr("favorite_count + ?", 1)).Error 40 | if err != nil { 41 | klog.Error("AddFavoriteCount error " + err.Error()) 42 | return err 43 | } 44 | 45 | err = tx.Table("favorite").Create(favorite).Error 46 | if err != nil { 47 | klog.Error("create favorite record fail " + err.Error()) 48 | return err 49 | } 50 | 51 | return nil 52 | }) 53 | return nil 54 | } 55 | 56 | //DeleteFavorite Delete a record in the favorite table and reduce the number of video likes 57 | func DeleteFavorite(ctx context.Context, currentId int64, videoId int64) error { 58 | DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error { 59 | var favorite *FavoriteRaw 60 | err := tx.Table("favorite").Where("user_id = ? AND video_id = ?", currentId, videoId).Delete(&favorite).Error 61 | if err != nil { 62 | klog.Error("delete favorite record fail " + err.Error()) 63 | return err 64 | } 65 | 66 | err = tx.Table("video").Where("id = ?", videoId).Update("favorite_count", gorm.Expr("favorite_count - ?", 1)).Error 67 | if err != nil { 68 | klog.Error("SubFavoriteCount error " + err.Error()) 69 | return err 70 | } 71 | return nil 72 | }) 73 | return nil 74 | } 75 | 76 | // QueryFavoriteById 通过一个用户id查询出该用户点赞的所有视频id号 77 | func QueryFavoriteById(ctx context.Context, userId int64) ([]int64, error) { 78 | var favorites []*FavoriteRaw 79 | err := DB.WithContext(ctx).Table("favorite").Where("user_id = ?", userId).Find(&favorites).Error 80 | if err != nil { 81 | klog.Error("query favorite record fail " + err.Error()) 82 | return nil, err 83 | } 84 | videoIds := make([]int64, 0) 85 | for _, favorite := range favorites { 86 | videoIds = append(videoIds, favorite.VideoId) 87 | } 88 | return videoIds, nil 89 | } 90 | -------------------------------------------------------------------------------- /dal/db/feed.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/chenmengangzhi29/douyin/pkg/constants" 8 | "github.com/cloudwego/kitex/pkg/klog" 9 | "gorm.io/gorm" 10 | ) 11 | 12 | // Video Gorm Data Structures 13 | type VideoRaw struct { 14 | gorm.Model 15 | UserId int64 `gorm:"column:user_id;not null;index:idx_userid"` 16 | Title string `gorm:"column:title;type:varchar(128);not null"` 17 | PlayUrl string `gorm:"column:play_url;varchar(128);not null"` 18 | CoverUrl string `gorm:"column:cover_url;varchar(128);not null"` 19 | FavoriteCount int64 `gorm:"column:favorite_count;default:0"` 20 | CommentCount int64 `gorm:"column:comment_count;default:0"` 21 | UpdatedAt time.Time `gorm:"column:update_time;not null;index:idx_update"` 22 | } 23 | 24 | func (v *VideoRaw) TableName() string { 25 | return constants.VideoTableName 26 | } 27 | 28 | //QueryVideoByLatestTime query video info by latest create Time 29 | func QueryVideoByLatestTime(ctx context.Context, latestTime int64) ([]*VideoRaw, error) { 30 | var videos []*VideoRaw 31 | time := time.UnixMilli(latestTime) 32 | err := DB.WithContext(ctx).Limit(30).Order("update_time desc").Where("update_time < ?", time).Find(&videos).Error 33 | if err != nil { 34 | klog.Error("QueryVideoByLatestTime find video error " + err.Error()) 35 | return videos, err 36 | } 37 | return videos, nil 38 | } 39 | 40 | //QueryVideoByVideoIds query video info by video ids 41 | func QueryVideoByVideoIds(ctx context.Context, videoIds []int64) ([]*VideoRaw, error) { 42 | var videos []*VideoRaw 43 | err := DB.WithContext(ctx).Where("id in (?)", videoIds).Find(&videos).Error 44 | if err != nil { 45 | klog.Error("QueryVideoByVideoIds error " + err.Error()) 46 | return nil, err 47 | } 48 | return videos, nil 49 | } 50 | -------------------------------------------------------------------------------- /dal/db/init.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "github.com/chenmengangzhi29/douyin/pkg/constants" 5 | "gorm.io/driver/mysql" 6 | "gorm.io/gorm" 7 | gormopentracing "gorm.io/plugin/opentracing" 8 | ) 9 | 10 | var DB *gorm.DB 11 | 12 | //Init init DB 13 | func Init() { 14 | var err error 15 | DB, err = gorm.Open(mysql.Open(constants.MySQLDefaultDSN), 16 | &gorm.Config{ 17 | PrepareStmt: true, 18 | SkipDefaultTransaction: true, 19 | }, 20 | ) 21 | if err != nil { 22 | panic(err) 23 | } 24 | 25 | if err = DB.Use(gormopentracing.New()); err != nil { 26 | panic(err) 27 | } 28 | 29 | err = DB.AutoMigrate(&UserRaw{}, &VideoRaw{}, &FavoriteRaw{}, &CommentRaw{}, &RelationRaw{}) 30 | if err != nil { 31 | panic(err) 32 | } 33 | 34 | sqlDB, err := DB.DB() 35 | if err != nil { 36 | panic(err) 37 | } 38 | 39 | if err := sqlDB.Ping(); err != nil { 40 | panic(err) 41 | } 42 | 43 | // SetMaxIdleConns 设置空闲连接池中连接的最大数量 44 | sqlDB.SetMaxIdleConns(constants.MySQLMaxIdleConns) 45 | 46 | // SetMaxOpenConns 设置打开数据库连接的最大数量 47 | sqlDB.SetMaxOpenConns(constants.MySQLMaxOpenConns) 48 | 49 | // SetConnMaxLifetime 设置了连接可复用的最大时间 50 | sqlDB.SetConnMaxLifetime(constants.MySQLConnMaxLifetime) 51 | 52 | } 53 | -------------------------------------------------------------------------------- /dal/db/publish.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cloudwego/kitex/pkg/klog" 7 | ) 8 | 9 | //向video表添加一条记录 10 | func PublishVideoData(ctx context.Context, videoData *VideoRaw) error { 11 | if err := DB.WithContext(ctx).Create(&videoData).Error; err != nil { 12 | klog.Error("PublishVideoData error " + err.Error()) 13 | return err 14 | } 15 | return nil 16 | } 17 | 18 | //新增通过用户id获取视频数据的功能 19 | func QueryVideoByUserId(ctx context.Context, userId int64) ([]*VideoRaw, error) { 20 | var videos []*VideoRaw 21 | err := DB.Table("video").WithContext(ctx).Order("update_time desc").Where("user_id = ?", userId).Find(&videos).Error 22 | if err != nil { 23 | klog.Error("QueryVideoByUserId find video error " + err.Error()) 24 | return nil, err 25 | } 26 | return videos, nil 27 | } 28 | -------------------------------------------------------------------------------- /dal/db/relation.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cloudwego/kitex/pkg/klog" 7 | "gorm.io/gorm" 8 | ) 9 | 10 | // Relation Gorm Data Structures 11 | type RelationRaw struct { 12 | gorm.Model 13 | UserId int64 `gorm:"column:user_id;not null;index:idx_userid"` 14 | ToUserId int64 `gorm:"column:to_user_id;not null;index:idx_touserid"` 15 | } 16 | 17 | func (RelationRaw) TableName() string { 18 | return "relation" 19 | } 20 | 21 | //根据当前用户id和目标用户id获取关注信息 22 | func QueryRelationByIds(ctx context.Context, currentId int64, userIds []int64) (map[int64]*RelationRaw, error) { 23 | var relations []*RelationRaw 24 | err := DB.WithContext(ctx).Where("user_id = ? AND to_user_id IN ?", currentId, userIds).Find(&relations).Error 25 | if err != nil { 26 | klog.Error("query relation by ids " + err.Error()) 27 | return nil, err 28 | } 29 | relationMap := make(map[int64]*RelationRaw) 30 | for _, relation := range relations { 31 | relationMap[relation.ToUserId] = relation 32 | } 33 | return relationMap, nil 34 | } 35 | 36 | //增加当前用户的关注总数,增加其他用户的粉丝总数,创建关注记录 37 | func Create(ctx context.Context, currentId int64, toUserId int64) error { 38 | relationRaw := &RelationRaw{ 39 | UserId: currentId, 40 | ToUserId: toUserId, 41 | } 42 | DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error { 43 | err := tx.Table("user").Where("id = ?", currentId).Update("follow_count", gorm.Expr("follow_count + ?", 1)).Error 44 | if err != nil { 45 | klog.Error("add user follow_count fail " + err.Error()) 46 | return err 47 | } 48 | 49 | err = tx.Table("user").Where("id = ?", toUserId).Update("follower_count", gorm.Expr("follower_count + ?", 1)).Error 50 | if err != nil { 51 | klog.Error("add user follower_count fail " + err.Error()) 52 | return err 53 | } 54 | 55 | err = tx.Table("relation").Create(&relationRaw).Error 56 | if err != nil { 57 | klog.Error("create relation record fail " + err.Error()) 58 | return err 59 | } 60 | 61 | return nil 62 | }) 63 | return nil 64 | } 65 | 66 | //减少当前用户的关注总数,减少其他用户的粉丝总数,删除关注记录 67 | func Delete(ctx context.Context, currentId int64, toUserId int64) error { 68 | var relationRaw *RelationRaw 69 | DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error { 70 | err := tx.Table("user").Where("id = ?", currentId).Update("follow_count", gorm.Expr("follow_count - ?", 1)).Error 71 | if err != nil { 72 | klog.Error("sub user follow_count fail " + err.Error()) 73 | return err 74 | } 75 | 76 | err = tx.Table("user").Where("id = ?", toUserId).Update("follower_count", gorm.Expr("follower_count - ?", 1)).Error 77 | if err != nil { 78 | klog.Error("sub user follower_count fail " + err.Error()) 79 | return err 80 | } 81 | 82 | err = tx.Table("relation").Where("user_id = ? AND to_user_id = ?", currentId, toUserId).Delete(&relationRaw).Error 83 | if err != nil { 84 | klog.Error("delete relation record fali " + err.Error()) 85 | return err 86 | } 87 | return nil 88 | }) 89 | return nil 90 | } 91 | 92 | //通过用户id,查询该用户关注的用户,返回两者之间的关注记录 93 | func QueryFollowById(ctx context.Context, userId int64) ([]*RelationRaw, error) { 94 | var relations []*RelationRaw 95 | err := DB.WithContext(ctx).Table("relation").Where("user_id = ?", userId).Find(&relations).Error 96 | if err != nil { 97 | klog.Error("query follow by id fail " + err.Error()) 98 | return nil, err 99 | } 100 | return relations, nil 101 | } 102 | 103 | //通过用户id,查询该用户的粉丝, 返回两者之间的关注记录 104 | func QueryFollowerById(ctx context.Context, userId int64) ([]*RelationRaw, error) { 105 | var relations []*RelationRaw 106 | err := DB.WithContext(ctx).Table("relation").Where("to_user_id = ?", userId).Find(&relations).Error 107 | if err != nil { 108 | klog.Error("query follower by id fail " + err.Error()) 109 | return nil, err 110 | } 111 | return relations, nil 112 | } 113 | -------------------------------------------------------------------------------- /dal/db/user.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cloudwego/kitex/pkg/klog" 7 | "gorm.io/gorm" 8 | ) 9 | 10 | // User Gorm Data structures 11 | type UserRaw struct { 12 | gorm.Model 13 | Name string `gorm:"column:name;index:idx_username,unique;type:varchar(32);not null"` 14 | Password string `gorm:"column:password;type:varchar(32);not null"` 15 | FollowCount int64 `gorm:"column:follow_count;default:0"` 16 | FollowerCount int64 `gorm:"column:follower_count;default:0"` 17 | } 18 | 19 | func (UserRaw) TableName() string { 20 | return "user" 21 | } 22 | 23 | //根据用户id获取用户信息 24 | func QueryUserByIds(ctx context.Context, userIds []int64) ([]*UserRaw, error) { 25 | var users []*UserRaw 26 | err := DB.WithContext(ctx).Where("id in (?)", userIds).Find(&users).Error 27 | if err != nil { 28 | klog.Error("query user by ids fail " + err.Error()) 29 | return nil, err 30 | } 31 | return users, nil 32 | } 33 | 34 | //根据用户名获取用户信息 35 | func QueryUserByName(ctx context.Context, userName string) ([]*UserRaw, error) { 36 | var users []*UserRaw 37 | err := DB.WithContext(ctx).Where("name = ?", userName).Find(&users).Error 38 | if err != nil { 39 | klog.Error("query user by name fail " + err.Error()) 40 | return nil, err 41 | } 42 | return users, nil 43 | } 44 | 45 | //上传用户信息到数据库 46 | func UploadUserData(ctx context.Context, username string, password string) (int64, error) { 47 | user := &UserRaw{ 48 | Name: username, 49 | Password: password, 50 | FollowCount: 0, 51 | FollowerCount: 0, 52 | } 53 | err := DB.WithContext(ctx).Create(&user).Error 54 | if err != nil { 55 | klog.Error("upload user data fail " + err.Error()) 56 | return 0, err 57 | } 58 | return int64(user.ID), nil 59 | } 60 | -------------------------------------------------------------------------------- /dal/init.go: -------------------------------------------------------------------------------- 1 | package dal 2 | 3 | import ( 4 | "github.com/chenmengangzhi29/douyin/dal/db" 5 | ) 6 | 7 | //Init init dal 8 | func Init() { 9 | db.Init() //mysql 10 | } 11 | -------------------------------------------------------------------------------- /dal/pack/comment.go: -------------------------------------------------------------------------------- 1 | package pack 2 | 3 | import ( 4 | "github.com/chenmengangzhi29/douyin/dal/db" 5 | "github.com/chenmengangzhi29/douyin/kitex_gen/comment" 6 | "github.com/chenmengangzhi29/douyin/pkg/constants" 7 | ) 8 | 9 | // 打包成可以直接返回的评论信息 10 | func CommentInfo(commentRaw *db.CommentRaw, user *db.UserRaw) *comment.Comment { 11 | comment := &comment.Comment{ 12 | Id: int64(commentRaw.ID), 13 | User: &comment.User{ 14 | Id: int64(user.ID), 15 | Name: user.Name, 16 | FollowCount: user.FollowCount, 17 | FollowerCount: user.FollowerCount, 18 | IsFollow: false, 19 | }, 20 | Content: commentRaw.Contents, 21 | CreateDate: commentRaw.UpdatedAt.Format(constants.TimeFormat), 22 | } 23 | return comment 24 | } 25 | 26 | func CommentList(currentId int64, comments []*db.CommentRaw, userMap map[int64]*db.UserRaw, relationMap map[int64]*db.RelationRaw) []*comment.Comment { 27 | commentList := make([]*comment.Comment, 0) 28 | for _, commentRaw := range comments { 29 | commentUser, ok := userMap[commentRaw.UserId] 30 | if !ok { 31 | commentUser = &db.UserRaw{ 32 | Name: "未知用户", 33 | FollowCount: 0, 34 | FollowerCount: 0, 35 | } 36 | commentUser.ID = 0 37 | } 38 | 39 | var isFollow bool = false 40 | 41 | if currentId != -1 { 42 | _, ok := relationMap[commentRaw.UserId] 43 | if ok { 44 | isFollow = true 45 | } 46 | } 47 | 48 | commentList = append(commentList, &comment.Comment{ 49 | Id: int64(commentRaw.ID), 50 | User: &comment.User{ 51 | Id: int64(commentUser.ID), 52 | Name: commentUser.Name, 53 | FollowCount: commentUser.FollowCount, 54 | FollowerCount: commentUser.FollowerCount, 55 | IsFollow: isFollow, 56 | }, 57 | Content: commentRaw.Contents, 58 | CreateDate: commentRaw.UpdatedAt.Format(constants.TimeFormat), 59 | }) 60 | } 61 | return commentList 62 | } 63 | -------------------------------------------------------------------------------- /dal/pack/favorite.go: -------------------------------------------------------------------------------- 1 | package pack 2 | 3 | import ( 4 | "github.com/chenmengangzhi29/douyin/dal/db" 5 | "github.com/chenmengangzhi29/douyin/kitex_gen/favorite" 6 | ) 7 | 8 | // VideoList pack video list info 9 | func VideoList(currentId int64, videoData []*db.VideoRaw, userMap map[int64]*db.UserRaw, favoriteMap map[int64]*db.FavoriteRaw, relationMap map[int64]*db.RelationRaw) []*favorite.Video { 10 | videoList := make([]*favorite.Video, 0) 11 | for _, video := range videoData { 12 | videoUser, ok := userMap[video.UserId] 13 | if !ok { 14 | videoUser = &db.UserRaw{ 15 | Name: "未知用户", 16 | FollowCount: 0, 17 | FollowerCount: 0, 18 | } 19 | videoUser.ID = 0 20 | } 21 | 22 | var isFavorite bool = false 23 | var isFollow bool = false 24 | 25 | if currentId != -1 { 26 | _, ok := favoriteMap[int64(video.ID)] 27 | if ok { 28 | isFavorite = true 29 | } 30 | _, ok = relationMap[video.UserId] 31 | if ok { 32 | isFollow = true 33 | } 34 | } 35 | videoList = append(videoList, &favorite.Video{ 36 | Id: int64(video.ID), 37 | Author: &favorite.User{ 38 | Id: int64(videoUser.ID), 39 | Name: videoUser.Name, 40 | FollowCount: videoUser.FollowCount, 41 | FollowerCount: videoUser.FollowerCount, 42 | IsFollow: isFollow, 43 | }, 44 | PlayUrl: video.PlayUrl, 45 | CoverUrl: video.CoverUrl, 46 | FavoriteCount: video.FavoriteCount, 47 | CommentCount: video.CommentCount, 48 | IsFavorite: isFavorite, 49 | Title: video.Title, 50 | }) 51 | } 52 | 53 | return videoList 54 | } 55 | -------------------------------------------------------------------------------- /dal/pack/feed.go: -------------------------------------------------------------------------------- 1 | package pack 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/chenmengangzhi29/douyin/dal/db" 7 | "github.com/chenmengangzhi29/douyin/kitex_gen/feed" 8 | ) 9 | 10 | // VideoInfo pack video list info 11 | func VideoInfo(currentId int64, videoData []*db.VideoRaw, userMap map[int64]*db.UserRaw, favoriteMap map[int64]*db.FavoriteRaw, relationMap map[int64]*db.RelationRaw) ([]*feed.Video, int64) { 12 | videoList := make([]*feed.Video, 0) 13 | var nextTime int64 14 | for _, video := range videoData { 15 | videoUser, ok := userMap[video.UserId] 16 | if !ok { 17 | videoUser = &db.UserRaw{ 18 | Name: "未知用户", 19 | FollowCount: 0, 20 | FollowerCount: 0, 21 | } 22 | videoUser.ID = 0 23 | } 24 | 25 | var isFavorite bool = false 26 | var isFollow bool = false 27 | 28 | if currentId != -1 { 29 | _, ok := favoriteMap[int64(video.ID)] 30 | if ok { 31 | isFavorite = true 32 | } 33 | _, ok = relationMap[video.UserId] 34 | if ok { 35 | isFollow = true 36 | } 37 | } 38 | videoList = append(videoList, &feed.Video{ 39 | Id: int64(video.ID), 40 | Author: &feed.User{ 41 | Id: int64(videoUser.ID), 42 | Name: videoUser.Name, 43 | FollowCount: videoUser.FollowCount, 44 | FollowerCount: videoUser.FollowerCount, 45 | IsFollow: isFollow, 46 | }, 47 | PlayUrl: video.PlayUrl, 48 | CoverUrl: video.CoverUrl, 49 | FavoriteCount: video.FavoriteCount, 50 | CommentCount: video.CommentCount, 51 | IsFavorite: isFavorite, 52 | Title: video.Title, 53 | }) 54 | } 55 | 56 | if len(videoData) == 0 { 57 | nextTime = time.Now().UnixMilli() 58 | } else { 59 | nextTime = videoData[len(videoData)-1].UpdatedAt.UnixMilli() 60 | } 61 | 62 | return videoList, nextTime 63 | } 64 | -------------------------------------------------------------------------------- /dal/pack/publish.go: -------------------------------------------------------------------------------- 1 | package pack 2 | 3 | import ( 4 | "github.com/chenmengangzhi29/douyin/dal/db" 5 | "github.com/chenmengangzhi29/douyin/kitex_gen/publish" 6 | ) 7 | 8 | // VideoInfo pack video list info 9 | func PublishInfo(currentId int64, videoData []*db.VideoRaw, userMap map[int64]*db.UserRaw, favoriteMap map[int64]*db.FavoriteRaw, relationMap map[int64]*db.RelationRaw) []*publish.Video { 10 | videoList := make([]*publish.Video, 0) 11 | for _, video := range videoData { 12 | videoUser, ok := userMap[video.UserId] 13 | if !ok { 14 | videoUser = &db.UserRaw{ 15 | Name: "未知用户", 16 | FollowCount: 0, 17 | FollowerCount: 0, 18 | } 19 | videoUser.ID = 0 20 | } 21 | 22 | var isFavorite bool = false 23 | var isFollow bool = false 24 | 25 | if currentId != -1 { 26 | _, ok := favoriteMap[int64(video.ID)] 27 | if ok { 28 | isFavorite = true 29 | } 30 | _, ok = relationMap[video.UserId] 31 | if ok { 32 | isFollow = true 33 | } 34 | } 35 | videoList = append(videoList, &publish.Video{ 36 | Id: int64(video.ID), 37 | Author: &publish.User{ 38 | Id: int64(videoUser.ID), 39 | Name: videoUser.Name, 40 | FollowCount: videoUser.FollowCount, 41 | FollowerCount: videoUser.FollowerCount, 42 | IsFollow: isFollow, 43 | }, 44 | PlayUrl: video.PlayUrl, 45 | CoverUrl: video.CoverUrl, 46 | FavoriteCount: video.FavoriteCount, 47 | CommentCount: video.CommentCount, 48 | IsFavorite: isFavorite, 49 | Title: video.Title, 50 | }) 51 | } 52 | 53 | return videoList 54 | } 55 | -------------------------------------------------------------------------------- /dal/pack/relation.go: -------------------------------------------------------------------------------- 1 | package pack 2 | 3 | import ( 4 | "github.com/chenmengangzhi29/douyin/dal/db" 5 | "github.com/chenmengangzhi29/douyin/kitex_gen/relation" 6 | ) 7 | 8 | func UserList(currentId int64, users []*db.UserRaw, relationMap map[int64]*db.RelationRaw) []*relation.User { 9 | userList := make([]*relation.User, 0) 10 | for _, user := range users { 11 | var isFollow bool = false 12 | 13 | if currentId != -1 { 14 | _, ok := relationMap[int64(user.ID)] 15 | if ok { 16 | isFollow = true 17 | } 18 | } 19 | userList = append(userList, &relation.User{ 20 | Id: int64(user.ID), 21 | Name: user.Name, 22 | FollowCount: user.FollowCount, 23 | FollowerCount: user.FollowerCount, 24 | IsFollow: isFollow, 25 | }) 26 | } 27 | return userList 28 | } 29 | -------------------------------------------------------------------------------- /dal/pack/resp.go: -------------------------------------------------------------------------------- 1 | package pack 2 | 3 | import ( 4 | "errors" 5 | "time" 6 | 7 | "github.com/chenmengangzhi29/douyin/kitex_gen/comment" 8 | "github.com/chenmengangzhi29/douyin/kitex_gen/favorite" 9 | "github.com/chenmengangzhi29/douyin/kitex_gen/feed" 10 | "github.com/chenmengangzhi29/douyin/kitex_gen/publish" 11 | "github.com/chenmengangzhi29/douyin/kitex_gen/relation" 12 | "github.com/chenmengangzhi29/douyin/kitex_gen/user" 13 | "github.com/chenmengangzhi29/douyin/pkg/errno" 14 | ) 15 | 16 | // BuildFeedBaseResp build feed baseResp from error 17 | func BuildFeedBaseResp(err error) *feed.BaseResp { 18 | if err == nil { 19 | return feedbaseResp(errno.Success) 20 | } 21 | 22 | e := errno.ErrNo{} 23 | if errors.As(err, &e) { 24 | return feedbaseResp(e) 25 | } 26 | 27 | s := errno.ServiceErr.WithMessage(err.Error()) 28 | return feedbaseResp(s) 29 | } 30 | 31 | func feedbaseResp(err errno.ErrNo) *feed.BaseResp { 32 | return &feed.BaseResp{StatusCode: err.ErrCode, StatusMessage: err.ErrMsg, ServiceTime: time.Now().Unix()} 33 | } 34 | 35 | //BuildPublishBaseResp build publish baseResp from error 36 | func BuildPublishBaseResp(err error) *publish.BaseResp { 37 | if err == nil { 38 | return publishbaseResp(errno.Success) 39 | } 40 | 41 | e := errno.ErrNo{} 42 | if errors.As(err, &e) { 43 | return publishbaseResp(e) 44 | } 45 | 46 | s := errno.ServiceErr.WithMessage(err.Error()) 47 | return publishbaseResp(s) 48 | } 49 | 50 | func publishbaseResp(err errno.ErrNo) *publish.BaseResp { 51 | return &publish.BaseResp{StatusCode: err.ErrCode, StatusMessage: err.ErrMsg, ServiceTime: time.Now().Unix()} 52 | } 53 | 54 | //BuildUserBaseResp build user baseResp from error 55 | func BuildUserBaseResp(err error) *user.BaseResp { 56 | if err == nil { 57 | return userbaseResp(errno.Success) 58 | } 59 | 60 | e := errno.ErrNo{} 61 | if errors.As(err, &e) { 62 | return userbaseResp(e) 63 | } 64 | 65 | s := errno.ServiceErr.WithMessage(err.Error()) 66 | return userbaseResp(s) 67 | } 68 | 69 | func userbaseResp(err errno.ErrNo) *user.BaseResp { 70 | return &user.BaseResp{StatusCode: err.ErrCode, StatusMessage: err.ErrMsg, ServiceTime: time.Now().Unix()} 71 | } 72 | 73 | //BuildFavoriteBaseResp build favorite baseResp from error 74 | func BuildFavoriteBaseResp(err error) *favorite.BaseResp { 75 | if err == nil { 76 | return favoritebaseResp(errno.Success) 77 | } 78 | 79 | e := errno.ErrNo{} 80 | if errors.As(err, &e) { 81 | return favoritebaseResp(e) 82 | } 83 | 84 | s := errno.ServiceErr.WithMessage(err.Error()) 85 | return favoritebaseResp(s) 86 | } 87 | 88 | func favoritebaseResp(err errno.ErrNo) *favorite.BaseResp { 89 | return &favorite.BaseResp{StatusCode: err.ErrCode, StatusMessage: err.ErrMsg, ServiceTime: time.Now().Unix()} 90 | } 91 | 92 | //BuildCommentBaseResp build comment baseResp from error 93 | func BuilCommentBaseResp(err error) *comment.BaseResp { 94 | if err == nil { 95 | return commentbaseResp(errno.Success) 96 | } 97 | 98 | e := errno.ErrNo{} 99 | if errors.As(err, &e) { 100 | return commentbaseResp(e) 101 | } 102 | 103 | s := errno.ServiceErr.WithMessage(err.Error()) 104 | return commentbaseResp(s) 105 | } 106 | 107 | func commentbaseResp(err errno.ErrNo) *comment.BaseResp { 108 | return &comment.BaseResp{StatusCode: err.ErrCode, StatusMessage: err.ErrMsg, ServiceTime: time.Now().Unix()} 109 | } 110 | 111 | //BuildRelationBaseResp build relation baseResp from error 112 | func BuilRelationBaseResp(err error) *relation.BaseResp { 113 | if err == nil { 114 | return relationbaseResp(errno.Success) 115 | } 116 | 117 | e := errno.ErrNo{} 118 | if errors.As(err, &e) { 119 | return relationbaseResp(e) 120 | } 121 | 122 | s := errno.ServiceErr.WithMessage(err.Error()) 123 | return relationbaseResp(s) 124 | } 125 | 126 | func relationbaseResp(err errno.ErrNo) *relation.BaseResp { 127 | return &relation.BaseResp{StatusCode: err.ErrCode, StatusMessage: err.ErrMsg, ServiceTime: time.Now().Unix()} 128 | } 129 | -------------------------------------------------------------------------------- /dal/pack/user.go: -------------------------------------------------------------------------------- 1 | package pack 2 | 3 | import ( 4 | "github.com/chenmengangzhi29/douyin/kitex_gen/user" 5 | 6 | "github.com/chenmengangzhi29/douyin/dal/db" 7 | ) 8 | 9 | func UserInfo(userRaw *db.UserRaw, isFollow bool) *user.User { 10 | userInfo := &user.User{ 11 | Id: int64(userRaw.ID), 12 | Name: userRaw.Name, 13 | FollowCount: userRaw.FollowCount, 14 | FollowerCount: userRaw.FollowerCount, 15 | IsFollow: isFollow, 16 | } 17 | return userInfo 18 | } 19 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | mysql: 5 | image: 'mysql:latest' 6 | ports: 7 | - 9910:3306 8 | environment: 9 | - MYSQL_DATABASE=gorm 10 | - MYSQL_USER=gorm 11 | - MYSQL_PASSWORD=gorm 12 | - MYSQL_RANDOM_ROOT_PASSWORD="yes" 13 | Etcd: 14 | image: 'bitnami/etcd:latest' 15 | environment: 16 | - ALLOW_NONE_AUTHENTICATION=yes 17 | ports: 18 | - 2379:2379 19 | jaeger: 20 | image: jaegertracing/all-in-one:latest 21 | ports: 22 | - "6831:6831/udp" 23 | - "16686:16686" -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/chenmengangzhi29/douyin 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/appleboy/gin-jwt/v2 v2.8.0 7 | github.com/cloudwego/kitex v0.3.2 8 | github.com/disintegration/imaging v1.6.2 9 | github.com/gin-gonic/gin v1.7.7 10 | github.com/golang-jwt/jwt v3.2.2+incompatible 11 | github.com/kitex-contrib/registry-etcd v0.0.0-20220618003451-c5459d0f49f5 12 | github.com/kitex-contrib/tracer-opentracing v0.0.3 13 | github.com/opentracing/opentracing-go v1.2.0 14 | github.com/shirou/gopsutil v3.21.11+incompatible 15 | github.com/u2takey/ffmpeg-go v0.4.1 16 | github.com/uber/jaeger-client-go v2.30.0+incompatible 17 | google.golang.org/protobuf v1.28.0 18 | gorm.io/driver/mysql v1.3.3 19 | gorm.io/gorm v1.23.5 20 | gorm.io/plugin/opentracing v0.0.0-20211220013347-7d2b2af23560 21 | ) 22 | 23 | require ( 24 | github.com/apache/thrift v0.13.0 // indirect 25 | github.com/aws/aws-sdk-go v1.38.20 // indirect 26 | github.com/bytedance/gopkg v0.0.0-20220509134931-d1878f638986 // indirect 27 | github.com/choleraehyq/pid v0.0.13 // indirect 28 | github.com/cloudwego/netpoll v0.2.4 // indirect 29 | github.com/cloudwego/thriftgo v0.1.2 // indirect 30 | github.com/coreos/go-semver v0.3.0 // indirect 31 | github.com/coreos/go-systemd/v22 v22.3.2 // indirect 32 | github.com/gin-contrib/sse v0.1.0 // indirect 33 | github.com/go-ole/go-ole v1.2.6 // indirect 34 | github.com/go-playground/locales v0.13.0 // indirect 35 | github.com/go-playground/universal-translator v0.17.0 // indirect 36 | github.com/go-playground/validator/v10 v10.4.1 // indirect 37 | github.com/go-sql-driver/mysql v1.6.0 // indirect 38 | github.com/gogo/protobuf v1.3.2 // indirect 39 | github.com/golang-jwt/jwt/v4 v4.2.0 // indirect 40 | github.com/golang/protobuf v1.5.2 // indirect 41 | github.com/jinzhu/inflection v1.0.0 // indirect 42 | github.com/jinzhu/now v1.1.4 // indirect 43 | github.com/jmespath/go-jmespath v0.4.0 // indirect 44 | github.com/json-iterator/go v1.1.12 // indirect 45 | github.com/leodido/go-urn v1.2.0 // indirect 46 | github.com/mattn/go-isatty v0.0.12 // indirect 47 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 48 | github.com/modern-go/reflect2 v1.0.2 // indirect 49 | github.com/pkg/errors v0.9.1 // indirect 50 | github.com/tidwall/gjson v1.12.1 // indirect 51 | github.com/tidwall/match v1.1.1 // indirect 52 | github.com/tidwall/pretty v1.2.0 // indirect 53 | github.com/tklauser/go-sysconf v0.3.10 // indirect 54 | github.com/tklauser/numcpus v0.4.0 // indirect 55 | github.com/u2takey/go-utils v0.3.1 // indirect 56 | github.com/uber/jaeger-lib v2.4.1+incompatible // indirect 57 | github.com/ugorji/go/codec v1.1.7 // indirect 58 | github.com/yusufpapurcu/wmi v1.2.2 // indirect 59 | go.etcd.io/etcd/api/v3 v3.5.1 // indirect 60 | go.etcd.io/etcd/client/pkg/v3 v3.5.1 // indirect 61 | go.etcd.io/etcd/client/v3 v3.5.1 // indirect 62 | go.uber.org/atomic v1.8.0 // indirect 63 | go.uber.org/multierr v1.6.0 // indirect 64 | golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e // indirect 65 | golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 // indirect 66 | golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect 67 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect 68 | golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27 // indirect 69 | golang.org/x/text v0.3.6 // indirect 70 | google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect 71 | google.golang.org/grpc v1.38.0 // indirect 72 | gopkg.in/yaml.v2 v2.4.0 // indirect 73 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect 74 | ) 75 | 76 | require ( 77 | github.com/aliyun/aliyun-oss-go-sdk v2.2.4+incompatible 78 | github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect 79 | github.com/satori/go.uuid v1.2.0 // indirect 80 | go.uber.org/zap v1.21.0 // indirect 81 | golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect 82 | ) 83 | -------------------------------------------------------------------------------- /idl/comment.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package comment; 3 | option go_package = "comment"; 4 | 5 | message BaseResp { 6 | int32 status_code = 1;//状态码 7 | string status_message = 2;//状态描述 8 | int64 service_time = 3;//服务时间 9 | } 10 | 11 | message User { 12 | int64 id = 1;//用户id 13 | string name = 2;//用户名称 14 | int64 follow_count = 3;//关注总数 15 | int64 follower_count = 4;//粉丝总数 16 | bool is_follow = 5;//true-已关注,false-未关注 17 | } 18 | 19 | message Comment { 20 | int64 id = 1;//视频评论id 21 | User user = 2;//评论用户信息 22 | string content = 3;//评论内容 23 | string create_date = 4;//评论发布日期,格式mm-dd 24 | } 25 | 26 | 27 | message create_comment_request { 28 | string token = 1;//用户鉴权token 29 | int64 video_id = 2;//视频id 30 | string comment_text = 3;////用户填写的评论内容 31 | } 32 | 33 | message create_comment_response { 34 | BaseResp base_resp = 1; 35 | Comment comment = 2;//评论成功返回评论内容,不需要重新拉取整个列表 36 | } 37 | 38 | message delete_comment_request { 39 | string token = 1;//用户鉴权token 40 | int64 video_id = 2;//视频id 41 | int64 comment_id = 3;//要删除的评论id 42 | } 43 | 44 | message delete_comment_response { 45 | BaseResp base_resp = 1; 46 | Comment comment = 2;//评论成功返回评论内容,不需要重新拉取整个列表 47 | } 48 | 49 | 50 | message comment_list_request { 51 | string token = 1;//用户鉴权token 52 | int64 video_id = 2;//视频id 53 | } 54 | 55 | message comment_list_response { 56 | BaseResp base_resp = 1; 57 | repeated Comment comment_list = 2;//评论列表 58 | } 59 | 60 | service CommentService { 61 | rpc CreateComment (create_comment_request) returns (create_comment_response) {} 62 | rpc DeleteComment (delete_comment_request) returns (delete_comment_response) {} 63 | rpc CommentList (comment_list_request) returns (comment_list_response) {} 64 | } 65 | 66 | -------------------------------------------------------------------------------- /idl/favorite.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package favorite; 3 | option go_package = "favorite"; 4 | 5 | message BaseResp { 6 | int32 status_code = 1;//状态码 7 | string status_message = 2;//状态描述 8 | int64 service_time = 3;//服务时间 9 | } 10 | 11 | message User { 12 | int64 id = 1;//用户id 13 | string name = 2;//用户名称 14 | int64 follow_count = 3;//关注总数 15 | int64 follower_count = 4;//粉丝总数 16 | bool is_follow = 5;//true-已关注,false-未关注 17 | } 18 | 19 | message Video { 20 | int64 id = 1;//视频唯一标识 21 | User author = 2;//视频作者信息 22 | string play_url = 3;//视频播放地址 23 | string cover_url = 4;//视频封面地址 24 | int64 favorite_count = 5;//视频的点赞总数 25 | int64 comment_count = 6;//视频的评论总数 26 | bool is_favorite = 7;//true-已点赞,false-未点赞 27 | string title = 8;//视频标题 28 | } 29 | 30 | message favorite_action_request { 31 | string token = 2;//用户鉴权token 32 | int64 video_id = 3;//视频id 33 | int32 action_type = 4;//1-点赞,2-取消点赞 34 | } 35 | 36 | message favorite_action_response { 37 | BaseResp base_resp = 1; 38 | } 39 | 40 | message favorite_list_request { 41 | int64 user_id = 1;//用户id 42 | string token = 2;//用户鉴权token 43 | } 44 | 45 | message favorite_list_response { 46 | BaseResp base_resp = 1; 47 | repeated Video video_list = 2;//用户点赞视频列表 48 | } 49 | 50 | service FavoriteService { 51 | rpc FavoriteAction (favorite_action_request) returns (favorite_action_response) {} 52 | rpc FavoriteList (favorite_list_request) returns (favorite_list_response) {} 53 | } -------------------------------------------------------------------------------- /idl/feed.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package feed; 3 | option go_package = "feed"; 4 | 5 | message BaseResp { 6 | int32 status_code = 1;//状态码 7 | string status_message = 2;//状态描述 8 | int64 service_time = 3;//服务时间 9 | } 10 | 11 | message User { 12 | int64 id = 1;//用户id 13 | string name = 2;//用户名称 14 | int64 follow_count = 3;//关注总数 15 | int64 follower_count = 4;//粉丝总数 16 | bool is_follow = 5;//true-已关注,false-未关注 17 | } 18 | 19 | message Video { 20 | int64 id = 1;//视频唯一标识 21 | User author = 2;//视频作者信息 22 | string play_url = 3;//视频播放地址 23 | string cover_url = 4;//视频封面地址 24 | int64 favorite_count = 5;//视频的点赞总数 25 | int64 comment_count = 6;//视频的评论总数 26 | bool is_favorite = 7;//true-已点赞,false-未点赞 27 | string title = 8;//视频标题 28 | 29 | } 30 | 31 | message FeedRequest { 32 | int64 latest_time = 1;//可选参数,限制返回视频的最新投稿时间戳,精确到秒,不填表示当前时间 33 | string token = 2;//可选参数,登陆用户设置 34 | } 35 | 36 | message FeedResponse { 37 | BaseResp base_resp = 1; 38 | repeated Video video_list = 2;//视频列表 39 | int64 next_time = 3;//本次返回的视频中,发布最早的时间,作为下次请求时的latest_time 40 | } 41 | 42 | service FeedService { 43 | rpc Feed (FeedRequest) returns (FeedResponse) {} 44 | } 45 | 46 | 47 | -------------------------------------------------------------------------------- /idl/publish.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package publish; 3 | option go_package = "publish"; 4 | 5 | message BaseResp { 6 | int32 status_code = 1;//状态码 7 | string status_message = 2;//状态描述 8 | int64 service_time = 3;//服务时间 9 | } 10 | 11 | message User { 12 | int64 id = 1;//用户id 13 | string name = 2;//用户名称 14 | int64 follow_count = 3;//关注总数 15 | int64 follower_count = 4;//粉丝总数 16 | bool is_follow = 5;//true-已关注,false-未关注 17 | } 18 | 19 | message Video { 20 | int64 id = 1;//视频唯一标识 21 | User author = 2;//视频作者信息 22 | string play_url = 3;//视频播放地址 23 | string cover_url = 4;//视频封面地址 24 | int64 favorite_count = 5;//视频的点赞总数 25 | int64 comment_count = 6;//视频的评论总数 26 | bool is_favorite = 7;//true-已点赞,false-未点赞 27 | string title = 8;//视频标题 28 | 29 | } 30 | 31 | message publish_action_request { 32 | string token = 1;//用户鉴权 33 | string title = 2;//视频标题 34 | bytes data = 3;//视频数据 35 | } 36 | 37 | message publish_action_response { 38 | BaseResp base_resp = 1; 39 | } 40 | 41 | 42 | message publish_list_request { 43 | int64 user_id = 1;//用户id 44 | string token = 2;//用户鉴权token 45 | } 46 | 47 | message publish_list_response { 48 | BaseResp base_resp = 1; 49 | repeated Video video_list = 2;//用户发布的视频列表 50 | } 51 | 52 | service PublishService { 53 | rpc PublishAction (publish_action_request) returns (publish_action_response) {} 54 | rpc PublishList (publish_list_request) returns (publish_list_response) {} 55 | } -------------------------------------------------------------------------------- /idl/relation.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package relation; 3 | option go_package = "relation"; 4 | 5 | message BaseResp { 6 | int32 status_code = 1;//状态码 7 | string status_message = 2;//状态描述 8 | int64 service_time = 3;//服务时间 9 | } 10 | 11 | message User { 12 | int64 id = 1;//用户id 13 | string name = 2;//用户名称 14 | int64 follow_count = 3;//关注总数 15 | int64 follower_count = 4;//粉丝总数 16 | bool is_follow = 5;//true-已关注,false-未关注 17 | } 18 | 19 | 20 | message relation_action_request { 21 | string token = 1;//用户鉴权token 22 | int64 to_user_id = 2;//对方用户id 23 | int32 action_type = 3;//1-关注,2-取消关注 24 | } 25 | 26 | message relation_action_response { 27 | BaseResp base_resp = 1; 28 | } 29 | 30 | message follow_list_request { 31 | int64 user_id = 1;//用户id 32 | string token = 2;//用户鉴权token 33 | } 34 | 35 | message follow_list_response { 36 | BaseResp base_resp = 1; 37 | repeated User user_list = 2;//用户信息列表 38 | } 39 | 40 | message follower_list_request { 41 | int64 user_id = 1;//用户id 42 | string token = 2;//用户鉴权token 43 | } 44 | 45 | message follower_list_response { 46 | BaseResp base_resp = 1; 47 | repeated User user_list = 2;//用户列表 48 | } 49 | 50 | service RelationService { 51 | rpc RelationAction (relation_action_request) returns (relation_action_response) {} 52 | rpc FollowList (follow_list_request) returns (follow_list_response) {} 53 | rpc FollowerList (follower_list_request) returns (follower_list_response ) {} 54 | } -------------------------------------------------------------------------------- /idl/user.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package user; 3 | option go_package = "user"; 4 | 5 | message BaseResp { 6 | int32 status_code = 1;//状态码 7 | string status_message = 2;//状态描述 8 | int64 service_time = 3;//服务时间 9 | } 10 | 11 | message User { 12 | int64 id = 1;//用户id 13 | string name = 2;//用户名称 14 | int64 follow_count = 3;//关注总数 15 | int64 follower_count = 4;//粉丝总数 16 | bool is_follow = 5;//true-已关注,false-未关注 17 | } 18 | 19 | message check_user_request { 20 | string username = 1; //登录用户名 21 | string password = 2;//登录密码 22 | } 23 | 24 | message check_user_response { 25 | BaseResp base_resp = 1; 26 | int64 user_id = 2;//用户id 27 | } 28 | 29 | message register_user_request { 30 | string username = 1;//注册用户名,最长32个字符 31 | string password = 2;//密码,最长32个字符 32 | } 33 | 34 | message register_user_response { 35 | BaseResp base_resp = 1; 36 | int64 user_id = 2;//用户id 37 | string token = 3;//用户鉴权token 38 | } 39 | 40 | 41 | message user_info_request { 42 | int64 user_id = 1;//用户id 43 | string token = 2;//用户鉴权token 44 | } 45 | 46 | message user_info_response { 47 | BaseResp base_resp = 1; 48 | User user = 2;//用户信息 49 | } 50 | 51 | service UserService { 52 | rpc CheckUser (check_user_request) returns (check_user_response) {} 53 | rpc RegisterUser (register_user_request) returns (register_user_response) {} 54 | rpc UserInfo (user_info_request) returns (user_info_response) {} 55 | } 56 | -------------------------------------------------------------------------------- /kitex_gen/comment/commentservice/client.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.3.2. DO NOT EDIT. 2 | 3 | package commentservice 4 | 5 | import ( 6 | "context" 7 | "github.com/chenmengangzhi29/douyin/kitex_gen/comment" 8 | "github.com/cloudwego/kitex/client" 9 | "github.com/cloudwego/kitex/client/callopt" 10 | ) 11 | 12 | // Client is designed to provide IDL-compatible methods with call-option parameter for kitex framework. 13 | type Client interface { 14 | CreateComment(ctx context.Context, Req *comment.CreateCommentRequest, callOptions ...callopt.Option) (r *comment.CreateCommentResponse, err error) 15 | DeleteComment(ctx context.Context, Req *comment.DeleteCommentRequest, callOptions ...callopt.Option) (r *comment.DeleteCommentResponse, err error) 16 | CommentList(ctx context.Context, Req *comment.CommentListRequest, callOptions ...callopt.Option) (r *comment.CommentListResponse, err error) 17 | } 18 | 19 | // NewClient creates a client for the service defined in IDL. 20 | func NewClient(destService string, opts ...client.Option) (Client, error) { 21 | var options []client.Option 22 | options = append(options, client.WithDestService(destService)) 23 | 24 | options = append(options, opts...) 25 | 26 | kc, err := client.NewClient(serviceInfo(), options...) 27 | if err != nil { 28 | return nil, err 29 | } 30 | return &kCommentServiceClient{ 31 | kClient: newServiceClient(kc), 32 | }, nil 33 | } 34 | 35 | // MustNewClient creates a client for the service defined in IDL. It panics if any error occurs. 36 | func MustNewClient(destService string, opts ...client.Option) Client { 37 | kc, err := NewClient(destService, opts...) 38 | if err != nil { 39 | panic(err) 40 | } 41 | return kc 42 | } 43 | 44 | type kCommentServiceClient struct { 45 | *kClient 46 | } 47 | 48 | func (p *kCommentServiceClient) CreateComment(ctx context.Context, Req *comment.CreateCommentRequest, callOptions ...callopt.Option) (r *comment.CreateCommentResponse, err error) { 49 | ctx = client.NewCtxWithCallOptions(ctx, callOptions) 50 | return p.kClient.CreateComment(ctx, Req) 51 | } 52 | 53 | func (p *kCommentServiceClient) DeleteComment(ctx context.Context, Req *comment.DeleteCommentRequest, callOptions ...callopt.Option) (r *comment.DeleteCommentResponse, err error) { 54 | ctx = client.NewCtxWithCallOptions(ctx, callOptions) 55 | return p.kClient.DeleteComment(ctx, Req) 56 | } 57 | 58 | func (p *kCommentServiceClient) CommentList(ctx context.Context, Req *comment.CommentListRequest, callOptions ...callopt.Option) (r *comment.CommentListResponse, err error) { 59 | ctx = client.NewCtxWithCallOptions(ctx, callOptions) 60 | return p.kClient.CommentList(ctx, Req) 61 | } 62 | -------------------------------------------------------------------------------- /kitex_gen/comment/commentservice/invoker.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.3.2. DO NOT EDIT. 2 | 3 | package commentservice 4 | 5 | import ( 6 | "github.com/chenmengangzhi29/douyin/kitex_gen/comment" 7 | "github.com/cloudwego/kitex/server" 8 | ) 9 | 10 | // NewInvoker creates a server.Invoker with the given handler and options. 11 | func NewInvoker(handler comment.CommentService, opts ...server.Option) server.Invoker { 12 | var options []server.Option 13 | 14 | options = append(options, opts...) 15 | 16 | s := server.NewInvoker(options...) 17 | if err := s.RegisterService(serviceInfo(), handler); err != nil { 18 | panic(err) 19 | } 20 | if err := s.Init(); err != nil { 21 | panic(err) 22 | } 23 | return s 24 | } 25 | -------------------------------------------------------------------------------- /kitex_gen/comment/commentservice/server.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.3.2. DO NOT EDIT. 2 | package commentservice 3 | 4 | import ( 5 | "github.com/chenmengangzhi29/douyin/kitex_gen/comment" 6 | "github.com/cloudwego/kitex/server" 7 | ) 8 | 9 | // NewServer creates a server.Server with the given handler and options. 10 | func NewServer(handler comment.CommentService, opts ...server.Option) server.Server { 11 | var options []server.Option 12 | 13 | options = append(options, opts...) 14 | 15 | svr := server.NewServer(options...) 16 | if err := svr.RegisterService(serviceInfo(), handler); err != nil { 17 | panic(err) 18 | } 19 | return svr 20 | } 21 | -------------------------------------------------------------------------------- /kitex_gen/favorite/favoriteservice/client.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.3.2. DO NOT EDIT. 2 | 3 | package favoriteservice 4 | 5 | import ( 6 | "context" 7 | "github.com/chenmengangzhi29/douyin/kitex_gen/favorite" 8 | "github.com/cloudwego/kitex/client" 9 | "github.com/cloudwego/kitex/client/callopt" 10 | ) 11 | 12 | // Client is designed to provide IDL-compatible methods with call-option parameter for kitex framework. 13 | type Client interface { 14 | FavoriteAction(ctx context.Context, Req *favorite.FavoriteActionRequest, callOptions ...callopt.Option) (r *favorite.FavoriteActionResponse, err error) 15 | FavoriteList(ctx context.Context, Req *favorite.FavoriteListRequest, callOptions ...callopt.Option) (r *favorite.FavoriteListResponse, err error) 16 | } 17 | 18 | // NewClient creates a client for the service defined in IDL. 19 | func NewClient(destService string, opts ...client.Option) (Client, error) { 20 | var options []client.Option 21 | options = append(options, client.WithDestService(destService)) 22 | 23 | options = append(options, opts...) 24 | 25 | kc, err := client.NewClient(serviceInfo(), options...) 26 | if err != nil { 27 | return nil, err 28 | } 29 | return &kFavoriteServiceClient{ 30 | kClient: newServiceClient(kc), 31 | }, nil 32 | } 33 | 34 | // MustNewClient creates a client for the service defined in IDL. It panics if any error occurs. 35 | func MustNewClient(destService string, opts ...client.Option) Client { 36 | kc, err := NewClient(destService, opts...) 37 | if err != nil { 38 | panic(err) 39 | } 40 | return kc 41 | } 42 | 43 | type kFavoriteServiceClient struct { 44 | *kClient 45 | } 46 | 47 | func (p *kFavoriteServiceClient) FavoriteAction(ctx context.Context, Req *favorite.FavoriteActionRequest, callOptions ...callopt.Option) (r *favorite.FavoriteActionResponse, err error) { 48 | ctx = client.NewCtxWithCallOptions(ctx, callOptions) 49 | return p.kClient.FavoriteAction(ctx, Req) 50 | } 51 | 52 | func (p *kFavoriteServiceClient) FavoriteList(ctx context.Context, Req *favorite.FavoriteListRequest, callOptions ...callopt.Option) (r *favorite.FavoriteListResponse, err error) { 53 | ctx = client.NewCtxWithCallOptions(ctx, callOptions) 54 | return p.kClient.FavoriteList(ctx, Req) 55 | } 56 | -------------------------------------------------------------------------------- /kitex_gen/favorite/favoriteservice/invoker.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.3.2. DO NOT EDIT. 2 | 3 | package favoriteservice 4 | 5 | import ( 6 | "github.com/chenmengangzhi29/douyin/kitex_gen/favorite" 7 | "github.com/cloudwego/kitex/server" 8 | ) 9 | 10 | // NewInvoker creates a server.Invoker with the given handler and options. 11 | func NewInvoker(handler favorite.FavoriteService, opts ...server.Option) server.Invoker { 12 | var options []server.Option 13 | 14 | options = append(options, opts...) 15 | 16 | s := server.NewInvoker(options...) 17 | if err := s.RegisterService(serviceInfo(), handler); err != nil { 18 | panic(err) 19 | } 20 | if err := s.Init(); err != nil { 21 | panic(err) 22 | } 23 | return s 24 | } 25 | -------------------------------------------------------------------------------- /kitex_gen/favorite/favoriteservice/server.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.3.2. DO NOT EDIT. 2 | package favoriteservice 3 | 4 | import ( 5 | "github.com/chenmengangzhi29/douyin/kitex_gen/favorite" 6 | "github.com/cloudwego/kitex/server" 7 | ) 8 | 9 | // NewServer creates a server.Server with the given handler and options. 10 | func NewServer(handler favorite.FavoriteService, opts ...server.Option) server.Server { 11 | var options []server.Option 12 | 13 | options = append(options, opts...) 14 | 15 | svr := server.NewServer(options...) 16 | if err := svr.RegisterService(serviceInfo(), handler); err != nil { 17 | panic(err) 18 | } 19 | return svr 20 | } 21 | -------------------------------------------------------------------------------- /kitex_gen/feed/feedservice/client.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.3.2. DO NOT EDIT. 2 | 3 | package feedservice 4 | 5 | import ( 6 | "context" 7 | "github.com/chenmengangzhi29/douyin/kitex_gen/feed" 8 | "github.com/cloudwego/kitex/client" 9 | "github.com/cloudwego/kitex/client/callopt" 10 | ) 11 | 12 | // Client is designed to provide IDL-compatible methods with call-option parameter for kitex framework. 13 | type Client interface { 14 | Feed(ctx context.Context, Req *feed.FeedRequest, callOptions ...callopt.Option) (r *feed.FeedResponse, err error) 15 | } 16 | 17 | // NewClient creates a client for the service defined in IDL. 18 | func NewClient(destService string, opts ...client.Option) (Client, error) { 19 | var options []client.Option 20 | options = append(options, client.WithDestService(destService)) 21 | 22 | options = append(options, opts...) 23 | 24 | kc, err := client.NewClient(serviceInfo(), options...) 25 | if err != nil { 26 | return nil, err 27 | } 28 | return &kFeedServiceClient{ 29 | kClient: newServiceClient(kc), 30 | }, nil 31 | } 32 | 33 | // MustNewClient creates a client for the service defined in IDL. It panics if any error occurs. 34 | func MustNewClient(destService string, opts ...client.Option) Client { 35 | kc, err := NewClient(destService, opts...) 36 | if err != nil { 37 | panic(err) 38 | } 39 | return kc 40 | } 41 | 42 | type kFeedServiceClient struct { 43 | *kClient 44 | } 45 | 46 | func (p *kFeedServiceClient) Feed(ctx context.Context, Req *feed.FeedRequest, callOptions ...callopt.Option) (r *feed.FeedResponse, err error) { 47 | ctx = client.NewCtxWithCallOptions(ctx, callOptions) 48 | return p.kClient.Feed(ctx, Req) 49 | } 50 | -------------------------------------------------------------------------------- /kitex_gen/feed/feedservice/feedservice.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.3.2. DO NOT EDIT. 2 | 3 | package feedservice 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | "github.com/chenmengangzhi29/douyin/kitex_gen/feed" 9 | "github.com/cloudwego/kitex/client" 10 | kitex "github.com/cloudwego/kitex/pkg/serviceinfo" 11 | "github.com/cloudwego/kitex/pkg/streaming" 12 | "google.golang.org/protobuf/proto" 13 | ) 14 | 15 | func serviceInfo() *kitex.ServiceInfo { 16 | return feedServiceServiceInfo 17 | } 18 | 19 | var feedServiceServiceInfo = NewServiceInfo() 20 | 21 | func NewServiceInfo() *kitex.ServiceInfo { 22 | serviceName := "FeedService" 23 | handlerType := (*feed.FeedService)(nil) 24 | methods := map[string]kitex.MethodInfo{ 25 | "Feed": kitex.NewMethodInfo(feedHandler, newFeedArgs, newFeedResult, false), 26 | } 27 | extra := map[string]interface{}{ 28 | "PackageName": "feed", 29 | } 30 | svcInfo := &kitex.ServiceInfo{ 31 | ServiceName: serviceName, 32 | HandlerType: handlerType, 33 | Methods: methods, 34 | PayloadCodec: kitex.Protobuf, 35 | KiteXGenVersion: "v0.3.2", 36 | Extra: extra, 37 | } 38 | return svcInfo 39 | } 40 | 41 | func feedHandler(ctx context.Context, handler interface{}, arg, result interface{}) error { 42 | switch s := arg.(type) { 43 | case *streaming.Args: 44 | st := s.Stream 45 | req := new(feed.FeedRequest) 46 | if err := st.RecvMsg(req); err != nil { 47 | return err 48 | } 49 | resp, err := handler.(feed.FeedService).Feed(ctx, req) 50 | if err != nil { 51 | return err 52 | } 53 | if err := st.SendMsg(resp); err != nil { 54 | return err 55 | } 56 | case *FeedArgs: 57 | success, err := handler.(feed.FeedService).Feed(ctx, s.Req) 58 | if err != nil { 59 | return err 60 | } 61 | realResult := result.(*FeedResult) 62 | realResult.Success = success 63 | } 64 | return nil 65 | } 66 | func newFeedArgs() interface{} { 67 | return &FeedArgs{} 68 | } 69 | 70 | func newFeedResult() interface{} { 71 | return &FeedResult{} 72 | } 73 | 74 | type FeedArgs struct { 75 | Req *feed.FeedRequest 76 | } 77 | 78 | func (p *FeedArgs) Marshal(out []byte) ([]byte, error) { 79 | if !p.IsSetReq() { 80 | return out, fmt.Errorf("no req in FeedArgs") 81 | } 82 | return proto.Marshal(p.Req) 83 | } 84 | 85 | func (p *FeedArgs) Unmarshal(in []byte) error { 86 | msg := new(feed.FeedRequest) 87 | if err := proto.Unmarshal(in, msg); err != nil { 88 | return err 89 | } 90 | p.Req = msg 91 | return nil 92 | } 93 | 94 | var FeedArgs_Req_DEFAULT *feed.FeedRequest 95 | 96 | func (p *FeedArgs) GetReq() *feed.FeedRequest { 97 | if !p.IsSetReq() { 98 | return FeedArgs_Req_DEFAULT 99 | } 100 | return p.Req 101 | } 102 | 103 | func (p *FeedArgs) IsSetReq() bool { 104 | return p.Req != nil 105 | } 106 | 107 | type FeedResult struct { 108 | Success *feed.FeedResponse 109 | } 110 | 111 | var FeedResult_Success_DEFAULT *feed.FeedResponse 112 | 113 | func (p *FeedResult) Marshal(out []byte) ([]byte, error) { 114 | if !p.IsSetSuccess() { 115 | return out, fmt.Errorf("no req in FeedResult") 116 | } 117 | return proto.Marshal(p.Success) 118 | } 119 | 120 | func (p *FeedResult) Unmarshal(in []byte) error { 121 | msg := new(feed.FeedResponse) 122 | if err := proto.Unmarshal(in, msg); err != nil { 123 | return err 124 | } 125 | p.Success = msg 126 | return nil 127 | } 128 | 129 | func (p *FeedResult) GetSuccess() *feed.FeedResponse { 130 | if !p.IsSetSuccess() { 131 | return FeedResult_Success_DEFAULT 132 | } 133 | return p.Success 134 | } 135 | 136 | func (p *FeedResult) SetSuccess(x interface{}) { 137 | p.Success = x.(*feed.FeedResponse) 138 | } 139 | 140 | func (p *FeedResult) IsSetSuccess() bool { 141 | return p.Success != nil 142 | } 143 | 144 | type kClient struct { 145 | c client.Client 146 | } 147 | 148 | func newServiceClient(c client.Client) *kClient { 149 | return &kClient{ 150 | c: c, 151 | } 152 | } 153 | 154 | func (p *kClient) Feed(ctx context.Context, Req *feed.FeedRequest) (r *feed.FeedResponse, err error) { 155 | var _args FeedArgs 156 | _args.Req = Req 157 | var _result FeedResult 158 | if err = p.c.Call(ctx, "Feed", &_args, &_result); err != nil { 159 | return 160 | } 161 | return _result.GetSuccess(), nil 162 | } 163 | -------------------------------------------------------------------------------- /kitex_gen/feed/feedservice/invoker.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.3.2. DO NOT EDIT. 2 | 3 | package feedservice 4 | 5 | import ( 6 | "github.com/chenmengangzhi29/douyin/kitex_gen/feed" 7 | "github.com/cloudwego/kitex/server" 8 | ) 9 | 10 | // NewInvoker creates a server.Invoker with the given handler and options. 11 | func NewInvoker(handler feed.FeedService, opts ...server.Option) server.Invoker { 12 | var options []server.Option 13 | 14 | options = append(options, opts...) 15 | 16 | s := server.NewInvoker(options...) 17 | if err := s.RegisterService(serviceInfo(), handler); err != nil { 18 | panic(err) 19 | } 20 | if err := s.Init(); err != nil { 21 | panic(err) 22 | } 23 | return s 24 | } 25 | -------------------------------------------------------------------------------- /kitex_gen/feed/feedservice/server.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.3.2. DO NOT EDIT. 2 | package feedservice 3 | 4 | import ( 5 | "github.com/chenmengangzhi29/douyin/kitex_gen/feed" 6 | "github.com/cloudwego/kitex/server" 7 | ) 8 | 9 | // NewServer creates a server.Server with the given handler and options. 10 | func NewServer(handler feed.FeedService, opts ...server.Option) server.Server { 11 | var options []server.Option 12 | 13 | options = append(options, opts...) 14 | 15 | svr := server.NewServer(options...) 16 | if err := svr.RegisterService(serviceInfo(), handler); err != nil { 17 | panic(err) 18 | } 19 | return svr 20 | } 21 | -------------------------------------------------------------------------------- /kitex_gen/publish/publishservice/client.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.3.2. DO NOT EDIT. 2 | 3 | package publishservice 4 | 5 | import ( 6 | "context" 7 | "github.com/chenmengangzhi29/douyin/kitex_gen/publish" 8 | "github.com/cloudwego/kitex/client" 9 | "github.com/cloudwego/kitex/client/callopt" 10 | ) 11 | 12 | // Client is designed to provide IDL-compatible methods with call-option parameter for kitex framework. 13 | type Client interface { 14 | PublishAction(ctx context.Context, Req *publish.PublishActionRequest, callOptions ...callopt.Option) (r *publish.PublishActionResponse, err error) 15 | PublishList(ctx context.Context, Req *publish.PublishListRequest, callOptions ...callopt.Option) (r *publish.PublishListResponse, err error) 16 | } 17 | 18 | // NewClient creates a client for the service defined in IDL. 19 | func NewClient(destService string, opts ...client.Option) (Client, error) { 20 | var options []client.Option 21 | options = append(options, client.WithDestService(destService)) 22 | 23 | options = append(options, opts...) 24 | 25 | kc, err := client.NewClient(serviceInfo(), options...) 26 | if err != nil { 27 | return nil, err 28 | } 29 | return &kPublishServiceClient{ 30 | kClient: newServiceClient(kc), 31 | }, nil 32 | } 33 | 34 | // MustNewClient creates a client for the service defined in IDL. It panics if any error occurs. 35 | func MustNewClient(destService string, opts ...client.Option) Client { 36 | kc, err := NewClient(destService, opts...) 37 | if err != nil { 38 | panic(err) 39 | } 40 | return kc 41 | } 42 | 43 | type kPublishServiceClient struct { 44 | *kClient 45 | } 46 | 47 | func (p *kPublishServiceClient) PublishAction(ctx context.Context, Req *publish.PublishActionRequest, callOptions ...callopt.Option) (r *publish.PublishActionResponse, err error) { 48 | ctx = client.NewCtxWithCallOptions(ctx, callOptions) 49 | return p.kClient.PublishAction(ctx, Req) 50 | } 51 | 52 | func (p *kPublishServiceClient) PublishList(ctx context.Context, Req *publish.PublishListRequest, callOptions ...callopt.Option) (r *publish.PublishListResponse, err error) { 53 | ctx = client.NewCtxWithCallOptions(ctx, callOptions) 54 | return p.kClient.PublishList(ctx, Req) 55 | } 56 | -------------------------------------------------------------------------------- /kitex_gen/publish/publishservice/invoker.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.3.2. DO NOT EDIT. 2 | 3 | package publishservice 4 | 5 | import ( 6 | "github.com/chenmengangzhi29/douyin/kitex_gen/publish" 7 | "github.com/cloudwego/kitex/server" 8 | ) 9 | 10 | // NewInvoker creates a server.Invoker with the given handler and options. 11 | func NewInvoker(handler publish.PublishService, opts ...server.Option) server.Invoker { 12 | var options []server.Option 13 | 14 | options = append(options, opts...) 15 | 16 | s := server.NewInvoker(options...) 17 | if err := s.RegisterService(serviceInfo(), handler); err != nil { 18 | panic(err) 19 | } 20 | if err := s.Init(); err != nil { 21 | panic(err) 22 | } 23 | return s 24 | } 25 | -------------------------------------------------------------------------------- /kitex_gen/publish/publishservice/publishservice.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.3.2. DO NOT EDIT. 2 | 3 | package publishservice 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | "github.com/chenmengangzhi29/douyin/kitex_gen/publish" 9 | "github.com/cloudwego/kitex/client" 10 | kitex "github.com/cloudwego/kitex/pkg/serviceinfo" 11 | "github.com/cloudwego/kitex/pkg/streaming" 12 | "google.golang.org/protobuf/proto" 13 | ) 14 | 15 | func serviceInfo() *kitex.ServiceInfo { 16 | return publishServiceServiceInfo 17 | } 18 | 19 | var publishServiceServiceInfo = NewServiceInfo() 20 | 21 | func NewServiceInfo() *kitex.ServiceInfo { 22 | serviceName := "PublishService" 23 | handlerType := (*publish.PublishService)(nil) 24 | methods := map[string]kitex.MethodInfo{ 25 | "PublishAction": kitex.NewMethodInfo(publishActionHandler, newPublishActionArgs, newPublishActionResult, false), 26 | "PublishList": kitex.NewMethodInfo(publishListHandler, newPublishListArgs, newPublishListResult, false), 27 | } 28 | extra := map[string]interface{}{ 29 | "PackageName": "publish", 30 | } 31 | svcInfo := &kitex.ServiceInfo{ 32 | ServiceName: serviceName, 33 | HandlerType: handlerType, 34 | Methods: methods, 35 | PayloadCodec: kitex.Protobuf, 36 | KiteXGenVersion: "v0.3.2", 37 | Extra: extra, 38 | } 39 | return svcInfo 40 | } 41 | 42 | func publishActionHandler(ctx context.Context, handler interface{}, arg, result interface{}) error { 43 | switch s := arg.(type) { 44 | case *streaming.Args: 45 | st := s.Stream 46 | req := new(publish.PublishActionRequest) 47 | if err := st.RecvMsg(req); err != nil { 48 | return err 49 | } 50 | resp, err := handler.(publish.PublishService).PublishAction(ctx, req) 51 | if err != nil { 52 | return err 53 | } 54 | if err := st.SendMsg(resp); err != nil { 55 | return err 56 | } 57 | case *PublishActionArgs: 58 | success, err := handler.(publish.PublishService).PublishAction(ctx, s.Req) 59 | if err != nil { 60 | return err 61 | } 62 | realResult := result.(*PublishActionResult) 63 | realResult.Success = success 64 | } 65 | return nil 66 | } 67 | func newPublishActionArgs() interface{} { 68 | return &PublishActionArgs{} 69 | } 70 | 71 | func newPublishActionResult() interface{} { 72 | return &PublishActionResult{} 73 | } 74 | 75 | type PublishActionArgs struct { 76 | Req *publish.PublishActionRequest 77 | } 78 | 79 | func (p *PublishActionArgs) Marshal(out []byte) ([]byte, error) { 80 | if !p.IsSetReq() { 81 | return out, fmt.Errorf("No req in PublishActionArgs") 82 | } 83 | return proto.Marshal(p.Req) 84 | } 85 | 86 | func (p *PublishActionArgs) Unmarshal(in []byte) error { 87 | msg := new(publish.PublishActionRequest) 88 | if err := proto.Unmarshal(in, msg); err != nil { 89 | return err 90 | } 91 | p.Req = msg 92 | return nil 93 | } 94 | 95 | var PublishActionArgs_Req_DEFAULT *publish.PublishActionRequest 96 | 97 | func (p *PublishActionArgs) GetReq() *publish.PublishActionRequest { 98 | if !p.IsSetReq() { 99 | return PublishActionArgs_Req_DEFAULT 100 | } 101 | return p.Req 102 | } 103 | 104 | func (p *PublishActionArgs) IsSetReq() bool { 105 | return p.Req != nil 106 | } 107 | 108 | type PublishActionResult struct { 109 | Success *publish.PublishActionResponse 110 | } 111 | 112 | var PublishActionResult_Success_DEFAULT *publish.PublishActionResponse 113 | 114 | func (p *PublishActionResult) Marshal(out []byte) ([]byte, error) { 115 | if !p.IsSetSuccess() { 116 | return out, fmt.Errorf("No req in PublishActionResult") 117 | } 118 | return proto.Marshal(p.Success) 119 | } 120 | 121 | func (p *PublishActionResult) Unmarshal(in []byte) error { 122 | msg := new(publish.PublishActionResponse) 123 | if err := proto.Unmarshal(in, msg); err != nil { 124 | return err 125 | } 126 | p.Success = msg 127 | return nil 128 | } 129 | 130 | func (p *PublishActionResult) GetSuccess() *publish.PublishActionResponse { 131 | if !p.IsSetSuccess() { 132 | return PublishActionResult_Success_DEFAULT 133 | } 134 | return p.Success 135 | } 136 | 137 | func (p *PublishActionResult) SetSuccess(x interface{}) { 138 | p.Success = x.(*publish.PublishActionResponse) 139 | } 140 | 141 | func (p *PublishActionResult) IsSetSuccess() bool { 142 | return p.Success != nil 143 | } 144 | 145 | func publishListHandler(ctx context.Context, handler interface{}, arg, result interface{}) error { 146 | switch s := arg.(type) { 147 | case *streaming.Args: 148 | st := s.Stream 149 | req := new(publish.PublishListRequest) 150 | if err := st.RecvMsg(req); err != nil { 151 | return err 152 | } 153 | resp, err := handler.(publish.PublishService).PublishList(ctx, req) 154 | if err != nil { 155 | return err 156 | } 157 | if err := st.SendMsg(resp); err != nil { 158 | return err 159 | } 160 | case *PublishListArgs: 161 | success, err := handler.(publish.PublishService).PublishList(ctx, s.Req) 162 | if err != nil { 163 | return err 164 | } 165 | realResult := result.(*PublishListResult) 166 | realResult.Success = success 167 | } 168 | return nil 169 | } 170 | func newPublishListArgs() interface{} { 171 | return &PublishListArgs{} 172 | } 173 | 174 | func newPublishListResult() interface{} { 175 | return &PublishListResult{} 176 | } 177 | 178 | type PublishListArgs struct { 179 | Req *publish.PublishListRequest 180 | } 181 | 182 | func (p *PublishListArgs) Marshal(out []byte) ([]byte, error) { 183 | if !p.IsSetReq() { 184 | return out, fmt.Errorf("No req in PublishListArgs") 185 | } 186 | return proto.Marshal(p.Req) 187 | } 188 | 189 | func (p *PublishListArgs) Unmarshal(in []byte) error { 190 | msg := new(publish.PublishListRequest) 191 | if err := proto.Unmarshal(in, msg); err != nil { 192 | return err 193 | } 194 | p.Req = msg 195 | return nil 196 | } 197 | 198 | var PublishListArgs_Req_DEFAULT *publish.PublishListRequest 199 | 200 | func (p *PublishListArgs) GetReq() *publish.PublishListRequest { 201 | if !p.IsSetReq() { 202 | return PublishListArgs_Req_DEFAULT 203 | } 204 | return p.Req 205 | } 206 | 207 | func (p *PublishListArgs) IsSetReq() bool { 208 | return p.Req != nil 209 | } 210 | 211 | type PublishListResult struct { 212 | Success *publish.PublishListResponse 213 | } 214 | 215 | var PublishListResult_Success_DEFAULT *publish.PublishListResponse 216 | 217 | func (p *PublishListResult) Marshal(out []byte) ([]byte, error) { 218 | if !p.IsSetSuccess() { 219 | return out, fmt.Errorf("No req in PublishListResult") 220 | } 221 | return proto.Marshal(p.Success) 222 | } 223 | 224 | func (p *PublishListResult) Unmarshal(in []byte) error { 225 | msg := new(publish.PublishListResponse) 226 | if err := proto.Unmarshal(in, msg); err != nil { 227 | return err 228 | } 229 | p.Success = msg 230 | return nil 231 | } 232 | 233 | func (p *PublishListResult) GetSuccess() *publish.PublishListResponse { 234 | if !p.IsSetSuccess() { 235 | return PublishListResult_Success_DEFAULT 236 | } 237 | return p.Success 238 | } 239 | 240 | func (p *PublishListResult) SetSuccess(x interface{}) { 241 | p.Success = x.(*publish.PublishListResponse) 242 | } 243 | 244 | func (p *PublishListResult) IsSetSuccess() bool { 245 | return p.Success != nil 246 | } 247 | 248 | type kClient struct { 249 | c client.Client 250 | } 251 | 252 | func newServiceClient(c client.Client) *kClient { 253 | return &kClient{ 254 | c: c, 255 | } 256 | } 257 | 258 | func (p *kClient) PublishAction(ctx context.Context, Req *publish.PublishActionRequest) (r *publish.PublishActionResponse, err error) { 259 | var _args PublishActionArgs 260 | _args.Req = Req 261 | var _result PublishActionResult 262 | if err = p.c.Call(ctx, "PublishAction", &_args, &_result); err != nil { 263 | return 264 | } 265 | return _result.GetSuccess(), nil 266 | } 267 | 268 | func (p *kClient) PublishList(ctx context.Context, Req *publish.PublishListRequest) (r *publish.PublishListResponse, err error) { 269 | var _args PublishListArgs 270 | _args.Req = Req 271 | var _result PublishListResult 272 | if err = p.c.Call(ctx, "PublishList", &_args, &_result); err != nil { 273 | return 274 | } 275 | return _result.GetSuccess(), nil 276 | } 277 | -------------------------------------------------------------------------------- /kitex_gen/publish/publishservice/server.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.3.2. DO NOT EDIT. 2 | package publishservice 3 | 4 | import ( 5 | "github.com/chenmengangzhi29/douyin/kitex_gen/publish" 6 | "github.com/cloudwego/kitex/server" 7 | ) 8 | 9 | // NewServer creates a server.Server with the given handler and options. 10 | func NewServer(handler publish.PublishService, opts ...server.Option) server.Server { 11 | var options []server.Option 12 | 13 | options = append(options, opts...) 14 | 15 | svr := server.NewServer(options...) 16 | if err := svr.RegisterService(serviceInfo(), handler); err != nil { 17 | panic(err) 18 | } 19 | return svr 20 | } 21 | -------------------------------------------------------------------------------- /kitex_gen/relation/relationservice/client.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.3.2. DO NOT EDIT. 2 | 3 | package relationservice 4 | 5 | import ( 6 | "context" 7 | "github.com/chenmengangzhi29/douyin/kitex_gen/relation" 8 | "github.com/cloudwego/kitex/client" 9 | "github.com/cloudwego/kitex/client/callopt" 10 | ) 11 | 12 | // Client is designed to provide IDL-compatible methods with call-option parameter for kitex framework. 13 | type Client interface { 14 | RelationAction(ctx context.Context, Req *relation.RelationActionRequest, callOptions ...callopt.Option) (r *relation.RelationActionResponse, err error) 15 | FollowList(ctx context.Context, Req *relation.FollowListRequest, callOptions ...callopt.Option) (r *relation.FollowListResponse, err error) 16 | FollowerList(ctx context.Context, Req *relation.FollowerListRequest, callOptions ...callopt.Option) (r *relation.FollowerListResponse, err error) 17 | } 18 | 19 | // NewClient creates a client for the service defined in IDL. 20 | func NewClient(destService string, opts ...client.Option) (Client, error) { 21 | var options []client.Option 22 | options = append(options, client.WithDestService(destService)) 23 | 24 | options = append(options, opts...) 25 | 26 | kc, err := client.NewClient(serviceInfo(), options...) 27 | if err != nil { 28 | return nil, err 29 | } 30 | return &kRelationServiceClient{ 31 | kClient: newServiceClient(kc), 32 | }, nil 33 | } 34 | 35 | // MustNewClient creates a client for the service defined in IDL. It panics if any error occurs. 36 | func MustNewClient(destService string, opts ...client.Option) Client { 37 | kc, err := NewClient(destService, opts...) 38 | if err != nil { 39 | panic(err) 40 | } 41 | return kc 42 | } 43 | 44 | type kRelationServiceClient struct { 45 | *kClient 46 | } 47 | 48 | func (p *kRelationServiceClient) RelationAction(ctx context.Context, Req *relation.RelationActionRequest, callOptions ...callopt.Option) (r *relation.RelationActionResponse, err error) { 49 | ctx = client.NewCtxWithCallOptions(ctx, callOptions) 50 | return p.kClient.RelationAction(ctx, Req) 51 | } 52 | 53 | func (p *kRelationServiceClient) FollowList(ctx context.Context, Req *relation.FollowListRequest, callOptions ...callopt.Option) (r *relation.FollowListResponse, err error) { 54 | ctx = client.NewCtxWithCallOptions(ctx, callOptions) 55 | return p.kClient.FollowList(ctx, Req) 56 | } 57 | 58 | func (p *kRelationServiceClient) FollowerList(ctx context.Context, Req *relation.FollowerListRequest, callOptions ...callopt.Option) (r *relation.FollowerListResponse, err error) { 59 | ctx = client.NewCtxWithCallOptions(ctx, callOptions) 60 | return p.kClient.FollowerList(ctx, Req) 61 | } 62 | -------------------------------------------------------------------------------- /kitex_gen/relation/relationservice/invoker.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.3.2. DO NOT EDIT. 2 | 3 | package relationservice 4 | 5 | import ( 6 | "github.com/chenmengangzhi29/douyin/kitex_gen/relation" 7 | "github.com/cloudwego/kitex/server" 8 | ) 9 | 10 | // NewInvoker creates a server.Invoker with the given handler and options. 11 | func NewInvoker(handler relation.RelationService, opts ...server.Option) server.Invoker { 12 | var options []server.Option 13 | 14 | options = append(options, opts...) 15 | 16 | s := server.NewInvoker(options...) 17 | if err := s.RegisterService(serviceInfo(), handler); err != nil { 18 | panic(err) 19 | } 20 | if err := s.Init(); err != nil { 21 | panic(err) 22 | } 23 | return s 24 | } 25 | -------------------------------------------------------------------------------- /kitex_gen/relation/relationservice/server.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.3.2. DO NOT EDIT. 2 | package relationservice 3 | 4 | import ( 5 | "github.com/chenmengangzhi29/douyin/kitex_gen/relation" 6 | "github.com/cloudwego/kitex/server" 7 | ) 8 | 9 | // NewServer creates a server.Server with the given handler and options. 10 | func NewServer(handler relation.RelationService, opts ...server.Option) server.Server { 11 | var options []server.Option 12 | 13 | options = append(options, opts...) 14 | 15 | svr := server.NewServer(options...) 16 | if err := svr.RegisterService(serviceInfo(), handler); err != nil { 17 | panic(err) 18 | } 19 | return svr 20 | } 21 | -------------------------------------------------------------------------------- /kitex_gen/user/userservice/client.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.3.2. DO NOT EDIT. 2 | 3 | package userservice 4 | 5 | import ( 6 | "context" 7 | "github.com/chenmengangzhi29/douyin/kitex_gen/user" 8 | "github.com/cloudwego/kitex/client" 9 | "github.com/cloudwego/kitex/client/callopt" 10 | ) 11 | 12 | // Client is designed to provide IDL-compatible methods with call-option parameter for kitex framework. 13 | type Client interface { 14 | CheckUser(ctx context.Context, Req *user.CheckUserRequest, callOptions ...callopt.Option) (r *user.CheckUserResponse, err error) 15 | RegisterUser(ctx context.Context, Req *user.RegisterUserRequest, callOptions ...callopt.Option) (r *user.RegisterUserResponse, err error) 16 | UserInfo(ctx context.Context, Req *user.UserInfoRequest, callOptions ...callopt.Option) (r *user.UserInfoResponse, err error) 17 | } 18 | 19 | // NewClient creates a client for the service defined in IDL. 20 | func NewClient(destService string, opts ...client.Option) (Client, error) { 21 | var options []client.Option 22 | options = append(options, client.WithDestService(destService)) 23 | 24 | options = append(options, opts...) 25 | 26 | kc, err := client.NewClient(serviceInfo(), options...) 27 | if err != nil { 28 | return nil, err 29 | } 30 | return &kUserServiceClient{ 31 | kClient: newServiceClient(kc), 32 | }, nil 33 | } 34 | 35 | // MustNewClient creates a client for the service defined in IDL. It panics if any error occurs. 36 | func MustNewClient(destService string, opts ...client.Option) Client { 37 | kc, err := NewClient(destService, opts...) 38 | if err != nil { 39 | panic(err) 40 | } 41 | return kc 42 | } 43 | 44 | type kUserServiceClient struct { 45 | *kClient 46 | } 47 | 48 | func (p *kUserServiceClient) CheckUser(ctx context.Context, Req *user.CheckUserRequest, callOptions ...callopt.Option) (r *user.CheckUserResponse, err error) { 49 | ctx = client.NewCtxWithCallOptions(ctx, callOptions) 50 | return p.kClient.CheckUser(ctx, Req) 51 | } 52 | 53 | func (p *kUserServiceClient) RegisterUser(ctx context.Context, Req *user.RegisterUserRequest, callOptions ...callopt.Option) (r *user.RegisterUserResponse, err error) { 54 | ctx = client.NewCtxWithCallOptions(ctx, callOptions) 55 | return p.kClient.RegisterUser(ctx, Req) 56 | } 57 | 58 | func (p *kUserServiceClient) UserInfo(ctx context.Context, Req *user.UserInfoRequest, callOptions ...callopt.Option) (r *user.UserInfoResponse, err error) { 59 | ctx = client.NewCtxWithCallOptions(ctx, callOptions) 60 | return p.kClient.UserInfo(ctx, Req) 61 | } 62 | -------------------------------------------------------------------------------- /kitex_gen/user/userservice/invoker.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.3.2. DO NOT EDIT. 2 | 3 | package userservice 4 | 5 | import ( 6 | "github.com/chenmengangzhi29/douyin/kitex_gen/user" 7 | "github.com/cloudwego/kitex/server" 8 | ) 9 | 10 | // NewInvoker creates a server.Invoker with the given handler and options. 11 | func NewInvoker(handler user.UserService, opts ...server.Option) server.Invoker { 12 | var options []server.Option 13 | 14 | options = append(options, opts...) 15 | 16 | s := server.NewInvoker(options...) 17 | if err := s.RegisterService(serviceInfo(), handler); err != nil { 18 | panic(err) 19 | } 20 | if err := s.Init(); err != nil { 21 | panic(err) 22 | } 23 | return s 24 | } 25 | -------------------------------------------------------------------------------- /kitex_gen/user/userservice/server.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.3.2. DO NOT EDIT. 2 | package userservice 3 | 4 | import ( 5 | "github.com/chenmengangzhi29/douyin/kitex_gen/user" 6 | "github.com/cloudwego/kitex/server" 7 | ) 8 | 9 | // NewServer creates a server.Server with the given handler and options. 10 | func NewServer(handler user.UserService, opts ...server.Option) server.Server { 11 | var options []server.Option 12 | 13 | options = append(options, opts...) 14 | 15 | svr := server.NewServer(options...) 16 | if err := svr.RegisterService(serviceInfo(), handler); err != nil { 17 | panic(err) 18 | } 19 | return svr 20 | } 21 | -------------------------------------------------------------------------------- /pkg/bound/cpu.go: -------------------------------------------------------------------------------- 1 | package bound 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net" 7 | 8 | "github.com/chenmengangzhi29/douyin/pkg/constants" 9 | "github.com/chenmengangzhi29/douyin/pkg/errno" 10 | "github.com/cloudwego/kitex/pkg/klog" 11 | "github.com/cloudwego/kitex/pkg/remote" 12 | "github.com/shirou/gopsutil/cpu" 13 | ) 14 | 15 | var _ remote.InboundHandler = &cpuLimitHandler{} 16 | 17 | type cpuLimitHandler struct{} 18 | 19 | func NewCpuLimitHandler() remote.InboundHandler { 20 | return &cpuLimitHandler{} 21 | } 22 | 23 | // OnActive implements the remote.InboundHandler interface. 24 | func (c *cpuLimitHandler) OnActive(ctx context.Context, conn net.Conn) (context.Context, error) { 25 | return ctx, nil 26 | } 27 | 28 | // OnRead implements the remote.InboundHandler interface. 29 | func (c *cpuLimitHandler) OnRead(ctx context.Context, conn net.Conn) (context.Context, error) { 30 | p := cpuPercent() 31 | klog.CtxInfof(ctx, "current cpu is %.2g", p) 32 | if p > constants.CPURateLimit { 33 | return ctx, errno.ServiceErr.WithMessage(fmt.Sprintf("cpu = %.2g", c)) 34 | } 35 | return ctx, nil 36 | } 37 | 38 | //OnInactive implements the remote.InboundHandler interface. 39 | func (c *cpuLimitHandler) OnInactive(ctx context.Context, conn net.Conn) context.Context { 40 | return ctx 41 | } 42 | 43 | // OnMessage implements the remote.InboundHandler interface. 44 | func (c *cpuLimitHandler) OnMessage(ctx context.Context, args, result remote.Message) (context.Context, error) { 45 | return ctx, nil 46 | } 47 | 48 | func cpuPercent() float64 { 49 | percent, _ := cpu.Percent(0, false) 50 | return percent[0] 51 | } 52 | -------------------------------------------------------------------------------- /pkg/constants/constant.go: -------------------------------------------------------------------------------- 1 | package constants 2 | 3 | import "time" 4 | 5 | const ( 6 | //地址配置 7 | MySQLDefaultDSN = "gorm:gorm@tcp(localhost:9910)/gorm?charset=utf8&parseTime=True&loc=Local" //MySQL DSN 8 | EtcdAddress = "127.0.0.1:2379" //Etcd 地址 9 | ApiAddress = ":8080" //Api层 地址 10 | FeedAddress = "127.0.0.1:8081" //Feed 服务地址 11 | PublishAddress = "127.0.0.1:8082" //Publish 服务地址 12 | UserAddress = "127.0.0.1:8083" //User服务地址 13 | FavoriteAddress = "127.0.0.1:8084" //Favorite服务地址 14 | CommentAddress = "127.0.0.1:8085" //Comment服务地址 15 | RelationAddress = "127.0.0.1:8086" //Relation服务地址 16 | OssEndPoint = "oss-cn-shenzhen.aliyuncs.com" //Oss 17 | OssAccessKeyId = "oss" 18 | OssAccessKeySecret = "oss" 19 | OssBucket = "dousheng1" 20 | 21 | //数据库表名 22 | VideoTableName = "video" 23 | UserTableName = "user" 24 | FavoriteTableName = "favorite" 25 | CommentTableName = "comment" 26 | ReltaionTableName = "relation" 27 | 28 | //jwt 29 | SecretKey = "secret key" 30 | IdentiryKey = "id" 31 | 32 | //时间字段格式 33 | TimeFormat = "2006-01-02 15:04:05" 34 | 35 | //favorite actiontype,1是点赞,2是取消点赞 36 | Like = 1 37 | Unlike = 2 38 | //comment actiontype,1是增加评论,2是删除评论 39 | AddComment = 1 40 | DelComment = 2 41 | //relation actiontypr,1是关注,2是取消关注 42 | Follow = 1 43 | UnFollow = 2 44 | 45 | //rpc服务名 46 | ApiServiceName = "api" 47 | FeedServiceName = "feed" 48 | PublishServiceName = "publish" 49 | UserServiceName = "user" 50 | FavoriteServiceName = "favorite" 51 | CommentServiceName = "comment" 52 | RelationServiceName = "relation" 53 | 54 | //Limit 55 | CPURateLimit = 80.0 56 | DefaultLimit = 10 57 | 58 | //MySQL配置 59 | MySQLMaxIdleConns = 10 //空闲连接池中连接的最大数量 60 | MySQLMaxOpenConns = 100 //打开数据库连接的最大数量 61 | MySQLConnMaxLifetime = time.Hour //连接可复用的最大时间 62 | 63 | ) 64 | -------------------------------------------------------------------------------- /pkg/errno/code.go: -------------------------------------------------------------------------------- 1 | package errno 2 | 3 | const ( 4 | SuccessCode = 0 5 | //service error 6 | ServiceErrCode = 10001 7 | ParamParseErrCode = 10002 8 | //General incoming parameter error 9 | ParamErrCode = 10101 10 | //User-related incoming parameter error 11 | LoginErrCode = 10202 12 | UserNotExistErrCode = 10203 13 | UserAlreadyExistErrCode = 10204 14 | TokenExpiredErrCode = 10205 15 | TokenValidationErrCode = 10206 16 | TokenInvalidErrCode = 10207 17 | UserNameValidationErrCode = 10208 18 | PasswordValidationErrCode = 10209 19 | //Video-related incoming parameter error 20 | VideoDataGetErrCode = 10301 21 | VideoDataCopyErrCode = 10302 22 | //Comment-related incoming parameter error 23 | CommentTextErrCode = 10401 24 | //Relation-related incoming parameter error 25 | ActionTypeErrCode = 10501 26 | ) 27 | 28 | var ( 29 | Success = NewErrNo(SuccessCode, "Success") 30 | //service error 31 | ServiceErr = NewErrNo(ServiceErrCode, "Service is unable to start successfully") 32 | ParamParseErr = NewErrNo(ParamParseErrCode, "Could not parse the param") 33 | //General incoming parameter error 34 | ParamErr = NewErrNo(ParamErrCode, "Wrong Parameter has been given") 35 | //User-related incoming parameter error 36 | LoginErr = NewErrNo(LoginErrCode, "Wrong username or password") 37 | UserNotExistErr = NewErrNo(UserNotExistErrCode, "User does not exists") 38 | UserAlreadyExistErr = NewErrNo(UserAlreadyExistErrCode, "User already exists") 39 | TokenExpiredErr = NewErrNo(TokenExpiredErrCode, "Token has been expired") 40 | TokenValidationErr = NewErrNo(TokenInvalidErrCode, "Token is not active yet") 41 | TokenInvalidErr = NewErrNo(TokenInvalidErrCode, "Token Invalid") 42 | UserNameValidationErr = NewErrNo(UserNameValidationErrCode, "Username is invalid") 43 | PasswordValidationErr = NewErrNo(PasswordValidationErrCode, "Password is invalid") 44 | //Video-related incoming parameter error 45 | VideoDataGetErr = NewErrNo(VideoDataGetErrCode, "Could not get video data") 46 | VideoDataCopyErr = NewErrNo(VideoDataCopyErrCode, "Could not copy video data") 47 | //Comment-related incoming parameter error 48 | CommentTextErr = NewErrNo(CommentTextErrCode, "Comment text too long") 49 | //Relation-related incoming parameter error 50 | ActionTypeErr = NewErrNo(ActionTypeErrCode, "Action type is invalid") 51 | ) 52 | -------------------------------------------------------------------------------- /pkg/errno/errno.go: -------------------------------------------------------------------------------- 1 | package errno 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | type ErrNo struct { 9 | ErrCode int32 10 | ErrMsg string 11 | } 12 | 13 | func (e ErrNo) Error() string { 14 | return fmt.Sprintf("err_code=%d, err_msg=%s", e.ErrCode, e.ErrMsg) 15 | } 16 | 17 | func NewErrNo(code int32, msg string) ErrNo { 18 | return ErrNo{code, msg} 19 | } 20 | 21 | func (e ErrNo) WithMessage(msg string) ErrNo { 22 | e.ErrMsg = msg 23 | return e 24 | } 25 | 26 | // ConvertErr convert error to Errno 27 | func ConvertErr(err error) ErrNo { 28 | Err := ErrNo{} 29 | if errors.As(err, &Err) { 30 | return Err 31 | } 32 | 33 | s := ServiceErr 34 | s.ErrMsg = err.Error() 35 | return s 36 | } 37 | -------------------------------------------------------------------------------- /pkg/errno/errors.go: -------------------------------------------------------------------------------- 1 | package errno 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | ) 7 | 8 | type stack []uintptr 9 | 10 | func callers() *stack { 11 | const depth = 32 12 | var pcs [depth]uintptr 13 | n := runtime.Callers(3, pcs[:]) 14 | var st stack = pcs[0:n] 15 | return &st 16 | } 17 | 18 | type withCode struct { 19 | err error 20 | code int 21 | cause error 22 | msg string 23 | *stack 24 | } 25 | 26 | func WithCode(code int, format string, args ...interface{}) error { 27 | return &withCode{ 28 | err: fmt.Errorf(format, args...), 29 | code: code, 30 | msg: format, 31 | stack: callers(), 32 | } 33 | } 34 | 35 | func WrapC(err error, code int, format string, args ...interface{}) error { 36 | if err == nil { 37 | return nil 38 | } 39 | 40 | return &withCode{ 41 | err: fmt.Errorf(format, args...), 42 | code: code, 43 | cause: err, 44 | msg: format, 45 | stack: callers(), 46 | } 47 | } 48 | 49 | func (w *withCode) Error() string { return w.msg } 50 | 51 | func (w *withCode) Cause() error { return w.cause } 52 | 53 | func (w *withCode) Unwrap() error { return w.cause } 54 | -------------------------------------------------------------------------------- /pkg/jwt/jwt.go: -------------------------------------------------------------------------------- 1 | package jwt 2 | 3 | import ( 4 | "github.com/chenmengangzhi29/douyin/pkg/errno" 5 | "github.com/golang-jwt/jwt" 6 | ) 7 | 8 | //JWT signing Key 9 | type JWT struct { 10 | SigningKey []byte 11 | } 12 | 13 | var ( 14 | ErrTokenExpired = errno.WithCode(errno.TokenExpiredErrCode, "Token expired") 15 | ErrTokenNotValidYet = errno.WithCode(errno.TokenValidationErrCode, "Token is not active yet") 16 | ErrTokenMalformed = errno.WithCode(errno.TokenInvalidErrCode, "That's not even a token") 17 | ErrTokenInvalid = errno.WithCode(errno.TokenInvalidErrCode, "Couldn't handle this token") 18 | ) 19 | 20 | //Structured version of Claims Section, as referenced at https://tools.ietf.org/html/rfc7519#section-4.1 See examples for how to use this with your own claim types 21 | type CustomClaims struct { 22 | Id int64 23 | AuthorityId int64 24 | jwt.StandardClaims 25 | } 26 | 27 | func NewJWT(SigningKey []byte) *JWT { 28 | return &JWT{ 29 | SigningKey, 30 | } 31 | } 32 | 33 | //CreateToken create a new token 34 | func (j *JWT) CreateToken(claims CustomClaims) (string, error) { 35 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 36 | return token.SignedString(j.SigningKey) 37 | } 38 | 39 | //ParseToken parses the token 40 | func (j *JWT) ParseToken(tokenString string) (*CustomClaims, error) { 41 | token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(t *jwt.Token) (interface{}, error) { 42 | return j.SigningKey, nil 43 | }) 44 | if err != nil { 45 | if ve, ok := err.(*jwt.ValidationError); ok { 46 | if ve.Errors&jwt.ValidationErrorMalformed != 0 { 47 | return nil, ErrTokenMalformed 48 | } else if ve.Errors&jwt.ValidationErrorExpired != 0 { 49 | return nil, ErrTokenExpired 50 | } else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 { 51 | return nil, ErrTokenNotValidYet 52 | } else { 53 | return nil, ErrTokenInvalid 54 | } 55 | } 56 | } 57 | if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid { 58 | return claims, nil 59 | } 60 | return nil, ErrTokenInvalid 61 | } 62 | 63 | //checkToken get userId by token 64 | func (j *JWT) CheckToken(token string) (int64, error) { 65 | if token == "" { 66 | return -1, nil 67 | } 68 | claim, err := j.ParseToken(token) 69 | if err != nil { 70 | return -1, ErrTokenInvalid 71 | } 72 | return claim.Id, nil 73 | } 74 | -------------------------------------------------------------------------------- /pkg/jwt/jwt_test.go: -------------------------------------------------------------------------------- 1 | package jwt 2 | 3 | import "testing" 4 | 5 | func TestNewJWT(t *testing.T) { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /pkg/middleware/client.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cloudwego/kitex/pkg/endpoint" 7 | "github.com/cloudwego/kitex/pkg/klog" 8 | "github.com/cloudwego/kitex/pkg/rpcinfo" 9 | ) 10 | 11 | var _ endpoint.Middleware = ClientMiddleware 12 | 13 | //ClientMiddleware client middleware print server address, rpc timeout and connection timeout 14 | func ClientMiddleware(next endpoint.Endpoint) endpoint.Endpoint { 15 | return func(ctx context.Context, req, resp interface{}) (err error) { 16 | ri := rpcinfo.GetRPCInfo(ctx) 17 | klog.Infof("server address: %v, rpc timeout: %v, readwrite timeout: %v\n", ri.To().Address(), ri.Config().RPCTimeout(), ri.Config().ConnectTimeout()) 18 | if err = next(ctx, req, resp); err != nil { 19 | return err 20 | } 21 | return nil 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /pkg/middleware/common.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cloudwego/kitex/pkg/endpoint" 7 | "github.com/cloudwego/kitex/pkg/klog" 8 | "github.com/cloudwego/kitex/pkg/rpcinfo" 9 | ) 10 | 11 | var _ endpoint.Middleware = CommonMiddleware 12 | 13 | //CommonMiddleware common middleware print some rpc info, real request and real response 14 | func CommonMiddleware(next endpoint.Endpoint) endpoint.Endpoint { 15 | return func(ctx context.Context, req, resp interface{}) (err error) { 16 | ri := rpcinfo.GetRPCInfo(ctx) 17 | //get real request 18 | klog.Infof("real request: %+v\n", req) 19 | //get remote service information 20 | klog.Infof("remote service name: %s, remote method: %s\n", ri.To().ServiceName(), ri.To().Method()) 21 | if err = next(ctx, req, resp); err != nil { 22 | return err 23 | } 24 | //get real response 25 | klog.Infof("real response: %+v\n", resp) 26 | return nil 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /pkg/middleware/server.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cloudwego/kitex/pkg/endpoint" 7 | "github.com/cloudwego/kitex/pkg/klog" 8 | "github.com/cloudwego/kitex/pkg/rpcinfo" 9 | ) 10 | 11 | var _ endpoint.Middleware = ServerMiddleware 12 | 13 | //ServerMiddleware server middleware print client address 14 | func ServerMiddleware(next endpoint.Endpoint) endpoint.Endpoint { 15 | return func(ctx context.Context, req, resp interface{}) (err error) { 16 | ri := rpcinfo.GetRPCInfo(ctx) 17 | //get client information 18 | klog.Infof("client address: %v\n", ri.From().Address()) 19 | if err = next(ctx, req, resp); err != nil { 20 | return err 21 | } 22 | return nil 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /pkg/oss/init.go: -------------------------------------------------------------------------------- 1 | package oss 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | 7 | "github.com/aliyun/aliyun-oss-go-sdk/oss" 8 | "github.com/chenmengangzhi29/douyin/pkg/constants" 9 | ) 10 | 11 | var Bucket *oss.Bucket 12 | var Path string 13 | 14 | func Init() { 15 | //算出绝对路径,防止service层测试时路径错误 16 | dir, err := os.Getwd() 17 | if err != nil { 18 | panic(err) 19 | } 20 | Path = strings.Split(dir, "/cmd")[0] 21 | //打开oss的Bucket 22 | endpoint := constants.OssEndPoint 23 | accesskeyid := constants.OssAccessKeyId 24 | accessKeySecret := constants.OssAccessKeySecret 25 | bucket := constants.OssBucket 26 | client, err := oss.New(endpoint, accesskeyid, accessKeySecret) 27 | if err != nil { 28 | panic(err) 29 | } 30 | Bucket, err = client.Bucket(bucket) 31 | if err != nil { 32 | panic(err) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /pkg/oss/utils.go: -------------------------------------------------------------------------------- 1 | package oss 2 | 3 | import ( 4 | "bytes" 5 | "os" 6 | 7 | "github.com/aliyun/aliyun-oss-go-sdk/oss" 8 | "github.com/cloudwego/kitex/pkg/klog" 9 | ) 10 | 11 | //将视频保存到本地文件夹中 12 | func PublishVideoToPublic(video []byte, filePath string) error { 13 | file, err := os.Create(filePath) 14 | if err != nil { 15 | klog.Errorf("create %v fail, %v", filePath, err.Error()) 16 | return err 17 | } 18 | defer file.Close() 19 | _, err = file.Write(video) 20 | if err != nil { 21 | klog.Errorf("write file fail, %v", err.Error()) 22 | return err 23 | } 24 | return nil 25 | } 26 | 27 | //分片将视频上传到Oss 28 | func PublishVideoToOss(objectKey string, filePath string) error { 29 | err := Bucket.UploadFile(objectKey, filePath, 1024*1024, oss.Routines(3)) 30 | if err != nil { 31 | klog.Errorf("publish %v to Oss fail, %v ", filePath, err.Error()) 32 | return err 33 | } 34 | return nil 35 | } 36 | 37 | //从oss上获取播放地址 38 | func QueryOssVideoURL(objectKey string) (string, error) { 39 | signedURL, err := Bucket.SignURL(objectKey, oss.HTTPPut, 60) 40 | if err != nil { 41 | klog.Errorf("Query %v Video URL fail, %v", objectKey, err.Error()) 42 | return "", err 43 | } 44 | return signedURL, nil 45 | } 46 | 47 | //上传封面到Oss 48 | func PublishCoverToOss(objectKey string, coverReader *bytes.Reader) error { 49 | err := Bucket.PutObject(objectKey, coverReader) 50 | if err != nil { 51 | klog.Errorf("publish %v to Oss fail, %v ", objectKey, err.Error()) 52 | return err 53 | } 54 | return nil 55 | } 56 | 57 | //从oss上获取封面地址 58 | func QueryOssCoverURL(objectKey string) (string, error) { 59 | signedURL, err := Bucket.SignURL(objectKey, oss.HTTPPut, 60) 60 | if err != nil { 61 | klog.Errorf("Query %v Cover URL fail, %v", objectKey, err.Error()) 62 | return "", err 63 | } 64 | return signedURL, nil 65 | } 66 | -------------------------------------------------------------------------------- /pkg/tracer/tracer.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/opentracing/opentracing-go" 7 | "github.com/uber/jaeger-client-go" 8 | jaegercfg "github.com/uber/jaeger-client-go/config" 9 | ) 10 | 11 | func InitJaeger(service string) { 12 | cfg, _ := jaegercfg.FromEnv() 13 | cfg.ServiceName = service 14 | tracer, _, err := cfg.NewTracer(jaegercfg.Logger(jaeger.StdLogger)) 15 | if err != nil { 16 | panic(fmt.Sprintf("ERROR: cannot init Jaeger: %v\n", err)) 17 | } 18 | opentracing.SetGlobalTracer(tracer) 19 | } 20 | -------------------------------------------------------------------------------- /public/girl.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenmengangzhi29/douyin/be7ee31f226f32db45377d5fb0fa7a96ce9515c8/public/girl.mp4 -------------------------------------------------------------------------------- /public/jaeger展示图.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenmengangzhi29/douyin/be7ee31f226f32db45377d5fb0fa7a96ce9515c8/public/jaeger展示图.jpg -------------------------------------------------------------------------------- /public/微服务数据库图.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenmengangzhi29/douyin/be7ee31f226f32db45377d5fb0fa7a96ce9515c8/public/微服务数据库图.jpg -------------------------------------------------------------------------------- /public/服务调用关系截图.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenmengangzhi29/douyin/be7ee31f226f32db45377d5fb0fa7a96ce9515c8/public/服务调用关系截图.jpg --------------------------------------------------------------------------------