├── .github
├── semantic.yml
└── workflows
│ ├── build.yml
│ └── ci.yml
├── .realeaserc.json
├── LICENSE
├── README.md
├── app
├── comment
│ ├── cmd
│ │ └── main.go
│ ├── dao
│ │ ├── comment.go
│ │ ├── init.go
│ │ └── migration.go
│ └── service
│ │ └── comment.go
├── favorite
│ ├── cmd
│ │ └── main.go
│ ├── dao
│ │ ├── favorite.go
│ │ ├── init.go
│ │ └── migration.go
│ ├── script
│ │ └── favorite.go
│ └── service
│ │ └── favorite.go
├── gateway
│ ├── cmd
│ │ └── main.go
│ ├── http
│ │ ├── comment.go
│ │ ├── favorite.go
│ │ ├── message.go
│ │ ├── relation.go
│ │ ├── user.go
│ │ └── video.go
│ ├── middleware
│ │ ├── jaeger.go
│ │ └── jwt.go
│ ├── router
│ │ └── router.go
│ ├── rpc
│ │ ├── comment.go
│ │ ├── favorite.go
│ │ ├── init.go
│ │ ├── message.go
│ │ ├── relation.go
│ │ ├── user.go
│ │ └── video.go
│ └── wrapper
│ │ ├── comment.go
│ │ ├── favorite.go
│ │ ├── jaeger.go
│ │ ├── message.go
│ │ ├── relation.go
│ │ ├── user.go
│ │ └── video.go
├── message
│ ├── cmd
│ │ └── main.go
│ ├── dao
│ │ ├── init.go
│ │ ├── message.go
│ │ └── migration.go
│ └── service
│ │ └── message.go
├── relation
│ ├── cmd
│ │ └── main.go
│ ├── dao
│ │ ├── init.go
│ │ ├── migration.go
│ │ └── relation.go
│ └── service
│ │ └── relation.go
├── user
│ ├── cmd
│ │ └── main.go
│ ├── dao
│ │ ├── init.go
│ │ ├── migration.go
│ │ └── user.go
│ └── service
│ │ └── user.go
└── video
│ ├── cmd
│ └── main.go
│ ├── dao
│ ├── init.go
│ ├── migration.go
│ └── video.go
│ ├── script
│ └── video.go
│ ├── service
│ └── video.go
│ └── tmp
│ └── README.md
├── build.sh
├── config
├── config.go
└── config.ini
├── consts
└── mq.go
├── dockerfile
├── go.mod
├── go.sum
├── idl
├── comment
│ ├── commentPb
│ │ ├── commentService.pb.go
│ │ └── commentService.pb.micro.go
│ └── commentService.proto
├── favorite
│ ├── favoritePb
│ │ ├── favoriteService.pb.go
│ │ └── favoriteService.pb.micro.go
│ └── favoriteService.proto
├── message
│ ├── messagePb
│ │ ├── messageService.pb.go
│ │ └── messageService.pb.micro.go
│ └── messageService.proto
├── relation
│ ├── relationPb
│ │ ├── relationService.pb.go
│ │ └── relationService.pb.micro.go
│ └── relationService.proto
├── user
│ ├── userPb
│ │ ├── userService.pb.go
│ │ └── userService.pb.micro.go
│ └── userService.proto
└── video
│ ├── videoPb
│ ├── videoService.pb.go
│ └── videoService.pb.micro.go
│ └── videoService.proto
├── model
├── comment.go
├── favorite.go
├── follow.go
├── message.go
├── user.go
└── video.go
├── mq
├── consumer.go
├── init.go
└── producer.go
├── run.sh
├── start.sh
├── test
├── comment_test.go
├── favorite_test.go
├── feed_test.go
├── login_test.go
├── message_test.go
├── publish_test.go
├── register_test.go
├── relation_test.go
└── user_test.go
└── util
├── array_change.go
├── fail_request.go
├── ffmpeg.go
├── md5.go
├── sort.go
├── token.go
├── upload.go
└── uuid.go
/.github/semantic.yml:
--------------------------------------------------------------------------------
1 | # Always validate the PR title AND all the commits
2 | titleAndCommits: true
3 | # Require at least one commit to be valid
4 | # this is only relevant when using commitsOnly: true or titleAndCommits: true,
5 | # which validate all commits by default
6 | anyCommit: true
7 | # Allow use of Merge commits (eg on github: "Merge branch 'master' into feature/ride-unicorns")
8 | # this is only relevant when using commitsOnly: true (or titleAndCommits: true)
9 | allowMergeCommits: false
10 | # Allow use of Revert commits (eg on github: "Revert "feat: ride unicorns"")
11 | # this is only relevant when using commitsOnly: true (or titleAndCommits: true)
12 | allowRevertCommits: false
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on: [ push, pull_request ]
4 |
5 | jobs:
6 |
7 | go-tests:
8 | name: Running Go tests
9 | runs-on: ubuntu-latest
10 | services:
11 | mysql:
12 | image: mysql:5.7
13 | env:
14 | MYSQL_DATABASE: tiktok
15 | MYSQL_ROOT_PASSWORD: 123456
16 | ports:
17 | - 3306:3306
18 | options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
19 | steps:
20 | - uses: actions/checkout@v3
21 | - uses: actions/setup-go@v4
22 | with:
23 | go-version: '^1.16.5'
24 | cache-dependency-path: ./go.mod
25 | - name: Tests
26 | run: |
27 | # go test -v $(go list ./...) -tags skipCi
28 | working-directory: ./
29 |
30 | release-and-push:
31 | name: Release And Push
32 | runs-on: ubuntu-latest
33 | if: github.repository == 'UESTCByteDance/ByteRhythm' && github.event_name == 'push'
34 | steps:
35 | - name: Checkout
36 | uses: actions/checkout@v3
37 | with:
38 | fetch-depth: -1
39 | - name: Setup Node.js
40 | uses: actions/setup-node@v3
41 | with:
42 | node-version: 20
43 |
44 | - name: Fetch Previous version
45 | id: get-previous-tag
46 | uses: actions-ecosystem/action-get-latest-tag@v1.6.0
47 |
48 | - name: Release
49 | run: yarn global add semantic-release@17.4.4 && semantic-release
50 | env:
51 | GH_TOKEN: ${{ secrets.GH_BOT_TOKEN }}
52 |
53 | - name: Fetch Current version
54 | id: get-current-tag
55 | uses: actions-ecosystem/action-get-latest-tag@v1.6.0
56 |
57 | - name: Decide Should_Push Or Not
58 | id: should_push
59 | run: |
60 | old_version=${{steps.get-previous-tag.outputs.tag}}
61 | new_version=${{steps.get-current-tag.outputs.tag }}
62 |
63 | old_array=(${old_version//\./ })
64 | new_array=(${new_version//\./ })
65 |
66 | if [ ${old_array[0]} != ${new_array[0]} ]
67 | then
68 | echo ::set-output name=push::'true'
69 | elif [ ${old_array[1]} != ${new_array[1]} ]
70 | then
71 | echo ::set-output name=push::'true'
72 |
73 | else
74 | echo ::set-output name=push::'false'
75 |
76 | fi
77 |
78 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | - push
5 | - pull_request
6 |
7 | jobs:
8 |
9 | test:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Set up Go
13 | uses: actions/setup-go@v2
14 | with:
15 | go-version: 1.18
16 |
17 | - uses: actions/checkout@v2
18 |
19 | semantic-release:
20 | needs: [ test ]
21 | runs-on: ubuntu-latest
22 | steps:
23 |
24 | - uses: actions/checkout@v2
25 | - name: Run semantic-release
26 | if: github.repository == 'UESTCByteDance/ByteRhythm' && github.event_name == 'push'
27 | run: |
28 | npm install --save-dev semantic-release@17.2.4
29 | npx semantic-release
30 | env:
31 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
32 |
--------------------------------------------------------------------------------
/.realeaserc.json:
--------------------------------------------------------------------------------
1 | {
2 | "debug": true,
3 | "branches": [
4 | "+([0-9])?(.{+([0-9]),x}).x",
5 | "master",
6 | {
7 | "name": "beta",
8 | "prerelease": true
9 | }
10 | ],
11 | "plugins": [
12 | "@semantic-release/commit-analyzer",
13 | "@semantic-release/release-notes-generator",
14 | "@semantic-release/github"
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
ByteRhythm
2 | 第六届字节跳动青训营后端进阶班大项目优秀奖获得者😎
3 | 本项目利用 Golang 以及相关技术如 Gorm、MySQL、Redis、JWT、RabbitMQ、Hystrix、七牛云 等构建了基于 Gin 和 Go-micro的微服务应用,实现了视频处理、对象存储、限流、降级熔断、负载均衡等功能,并通过 Opentracing、Jaeger 等工具进行监控与追踪,Testify进行单元测试,Docker进行容器化部署,形成高可用高性能的分布式服务。
4 |
36 |
37 | #### 👀仓库地址:
38 |
39 | #### 📚文档地址:
40 |
41 | #### 🥽视频地址:[https://www.bilibili.com/video/BV1Y14y1k7gG](https://www.bilibili.com/video/BV1JF41167UL)
42 |
43 | #### 🎶PPT地址:
44 |
45 |
46 |
47 | # 使用说明
48 | 本项目有v1、v2两个版本,可前往[Releases]()下载使用,前者是传统的单体架构,用beego实现,后者是微服务架构,由gin+go-micro实现。
49 |
50 | 下面介绍v2版的使用:
51 |
52 | 如果不使用docker进行容器化部署(docker部署参照文末),可以参考以下步骤进行本地部署。建议使用环境为`Ubuntu20.04`。
53 |
54 | ## 1.克隆到本地
55 |
56 | ```bash
57 | git clone https://github.com/UESTCByteDance/ByteRhythm.git
58 | ```
59 |
60 | ## 2.安装依赖
61 |
62 | ```bash
63 | go mod tidy
64 | ```
65 |
66 | ## 3.数据库配置
67 |
68 | 打开`config.ini`,修改以下内容:
69 |
70 | ```ini
71 | DBHost = 127.0.0.1
72 | DBPort = 3306
73 | DBUser = root
74 | DBPassWord = 123456
75 | DBName = tiktok
76 | ```
77 |
78 | 确保你的`Ubuntu20.04`已经装了`MySQL`,并且能够连接上,然后新建数据库`tiktok`
79 |
80 | ## 4.配置`ffmpeg`环境
81 |
82 | 打开终端,依次执行下列命令(逐条执行):
83 |
84 | ```bash
85 | sudo apt-get -y install autoconf automake build-essential libass-dev libfreetype6-dev libsdl1.2-dev libtheora-dev libtool libva-dev libvdpau-dev libvorbis-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev pkg-config texi2html zlib1g-dev
86 |
87 | sudo apt install -y libavdevice-dev libavfilter-dev libswscale-dev libavcodec-dev libavformat-dev libswresample-dev libavutil-dev
88 |
89 | sudo apt-get install yasm
90 |
91 | export FFMPEG_ROOT=$HOME/ffmpeg
92 | export CGO_LDFLAGS="-L$FFMPEG_ROOT/lib/ -lavcodec -lavformat -lavutil -lswscale -lswresample -lavdevice -lavfilter"
93 | export CGO_CFLAGS="-I$FFMPEG_ROOT/include"
94 | export LD_LIBRARY_PATH=$HOME/ffmpeg/lib
95 | ```
96 |
97 | ## 5.启动etcd
98 |
99 | 如果未安装,前往官方网站:下载适合你系统的安装包并解压。
100 |
101 | 按需修改配置:
102 |
103 | ```ini
104 | EtcdHost = 127.0.0.1
105 | EtcdPort = 2379
106 | ```
107 |
108 | 在对应终端执行:
109 |
110 | ```bash
111 | ./etcd
112 | ```
113 |
114 | 如果权限不够,可以使用`chmod +x etcd`赋予可执行权限再执行`./etcd`。
115 | 可以安装`etcdkeeper`进入UI界面进行查看。
116 |
117 | ## 6.启动Jaeger
118 |
119 | 如果未安装,前往官方网站:下载适合你系统的安装包并解压。
120 |
121 | 按需修改配置:
122 |
123 | ```ini
124 | JaegerHost = 127.0.0.1
125 | JaegerPort = 6831
126 | ```
127 |
128 | 在对应终端执行:
129 |
130 | ```bash
131 | ./jaeger-all-in-one --collector.zipkin.host-port=:9411
132 | ```
133 |
134 | 如果权限不够,可以使用`chmod +x jaeger-all-in-one`
135 | 赋予可执行权限再执行`./jaeger-all-in-one --collector.zipkin.host-port=:9411`。
136 | 可以访问:进入UI界面。
137 |
138 | ## 7.启动RabbitMQ
139 |
140 | 如果未安装,前往官方网站:下载安装。
141 |
142 | 按需修改配置:
143 |
144 | ```ini
145 | RabbitMQ = amqp
146 | RabbitMQHost = 127.0.0.1
147 | RabbitMQPort = 5672
148 | RabbitMQUser = guest
149 | RabbitMQPassWord = guest
150 | ```
151 |
152 | 确保RabbitMQ能在本地运行。
153 |
154 | ## 8.配置Redis
155 |
156 | 如果未安装,打开终端,依次执行下列命令(逐条执行):
157 |
158 | ```bash
159 | sudo apt update
160 | sudo apt install redis-server
161 | ```
162 |
163 | 按需修改配置:
164 |
165 | ```ini
166 | RedisHost = 127.0.0.1
167 | RedisPort = 6379
168 | ```
169 |
170 | 确保Redis能在本地运行。
171 |
172 | ## 9.配置七牛云
173 |
174 | 根据你的七牛云账户信息,修改以下配置:
175 |
176 | ```ini
177 | Bucket = your bucket
178 | AccessKey = your access key
179 | SecretKey = your secret key
180 | Domain = your domain
181 | ```
182 | ## 10.运行项目
183 |
184 | ```bash
185 | //构建项目
186 | chmod +x build.sh
187 | ./build.sh
188 |
189 | //运行项目
190 | chmod +x run.sh
191 | ./run.sh
192 | ```
193 | 如果你是将项目克隆到Windows系统里,再转移到Ubuntu系统里,很可能会遇到Windows和Linux换行不兼容的问题,建议先执行命令`sed -i -e 's/\r$//' build.sh run.sh`,再执行上述命令。
194 | ## 11.运行测试
195 | ```bash
196 | cd test
197 | go test -v
198 | ```
199 | 注:测试文件的参数可能会需要根据实际情况更改。
200 |
201 | ## 12.docker 运行
202 | tips:由于服务器性能限制,因此将所有微服务放在同一个容器中,后续可用docker-compose部署为7个业务容器
203 | ### (1)拉取 mysql 镜像并运行
204 | ```sh
205 | docker run -d -p 3306:3306 --name tiktok-mysql -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=tiktok mysql/mysql-server:latest
206 | ```
207 | ### (2)拉取 byterhythm:v2.1 镜像并运行
208 | ```sh
209 | docker run -it -p 8080:8080/tcp -p 16686:16686/tcp --name byterhythm david945/byterhythm:v2.1
210 | ```
211 | # 项目贡献者
212 | * [palp1tate](https://github.com/palp1tate)
213 |
214 | * [youyou0805](https://github.com/youyou0805)
215 |
216 | * [Chiba-little-black-cat](https://github.com/Chiba-little-black-cat)
217 |
218 | * [DavidHGS](https://github.com/DavidHGS)
219 |
220 | * [tangyiheng](https://github.com/tangyiheng)
221 |
222 | * [woniu-huang](https://github.com/woniu-huang)
223 |
224 | * [janjiang005](https://github.com/janjiang005)
225 |
226 | # 获奖
227 |
228 | 
229 |
230 | 
231 |
232 | # Stargazers over time
233 | [](https://starchart.cc/UESTCByteDance/ByteRhythm)
234 |
235 |
236 |
--------------------------------------------------------------------------------
/app/comment/cmd/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "ByteRhythm/app/comment/dao"
5 | "ByteRhythm/app/comment/service"
6 | "ByteRhythm/app/gateway/wrapper"
7 | "ByteRhythm/config"
8 | "ByteRhythm/idl/comment/commentPb"
9 | "fmt"
10 | "os"
11 |
12 | "github.com/go-micro/plugins/v4/registry/etcd"
13 | ratelimit "github.com/go-micro/plugins/v4/wrapper/ratelimiter/uber"
14 | "github.com/go-micro/plugins/v4/wrapper/select/roundrobin"
15 | "github.com/go-micro/plugins/v4/wrapper/trace/opentracing"
16 |
17 | "go-micro.dev/v4"
18 | "go-micro.dev/v4/registry"
19 | )
20 |
21 | func main() {
22 | config.Init()
23 | dao.InitMySQL()
24 | dao.InitRedis()
25 |
26 | defer dao.RedisNo1Client.Close()
27 | defer dao.RedisNo2Client.Close()
28 |
29 | // etcd注册件
30 | etcdReg := etcd.NewRegistry(
31 | registry.Addrs(fmt.Sprintf("%s:%s", config.EtcdHost, config.EtcdPort)),
32 | )
33 |
34 | // 链路追踪
35 | tracer, closer, err := wrapper.InitJaeger("CommentService", fmt.Sprintf("%s:%s", config.JaegerHost, config.JaegerPort))
36 | if err != nil {
37 | fmt.Printf("new tracer err: %+v\n", err)
38 | os.Exit(-1)
39 | }
40 | defer closer.Close()
41 |
42 | // 得到一个微服务实例
43 | microService := micro.NewService(
44 | micro.Name("CommentService"), // 微服务名字
45 | micro.Address(config.CommentServiceAddress),
46 | micro.Registry(etcdReg), // etcd注册件
47 | micro.WrapHandler(ratelimit.NewHandlerWrapper(50000)), //限流处理
48 | micro.WrapClient(roundrobin.NewClientWrapper()), // 负载均衡
49 | micro.WrapHandler(opentracing.NewHandlerWrapper(tracer)), // 链路追踪
50 | micro.WrapClient(opentracing.NewClientWrapper(tracer)), // 链路追踪
51 | )
52 |
53 | // 结构命令行参数,初始化
54 | microService.Init()
55 | // 服务注册
56 | _ = commentPb.RegisterCommentServiceHandler(microService.Server(), service.GetCommentSrv())
57 | // 启动微服务
58 | _ = microService.Run()
59 | }
60 |
--------------------------------------------------------------------------------
/app/comment/dao/comment.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "ByteRhythm/model"
5 | "ByteRhythm/util"
6 | "context"
7 |
8 | "gorm.io/gorm"
9 | )
10 |
11 | type CommentDao struct {
12 | *gorm.DB
13 | }
14 |
15 | func NewCommentDao(ctx context.Context) *CommentDao {
16 | if ctx == nil {
17 | ctx = context.Background()
18 | }
19 | return &CommentDao{NewDBClient(ctx)}
20 | }
21 |
22 | func (c *CommentDao) CreateComment(comment *model.Comment) (err error) {
23 | err = c.Model(&model.Comment{}).Create(&comment).Error
24 | if err != nil {
25 | return
26 | }
27 | return nil
28 | }
29 |
30 | func (c *CommentDao) DeleteComment(comment *model.Comment) (err error) {
31 | err = c.Model(&model.Comment{}).Where(&comment).Delete(&comment).Error
32 | if err != nil {
33 | return
34 | }
35 | return nil
36 | }
37 |
38 | func (c *CommentDao) GetCommentListByVideoId(vid int64) (comments []*model.Comment, err error) {
39 | err = c.Model(&model.Comment{}).Where("video_id = ?", vid).Order("id desc").Find(&comments).Error
40 | if err != nil {
41 | return
42 | }
43 | return
44 | }
45 |
46 | func (c *CommentDao) GetUsernameByUid(uid int64) (username string, err error) {
47 | err = c.Model(&model.User{}).Where("id = ?", uid).Select("username").Find(&username).Error
48 | if err != nil {
49 | return
50 | }
51 | return
52 | }
53 |
54 | func (c *CommentDao) GetAvatarByUid(uid int64) (avatar string, err error) {
55 | err = c.Model(&model.User{}).Where("id = ?", uid).Select("avatar").Find(&avatar).Error
56 | if err != nil {
57 | return
58 | }
59 | return
60 | }
61 |
62 | func (c *CommentDao) GetFollowCount(uid int) (count int64, err error) {
63 | err = c.Model(&model.Follow{}).Where("followed_user_id = ?", uid).Count(&count).Error
64 | if err != nil {
65 | return
66 | }
67 | return
68 | }
69 |
70 | func (c *CommentDao) GetFollowerCount(uid int) (count int64, err error) {
71 | err = c.Model(&model.Follow{}).Where("user_id = ?", uid).Count(&count).Error
72 | if err != nil {
73 | return
74 | }
75 | return
76 | }
77 |
78 | func (c *CommentDao) GetWorkCount(uid int) (count int64, err error) {
79 | err = c.Model(&model.Video{}).Where("author_id = ?", uid).Count(&count).Error
80 | if err != nil {
81 | return
82 | }
83 | return
84 | }
85 |
86 | func (c *CommentDao) GetUserFavoriteCount(uid int) (count int64, err error) {
87 | err = c.Model(&model.Favorite{}).Where("user_id = ?", uid).Count(&count).Error
88 | if err != nil {
89 | return
90 | }
91 | return
92 | }
93 |
94 | func (c *CommentDao) GetFavoriteCount(vid int) (count int64, err error) {
95 | err = c.Model(&model.Favorite{}).Where("video_id = ?", vid).Count(&count).Error
96 | if err != nil {
97 | return
98 | }
99 | return
100 | }
101 |
102 | func (c *CommentDao) GetTotalFavorited(uid int) (count int64, err error) {
103 | var videos []*model.Video
104 | err = c.Model(&model.Video{}).Where("author_id = ?", uid).Find(&videos).Error
105 | if err != nil {
106 | return
107 | }
108 | for _, video := range videos {
109 | var favoriteCount int64
110 | err = c.Model(&model.Favorite{}).Where("video_id = ?", video.ID).Count(&favoriteCount).Error
111 | if err != nil {
112 | return
113 | }
114 | count += favoriteCount
115 | }
116 | return
117 | }
118 |
119 | func (c *CommentDao) GetIsFollowed(uid int, token string) (isFollowed bool, err error) {
120 |
121 | baseID, err := util.GetUserIdFromToken(token)
122 | if err != nil {
123 | return
124 | }
125 | var follow model.Follow
126 | err = c.Model(&model.Follow{}).Where("user_id = ?", baseID).Where("followed_user_id = ?", uid).Limit(1).Find(&follow).Error
127 | if err != nil {
128 | return
129 | }
130 | if follow.ID != 0 {
131 | isFollowed = true
132 | } else {
133 | isFollowed = false
134 | }
135 | return
136 | }
137 |
--------------------------------------------------------------------------------
/app/comment/dao/init.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "github.com/go-redis/redis/v8"
7 | "time"
8 |
9 | "github.com/gin-gonic/gin"
10 | "gorm.io/driver/mysql"
11 | "gorm.io/gorm"
12 | "gorm.io/gorm/logger"
13 | "gorm.io/gorm/schema"
14 |
15 | "ByteRhythm/config"
16 | )
17 |
18 | var db *gorm.DB
19 |
20 | var RedisNo2Client *redis.Client
21 | var RedisNo1Client *redis.Client
22 |
23 | func InitRedis() {
24 | // 初始化 Redis 客户端
25 | host := config.RedisHost
26 | port := config.RedisPort
27 | RedisNo2Client = redis.NewClient(&redis.Options{
28 | Addr: host + ":" + port, // Redis 服务器地址
29 | Password: "", // Redis 访问密码(如果有的话)
30 | DB: 2, // Redis 数据库索引
31 | })
32 |
33 | RedisNo1Client = redis.NewClient(&redis.Options{
34 | Addr: host + ":" + port, // Redis 服务器地址
35 | Password: "", // Redis 访问密码(如果有的话)
36 | DB: 1, // Redis 数据库索引
37 | })
38 | }
39 |
40 | func InitMySQL() {
41 | host := config.DBHost
42 | port := config.DBPort
43 | database := config.DBName
44 | username := config.DBUser
45 | password := config.DBPassWord
46 | charset := config.Charset
47 | dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&parseTime=True&loc=Local", username, password, host, port, database, charset)
48 | err := Database(dsn)
49 | if err != nil {
50 | fmt.Println(err)
51 | }
52 | }
53 |
54 | func Database(connString string) error {
55 | var ormLogger logger.Interface
56 | if gin.Mode() == "debug" {
57 | ormLogger = logger.Default.LogMode(logger.Info)
58 | } else {
59 | ormLogger = logger.Default
60 | }
61 | DB, err := gorm.Open(mysql.New(mysql.Config{
62 | DSN: connString, // DSN data source name
63 | DefaultStringSize: 256, // string 类型字段的默认长度
64 | DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
65 | DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
66 | DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
67 | SkipInitializeWithVersion: false, // 根据版本自动配置
68 | }), &gorm.Config{
69 | Logger: ormLogger,
70 | NamingStrategy: schema.NamingStrategy{
71 | SingularTable: true,
72 | },
73 | })
74 | if err != nil {
75 | panic(err)
76 | }
77 | sqlDB, _ := DB.DB()
78 | sqlDB.SetMaxIdleConns(20)
79 | sqlDB.SetMaxOpenConns(100)
80 | sqlDB.SetConnMaxLifetime(time.Second * 30)
81 | db = DB
82 | migration()
83 | return err
84 | }
85 |
86 | func NewDBClient(ctx context.Context) *gorm.DB {
87 | DB := db
88 | return DB.WithContext(ctx)
89 | }
90 |
--------------------------------------------------------------------------------
/app/comment/dao/migration.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "ByteRhythm/model"
5 | "log"
6 | )
7 |
8 | func migration() {
9 | err := db.Set(`gorm:table_options`, "charset=utf8mb4").
10 | AutoMigrate(&model.User{}, &model.Follow{}, &model.Video{}, &model.Favorite{}, &model.Comment{})
11 | if err != nil {
12 | log.Fatal(err)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/favorite/cmd/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "ByteRhythm/app/favorite/dao"
5 | "ByteRhythm/app/favorite/script"
6 | "ByteRhythm/app/favorite/service"
7 | "ByteRhythm/app/gateway/wrapper"
8 | "ByteRhythm/config"
9 | "ByteRhythm/idl/favorite/favoritePb"
10 | "ByteRhythm/mq"
11 | "context"
12 | "fmt"
13 | "os"
14 |
15 | "github.com/go-micro/plugins/v4/registry/etcd"
16 | ratelimit "github.com/go-micro/plugins/v4/wrapper/ratelimiter/uber"
17 | "github.com/go-micro/plugins/v4/wrapper/select/roundrobin"
18 | "github.com/go-micro/plugins/v4/wrapper/trace/opentracing"
19 |
20 | "go-micro.dev/v4"
21 | "go-micro.dev/v4/registry"
22 | )
23 |
24 | func main() {
25 | config.Init()
26 | dao.InitMySQL()
27 | dao.InitRedis()
28 | mq.InitRabbitMQ()
29 | loadingScript()
30 |
31 | defer dao.RedisClient.Close()
32 |
33 | // etcd注册件
34 | etcdReg := etcd.NewRegistry(
35 | registry.Addrs(fmt.Sprintf("%s:%s", config.EtcdHost, config.EtcdPort)),
36 | )
37 |
38 | // 链路追踪
39 | tracer, closer, err := wrapper.InitJaeger("FavoriteService", fmt.Sprintf("%s:%s", config.JaegerHost, config.JaegerPort))
40 | if err != nil {
41 | fmt.Printf("new tracer err: %+v\n", err)
42 | os.Exit(-1)
43 | }
44 | defer closer.Close()
45 |
46 | // 得到一个微服务实例
47 | microService := micro.NewService(
48 | micro.Name("FavoriteService"), // 微服务名字
49 | micro.Address(config.FavoriteServiceAddress),
50 | micro.Registry(etcdReg), // etcd注册件
51 | micro.WrapHandler(ratelimit.NewHandlerWrapper(50000)), //限流处理
52 | micro.WrapClient(roundrobin.NewClientWrapper()), // 负载均衡
53 | micro.WrapHandler(opentracing.NewHandlerWrapper(tracer)), // 链路追踪
54 | micro.WrapClient(opentracing.NewClientWrapper(tracer)), // 链路追踪
55 | )
56 |
57 | // 结构命令行参数,初始化
58 | microService.Init()
59 | // 服务注册
60 | _ = favoritePb.RegisterFavoriteServiceHandler(microService.Server(), service.GetFavoriteSrv())
61 | // 启动微服务
62 | _ = microService.Run()
63 | }
64 |
65 | func loadingScript() {
66 | ctx := context.Background()
67 | go script.FavoriteCreateSync(ctx)
68 | go script.FavoriteDeleteSync(ctx)
69 | }
70 |
--------------------------------------------------------------------------------
/app/favorite/dao/favorite.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "ByteRhythm/model"
5 | "ByteRhythm/util"
6 | "context"
7 |
8 | "gorm.io/gorm"
9 | )
10 |
11 | type FavoriteDao struct {
12 | *gorm.DB
13 | }
14 |
15 | func NewFavoriteDao(ctx context.Context) *FavoriteDao {
16 | if ctx == nil {
17 | ctx = context.Background()
18 | }
19 | return &FavoriteDao{NewDBClient(ctx)}
20 | }
21 |
22 | func (f *FavoriteDao) CreateFavorite(favorite *model.Favorite) (err error) {
23 | err = f.Model(&model.Favorite{}).Create(&favorite).Error
24 | if err != nil {
25 | return
26 | }
27 | return nil
28 | }
29 |
30 | func (f *FavoriteDao) DeleteFavorite(favorite *model.Favorite) (err error) {
31 | err = f.Model(&model.Favorite{}).Where(&favorite).Delete(&favorite).Error
32 | if err != nil {
33 | return
34 | }
35 | return nil
36 | }
37 |
38 | func (f *FavoriteDao) GetFavoriteListByUserId(uid int64) (favorites []*model.Favorite, err error) {
39 | err = f.Model(&model.Favorite{}).Where("user_id = ?", uid).Find(&favorites).Error
40 | if err != nil {
41 | return
42 | }
43 | return
44 | }
45 |
46 | func (f *FavoriteDao) GetIsFavoriteByUserIdAndVid(uid int64, vid int64) (isFavorite bool, err error) {
47 | var count int64
48 | err = f.Model(&model.Favorite{}).Where("user_id = ?", uid).Where("video_id = ?", vid).Count(&count).Error
49 | if err != nil {
50 | return false, err
51 | }
52 | if count > 0 {
53 | return true, nil
54 | }
55 | return false, nil
56 | }
57 |
58 | func (f *FavoriteDao) GetFollowCount(uid int) (count int64, err error) {
59 | err = f.Model(&model.Follow{}).Where("followed_user_id = ?", uid).Count(&count).Error
60 | if err != nil {
61 | return
62 | }
63 | return
64 | }
65 |
66 | func (f *FavoriteDao) GetFollowerCount(uid int) (count int64, err error) {
67 | err = f.Model(&model.Follow{}).Where("user_id = ?", uid).Count(&count).Error
68 | if err != nil {
69 | return
70 | }
71 | return
72 | }
73 |
74 | func (f *FavoriteDao) GetWorkCount(uid int) (count int64, err error) {
75 | err = f.Model(&model.Video{}).Where("author_id = ?", uid).Count(&count).Error
76 | if err != nil {
77 | return
78 | }
79 | return
80 | }
81 |
82 | func (f *FavoriteDao) GetUserFavoriteCount(uid int) (count int64, err error) {
83 | err = f.Model(&model.Favorite{}).Where("user_id = ?", uid).Count(&count).Error
84 | if err != nil {
85 | return
86 | }
87 | return
88 | }
89 |
90 | func (f *FavoriteDao) GetFavoriteCount(vid int) (count int64, err error) {
91 | err = f.Model(&model.Favorite{}).Where("video_id = ?", vid).Count(&count).Error
92 | if err != nil {
93 | return
94 | }
95 | return
96 | }
97 |
98 | func (f *FavoriteDao) GetTotalFavorited(uid int) (count int64, err error) {
99 | var videos []*model.Video
100 | err = f.Model(&model.Video{}).Where("author_id = ?", uid).Find(&videos).Error
101 | if err != nil {
102 | return
103 | }
104 | for _, video := range videos {
105 | var favoriteCount int64
106 | err = f.Model(&model.Favorite{}).Where("video_id = ?", video.ID).Count(&favoriteCount).Error
107 | if err != nil {
108 | return
109 | }
110 | count += favoriteCount
111 | }
112 | return
113 | }
114 |
115 | func (f *FavoriteDao) GetIsFollowed(uid int, token string) (isFollowed bool, err error) {
116 |
117 | baseID, err := util.GetUserIdFromToken(token)
118 | if err != nil {
119 | return
120 | }
121 | var follow model.Follow
122 | err = f.Model(&model.Follow{}).Where("user_id = ?", baseID).Where("followed_user_id = ?", uid).Limit(1).Find(&follow).Error
123 | if err != nil {
124 | return
125 | }
126 | if follow.ID != 0 {
127 | isFollowed = true
128 | } else {
129 | isFollowed = false
130 | }
131 | return
132 | }
133 |
134 | func (f *FavoriteDao) GetPlayUrlByVid(vid int) (playUrl string, err error) {
135 | err = f.Model(&model.Video{}).Where("id = ?", vid).Select("play_url").First(&playUrl).Error
136 | if err != nil {
137 | return
138 | }
139 | return
140 | }
141 |
142 | func (f *FavoriteDao) GetCoverUrlByVid(vid int) (playUrl string, err error) {
143 | err = f.Model(&model.Video{}).Where("id = ?", vid).Select("cover_url").First(&playUrl).Error
144 | if err != nil {
145 | return
146 | }
147 | return
148 | }
149 |
--------------------------------------------------------------------------------
/app/favorite/dao/init.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "time"
7 |
8 | "github.com/gin-gonic/gin"
9 | "github.com/go-redis/redis/v8"
10 | "gorm.io/driver/mysql"
11 | "gorm.io/gorm"
12 | "gorm.io/gorm/logger"
13 | "gorm.io/gorm/schema"
14 |
15 | "ByteRhythm/config"
16 | )
17 |
18 | var db *gorm.DB
19 |
20 | var RedisClient *redis.Client
21 |
22 | func InitMySQL() {
23 | host := config.DBHost
24 | port := config.DBPort
25 | database := config.DBName
26 | username := config.DBUser
27 | password := config.DBPassWord
28 | charset := config.Charset
29 | dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&parseTime=True&loc=Local", username, password, host, port, database, charset)
30 | err := Database(dsn)
31 | if err != nil {
32 | fmt.Println(err)
33 | }
34 | }
35 |
36 | func InitRedis() {
37 | // 初始化 Redis 客户端
38 | host := config.RedisHost
39 | port := config.RedisPort
40 | RedisClient = redis.NewClient(&redis.Options{
41 | Addr: host + ":" + port, // Redis 服务器地址
42 | Password: "", // Redis 访问密码(如果有的话)
43 | DB: 1, // Redis 数据库索引
44 | })
45 | }
46 |
47 | func Database(connString string) error {
48 | var ormLogger logger.Interface
49 | if gin.Mode() == "debug" {
50 | ormLogger = logger.Default.LogMode(logger.Info)
51 | } else {
52 | ormLogger = logger.Default
53 | }
54 | DB, err := gorm.Open(mysql.New(mysql.Config{
55 | DSN: connString, // DSN data source name
56 | DefaultStringSize: 256, // string 类型字段的默认长度
57 | DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
58 | DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
59 | DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
60 | SkipInitializeWithVersion: false, // 根据版本自动配置
61 | }), &gorm.Config{
62 | Logger: ormLogger,
63 | NamingStrategy: schema.NamingStrategy{
64 | SingularTable: true,
65 | },
66 | })
67 | if err != nil {
68 | panic(err)
69 | }
70 | sqlDB, _ := DB.DB()
71 | sqlDB.SetMaxIdleConns(20)
72 | sqlDB.SetMaxOpenConns(100)
73 | sqlDB.SetConnMaxLifetime(time.Second * 30)
74 | db = DB
75 | migration()
76 | return err
77 | }
78 |
79 | func NewDBClient(ctx context.Context) *gorm.DB {
80 | DB := db
81 | return DB.WithContext(ctx)
82 | }
83 |
--------------------------------------------------------------------------------
/app/favorite/dao/migration.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "ByteRhythm/model"
5 | "log"
6 | )
7 |
8 | func migration() {
9 | err := db.Set(`gorm:table_options`, "charset=utf8mb4").
10 | AutoMigrate(&model.Follow{}, &model.Video{}, &model.Favorite{}, &model.Comment{})
11 | if err != nil {
12 | log.Fatal(err)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/favorite/script/favorite.go:
--------------------------------------------------------------------------------
1 | package script
2 |
3 | import (
4 | "ByteRhythm/app/favorite/service"
5 | "ByteRhythm/idl/favorite/favoritePb"
6 | "ByteRhythm/mq"
7 | "context"
8 | "encoding/json"
9 | )
10 |
11 | func FavoriteCreateSync(ctx context.Context) {
12 | Sync := new(SyncFavorite)
13 | err := Sync.SyncFavoriteCreate(ctx)
14 | if err != nil {
15 | return
16 | }
17 | }
18 |
19 | func FavoriteDeleteSync(ctx context.Context) {
20 | Sync := new(SyncFavorite)
21 | err := Sync.SyncFavoriteDelete(ctx)
22 | if err != nil {
23 | return
24 | }
25 | }
26 |
27 | type SyncFavorite struct {
28 | }
29 |
30 | func (s *SyncFavorite) SyncFavoriteCreate(ctx context.Context) error {
31 | RabbitMQName := "favorite-create-queue"
32 | msg, err := mq.ConsumeMessage(ctx, RabbitMQName)
33 | if err != nil {
34 | return err
35 | }
36 | var forever chan struct{}
37 | go func() {
38 | for d := range msg {
39 | // 落库
40 | var req *favoritePb.FavoriteActionRequest
41 | err = json.Unmarshal(d.Body, &req)
42 | if err != nil {
43 | return
44 | }
45 | err = service.FavoriteMQ2DB(ctx, req)
46 | if err != nil {
47 | return
48 | }
49 | d.Ack(false)
50 | }
51 | }()
52 | <-forever
53 | return nil
54 | }
55 |
56 | func (s *SyncFavorite) SyncFavoriteDelete(ctx context.Context) error {
57 | RabbitMQName := "favorite-delete-queue"
58 | msg, err := mq.ConsumeMessage(ctx, RabbitMQName)
59 | if err != nil {
60 | return err
61 | }
62 | var forever chan struct{}
63 | go func() {
64 | for d := range msg {
65 | // 落库
66 | var req *favoritePb.FavoriteActionRequest
67 | err = json.Unmarshal(d.Body, &req)
68 | if err != nil {
69 | return
70 | }
71 | err = service.FavoriteMQ2DB(ctx, req)
72 | if err != nil {
73 | return
74 | }
75 | d.Ack(false)
76 | }
77 | }()
78 | <-forever
79 | return nil
80 | }
81 |
--------------------------------------------------------------------------------
/app/favorite/service/favorite.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "ByteRhythm/app/favorite/dao"
5 | "ByteRhythm/consts"
6 | "ByteRhythm/idl/favorite/favoritePb"
7 | "ByteRhythm/model"
8 | "ByteRhythm/mq"
9 | "ByteRhythm/util"
10 | "context"
11 | "encoding/json"
12 | "fmt"
13 | "sync"
14 | "time"
15 |
16 | "github.com/go-redis/redis/v8"
17 | )
18 |
19 | type FavoriteSrv struct {
20 | }
21 |
22 | var FavoriteSrvIns *FavoriteSrv
23 | var FavoriteSrvOnce sync.Once
24 |
25 | // GetFavoriteSrv 懒汉式的单例模式 lazy-loading --> 懒汉式
26 | func GetFavoriteSrv() *FavoriteSrv {
27 | FavoriteSrvOnce.Do(func() {
28 | FavoriteSrvIns = &FavoriteSrv{}
29 | })
30 | return FavoriteSrvIns
31 | }
32 |
33 | func (c FavoriteSrv) FavoriteAction(ctx context.Context, req *favoritePb.FavoriteActionRequest, res *favoritePb.FavoriteActionResponse) error {
34 | actionType := req.ActionType
35 | vid := req.VideoId
36 | uid, _ := util.GetUserIdFromToken(req.Token)
37 |
38 | body, _ := json.Marshal(&req)
39 |
40 | // 点赞
41 | if actionType == 1 {
42 | // 不能重复点赞
43 | isFavorite, _ := dao.NewFavoriteDao(ctx).GetIsFavoriteByUserIdAndVid(int64(uid), vid)
44 |
45 | if isFavorite {
46 | FavoriteActionResponseData(res, 1, "重复点赞")
47 | return nil
48 | }
49 |
50 | //修改redis
51 | key := fmt.Sprintf("%d", vid)
52 | redisResult, err := dao.RedisClient.Get(ctx, key).Result()
53 | if err != nil && err != redis.Nil {
54 | FavoriteActionResponseData(res, 1, "点赞失败")
55 | return err
56 | }
57 |
58 | if err != redis.Nil { // 在redis中找到了视频信息
59 | var video favoritePb.Video
60 | err = json.Unmarshal([]byte(redisResult), &video)
61 | if err != nil {
62 | FavoriteActionResponseData(res, 1, "点赞失败")
63 | return err
64 | }
65 |
66 | video.FavoriteCount += 1
67 |
68 | videoJson, _ := json.Marshal(&video)
69 | dao.RedisClient.Set(ctx, key, videoJson, time.Hour)
70 | }
71 |
72 | // 加入消息队列
73 | err = mq.SendMessage2MQ(body, consts.CreateFavorite2MQ)
74 | if err != nil {
75 | FavoriteActionResponseData(res, 1, "点赞失败")
76 | return err
77 | }
78 | }
79 |
80 | // 取消点赞
81 | if actionType == 2 {
82 | //修改redis
83 | key := fmt.Sprintf("%d", vid)
84 | redisResult, err := dao.RedisClient.Get(ctx, key).Result()
85 | if err != nil && err != redis.Nil {
86 | FavoriteActionResponseData(res, 1, "取消点赞失败")
87 | return err
88 | }
89 |
90 | if err != redis.Nil { // 在redis中找到了视频信息
91 | var video favoritePb.Video
92 | err = json.Unmarshal([]byte(redisResult), &video)
93 | if err != nil {
94 | FavoriteActionResponseData(res, 1, "取消点赞失败")
95 | return err
96 | }
97 |
98 | video.FavoriteCount -= 1
99 |
100 | videoJson, _ := json.Marshal(&video)
101 | dao.RedisClient.Set(ctx, key, videoJson, time.Hour)
102 | }
103 |
104 | // 加入消息队列
105 | err = mq.SendMessage2MQ(body, consts.DeleteFavorite2MQ)
106 | if err != nil {
107 | FavoriteActionResponseData(res, 1, "取消点赞失败")
108 | return err
109 | }
110 | }
111 |
112 | return nil
113 | }
114 |
115 | func (c FavoriteSrv) FavoriteList(ctx context.Context, req *favoritePb.FavoriteListRequest, res *favoritePb.FavoriteListResponse) error {
116 | uid := req.UserId
117 |
118 | favorites, err := dao.NewFavoriteDao(ctx).GetFavoriteListByUserId(uid)
119 |
120 | if err != nil {
121 | return err
122 | }
123 |
124 | res.StatusCode = 0
125 | res.StatusMsg = "获取喜欢列表成功"
126 |
127 | for _, favorite := range favorites {
128 | vid := favorite.VideoID
129 | res.VideoList = append(res.VideoList, BuildVideoPbModelByVid(ctx, vid))
130 | }
131 | return nil
132 | }
133 |
134 | func BuildVideoPbModelByVid(ctx context.Context, vid int) *favoritePb.Video {
135 | FavoriteCount, _ := dao.NewFavoriteDao(ctx).GetFavoriteCount(vid)
136 | PlayUrl, _ := dao.NewFavoriteDao(ctx).GetPlayUrlByVid(vid)
137 | CoverUrl, _ := dao.NewFavoriteDao(ctx).GetCoverUrlByVid(vid)
138 |
139 | return &favoritePb.Video{
140 | Id: int64(vid),
141 | PlayUrl: PlayUrl,
142 | CoverUrl: CoverUrl,
143 | FavoriteCount: FavoriteCount,
144 | }
145 | }
146 |
147 | func BuildUserPbModel(ctx context.Context, user *model.User, token string) *favoritePb.User {
148 | uid := int(user.ID)
149 | FollowCount, _ := dao.NewFavoriteDao(ctx).GetFollowCount(uid)
150 | FollowerCount, _ := dao.NewFavoriteDao(ctx).GetFollowerCount(uid)
151 | WorkCount, _ := dao.NewFavoriteDao(ctx).GetWorkCount(uid)
152 | FavoriteCount, _ := dao.NewFavoriteDao(ctx).GetFavoriteCount(uid)
153 | TotalFavorited, _ := dao.NewFavoriteDao(ctx).GetTotalFavorited(uid)
154 | IsFollow, _ := dao.NewFavoriteDao(ctx).GetIsFollowed(uid, token)
155 | return &favoritePb.User{
156 | Id: int64(uid),
157 | Name: user.Username,
158 | Avatar: user.Avatar,
159 | BackgroundImage: user.BackgroundImage,
160 | Signature: user.Signature,
161 | FollowCount: FollowCount,
162 | FollowerCount: FollowerCount,
163 | WorkCount: WorkCount,
164 | FavoriteCount: FavoriteCount,
165 | TotalFavorited: TotalFavorited,
166 | IsFollow: IsFollow,
167 | }
168 | }
169 |
170 | func FavoriteActionResponseData(res *favoritePb.FavoriteActionResponse, StatusCode int32, StatusMsg string) {
171 | res.StatusCode = StatusCode
172 | res.StatusMsg = StatusMsg
173 | }
174 |
175 | func FavoriteMQ2DB(ctx context.Context, req *favoritePb.FavoriteActionRequest) error {
176 | token := req.Token
177 | videoId := req.VideoId
178 | actionType := req.ActionType
179 |
180 | // 解析token
181 | user, _ := util.GetUserFromToken(token)
182 |
183 | // 点赞
184 | if actionType == 1 {
185 | //加入redis
186 | //加入校队列
187 |
188 | favorite := model.Favorite{
189 | UserID: int(user.ID), // uint to int
190 | VideoID: int(videoId), // int64 to int
191 | }
192 | if err := dao.NewFavoriteDao(ctx).CreateFavorite(&favorite); err != nil {
193 | return err
194 | }
195 |
196 | return nil
197 | }
198 |
199 | // 取消点赞
200 | favorite := model.Favorite{
201 | UserID: int(user.ID), // uint to int
202 | VideoID: int(videoId), // int64 to int
203 | }
204 |
205 | if err := dao.NewFavoriteDao(ctx).DeleteFavorite(&favorite); err != nil {
206 | return err
207 | }
208 |
209 | return nil
210 | }
211 |
--------------------------------------------------------------------------------
/app/gateway/cmd/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "ByteRhythm/app/gateway/router"
5 | "ByteRhythm/app/gateway/rpc"
6 | "ByteRhythm/config"
7 | "fmt"
8 | "time"
9 |
10 | "github.com/go-micro/plugins/v4/registry/etcd"
11 | "go-micro.dev/v4/registry"
12 | "go-micro.dev/v4/web"
13 | )
14 |
15 | func main() {
16 | config.Init()
17 | rpc.InitRPC()
18 | etcdReg := etcd.NewRegistry(
19 | registry.Addrs(fmt.Sprintf("%s:%s", config.EtcdHost, config.EtcdPort)),
20 | )
21 |
22 | // 得到一个微服务实例
23 | webService := web.NewService(
24 | web.Name("HttpService"), // 微服务名字
25 | web.Address(fmt.Sprintf("%s:%s", config.HttpHost, config.HttpPort)),
26 | web.Registry(etcdReg), // etcd注册件
27 | web.Handler(router.NewRouter()), // 路由
28 | web.RegisterTTL(time.Second*30), // 服务注册时间
29 | web.Metadata(map[string]string{"protocol": "http"}),
30 | )
31 |
32 | _ = webService.Init()
33 | _ = webService.Run()
34 | }
35 |
--------------------------------------------------------------------------------
/app/gateway/http/comment.go:
--------------------------------------------------------------------------------
1 | package http
2 |
3 | import (
4 | "ByteRhythm/app/gateway/rpc"
5 | "ByteRhythm/app/gateway/wrapper"
6 | "ByteRhythm/idl/comment/commentPb"
7 | "ByteRhythm/util"
8 | "github.com/afex/hystrix-go/hystrix"
9 | "net/http"
10 | "strconv"
11 |
12 | "github.com/gin-gonic/gin"
13 | )
14 |
15 | func CommentActionHandler(ctx *gin.Context) {
16 | var req commentPb.CommentActionRequest
17 | req.Token = ctx.Query("token")
18 | vid, _ := strconv.Atoi(ctx.Query("video_id"))
19 | req.VideoId = int64(vid)
20 | ActionType, _ := strconv.Atoi(ctx.Query("action_type"))
21 | req.ActionType = int32(ActionType)
22 | CommentId, _ := strconv.Atoi(ctx.Query("comment_id"))
23 | req.CommentText = ctx.Query("comment_text")
24 | req.CommentId = int64(CommentId)
25 |
26 | var res *commentPb.CommentActionResponse
27 |
28 | hystrix.ConfigureCommand("CommentAction", wrapper.CommentActionFuseConfig)
29 | err := hystrix.Do("CommentAction", func() (err error) {
30 | res, err = rpc.CommentAction(ctx, &req)
31 | if err != nil {
32 | return err
33 | }
34 | return err
35 | }, func(err error) error {
36 | return err
37 | })
38 |
39 | if err != nil {
40 | ctx.JSON(http.StatusInternalServerError, util.FailRequest(err.Error()))
41 | return
42 | }
43 | ctx.JSON(http.StatusOK, gin.H{
44 | "status_code": res.StatusCode,
45 | "status_msg": res.StatusMsg,
46 | "comment": res.Comment,
47 | })
48 | }
49 |
50 | func CommentListHandler(ctx *gin.Context) {
51 | var req commentPb.CommentListRequest
52 | req.Token = ctx.Query("token")
53 | vid, _ := strconv.Atoi(ctx.Query("video_id"))
54 | req.VideoId = int64(vid)
55 |
56 | var res *commentPb.CommentListResponse
57 |
58 | hystrix.ConfigureCommand("CommentList", wrapper.CommentListFuseConfig)
59 | err := hystrix.Do("CommentList", func() (err error) {
60 | res, err = rpc.CommentList(ctx, &req)
61 | if err != nil {
62 | return err
63 | }
64 | return err
65 | }, func(err error) error {
66 | return err
67 | })
68 |
69 | if err != nil {
70 | ctx.JSON(http.StatusInternalServerError, util.FailRequest(err.Error()))
71 | return
72 | }
73 | ctx.JSON(http.StatusOK, gin.H{
74 | "status_code": res.StatusCode,
75 | "status_msg": res.StatusMsg,
76 | "comment_list": res.CommentList,
77 | })
78 | }
79 |
--------------------------------------------------------------------------------
/app/gateway/http/favorite.go:
--------------------------------------------------------------------------------
1 | package http
2 |
3 | import (
4 | "ByteRhythm/app/gateway/rpc"
5 | "ByteRhythm/app/gateway/wrapper"
6 | "ByteRhythm/idl/favorite/favoritePb"
7 | "ByteRhythm/util"
8 | "github.com/afex/hystrix-go/hystrix"
9 | "net/http"
10 | "strconv"
11 |
12 | "github.com/gin-gonic/gin"
13 | )
14 |
15 | func FavoriteActionHandler(ctx *gin.Context) {
16 | var req favoritePb.FavoriteActionRequest
17 | req.Token = ctx.Query("token")
18 | vid, _ := strconv.Atoi(ctx.Query("video_id"))
19 | req.VideoId = int64(vid)
20 | ActionType, _ := strconv.Atoi(ctx.Query("action_type"))
21 | req.ActionType = int32(ActionType)
22 |
23 | var res *favoritePb.FavoriteActionResponse
24 |
25 | hystrix.ConfigureCommand("FavoriteAction", wrapper.FavoriteActionFuseConfig)
26 | err := hystrix.Do("FavoriteAction", func() (err error) {
27 | res, err = rpc.FavoriteAction(ctx, &req)
28 | if err != nil {
29 | return err
30 | }
31 | return err
32 | }, func(err error) error {
33 | return err
34 | })
35 |
36 | if err != nil {
37 | ctx.JSON(http.StatusInternalServerError, util.FailRequest(err.Error()))
38 | return
39 | }
40 |
41 | ctx.JSON(http.StatusOK, gin.H{
42 | "status_code": res.StatusCode,
43 | "status_msg": res.StatusMsg,
44 | })
45 | }
46 |
47 | func FavoriteListHandler(ctx *gin.Context) {
48 | var req favoritePb.FavoriteListRequest
49 | uid, _ := strconv.Atoi(ctx.Query("user_id"))
50 | req.UserId = int64(uid)
51 | req.Token = ctx.Query("token")
52 |
53 | var res *favoritePb.FavoriteListResponse
54 |
55 | hystrix.ConfigureCommand("FavoriteList", wrapper.FavoriteListFuseConfig)
56 | err := hystrix.Do("FavoriteList", func() (err error) {
57 | res, err = rpc.FavoriteList(ctx, &req)
58 | if err != nil {
59 | return err
60 | }
61 | return err
62 | }, func(err error) error {
63 | return err
64 | })
65 |
66 | if err != nil {
67 | ctx.JSON(http.StatusInternalServerError, util.FailRequest(err.Error()))
68 | return
69 | }
70 | ctx.JSON(http.StatusOK, gin.H{
71 | "status_code": res.StatusCode,
72 | "status_msg": res.StatusMsg,
73 | "video_list": res.VideoList,
74 | })
75 | }
76 |
--------------------------------------------------------------------------------
/app/gateway/http/message.go:
--------------------------------------------------------------------------------
1 | package http
2 |
3 | import (
4 | "ByteRhythm/app/gateway/rpc"
5 | "ByteRhythm/app/gateway/wrapper"
6 | "ByteRhythm/idl/message/messagePb"
7 | "ByteRhythm/util"
8 | "github.com/afex/hystrix-go/hystrix"
9 | "github.com/gin-gonic/gin"
10 | "net/http"
11 | "strconv"
12 | )
13 |
14 | func ActionMessageHandler(ctx *gin.Context) {
15 | var req messagePb.MessageActionRequest
16 | req.Token = ctx.Query("token")
17 | toUserId, _ := strconv.Atoi(ctx.Query("to_user_id"))
18 | req.ToUserId = int64(toUserId)
19 | actionType, _ := strconv.Atoi(ctx.Query("action_type"))
20 | req.ActionType = int32(actionType)
21 | req.Content = ctx.Query("content")
22 | var res *messagePb.MessageActionResponse
23 | hystrix.ConfigureCommand("ActionMessage", wrapper.ActionMessageFuseConfig)
24 | err := hystrix.Do("ActionMessage", func() (err error) {
25 | res, err = rpc.ActionMessage(ctx, &req)
26 | return err
27 | }, func(err error) error {
28 | return err
29 | })
30 | if err != nil {
31 | ctx.JSON(http.StatusInternalServerError, util.FailRequest(err.Error()))
32 | return
33 | }
34 | ctx.JSON(http.StatusOK, gin.H{
35 | "status_code": res.StatusCode,
36 | "status_msg": res.StatusMsg,
37 | })
38 | }
39 |
40 | func ChatMessageHandler(ctx *gin.Context) {
41 | var req messagePb.MessageChatRequest
42 | req.Token = ctx.Query("token")
43 | toUserId, _ := strconv.Atoi(ctx.Query("to_user_id"))
44 | req.ToUserId = int64(toUserId)
45 | var res *messagePb.MessageChatResponse
46 | hystrix.ConfigureCommand("ChatMessage", wrapper.ChatMessageFuseConfig)
47 | err := hystrix.Do("ChatMessage", func() (err error) {
48 | res, err = rpc.ChatMessage(ctx, &req)
49 | return err
50 | }, func(err error) error {
51 | return err
52 | })
53 | if err != nil {
54 | ctx.JSON(http.StatusInternalServerError, util.FailRequest(err.Error()))
55 | return
56 | }
57 | ctx.JSON(http.StatusOK, gin.H{
58 | "status_code": res.StatusCode,
59 | "status_msg": res.StatusMsg,
60 | "message_list": res.MessageList,
61 | })
62 | }
63 |
--------------------------------------------------------------------------------
/app/gateway/http/relation.go:
--------------------------------------------------------------------------------
1 | package http
2 |
3 | import (
4 | "ByteRhythm/app/gateway/rpc"
5 | "ByteRhythm/app/gateway/wrapper"
6 | "ByteRhythm/idl/relation/relationPb"
7 | "ByteRhythm/util"
8 | "github.com/afex/hystrix-go/hystrix"
9 | "net/http"
10 | "strconv"
11 |
12 | "github.com/gin-gonic/gin"
13 | )
14 |
15 | func ActionRelationHandler(ctx *gin.Context) {
16 | var req relationPb.RelationActionRequest
17 | req.Token = ctx.Query("token")
18 | toUserId, _ := strconv.Atoi(ctx.Query("to_user_id"))
19 | req.ToUserId = int64(toUserId)
20 | actionType, _ := strconv.Atoi(ctx.Query("action_type"))
21 | req.ActionType = int32(actionType)
22 | var res *relationPb.RelationActionResponse
23 | hystrix.ConfigureCommand("ActionRelation", wrapper.ActionRelationFuseConfig)
24 | err := hystrix.Do("ActionRelation", func() (err error) {
25 | res, err = rpc.ActionRelation(ctx, &req)
26 | return err
27 | }, func(err error) error {
28 | return err
29 | })
30 | if err != nil {
31 | ctx.JSON(http.StatusInternalServerError, util.FailRequest(err.Error()))
32 | return
33 | }
34 | ctx.JSON(http.StatusOK, gin.H{
35 | "status_code": res.StatusCode,
36 | "status_msg": res.StatusMsg,
37 | })
38 | }
39 |
40 | func ListFollowRelationHandler(ctx *gin.Context) {
41 | var req relationPb.RelationFollowRequest
42 | uid, _ := strconv.Atoi(ctx.Query("user_id"))
43 | req.UserId = int64(uid)
44 | req.Token = ctx.Query("token")
45 | var res *relationPb.RelationFollowResponse
46 | hystrix.ConfigureCommand("ListFollowRelation", wrapper.ListFollowRelationFuseConfig)
47 | err := hystrix.Do("ListFollowRelation", func() (err error) {
48 | res, err = rpc.ListFollowRelation(ctx, &req)
49 | return err
50 | }, func(err error) error {
51 | return err
52 | })
53 | if err != nil {
54 | ctx.JSON(http.StatusInternalServerError, util.FailRequest(err.Error()))
55 | return
56 | }
57 | ctx.JSON(http.StatusOK, gin.H{
58 | "status_code": res.StatusCode,
59 | "status_msg": res.StatusMsg,
60 | "user_list": res.UserList,
61 | })
62 | }
63 |
64 | func ListFollowerRelationHandler(ctx *gin.Context) {
65 | var req relationPb.RelationFollowerRequest
66 | uid, _ := strconv.Atoi(ctx.Query("user_id"))
67 | req.UserId = int64(uid)
68 | req.Token = ctx.Query("token")
69 | var res *relationPb.RelationFollowerResponse
70 | hystrix.ConfigureCommand("ListFollowerRelation", wrapper.ListFollowerRelationFuseConfig)
71 | err := hystrix.Do("ListFollowerRelation", func() (err error) {
72 | res, err = rpc.ListFollowerRelation(ctx, &req)
73 | return err
74 | }, func(err error) error {
75 | return err
76 | })
77 | if err != nil {
78 | ctx.JSON(http.StatusInternalServerError, util.FailRequest(err.Error()))
79 | return
80 | }
81 | ctx.JSON(http.StatusOK, gin.H{
82 | "status_code": res.StatusCode,
83 | "status_msg": res.StatusMsg,
84 | "user_list": res.UserList,
85 | })
86 | }
87 |
88 | func ListFriendRelationHandler(ctx *gin.Context) {
89 | var req relationPb.RelationFriendRequest
90 | uid, _ := strconv.Atoi(ctx.Query("user_id"))
91 | req.UserId = int64(uid)
92 | req.Token = ctx.Query("token")
93 | var res *relationPb.RelationFriendResponse
94 | hystrix.ConfigureCommand("ListFriendRelation", wrapper.ListFriendRelationFuseConfig)
95 | err := hystrix.Do("ListFriendRelation", func() (err error) {
96 | res, err = rpc.ListFriendRelation(ctx, &req)
97 | return err
98 | }, func(err error) error {
99 | return err
100 | })
101 | if err != nil {
102 | ctx.JSON(http.StatusInternalServerError, util.FailRequest(err.Error()))
103 | return
104 | }
105 | ctx.JSON(http.StatusOK, gin.H{
106 | "status_code": res.StatusCode,
107 | "status_msg": res.StatusMsg,
108 | "user_list": res.UserList,
109 | })
110 | }
111 |
--------------------------------------------------------------------------------
/app/gateway/http/user.go:
--------------------------------------------------------------------------------
1 | package http
2 |
3 | import (
4 | "ByteRhythm/app/gateway/rpc"
5 | "ByteRhythm/app/gateway/wrapper"
6 | "ByteRhythm/idl/user/userPb"
7 | "ByteRhythm/util"
8 | "net/http"
9 | "strconv"
10 |
11 | "github.com/afex/hystrix-go/hystrix"
12 |
13 | "github.com/gin-gonic/gin"
14 | )
15 |
16 | func RegisterHandler(ctx *gin.Context) {
17 | var req userPb.UserRequest
18 | req.Username = ctx.Query("username")
19 | req.Password = ctx.Query("password")
20 | var res *userPb.UserResponse
21 | hystrix.ConfigureCommand("Register", wrapper.RegisterFuseConfig)
22 | err := hystrix.Do("Register", func() (err error) {
23 | res, err = rpc.Register(ctx, &req)
24 | return err
25 | }, func(err error) error {
26 | return err
27 | })
28 | if err != nil {
29 | ctx.JSON(http.StatusInternalServerError, util.FailRequest(err.Error()))
30 | return
31 | }
32 | ctx.JSON(http.StatusOK, gin.H{
33 | "status_code": res.StatusCode,
34 | "status_msg": res.StatusMsg,
35 | "user_id": res.UserId,
36 | "token": res.Token,
37 | })
38 | }
39 |
40 | func LoginHandler(ctx *gin.Context) {
41 | var req userPb.UserRequest
42 | req.Username = ctx.Query("username")
43 | req.Password = ctx.Query("password")
44 | var res *userPb.UserResponse
45 | hystrix.ConfigureCommand("Login", wrapper.LoginFuseConfig)
46 | err := hystrix.Do("Login", func() (err error) {
47 | res, err = rpc.Login(ctx, &req)
48 | return err
49 | }, func(err error) error {
50 | return err
51 | })
52 | if err != nil {
53 | ctx.JSON(http.StatusInternalServerError, util.FailRequest(err.Error()))
54 | return
55 | }
56 | ctx.JSON(http.StatusOK, gin.H{
57 | "status_code": res.StatusCode,
58 | "status_msg": res.StatusMsg,
59 | "user_id": res.UserId,
60 | "token": res.Token,
61 | })
62 | }
63 |
64 | func UserInfoHandler(ctx *gin.Context) {
65 | var req userPb.UserInfoRequest
66 | uid, _ := strconv.Atoi(ctx.Query("user_id"))
67 | req.UserId = int64(uid)
68 | req.Token = ctx.Query("token")
69 | var res *userPb.UserInfoResponse
70 | hystrix.ConfigureCommand("UserInfo", wrapper.UserInfoFuseConfig)
71 | err := hystrix.Do("UserInfo", func() (err error) {
72 | res, err = rpc.UserInfo(ctx, &req)
73 | return err
74 | }, func(err error) error {
75 | return err
76 | })
77 | if err != nil {
78 | ctx.JSON(http.StatusInternalServerError, util.FailRequest(err.Error()))
79 | return
80 | }
81 | ctx.JSON(http.StatusOK, gin.H{
82 | "status_code": res.StatusCode,
83 | "status_msg": res.StatusMsg,
84 | "user": res.User,
85 | })
86 | }
87 |
--------------------------------------------------------------------------------
/app/gateway/http/video.go:
--------------------------------------------------------------------------------
1 | package http
2 |
3 | import (
4 | "ByteRhythm/app/gateway/rpc"
5 | "ByteRhythm/app/gateway/wrapper"
6 | "ByteRhythm/idl/video/videoPb"
7 | "ByteRhythm/util"
8 | "bytes"
9 | "io"
10 | "net/http"
11 | "strconv"
12 |
13 | "github.com/afex/hystrix-go/hystrix"
14 |
15 | "github.com/gin-gonic/gin"
16 | )
17 |
18 | func FeedHandler(ctx *gin.Context) {
19 | var req videoPb.FeedRequest
20 | LatestTime, _ := strconv.Atoi(ctx.Query("latest_time"))
21 | req.LatestTime = int64(LatestTime)
22 | req.Token = ctx.Query("token")
23 | var res *videoPb.FeedResponse
24 | hystrix.ConfigureCommand("Feed", wrapper.FeedFuseConfig)
25 | err := hystrix.Do("Feed", func() (err error) {
26 | res, err = rpc.Feed(ctx, &req)
27 | return err
28 | }, func(err error) error {
29 | //降级处理
30 | wrapper.DefaultFeed(res)
31 | return err
32 | })
33 | if err != nil {
34 | ctx.JSON(http.StatusInternalServerError, util.FailRequest(err.Error()))
35 | return
36 | }
37 | ctx.JSON(http.StatusOK, gin.H{
38 | "status_code": res.StatusCode,
39 | "status_msg": res.StatusMsg,
40 | "video_list": res.VideoList,
41 | })
42 | }
43 |
44 | func PublishHandler(ctx *gin.Context) {
45 | var req videoPb.PublishRequest
46 | req.Title = ctx.PostForm("title")
47 | req.Token = ctx.PostForm("token")
48 | //将获得的文件转为[]byte类型
49 | data, err := ctx.FormFile("data")
50 | if err != nil {
51 | ctx.JSON(http.StatusInternalServerError, util.FailRequest(err.Error()))
52 | }
53 | file, err := data.Open()
54 | defer file.Close()
55 | if err != nil {
56 | ctx.JSON(http.StatusInternalServerError, util.FailRequest(err.Error()))
57 | }
58 | // 使用缓冲区逐块读取文件内容并写入 req.Data
59 | var buffer bytes.Buffer
60 | _, err = io.Copy(&buffer, file)
61 | if err != nil {
62 | ctx.JSON(http.StatusInternalServerError, util.FailRequest(err.Error()))
63 | return
64 | }
65 | req.Data = buffer.Bytes()
66 | var res *videoPb.PublishResponse
67 | hystrix.ConfigureCommand("Publish", wrapper.PublishFuseConfig)
68 | err = hystrix.Do("Publish", func() (err error) {
69 | res, err = rpc.Publish(ctx, &req)
70 | return err
71 | }, func(err error) error {
72 | return err
73 | })
74 | if err != nil {
75 | ctx.JSON(http.StatusInternalServerError, util.FailRequest(err.Error()))
76 | return
77 | }
78 | ctx.JSON(http.StatusOK, gin.H{
79 | "status_code": res.StatusCode,
80 | "status_msg": res.StatusMsg,
81 | })
82 | }
83 |
84 | func PublishListHandler(ctx *gin.Context) {
85 | var req videoPb.PublishListRequest
86 | uid, _ := strconv.Atoi(ctx.Query("user_id"))
87 | req.UserId = int64(uid)
88 | req.Token = ctx.Query("token")
89 | var res *videoPb.PublishListResponse
90 | hystrix.ConfigureCommand("PublishList", wrapper.PublishListFuseConfig)
91 | err := hystrix.Do("PublishList", func() (err error) {
92 | res, err = rpc.PublishList(ctx, &req)
93 | return err
94 | }, func(err error) error {
95 | return err
96 | })
97 | if err != nil {
98 | ctx.JSON(http.StatusInternalServerError, util.FailRequest(err.Error()))
99 | return
100 | }
101 | ctx.JSON(http.StatusOK, gin.H{
102 | "status_code": res.StatusCode,
103 | "status_msg": res.StatusMsg,
104 | "video_list": res.VideoList,
105 | })
106 | }
107 |
--------------------------------------------------------------------------------
/app/gateway/middleware/jaeger.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "log"
5 |
6 | "github.com/gin-gonic/gin"
7 | "github.com/opentracing/opentracing-go"
8 | "go-micro.dev/v4/metadata"
9 | )
10 |
11 | func Jaeger(tracer opentracing.Tracer) gin.HandlerFunc {
12 | return func(ctx *gin.Context) {
13 | var md = make(metadata.Metadata, 1)
14 | opName := ctx.Request.URL.Path + "-" + ctx.Request.Method
15 | parentSpan := tracer.StartSpan(opName)
16 | defer parentSpan.Finish()
17 | injectErr := tracer.Inject(parentSpan.Context(), opentracing.TextMap, opentracing.TextMapCarrier(md))
18 | if injectErr != nil {
19 | log.Fatalf("%s: Couldn't inject metadata", injectErr)
20 | }
21 | newCtx := metadata.NewContext(ctx.Request.Context(), md)
22 | ctx.Request = ctx.Request.WithContext(newCtx)
23 | ctx.Next()
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/gateway/middleware/jwt.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "ByteRhythm/util"
5 | "net/http"
6 |
7 | "github.com/gin-gonic/gin"
8 | )
9 |
10 | type Request struct {
11 | Token string `json:"token" form:"token" binding:"required"`
12 | }
13 |
14 | func JWT() gin.HandlerFunc {
15 | return func(c *gin.Context) {
16 | if c.Request.URL.Path != "/douyin/user/register" && c.Request.URL.Path != "/douyin/user/login" {
17 | var request Request
18 | c.ShouldBind(&request)
19 | token := request.Token
20 | if token != "" {
21 | if err := util.ValidateToken(token); err != nil {
22 | if c.Request.URL.Path != "/douyin/feed" {
23 | c.JSON(http.StatusForbidden, util.FailRequest("token验证失败,请重新登录"))
24 | c.Abort()
25 | }
26 | }
27 | }
28 | }
29 | c.Next()
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/gateway/router/router.go:
--------------------------------------------------------------------------------
1 | package router
2 |
3 | import (
4 | "ByteRhythm/app/gateway/http"
5 | "ByteRhythm/app/gateway/middleware"
6 | "ByteRhythm/app/gateway/wrapper"
7 | "ByteRhythm/config"
8 | "fmt"
9 |
10 | "github.com/gin-contrib/cors"
11 | "github.com/gin-gonic/gin"
12 | "go-micro.dev/v4/logger"
13 | )
14 |
15 | func NewRouter() *gin.Engine {
16 | config.Init()
17 | r := gin.Default()
18 | jaeger, closer, err := wrapper.InitJaeger("HttpService", fmt.Sprintf("%s:%s", config.JaegerHost, config.JaegerPort))
19 | defer closer.Close()
20 | if err != nil {
21 | logger.Info("HttpService init jaeger failed, err:", err)
22 | }
23 | r.Use(
24 | middleware.JWT(),
25 | cors.Default(),
26 | middleware.Jaeger(jaeger),
27 | )
28 | v1 := r.Group("/douyin")
29 | {
30 | v1.GET("/feed", http.FeedHandler)
31 |
32 | v2 := v1.Group("/user")
33 | {
34 | v2.POST("/register/", http.RegisterHandler)
35 | v2.POST("/login/", http.LoginHandler)
36 | v2.GET("/", http.UserInfoHandler)
37 | }
38 |
39 | v2 = v1.Group("/publish")
40 | {
41 | v2.POST("/action/", http.PublishHandler)
42 | v2.GET("/list/", http.PublishListHandler)
43 | }
44 |
45 | v2 = v1.Group("/relation")
46 | {
47 | v2.POST("/action/", http.ActionRelationHandler)
48 | v2.GET("/follow/list/", http.ListFollowRelationHandler)
49 | v2.GET("/follower/list/", http.ListFollowerRelationHandler)
50 | v2.GET("/friend/list/", http.ListFriendRelationHandler)
51 | }
52 |
53 | v2 = v1.Group("/message")
54 | {
55 | v2.POST("/action/", http.ActionMessageHandler)
56 | v2.GET("/chat/", http.ChatMessageHandler)
57 | }
58 |
59 | v2 = v1.Group("/comment")
60 | {
61 | v2.POST("/action/", http.CommentActionHandler)
62 | v2.GET("/list/", http.CommentListHandler)
63 | }
64 |
65 | v2 = v1.Group("/favorite")
66 | {
67 | v2.POST("/action/", http.FavoriteActionHandler)
68 | v2.GET("/list/", http.FavoriteListHandler)
69 | }
70 |
71 | }
72 | return r
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/app/gateway/rpc/comment.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "ByteRhythm/idl/comment/commentPb"
5 | "context"
6 | )
7 |
8 | func CommentAction(ctx context.Context, req *commentPb.CommentActionRequest) (res *commentPb.CommentActionResponse, err error) {
9 | res, err = CommentService.CommentAction(ctx, req)
10 | if err != nil {
11 | return
12 | }
13 | return
14 | }
15 |
16 | func CommentList(ctx context.Context, req *commentPb.CommentListRequest) (res *commentPb.CommentListResponse, err error) {
17 | res, err = CommentService.CommentList(ctx, req)
18 | if err != nil {
19 | return
20 | }
21 | return
22 | }
23 |
--------------------------------------------------------------------------------
/app/gateway/rpc/favorite.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "ByteRhythm/idl/favorite/favoritePb"
5 | "context"
6 | )
7 |
8 | func FavoriteAction(ctx context.Context, req *favoritePb.FavoriteActionRequest) (res *favoritePb.FavoriteActionResponse, err error) {
9 | res, err = FavoriteService.FavoriteAction(ctx, req)
10 | if err != nil {
11 | return
12 | }
13 | return
14 | }
15 |
16 | func FavoriteList(ctx context.Context, req *favoritePb.FavoriteListRequest) (res *favoritePb.FavoriteListResponse, err error) {
17 | res, err = FavoriteService.FavoriteList(ctx, req)
18 | if err != nil {
19 | return
20 | }
21 | return
22 | }
23 |
--------------------------------------------------------------------------------
/app/gateway/rpc/init.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "ByteRhythm/idl/comment/commentPb"
5 | "ByteRhythm/idl/favorite/favoritePb"
6 | "ByteRhythm/idl/message/messagePb"
7 | "ByteRhythm/idl/relation/relationPb"
8 | "ByteRhythm/idl/user/userPb"
9 | "ByteRhythm/idl/video/videoPb"
10 |
11 | "go-micro.dev/v4"
12 | )
13 |
14 | var (
15 | UserService userPb.UserService
16 | VideoService videoPb.VideoService
17 | MessageService messagePb.MessageService
18 | RelationService relationPb.RelationService
19 | CommentService commentPb.CommentService
20 | FavoriteService favoritePb.FavoriteService
21 | )
22 |
23 | func InitRPC() {
24 | UserMicroService := micro.NewService(micro.Name("UserService.client"))
25 | userService := userPb.NewUserService("UserService", UserMicroService.Client())
26 | UserService = userService
27 |
28 | VideoMicroService := micro.NewService(micro.Name("VideoService.client"))
29 | videoService := videoPb.NewVideoService("VideoService", VideoMicroService.Client())
30 | VideoService = videoService
31 |
32 | MessageMicroService := micro.NewService(micro.Name("MessageService.client"))
33 | messageService := messagePb.NewMessageService("MessageService", MessageMicroService.Client())
34 | MessageService = messageService
35 |
36 | RelationMicroService := micro.NewService(micro.Name("RelationService.client"))
37 | relationService := relationPb.NewRelationService("RelationService", RelationMicroService.Client())
38 | RelationService = relationService
39 |
40 | CommentMicroService := micro.NewService(micro.Name("CommentService.client"))
41 | commentService := commentPb.NewCommentService("CommentService", CommentMicroService.Client())
42 | CommentService = commentService
43 |
44 | FavoriteMicroService := micro.NewService(micro.Name("FavoriteService.client"))
45 | favoriteService := favoritePb.NewFavoriteService("FavoriteService", FavoriteMicroService.Client())
46 | FavoriteService = favoriteService
47 | }
48 |
--------------------------------------------------------------------------------
/app/gateway/rpc/message.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "ByteRhythm/idl/message/messagePb"
5 | "context"
6 | )
7 |
8 | func ActionMessage(ctx context.Context, req *messagePb.MessageActionRequest) (res *messagePb.MessageActionResponse, err error) {
9 | res, err = MessageService.ActionMessage(ctx, req)
10 | if err != nil {
11 | return
12 | }
13 | return
14 | }
15 |
16 | func ChatMessage(ctx context.Context, req *messagePb.MessageChatRequest) (res *messagePb.MessageChatResponse, err error) {
17 | res, err = MessageService.ChatMessage(ctx, req)
18 | if err != nil {
19 | return
20 | }
21 | return
22 | }
23 |
--------------------------------------------------------------------------------
/app/gateway/rpc/relation.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "ByteRhythm/idl/relation/relationPb"
5 | "context"
6 | )
7 |
8 | func ActionRelation(ctx context.Context, req *relationPb.RelationActionRequest) (res *relationPb.RelationActionResponse, err error) {
9 | res, err = RelationService.ActionRelation(ctx, req)
10 | if err != nil {
11 | return
12 | }
13 | return
14 | }
15 |
16 | func ListFollowRelation(ctx context.Context, req *relationPb.RelationFollowRequest) (res *relationPb.RelationFollowResponse, err error) {
17 | res, err = RelationService.ListFollowRelation(ctx, req)
18 | if err != nil {
19 | return
20 | }
21 | return
22 | }
23 |
24 | func ListFollowerRelation(ctx context.Context, req *relationPb.RelationFollowerRequest) (res *relationPb.RelationFollowerResponse, err error) {
25 | res, err = RelationService.ListFollowerRelation(ctx, req)
26 | if err != nil {
27 | return
28 | }
29 | return
30 | }
31 |
32 | func ListFriendRelation(ctx context.Context, req *relationPb.RelationFriendRequest) (res *relationPb.RelationFriendResponse, err error) {
33 | res, err = RelationService.ListFriendRelation(ctx, req)
34 | if err != nil {
35 | return
36 | }
37 | return
38 | }
39 |
--------------------------------------------------------------------------------
/app/gateway/rpc/user.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "ByteRhythm/idl/user/userPb"
5 | "context"
6 | )
7 |
8 | func Login(ctx context.Context, req *userPb.UserRequest) (res *userPb.UserResponse, err error) {
9 | res, err = UserService.Login(ctx, req)
10 | if err != nil {
11 | return
12 | }
13 | return
14 | }
15 |
16 | func Register(ctx context.Context, req *userPb.UserRequest) (res *userPb.UserResponse, err error) {
17 | res, err = UserService.Register(ctx, req)
18 | if err != nil {
19 | return
20 | }
21 | return
22 | }
23 |
24 | func UserInfo(ctx context.Context, req *userPb.UserInfoRequest) (res *userPb.UserInfoResponse, err error) {
25 | res, err = UserService.UserInfo(ctx, req)
26 | if err != nil {
27 | return
28 | }
29 | return
30 | }
31 |
--------------------------------------------------------------------------------
/app/gateway/rpc/video.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "ByteRhythm/idl/video/videoPb"
5 | "context"
6 | )
7 |
8 | func Feed(ctx context.Context, req *videoPb.FeedRequest) (res *videoPb.FeedResponse, err error) {
9 | res, err = VideoService.Feed(ctx, req)
10 | if err != nil {
11 | return
12 | }
13 | return
14 |
15 | }
16 |
17 | func Publish(ctx context.Context, req *videoPb.PublishRequest) (res *videoPb.PublishResponse, err error) {
18 | res, err = VideoService.Publish(ctx, req)
19 | if err != nil {
20 | return
21 | }
22 | return
23 | }
24 |
25 | func PublishList(ctx context.Context, req *videoPb.PublishListRequest) (res *videoPb.PublishListResponse, err error) {
26 | res, err = VideoService.PublishList(ctx, req)
27 | if err != nil {
28 | return
29 | }
30 | return
31 | }
32 |
--------------------------------------------------------------------------------
/app/gateway/wrapper/comment.go:
--------------------------------------------------------------------------------
1 | package wrapper
2 |
3 | import "github.com/afex/hystrix-go/hystrix"
4 |
5 | var CommentActionFuseConfig = hystrix.CommandConfig{
6 | Timeout: 1000,
7 | RequestVolumeThreshold: 5000, // 10秒内的请求量,默认值是20,如果超过20那么就判断是否熔断
8 | ErrorPercentThreshold: 50, // 错误百分比,当错误超过百分比时,直接进行降级处理,直至熔断器再次 开启,默认50%
9 | SleepWindow: 5000, // 过多长时间,熔断器再次检测是否开启,单位毫秒ms(默认5秒)
10 | MaxConcurrentRequests: 50000,
11 | }
12 |
13 | var CommentListFuseConfig = hystrix.CommandConfig{
14 | Timeout: 1000,
15 | RequestVolumeThreshold: 5000, // 熔断器请求阈值,默认20,意思是有20个请求才能进行错误百分比计算
16 | ErrorPercentThreshold: 50, // 错误百分比,当错误超过百分比时,直接进行降级处理,直至熔断器再次 开启,默认50%
17 | SleepWindow: 5000, // 过多长时间,熔断器再次检测是否开启,单位毫秒ms(默认5秒)
18 | MaxConcurrentRequests: 50000,
19 | }
20 |
--------------------------------------------------------------------------------
/app/gateway/wrapper/favorite.go:
--------------------------------------------------------------------------------
1 | package wrapper
2 |
3 | import "github.com/afex/hystrix-go/hystrix"
4 |
5 | var FavoriteActionFuseConfig = hystrix.CommandConfig{
6 | Timeout: 1000,
7 | RequestVolumeThreshold: 5000, // 10秒内的请求量,默认值是20,如果超过20那么就判断是否熔断
8 | ErrorPercentThreshold: 50, // 错误百分比,当错误超过百分比时,直接进行降级处理,直至熔断器再次 开启,默认50%
9 | SleepWindow: 5000, // 过多长时间,熔断器再次检测是否开启,单位毫秒ms(默认5秒)
10 | MaxConcurrentRequests: 50000,
11 | }
12 |
13 | var FavoriteListFuseConfig = hystrix.CommandConfig{
14 | Timeout: 1000,
15 | RequestVolumeThreshold: 5000, // 熔断器请求阈值,默认20,意思是有20个请求才能进行错误百分比计算
16 | ErrorPercentThreshold: 50, // 错误百分比,当错误超过百分比时,直接进行降级处理,直至熔断器再次 开启,默认50%
17 | SleepWindow: 5000, // 过多长时间,熔断器再次检测是否开启,单位毫秒ms(默认5秒)
18 | MaxConcurrentRequests: 50000,
19 | }
20 |
--------------------------------------------------------------------------------
/app/gateway/wrapper/jaeger.go:
--------------------------------------------------------------------------------
1 | package wrapper
2 |
3 | import (
4 | "fmt"
5 | "io"
6 | "time"
7 |
8 | "github.com/opentracing/opentracing-go"
9 | "github.com/uber/jaeger-client-go"
10 | "github.com/uber/jaeger-client-go/config"
11 | )
12 |
13 | var Tracer opentracing.Tracer
14 |
15 | func InitJaeger(serviceName string, jaegerHostPort string) (opentracing.Tracer, io.Closer, error) {
16 | cfg := config.Configuration{
17 | ServiceName: serviceName,
18 | Sampler: &config.SamplerConfig{
19 | Type: jaeger.SamplerTypeConst,
20 | Param: 1,
21 | },
22 | Reporter: &config.ReporterConfig{
23 | LogSpans: true,
24 | BufferFlushInterval: 1 * time.Second,
25 | LocalAgentHostPort: jaegerHostPort,
26 | },
27 | }
28 | var closer io.Closer
29 | var err error
30 | Tracer, closer, err = cfg.NewTracer(
31 | config.Logger(jaeger.StdLogger),
32 | )
33 | if err != nil {
34 | panic(fmt.Sprintf("ERROR: cannot init Jaeger: %v\n", err))
35 | }
36 | opentracing.SetGlobalTracer(Tracer)
37 | return Tracer, closer, err
38 | }
39 |
--------------------------------------------------------------------------------
/app/gateway/wrapper/message.go:
--------------------------------------------------------------------------------
1 | package wrapper
2 |
3 | import "github.com/afex/hystrix-go/hystrix"
4 |
5 | var ActionMessageFuseConfig = hystrix.CommandConfig{
6 | Timeout: 1000,
7 | RequestVolumeThreshold: 5000, // 10秒内的请求量,默认值是20,如果超过20那么就判断是否熔断
8 | ErrorPercentThreshold: 50, // 错误百分比,当错误超过百分比时,直接进行降级处理,直至熔断器再次 开启,默认50%
9 | SleepWindow: 5000, // 过多长时间,熔断器再次检测是否开启,单位毫秒ms(默认5秒)
10 | MaxConcurrentRequests: 50000,
11 | }
12 |
13 | var ChatMessageFuseConfig = hystrix.CommandConfig{
14 | Timeout: 1000,
15 | RequestVolumeThreshold: 5000, // 熔断器请求阈值,默认20,意思是有20个请求才能进行错误百分比计算
16 | ErrorPercentThreshold: 50, // 错误百分比,当错误超过百分比时,直接进行降级处理,直至熔断器再次 开启,默认50%
17 | SleepWindow: 5000, // 过多长时间,熔断器再次检测是否开启,单位毫秒ms(默认5秒)
18 | MaxConcurrentRequests: 50000,
19 | }
20 |
--------------------------------------------------------------------------------
/app/gateway/wrapper/relation.go:
--------------------------------------------------------------------------------
1 | package wrapper
2 |
3 | import "github.com/afex/hystrix-go/hystrix"
4 |
5 | var ActionRelationFuseConfig = hystrix.CommandConfig{
6 | Timeout: 1000,
7 | RequestVolumeThreshold: 5000, // 10秒内的请求量,默认值是20,如果超过20那么就判断是否熔断
8 | ErrorPercentThreshold: 50, // 错误百分比,当错误超过百分比时,直接进行降级处理,直至熔断器再次 开启,默认50%
9 | SleepWindow: 5000, // 过多长时间,熔断器再次检测是否开启,单位毫秒ms(默认5秒)
10 | MaxConcurrentRequests: 50000,
11 | }
12 |
13 | var ListFollowRelationFuseConfig = hystrix.CommandConfig{
14 | Timeout: 1000,
15 | RequestVolumeThreshold: 5000, // 熔断器请求阈值,默认20,意思是有20个请求才能进行错误百分比计算
16 | ErrorPercentThreshold: 50, // 错误百分比,当错误超过百分比时,直接进行降级处理,直至熔断器再次 开启,默认50%
17 | SleepWindow: 5000, // 过多长时间,熔断器再次检测是否开启,单位毫秒ms(默认5秒)
18 | MaxConcurrentRequests: 50000,
19 | }
20 |
21 | var ListFollowerRelationFuseConfig = hystrix.CommandConfig{
22 | Timeout: 1000,
23 | RequestVolumeThreshold: 5000, // 熔断器请求阈值,默认20,意思是有20个请求才能进行错误百分比计算
24 | ErrorPercentThreshold: 50, // 错误百分比,当错误超过百分比时,直接进行降级处理,直至熔断器再次 开启,默认50%
25 | SleepWindow: 5000, // 过多长时间,熔断器再次检测是否开启,单位毫秒ms(默认5秒)
26 | MaxConcurrentRequests: 50000,
27 | }
28 |
29 | var ListFriendRelationFuseConfig = hystrix.CommandConfig{
30 | Timeout: 1000,
31 | RequestVolumeThreshold: 5000, // 熔断器请求阈值,默认20,意思是有20个请求才能进行错误百分比计算
32 | ErrorPercentThreshold: 50, // 错误百分比,当错误超过百分比时,直接进行降级处理,直至熔断器再次 开启,默认50%
33 | SleepWindow: 5000, // 过多长时间,熔断器再次检测是否开启,单位毫秒ms(默认5秒)
34 | MaxConcurrentRequests: 50000,
35 | }
36 |
--------------------------------------------------------------------------------
/app/gateway/wrapper/user.go:
--------------------------------------------------------------------------------
1 | package wrapper
2 |
3 | import "github.com/afex/hystrix-go/hystrix"
4 |
5 | var RegisterFuseConfig = hystrix.CommandConfig{
6 | Timeout: 1000,
7 | RequestVolumeThreshold: 5000, // 10秒内的请求量,默认值是20,如果超过20那么就判断是否熔断
8 | ErrorPercentThreshold: 50, // 错误百分比,当错误超过百分比时,直接进行降级处理,直至熔断器再次 开启,默认50%
9 | SleepWindow: 5000, // 过多长时间,熔断器再次检测是否开启,单位毫秒ms(默认5秒)
10 | MaxConcurrentRequests: 50000,
11 | }
12 |
13 | var LoginFuseConfig = hystrix.CommandConfig{
14 | Timeout: 1000,
15 | RequestVolumeThreshold: 5000, // 熔断器请求阈值,默认20,意思是有20个请求才能进行错误百分比计算
16 | ErrorPercentThreshold: 50, // 错误百分比,当错误超过百分比时,直接进行降级处理,直至熔断器再次 开启,默认50%
17 | SleepWindow: 5000, // 过多长时间,熔断器再次检测是否开启,单位毫秒ms(默认5秒)
18 | MaxConcurrentRequests: 50000,
19 | }
20 | var UserInfoFuseConfig = hystrix.CommandConfig{
21 | Timeout: 1000,
22 | RequestVolumeThreshold: 5000, // 熔断器请求阈值,默认20,意思是有20个请求才能进行错误百分比计算
23 | ErrorPercentThreshold: 50, // 错误百分比,当错误超过百分比时,直接进行降级处理,直至熔断器再次 开启,默认50%
24 | SleepWindow: 5000, // 过多长时间,熔断器再次检测是否开启,单位毫秒ms(默认5秒)
25 | MaxConcurrentRequests: 50000,
26 | }
27 |
--------------------------------------------------------------------------------
/app/gateway/wrapper/video.go:
--------------------------------------------------------------------------------
1 | package wrapper
2 |
3 | import (
4 | "ByteRhythm/idl/video/videoPb"
5 | "strconv"
6 |
7 | "github.com/afex/hystrix-go/hystrix"
8 | )
9 |
10 | var FeedFuseConfig = hystrix.CommandConfig{
11 | Timeout: 1000,
12 | RequestVolumeThreshold: 5000, // 熔断器请求阈值,默认20,意思是有20个请求才能进行错误百分比计算
13 | ErrorPercentThreshold: 50, // 错误百分比,当错误超过百分比时,直接进行降级处理,直至熔断器再次 开启,默认50%
14 | SleepWindow: 5000, // 过多长时间,熔断器再次检测是否开启,单位毫秒ms(默认5秒)
15 | MaxConcurrentRequests: 50000,
16 | }
17 |
18 | var PublishFuseConfig = hystrix.CommandConfig{
19 | Timeout: 5000,
20 | RequestVolumeThreshold: 5000, // 熔断器请求阈值,默认20,意思是有20个请求才能进行错误百分比计算
21 | ErrorPercentThreshold: 50, // 错误百分比,当错误超过百分比时,直接进行降级处理,直至熔断器再次 开启,默认50%
22 | SleepWindow: 5000, // 过多长时间,熔断器再次检测是否开启,单位毫秒ms(默认5秒)
23 | MaxConcurrentRequests: 50000,
24 | }
25 | var PublishListFuseConfig = hystrix.CommandConfig{
26 | Timeout: 1000,
27 | RequestVolumeThreshold: 5000, // 熔断器请求阈值,默认20,意思是有20个请求才能进行错误百分比计算
28 | ErrorPercentThreshold: 50, // 错误百分比,当错误超过百分比时,直接进行降级处理,直至熔断器再次 开启,默认50%
29 | SleepWindow: 5000, // 过多长时间,熔断器再次检测是否开启,单位毫秒ms(默认5秒)
30 | MaxConcurrentRequests: 50000,
31 | }
32 |
33 | func NewVideo(vid int64, title string) *videoPb.Video {
34 | return &videoPb.Video{
35 | Id: vid,
36 | Author: nil,
37 | Title: title,
38 | PlayUrl: "",
39 | CoverUrl: "",
40 | FavoriteCount: 0,
41 | CommentCount: 0,
42 | IsFavorite: false,
43 | }
44 | }
45 |
46 | // DefaultFeed 降级函数
47 | func DefaultFeed(res interface{}) {
48 | models := make([]*videoPb.Video, 0)
49 | for i := 0; i < 30; i++ {
50 | models = append(models, NewVideo(int64(i), "降级视频流"+strconv.Itoa(i)))
51 | }
52 | result := res.(*videoPb.FeedResponse)
53 | result.VideoList = models
54 | }
55 |
--------------------------------------------------------------------------------
/app/message/cmd/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "ByteRhythm/app/gateway/wrapper"
5 | "ByteRhythm/app/message/dao"
6 | "ByteRhythm/app/message/service"
7 | "ByteRhythm/config"
8 | "ByteRhythm/idl/message/messagePb"
9 | "fmt"
10 | "os"
11 |
12 | "github.com/go-micro/plugins/v4/registry/etcd"
13 | ratelimit "github.com/go-micro/plugins/v4/wrapper/ratelimiter/uber"
14 | "github.com/go-micro/plugins/v4/wrapper/select/roundrobin"
15 | "github.com/go-micro/plugins/v4/wrapper/trace/opentracing"
16 |
17 | "go-micro.dev/v4"
18 | "go-micro.dev/v4/registry"
19 | )
20 |
21 | func main() {
22 | config.Init()
23 | dao.InitMySQL()
24 | dao.InitRedis()
25 |
26 | defer dao.RedisClient.Close()
27 |
28 | // etcd注册件
29 | etcdReg := etcd.NewRegistry(
30 | registry.Addrs(fmt.Sprintf("%s:%s", config.EtcdHost, config.EtcdPort)),
31 | )
32 | // 链路追踪
33 | tracer, closer, err := wrapper.InitJaeger("MessageService", fmt.Sprintf("%s:%s", config.JaegerHost, config.JaegerPort))
34 | if err != nil {
35 | fmt.Printf("new tracer err: %+v\n", err)
36 | os.Exit(-1)
37 | }
38 | defer closer.Close()
39 | // 得到一个微服务实例
40 | microService := micro.NewService(
41 | micro.Name("MessageService"), // 微服务名字
42 | micro.Address(config.MessageServiceAddress),
43 | micro.Registry(etcdReg), // etcd注册件
44 | micro.WrapHandler(ratelimit.NewHandlerWrapper(50000)), //限流处理
45 | micro.WrapClient(roundrobin.NewClientWrapper()), // 负载均衡
46 | micro.WrapHandler(opentracing.NewHandlerWrapper(tracer)), // 链路追踪
47 | micro.WrapClient(opentracing.NewClientWrapper(tracer)), // 链路追踪
48 | )
49 |
50 | // 结构命令行参数,初始化
51 | microService.Init()
52 | // 服务注册
53 | _ = messagePb.RegisterMessageServiceHandler(microService.Server(), service.GetMessageSrv())
54 | // 启动微服务
55 | _ = microService.Run()
56 | }
57 |
--------------------------------------------------------------------------------
/app/message/dao/init.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "time"
7 |
8 | "github.com/go-redis/redis/v8"
9 |
10 | "github.com/gin-gonic/gin"
11 | "gorm.io/driver/mysql"
12 | "gorm.io/gorm"
13 | "gorm.io/gorm/logger"
14 | "gorm.io/gorm/schema"
15 |
16 | "ByteRhythm/config"
17 | )
18 |
19 | var db *gorm.DB
20 | var RedisClient *redis.Client
21 |
22 | func InitMySQL() {
23 | host := config.DBHost
24 | port := config.DBPort
25 | database := config.DBName
26 | username := config.DBUser
27 | password := config.DBPassWord
28 | charset := config.Charset
29 | dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&parseTime=True&loc=Local", username, password, host, port, database, charset)
30 | err := Database(dsn)
31 | if err != nil {
32 | fmt.Println(err)
33 | }
34 | }
35 |
36 | func InitRedis() {
37 | // 初始化 Redis 客户端
38 | host := config.RedisHost
39 | port := config.RedisPort
40 | RedisClient = redis.NewClient(&redis.Options{
41 | Addr: host + ":" + port, // Redis 服务器地址
42 | Password: "", // Redis 访问密码(如果有的话)
43 | DB: 0, // Redis 数据库索引
44 | })
45 | }
46 |
47 | func Database(connString string) error {
48 | var ormLogger logger.Interface
49 | if gin.Mode() == "debug" {
50 | ormLogger = logger.Default.LogMode(logger.Info)
51 | } else {
52 | ormLogger = logger.Default
53 | }
54 | DB, err := gorm.Open(mysql.New(mysql.Config{
55 | DSN: connString, // DSN data source name
56 | DefaultStringSize: 256, // string 类型字段的默认长度
57 | DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
58 | DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
59 | DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
60 | SkipInitializeWithVersion: false, // 根据版本自动配置
61 | }), &gorm.Config{
62 | Logger: ormLogger,
63 | NamingStrategy: schema.NamingStrategy{
64 | SingularTable: true,
65 | },
66 | })
67 | if err != nil {
68 | panic(err)
69 | }
70 | sqlDB, _ := DB.DB()
71 | sqlDB.SetMaxIdleConns(20)
72 | sqlDB.SetMaxOpenConns(100)
73 | sqlDB.SetConnMaxLifetime(time.Second * 30)
74 | db = DB
75 | migration()
76 | return err
77 | }
78 |
79 | func NewDBClient(ctx context.Context) *gorm.DB {
80 | DB := db
81 | return DB.WithContext(ctx)
82 | }
83 |
--------------------------------------------------------------------------------
/app/message/dao/message.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "ByteRhythm/model"
5 | "context"
6 |
7 | "gorm.io/gorm"
8 | )
9 |
10 | type MessageDao struct {
11 | *gorm.DB
12 | }
13 |
14 | func NewMessageDao(ctx context.Context) *MessageDao {
15 | if ctx == nil {
16 | ctx = context.Background()
17 | }
18 | return &MessageDao{NewDBClient(ctx)}
19 | }
20 |
21 | func (m *MessageDao) CreateMessage(message *model.Message) (id int64, err error) {
22 | err = m.Model(&model.Message{}).Create(&message).Error
23 | if err != nil {
24 | return
25 | }
26 | return int64(message.ID), nil
27 | }
28 |
29 | func (m *MessageDao) FindAllMessages(fromUserID int64, toUserID int64) (messages []*model.Message, err error) {
30 | err = m.Model(&model.Message{}).Where("(from_user_id = ? AND to_user_id = ?) OR (from_user_id = ? AND to_user_id = ?)", fromUserID, toUserID, toUserID, fromUserID).Order("id ASC").Find(&messages).Error
31 | if err != nil {
32 | return
33 | }
34 | return messages, nil
35 | }
36 |
--------------------------------------------------------------------------------
/app/message/dao/migration.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "ByteRhythm/model"
5 | "log"
6 | )
7 |
8 | func migration() {
9 | err := db.Set(`gorm:table_options`, "charset=utf8mb4").
10 | AutoMigrate(&model.Message{}, &model.User{})
11 | if err != nil {
12 | log.Fatal(err)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/message/service/message.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "ByteRhythm/app/message/dao"
5 | "ByteRhythm/idl/message/messagePb"
6 | "ByteRhythm/model"
7 | "ByteRhythm/util"
8 | "context"
9 | "encoding/json"
10 | "strconv"
11 | "sync"
12 | "time"
13 |
14 | "github.com/go-redis/redis/v8"
15 | )
16 |
17 | type MessageSrv struct {
18 | }
19 |
20 | var MessageSrvIns *MessageSrv
21 | var MessageSrvOnce sync.Once
22 |
23 | // GetMessageSrv 懒汉式的单例模式 lazy-loading --> 懒汉式
24 | func GetMessageSrv() *MessageSrv {
25 | MessageSrvOnce.Do(func() {
26 | MessageSrvIns = &MessageSrv{}
27 | })
28 | return MessageSrvIns
29 | }
30 |
31 | func (m MessageSrv) ChatMessage(ctx context.Context, req *messagePb.MessageChatRequest, res *messagePb.MessageChatResponse) error {
32 | token := req.Token
33 | toUserId := req.ToUserId
34 |
35 | fromUserId, err := util.GetUserIdFromToken(token)
36 | if err != nil {
37 | return nil
38 | }
39 |
40 | if int64(fromUserId) == toUserId {
41 | MessageChatResponseData(res, 1, "不能查看与自己的聊天记录!")
42 | return nil
43 | }
44 |
45 | // 构建 Redis 键
46 | var redisKey string
47 | if strconv.Itoa(fromUserId) < strconv.Itoa(int(toUserId)) {
48 | redisKey = "chat_messages:" + strconv.Itoa(fromUserId) + ":" + strconv.Itoa(int(toUserId))
49 | } else {
50 | redisKey = "chat_messages:" + strconv.Itoa(int(toUserId)) + ":" + strconv.Itoa(fromUserId)
51 | }
52 |
53 | // 尝试从 Redis 缓存中获取数据
54 | redisResult, err := dao.RedisClient.Get(ctx, redisKey).Result()
55 | if err != nil && err != redis.Nil {
56 | MessageChatResponseData(res, 1, "获取聊天记录失败!")
57 | return err
58 | }
59 |
60 | if redisResult != "" {
61 | // 如果缓存中存在数据,则解码并直接返回缓存数据
62 | err = json.Unmarshal([]byte(redisResult), &res.MessageList)
63 | if err != nil {
64 | MessageChatResponseData(res, 1, "获取聊天记录失败!")
65 | return err
66 | }
67 |
68 | MessageChatResponseData(res, 0, "获取聊天记录成功!")
69 | return nil
70 | }
71 |
72 | // 缓存中不存在数据,则从数据库获取聊天记录
73 | messages, err := dao.NewMessageDao(ctx).FindAllMessages(int64(fromUserId), toUserId)
74 | if err != nil {
75 | MessageChatResponseData(res, 1, "获取聊天记录失败!")
76 | return err
77 | }
78 |
79 | for _, message := range messages {
80 | res.MessageList = append(res.MessageList, BuildMessagePbModel(message))
81 | }
82 |
83 | // 将结果存入 Redis 缓存
84 | jsonBytes, err := json.Marshal(&res.MessageList)
85 | if err != nil {
86 | MessageChatResponseData(res, 1, "获取聊天记录失败!")
87 | return err
88 | }
89 |
90 | err = dao.RedisClient.Set(ctx, redisKey, string(jsonBytes), time.Hour).Err()
91 | if err != nil {
92 | MessageChatResponseData(res, 1, "获取聊天记录失败!")
93 | return err
94 | }
95 |
96 | MessageChatResponseData(res, 0, "获取聊天记录成功!")
97 | return nil
98 | }
99 |
100 | func (m MessageSrv) ActionMessage(ctx context.Context, req *messagePb.MessageActionRequest, res *messagePb.MessageActionResponse) error {
101 | token := req.Token
102 | fromUserID, err := util.GetUserIdFromToken(token)
103 | if err != nil {
104 | return err
105 | }
106 |
107 | actionType := req.ActionType
108 | if actionType == 1 {
109 | toUserID := req.ToUserId
110 | content := req.Content
111 |
112 | message := BuildMessageModel(fromUserID, int(toUserID), content)
113 | id, err := dao.NewMessageDao(ctx).CreateMessage(&message)
114 | if err != nil {
115 | MessageActionResponseData(res, 1, "发送消息失败!")
116 | return err
117 | }
118 | message.ID = uint(id)
119 |
120 | // 构建 Redis 键
121 | var redisKey string
122 | if strconv.Itoa(fromUserID) < strconv.Itoa(int(toUserID)) {
123 | redisKey = "chat_messages:" + strconv.Itoa(fromUserID) + ":" + strconv.Itoa(int(toUserID))
124 | } else {
125 | redisKey = "chat_messages:" + strconv.Itoa(int(toUserID)) + ":" + strconv.Itoa(fromUserID)
126 | }
127 |
128 | // 尝试从 Redis 缓存中获取数据
129 | redisResult, err := dao.RedisClient.Get(ctx, redisKey).Result()
130 | if err != nil && err != redis.Nil {
131 | MessageActionResponseData(res, 1, "操作失败!")
132 | return err
133 | }
134 |
135 | var messageList []*messagePb.Message
136 | // 如果缓存中存在数据,则解码并合并到 messageList 中
137 | if redisResult != "" {
138 | // 解码 Redis 结果
139 | err = json.Unmarshal([]byte(redisResult), &messageList)
140 | if err != nil {
141 | MessageActionResponseData(res, 1, "操作失败!")
142 | return err
143 | }
144 | messageList = append(messageList, BuildMessagePbModel(&message))
145 | } else {
146 | // 如果缓存中不存在数据,则创建新的 messageList 切片
147 | messageList = []*messagePb.Message{}
148 | }
149 |
150 | // 将结果存入 Redis 缓存
151 | jsonBytes, err := json.Marshal(&messageList)
152 | if err != nil {
153 | MessageActionResponseData(res, 1, "操作失败!")
154 | return err
155 | }
156 |
157 | err = dao.RedisClient.Set(ctx, redisKey, string(jsonBytes), time.Hour).Err()
158 | if err != nil {
159 | MessageActionResponseData(res, 1, "操作失败!")
160 | return err
161 | }
162 |
163 | MessageActionResponseData(res, 0, "发送消息成功!")
164 | return nil
165 |
166 | } else {
167 | MessageActionResponseData(res, 1, "非发送消息操作!")
168 | return nil
169 | }
170 | }
171 |
172 | func MessageChatResponseData(res *messagePb.MessageChatResponse, StatusCode int32, StatusMsg string, params ...interface{}) {
173 | res.StatusCode = StatusCode
174 | res.StatusMsg = StatusMsg
175 | if len(params) != 0 {
176 | res.MessageList = params[0].([]*messagePb.Message)
177 | }
178 | }
179 |
180 | func MessageActionResponseData(res *messagePb.MessageActionResponse, StatusCode int32, StatusMsg string) {
181 | res.StatusCode = StatusCode
182 | res.StatusMsg = StatusMsg
183 | }
184 |
185 | func BuildMessageModel(fromUserID int, toUserID int, content string) model.Message {
186 | return model.Message{
187 | FromUserID: fromUserID,
188 | ToUserID: toUserID,
189 | Content: content,
190 | CreatedAt: time.Now(),
191 | }
192 | }
193 |
194 | func BuildMessagePbModel(message *model.Message) *messagePb.Message {
195 | return &messagePb.Message{
196 | Id: int64(message.ID),
197 | FromUserId: int64(message.FromUserID),
198 | ToUserId: int64(message.ToUserID),
199 | Content: message.Content,
200 | //CreateTime: message.CreatedAt.Format("2006-01-02 15:04:05"),
201 | //CreateTime: time.ParseInLocation("2006-01-02 15:04:05", message.CreatedAt, time.Local),
202 | CreateTime: "0",
203 | }
204 | }
205 |
--------------------------------------------------------------------------------
/app/relation/cmd/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "ByteRhythm/app/gateway/wrapper"
5 | "ByteRhythm/app/relation/dao"
6 | "ByteRhythm/app/relation/service"
7 | "ByteRhythm/config"
8 | "ByteRhythm/idl/relation/relationPb"
9 | "fmt"
10 | "os"
11 |
12 | "github.com/go-micro/plugins/v4/registry/etcd"
13 | ratelimit "github.com/go-micro/plugins/v4/wrapper/ratelimiter/uber"
14 | "github.com/go-micro/plugins/v4/wrapper/select/roundrobin"
15 | "github.com/go-micro/plugins/v4/wrapper/trace/opentracing"
16 |
17 | "go-micro.dev/v4"
18 | "go-micro.dev/v4/registry"
19 | )
20 |
21 | func main() {
22 | config.Init()
23 | dao.InitMySQL()
24 | dao.InitRedis()
25 |
26 | defer dao.RedisClient.Close()
27 |
28 | // etcd注册件
29 | etcdReg := etcd.NewRegistry(
30 | registry.Addrs(fmt.Sprintf("%s:%s", config.EtcdHost, config.EtcdPort)),
31 | )
32 | // 链路追踪
33 | tracer, closer, err := wrapper.InitJaeger("RelationService", fmt.Sprintf("%s:%s", config.JaegerHost, config.JaegerPort))
34 | if err != nil {
35 | fmt.Printf("new tracer err: %+v\n", err)
36 | os.Exit(-1)
37 | }
38 | defer closer.Close()
39 | // 得到一个微服务实例
40 | microService := micro.NewService(
41 | micro.Name("RelationService"), // 微服务名字
42 | micro.Address(config.RelationServiceAddress),
43 | micro.Registry(etcdReg), // etcd注册件
44 | micro.WrapHandler(ratelimit.NewHandlerWrapper(50000)), //限流处理
45 | micro.WrapClient(roundrobin.NewClientWrapper()), // 负载均衡
46 | micro.WrapHandler(opentracing.NewHandlerWrapper(tracer)), // 链路追踪
47 | micro.WrapClient(opentracing.NewClientWrapper(tracer)), // 链路追踪
48 | )
49 |
50 | // 结构命令行参数,初始化
51 | microService.Init()
52 | // 服务注册
53 | _ = relationPb.RegisterRelationServiceHandler(microService.Server(), service.GetRelationSrv())
54 | // 启动微服务
55 | _ = microService.Run()
56 | }
57 |
--------------------------------------------------------------------------------
/app/relation/dao/init.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "github.com/go-redis/redis/v8"
7 | "time"
8 |
9 | "github.com/gin-gonic/gin"
10 | "gorm.io/driver/mysql"
11 | "gorm.io/gorm"
12 | "gorm.io/gorm/logger"
13 | "gorm.io/gorm/schema"
14 |
15 | "ByteRhythm/config"
16 | )
17 |
18 | var db *gorm.DB
19 | var RedisClient *redis.Client
20 |
21 | func InitMySQL() {
22 | host := config.DBHost
23 | port := config.DBPort
24 | database := config.DBName
25 | username := config.DBUser
26 | password := config.DBPassWord
27 | charset := config.Charset
28 | dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&parseTime=True&loc=Local", username, password, host, port, database, charset)
29 | err := Database(dsn)
30 | if err != nil {
31 | fmt.Println(err)
32 | }
33 | }
34 |
35 | func InitRedis() {
36 | // 初始化 Redis 客户端
37 | host := config.RedisHost
38 | port := config.RedisPort
39 | RedisClient = redis.NewClient(&redis.Options{
40 | Addr: host + ":" + port, // Redis 服务器地址
41 | Password: "", // Redis 访问密码(如果有的话)
42 | DB: 1, // Redis 数据库索引
43 | })
44 | }
45 |
46 | func Database(connString string) error {
47 | var ormLogger logger.Interface
48 | if gin.Mode() == "debug" {
49 | ormLogger = logger.Default.LogMode(logger.Info)
50 | } else {
51 | ormLogger = logger.Default
52 | }
53 | DB, err := gorm.Open(mysql.New(mysql.Config{
54 | DSN: connString, // DSN data source name
55 | DefaultStringSize: 256, // string 类型字段的默认长度
56 | DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
57 | DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
58 | DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
59 | SkipInitializeWithVersion: false, // 根据版本自动配置
60 | }), &gorm.Config{
61 | Logger: ormLogger,
62 | NamingStrategy: schema.NamingStrategy{
63 | SingularTable: true,
64 | },
65 | })
66 | if err != nil {
67 | panic(err)
68 | }
69 | sqlDB, _ := DB.DB()
70 | sqlDB.SetMaxIdleConns(20)
71 | sqlDB.SetMaxOpenConns(100)
72 | sqlDB.SetConnMaxLifetime(time.Second * 30)
73 | db = DB
74 | migration()
75 | return err
76 | }
77 |
78 | func NewDBClient(ctx context.Context) *gorm.DB {
79 | DB := db
80 | return DB.WithContext(ctx)
81 | }
82 |
--------------------------------------------------------------------------------
/app/relation/dao/migration.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "ByteRhythm/model"
5 | "log"
6 | )
7 |
8 | func migration() {
9 | err := db.Set(`gorm:table_options`, "charset=utf8mb4").
10 | AutoMigrate(&model.User{}, &model.Follow{})
11 | if err != nil {
12 | log.Fatal(err)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/relation/dao/relation.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "ByteRhythm/model"
5 | "ByteRhythm/util"
6 | "context"
7 |
8 | "gorm.io/gorm"
9 | )
10 |
11 | type RelationDao struct {
12 | *gorm.DB
13 | }
14 |
15 | func NewRelationDao(ctx context.Context) *RelationDao {
16 | if ctx == nil {
17 | ctx = context.Background()
18 | }
19 | return &RelationDao{NewDBClient(ctx)}
20 | }
21 |
22 | func (r *RelationDao) FindUserById(uid int) (user *model.User, err error) {
23 | err = r.Model(&model.User{}).Where("id = ?", uid).Limit(1).Find(&user).Error
24 | if err != nil {
25 | return
26 | }
27 | return
28 | }
29 |
30 | func (r *RelationDao) FindAllFollow(uid int) (follows []*model.Follow, err error) {
31 | err = r.Model(&model.Follow{}).Where("followed_user_id = ?", uid).Find(&follows).Error
32 | if err != nil {
33 | return
34 | }
35 | return follows, nil
36 | }
37 |
38 | func (r *RelationDao) FindAllFollower(uid int) (follows []*model.Follow, err error) {
39 | err = r.Model(&model.Follow{}).Where("user_id = ?", uid).Find(&follows).Error
40 | if err != nil {
41 | return
42 | }
43 | return follows, nil
44 | }
45 |
46 | func (r *RelationDao) AddFollow(follow *model.Follow) (id int64, err error) {
47 | result := r.Model(&model.Follow{}).Where("user_id = ?", follow.UserID).Where("followed_user_id=?", follow.FollowedUserID).FirstOrCreate(&follow)
48 | if result.Error != nil {
49 | return
50 | }
51 | return result.RowsAffected, nil
52 | }
53 |
54 | func (r *RelationDao) CancelFollow(follow *model.Follow) (relation *model.Follow, err error) {
55 | err = r.Model(&model.Follow{}).Where("user_id = ?", follow.UserID).Where("followed_user_id=?", follow.FollowedUserID).Delete(&follow).Error
56 | if err != nil {
57 | return
58 | }
59 | return
60 | }
61 |
62 | func (r *RelationDao) GetFriendCount(userId int, followedUserId int) (count int64, err error) {
63 | err = r.Model(&model.Follow{}).Where("user_id = ?", userId).Where("followed_user_id=?", followedUserId).Count(&count).Error
64 | if err != nil {
65 | return
66 | }
67 | return
68 | }
69 |
70 | func (r *RelationDao) GetFollowCount(uid int) (count int64, err error) {
71 | err = r.Model(&model.Follow{}).Where("followed_user_id = ?", uid).Count(&count).Error
72 | if err != nil {
73 | return
74 | }
75 | return
76 | }
77 |
78 | func (r *RelationDao) GetFollowerCount(uid int) (count int64, err error) {
79 | err = r.Model(&model.Follow{}).Where("user_id = ?", uid).Count(&count).Error
80 | if err != nil {
81 | return
82 | }
83 | return
84 | }
85 |
86 | func (r *RelationDao) GetWorkCount(uid int) (count int64, err error) {
87 | err = r.Model(&model.Video{}).Where("author_id = ?", uid).Count(&count).Error
88 | if err != nil {
89 | return
90 | }
91 | return
92 | }
93 |
94 | func (r *RelationDao) GetFavoriteCount(uid int) (count int64, err error) {
95 | err = r.Model(&model.Favorite{}).Where("user_id = ?", uid).Count(&count).Error
96 | if err != nil {
97 | return
98 | }
99 | return
100 | }
101 |
102 | func (r *RelationDao) GetTotalFavorited(uid int) (count int64, err error) {
103 | var videos []*model.Video
104 | err = r.Model(&model.Video{}).Where("author_id = ?", uid).Find(&videos).Error
105 | if err != nil {
106 | return
107 | }
108 | for _, video := range videos {
109 | var favoriteCount int64
110 | err = r.Model(&model.Favorite{}).Where("video_id = ?", video.ID).Count(&favoriteCount).Error
111 | if err != nil {
112 | return
113 | }
114 | count += favoriteCount
115 | }
116 | return
117 | }
118 |
119 | func (r *RelationDao) GetIsFollowed(uid int, token string) (isFollowed bool, err error) {
120 |
121 | baseId, err := util.GetUserIdFromToken(token)
122 | if err != nil {
123 | return
124 | }
125 | var follow model.Follow
126 | err = r.Model(&model.Follow{}).Where("user_id = ?", uid).Where("followed_user_id = ?", baseId).Limit(1).Find(&follow).Error
127 | if err != nil {
128 | return
129 | }
130 | if follow.ID != 0 {
131 | isFollowed = true
132 | } else {
133 | isFollowed = false
134 | }
135 | return
136 | }
137 |
--------------------------------------------------------------------------------
/app/user/cmd/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "ByteRhythm/app/gateway/wrapper"
5 | "ByteRhythm/app/user/dao"
6 | "ByteRhythm/app/user/service"
7 | "ByteRhythm/config"
8 | "ByteRhythm/idl/user/userPb"
9 | "fmt"
10 | "os"
11 |
12 | "github.com/go-micro/plugins/v4/registry/etcd"
13 | ratelimit "github.com/go-micro/plugins/v4/wrapper/ratelimiter/uber"
14 | "github.com/go-micro/plugins/v4/wrapper/select/roundrobin"
15 | "github.com/go-micro/plugins/v4/wrapper/trace/opentracing"
16 | "go-micro.dev/v4"
17 | "go-micro.dev/v4/registry"
18 | )
19 |
20 | func main() {
21 | config.Init()
22 | dao.InitMySQL()
23 |
24 | //etcd注册件
25 | etcdReg := etcd.NewRegistry(
26 | registry.Addrs(fmt.Sprintf("%s:%s", config.EtcdHost, config.EtcdPort)),
27 | )
28 | // 链路追踪
29 | tracer, closer, err := wrapper.InitJaeger("UserService", fmt.Sprintf("%s:%s", config.JaegerHost, config.JaegerPort))
30 | if err != nil {
31 | fmt.Printf("new tracer err: %+v\n", err)
32 | os.Exit(-1)
33 | }
34 | defer closer.Close()
35 | // 得到一个微服务实例
36 | microService := micro.NewService(
37 | micro.Name("UserService"), // 微服务名字
38 | micro.Address(config.UserServiceAddress),
39 | micro.Registry(etcdReg), // etcd注册件
40 | micro.WrapHandler(ratelimit.NewHandlerWrapper(50000)), //限流处理
41 | micro.WrapClient(roundrobin.NewClientWrapper()), // 负载均衡
42 | micro.WrapHandler(opentracing.NewHandlerWrapper(tracer)), // 链路追踪
43 | micro.WrapClient(opentracing.NewClientWrapper(tracer)), // 链路追踪
44 | )
45 |
46 | // 结构命令行参数,初始化
47 | microService.Init()
48 | // 服务注册
49 | _ = userPb.RegisterUserServiceHandler(microService.Server(), service.GetUserSrv())
50 | // 启动微服务
51 | _ = microService.Run()
52 | }
53 |
--------------------------------------------------------------------------------
/app/user/dao/init.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "github.com/gin-gonic/gin"
7 | "gorm.io/driver/mysql"
8 | "gorm.io/gorm"
9 | "gorm.io/gorm/logger"
10 | "gorm.io/gorm/schema"
11 | "time"
12 |
13 | "ByteRhythm/config"
14 | )
15 |
16 | var db *gorm.DB
17 |
18 | func InitMySQL() {
19 | host := config.DBHost
20 | port := config.DBPort
21 | database := config.DBName
22 | username := config.DBUser
23 | password := config.DBPassWord
24 | charset := config.Charset
25 | dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&parseTime=True&loc=Local", username, password, host, port, database, charset)
26 | err := Database(dsn)
27 | if err != nil {
28 | fmt.Println(err)
29 | }
30 | }
31 |
32 | func Database(connString string) error {
33 | var ormLogger logger.Interface
34 | if gin.Mode() == "debug" {
35 | ormLogger = logger.Default.LogMode(logger.Info)
36 | } else {
37 | ormLogger = logger.Default
38 | }
39 | DB, err := gorm.Open(mysql.New(mysql.Config{
40 | DSN: connString, // DSN data source name
41 | DefaultStringSize: 256, // string 类型字段的默认长度
42 | DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
43 | DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
44 | DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
45 | SkipInitializeWithVersion: false, // 根据版本自动配置
46 | }), &gorm.Config{
47 | Logger: ormLogger,
48 | NamingStrategy: schema.NamingStrategy{
49 | SingularTable: true,
50 | },
51 | })
52 | if err != nil {
53 | panic(err)
54 | }
55 | sqlDB, _ := DB.DB()
56 | sqlDB.SetMaxIdleConns(20)
57 | sqlDB.SetMaxOpenConns(100)
58 | sqlDB.SetConnMaxLifetime(time.Second * 30)
59 | db = DB
60 | migration()
61 | return err
62 | }
63 |
64 | func NewDBClient(ctx context.Context) *gorm.DB {
65 | DB := db
66 | return DB.WithContext(ctx)
67 | }
68 |
--------------------------------------------------------------------------------
/app/user/dao/migration.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "ByteRhythm/model"
5 | "log"
6 | )
7 |
8 | func migration() {
9 | err := db.Set(`gorm:table_options`, "charset=utf8mb4").
10 | AutoMigrate(&model.User{}, &model.Follow{}, &model.Video{}, &model.Favorite{})
11 | if err != nil {
12 | log.Fatal(err)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/user/dao/user.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "ByteRhythm/model"
5 | "ByteRhythm/util"
6 | "context"
7 |
8 | "gorm.io/gorm"
9 | )
10 |
11 | type UserDao struct {
12 | *gorm.DB
13 | }
14 |
15 | func NewUserDao(ctx context.Context) *UserDao {
16 | if ctx == nil {
17 | ctx = context.Background()
18 | }
19 | return &UserDao{NewDBClient(ctx)}
20 | }
21 |
22 | func (u *UserDao) FindUserByUserName(username string) (user *model.User, err error) {
23 | //查看该用户是否存在
24 | err = u.Model(&model.User{}).Where("username = ?", username).Limit(1).Find(&user).Error
25 | if err != nil {
26 | return
27 | }
28 | return
29 | }
30 | func (u *UserDao) CreateUser(user *model.User) (id int64, err error) {
31 | err = u.Model(&model.User{}).Create(&user).Error
32 | if err != nil {
33 | return
34 | }
35 | return int64(user.ID), nil
36 | }
37 |
38 | func (u *UserDao) FindUserById(uid int) (user *model.User, err error) {
39 | err = u.Model(&model.User{}).Where("id = ?", uid).Limit(1).Find(&user).Error
40 | if err != nil {
41 | return
42 | }
43 | return
44 | }
45 |
46 | func (u *UserDao) GetFollowCount(uid int) (count int64, err error) {
47 | err = u.Model(&model.Follow{}).Where("followed_user_id = ?", uid).Count(&count).Error
48 | if err != nil {
49 | return
50 | }
51 | return
52 | }
53 |
54 | func (u *UserDao) GetFollowerCount(uid int) (count int64, err error) {
55 | err = u.Model(&model.Follow{}).Where("user_id = ?", uid).Count(&count).Error
56 | if err != nil {
57 | return
58 | }
59 | return
60 | }
61 |
62 | func (u *UserDao) GetWorkCount(uid int) (count int64, err error) {
63 | err = u.Model(&model.Video{}).Where("author_id = ?", uid).Count(&count).Error
64 | if err != nil {
65 | return
66 | }
67 | return
68 | }
69 |
70 | func (u *UserDao) GetFavoriteCount(uid int) (count int64, err error) {
71 | err = u.Model(&model.Favorite{}).Where("user_id = ?", uid).Count(&count).Error
72 | if err != nil {
73 | return
74 | }
75 | return
76 | }
77 |
78 | func (u *UserDao) GetTotalFavorited(uid int) (count int64, err error) {
79 | var videos []*model.Video
80 | err = u.Model(&model.Video{}).Where("author_id = ?", uid).Find(&videos).Error
81 | if err != nil {
82 | return
83 | }
84 | for _, video := range videos {
85 | var favoriteCount int64
86 | err = u.Model(&model.Favorite{}).Where("video_id = ?", video.ID).Count(&favoriteCount).Error
87 | if err != nil {
88 | return
89 | }
90 | count += favoriteCount
91 | }
92 | return
93 | }
94 |
95 | func (u *UserDao) GetIsFollowed(uid int, token string) (isFollowed bool, err error) {
96 |
97 | baseID, err := util.GetUserIdFromToken(token)
98 | if err != nil {
99 | return
100 | }
101 | var follow model.Follow
102 | err = u.Model(&model.Follow{}).Where("user_id = ?", uid).Where("followed_user_id = ?", baseID).Limit(1).Find(&follow).Error
103 | if err != nil {
104 | return
105 | }
106 | if follow.ID != 0 {
107 | isFollowed = true
108 | } else {
109 | isFollowed = false
110 | }
111 | return
112 | }
113 |
--------------------------------------------------------------------------------
/app/user/service/user.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "ByteRhythm/app/user/dao"
5 | "ByteRhythm/config"
6 | "ByteRhythm/idl/user/userPb"
7 | "ByteRhythm/model"
8 |
9 | "ByteRhythm/util"
10 | "context"
11 | "sync"
12 | )
13 |
14 | type UserSrv struct {
15 | }
16 |
17 | var UserSrvIns *UserSrv
18 | var UserSrvOnce sync.Once
19 |
20 | func GetUserSrv() *UserSrv {
21 | UserSrvOnce.Do(func() {
22 | UserSrvIns = &UserSrv{}
23 | })
24 | return UserSrvIns
25 | }
26 |
27 | func (u *UserSrv) Login(ctx context.Context, req *userPb.UserRequest, res *userPb.UserResponse) (err error) {
28 | user, err := dao.NewUserDao(ctx).FindUserByUserName(req.Username)
29 | if user.ID == 0 || util.Md5(req.Password) != user.Password {
30 | UserResponseData(res, 1, "用户名或密码错误")
31 | return nil
32 | }
33 | token := util.GenerateToken(user, 0)
34 | uid := int64(user.ID)
35 | UserResponseData(res, 0, "登录成功", uid, token)
36 | return nil
37 | }
38 |
39 | func (u *UserSrv) Register(ctx context.Context, req *userPb.UserRequest, res *userPb.UserResponse) (err error) {
40 | username := req.Username
41 | password := req.Password
42 | if len(username) > 32 || len(password) > 32 {
43 | UserResponseData(res, 1, "用户名或密码不能超过32位")
44 | return nil
45 | }
46 | if user, _ := dao.NewUserDao(ctx).FindUserByUserName(username); user.ID != 0 {
47 | UserResponseData(res, 1, "用户名已存在")
48 | return nil
49 | }
50 |
51 | user := BuildUserModel(username, password)
52 | if id, err := dao.NewUserDao(ctx).CreateUser(&user); err != nil {
53 | UserResponseData(res, 1, "注册失败")
54 | return err
55 | } else {
56 | token := util.GenerateToken(&user, 0)
57 | UserResponseData(res, 0, "注册成功", id, token)
58 | return nil
59 | }
60 | }
61 |
62 | func (u *UserSrv) UserInfo(ctx context.Context, req *userPb.UserInfoRequest, res *userPb.UserInfoResponse) error {
63 | token := req.Token
64 | uid := int(req.UserId)
65 |
66 | user, _ := dao.NewUserDao(ctx).FindUserById(uid)
67 | if user.ID == 0 {
68 | UserInfoResponseData(res, 1, "用户不存在")
69 | return nil
70 | }
71 |
72 | User := BuildUserPbModel(ctx, user, token)
73 | UserInfoResponseData(res, 0, "获取用户信息成功", User)
74 | return nil
75 | }
76 |
77 | func UserResponseData(res *userPb.UserResponse, StatusCode int32, StatusMsg string, params ...interface{}) {
78 | res.StatusCode = StatusCode
79 | res.StatusMsg = StatusMsg
80 | if len(params) != 0 {
81 | res.UserId = params[0].(int64)
82 | res.Token = params[1].(string)
83 | }
84 | }
85 |
86 | func UserInfoResponseData(res *userPb.UserInfoResponse, StatusCode int32, StatusMsg string, params ...interface{}) {
87 | res.StatusCode = StatusCode
88 | res.StatusMsg = StatusMsg
89 | if len(params) != 0 {
90 | res.User = params[0].(*userPb.User)
91 | }
92 | }
93 |
94 | func BuildUserModel(username string, password string) model.User {
95 | config.Init()
96 | avatar := config.Avatar
97 | background := config.Background
98 | signature := config.Signature
99 | return model.User{
100 | Username: username,
101 | Password: util.Md5(password),
102 | Avatar: avatar,
103 | BackgroundImage: background,
104 | Signature: signature,
105 | }
106 | }
107 | func BuildUserPbModel(ctx context.Context, user *model.User, token string) *userPb.User {
108 | uid := int(user.ID)
109 | FollowCount, _ := dao.NewUserDao(ctx).GetFollowCount(uid)
110 | FollowerCount, _ := dao.NewUserDao(ctx).GetFollowerCount(uid)
111 | WorkCount, _ := dao.NewUserDao(ctx).GetWorkCount(uid)
112 | FavoriteCount, _ := dao.NewUserDao(ctx).GetFavoriteCount(uid)
113 | TotalFavorited, _ := dao.NewUserDao(ctx).GetTotalFavorited(uid)
114 | IsFollow, _ := dao.NewUserDao(ctx).GetIsFollowed(uid, token)
115 | return &userPb.User{
116 | Id: int64(uid),
117 | Name: user.Username,
118 | Avatar: user.Avatar,
119 | BackgroundImage: user.BackgroundImage,
120 | Signature: user.Signature,
121 | FollowCount: FollowCount,
122 | FollowerCount: FollowerCount,
123 | WorkCount: WorkCount,
124 | FavoriteCount: FavoriteCount,
125 | TotalFavorited: TotalFavorited,
126 | IsFollow: IsFollow,
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/app/video/cmd/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "ByteRhythm/app/gateway/wrapper"
5 | "ByteRhythm/app/video/dao"
6 | "ByteRhythm/app/video/script"
7 | "ByteRhythm/app/video/service"
8 | "ByteRhythm/config"
9 | "ByteRhythm/idl/video/videoPb"
10 | "ByteRhythm/mq"
11 | "context"
12 | "fmt"
13 | "os"
14 |
15 | "github.com/go-micro/plugins/v4/registry/etcd"
16 | ratelimit "github.com/go-micro/plugins/v4/wrapper/ratelimiter/uber"
17 | "github.com/go-micro/plugins/v4/wrapper/select/roundrobin"
18 | "github.com/go-micro/plugins/v4/wrapper/trace/opentracing"
19 | "go-micro.dev/v4"
20 | "go-micro.dev/v4/registry"
21 | )
22 |
23 | func main() {
24 | config.Init()
25 | dao.InitMySQL()
26 | dao.InitRedis()
27 | mq.InitRabbitMQ()
28 | loadingScript()
29 |
30 | defer dao.RedisClient.Close()
31 |
32 | // etcd注册件
33 | etcdReg := etcd.NewRegistry(
34 | registry.Addrs(fmt.Sprintf("%s:%s", config.EtcdHost, config.EtcdPort)),
35 | )
36 |
37 | // 链路追踪
38 | tracer, closer, err := wrapper.InitJaeger("VideoService", fmt.Sprintf("%s:%s", config.JaegerHost, config.JaegerPort))
39 | if err != nil {
40 | fmt.Printf("new tracer err: %+v\n", err)
41 | os.Exit(-1)
42 | }
43 | defer closer.Close()
44 | // 得到一个微服务实例
45 | microService := micro.NewService(
46 | micro.Name("VideoService"), // 微服务名字
47 | micro.Address(config.VideoServiceAddress),
48 | micro.Registry(etcdReg), // etcd注册件
49 | micro.WrapHandler(ratelimit.NewHandlerWrapper(50000)), //限流处理
50 | micro.WrapClient(roundrobin.NewClientWrapper()), // 负载均衡
51 | micro.WrapHandler(opentracing.NewHandlerWrapper(tracer)), // 链路追踪
52 | micro.WrapClient(opentracing.NewClientWrapper(tracer)), // 链路追踪
53 | )
54 |
55 | // 结构命令行参数,初始化
56 | microService.Init()
57 | // 服务注册
58 | _ = videoPb.RegisterVideoServiceHandler(microService.Server(), service.GetVideoSrv())
59 | // 启动微服务
60 | _ = microService.Run()
61 | }
62 |
63 | func loadingScript() {
64 | ctx := context.Background()
65 | go script.VideoCreateSync(ctx)
66 | go script.Video2RedisSync(ctx)
67 | }
68 |
--------------------------------------------------------------------------------
/app/video/dao/init.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "ByteRhythm/config"
5 | "context"
6 | "fmt"
7 | "time"
8 |
9 | "github.com/gin-gonic/gin"
10 | "github.com/go-redis/redis/v8"
11 | "gorm.io/driver/mysql"
12 | "gorm.io/gorm"
13 | "gorm.io/gorm/logger"
14 | "gorm.io/gorm/schema"
15 | )
16 |
17 | var db *gorm.DB
18 |
19 | var RedisClient *redis.Client
20 |
21 | func InitMySQL() {
22 | host := config.DBHost
23 | port := config.DBPort
24 | database := config.DBName
25 | username := config.DBUser
26 | password := config.DBPassWord
27 | charset := config.Charset
28 | dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&parseTime=True&loc=Local", username, password, host, port, database, charset)
29 | err := Database(dsn)
30 | if err != nil {
31 | fmt.Println(err)
32 | }
33 | }
34 |
35 | func InitRedis() {
36 | // 初始化 Redis 客户端
37 | host := config.RedisHost
38 | port := config.RedisPort
39 | RedisClient = redis.NewClient(&redis.Options{
40 | Addr: host + ":" + port, // Redis 服务器地址
41 | Password: "", // Redis 访问密码(如果有的话)
42 | DB: 1, // Redis 数据库索引
43 | })
44 | }
45 | func Database(connString string) error {
46 | var ormLogger logger.Interface
47 | if gin.Mode() == "debug" {
48 | ormLogger = logger.Default.LogMode(logger.Info)
49 | } else {
50 | ormLogger = logger.Default
51 | }
52 | DB, err := gorm.Open(mysql.New(mysql.Config{
53 | DSN: connString, // DSN data source name
54 | DefaultStringSize: 256, // string 类型字段的默认长度
55 | DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
56 | DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
57 | DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
58 | SkipInitializeWithVersion: false, // 根据版本自动配置
59 | }), &gorm.Config{
60 | Logger: ormLogger,
61 | NamingStrategy: schema.NamingStrategy{
62 | SingularTable: true,
63 | },
64 | })
65 | if err != nil {
66 | panic(err)
67 | }
68 | sqlDB, _ := DB.DB()
69 | sqlDB.SetMaxIdleConns(20)
70 | sqlDB.SetMaxOpenConns(100)
71 | sqlDB.SetConnMaxLifetime(time.Second * 30)
72 | db = DB
73 | migration()
74 | return err
75 | }
76 |
77 | func NewDBClient(ctx context.Context) *gorm.DB {
78 | DB := db
79 | return DB.WithContext(ctx)
80 | }
81 |
--------------------------------------------------------------------------------
/app/video/dao/migration.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "ByteRhythm/model"
5 | "log"
6 | )
7 |
8 | func migration() {
9 | err := db.Set(`gorm:table_options`, "charset=utf8mb4").
10 | AutoMigrate(&model.User{}, &model.Follow{}, &model.Video{}, &model.Favorite{}, &model.Comment{})
11 | if err != nil {
12 | log.Fatal(err)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/video/dao/video.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "ByteRhythm/model"
5 | "ByteRhythm/util"
6 | "context"
7 | "time"
8 |
9 | "gorm.io/gorm"
10 | )
11 |
12 | type VideoDao struct {
13 | *gorm.DB
14 | }
15 |
16 | func NewVideoDao(ctx context.Context) *VideoDao {
17 | if ctx == nil {
18 | ctx = context.Background()
19 | }
20 | return &VideoDao{NewDBClient(ctx)}
21 | }
22 |
23 | func (v *VideoDao) GetVideoListByLatestTime(latestTime time.Time, vidExistArray []int, limit int) (videos []*model.Video, err error) {
24 | if limit != 30 {
25 | err = v.Model(&model.Video{}).Where("created_at <= ?", latestTime).Not("id", vidExistArray).Order("created_at desc").Limit(limit).Find(&videos).Error
26 | if err != nil {
27 | return
28 | }
29 | }
30 | err = v.Model(&model.Video{}).Where("created_at <= ?", latestTime).Order("created_at desc").Limit(limit).Find(&videos).Error
31 | if err != nil {
32 | return
33 | }
34 | return
35 | }
36 |
37 | func (v *VideoDao) GetFavoriteCount(vid int) (count int64, err error) {
38 | err = v.Model(&model.Favorite{}).Where("video_id = ?", vid).Count(&count).Error
39 | if err != nil {
40 | return
41 | }
42 | return
43 | }
44 |
45 | func (v *VideoDao) GetCommentCount(vid int) (count int64, err error) {
46 | err = v.Model(&model.Comment{}).Where("video_id = ?", vid).Count(&count).Error
47 | if err != nil {
48 | return
49 | }
50 | return
51 | }
52 |
53 | func (v *VideoDao) GetIsFavorite(vid int, token string) (isFavorite bool, err error) {
54 | baseID, err := util.GetUserIdFromToken(token)
55 | if err != nil {
56 | return
57 | }
58 | var favorite model.Favorite
59 | err = v.Model(&model.Favorite{}).Where("user_id = ? and video_id = ?", baseID, vid).Limit(1).Find(&favorite).Error
60 | if err != nil {
61 | return
62 | }
63 | if favorite.ID != 0 {
64 | isFavorite = true
65 | } else {
66 | isFavorite = false
67 | }
68 | return
69 | }
70 |
71 | func (v *VideoDao) GetFollowCount(uid int) (count int64, err error) {
72 | err = v.Model(&model.Follow{}).Where("followed_user_id = ?", uid).Count(&count).Error
73 | if err != nil {
74 | return
75 | }
76 | return
77 | }
78 |
79 | func (v *VideoDao) GetFollowerCount(uid int) (count int64, err error) {
80 | err = v.Model(&model.Follow{}).Where("user_id = ?", uid).Count(&count).Error
81 | if err != nil {
82 | return
83 | }
84 | return
85 | }
86 |
87 | func (v *VideoDao) GetWorkCount(uid int) (count int64, err error) {
88 | err = v.Model(&model.Video{}).Where("author_id = ?", uid).Count(&count).Error
89 | if err != nil {
90 | return
91 | }
92 | return
93 | }
94 |
95 | func (v *VideoDao) GetUserFavoriteCount(uid int) (count int64, err error) {
96 | err = v.Model(&model.Favorite{}).Where("user_id = ?", uid).Count(&count).Error
97 | if err != nil {
98 | return
99 | }
100 | return
101 | }
102 |
103 | func (v *VideoDao) GetTotalFavorited(uid int) (count int64, err error) {
104 | var videos []*model.Video
105 | err = v.Model(&model.Video{}).Where("author_id = ?", uid).Find(&videos).Error
106 | if err != nil {
107 | return
108 | }
109 | for _, video := range videos {
110 | var favoriteCount int64
111 | err = v.Model(&model.Favorite{}).Where("video_id = ?", video.ID).Count(&favoriteCount).Error
112 | if err != nil {
113 | return
114 | }
115 | count += favoriteCount
116 | }
117 | return
118 | }
119 |
120 | func (v *VideoDao) GetIsFollowed(uid int, token string) (isFollowed bool, err error) {
121 |
122 | baseID, err := util.GetUserIdFromToken(token)
123 | if err != nil {
124 | return
125 | }
126 | var follow model.Follow
127 | err = v.Model(&model.Follow{}).Where("user_id = ?", uid).Where("followed_user_id = ?", baseID).Limit(1).Find(&follow).Error
128 | if err != nil {
129 | return
130 | }
131 | if follow.ID != 0 {
132 | isFollowed = true
133 | } else {
134 | isFollowed = false
135 | }
136 | return
137 | }
138 |
139 | func (v *VideoDao) GetVideoListByUserId(uid int) (videos []*model.Video, err error) {
140 | err = v.Model(&model.Video{}).Where("author_id = ?", uid).Order("created_at desc").Limit(30).Find(&videos).Error
141 | if err != nil {
142 | return
143 | }
144 | return
145 | }
146 |
147 | func (v *VideoDao) CreateVideo(video *model.Video) (err error) {
148 | err = v.Model(&model.Video{}).Create(&video).Error
149 | if err != nil {
150 | return
151 | }
152 | return
153 | }
154 |
155 | func (v *VideoDao) FindUser(video *model.Video) (user *model.User, err error) {
156 | //gorm通过外键查询
157 | err = v.Model(&video).Association("Author").Find(&user)
158 | if err != nil {
159 | return
160 | }
161 | return
162 | }
163 |
--------------------------------------------------------------------------------
/app/video/script/video.go:
--------------------------------------------------------------------------------
1 | package script
2 |
3 | import (
4 | "ByteRhythm/app/video/service"
5 | "ByteRhythm/consts"
6 | "ByteRhythm/idl/video/videoPb"
7 | "ByteRhythm/mq"
8 | "context"
9 | "encoding/json"
10 | )
11 |
12 | type SyncVideo struct {
13 | }
14 |
15 | func VideoCreateSync(ctx context.Context) {
16 | Sync := new(SyncVideo)
17 | err := Sync.SyncVideoCreate(ctx, consts.CreateVideoQueue)
18 | if err != nil {
19 | return
20 | }
21 | }
22 |
23 | func Video2RedisSync(ctx context.Context) {
24 | Sync := new(SyncVideo)
25 | err := Sync.SyncVideo2Redis(ctx, consts.Video2RedisQueue)
26 | if err != nil {
27 | return
28 | }
29 | }
30 |
31 | func (s *SyncVideo) SyncVideoCreate(ctx context.Context, queueName string) error {
32 | msg, err := mq.ConsumeMessage(ctx, queueName)
33 | if err != nil {
34 | return err
35 | }
36 | var forever chan struct{}
37 | go func() {
38 | for d := range msg {
39 | // 落库
40 | var req *videoPb.PublishRequest
41 | err = json.Unmarshal(d.Body, &req)
42 | if err != nil {
43 | return
44 | }
45 | err = service.VideoMQ2DB(ctx, req)
46 | if err != nil {
47 | return
48 | }
49 | d.Ack(false)
50 | }
51 | }()
52 | <-forever
53 | return nil
54 | }
55 |
56 | func (s *SyncVideo) SyncVideo2Redis(ctx context.Context, queueName string) error {
57 | msg, err := mq.ConsumeMessage(ctx, queueName)
58 | if err != nil {
59 | return err
60 | }
61 | var forever chan struct{}
62 | go func() {
63 | for d := range msg {
64 | // 落库
65 | var req *videoPb.Video
66 | err = json.Unmarshal(d.Body, &req)
67 | if err != nil {
68 | return
69 | }
70 | err = service.VideoMQ2Redis(ctx, req)
71 | if err != nil {
72 | return
73 | }
74 | d.Ack(false)
75 | }
76 | }()
77 | <-forever
78 | return nil
79 | }
80 |
--------------------------------------------------------------------------------
/app/video/tmp/README.md:
--------------------------------------------------------------------------------
1 | # 存放保存到本地的临时文件
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | go build -o gateway app/gateway/cmd/main.go
3 | go build -o user app/user/cmd/main.go
4 | go build -o video app/video/cmd/main.go
5 | go build -o favorite app/favorite/cmd/main.go
6 | go build -o comment app/comment/cmd/main.go
7 | go build -o relation app/relation/cmd/main.go
8 | go build -o message app/message/cmd/main.go
9 |
--------------------------------------------------------------------------------
/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "fmt"
5 | "sync"
6 |
7 | "gopkg.in/ini.v1"
8 | )
9 |
10 | var (
11 | wg sync.WaitGroup
12 | DBHost string
13 | DBPort string
14 | DBUser string
15 | DBPassWord string
16 | DBName string
17 | Charset string
18 | Avatar string
19 | Background string
20 | Signature string
21 | EtcdHost string
22 | EtcdPort string
23 | RedisHost string
24 | RedisPort string
25 | JaegerHost string
26 | JaegerPort string
27 | HttpHost string
28 | HttpPort string
29 | UserServiceAddress string
30 | VideoServiceAddress string
31 | MessageServiceAddress string
32 | CommentServiceAddress string
33 | RelationServiceAddress string
34 | FavoriteServiceAddress string
35 | Bucket string
36 | AccessKey string
37 | SecretKey string
38 | Domain string
39 | RabbitMQ string
40 | RabbitMQHost string
41 | RabbitMQPort string
42 | RabbitMQUser string
43 | RabbitMQPassWord string
44 | )
45 |
46 | func Init() {
47 | file, err := ini.Load("./config/config.ini")
48 | if err != nil {
49 | fmt.Printf("load config failed,err:%v\n", err)
50 | }
51 | wg.Add(9)
52 | go LoadMySQL(file)
53 | go LoadUser(file)
54 | go LoadEtcd(file)
55 | go LoadJaeger(file)
56 | go LoadGin(file)
57 | go LoadService(file)
58 | go LoadQiNiuYun(file)
59 | go LoadRabbitMQ(file)
60 | go LoadRedis(file)
61 | wg.Wait()
62 | }
63 |
64 | func LoadMySQL(file *ini.File) {
65 | DBHost = file.Section("MySQL").Key("DBHost").String()
66 | DBPort = file.Section("MySQL").Key("DBPort").String()
67 | DBUser = file.Section("MySQL").Key("DBUser").String()
68 | DBPassWord = file.Section("MySQL").Key("DBPassWord").String()
69 | DBName = file.Section("MySQL").Key("DBName").String()
70 | Charset = file.Section("MySQL").Key("Charset").String()
71 | wg.Done()
72 | }
73 |
74 | func LoadUser(file *ini.File) {
75 | Avatar = file.Section("User").Key("Avatar").String()
76 | Background = file.Section("User").Key("Background").String()
77 | Signature = file.Section("User").Key("Signature").String()
78 | wg.Done()
79 | }
80 |
81 | func LoadEtcd(file *ini.File) {
82 | EtcdHost = file.Section("Etcd").Key("EtcdHost").String()
83 | EtcdPort = file.Section("Etcd").Key("EtcdPort").String()
84 | wg.Done()
85 | }
86 |
87 | func LoadJaeger(file *ini.File) {
88 | JaegerHost = file.Section("Jaeger").Key("JaegerHost").String()
89 | JaegerPort = file.Section("Jaeger").Key("JaegerPort").String()
90 | wg.Done()
91 | }
92 |
93 | func LoadGin(file *ini.File) {
94 | HttpHost = file.Section("Gin").Key("HttpHost").String()
95 | HttpPort = file.Section("Gin").Key("HttpPort").String()
96 | wg.Done()
97 | }
98 |
99 | func LoadService(file *ini.File) {
100 | UserServiceAddress = file.Section("Service").Key("UserServiceAddress").String()
101 | VideoServiceAddress = file.Section("Service").Key("VideoServiceAddress").String()
102 | MessageServiceAddress = file.Section("Service").Key("MessageServiceAddress").String()
103 | CommentServiceAddress = file.Section("Service").Key("CommentServiceAddress").String()
104 | RelationServiceAddress = file.Section("Service").Key("RelationServiceAddress").String()
105 | FavoriteServiceAddress = file.Section("Service").Key("FavoriteServiceAddress").String()
106 | wg.Done()
107 | }
108 |
109 | func LoadQiNiuYun(file *ini.File) {
110 | Bucket = file.Section("QiNiuYun").Key("Bucket").String()
111 | AccessKey = file.Section("QiNiuYun").Key("AccessKey").String()
112 | SecretKey = file.Section("QiNiuYun").Key("SecretKey").String()
113 | Domain = file.Section("QiNiuYun").Key("Domain").String()
114 | wg.Done()
115 | }
116 |
117 | func LoadRabbitMQ(file *ini.File) {
118 | RabbitMQ = file.Section("RabbitMQ").Key("RabbitMQ").String()
119 | RabbitMQHost = file.Section("RabbitMQ").Key("RabbitMQHost").String()
120 | RabbitMQPort = file.Section("RabbitMQ").Key("RabbitMQPort").String()
121 | RabbitMQUser = file.Section("RabbitMQ").Key("RabbitMQUser").String()
122 | RabbitMQPassWord = file.Section("RabbitMQ").Key("RabbitMQPassWord").String()
123 | wg.Done()
124 | }
125 |
126 | func LoadRedis(file *ini.File) {
127 | RedisHost = file.Section("Redis").Key("RedisHost").String()
128 | RedisPort = file.Section("Redis").Key("RedisPort").String()
129 | wg.Done()
130 | }
131 |
--------------------------------------------------------------------------------
/config/config.ini:
--------------------------------------------------------------------------------
1 | [Gin]
2 | AppMode = debug
3 | HttpHost = 0.0.0.0
4 | HttpPort = 8080
5 |
6 | [MySQL]
7 | DBHost = 127.0.0.1
8 | DBPort = 3306
9 | DBUser = root
10 | DBPassWord = 123456
11 | DBName = tiktok
12 | Charset = utf8mb4
13 |
14 | [Etcd]
15 | EtcdHost = 127.0.0.1
16 | EtcdPort = 2379
17 |
18 | [Jaeger]
19 | JaegerHost = 127.0.0.1
20 | JaegerPort = 6831
21 |
22 | [Redis]
23 | RedisHost = 127.0.0.1
24 | RedisPort = 6379
25 |
26 | [Service]
27 | UserServiceAddress = 127.0.0.1:8081
28 | VideoServiceAddress = 127.0.0.1:8082
29 | FavoriteServiceAddress = 127.0.0.1:8083
30 | CommentServiceAddress = 127.0.0.1:8084
31 | RelationServiceAddress = 127.0.0.1:8085
32 | MessageServiceAddress = 127.0.0.1:8086
33 |
34 | [RabbitMQ]
35 | RabbitMQ = amqp
36 | RabbitMQHost = 127.0.0.1
37 | RabbitMQPort = 5672
38 | RabbitMQUser = guest
39 | RabbitMQPassWord = guest
40 |
41 | [User]
42 | Avatar = http://rz2n87yck.hn-bkt.clouddn.com/avatar.jpg
43 | Background = http://rz2n87yck.hn-bkt.clouddn.com/background.png
44 | Signature = 又来看我的主页啦~
45 |
46 | [QiNiuYun]
47 | Bucket = your bucket
48 | AccessKey = your access key
49 | SecretKey = your secret key
50 | Domain = your domain
51 |
52 |
--------------------------------------------------------------------------------
/consts/mq.go:
--------------------------------------------------------------------------------
1 | package consts
2 |
3 | const CreateVideoQueue = "video-create-queue"
4 | const Video2RedisQueue = "video-redis-queue"
5 | const CreateFavorite2MQ = "favorite-create-queue"
6 | const DeleteFavorite2MQ = "favorite-delete-queue"
7 |
--------------------------------------------------------------------------------
/dockerfile:
--------------------------------------------------------------------------------
1 | #第一阶段
2 | FROM ubuntu:20.04 as builder
3 | ## 设置时区
4 | RUN apt-get -y update && DEBIAN_FRONTEND="noninteractive" apt -y install tzdata
5 | ENV TZ=Asia/Shanghai
6 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
7 |
8 | WORKDIR /workspace
9 |
10 | COPY . .
11 |
12 | ENV GO111MODULE=on \
13 | GOPROXY=https://goproxy.cn,direct
14 |
15 | ## 安装go1.20.7
16 | RUN apt install -y wget
17 | RUN wget https://go.dev/dl/go1.20.7.linux-amd64.tar.gz &&\
18 | tar -C /usr/local -xzf go1.20.7.linux-amd64.tar.gz &&\
19 | ## 软链接
20 | ln -s /usr/local/go/bin/* /usr/bin/
21 | # 配置ffmpeg环境
22 | RUN apt-get install -y autoconf automake build-essential libass-dev libfreetype6-dev libsdl1.2-dev libtheora-dev libtool libva-dev libvdpau-dev libvorbis-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev pkg-config texi2html zlib1g-dev
23 | RUN apt install -y libavdevice-dev libavfilter-dev libswscale-dev libavcodec-dev libavformat-dev libswresample-dev libavutil-dev
24 | RUN apt-get install -y yasm
25 | # 设置环境变量
26 | ENV FFMPEG_ROOT=$HOME/ffmpeg \
27 | CGO_LDFLAGS="-L$FFMPEG_ROOT/lib/ -lavcodec -lavformat -lavutil -lswscale -lswresample -lavdevice -lavfilter" \
28 | CGO_CFLAGS="-I$FFMPEG_ROOT/include" \
29 | LD_LIBRARY_PATH=$HOME/ffmpeg/lib
30 |
31 |
32 | RUN go mod download
33 |
34 | RUN chmod +x build.sh && ./build.sh
35 |
36 | #第二阶段
37 | FROM ubuntu:20.04 AS production
38 | WORKDIR /app
39 |
40 | ## 设置时区
41 | RUN apt-get -y update && DEBIAN_FRONTEND="noninteractive" apt -y install tzdata
42 | ENV TZ=Asia/Shanghai
43 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
44 |
45 | COPY ./*.sh .
46 |
47 | #本机目录不能使用绝对路径,因为它本身就是一个相对路径
48 | #只会复制本机的config目录下所有文件,而不会创建config目录,所以后面需要指定
49 | COPY ./config ./config
50 | # COPY . .
51 | RUN mkdir -p ./app/video/tmp/
52 | RUN apt install -y wget systemctl
53 |
54 | # 配置ffmpeg环境
55 | RUN apt-get install -y autoconf automake build-essential libass-dev libfreetype6-dev libsdl1.2-dev libtheora-dev libtool libva-dev libvdpau-dev libvorbis-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev pkg-config texi2html zlib1g-dev
56 | RUN apt install -y libavdevice-dev libavfilter-dev libswscale-dev libavcodec-dev libavformat-dev libswresample-dev libavutil-dev
57 | RUN apt-get install -y yasm
58 | # 设置环境变量
59 | ENV FFMPEG_ROOT=$HOME/ffmpeg \
60 | CGO_LDFLAGS="-L$FFMPEG_ROOT/lib/ -lavcodec -lavformat -lavutil -lswscale -lswresample -lavdevice -lavfilter" \
61 | CGO_CFLAGS="-I$FFMPEG_ROOT/include" \
62 | LD_LIBRARY_PATH=$HOME/ffmpeg/lib
63 |
64 | ## 安装 etcd v3.5.9
65 | RUN wget https://github.com/etcd-io/etcd/releases/download/v3.5.9/etcd-v3.5.9-linux-amd64.tar.gz &&\
66 | tar -zxvf etcd-v3.5.9-linux-amd64.tar.gz &&\
67 | cd etcd-v3.5.9-linux-amd64 &&\
68 | chmod +x etcd &&\
69 | mv ./etcd* /usr/local/bin/
70 |
71 |
72 | ## 安装 Jaeger v3.5.9
73 | RUN wget -c https://github.com/jaegertracing/jaeger/releases/download/v1.48.0/jaeger-1.48.0-linux-amd64.tar.gz &&\
74 | tar -zxvf jaeger-1.48.0-linux-amd64.tar.gz &&\
75 | cd jaeger-1.48.0-linux-amd64 &&\
76 | chmod a+x jaeger-* &&\
77 | mv ./jaeger-* /usr/local/bin/
78 | # nohup ./jaeger-all-in-one --collector.zipkin.host-port=:9411 &
79 |
80 | ## 安装 RabbitMQ
81 | ### 导入 RabbitMQ 的存储库密钥
82 | RUN wget -O- https://github.com/rabbitmq/signing-keys/releases/download/2.0/rabbitmq-release-signing-key.asc | apt-key add -
83 | ### 将存储库添加到系统
84 | RUN apt-get install -y apt-transport-https &&\
85 | cho "deb https://dl.bintray.com/rabbitmq-erlang/debian focal erlang" | tee /etc/apt/sources.list.d/bintray.erlang.list &&\
86 | echo "deb https://dl.bintray.com/rabbitmq/debian focal main" | tee /etc/apt/sources.list.d/bintray.rabbitmq.list
87 | ### 安装 RabbitMQ 和 Erlang
88 | RUN apt-get install -y rabbitmq-server
89 |
90 | ## 安装 注意:Redis安装会自动启动
91 | RUN apt install -y redis-server
92 |
93 |
94 | COPY --from=builder /workspace/gateway .
95 | COPY --from=builder /workspace/user .
96 | COPY --from=builder /workspace/video .
97 | COPY --from=builder /workspace/relation .
98 | COPY --from=builder /workspace/favorite .
99 | COPY --from=builder /workspace/comment .
100 | COPY --from=builder /workspace/message .
101 | EXPOSE 8080 16686
102 |
103 | # RUN chmod +x /app/run.sh 等效下面语句
104 | RUN cd /app &&chmod +x start.sh
105 | CMD ["/app/start.sh"]
106 |
107 |
108 | ## docker build -t david945/byterhythm:v2.1 .
109 | ## docker run -it -p 8080:8080/tcp -p 16686:16686/tcp --name byterhythm david945/byterhythm:v2.1
110 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module ByteRhythm
2 |
3 | go 1.20
4 |
5 | replace google.golang.org/grpc => google.golang.org/grpc v1.29.0
6 |
7 | require (
8 | github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5
9 | github.com/dgrijalva/jwt-go v3.2.0+incompatible
10 | github.com/gin-contrib/cors v1.4.0
11 | github.com/gin-gonic/gin v1.9.1
12 | github.com/giorgisio/goav v0.1.0
13 | github.com/go-micro/plugins/v4/registry/etcd v1.2.0
14 | github.com/go-micro/plugins/v4/wrapper/ratelimiter/uber v1.2.0
15 | github.com/go-micro/plugins/v4/wrapper/select/roundrobin v1.2.0
16 | github.com/go-micro/plugins/v4/wrapper/trace/opentracing v1.2.0
17 | github.com/go-redis/redis/v8 v8.11.5
18 | github.com/google/uuid v1.2.0
19 | github.com/opentracing/opentracing-go v1.2.0
20 | github.com/qiniu/go-sdk/v7 v7.17.0
21 | github.com/streadway/amqp v1.1.0
22 | github.com/stretchr/testify v1.8.4
23 | github.com/uber/jaeger-client-go v2.30.0+incompatible
24 | go-micro.dev/v4 v4.9.0
25 | google.golang.org/protobuf v1.30.0
26 | gopkg.in/ini.v1 v1.62.0
27 | gorm.io/driver/mysql v1.5.1
28 | gorm.io/gorm v1.25.4
29 | )
30 |
31 | require (
32 | github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect
33 | github.com/Microsoft/go-winio v0.5.0 // indirect
34 | github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
35 | github.com/acomagu/bufpipe v1.0.3 // indirect
36 | github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
37 | github.com/bitly/go-simplejson v0.5.0 // indirect
38 | github.com/bytedance/sonic v1.9.1 // indirect
39 | github.com/cespare/xxhash/v2 v2.1.2 // indirect
40 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
41 | github.com/coreos/go-semver v0.3.0 // indirect
42 | github.com/coreos/go-systemd/v22 v22.3.2 // indirect
43 | github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
44 | github.com/davecgh/go-spew v1.1.1 // indirect
45 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
46 | github.com/emirpasic/gods v1.12.0 // indirect
47 | github.com/evanphx/json-patch/v5 v5.5.0 // indirect
48 | github.com/felixge/httpsnoop v1.0.1 // indirect
49 | github.com/fsnotify/fsnotify v1.4.9 // indirect
50 | github.com/gabriel-vasile/mimetype v1.4.2 // indirect
51 | github.com/gin-contrib/sse v0.1.0 // indirect
52 | github.com/go-acme/lego/v4 v4.4.0 // indirect
53 | github.com/go-git/gcfg v1.5.0 // indirect
54 | github.com/go-git/go-billy/v5 v5.3.1 // indirect
55 | github.com/go-git/go-git/v5 v5.4.2 // indirect
56 | github.com/go-playground/locales v0.14.1 // indirect
57 | github.com/go-playground/universal-translator v0.18.1 // indirect
58 | github.com/go-playground/validator/v10 v10.14.0 // indirect
59 | github.com/go-sql-driver/mysql v1.7.0 // indirect
60 | github.com/gobwas/httphead v0.1.0 // indirect
61 | github.com/gobwas/pool v0.2.1 // indirect
62 | github.com/gobwas/ws v1.0.4 // indirect
63 | github.com/goccy/go-json v0.10.2 // indirect
64 | github.com/gogo/protobuf v1.3.2 // indirect
65 | github.com/golang/protobuf v1.5.2 // indirect
66 | github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect
67 | github.com/gorilla/handlers v1.5.1 // indirect
68 | github.com/imdario/mergo v0.3.12 // indirect
69 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
70 | github.com/jinzhu/inflection v1.0.0 // indirect
71 | github.com/jinzhu/now v1.1.5 // indirect
72 | github.com/json-iterator/go v1.1.12 // indirect
73 | github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
74 | github.com/klauspost/cpuid/v2 v2.2.4 // indirect
75 | github.com/leodido/go-urn v1.2.4 // indirect
76 | github.com/mattn/go-isatty v0.0.19 // indirect
77 | github.com/miekg/dns v1.1.43 // indirect
78 | github.com/mitchellh/go-homedir v1.1.0 // indirect
79 | github.com/mitchellh/hashstructure v1.1.0 // indirect
80 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
81 | github.com/modern-go/reflect2 v1.0.2 // indirect
82 | github.com/nxadm/tail v1.4.8 // indirect
83 | github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect
84 | github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
85 | github.com/pelletier/go-toml/v2 v2.0.8 // indirect
86 | github.com/pkg/errors v0.9.1 // indirect
87 | github.com/pmezard/go-difflib v1.0.0 // indirect
88 | github.com/russross/blackfriday/v2 v2.0.1 // indirect
89 | github.com/sergi/go-diff v1.1.0 // indirect
90 | github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
91 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
92 | github.com/uber/jaeger-lib v2.4.1+incompatible // indirect
93 | github.com/ugorji/go/codec v1.2.11 // indirect
94 | github.com/urfave/cli/v2 v2.3.0 // indirect
95 | github.com/xanzy/ssh-agent v0.3.0 // indirect
96 | go.etcd.io/etcd/api/v3 v3.5.2 // indirect
97 | go.etcd.io/etcd/client/pkg/v3 v3.5.2 // indirect
98 | go.etcd.io/etcd/client/v3 v3.5.2 // indirect
99 | go.uber.org/atomic v1.7.0 // indirect
100 | go.uber.org/multierr v1.6.0 // indirect
101 | go.uber.org/ratelimit v0.2.0 // indirect
102 | go.uber.org/zap v1.17.0 // indirect
103 | golang.org/x/arch v0.3.0 // indirect
104 | golang.org/x/crypto v0.9.0 // indirect
105 | golang.org/x/net v0.10.0 // indirect
106 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
107 | golang.org/x/sys v0.8.0 // indirect
108 | golang.org/x/text v0.9.0 // indirect
109 | google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect
110 | google.golang.org/grpc v1.38.0 // indirect
111 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
112 | gopkg.in/warnings.v0 v0.1.2 // indirect
113 | gopkg.in/yaml.v3 v3.0.1 // indirect
114 | )
115 |
--------------------------------------------------------------------------------
/idl/comment/commentPb/commentService.pb.micro.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-micro. DO NOT EDIT.
2 | // source: commentService.proto
3 |
4 | package commentPb
5 |
6 | import (
7 | fmt "fmt"
8 | proto "google.golang.org/protobuf/proto"
9 | math "math"
10 | )
11 |
12 | import (
13 | context "context"
14 | api "go-micro.dev/v4/api"
15 | client "go-micro.dev/v4/client"
16 | server "go-micro.dev/v4/server"
17 | )
18 |
19 | // Reference imports to suppress errors if they are not otherwise used.
20 | var _ = proto.Marshal
21 | var _ = fmt.Errorf
22 | var _ = math.Inf
23 |
24 | // Reference imports to suppress errors if they are not otherwise used.
25 | var _ api.Endpoint
26 | var _ context.Context
27 | var _ client.Option
28 | var _ server.Option
29 |
30 | // Api Endpoints for CommentService service
31 |
32 | func NewCommentServiceEndpoints() []*api.Endpoint {
33 | return []*api.Endpoint{}
34 | }
35 |
36 | // Client API for CommentService service
37 |
38 | type CommentService interface {
39 | CommentAction(ctx context.Context, in *CommentActionRequest, opts ...client.CallOption) (*CommentActionResponse, error)
40 | CommentList(ctx context.Context, in *CommentListRequest, opts ...client.CallOption) (*CommentListResponse, error)
41 | }
42 |
43 | type commentService struct {
44 | c client.Client
45 | name string
46 | }
47 |
48 | func NewCommentService(name string, c client.Client) CommentService {
49 | return &commentService{
50 | c: c,
51 | name: name,
52 | }
53 | }
54 |
55 | func (c *commentService) CommentAction(ctx context.Context, in *CommentActionRequest, opts ...client.CallOption) (*CommentActionResponse, error) {
56 | req := c.c.NewRequest(c.name, "CommentService.CommentAction", in)
57 | out := new(CommentActionResponse)
58 | err := c.c.Call(ctx, req, out, opts...)
59 | if err != nil {
60 | return nil, err
61 | }
62 | return out, nil
63 | }
64 |
65 | func (c *commentService) CommentList(ctx context.Context, in *CommentListRequest, opts ...client.CallOption) (*CommentListResponse, error) {
66 | req := c.c.NewRequest(c.name, "CommentService.CommentList", in)
67 | out := new(CommentListResponse)
68 | err := c.c.Call(ctx, req, out, opts...)
69 | if err != nil {
70 | return nil, err
71 | }
72 | return out, nil
73 | }
74 |
75 | // Server API for CommentService service
76 |
77 | type CommentServiceHandler interface {
78 | CommentAction(context.Context, *CommentActionRequest, *CommentActionResponse) error
79 | CommentList(context.Context, *CommentListRequest, *CommentListResponse) error
80 | }
81 |
82 | func RegisterCommentServiceHandler(s server.Server, hdlr CommentServiceHandler, opts ...server.HandlerOption) error {
83 | type commentService interface {
84 | CommentAction(ctx context.Context, in *CommentActionRequest, out *CommentActionResponse) error
85 | CommentList(ctx context.Context, in *CommentListRequest, out *CommentListResponse) error
86 | }
87 | type CommentService struct {
88 | commentService
89 | }
90 | h := &commentServiceHandler{hdlr}
91 | return s.Handle(s.NewHandler(&CommentService{h}, opts...))
92 | }
93 |
94 | type commentServiceHandler struct {
95 | CommentServiceHandler
96 | }
97 |
98 | func (h *commentServiceHandler) CommentAction(ctx context.Context, in *CommentActionRequest, out *CommentActionResponse) error {
99 | return h.CommentServiceHandler.CommentAction(ctx, in, out)
100 | }
101 |
102 | func (h *commentServiceHandler) CommentList(ctx context.Context, in *CommentListRequest, out *CommentListResponse) error {
103 | return h.CommentServiceHandler.CommentList(ctx, in, out)
104 | }
105 |
--------------------------------------------------------------------------------
/idl/comment/commentService.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package commentService;
3 | option go_package = "./commentPb";
4 |
5 | message Comment {
6 | // @inject_tag: json:"id" form:"id"
7 | int64 id = 1; // 视频评论id
8 | // @inject_tag: json:"user" form:"user"
9 | User user = 2; // 评论用户信息
10 | // @inject_tag: json:"content" form:"content"
11 | string content = 3; // 评论内容
12 | // @inject_tag: json:"create_date" form:"create_date"
13 | string create_date = 4; // 评论发布日期,格式 mm-dd
14 | }
15 |
16 | message User {
17 | // @inject_tag: json:"id" form:"id"
18 | int64 id = 1; // 用户id
19 | // @inject_tag: json:"name" form:"name"
20 | string name = 2; // 用户名称
21 | // @inject_tag: json:"follow_count" form:"follow_count"
22 | int64 follow_count = 3; // 关注总数
23 | // @inject_tag: json:"follower_count" form:"follower_count"
24 | int64 follower_count = 4; // 粉丝总数
25 | // @inject_tag: json:"is_follow" form:"is_follow"
26 | bool is_follow = 5; // true-已关注,false-未关注
27 | // @inject_tag: json:"avatar" form:"avatar"
28 | string avatar = 6; //用户头像
29 | // @inject_tag: json:"background_image" form:"background_image"
30 | string background_image = 7; //用户个人页顶部大图
31 | // @inject_tag: json:"signature" form:"signature"
32 | string signature = 8; //个人简介
33 | // @inject_tag: json:"total_favorited" form:"total_favorited"
34 | int64 total_favorited = 9; //获赞数量
35 | // @inject_tag: json:"work_count" form:"work_count"
36 | int64 work_count = 10; //作品数量
37 | // @inject_tag: json:"favorite_count" form:"favorite_count"
38 | int64 favorite_count = 11; //点赞数量
39 | }
40 |
41 | message CommentActionRequest {
42 | // @inject_tag: json:"token" form:"token"
43 | string token = 1; // 用户鉴权token
44 | // @inject_tag: json:"video_id" form:"video_id"
45 | int64 video_id = 2; // 视频id
46 | // @inject_tag: json:"action_type" form:"action_type"
47 | int32 action_type = 3; // 1-发布评论,2-删除评论
48 | // @inject_tag: json:"comment_text" form:"comment_text"
49 | string comment_text = 4; // 用户填写的评论内容,在action_type=1的时候使用
50 | // @inject_tag: json:"comment_id" form:"comment_id"
51 | int64 comment_id = 5; // 要删除的评论id,在action_type=2的时候使用
52 | }
53 |
54 | message CommentActionResponse {
55 | // @inject_tag: json:"status_code" form:"status_code"
56 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
57 | // @inject_tag: json:"status_msg" form:"status_msg"
58 | string status_msg = 2; // 返回状态描述
59 | // @inject_tag: json:"comment" form:"comment"
60 | Comment comment = 3; // 评论成功返回评论内容,不需要重新拉取整个列表
61 | }
62 |
63 | message CommentListRequest {
64 | // @inject_tag: json:"token" form:"token"
65 | string token = 1; // 用户鉴权token
66 | // @inject_tag: json:"video_id" form:"video_id"
67 | int64 video_id = 2; // 视频id
68 | }
69 |
70 | message CommentListResponse {
71 | // @inject_tag: json:"status_code" form:"status_code"
72 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
73 | // @inject_tag: json:"status_msg" form:"status_msg"
74 | string status_msg = 2; // 返回状态描述
75 | // @inject_tag: json:"comment_list" form:"comment_list"
76 | repeated Comment comment_list = 3; // 评论列表
77 | }
78 |
79 | service CommentService {
80 | rpc CommentAction(CommentActionRequest) returns (CommentActionResponse) {}
81 | rpc CommentList(CommentListRequest) returns (CommentListResponse) {}
82 | }
83 |
--------------------------------------------------------------------------------
/idl/favorite/favoritePb/favoriteService.pb.micro.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-micro. DO NOT EDIT.
2 | // source: favoriteService.proto
3 |
4 | package favoritePb
5 |
6 | import (
7 | fmt "fmt"
8 | proto "google.golang.org/protobuf/proto"
9 | math "math"
10 | )
11 |
12 | import (
13 | context "context"
14 | api "go-micro.dev/v4/api"
15 | client "go-micro.dev/v4/client"
16 | server "go-micro.dev/v4/server"
17 | )
18 |
19 | // Reference imports to suppress errors if they are not otherwise used.
20 | var _ = proto.Marshal
21 | var _ = fmt.Errorf
22 | var _ = math.Inf
23 |
24 | // Reference imports to suppress errors if they are not otherwise used.
25 | var _ api.Endpoint
26 | var _ context.Context
27 | var _ client.Option
28 | var _ server.Option
29 |
30 | // Api Endpoints for FavoriteService service
31 |
32 | func NewFavoriteServiceEndpoints() []*api.Endpoint {
33 | return []*api.Endpoint{}
34 | }
35 |
36 | // Client API for FavoriteService service
37 |
38 | type FavoriteService interface {
39 | FavoriteAction(ctx context.Context, in *FavoriteActionRequest, opts ...client.CallOption) (*FavoriteActionResponse, error)
40 | FavoriteList(ctx context.Context, in *FavoriteListRequest, opts ...client.CallOption) (*FavoriteListResponse, error)
41 | }
42 |
43 | type favoriteService struct {
44 | c client.Client
45 | name string
46 | }
47 |
48 | func NewFavoriteService(name string, c client.Client) FavoriteService {
49 | return &favoriteService{
50 | c: c,
51 | name: name,
52 | }
53 | }
54 |
55 | func (c *favoriteService) FavoriteAction(ctx context.Context, in *FavoriteActionRequest, opts ...client.CallOption) (*FavoriteActionResponse, error) {
56 | req := c.c.NewRequest(c.name, "FavoriteService.FavoriteAction", in)
57 | out := new(FavoriteActionResponse)
58 | err := c.c.Call(ctx, req, out, opts...)
59 | if err != nil {
60 | return nil, err
61 | }
62 | return out, nil
63 | }
64 |
65 | func (c *favoriteService) FavoriteList(ctx context.Context, in *FavoriteListRequest, opts ...client.CallOption) (*FavoriteListResponse, error) {
66 | req := c.c.NewRequest(c.name, "FavoriteService.FavoriteList", in)
67 | out := new(FavoriteListResponse)
68 | err := c.c.Call(ctx, req, out, opts...)
69 | if err != nil {
70 | return nil, err
71 | }
72 | return out, nil
73 | }
74 |
75 | // Server API for FavoriteService service
76 |
77 | type FavoriteServiceHandler interface {
78 | FavoriteAction(context.Context, *FavoriteActionRequest, *FavoriteActionResponse) error
79 | FavoriteList(context.Context, *FavoriteListRequest, *FavoriteListResponse) error
80 | }
81 |
82 | func RegisterFavoriteServiceHandler(s server.Server, hdlr FavoriteServiceHandler, opts ...server.HandlerOption) error {
83 | type favoriteService interface {
84 | FavoriteAction(ctx context.Context, in *FavoriteActionRequest, out *FavoriteActionResponse) error
85 | FavoriteList(ctx context.Context, in *FavoriteListRequest, out *FavoriteListResponse) error
86 | }
87 | type FavoriteService struct {
88 | favoriteService
89 | }
90 | h := &favoriteServiceHandler{hdlr}
91 | return s.Handle(s.NewHandler(&FavoriteService{h}, opts...))
92 | }
93 |
94 | type favoriteServiceHandler struct {
95 | FavoriteServiceHandler
96 | }
97 |
98 | func (h *favoriteServiceHandler) FavoriteAction(ctx context.Context, in *FavoriteActionRequest, out *FavoriteActionResponse) error {
99 | return h.FavoriteServiceHandler.FavoriteAction(ctx, in, out)
100 | }
101 |
102 | func (h *favoriteServiceHandler) FavoriteList(ctx context.Context, in *FavoriteListRequest, out *FavoriteListResponse) error {
103 | return h.FavoriteServiceHandler.FavoriteList(ctx, in, out)
104 | }
105 |
--------------------------------------------------------------------------------
/idl/favorite/favoriteService.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package favoriteService;
3 | option go_package = "./favoritePb";
4 |
5 | message Video {
6 | // @inject_tag: json:"id" form:"id"
7 | int64 id = 1; // 视频唯一标识
8 | // @inject_tag: json:"author" form:"author"
9 | User author = 2; // 视频作者信息
10 | // @inject_tag: json:"play_url" form:"play_url"
11 | string play_url = 3; // 视频播放地址
12 | // @inject_tag: json:"cover_url" form:"cover_url"
13 | string cover_url = 4; // 视频封面地址
14 | // @inject_tag: json:"favorite_count" form:"favorite_count"
15 | int64 favorite_count = 5; // 视频的点赞总数
16 | // @inject_tag: json:"comment_count" form:"comment_count"
17 | int64 comment_count = 6; // 视频的评论总数
18 | // @inject_tag: json:"is_favorite" form:"is_favorite"
19 | bool is_favorite = 7; // true-已点赞,false-未点赞
20 | // @inject_tag: json:"title" form:"title"
21 | string title = 8; // 视频标题
22 | }
23 |
24 | message User {
25 | // @inject_tag: json:"id" form:"id"
26 | int64 id = 1; // 用户id
27 | // @inject_tag: json:"name" form:"name"
28 | string name = 2; // 用户名称
29 | // @inject_tag: json:"follow_count" form:"follow_count"
30 | int64 follow_count = 3; // 关注总数
31 | // @inject_tag: json:"follower_count" form:"follower_count"
32 | int64 follower_count = 4; // 粉丝总数
33 | // @inject_tag: json:"is_follow" form:"is_follow"
34 | bool is_follow = 5; // true-已关注,false-未关注
35 | // @inject_tag: json:"avatar" form:"avatar"
36 | string avatar = 6; //用户头像
37 | // @inject_tag: json:"background_image" form:"background_image"
38 | string background_image = 7; //用户个人页顶部大图
39 | // @inject_tag: json:"signature" form:"signature"
40 | string signature = 8; //个人简介
41 | // @inject_tag: json:"total_favorited" form:"total_favorited"
42 | int64 total_favorited = 9; //获赞数量
43 | // @inject_tag: json:"work_count" form:"work_count"
44 | int64 work_count = 10; //作品数量
45 | // @inject_tag: json:"favorite_count" form:"favorite_count"
46 | int64 favorite_count = 11; //点赞数量
47 | }
48 |
49 | message FavoriteActionRequest {
50 | // @inject_tag: json:"token" form:"token"
51 | string token = 1; // 用户鉴权token
52 | // @inject_tag: json:"video_id" form:"video_id"
53 | int64 video_id = 2; // 视频id
54 | // @inject_tag: json:"action_type" form:"action_type"
55 | int32 action_type = 3; // 1-点赞,2-取消点赞
56 | }
57 |
58 | message FavoriteActionResponse {
59 | // @inject_tag: json:"status_code" form:"status_code"
60 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
61 | // @inject_tag: json:"status_msg" form:"status_msg"
62 | string status_msg = 2; // 返回状态描述
63 | }
64 |
65 | message FavoriteListRequest {
66 | // @inject_tag: json:"user_id" form:"user_id"
67 | int64 user_id = 1; // 视频id
68 | // @inject_tag: json:"token" form:"token"
69 | string token = 2; // 用户鉴权token
70 | }
71 |
72 | message FavoriteListResponse {
73 | // @inject_tag: json:"status_code" form:"status_code"
74 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
75 | // @inject_tag: json:"status_msg" form:"status_msg"
76 | string status_msg = 2; // 返回状态描述
77 | // @inject_tag: json:"video_list" form:"video_list"
78 | repeated Video video_list = 3; // 视频列表
79 | }
80 |
81 | service FavoriteService {
82 | rpc FavoriteAction(FavoriteActionRequest) returns (FavoriteActionResponse) {}
83 | rpc FavoriteList(FavoriteListRequest) returns (FavoriteListResponse) {}
84 | }
85 |
--------------------------------------------------------------------------------
/idl/message/messagePb/messageService.pb.micro.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-micro. DO NOT EDIT.
2 | // source: messageService.proto
3 |
4 | package messagePb
5 |
6 | import (
7 | fmt "fmt"
8 | proto "google.golang.org/protobuf/proto"
9 | math "math"
10 | )
11 |
12 | import (
13 | context "context"
14 | api "go-micro.dev/v4/api"
15 | client "go-micro.dev/v4/client"
16 | server "go-micro.dev/v4/server"
17 | )
18 |
19 | // Reference imports to suppress errors if they are not otherwise used.
20 | var _ = proto.Marshal
21 | var _ = fmt.Errorf
22 | var _ = math.Inf
23 |
24 | // Reference imports to suppress errors if they are not otherwise used.
25 | var _ api.Endpoint
26 | var _ context.Context
27 | var _ client.Option
28 | var _ server.Option
29 |
30 | // Api Endpoints for MessageService service
31 |
32 | func NewMessageServiceEndpoints() []*api.Endpoint {
33 | return []*api.Endpoint{}
34 | }
35 |
36 | // Client API for MessageService service
37 |
38 | type MessageService interface {
39 | ChatMessage(ctx context.Context, in *MessageChatRequest, opts ...client.CallOption) (*MessageChatResponse, error)
40 | ActionMessage(ctx context.Context, in *MessageActionRequest, opts ...client.CallOption) (*MessageActionResponse, error)
41 | }
42 |
43 | type messageService struct {
44 | c client.Client
45 | name string
46 | }
47 |
48 | func NewMessageService(name string, c client.Client) MessageService {
49 | return &messageService{
50 | c: c,
51 | name: name,
52 | }
53 | }
54 |
55 | func (c *messageService) ChatMessage(ctx context.Context, in *MessageChatRequest, opts ...client.CallOption) (*MessageChatResponse, error) {
56 | req := c.c.NewRequest(c.name, "MessageService.ChatMessage", in)
57 | out := new(MessageChatResponse)
58 | err := c.c.Call(ctx, req, out, opts...)
59 | if err != nil {
60 | return nil, err
61 | }
62 | return out, nil
63 | }
64 |
65 | func (c *messageService) ActionMessage(ctx context.Context, in *MessageActionRequest, opts ...client.CallOption) (*MessageActionResponse, error) {
66 | req := c.c.NewRequest(c.name, "MessageService.ActionMessage", in)
67 | out := new(MessageActionResponse)
68 | err := c.c.Call(ctx, req, out, opts...)
69 | if err != nil {
70 | return nil, err
71 | }
72 | return out, nil
73 | }
74 |
75 | // Server API for MessageService service
76 |
77 | type MessageServiceHandler interface {
78 | ChatMessage(context.Context, *MessageChatRequest, *MessageChatResponse) error
79 | ActionMessage(context.Context, *MessageActionRequest, *MessageActionResponse) error
80 | }
81 |
82 | func RegisterMessageServiceHandler(s server.Server, hdlr MessageServiceHandler, opts ...server.HandlerOption) error {
83 | type messageService interface {
84 | ChatMessage(ctx context.Context, in *MessageChatRequest, out *MessageChatResponse) error
85 | ActionMessage(ctx context.Context, in *MessageActionRequest, out *MessageActionResponse) error
86 | }
87 | type MessageService struct {
88 | messageService
89 | }
90 | h := &messageServiceHandler{hdlr}
91 | return s.Handle(s.NewHandler(&MessageService{h}, opts...))
92 | }
93 |
94 | type messageServiceHandler struct {
95 | MessageServiceHandler
96 | }
97 |
98 | func (h *messageServiceHandler) ChatMessage(ctx context.Context, in *MessageChatRequest, out *MessageChatResponse) error {
99 | return h.MessageServiceHandler.ChatMessage(ctx, in, out)
100 | }
101 |
102 | func (h *messageServiceHandler) ActionMessage(ctx context.Context, in *MessageActionRequest, out *MessageActionResponse) error {
103 | return h.MessageServiceHandler.ActionMessage(ctx, in, out)
104 | }
105 |
--------------------------------------------------------------------------------
/idl/message/messageService.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package messageService;
3 | option go_package = "./messagePb";
4 |
5 | message Message {
6 | // @inject_tag: json:"id" form:"id"
7 | int64 id = 1; // 消息id
8 | // @inject_tag: json:"to_user_id" form:"to_user_id"
9 | int64 to_user_id = 2; // 该消息接收者的id
10 | // @inject_tag: json:"from_user_id" form:"from_user_id"
11 | int64 from_user_id =3; // 该消息发送者的id
12 | // @inject_tag: json:"content" form:"content"
13 | string content = 4; // 消息内容
14 | // @inject_tag: json:"create_time" form:"create_time"
15 | string create_time = 5; // 消息创建时间
16 | }
17 |
18 | message MessageChatRequest {
19 | // @inject_tag: json:"token" form:"token"
20 | string token = 1; // 用户鉴权token
21 | // @inject_tag: json:"to_user_id" form:"to_user_id"
22 | int64 to_user_id = 2; // 对方用户id
23 | // @inject_tag: json:"pre_msg_time" form:"pre_msg_time"
24 | int64 pre_msg_time=3;//上次最新消息的时间(新增字段-apk更新中)
25 | }
26 |
27 | message MessageChatResponse {
28 | // @inject_tag: json:"status_code" form:"status_code"
29 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
30 | // @inject_tag: json:"status_msg" form:"status_msg"
31 | string status_msg = 2; // 返回状态描述
32 | // @inject_tag: json:"message_list" form:"message_list"
33 | repeated Message message_list = 3; // 消息列表
34 | }
35 |
36 | message MessageActionRequest {
37 | // @inject_tag: json:"token" form:"token"
38 | string token = 1; // 用户鉴权token
39 | // @inject_tag: json:"to_user_id" form:"to_user_id"
40 | int64 to_user_id = 2; // 对方用户id
41 | // @inject_tag: json:"action_type" form:"action_type"
42 | int32 action_type = 3; // 1-发送消息
43 | // @inject_tag: json:"content" form:"content"
44 | string content = 4; // 消息内容
45 | }
46 |
47 | message MessageActionResponse {
48 | // @inject_tag: json:"status_code" form:"status_code"
49 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
50 | // @inject_tag: json:"status_msg" form:"status_msg"
51 | string status_msg = 2; // 返回状态描述
52 | }
53 |
54 | service MessageService {
55 | rpc ChatMessage(MessageChatRequest) returns (MessageChatResponse) {}
56 | rpc ActionMessage(MessageActionRequest) returns (MessageActionResponse) {}
57 | }
58 |
59 |
--------------------------------------------------------------------------------
/idl/relation/relationPb/relationService.pb.micro.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-micro. DO NOT EDIT.
2 | // source: relationService.proto
3 |
4 | package relationPb
5 |
6 | import (
7 | fmt "fmt"
8 | proto "google.golang.org/protobuf/proto"
9 | math "math"
10 | )
11 |
12 | import (
13 | context "context"
14 | api "go-micro.dev/v4/api"
15 | client "go-micro.dev/v4/client"
16 | server "go-micro.dev/v4/server"
17 | )
18 |
19 | // Reference imports to suppress errors if they are not otherwise used.
20 | var _ = proto.Marshal
21 | var _ = fmt.Errorf
22 | var _ = math.Inf
23 |
24 | // Reference imports to suppress errors if they are not otherwise used.
25 | var _ api.Endpoint
26 | var _ context.Context
27 | var _ client.Option
28 | var _ server.Option
29 |
30 | // Api Endpoints for RelationService service
31 |
32 | func NewRelationServiceEndpoints() []*api.Endpoint {
33 | return []*api.Endpoint{}
34 | }
35 |
36 | // Client API for RelationService service
37 |
38 | type RelationService interface {
39 | ActionRelation(ctx context.Context, in *RelationActionRequest, opts ...client.CallOption) (*RelationActionResponse, error)
40 | ListFollowRelation(ctx context.Context, in *RelationFollowRequest, opts ...client.CallOption) (*RelationFollowResponse, error)
41 | ListFollowerRelation(ctx context.Context, in *RelationFollowerRequest, opts ...client.CallOption) (*RelationFollowerResponse, error)
42 | ListFriendRelation(ctx context.Context, in *RelationFriendRequest, opts ...client.CallOption) (*RelationFriendResponse, error)
43 | }
44 |
45 | type relationService struct {
46 | c client.Client
47 | name string
48 | }
49 |
50 | func NewRelationService(name string, c client.Client) RelationService {
51 | return &relationService{
52 | c: c,
53 | name: name,
54 | }
55 | }
56 |
57 | func (c *relationService) ActionRelation(ctx context.Context, in *RelationActionRequest, opts ...client.CallOption) (*RelationActionResponse, error) {
58 | req := c.c.NewRequest(c.name, "RelationService.ActionRelation", in)
59 | out := new(RelationActionResponse)
60 | err := c.c.Call(ctx, req, out, opts...)
61 | if err != nil {
62 | return nil, err
63 | }
64 | return out, nil
65 | }
66 |
67 | func (c *relationService) ListFollowRelation(ctx context.Context, in *RelationFollowRequest, opts ...client.CallOption) (*RelationFollowResponse, error) {
68 | req := c.c.NewRequest(c.name, "RelationService.ListFollowRelation", in)
69 | out := new(RelationFollowResponse)
70 | err := c.c.Call(ctx, req, out, opts...)
71 | if err != nil {
72 | return nil, err
73 | }
74 | return out, nil
75 | }
76 |
77 | func (c *relationService) ListFollowerRelation(ctx context.Context, in *RelationFollowerRequest, opts ...client.CallOption) (*RelationFollowerResponse, error) {
78 | req := c.c.NewRequest(c.name, "RelationService.ListFollowerRelation", in)
79 | out := new(RelationFollowerResponse)
80 | err := c.c.Call(ctx, req, out, opts...)
81 | if err != nil {
82 | return nil, err
83 | }
84 | return out, nil
85 | }
86 |
87 | func (c *relationService) ListFriendRelation(ctx context.Context, in *RelationFriendRequest, opts ...client.CallOption) (*RelationFriendResponse, error) {
88 | req := c.c.NewRequest(c.name, "RelationService.ListFriendRelation", in)
89 | out := new(RelationFriendResponse)
90 | err := c.c.Call(ctx, req, out, opts...)
91 | if err != nil {
92 | return nil, err
93 | }
94 | return out, nil
95 | }
96 |
97 | // Server API for RelationService service
98 |
99 | type RelationServiceHandler interface {
100 | ActionRelation(context.Context, *RelationActionRequest, *RelationActionResponse) error
101 | ListFollowRelation(context.Context, *RelationFollowRequest, *RelationFollowResponse) error
102 | ListFollowerRelation(context.Context, *RelationFollowerRequest, *RelationFollowerResponse) error
103 | ListFriendRelation(context.Context, *RelationFriendRequest, *RelationFriendResponse) error
104 | }
105 |
106 | func RegisterRelationServiceHandler(s server.Server, hdlr RelationServiceHandler, opts ...server.HandlerOption) error {
107 | type relationService interface {
108 | ActionRelation(ctx context.Context, in *RelationActionRequest, out *RelationActionResponse) error
109 | ListFollowRelation(ctx context.Context, in *RelationFollowRequest, out *RelationFollowResponse) error
110 | ListFollowerRelation(ctx context.Context, in *RelationFollowerRequest, out *RelationFollowerResponse) error
111 | ListFriendRelation(ctx context.Context, in *RelationFriendRequest, out *RelationFriendResponse) error
112 | }
113 | type RelationService struct {
114 | relationService
115 | }
116 | h := &relationServiceHandler{hdlr}
117 | return s.Handle(s.NewHandler(&RelationService{h}, opts...))
118 | }
119 |
120 | type relationServiceHandler struct {
121 | RelationServiceHandler
122 | }
123 |
124 | func (h *relationServiceHandler) ActionRelation(ctx context.Context, in *RelationActionRequest, out *RelationActionResponse) error {
125 | return h.RelationServiceHandler.ActionRelation(ctx, in, out)
126 | }
127 |
128 | func (h *relationServiceHandler) ListFollowRelation(ctx context.Context, in *RelationFollowRequest, out *RelationFollowResponse) error {
129 | return h.RelationServiceHandler.ListFollowRelation(ctx, in, out)
130 | }
131 |
132 | func (h *relationServiceHandler) ListFollowerRelation(ctx context.Context, in *RelationFollowerRequest, out *RelationFollowerResponse) error {
133 | return h.RelationServiceHandler.ListFollowerRelation(ctx, in, out)
134 | }
135 |
136 | func (h *relationServiceHandler) ListFriendRelation(ctx context.Context, in *RelationFriendRequest, out *RelationFriendResponse) error {
137 | return h.RelationServiceHandler.ListFriendRelation(ctx, in, out)
138 | }
139 |
--------------------------------------------------------------------------------
/idl/relation/relationService.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package relationService;
3 | option go_package = "./relationPb";
4 |
5 | message User {
6 | // @inject_tag: json:"id" form:"id"
7 | int64 id = 1; // 用户id
8 | // @inject_tag: json:"name" form:"name"
9 | string name = 2; // 用户名称
10 | // @inject_tag: json:"follow_count" form:"follow_count"
11 | int64 follow_count = 3; // 关注总数
12 | // @inject_tag: json:"follower_count" form:"follower_count"
13 | int64 follower_count = 4; // 粉丝总数
14 | // @inject_tag: json:"is_follow" form:"is_follow"
15 | bool is_follow = 5; // true-已关注,false-未关注
16 | // @inject_tag: json:"avatar" form:"avatar"
17 | string avatar = 6; //用户头像
18 | // @inject_tag: json:"background_image" form:"background_image"
19 | string background_image = 7; //用户个人页顶部大图
20 | // @inject_tag: json:"signature" form:"signature"
21 | string signature = 8; //个人简介
22 | // @inject_tag: json:"total_favorited" form:"total_favorited"
23 | int64 total_favorited = 9; //获赞数量
24 | // @inject_tag: json:"work_count" form:"work_count"
25 | int64 work_count = 10; //作品数量
26 | // @inject_tag: json:"favorite_count" form:"favorite_count"
27 | int64 favorite_count = 11; //点赞数量
28 | }
29 |
30 | message RelationActionRequest {
31 | // @inject_tag: json:"token" form:"token"
32 | string token = 1; // 用户鉴权token
33 | // @inject_tag: json:"to_user_id" form:"to_user_id"
34 | int64 to_user_id = 2; // 对方用户id
35 | // @inject_tag: json:"action_type" form:"action_type"
36 | int32 action_type = 3; // 1-关注,2-取消关注
37 | }
38 |
39 | message RelationActionResponse {
40 | // @inject_tag: json:"status_code" form:"status_code"
41 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
42 | // @inject_tag: json:"status_msg" form:"status_msg"
43 | string status_msg = 2; // 返回状态描述
44 | }
45 |
46 | message RelationFollowRequest {
47 | // @inject_tag: json:"user_id" form:"user_id"
48 | int64 user_id = 1; // 用户id
49 | // @inject_tag: json:"token" form:"token"
50 | string token = 2; // 用户鉴权token
51 | }
52 |
53 | message RelationFollowResponse {
54 | // @inject_tag: json:"status_code" form:"status_code"
55 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
56 | // @inject_tag: json:"status_msg" form:"status_msg"
57 | string status_msg = 2; // 返回状态描述
58 | // @inject_tag: json:"user_list" form:"user_list"
59 | repeated User user_list = 3; // 用户信息列表
60 | }
61 |
62 | message RelationFollowerRequest {
63 | // @inject_tag: json:"user_id" form:"user_id"
64 | int64 user_id = 1; // 用户id
65 | // @inject_tag: json:"token" form:"token"
66 | string token = 2; // 用户鉴权token
67 | }
68 |
69 | message RelationFollowerResponse {
70 | // @inject_tag: json:"status_code" form:"status_code"
71 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
72 | // @inject_tag: json:"status_msg" form:"status_msg"
73 | string status_msg = 2; // 返回状态描述
74 | // @inject_tag: json:"user_list" form:"user_list"
75 | repeated User user_list = 3; // 用户列表
76 | }
77 |
78 | message RelationFriendRequest {
79 | // @inject_tag: json:"user_id" form:"user_id"
80 | int64 user_id = 1; // 用户id
81 | // @inject_tag: json:"token" form:"token"
82 | string token = 2; // 用户鉴权token
83 | }
84 |
85 | message RelationFriendResponse {
86 | // @inject_tag: json:"status_code" form:"status_code"
87 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
88 | // @inject_tag: json:"status_msg" form:"status_msg"
89 | string status_msg = 2; // 返回状态描述
90 | // @inject_tag: json:"user_list" form:"user_list"
91 | repeated User user_list = 3; // 用户列表
92 | }
93 |
94 | service RelationService {
95 | rpc ActionRelation(RelationActionRequest) returns (RelationActionResponse) {}
96 | rpc ListFollowRelation(RelationFollowRequest) returns (RelationFollowResponse) {}
97 | rpc ListFollowerRelation(RelationFollowerRequest) returns (RelationFollowerResponse) {}
98 | rpc ListFriendRelation(RelationFriendRequest) returns (RelationFriendResponse) {}
99 | }
100 |
101 |
--------------------------------------------------------------------------------
/idl/user/userPb/userService.pb.micro.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-micro. DO NOT EDIT.
2 | // source: userService.proto
3 |
4 | package userPb
5 |
6 | import (
7 | fmt "fmt"
8 | proto "google.golang.org/protobuf/proto"
9 | math "math"
10 | )
11 |
12 | import (
13 | context "context"
14 | api "go-micro.dev/v4/api"
15 | client "go-micro.dev/v4/client"
16 | server "go-micro.dev/v4/server"
17 | )
18 |
19 | // Reference imports to suppress errors if they are not otherwise used.
20 | var _ = proto.Marshal
21 | var _ = fmt.Errorf
22 | var _ = math.Inf
23 |
24 | // Reference imports to suppress errors if they are not otherwise used.
25 | var _ api.Endpoint
26 | var _ context.Context
27 | var _ client.Option
28 | var _ server.Option
29 |
30 | // Api Endpoints for UserService service
31 |
32 | func NewUserServiceEndpoints() []*api.Endpoint {
33 | return []*api.Endpoint{}
34 | }
35 |
36 | // Client API for UserService service
37 |
38 | type UserService interface {
39 | Register(ctx context.Context, in *UserRequest, opts ...client.CallOption) (*UserResponse, error)
40 | Login(ctx context.Context, in *UserRequest, opts ...client.CallOption) (*UserResponse, error)
41 | UserInfo(ctx context.Context, in *UserInfoRequest, opts ...client.CallOption) (*UserInfoResponse, error)
42 | }
43 |
44 | type userService struct {
45 | c client.Client
46 | name string
47 | }
48 |
49 | func NewUserService(name string, c client.Client) UserService {
50 | return &userService{
51 | c: c,
52 | name: name,
53 | }
54 | }
55 |
56 | func (c *userService) Register(ctx context.Context, in *UserRequest, opts ...client.CallOption) (*UserResponse, error) {
57 | req := c.c.NewRequest(c.name, "UserService.Register", in)
58 | out := new(UserResponse)
59 | err := c.c.Call(ctx, req, out, opts...)
60 | if err != nil {
61 | return nil, err
62 | }
63 | return out, nil
64 | }
65 |
66 | func (c *userService) Login(ctx context.Context, in *UserRequest, opts ...client.CallOption) (*UserResponse, error) {
67 | req := c.c.NewRequest(c.name, "UserService.Login", in)
68 | out := new(UserResponse)
69 | err := c.c.Call(ctx, req, out, opts...)
70 | if err != nil {
71 | return nil, err
72 | }
73 | return out, nil
74 | }
75 |
76 | func (c *userService) UserInfo(ctx context.Context, in *UserInfoRequest, opts ...client.CallOption) (*UserInfoResponse, error) {
77 | req := c.c.NewRequest(c.name, "UserService.UserInfo", in)
78 | out := new(UserInfoResponse)
79 | err := c.c.Call(ctx, req, out, opts...)
80 | if err != nil {
81 | return nil, err
82 | }
83 | return out, nil
84 | }
85 |
86 | // Server API for UserService service
87 |
88 | type UserServiceHandler interface {
89 | Register(context.Context, *UserRequest, *UserResponse) error
90 | Login(context.Context, *UserRequest, *UserResponse) error
91 | UserInfo(context.Context, *UserInfoRequest, *UserInfoResponse) error
92 | }
93 |
94 | func RegisterUserServiceHandler(s server.Server, hdlr UserServiceHandler, opts ...server.HandlerOption) error {
95 | type userService interface {
96 | Register(ctx context.Context, in *UserRequest, out *UserResponse) error
97 | Login(ctx context.Context, in *UserRequest, out *UserResponse) error
98 | UserInfo(ctx context.Context, in *UserInfoRequest, out *UserInfoResponse) error
99 | }
100 | type UserService struct {
101 | userService
102 | }
103 | h := &userServiceHandler{hdlr}
104 | return s.Handle(s.NewHandler(&UserService{h}, opts...))
105 | }
106 |
107 | type userServiceHandler struct {
108 | UserServiceHandler
109 | }
110 |
111 | func (h *userServiceHandler) Register(ctx context.Context, in *UserRequest, out *UserResponse) error {
112 | return h.UserServiceHandler.Register(ctx, in, out)
113 | }
114 |
115 | func (h *userServiceHandler) Login(ctx context.Context, in *UserRequest, out *UserResponse) error {
116 | return h.UserServiceHandler.Login(ctx, in, out)
117 | }
118 |
119 | func (h *userServiceHandler) UserInfo(ctx context.Context, in *UserInfoRequest, out *UserInfoResponse) error {
120 | return h.UserServiceHandler.UserInfo(ctx, in, out)
121 | }
122 |
--------------------------------------------------------------------------------
/idl/user/userService.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package userService;
3 | option go_package = "./userPb";
4 |
5 | message User {
6 | // @inject_tag: json:"id" form:"id"
7 | int64 id = 1; // 用户id
8 | // @inject_tag: json:"name" form:"name"
9 | string name = 2; // 用户名称
10 | // @inject_tag: json:"follow_count" form:"follow_count"
11 | int64 follow_count = 3; // 关注总数
12 | // @inject_tag: json:"follower_count" form:"follower_count"
13 | int64 follower_count = 4; // 粉丝总数
14 | // @inject_tag: json:"is_follow" form:"is_follow"
15 | bool is_follow = 5; // true-已关注,false-未关注
16 | // @inject_tag: json:"avatar" form:"avatar"
17 | string avatar = 6; //用户头像
18 | // @inject_tag: json:"background_image" form:"background_image"
19 | string background_image = 7; //用户个人页顶部大图
20 | // @inject_tag: json:"signature" form:"signature"
21 | string signature = 8; //个人简介
22 | // @inject_tag: json:"total_favorited" form:"total_favorited"
23 | int64 total_favorited = 9; //获赞数量
24 | // @inject_tag: json:"work_count" form:"work_count"
25 | int64 work_count = 10; //作品数量
26 | // @inject_tag: json:"favorite_count" form:"favorite_count"
27 | int64 favorite_count = 11; //点赞数量
28 | }
29 |
30 | message UserRequest {
31 | // @inject_tag: json:"username" form:"username"
32 | string username = 1; // 注册用户名,最长32个字符
33 | // @inject_tag: json:"password" form:"password"
34 | string password = 2; // 密码,最长32个字符
35 | }
36 |
37 | message UserResponse {
38 | // @inject_tag: json:"status_code" form:"status_code"
39 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
40 | // @inject_tag: json:"status_msg" form:"status_msg"
41 | string status_msg = 2; // 返回状态描述
42 | // @inject_tag: json:"user_id" form:"user_id"
43 | int64 user_id = 3; // 用户id
44 | // @inject_tag: json:"token" form:"token"
45 | string token = 4; // 用户鉴权token
46 | }
47 |
48 | message UserInfoRequest {
49 | int64 user_id = 1; // 用户id
50 | string token = 2; // 用户鉴权token
51 | }
52 |
53 | message UserInfoResponse {
54 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
55 | string status_msg = 2; // 返回状态描述
56 | User user = 3; // 用户信息
57 | }
58 |
59 | service UserService {
60 | rpc Register(UserRequest) returns (UserResponse) {}
61 | rpc Login(UserRequest) returns (UserResponse) {}
62 | rpc UserInfo(UserInfoRequest) returns (UserInfoResponse) {}
63 | }
64 |
65 |
--------------------------------------------------------------------------------
/idl/video/videoPb/videoService.pb.micro.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-micro. DO NOT EDIT.
2 | // source: videoService.proto
3 |
4 | package videoPb
5 |
6 | import (
7 | fmt "fmt"
8 | proto "google.golang.org/protobuf/proto"
9 | math "math"
10 | )
11 |
12 | import (
13 | context "context"
14 | api "go-micro.dev/v4/api"
15 | client "go-micro.dev/v4/client"
16 | server "go-micro.dev/v4/server"
17 | )
18 |
19 | // Reference imports to suppress errors if they are not otherwise used.
20 | var _ = proto.Marshal
21 | var _ = fmt.Errorf
22 | var _ = math.Inf
23 |
24 | // Reference imports to suppress errors if they are not otherwise used.
25 | var _ api.Endpoint
26 | var _ context.Context
27 | var _ client.Option
28 | var _ server.Option
29 |
30 | // Api Endpoints for VideoService service
31 |
32 | func NewVideoServiceEndpoints() []*api.Endpoint {
33 | return []*api.Endpoint{}
34 | }
35 |
36 | // Client API for VideoService service
37 |
38 | type VideoService interface {
39 | Feed(ctx context.Context, in *FeedRequest, opts ...client.CallOption) (*FeedResponse, error)
40 | Publish(ctx context.Context, in *PublishRequest, opts ...client.CallOption) (*PublishResponse, error)
41 | PublishList(ctx context.Context, in *PublishListRequest, opts ...client.CallOption) (*PublishListResponse, error)
42 | }
43 |
44 | type videoService struct {
45 | c client.Client
46 | name string
47 | }
48 |
49 | func NewVideoService(name string, c client.Client) VideoService {
50 | return &videoService{
51 | c: c,
52 | name: name,
53 | }
54 | }
55 |
56 | func (c *videoService) Feed(ctx context.Context, in *FeedRequest, opts ...client.CallOption) (*FeedResponse, error) {
57 | req := c.c.NewRequest(c.name, "VideoService.Feed", in)
58 | out := new(FeedResponse)
59 | err := c.c.Call(ctx, req, out, opts...)
60 | if err != nil {
61 | return nil, err
62 | }
63 | return out, nil
64 | }
65 |
66 | func (c *videoService) Publish(ctx context.Context, in *PublishRequest, opts ...client.CallOption) (*PublishResponse, error) {
67 | req := c.c.NewRequest(c.name, "VideoService.Publish", in)
68 | out := new(PublishResponse)
69 | err := c.c.Call(ctx, req, out, opts...)
70 | if err != nil {
71 | return nil, err
72 | }
73 | return out, nil
74 | }
75 |
76 | func (c *videoService) PublishList(ctx context.Context, in *PublishListRequest, opts ...client.CallOption) (*PublishListResponse, error) {
77 | req := c.c.NewRequest(c.name, "VideoService.PublishList", in)
78 | out := new(PublishListResponse)
79 | err := c.c.Call(ctx, req, out, opts...)
80 | if err != nil {
81 | return nil, err
82 | }
83 | return out, nil
84 | }
85 |
86 | // Server API for VideoService service
87 |
88 | type VideoServiceHandler interface {
89 | Feed(context.Context, *FeedRequest, *FeedResponse) error
90 | Publish(context.Context, *PublishRequest, *PublishResponse) error
91 | PublishList(context.Context, *PublishListRequest, *PublishListResponse) error
92 | }
93 |
94 | func RegisterVideoServiceHandler(s server.Server, hdlr VideoServiceHandler, opts ...server.HandlerOption) error {
95 | type videoService interface {
96 | Feed(ctx context.Context, in *FeedRequest, out *FeedResponse) error
97 | Publish(ctx context.Context, in *PublishRequest, out *PublishResponse) error
98 | PublishList(ctx context.Context, in *PublishListRequest, out *PublishListResponse) error
99 | }
100 | type VideoService struct {
101 | videoService
102 | }
103 | h := &videoServiceHandler{hdlr}
104 | return s.Handle(s.NewHandler(&VideoService{h}, opts...))
105 | }
106 |
107 | type videoServiceHandler struct {
108 | VideoServiceHandler
109 | }
110 |
111 | func (h *videoServiceHandler) Feed(ctx context.Context, in *FeedRequest, out *FeedResponse) error {
112 | return h.VideoServiceHandler.Feed(ctx, in, out)
113 | }
114 |
115 | func (h *videoServiceHandler) Publish(ctx context.Context, in *PublishRequest, out *PublishResponse) error {
116 | return h.VideoServiceHandler.Publish(ctx, in, out)
117 | }
118 |
119 | func (h *videoServiceHandler) PublishList(ctx context.Context, in *PublishListRequest, out *PublishListResponse) error {
120 | return h.VideoServiceHandler.PublishList(ctx, in, out)
121 | }
122 |
--------------------------------------------------------------------------------
/idl/video/videoService.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package videoService;
3 | option go_package = "./videoPb";
4 |
5 |
6 | message Video {
7 | // @inject_tag: json:"id" form:"id"
8 | int64 id = 1; // 视频唯一标识
9 | // @inject_tag: json:"author" form:"author"
10 | User author = 2; // 视频作者信息
11 | // @inject_tag: json:"play_url" form:"play_url"
12 | string play_url = 3; // 视频播放地址
13 | // @inject_tag: json:"cover_url" form:"cover_url"
14 | string cover_url = 4; // 视频封面地址
15 | // @inject_tag: json:"favorite_count" form:"favorite_count"
16 | int64 favorite_count = 5; // 视频的点赞总数
17 | // @inject_tag: json:"comment_count" form:"comment_count"
18 | int64 comment_count = 6; // 视频的评论总数
19 | // @inject_tag: json:"is_favorite" form:"is_favorite"
20 | bool is_favorite = 7; // true-已点赞,false-未点赞
21 | // @inject_tag: json:"title" form:"title"
22 | string title = 8; // 视频标题
23 | }
24 |
25 | message User {
26 | // @inject_tag: json:"id" form:"id"
27 | int64 id = 1; // 用户id
28 | // @inject_tag: json:"name" form:"name"
29 | string name = 2; // 用户名称
30 | // @inject_tag: json:"follow_count" form:"follow_count"
31 | int64 follow_count = 3; // 关注总数
32 | // @inject_tag: json:"follower_count" form:"follower_count"
33 | int64 follower_count = 4; // 粉丝总数
34 | // @inject_tag: json:"is_follow" form:"is_follow"
35 | bool is_follow = 5; // true-已关注,false-未关注
36 | // @inject_tag: json:"avatar" form:"avatar"
37 | string avatar = 6; //用户头像
38 | // @inject_tag: json:"background_image" form:"background_image"
39 | string background_image = 7; //用户个人页顶部大图
40 | // @inject_tag: json:"signature" form:"signature"
41 | string signature = 8; //个人简介
42 | // @inject_tag: json:"total_favorited" form:"total_favorited"
43 | int64 total_favorited = 9; //获赞数量
44 | // @inject_tag: json:"work_count" form:"work_count"
45 | int64 work_count = 10; //作品数量
46 | // @inject_tag: json:"favorite_count" form:"favorite_count"
47 | int64 favorite_count = 11; //点赞数量
48 | }
49 |
50 | message FeedRequest {
51 | int64 latest_time = 1; // 可选参数,限制返回视频的最新投稿时间戳,精确到秒,不填表示当前时间
52 | string token = 2; // 可选参数,登录用户设置
53 | }
54 |
55 | message FeedResponse {
56 | // @inject_tag: json:"status_code" form:"status_code"
57 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
58 | // @inject_tag: json:"status_msg" form:"status_msg"
59 | string status_msg = 2; // 返回状态描述
60 | // @inject_tag: json:"video_list" form:"video_list"
61 | repeated Video video_list = 3; // 视频列表
62 | // @inject_tag: json:"next_time" form:"next_time"
63 | int64 next_time = 4; // 本次返回的视频中,发布最早的时间,作为下次请求时的latest_time
64 | }
65 |
66 |
67 | message PublishRequest{
68 | // @inject_tag: json:"token" form:"token"
69 | string token = 1; // 用户鉴权token
70 | // @inject_tag: json:"data" form:"data"
71 | bytes data = 2; // 视频数据
72 | // @inject_tag: json:"title" form:"title"
73 | string title = 3; // 视频标题
74 | }
75 |
76 | message PublishResponse {
77 | // @inject_tag: json:"status_code" form:"status_code"
78 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
79 | // @inject_tag: json:"status_msg" form:"status_msg"
80 | string status_msg = 2; // 返回状态描述
81 | }
82 |
83 | message PublishListRequest {
84 | // @inject_tag: json:"user_id" form:"user_id"
85 | int64 user_id = 1; // 用户id
86 | // @inject_tag: json:"token" form:"token"
87 | string token = 2; // 用户鉴权token
88 | }
89 |
90 | message PublishListResponse {
91 | // @inject_tag: json:"status_code" form:"status_code"
92 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
93 | // @inject_tag: json:"status_msg" form:"status_msg"
94 | string status_msg = 2; // 返回状态描述
95 | // @inject_tag: json:"video_list" form:"video_list"
96 | repeated Video video_list = 3; // 用户发布的视频列表
97 | }
98 |
99 | service VideoService {
100 | rpc Feed(FeedRequest) returns (FeedResponse) {}
101 | rpc Publish(PublishRequest) returns (PublishResponse) {}
102 | rpc PublishList(PublishListRequest) returns (PublishListResponse) {}
103 | }
--------------------------------------------------------------------------------
/model/comment.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import "time"
4 |
5 | type Comment struct {
6 | ID uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
7 | UserID int `gorm:"column:user_id" json:"user_id"`
8 | User User `gorm:"foreignKey:UserID;AssociationForeignKey:ID" json:"user"`
9 | VideoID int `gorm:"column:video_id;index" json:"video_id"`
10 | Video Video `gorm:"foreignKey:VideoID;AssociationForeignKey:ID" json:"video"`
11 | Content string `gorm:"column:content;size:1024" json:"content"`
12 | CreatedAt time.Time ` json:"created_at"`
13 | }
14 |
--------------------------------------------------------------------------------
/model/favorite.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import "time"
4 |
5 | type Favorite struct {
6 | ID uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
7 | UserID int `gorm:"column:user_id;index:idx_favorite" json:"user_id"`
8 | User User `gorm:"foreignKey:UserID;AssociationForeignKey:ID" json:"user"`
9 | VideoID int `gorm:"column:video_id;index:idx_favorite_video;index:idx_favorite" json:"video_id"`
10 | Video Video `gorm:"foreignKey:VideoID;AssociationForeignKey:ID" json:"video"`
11 | CreatedAt time.Time ` json:"created_at"`
12 | }
13 |
--------------------------------------------------------------------------------
/model/follow.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import "time"
4 |
5 | type Follow struct {
6 | ID uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
7 | UserID int `gorm:"column:user_id;index:idx_follow_user;index:idx_follow" json:"user_id"`
8 | User User `gorm:"foreignKey:UserID;AssociationForeignKey:ID" json:"user"`
9 | FollowedUserID int `gorm:"column:followed_user_id;index:idx_follow_followed;index:idx_follow" json:"followed_user_id"`
10 | FollowedUser User `gorm:"foreignKey:FollowedUserID;AssociationForeignKey:ID" json:"followed_user"`
11 | CreatedAt time.Time `json:"created_at"`
12 | }
13 |
--------------------------------------------------------------------------------
/model/message.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import "time"
4 |
5 | type Message struct {
6 | ID uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
7 | FromUserID int `gorm:"column:from_user_id;index:idx_message" json:"from_user_id"`
8 | FromUser User `gorm:"foreignKey:FromUserID;AssociationForeignKey:ID" json:"from_user"`
9 | ToUserID int `gorm:"column:to_user_id;index:idx_message" json:"to_user_id"`
10 | ToUser User `gorm:"foreignKey:ToUserID;AssociationForeignKey:ID" json:"to_user"`
11 | Content string `gorm:"column:content;size:1024" json:"content"`
12 | CreatedAt time.Time ` json:"created_at"`
13 | }
14 |
--------------------------------------------------------------------------------
/model/user.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import "time"
4 |
5 | type User struct {
6 | ID uint `gorm:"column:id;primaryKey;autoIncrement;index" json:"id"`
7 | Username string `gorm:"column:username;unique;index" json:"username"`
8 | Password string `gorm:"column:password" json:"password"`
9 | Avatar string `gorm:"column:avatar" json:"avatar"`
10 | BackgroundImage string `gorm:"column:background_image" json:"background_image"`
11 | Signature string `gorm:"column:signature" json:"signature"`
12 | CreatedAt time.Time `json:"created_at"`
13 | }
14 |
--------------------------------------------------------------------------------
/model/video.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import "time"
4 |
5 | type Video struct {
6 | ID uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
7 | AuthorID int `gorm:"column:author_id;index" json:"author_id"`
8 | Author User `gorm:"foreignKey:AuthorID;AssociationForeignKey:ID" json:"author"`
9 | PlayUrl string `gorm:"column:play_url" json:"play_url"`
10 | CoverUrl string `gorm:"column:cover_url" json:"cover_url"`
11 | Title string `gorm:"column:title" json:"title"`
12 | CreatedAt time.Time `json:"created_at"`
13 | }
14 |
--------------------------------------------------------------------------------
/mq/consumer.go:
--------------------------------------------------------------------------------
1 | package mq
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/streadway/amqp"
7 | )
8 |
9 | // ConsumeMessage MQ到mysql
10 | func ConsumeMessage(ctx context.Context, queueName string) (msg <-chan amqp.Delivery, err error) {
11 | ch, err := RabbitMQ.Channel()
12 | if err != nil {
13 | return
14 | }
15 | q, _ := ch.QueueDeclare(queueName, true, false, false, false, nil)
16 | err = ch.Qos(1, 0, false)
17 | return ch.Consume(q.Name, "", false, false, false, false, nil)
18 | }
19 |
--------------------------------------------------------------------------------
/mq/init.go:
--------------------------------------------------------------------------------
1 | package mq
2 |
3 | import (
4 | "ByteRhythm/config"
5 | "fmt"
6 |
7 | "github.com/streadway/amqp"
8 | )
9 |
10 | var RabbitMQ *amqp.Connection
11 |
12 | func InitRabbitMQ() {
13 | config.Init()
14 | connString := fmt.Sprintf("%s://%s:%s@%s:%s/",
15 | config.RabbitMQ,
16 | config.RabbitMQUser,
17 | config.RabbitMQPassWord,
18 | config.RabbitMQHost,
19 | config.RabbitMQPort,
20 | )
21 | conn, err := amqp.Dial(connString)
22 | if err != nil {
23 | panic(err)
24 | }
25 | RabbitMQ = conn
26 | }
27 |
--------------------------------------------------------------------------------
/mq/producer.go:
--------------------------------------------------------------------------------
1 | package mq
2 |
3 | import (
4 | "github.com/streadway/amqp"
5 | )
6 |
7 | func SendMessage2MQ(body []byte, queueName string) (err error) {
8 | ch, err := RabbitMQ.Channel()
9 | if err != nil {
10 | return
11 | }
12 | q, _ := ch.QueueDeclare(queueName, true, false, false, false, nil)
13 | err = ch.Publish("", q.Name, false, false, amqp.Publishing{
14 | DeliveryMode: amqp.Persistent,
15 | ContentType: "application/json",
16 | Body: body,
17 | })
18 | if err != nil {
19 | return
20 | }
21 | return
22 | }
23 |
--------------------------------------------------------------------------------
/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | ./gateway &
3 | ./user &
4 | ./video &
5 | ./favorite &
6 | ./comment &
7 | ./relation &
8 | ./message &
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | cd /usr/local/bin/ &&./etcd &
3 | cd /usr/local/bin/ &&./jaeger-all-in-one --collector.zipkin.host-port=:9411 &
4 | systemctl start rabbitmq-server &
5 | systemctl start redis-server
6 |
7 | ./gateway &
8 | ./user &
9 | ./video &
10 | ./favorite &
11 | ./comment &
12 | ./relation &
13 | ./message &
14 |
--------------------------------------------------------------------------------
/test/comment_test.go:
--------------------------------------------------------------------------------
1 | package test
2 |
3 | import (
4 | "ByteRhythm/app/favorite/service"
5 | "encoding/json"
6 | "fmt"
7 | "github.com/stretchr/testify/assert"
8 | "io"
9 | "net/http"
10 | "strings"
11 | "testing"
12 | )
13 |
14 | // -----------------/douyin/comment/action/接口测试--------------------
15 | // 发布评论
16 | func TestCommentAction(t *testing.T) {
17 | baseUrl := "http://192.168.256.128:8080/douyin/comment/action/?"
18 | token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6OSwidXNlcm5hbWUiOiJ0ZXN0MiIsInBhc3N3b3JkIjoiZTEwYWRjMzk0OWJhNTlhYmJlNTZlMDU3ZjIwZjg4M2UiLCJhdmF0YXIiOiJodHRwOi8vcnoybjg3eWNrLmhuLWJrdC5jbG91ZGRuLmNvbS9hdmF0YXIuanBnIiwiYmFja2dyb3VuZF9pbWFnZSI6Imh0dHA6Ly9yejJuODd5Y2suaG4tYmt0LmNsb3VkZG4uY29tL2JhY2tncm91bmQucG5nIiwic2lnbmF0dXJlIjoi5Y-I5p2l55yL5oiR55qE5Li76aG15ZWmfiIsImNyZWF0ZWRfYXQiOiIyMDIzLTA4LTMwVDAyOjIyOjMzLTA3OjAwIiwiZXhwIjoxNjkzMzg4MDAxLCJpYXQiOjE2OTMzODc5NzEsImlzcyI6InRlc3QyIn0.11RhlMGupHJDoetGndAWSiuNyBeAUHNEIF81VcVJkR0"
19 | video_id := "1"
20 | action_type := "1" //发布评论
21 | comment_text := "只狼太好玩了!"
22 | url := baseUrl + "token=" + token + "&video_id=" + video_id + "&action_type=" + action_type + "&comment_text=" + comment_text
23 |
24 | method := "POST"
25 | client := &http.Client{}
26 | req, err := http.NewRequest(method, url, nil)
27 | assert.Empty(t, err)
28 |
29 | res, err := client.Do(req)
30 | assert.Empty(t, err)
31 | defer func(Body io.ReadCloser) {
32 | err := Body.Close()
33 | assert.Empty(t, err)
34 | }(res.Body)
35 |
36 | body, err := io.ReadAll(res.Body)
37 | str := string(body)
38 | assert.Empty(t, err)
39 | favor := service.GetFavoriteSrv()
40 | err = json.Unmarshal(body, &favor)
41 | assert.Empty(t, err)
42 | fmt.Printf(string(body))
43 | assert.Equal(t, strings.Contains(str, "评论成功"), true)
44 |
45 | }
46 |
47 | // 删除评论
48 | func TestDeleteCommentAction(t *testing.T) {
49 | baseUrl := "http://192.168.256.128:8080/douyin/comment/action/?"
50 | token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6OSwidXNlcm5hbWUiOiJ0ZXN0MiIsInBhc3N3b3JkIjoiZTEwYWRjMzk0OWJhNTlhYmJlNTZlMDU3ZjIwZjg4M2UiLCJhdmF0YXIiOiJodHRwOi8vcnoybjg3eWNrLmhuLWJrdC5jbG91ZGRuLmNvbS9hdmF0YXIuanBnIiwiYmFja2dyb3VuZF9pbWFnZSI6Imh0dHA6Ly9yejJuODd5Y2suaG4tYmt0LmNsb3VkZG4uY29tL2JhY2tncm91bmQucG5nIiwic2lnbmF0dXJlIjoi5Y-I5p2l55yL5oiR55qE5Li76aG15ZWmfiIsImNyZWF0ZWRfYXQiOiIyMDIzLTA4LTMwVDAyOjIyOjMzLTA3OjAwIiwiZXhwIjoxNjkzMzg4MDAxLCJpYXQiOjE2OTMzODc5NzEsImlzcyI6InRlc3QyIn0.11RhlMGupHJDoetGndAWSiuNyBeAUHNEIF81VcVJkR0"
51 | video_id := "1"
52 | action_type := "2" //删除评论
53 | comment_id := "1"
54 | url := baseUrl + "token=" + token + "&video_id=" + video_id + "&action_type=" + action_type + "&comment_id=" + comment_id
55 |
56 | method := "POST"
57 | client := &http.Client{}
58 | req, err := http.NewRequest(method, url, nil)
59 | assert.Empty(t, err)
60 |
61 | res, err := client.Do(req)
62 | assert.Empty(t, err)
63 | defer func(Body io.ReadCloser) {
64 | err := Body.Close()
65 | assert.Empty(t, err)
66 | }(res.Body)
67 |
68 | body, err := io.ReadAll(res.Body)
69 | str := string(body)
70 | assert.Empty(t, err)
71 | favor := service.GetFavoriteSrv()
72 | err = json.Unmarshal(body, &favor)
73 | assert.Empty(t, err)
74 | fmt.Printf(string(body))
75 | assert.Equal(t, strings.Contains(str, "评论删除成功"), true)
76 |
77 | }
78 |
79 | //-----------------/douyin/comment/list接口测试--------------------
80 |
81 | // 获取评论列表成功
82 | func TestCommentList(t *testing.T) {
83 | baseUrl := "http://192.168.256.128:8080/douyin/comment/list/?"
84 | token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6OSwidXNlcm5hbWUiOiJ0ZXN0MiIsInBhc3N3b3JkIjoiZTEwYWRjMzk0OWJhNTlhYmJlNTZlMDU3ZjIwZjg4M2UiLCJhdmF0YXIiOiJodHRwOi8vcnoybjg3eWNrLmhuLWJrdC5jbG91ZGRuLmNvbS9hdmF0YXIuanBnIiwiYmFja2dyb3VuZF9pbWFnZSI6Imh0dHA6Ly9yejJuODd5Y2suaG4tYmt0LmNsb3VkZG4uY29tL2JhY2tncm91bmQucG5nIiwic2lnbmF0dXJlIjoi5Y-I5p2l55yL5oiR55qE5Li76aG15ZWmfiIsImNyZWF0ZWRfYXQiOiIyMDIzLTA4LTMwVDAyOjIyOjMzLTA3OjAwIiwiZXhwIjoxNjkzMzg4MDAxLCJpYXQiOjE2OTMzODc5NzEsImlzcyI6InRlc3QyIn0.11RhlMGupHJDoetGndAWSiuNyBeAUHNEIF81VcVJkR0"
85 | video_id := "1"
86 | url := baseUrl + "token=" + token + "&video_id=" + video_id
87 |
88 | method := "GET"
89 | client := &http.Client{}
90 | req, err := http.NewRequest(method, url, nil)
91 | assert.Empty(t, err)
92 |
93 | res, err := client.Do(req)
94 | assert.Empty(t, err)
95 | defer func(Body io.ReadCloser) {
96 | err := Body.Close()
97 | assert.Empty(t, err)
98 | }(res.Body)
99 |
100 | body, err := io.ReadAll(res.Body)
101 | str := string(body)
102 | assert.Empty(t, err)
103 | favor := service.GetFavoriteSrv()
104 | err = json.Unmarshal(body, &favor)
105 | assert.Empty(t, err)
106 | fmt.Printf(string(body))
107 | assert.Equal(t, strings.Contains(str, "获取评论列表成功"), true)
108 |
109 | }
110 |
--------------------------------------------------------------------------------
/test/favorite_test.go:
--------------------------------------------------------------------------------
1 | package test
2 |
3 | import (
4 | "ByteRhythm/app/favorite/service"
5 | "encoding/json"
6 | "fmt"
7 | "github.com/stretchr/testify/assert"
8 | "io"
9 | "net/http"
10 | "strings"
11 | "testing"
12 | )
13 |
14 | // -----------------/douyin/favorite/action/接口测试--------------------
15 | // 点赞成功
16 | func TestFavoriteAction(t *testing.T) {
17 | baseUrl := "http://192.168.256.128:8080/douyin/favorite/action/?"
18 | video_id := "1"
19 | token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTEsInVzZXJuYW1lIjoidGVzdDIiLCJwYXNzd29yZCI6IjIwMmNiOTYyYWM1OTA3NWI5NjRiMDcxNTJkMjM0YjcwIiwiYXZhdGFyIjoiaHR0cDovL3J6Mm44N3ljay5obi1ia3QuY2xvdWRkbi5jb20vYXZhdGFyLmpwZyIsImJhY2tncm91bmRfaW1hZ2UiOiJodHRwOi8vcnoybjg3eWNrLmhuLWJrdC5jbG91ZGRuLmNvbS9iYWNrZ3JvdW5kLnBuZyIsInNpZ25hdHVyZSI6IuWPiOadpeeci-aIkeeahOS4u-mhteWVpn4iLCJjcmVhdGVkX2F0IjoiMjAyMy0wOC0zMFQwNDo1NDo1Mi0wNzowMCIsImV4cCI6MTY5MzM5NjYyMiwiaWF0IjoxNjkzMzk2NTAyLCJpc3MiOiJ0ZXN0MiJ9.6CiMJkVJDk-nSpl28qgQXmTkgGqIobQff-Zg7MBMioc"
20 | action_type := "1"
21 | url := baseUrl + "token=" + token + "&video_id=" + video_id + "&action_type=" + action_type
22 |
23 | method := "POST"
24 | client := &http.Client{}
25 | req, err := http.NewRequest(method, url, nil)
26 | assert.Empty(t, err)
27 |
28 | res, err := client.Do(req)
29 | assert.Empty(t, err)
30 | defer func(Body io.ReadCloser) {
31 | err := Body.Close()
32 | assert.Empty(t, err)
33 | }(res.Body)
34 |
35 | body, err := io.ReadAll(res.Body)
36 | assert.Empty(t, err)
37 | favor := service.GetFavoriteSrv()
38 | err = json.Unmarshal(body, &favor)
39 | assert.Empty(t, err)
40 | fmt.Printf(string(body))
41 |
42 | }
43 |
44 | // 重复点赞
45 | func TestDuplicateFavoriteAction(t *testing.T) {
46 | baseUrl := "http://192.168.256.128:8080/douyin/favorite/action/?"
47 | video_id := "3"
48 | token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTEsInVzZXJuYW1lIjoidGVzdDIiLCJwYXNzd29yZCI6IjIwMmNiOTYyYWM1OTA3NWI5NjRiMDcxNTJkMjM0YjcwIiwiYXZhdGFyIjoiaHR0cDovL3J6Mm44N3ljay5obi1ia3QuY2xvdWRkbi5jb20vYXZhdGFyLmpwZyIsImJhY2tncm91bmRfaW1hZ2UiOiJodHRwOi8vcnoybjg3eWNrLmhuLWJrdC5jbG91ZGRuLmNvbS9iYWNrZ3JvdW5kLnBuZyIsInNpZ25hdHVyZSI6IuWPiOadpeeci-aIkeeahOS4u-mhteWVpn4iLCJjcmVhdGVkX2F0IjoiMjAyMy0wOC0zMFQwNDo1NDo1Mi0wNzowMCIsImV4cCI6MTY5MzM5NjYyMiwiaWF0IjoxNjkzMzk2NTAyLCJpc3MiOiJ0ZXN0MiJ9.6CiMJkVJDk-nSpl28qgQXmTkgGqIobQff-Zg7MBMioc"
49 | action_type := "1"
50 | url := baseUrl + "token=" + token + "&video_id=" + video_id + "&action_type=" + action_type
51 |
52 | method := "POST"
53 | client := &http.Client{}
54 | req, err := http.NewRequest(method, url, nil)
55 | assert.Empty(t, err)
56 |
57 | res, err := client.Do(req)
58 | assert.Empty(t, err)
59 | defer func(Body io.ReadCloser) {
60 | err := Body.Close()
61 | assert.Empty(t, err)
62 | }(res.Body)
63 |
64 | body, err := io.ReadAll(res.Body)
65 | str := string(body)
66 | assert.Empty(t, err)
67 | favor := service.GetFavoriteSrv()
68 | err = json.Unmarshal(body, &favor)
69 | assert.Empty(t, err)
70 | fmt.Printf(string(body))
71 | assert.Equal(t, strings.Contains(str, "重复点赞"), true)
72 | }
73 |
74 | // 取消点赞
75 | func TestCancelFavoriteAction(t *testing.T) {
76 | baseUrl := "http://192.168.256.128:8080/douyin/favorite/action/?"
77 | video_id := "3"
78 | token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTEsInVzZXJuYW1lIjoidGVzdDIiLCJwYXNzd29yZCI6IjIwMmNiOTYyYWM1OTA3NWI5NjRiMDcxNTJkMjM0YjcwIiwiYXZhdGFyIjoiaHR0cDovL3J6Mm44N3ljay5obi1ia3QuY2xvdWRkbi5jb20vYXZhdGFyLmpwZyIsImJhY2tncm91bmRfaW1hZ2UiOiJodHRwOi8vcnoybjg3eWNrLmhuLWJrdC5jbG91ZGRuLmNvbS9iYWNrZ3JvdW5kLnBuZyIsInNpZ25hdHVyZSI6IuWPiOadpeeci-aIkeeahOS4u-mhteWVpn4iLCJjcmVhdGVkX2F0IjoiMjAyMy0wOC0zMFQwNDo1NDo1Mi0wNzowMCIsImV4cCI6MTY5MzM5NjYyMiwiaWF0IjoxNjkzMzk2NTAyLCJpc3MiOiJ0ZXN0MiJ9.6CiMJkVJDk-nSpl28qgQXmTkgGqIobQff-Zg7MBMioc"
79 | action_type := "2"
80 | url := baseUrl + "token=" + token + "&video_id=" + video_id + "&action_type=" + action_type
81 |
82 | method := "POST"
83 | client := &http.Client{}
84 | req, err := http.NewRequest(method, url, nil)
85 | assert.Empty(t, err)
86 |
87 | res, err := client.Do(req)
88 | assert.Empty(t, err)
89 | defer func(Body io.ReadCloser) {
90 | err := Body.Close()
91 | assert.Empty(t, err)
92 | }(res.Body)
93 |
94 | body, err := io.ReadAll(res.Body)
95 | assert.Empty(t, err)
96 | favor := service.GetFavoriteSrv()
97 | err = json.Unmarshal(body, &favor)
98 | assert.Empty(t, err)
99 | fmt.Printf(string(body))
100 |
101 | }
102 |
103 | // -----------------/douyin/favorite/list/接口测试--------------------
104 | func TestFavoriteList(t *testing.T) {
105 | baseUrl := "http://192.168.256.128:8080/douyin/favorite/list/?"
106 | user_id := "11"
107 | token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTEsInVzZXJuYW1lIjoidGVzdDIiLCJwYXNzd29yZCI6IjIwMmNiOTYyYWM1OTA3NWI5NjRiMDcxNTJkMjM0YjcwIiwiYXZhdGFyIjoiaHR0cDovL3J6Mm44N3ljay5obi1ia3QuY2xvdWRkbi5jb20vYXZhdGFyLmpwZyIsImJhY2tncm91bmRfaW1hZ2UiOiJodHRwOi8vcnoybjg3eWNrLmhuLWJrdC5jbG91ZGRuLmNvbS9iYWNrZ3JvdW5kLnBuZyIsInNpZ25hdHVyZSI6IuWPiOadpeeci-aIkeeahOS4u-mhteWVpn4iLCJjcmVhdGVkX2F0IjoiMjAyMy0wOC0zMFQwNDo1NDo1Mi0wNzowMCIsImV4cCI6MTY5MzM5NjYyMiwiaWF0IjoxNjkzMzk2NTAyLCJpc3MiOiJ0ZXN0MiJ9.6CiMJkVJDk-nSpl28qgQXmTkgGqIobQff-Zg7MBMioc"
108 | url := baseUrl + "user_id=" + user_id + "token=" + token
109 |
110 | method := "GET"
111 | client := &http.Client{}
112 | req, err := http.NewRequest(method, url, nil)
113 | assert.Empty(t, err)
114 |
115 | res, err := client.Do(req)
116 | assert.Empty(t, err)
117 | defer func(Body io.ReadCloser) {
118 | err := Body.Close()
119 | assert.Empty(t, err)
120 | }(res.Body)
121 |
122 | body, err := io.ReadAll(res.Body)
123 | str := string(body)
124 | assert.Empty(t, err)
125 | favor := service.GetFavoriteSrv()
126 | err = json.Unmarshal(body, &favor)
127 | assert.Empty(t, err)
128 | fmt.Printf(string(body))
129 | assert.Equal(t, strings.Contains(str, "获取喜欢列表成功"), true)
130 | }
131 |
--------------------------------------------------------------------------------
/test/feed_test.go:
--------------------------------------------------------------------------------
1 | package test
2 |
3 | import (
4 | "ByteRhythm/app/video/service"
5 | "encoding/json"
6 | "fmt"
7 | "github.com/stretchr/testify/assert"
8 | "io/ioutil"
9 | "net/http"
10 | "strings"
11 | "testing"
12 | )
13 |
14 | //-----------------/douyin/feed/接口测试--------------------
15 |
16 | // 获取视频流成功
17 | func TestFeed(t *testing.T) {
18 |
19 | url := "http://192.168.256.128:8080/douyin/feed/?latest_time=2023-08-30%2007%3A56%3A40.213&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJ0ZXN0MSIsInBhc3N3b3JkIjoiZTEwYWRjMzk0OWJhNTlhYmJlNTZlMDU3ZjIwZjg4M2UiLCJhdmF0YXIiOiJodHRwOi8vcnoybjg3eWNrLmhuLWJrdC5jbG91ZGRuLmNvbS9hdmF0YXIuanBnIiwiYmFja2dyb3VuZF9pbWFnZSI6Imh0dHA6Ly9yejJuODd5Y2suaG4tYmt0LmNsb3VkZG4uY29tL2JhY2tncm91bmQucG5nIiwic2lnbmF0dXJlIjoi5Y-I5p2l55yL5oiR55qE5Li76aG15ZWmfiIsImNyZWF0ZWRfYXQiOiIyMDIzLTA4LTMwVDA3OjI5OjAwLTA3OjAwIiwiZXhwIjoxNjkzNDA3NzEyLCJpYXQiOjE2OTM0MDc1OTIsImlzcyI6InRlc3QxIn0.ns00VSFt3dGUYY73h4P_njeP6F17HahTlmLJ7sJZWjM"
20 | method := "GET"
21 |
22 | client := &http.Client{}
23 | req, err := http.NewRequest(method, url, nil)
24 |
25 | if err != nil {
26 | fmt.Println(err)
27 | return
28 | }
29 | res, err := client.Do(req)
30 | if err != nil {
31 | fmt.Println(err)
32 | return
33 | }
34 | defer res.Body.Close()
35 |
36 | body, err := ioutil.ReadAll(res.Body)
37 | str := string(body)
38 | if err != nil {
39 | fmt.Println(err)
40 | return
41 | }
42 | fmt.Println(string(body))
43 | assert.Empty(t, err)
44 | video := service.GetVideoSrv()
45 | err = json.Unmarshal(body, &video)
46 | assert.Empty(t, err)
47 | assert.Equal(t, strings.Contains(str, "获取视频流成功"), true)
48 | }
49 |
--------------------------------------------------------------------------------
/test/login_test.go:
--------------------------------------------------------------------------------
1 | package test
2 |
3 | import (
4 | "ByteRhythm/app/user/service"
5 | "encoding/json"
6 | "fmt"
7 | "github.com/stretchr/testify/assert"
8 | "io/ioutil"
9 | "net/http"
10 | "strings"
11 | "testing"
12 | )
13 |
14 | //-----------------/douyin/user/login/接口测试--------------------
15 |
16 | // 登陆成功
17 | func TestLogin(t *testing.T) {
18 |
19 | url := "http://192.168.256.128:8080/douyin/user/login/?username=test1&password=123456"
20 | method := "POST"
21 | client := &http.Client{}
22 | req, err := http.NewRequest(method, url, nil)
23 |
24 | if err != nil {
25 | fmt.Println(err)
26 | return
27 | }
28 | res, err := client.Do(req)
29 | if err != nil {
30 | fmt.Println(err)
31 | return
32 | }
33 | defer res.Body.Close()
34 |
35 | body, err := ioutil.ReadAll(res.Body)
36 | if err != nil {
37 | fmt.Println(err)
38 | return
39 | }
40 | str := string(body)
41 | fmt.Println(str)
42 | user := service.GetUserSrv()
43 | err = json.Unmarshal(body, &user)
44 | assert.Empty(t, err)
45 | assert.Equal(t, strings.Contains(str, "登录成功"), true) //登录成功则为true
46 | }
47 |
48 | // 用户名为空进行登录
49 | func TestLogin_EmptyName(t *testing.T) {
50 |
51 | url := "http://192.168.256.128:8080/douyin/user/login/?username=&password=123456"
52 | method := "POST"
53 | client := &http.Client{}
54 | req, err := http.NewRequest(method, url, nil)
55 |
56 | if err != nil {
57 | fmt.Println(err)
58 | return
59 | }
60 | res, err := client.Do(req)
61 | if err != nil {
62 | fmt.Println(err)
63 | return
64 | }
65 | defer res.Body.Close()
66 |
67 | body, err := ioutil.ReadAll(res.Body)
68 | if err != nil {
69 | fmt.Println(err)
70 | return
71 | }
72 | str := string(body)
73 | fmt.Println(str)
74 | user := service.GetUserSrv()
75 | err = json.Unmarshal(body, &user)
76 | assert.Empty(t, err)
77 | assert.Equal(t, strings.Contains(str, "用户名或密码错误"), true)
78 | }
79 |
80 | // 密码为空进行登录
81 | func TestLogin_EmptyPassword(t *testing.T) {
82 |
83 | url := "http://192.168.256.128:8080/douyin/user/login/?username=test1&password="
84 | method := "POST"
85 | client := &http.Client{}
86 | req, err := http.NewRequest(method, url, nil)
87 |
88 | if err != nil {
89 | fmt.Println(err)
90 | return
91 | }
92 | res, err := client.Do(req)
93 | if err != nil {
94 | fmt.Println(err)
95 | return
96 | }
97 | defer res.Body.Close()
98 |
99 | body, err := ioutil.ReadAll(res.Body)
100 | if err != nil {
101 | fmt.Println(err)
102 | return
103 | }
104 | str := string(body)
105 | fmt.Println(str)
106 | user := service.GetUserSrv()
107 | err = json.Unmarshal(body, &user)
108 | assert.Empty(t, err)
109 | assert.Equal(t, strings.Contains(str, "用户名或密码错误"), true)
110 | }
111 |
112 | // 未注册的用户名登录
113 | func TestLogin_NoRegister(t *testing.T) {
114 |
115 | url := "http://192.168.256.128:8080/douyin/user/login/?username=test3&password=123"
116 | method := "POST"
117 | client := &http.Client{}
118 | req, err := http.NewRequest(method, url, nil)
119 |
120 | if err != nil {
121 | fmt.Println(err)
122 | return
123 | }
124 | res, err := client.Do(req)
125 | if err != nil {
126 | fmt.Println(err)
127 | return
128 | }
129 | defer res.Body.Close()
130 |
131 | body, err := ioutil.ReadAll(res.Body)
132 | if err != nil {
133 | fmt.Println(err)
134 | return
135 | }
136 | str := string(body)
137 | fmt.Println(str)
138 | user := service.GetUserSrv()
139 | err = json.Unmarshal(body, &user)
140 | assert.Empty(t, err)
141 | assert.Equal(t, strings.Contains(str, "用户名或密码错误"), true)
142 | }
143 |
--------------------------------------------------------------------------------
/test/message_test.go:
--------------------------------------------------------------------------------
1 | package test
2 |
3 | import (
4 | "ByteRhythm/app/favorite/service"
5 | "encoding/json"
6 | "fmt"
7 | "github.com/stretchr/testify/assert"
8 | "io"
9 | "net/http"
10 | "strings"
11 | "testing"
12 | )
13 |
14 | // -----------------/douyin/message/chat/接口测试--------------------
15 | // 获取聊天记录成功!
16 | func TestChatMessage(t *testing.T) {
17 | baseUrl := "http://192.168.256.128:8080/douyin/message/chat/?"
18 | token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6OSwidXNlcm5hbWUiOiJ0ZXN0MiIsInBhc3N3b3JkIjoiZTEwYWRjMzk0OWJhNTlhYmJlNTZlMDU3ZjIwZjg4M2UiLCJhdmF0YXIiOiJodHRwOi8vcnoybjg3eWNrLmhuLWJrdC5jbG91ZGRuLmNvbS9hdmF0YXIuanBnIiwiYmFja2dyb3VuZF9pbWFnZSI6Imh0dHA6Ly9yejJuODd5Y2suaG4tYmt0LmNsb3VkZG4uY29tL2JhY2tncm91bmQucG5nIiwic2lnbmF0dXJlIjoi5Y-I5p2l55yL5oiR55qE5Li76aG15ZWmfiIsImNyZWF0ZWRfYXQiOiIyMDIzLTA4LTMwVDAyOjIyOjMzLTA3OjAwIiwiZXhwIjoxNjkzMzg4MDAxLCJpYXQiOjE2OTMzODc5NzEsImlzcyI6InRlc3QyIn0.11RhlMGupHJDoetGndAWSiuNyBeAUHNEIF81VcVJkR0"
19 | to_user_id := "2"
20 | pre_msg_time := "2023-08-30%2023%3A32%3A49"
21 | url := baseUrl + "token=" + token + "&to_user_id=" + to_user_id + "&pre_msg_time=" + pre_msg_time
22 |
23 | method := "GET"
24 | client := &http.Client{}
25 | req, err := http.NewRequest(method, url, nil)
26 | assert.Empty(t, err)
27 |
28 | res, err := client.Do(req)
29 | assert.Empty(t, err)
30 | defer func(Body io.ReadCloser) {
31 | err := Body.Close()
32 | assert.Empty(t, err)
33 | }(res.Body)
34 |
35 | body, err := io.ReadAll(res.Body)
36 | str := string(body)
37 | assert.Empty(t, err)
38 | favor := service.GetFavoriteSrv()
39 | err = json.Unmarshal(body, &favor)
40 | assert.Empty(t, err)
41 | fmt.Printf(string(body))
42 | assert.Equal(t, strings.Contains(str, "获取聊天记录成功!"), true)
43 |
44 | }
45 |
46 | // 不能查看与自己的聊天记录!
47 | func TestChatMessageMyself(t *testing.T) {
48 | baseUrl := "http://192.168.256.128:8080/douyin/message/chat/?"
49 | token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6OSwidXNlcm5hbWUiOiJ0ZXN0MiIsInBhc3N3b3JkIjoiZTEwYWRjMzk0OWJhNTlhYmJlNTZlMDU3ZjIwZjg4M2UiLCJhdmF0YXIiOiJodHRwOi8vcnoybjg3eWNrLmhuLWJrdC5jbG91ZGRuLmNvbS9hdmF0YXIuanBnIiwiYmFja2dyb3VuZF9pbWFnZSI6Imh0dHA6Ly9yejJuODd5Y2suaG4tYmt0LmNsb3VkZG4uY29tL2JhY2tncm91bmQucG5nIiwic2lnbmF0dXJlIjoi5Y-I5p2l55yL5oiR55qE5Li76aG15ZWmfiIsImNyZWF0ZWRfYXQiOiIyMDIzLTA4LTMwVDAyOjIyOjMzLTA3OjAwIiwiZXhwIjoxNjkzMzg4MDAxLCJpYXQiOjE2OTMzODc5NzEsImlzcyI6InRlc3QyIn0.11RhlMGupHJDoetGndAWSiuNyBeAUHNEIF81VcVJkR0"
50 | to_user_id := "1"
51 | pre_msg_time := "2023-08-30%2023%3A32%3A49"
52 | url := baseUrl + "token=" + token + "&to_user_id=" + to_user_id + "&pre_msg_time=" + pre_msg_time
53 |
54 | method := "GET"
55 | client := &http.Client{}
56 | req, err := http.NewRequest(method, url, nil)
57 | assert.Empty(t, err)
58 |
59 | res, err := client.Do(req)
60 | assert.Empty(t, err)
61 | defer func(Body io.ReadCloser) {
62 | err := Body.Close()
63 | assert.Empty(t, err)
64 | }(res.Body)
65 |
66 | body, err := io.ReadAll(res.Body)
67 | str := string(body)
68 | assert.Empty(t, err)
69 | favor := service.GetFavoriteSrv()
70 | err = json.Unmarshal(body, &favor)
71 | assert.Empty(t, err)
72 | fmt.Printf(string(body))
73 | assert.Equal(t, strings.Contains(str, "不能查看与自己的聊天记录!"), true)
74 |
75 | }
76 |
77 | // -----------------/douyin/message/action/接口测试--------------------
78 | // 发送消息成功!
79 | func TestActionMessage(t *testing.T) {
80 | baseUrl := "http://192.168.256.128:8080/douyin/message/action/?"
81 | token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6OSwidXNlcm5hbWUiOiJ0ZXN0MiIsInBhc3N3b3JkIjoiZTEwYWRjMzk0OWJhNTlhYmJlNTZlMDU3ZjIwZjg4M2UiLCJhdmF0YXIiOiJodHRwOi8vcnoybjg3eWNrLmhuLWJrdC5jbG91ZGRuLmNvbS9hdmF0YXIuanBnIiwiYmFja2dyb3VuZF9pbWFnZSI6Imh0dHA6Ly9yejJuODd5Y2suaG4tYmt0LmNsb3VkZG4uY29tL2JhY2tncm91bmQucG5nIiwic2lnbmF0dXJlIjoi5Y-I5p2l55yL5oiR55qE5Li76aG15ZWmfiIsImNyZWF0ZWRfYXQiOiIyMDIzLTA4LTMwVDAyOjIyOjMzLTA3OjAwIiwiZXhwIjoxNjkzMzg4MDAxLCJpYXQiOjE2OTMzODc5NzEsImlzcyI6InRlc3QyIn0.11RhlMGupHJDoetGndAWSiuNyBeAUHNEIF81VcVJkR0"
82 | to_user_id := "2"
83 | action_type := "1"
84 | content := "你弦一郎打了多少遍?"
85 | url := baseUrl + "token=" + token + "&to_user_id=" + to_user_id + "&action_type=" + action_type + "&content=" + content
86 |
87 | method := "POST"
88 | client := &http.Client{}
89 | req, err := http.NewRequest(method, url, nil)
90 | assert.Empty(t, err)
91 |
92 | res, err := client.Do(req)
93 | assert.Empty(t, err)
94 | defer func(Body io.ReadCloser) {
95 | err := Body.Close()
96 | assert.Empty(t, err)
97 | }(res.Body)
98 |
99 | body, err := io.ReadAll(res.Body)
100 | str := string(body)
101 | assert.Empty(t, err)
102 | favor := service.GetFavoriteSrv()
103 | err = json.Unmarshal(body, &favor)
104 | assert.Empty(t, err)
105 | fmt.Printf(string(body))
106 | assert.Equal(t, strings.Contains(str, "发送消息成功!"), true)
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/test/publish_test.go:
--------------------------------------------------------------------------------
1 | package test
2 |
3 | import (
4 | "ByteRhythm/app/video/service"
5 | "bytes"
6 | "encoding/json"
7 | "fmt"
8 | "github.com/stretchr/testify/assert"
9 | "io"
10 | "mime/multipart"
11 | "net/http"
12 | "os"
13 | "strings"
14 | "testing"
15 | )
16 |
17 | //-----------------/douyin/publish/action/接口测试--------------------
18 |
19 | // 发布成功
20 | func TestPublish(t *testing.T) {
21 | url := "http://192.168.256.128:8080/douyin/publish/action/"
22 | method := "POST"
23 | filePath := "/home/jan/Downloads/1.png" //这里的视频一定要确保自己电脑上有
24 | client := &http.Client{}
25 |
26 | payload := &bytes.Buffer{}
27 | writer := multipart.NewWriter(payload)
28 |
29 | file, err := os.Open(filePath)
30 | assert.Empty(t, err)
31 | defer func(file *os.File) {
32 | err := file.Close()
33 | assert.Empty(t, err)
34 | }(file)
35 | fileWriter, err := writer.CreateFormFile("data", file.Name())
36 | fmt.Printf(file.Name())
37 | assert.Empty(t, err)
38 |
39 | _, err = io.Copy(fileWriter, file)
40 | assert.Empty(t, err)
41 |
42 | _ = writer.WriteField("token", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTYsInVzZXJuYW1lIjoidGVzdDEiLCJwYXNzd29yZCI6ImUxMGFkYzM5NDliYTU5YWJiZTU2ZTA1N2YyMGY4ODNlIiwiYXZhdGFyIjoiaHR0cDovL3J6Mm44N3ljay5obi1ia3QuY2xvdWRkbi5jb20vYXZhdGFyLmpwZyIsImJhY2tncm91bmRfaW1hZ2UiOiJodHRwOi8vcnoybjg3eWNrLmhuLWJrdC5jbG91ZGRuLmNvbS9iYWNrZ3JvdW5kLnBuZyIsInNpZ25hdHVyZSI6IuWPiOadpeeci-aIkeeahOS4u-mhteWVpn4iLCJjcmVhdGVkX2F0IjoiMjAyMy0wOC0zMFQwNjowMzowMy0wNzowMCIsImV4cCI6MTY5MzQwNDY2OCwiaWF0IjoxNjkzNDA0NTQ4LCJpc3MiOiJ0ZXN0MSJ9.2dlgE1GpvyIjvXTK5Jl9d4ayQm2Rd9Czhf1tGMzue4A")
43 | _ = writer.WriteField("title", "只狼")
44 | err = writer.Close()
45 | assert.Empty(t, err)
46 |
47 | req, err := http.NewRequest(method, url, payload)
48 | assert.Empty(t, err)
49 |
50 | req.Header.Set("Content-Type", writer.FormDataContentType())
51 |
52 | res, err := client.Do(req)
53 | assert.Empty(t, err)
54 | defer func(Body io.ReadCloser) {
55 | err := Body.Close()
56 | assert.Empty(t, err)
57 | }(res.Body)
58 |
59 | body, err := io.ReadAll(res.Body)
60 | str := string(body)
61 | //fmt.Printf(str)
62 | assert.Empty(t, err)
63 | video := service.GetVideoSrv()
64 | err = json.Unmarshal(body, &video)
65 | assert.Empty(t, err)
66 | assert.Equal(t, strings.Contains(str, "发布成功"), true)
67 |
68 | }
69 |
70 | //-----------------/douyin/publish/list/接口测试--------------------
71 |
72 | // 获取成功
73 | func TestPublishList(t *testing.T) {
74 | url := "http://192.168.256.128:8080/douyin/publish/list/?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJ0ZXN0MSIsInBhc3N3b3JkIjoiZTEwYWRjMzk0OWJhNTlhYmJlNTZlMDU3ZjIwZjg4M2UiLCJhdmF0YXIiOiJodHRwOi8vcnoybjg3eWNrLmhuLWJrdC5jbG91ZGRuLmNvbS9hdmF0YXIuanBnIiwiYmFja2dyb3VuZF9pbWFnZSI6Imh0dHA6Ly9yejJuODd5Y2suaG4tYmt0LmNsb3VkZG4uY29tL2JhY2tncm91bmQucG5nIiwic2lnbmF0dXJlIjoi5Y-I5p2l55yL5oiR55qE5Li76aG15ZWmfiIsImNyZWF0ZWRfYXQiOiIyMDIzLTA4LTMwVDA3OjI5OjAwLTA3OjAwIiwiZXhwIjoxNjkzNDA3ODEwLCJpYXQiOjE2OTM0MDc2OTAsImlzcyI6InRlc3QxIn0.hFbVJbjg-Ec9sdR0P1ufO4SM4goIU4njlPHMVScHZ0s&user_id=1"
75 | method := "GET"
76 | client := &http.Client{}
77 | req, err := http.NewRequest(method, url, nil)
78 | assert.Empty(t, err)
79 |
80 | res, err := client.Do(req)
81 | assert.Empty(t, err)
82 | defer func(Body io.ReadCloser) {
83 | err := Body.Close()
84 | assert.Empty(t, err)
85 | }(res.Body)
86 |
87 | body, err := io.ReadAll(res.Body)
88 | str := string(body)
89 | assert.Empty(t, err)
90 | feed := service.GetVideoSrv()
91 | err = json.Unmarshal(body, &feed)
92 | assert.Empty(t, err)
93 | assert.Equal(t, strings.Contains(str, "获取成功"), true)
94 | }
95 |
--------------------------------------------------------------------------------
/test/register_test.go:
--------------------------------------------------------------------------------
1 | package test
2 |
3 | import (
4 | "ByteRhythm/app/user/service"
5 | "encoding/json"
6 | "fmt"
7 | "github.com/stretchr/testify/assert"
8 | "io/ioutil"
9 | "net/http"
10 | "strings"
11 | "testing"
12 | )
13 |
14 | //-----------------/douyin/user/register/接口测试--------------------
15 |
16 | // 用用户名为test1,密码为123456进行注册
17 | func TestRegister(t *testing.T) {
18 | url := "http://192.168.256.128:8080/douyin/user/register/?username=test1&password=123456"
19 | method := "POST"
20 |
21 | client := &http.Client{}
22 | req, err := http.NewRequest(method, url, nil)
23 |
24 | if err != nil {
25 | fmt.Println(err)
26 | return
27 | }
28 | res, err := client.Do(req)
29 | if err != nil {
30 | fmt.Println(err)
31 | return
32 | }
33 | defer res.Body.Close()
34 |
35 | body, err := ioutil.ReadAll(res.Body)
36 | if err != nil {
37 | fmt.Println(err)
38 | return
39 | }
40 | str := string(body)
41 | fmt.Println(str)
42 | user := service.GetUserSrv()
43 | err = json.Unmarshal(body, &user)
44 | assert.Empty(t, err)
45 | assert.Equal(t, strings.Contains(str, "注册成功"), true) //如果是第一次注册则为true
46 | }
47 |
48 | // 重复用户名进行注册
49 | func TestDisplayRegister_DuplicatedName(t *testing.T) {
50 | url := "http://192.168.256.128:8080/douyin/user/register/?username=test1&password=123"
51 | method := "POST"
52 | client := &http.Client{}
53 | req, err := http.NewRequest(method, url, nil)
54 |
55 | if err != nil {
56 | fmt.Println(err)
57 | return
58 | }
59 | res, err := client.Do(req)
60 | if err != nil {
61 | fmt.Println(err)
62 | return
63 | }
64 | defer res.Body.Close()
65 |
66 | body, err := ioutil.ReadAll(res.Body)
67 | if err != nil {
68 | fmt.Println(err)
69 | return
70 | }
71 | str := string(body)
72 | fmt.Println(str)
73 | user := service.GetUserSrv()
74 | err = json.Unmarshal(body, &user)
75 | assert.Empty(t, err)
76 | assert.Equal(t, strings.Contains(str, "用户名已存在"), true) //如果该用户名已经注册了则为true
77 | }
78 |
79 | // 用户名超过32位
80 | func TestOver32Name(t *testing.T) {
81 | url := "http://192.168.256.128:8080/douyin/user/register/?username=123456789999999999999999999999999996666666&password=123"
82 | method := "POST"
83 | client := &http.Client{}
84 | req, err := http.NewRequest(method, url, nil)
85 |
86 | if err != nil {
87 | fmt.Println(err)
88 | return
89 | }
90 | res, err := client.Do(req)
91 | if err != nil {
92 | fmt.Println(err)
93 | return
94 | }
95 | defer res.Body.Close()
96 |
97 | body, err := ioutil.ReadAll(res.Body)
98 | if err != nil {
99 | fmt.Println(err)
100 | return
101 | }
102 | str := string(body)
103 | fmt.Println(str)
104 | user := service.GetUserSrv()
105 | err = json.Unmarshal(body, &user)
106 | assert.Empty(t, err)
107 | assert.Equal(t, strings.Contains(str, "用户名或密码不能超过32位"), true) //如果该用户名超过32位则为true
108 | }
109 |
110 | // 密码超过32位
111 | func TestOver32Password(t *testing.T) {
112 | url := "http://192.168.256.128:8080/douyin/user/register/?username=test2&password=12345678999999999999999999999999999"
113 | method := "POST"
114 | client := &http.Client{}
115 | req, err := http.NewRequest(method, url, nil)
116 |
117 | if err != nil {
118 | fmt.Println(err)
119 | return
120 | }
121 | res, err := client.Do(req)
122 | if err != nil {
123 | fmt.Println(err)
124 | return
125 | }
126 | defer res.Body.Close()
127 |
128 | body, err := ioutil.ReadAll(res.Body)
129 | if err != nil {
130 | fmt.Println(err)
131 | return
132 | }
133 | str := string(body)
134 | fmt.Println(str)
135 | user := service.GetUserSrv()
136 | err = json.Unmarshal(body, &user)
137 | assert.Empty(t, err)
138 | assert.Equal(t, strings.Contains(str, "用户名或密码不能超过32位"), true) //如果密码超过32位则为true
139 | }
140 |
--------------------------------------------------------------------------------
/test/user_test.go:
--------------------------------------------------------------------------------
1 | package test
2 |
3 | import (
4 | "ByteRhythm/app/user/service"
5 | "encoding/json"
6 | "fmt"
7 | "github.com/stretchr/testify/assert"
8 | "io/ioutil"
9 | "net/http"
10 | "strings"
11 | "testing"
12 | )
13 |
14 | //--------/douyin/user/接口测试--------------------------
15 |
16 | // 获取用户信息成功,是test1用户的
17 | func TestUserInfo(t *testing.T) {
18 | url := "http://192.168.256.128:8080/douyin/user/?user_id=10&token=" +
19 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTAsInVzZXJuYW1lIjoidGVzdDEiLCJwYXNzd29yZCI6ImUxMGFkYzM5NDliYTU5YWJiZTU2ZTA1N2YyMGY4ODNlIiwiYXZhdGFyIjoiaHR0cDovL3J6Mm44N3ljay5obi1ia3QuY2xvdWRkbi5jb20vYXZhdGFyLmpwZyIsImJhY2tncm91bmRfaW1hZ2UiOiJodHRwOi8vcnoybjg3eWNrLmhuLWJrdC5jbG91ZGRuLmNvbS9iYWNrZ3JvdW5kLnBuZyIsInNpZ25hdHVyZSI6IuWPiOadpeeci-aIkeeahOS4u-mhteWVpn4iLCJjcmVhdGVkX2F0IjoiMjAyMy0wOC0zMFQwNDo0Njo1OC0wNzowMCIsImV4cCI6MTY5MzM5NjQzOCwiaWF0IjoxNjkzMzk2MzE4LCJpc3MiOiJ0ZXN0MSJ9.NQymZNYRryaBFpbaYApSqBuwKfyYEIIGHLZGRNEn1as"
20 | //token随时都在变,要测的时候再具体修改
21 | method := "GET"
22 | client := &http.Client{}
23 | req, err := http.NewRequest(method, url, nil)
24 | if err != nil {
25 | fmt.Println(err)
26 | return
27 | }
28 | res, err := client.Do(req)
29 | if err != nil {
30 | fmt.Println(err)
31 | return
32 | }
33 | defer res.Body.Close()
34 |
35 | body, err := ioutil.ReadAll(res.Body)
36 | if err != nil {
37 | fmt.Println(err)
38 | return
39 | }
40 | str := string(body)
41 | fmt.Println(str)
42 | user := service.GetUserSrv()
43 | err = json.Unmarshal(body, &user)
44 | assert.Empty(t, err)
45 | assert.Equal(t, strings.Contains(str, "获取用户信息成功"), true)
46 | }
47 |
48 | // 用户不存在
49 | func TestNoUser(t *testing.T) {
50 | url := "http://192.168.256.128:8080/douyin/user/?user_id=14&token=" +
51 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTAsInVzZXJuYW1lIjoidGVzdDEiLCJwYXNzd29yZCI6ImUxMGFkYzM5NDliYTU5YWJiZTU2ZTA1N2YyMGY4ODNlIiwiYXZhdGFyIjoiaHR0cDovL3J6Mm44N3ljay5obi1ia3QuY2xvdWRkbi5jb20vYXZhdGFyLmpwZyIsImJhY2tncm91bmRfaW1hZ2UiOiJodHRwOi8vcnoybjg3eWNrLmhuLWJrdC5jbG91ZGRuLmNvbS9iYWNrZ3JvdW5kLnBuZyIsInNpZ25hdHVyZSI6IuWPiOadpeeci-aIkeeahOS4u-mhteWVpn4iLCJjcmVhdGVkX2F0IjoiMjAyMy0wOC0zMFQwNDo0Njo1OC0wNzowMCIsImV4cCI6MTY5MzM5NjQzOCwiaWF0IjoxNjkzMzk2MzE4LCJpc3MiOiJ0ZXN0MSJ9.NQymZNYRryaBFpbaYApSqBuwKfyYEIIGHLZGRNEn1as"
52 | method := "GET"
53 | client := &http.Client{}
54 | req, err := http.NewRequest(method, url, nil)
55 | if err != nil {
56 | fmt.Println(err)
57 | return
58 | }
59 | res, err := client.Do(req)
60 | if err != nil {
61 | fmt.Println(err)
62 | return
63 | }
64 | defer res.Body.Close()
65 |
66 | body, err := ioutil.ReadAll(res.Body)
67 | if err != nil {
68 | fmt.Println(err)
69 | return
70 | }
71 | str := string(body)
72 | fmt.Println(str)
73 | user := service.GetUserSrv()
74 | err = json.Unmarshal(body, &user)
75 | assert.Empty(t, err)
76 | assert.Equal(t, strings.Contains(str, "用户不存在"), true)
77 | }
78 |
--------------------------------------------------------------------------------
/util/array_change.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "strconv"
5 | "strings"
6 | )
7 |
8 | // StringArray2IntArray 将字符串数组转化为整数数组
9 | func StringArray2IntArray(strArray []string) []int {
10 | var intArray []int
11 | for _, str := range strArray {
12 | num, _ := strconv.Atoi(str[strings.Index(str, ":")+1:])
13 | intArray = append(intArray, num)
14 | }
15 | return intArray
16 | }
17 |
--------------------------------------------------------------------------------
/util/fail_request.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import "github.com/gin-gonic/gin"
4 |
5 | func FailRequest(StatusMsg string) gin.H {
6 | return gin.H{
7 | "status_code": 1,
8 | "status_msg": StatusMsg,
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/util/md5.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "crypto/md5"
5 | "fmt"
6 | )
7 |
8 | func Md5(str string) string {
9 | hash := md5.New()
10 | hash.Write([]byte(str))
11 | return fmt.Sprintf("%x", hash.Sum(nil))
12 | }
13 |
--------------------------------------------------------------------------------
/util/sort.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "sort"
5 | "strconv"
6 | "strings"
7 | )
8 |
9 | func SortKeys(keys []string) []string {
10 | // 对key按照第二个%d排序(%d可能不止一位),并改为降序排序
11 | sort.Slice(keys, func(i, j int) bool {
12 | // 提取第二个%d后的数字进行比较
13 | num1, _ := strconv.Atoi(keys[i][strings.Index(keys[i], ":")+1:])
14 | num2, _ := strconv.Atoi(keys[j][strings.Index(keys[j], ":")+1:])
15 | return num1 > num2
16 | })
17 |
18 | // 只保留前30个视频
19 | if len(keys) > 30 {
20 | keys = keys[:30]
21 | }
22 |
23 | return keys
24 | }
25 |
--------------------------------------------------------------------------------
/util/token.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "ByteRhythm/model"
5 | "fmt"
6 | "log"
7 | "time"
8 |
9 | "github.com/dgrijalva/jwt-go"
10 | )
11 |
12 | const (
13 | KEY string = "JWT-ARY-STARK"
14 | DefaultExpireSeconds int = 3600 // default 60 minutes
15 | )
16 |
17 | // MyCustomClaims JWT -- json web token
18 | // HEADER PAYLOAD SIGNATURE
19 | // This struct is the PAYLOAD
20 | type MyCustomClaims struct {
21 | model.User
22 | jwt.StandardClaims
23 | }
24 |
25 | // RefreshToken update expireAt and return a new token
26 | func RefreshToken(tokenString string) (string, error) {
27 | // first get previous token
28 | token, err := jwt.ParseWithClaims(
29 | tokenString,
30 | &MyCustomClaims{},
31 | func(token *jwt.Token) (interface{}, error) {
32 | return []byte(KEY), nil
33 | })
34 | claims, ok := token.Claims.(*MyCustomClaims)
35 | if !ok || !token.Valid {
36 | return "", err
37 | }
38 | mySigningKey := []byte(KEY)
39 | expireAt := time.Now().Add(time.Second * time.Duration(DefaultExpireSeconds)).Unix()
40 | newClaims := MyCustomClaims{
41 | claims.User,
42 | jwt.StandardClaims{
43 | ExpiresAt: expireAt,
44 | Issuer: claims.User.Username,
45 | IssuedAt: time.Now().Unix(),
46 | },
47 | }
48 | // generate new token with new claims
49 | newToken := jwt.NewWithClaims(jwt.SigningMethodHS256, newClaims)
50 | tokenStr, err := newToken.SignedString(mySigningKey)
51 | if err != nil {
52 | fmt.Println("generate new fresh json web token failed !! error :", err)
53 | return "", err
54 | }
55 | return tokenStr, err
56 | }
57 |
58 | func ValidateToken(tokenString string) error {
59 | token, err := jwt.ParseWithClaims(
60 | tokenString,
61 | &MyCustomClaims{},
62 | func(token *jwt.Token) (interface{}, error) {
63 | return []byte(KEY), nil
64 | })
65 | if err != nil {
66 | log.Println("validate tokenString failed !!!", err)
67 | return err
68 | }
69 | if claims, ok := token.Claims.(*MyCustomClaims); ok && token.Valid {
70 | //fmt.Printf("%v %v", claims.User, claims.StandardClaims.ExpiresAt)
71 | fmt.Println("token will be expired at ", time.Unix(claims.StandardClaims.ExpiresAt, 0))
72 | } else {
73 | log.Println("validate tokenString failed !!!", err)
74 | return err
75 | }
76 | return nil
77 | }
78 |
79 | func GenerateToken(user *model.User, expiredSeconds int) (tokenString string) {
80 | if expiredSeconds == 0 {
81 | expiredSeconds = DefaultExpireSeconds
82 | }
83 | // Create the Claims
84 | mySigningKey := []byte(KEY)
85 | expireAt := time.Now().Add(time.Second * time.Duration(expiredSeconds)).Unix()
86 | fmt.Println("token will be expired at ", time.Unix(expireAt, 0))
87 | // pass parameter to this func or not
88 |
89 | claims := MyCustomClaims{
90 | *user,
91 | jwt.StandardClaims{
92 | ExpiresAt: expireAt,
93 | Issuer: user.Username,
94 | IssuedAt: time.Now().Unix(),
95 | },
96 | }
97 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
98 | tokenStr, err := token.SignedString(mySigningKey)
99 | if err != nil {
100 | log.Println("generate json web token failed !! error :", err)
101 | }
102 | return tokenStr
103 |
104 | }
105 |
106 | func GetUsernameFromToken(tokenString string) (string, error) {
107 | token, err := jwt.ParseWithClaims(
108 | tokenString,
109 | &MyCustomClaims{},
110 | func(token *jwt.Token) (interface{}, error) {
111 | return []byte(KEY), nil
112 | })
113 |
114 | if err != nil {
115 | return "", err
116 | }
117 |
118 | claims, ok := token.Claims.(*MyCustomClaims)
119 | if !ok || !token.Valid {
120 | return "", fmt.Errorf("invalid token")
121 | }
122 |
123 | return claims.User.Username, nil
124 | }
125 |
126 | func GetUserFromToken(tokenString string) (*model.User, error) {
127 | token, err := jwt.ParseWithClaims(
128 | tokenString,
129 | &MyCustomClaims{},
130 | func(token *jwt.Token) (interface{}, error) {
131 | return []byte(KEY), nil
132 | })
133 |
134 | if err != nil {
135 | return nil, err
136 | }
137 |
138 | claims, ok := token.Claims.(*MyCustomClaims)
139 | if !ok || !token.Valid {
140 | return nil, fmt.Errorf("invalid token")
141 | }
142 |
143 | return &claims.User, nil
144 | }
145 |
146 | func GetUserIdFromToken(tokenString string) (int, error) {
147 | token, err := jwt.ParseWithClaims(
148 | tokenString,
149 | &MyCustomClaims{},
150 | func(token *jwt.Token) (interface{}, error) {
151 | return []byte(KEY), nil
152 | })
153 |
154 | if err != nil {
155 | return -1, err
156 | }
157 |
158 | claims, ok := token.Claims.(*MyCustomClaims)
159 | if !ok || !token.Valid {
160 | return -1, fmt.Errorf("invalid token")
161 | }
162 |
163 | return int(claims.User.ID), nil
164 | }
165 |
--------------------------------------------------------------------------------
/util/upload.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "ByteRhythm/config"
5 | "bytes"
6 | "context"
7 | "fmt"
8 | "strings"
9 |
10 | "github.com/qiniu/go-sdk/v7/auth/qbox"
11 | "github.com/qiniu/go-sdk/v7/storage"
12 | )
13 |
14 | func UploadVideo(data []byte) (VideoUrl string, err error) {
15 | config.Init()
16 | size := int64(len(data))
17 | key := fmt.Sprintf("%s.mp4", GenerateUUID())
18 | putPolicy := storage.PutPolicy{
19 | Scope: fmt.Sprintf("%s:%s", config.Bucket, key),
20 | }
21 | mac := qbox.NewMac(config.AccessKey, config.SecretKey)
22 | upToken := putPolicy.UploadToken(mac)
23 | cfg := storage.Config{}
24 | uploader := storage.NewFormUploader(&cfg)
25 | ret := storage.PutRet{}
26 | putExtra := storage.PutExtra{
27 | Params: map[string]string{
28 | "x:name": "github logo",
29 | },
30 | }
31 | err = uploader.Put(context.Background(), &ret, upToken, key, bytes.NewReader(data), size, &putExtra)
32 | if err != nil {
33 | return "", err
34 | }
35 |
36 | return fmt.Sprintf("%s/%s", config.Domain, ret.Key), nil
37 | }
38 |
39 | func UploadJPG(imgPath string, videoUrl string) string {
40 | config.Init()
41 |
42 | videoName := strings.Split(strings.Replace(videoUrl, config.Domain+"/", "", -1), ".")[0]
43 | key := fmt.Sprintf("%s.%s", videoName+"_cover", "jpg")
44 |
45 | putPolicy := storage.PutPolicy{
46 | Scope: config.Bucket,
47 | }
48 | mac := qbox.NewMac(config.AccessKey, config.SecretKey)
49 | upToken := putPolicy.UploadToken(mac)
50 | cfg := storage.Config{}
51 |
52 | // 构建表单上传的对象
53 | formUploader := storage.NewFormUploader(&cfg)
54 | ret := storage.PutRet{}
55 |
56 | // 可选配置
57 | putExtra := storage.PutExtra{
58 | Params: map[string]string{
59 | "x:name": "github logo",
60 | },
61 | }
62 | err := formUploader.PutFile(context.Background(), &ret, upToken, key, imgPath, &putExtra)
63 | if err != nil {
64 | fmt.Println(err)
65 | return ""
66 | }
67 | return fmt.Sprintf("%s/%s", config.Domain, ret.Key)
68 | }
69 |
--------------------------------------------------------------------------------
/util/uuid.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import "github.com/google/uuid"
4 |
5 | func GenerateUUID() string {
6 | id := uuid.New()
7 | return id.String()
8 | }
9 |
--------------------------------------------------------------------------------