├── .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 | [](https://go.dev/)
4 | [](https://github.com/cloudwego/kitex)
5 | [](https://github.com/gin-gonic/gin)
6 | [](https://gorm.io/)
7 | [](https://github.com/go-gorm/mysql)
8 | [](https://github.com/aliyun/aliyun-oss-go-sdk)
9 | [](https://github.com/kitex-contrib/registry-etcd)
10 | [](https://github.com/jaegertracing/jaeger-client-go)
11 | [](https://github.com/golang-jwt/jwt)
12 | [](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
--------------------------------------------------------------------------------