├── How We Built Whimsical@1.100000023841858x.webp
├── LICENSE
├── README.md
├── common
├── globalkey
│ ├── constantKey.go
│ └── redisCacheKey.go
├── help
│ ├── cos
│ │ ├── download.go
│ │ ├── snowAlg.go
│ │ ├── updateComment.go
│ │ └── uploadVideo.go
│ └── token
│ │ ├── common.go
│ │ ├── genToken.go
│ │ └── parseToken.go
├── messageTypes
│ └── message.go
└── xerr
│ ├── errCode.go
│ ├── errMsg.go
│ └── errors.go
├── deploy
├── README.md
├── api
│ └── Dockerfile
├── asynqJob
│ ├── client-scheduler
│ │ └── Dockerfile
│ └── server
│ │ └── Dockerfile
├── asynqUI.sh
├── docker-compose.yml
├── mq
│ └── Dockerfile
├── rpc-user-info
│ └── Dockerfile
├── rpc-user-operate
│ └── Dockerfile
└── rpc-video-service
│ └── Dockerfile
├── go.mod
├── go.sum
├── service
├── api
│ ├── api
│ │ ├── build.sh
│ │ ├── user.api
│ │ ├── userOpt.api
│ │ └── video.api
│ ├── etc
│ │ └── user-api.yaml
│ ├── internal
│ │ ├── config
│ │ │ └── config.go
│ │ ├── handler
│ │ │ ├── feed
│ │ │ │ └── feedVideoListHandler.go
│ │ │ ├── publish
│ │ │ │ ├── getPublishVideoListHandler.go
│ │ │ │ └── publishVideoHandler.go
│ │ │ ├── routes.go
│ │ │ ├── user
│ │ │ │ ├── userInfoHandler.go
│ │ │ │ ├── userLoginHandler.go
│ │ │ │ └── userRegisterHandler.go
│ │ │ └── userOpt
│ │ │ │ ├── commentOptHandler.go
│ │ │ │ ├── favoriteOptHandler.go
│ │ │ │ ├── followOptHandler.go
│ │ │ │ ├── getCommentListHandler.go
│ │ │ │ ├── getFavoriteListHandler.go
│ │ │ │ ├── getFollowListHandler.go
│ │ │ │ └── getFollowerListHandler.go
│ │ ├── logic
│ │ │ ├── feed
│ │ │ │ └── feedVideoListLogic.go
│ │ │ ├── publish
│ │ │ │ ├── getPublishVideoListLogic.go
│ │ │ │ └── publishVideoLogic.go
│ │ │ ├── user
│ │ │ │ ├── userInfoLogic.go
│ │ │ │ ├── userLoginLogic.go
│ │ │ │ └── userRegisterLogic.go
│ │ │ └── userOpt
│ │ │ │ ├── commentOptLogic.go
│ │ │ │ ├── favoriteOptLogic.go
│ │ │ │ ├── followOptLogic.go
│ │ │ │ ├── getCommentListLogic.go
│ │ │ │ ├── getFavoriteListLogic.go
│ │ │ │ ├── getFollowListLogic.go
│ │ │ │ └── getFollowerListLogic.go
│ │ ├── middleware
│ │ │ ├── authjwtMiddleware.go
│ │ │ └── isloginMiddleware.go
│ │ ├── svc
│ │ │ └── serviceContext.go
│ │ └── types
│ │ │ └── types.go
│ └── user.go
├── asynqJob
│ ├── README.md
│ ├── client-scheduler
│ │ ├── etc
│ │ │ └── scheduler.yaml
│ │ ├── internal
│ │ │ ├── config
│ │ │ │ └── config.go
│ │ │ ├── logic
│ │ │ │ ├── register.go
│ │ │ │ └── setScheduler.go
│ │ │ └── svc
│ │ │ │ ├── scheduler.go
│ │ │ │ └── serviceContext.go
│ │ └── scheduler.go
│ └── server
│ │ ├── etc
│ │ └── server.yaml
│ │ ├── internal
│ │ ├── config
│ │ │ └── config.go
│ │ ├── logic
│ │ │ ├── jobs
│ │ │ │ ├── GetUserFavoriteStatus.go
│ │ │ │ └── GetUserFollowStatus.go
│ │ │ └── routes.go
│ │ └── svc
│ │ │ ├── asynqServer.go
│ │ │ └── serviceContext.go
│ │ ├── jobtype
│ │ └── jobtype.go
│ │ └── server.go
├── mq
│ ├── etc
│ │ └── mq.yaml
│ ├── internal
│ │ ├── config
│ │ │ └── config.go
│ │ ├── listen
│ │ │ ├── kqMqs.go
│ │ │ └── listen.go
│ │ ├── mqs
│ │ │ └── kq
│ │ │ │ ├── userCommentUpdate.go
│ │ │ │ ├── userFavoriteUpdate.go
│ │ │ │ └── userFollowUpdate.go
│ │ └── svc
│ │ │ └── serviceContext.go
│ └── mq.go
├── rpc-user-info
│ ├── etc
│ │ └── userInfoService.yaml
│ ├── internal
│ │ ├── config
│ │ │ └── config.go
│ │ ├── logic
│ │ │ ├── authsInfoLogic.go
│ │ │ ├── infoLogic.go
│ │ │ ├── loginLogic.go
│ │ │ └── registerLogic.go
│ │ ├── server
│ │ │ └── userInfoServiceServer.go
│ │ └── svc
│ │ │ └── serviceContext.go
│ ├── model
│ │ ├── userModel.go
│ │ ├── userModel_gen.go
│ │ └── vars.go
│ ├── userInfoPb
│ │ ├── GenPb
│ │ │ ├── UserInfoService.proto
│ │ │ └── build.sh
│ │ ├── UserInfoService.pb.go
│ │ └── UserInfoService_grpc.pb.go
│ ├── userInfoService.go
│ └── userinfoservice
│ │ └── userInfoService.go
├── rpc-user-operate
│ ├── etc
│ │ └── userOptService.yaml
│ ├── internal
│ │ ├── config
│ │ │ └── config.go
│ │ ├── logic
│ │ │ ├── getUserFavoriteLogic.go
│ │ │ ├── getUserFollowLogic.go
│ │ │ ├── getUserFollowerLogic.go
│ │ │ ├── getVideoCommentLogic.go
│ │ │ ├── updateCommentStatusLogic.go
│ │ │ ├── updateFavoriteStatusLogic.go
│ │ │ └── updateFollowStatusLogic.go
│ │ ├── server
│ │ │ └── userOptServiceServer.go
│ │ └── svc
│ │ │ └── serviceContext.go
│ ├── model
│ │ ├── userCommentListModel.go
│ │ ├── userCommentListModel_gen.go
│ │ ├── userFavoriteListModel.go
│ │ ├── userFavoriteListModel_gen.go
│ │ ├── userFollowListModel.go
│ │ ├── userFollowListModel_gen.go
│ │ └── vars.go
│ ├── userOptPb
│ │ ├── GenPb
│ │ │ ├── UserOptService.proto
│ │ │ └── build.sh
│ │ ├── UserOptService.pb.go
│ │ └── UserOptService_grpc.pb.go
│ ├── userOptService.go
│ └── useroptservice
│ │ └── userOptService.go
└── rpc-video-service
│ ├── etc
│ └── videoService.yaml
│ ├── internal
│ ├── config
│ │ └── config.go
│ ├── logic
│ │ ├── feedVideosLogic.go
│ │ ├── getMyFavoriteVideosLogic.go
│ │ ├── getVideoListLogic.go
│ │ └── pubVideoLogic.go
│ ├── server
│ │ └── videoServiceServer.go
│ └── svc
│ │ └── serviceContext.go
│ ├── model
│ ├── vars.go
│ ├── videoModel.go
│ └── videoModel_gen.go
│ ├── videoService.go
│ ├── videoSvcPb
│ ├── GenPb
│ │ ├── VideoService.proto
│ │ └── build.sh
│ ├── VideoService.pb.go
│ └── VideoService_grpc.pb.go
│ └── videoservice
│ └── videoService.go
└── tpl
└── 1.3.5
├── api
├── config.tpl
├── context.tpl
├── etc.tpl
├── handler.tpl
├── logic.tpl
├── main.tpl
├── middleware.tpl
├── route-addition.tpl
├── routes.tpl
├── template.tpl
└── types.tpl
├── docker
└── docker.tpl
├── kube
├── deployment.tpl
└── job.tpl
├── model
├── delete.tpl
├── err.tpl
├── field.tpl
├── find-one-by-field-extra-method.tpl
├── find-one-by-field.tpl
├── find-one.tpl
├── import-no-cache.tpl
├── import.tpl
├── insert.tpl
├── interface-delete.tpl
├── interface-find-one-by-field.tpl
├── interface-find-one.tpl
├── interface-insert.tpl
├── interface-update.tpl
├── model-gen.tpl
├── model-new.tpl
├── model.tpl
├── table-name.tpl
├── tag.tpl
├── types.tpl
├── update.tpl
└── var.tpl
├── mongo
├── err.tpl
└── model.tpl
├── newapi
└── newtemplate.tpl
└── rpc
├── call-func.tpl
├── call-interface-func.tpl
├── call.tpl
├── config.tpl
├── etc.tpl
├── logic-func.tpl
├── logic.tpl
├── main.tpl
├── server-func.tpl
├── server.tpl
├── svc.tpl
└── template.tpl
/How We Built Whimsical@1.100000023841858x.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/healerwhy/douyin/267d2fba80ddbafb46dedc734c4eb6d3fefd25f7/How We Built Whimsical@1.100000023841858x.webp
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 healerwhy
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 字节跳动青训营 - douyin [项目文档](https://ljxltr3g7w.feishu.cn/docs/doccnLberlBxkQjylBal5I6Tg6g)
2 |
3 | # 业务架构
4 | 
5 | # common
6 | 存放公共代码:主要有对象存储的上传、下载、删除,token的生成与解析,Kafka所需的消息结构体,公共错误代码等。
7 |
8 | # deploy
9 | 通过docker-compose.yml部署Kafka、ZooKeeper,Dockerfile是服务的镜像生成文件,具体使用在deploy下的README文件
10 |
11 | # service
12 | ## api
13 | 暴露RESTful API,主要是对接口的访问控制、接口的参数校验、接口的返回结构体等。
14 | ## asynqJob
15 | 存放异步任务,client-scheduler 调度server执行任务(设置的每10s一次)
16 | ## mq
17 | 存放消息队列的消费者,将api放在redis中的数据取出后写入mysql中
18 | ## rpc-user-info
19 | 存放用户信息的rpc接口,完成用户注册、登陆、查看用户信息,主要是对user表的CURD
20 | ## rpc-user-operate
21 | 存放用户操作的rpc接口,完成点赞、评论、关注操作,主要是对user_favorite_list、user_follow_list、user_comment_list表的CURD
22 | ## rpc-video-service
23 | 存放对视频的操作的rpc接口,完成视频的发布、拉取、视频流操作,主要是对video表进行CURD
24 |
25 | # tpl
26 | 是goctl脚手架工具所需的模板文件,因为对原本的模板文件进行了修改,所以在生成时需要指定为该目录下的tpl文件
27 |
28 | # 写在最后
29 | 感谢以下项目让我学到了很多东西:
30 | [go-zero](https://github.com/zeromicro/go-zero) [万总b站账号](https://space.bilibili.com/434639159?spm_id_from=333.337.search-card.all.click)
31 | go-zero真的是超棒的项目!一定要加万总的社群,疯狂安利!大佬云集!
32 | 向万总和安前松前辈学到了很多东西!谢谢~!每次深夜去找他们请教问题,两位大佬也会很快回复我的消息,真的超感动!
33 |
34 | [go-zero-looklook](https://github.com/Mikaelemmmm/go-zero-looklook) [B站up主~](https://space.bilibili.com/389552232)
35 | Mikaelemmmm大佬人超级棒~! 谢谢~!正如他的签名:“做人但求无愧于心,做事但求无愧于人”,很佩服!
36 |
37 | [cos-go-sdk-v5](https://github.com/tencentyun/cos-go-sdk-v5)
38 | 腾讯云的技术客服超级耐心~!谢谢~!
39 |
40 | ```
41 | 最后的最后:
42 | 感谢字节跳动青训营的所有工作人员无私的辛苦付出,祝青训营越办越好~
43 | 这段时间接触到了很多的大佬!为你们的开源精神致敬!salute~
44 | 我会继续加油,努力追赶你们的脚步
45 | 欢迎一起学习讨论~ 对该项目有任何建议或者意见,请发邮件至:645582687@qq.com,谢谢!
46 |
47 | 2022.7.3
48 | ```
49 |
--------------------------------------------------------------------------------
/common/globalkey/constantKey.go:
--------------------------------------------------------------------------------
1 | package globalkey
2 |
3 | // 软删除只是在数据库表中添加量该字段 但是项目中并未用到
4 |
5 | var DelStateNo int64 = 0 //未删除
6 | var DelStateYes int64 = 1 //已删除
7 |
--------------------------------------------------------------------------------
/common/globalkey/redisCacheKey.go:
--------------------------------------------------------------------------------
1 | package globalkey
2 |
3 | // 存放的是api记录点赞和关注放在redis的key与valTpl
4 |
5 | const FavoriteSetKey = "FavoriteKey"
6 | const FavoriteSetValTpl = "Favorite&Video&Id:%d"
7 |
8 | const FollowSetKey = "FollowKey"
9 | const FollowSetValTpl = "Follow&Follow&Id:%d"
10 |
11 | const ExistDataValTpl = "%d:%d"
12 |
--------------------------------------------------------------------------------
/common/help/cos/download.go:
--------------------------------------------------------------------------------
1 | package cos
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "fmt"
7 | "github.com/tencentyun/cos-go-sdk-v5"
8 | "github.com/zeromicro/go-zero/core/logx"
9 | "io/ioutil"
10 | "net/http"
11 | "net/url"
12 | "sync"
13 | )
14 |
15 | type DownloadComment struct {
16 | Key string
17 | CommentBucket string
18 | SecretID string
19 | SecretKey string
20 | }
21 |
22 | func (l *DownloadComment) DownloadComment(ctx context.Context) ([]*CommentFile, error) {
23 |
24 | u, _ := url.Parse(l.CommentBucket)
25 | b := &cos.BaseURL{BucketURL: u}
26 | c := cos.NewClient(b, &http.Client{
27 | Transport: &cos.AuthorizationTransport{
28 | SecretID: l.SecretID,
29 | SecretKey: l.SecretKey,
30 | },
31 | })
32 |
33 | // 多goroutine同时读取文件
34 | keysCh := make(chan []string, 3)
35 | var wg sync.WaitGroup
36 | threadpool := 3
37 | var allComment []*CommentFile
38 | for i := 0; i < threadpool; i++ {
39 | wg.Add(1)
40 |
41 | go func() {
42 | defer wg.Done()
43 | for keys := range keysCh {
44 | key := keys[0]
45 | resp, err := c.Object.Get(ctx, key, nil)
46 | if err != nil {
47 | panic(err)
48 | }
49 | all, err := ioutil.ReadAll(resp.Body)
50 | _ = resp.Body.Close()
51 |
52 | if err != nil {
53 | return
54 | }
55 | var ret CommentFile
56 | _ = json.Unmarshal(all, &ret)
57 |
58 | allComment = append(allComment, &ret)
59 |
60 | if err != nil {
61 | fmt.Println(err)
62 | }
63 | }
64 | }()
65 | }
66 |
67 | isTruncated := true
68 | prefix := l.Key // 下载 dir 目录下所有文件
69 |
70 | logx.Infof("-------------%+v", prefix)
71 |
72 | for isTruncated {
73 | opt := &cos.BucketGetOptions{
74 | Prefix: prefix,
75 | EncodingType: "url", // url编码
76 | }
77 | // 列出目录
78 | v, _, err := c.Bucket.Get(ctx, opt)
79 | if err != nil {
80 | fmt.Println(err)
81 | break
82 | }
83 | for _, c := range v.Contents {
84 | key, _ := cos.DecodeURIComponent(c.Key) //EncodingType: "url",先对 Key 进行 url decode
85 |
86 | keysCh <- []string{key}
87 | }
88 | isTruncated = v.IsTruncated
89 | }
90 | close(keysCh)
91 | wg.Wait()
92 |
93 | return allComment, nil
94 | }
95 |
--------------------------------------------------------------------------------
/common/help/cos/snowAlg.go:
--------------------------------------------------------------------------------
1 | package cos
2 |
3 | import (
4 | "github.com/pkg/errors"
5 | "github.com/sony/sonyflake"
6 | )
7 |
8 | type GenSnowFlake struct{}
9 |
10 | var sf *sonyflake.Sonyflake
11 |
12 | func (*GenSnowFlake) GenSnowFlake(machineId uint16) (uint64, error) {
13 | var st sonyflake.Settings
14 | st.MachineID = func() (uint16, error) {
15 | return machineId, nil
16 | }
17 | sf = sonyflake.NewSonyflake(st)
18 |
19 | id, err := sf.NextID()
20 | if err != nil {
21 | return 0, errors.Wrap(err, " + sonyflake not created")
22 | }
23 | return id, nil
24 | }
25 |
--------------------------------------------------------------------------------
/common/help/cos/updateComment.go:
--------------------------------------------------------------------------------
1 | package cos
2 |
3 | import (
4 | "bytes"
5 | "context"
6 | "encoding/json"
7 | "fmt"
8 | "github.com/jinzhu/copier"
9 | "github.com/tencentyun/cos-go-sdk-v5"
10 | "github.com/zeromicro/go-zero/core/logx"
11 | "net/http"
12 | "net/url"
13 | )
14 |
15 | type UpdateComment struct {
16 | VideoId int64 `json:"video_id"`
17 | CommentId int64 `json:"id"`
18 | UserId int64 `json:"user_id"`
19 |
20 | Content string `json:"content,omitempty" copier:"CommentText"`
21 | CreateDate string `json:"create_date,omitempty"`
22 | }
23 |
24 | type CommentFile struct {
25 | UserId int64 `json:"user_id"`
26 | CommentId int64 `json:"comment_id"`
27 | CreateDate string `json:"create_date"`
28 | Content string `json:"content" copier:"CommentText"`
29 | }
30 |
31 | func (l *UpdateComment) UploadComment(ctx context.Context, CommentBucket, SecretID, SecretKey string) (string, error) {
32 | u, _ := url.Parse(CommentBucket)
33 | b := &cos.BaseURL{BucketURL: u}
34 | c := cos.NewClient(b, &http.Client{
35 | Transport: &cos.AuthorizationTransport{
36 | SecretID: SecretID,
37 | SecretKey: SecretKey,
38 | },
39 | })
40 |
41 | var commentFile CommentFile
42 | _ = copier.Copy(&commentFile, l)
43 |
44 | ret, _ := json.Marshal(commentFile)
45 | buf := bytes.NewBuffer(ret)
46 | key := "video_id_" + fmt.Sprintf("%d/", l.VideoId) + fmt.Sprintf("user_id_%d_comment_id_%d", l.UserId, l.CommentId) + ".json"
47 |
48 | _, err := c.Object.Put(ctx, key, buf, nil)
49 | if err != nil {
50 | logx.Errorf("UploadComment error: %v", err)
51 | return "", err
52 | }
53 |
54 | return "", nil
55 | }
56 |
57 | func (l *UpdateComment) DeleteComment(ctx context.Context, CommentBucket, SecretID, SecretKey string) (string, error) {
58 | u, _ := url.Parse(CommentBucket)
59 | b := &cos.BaseURL{BucketURL: u}
60 | c := cos.NewClient(b, &http.Client{
61 | Transport: &cos.AuthorizationTransport{
62 | SecretID: SecretID,
63 | SecretKey: SecretKey,
64 | },
65 | })
66 |
67 | key := "video_id_" + fmt.Sprintf("%d/", l.VideoId) + fmt.Sprintf("user_id_%d_comment_id_%d", l.UserId, l.CommentId) + ".json"
68 |
69 | _, err := c.Object.Delete(ctx, key)
70 | if err != nil {
71 | logx.Errorf("UploadComment error: %v", err)
72 | return "", err
73 | }
74 |
75 | return "", nil
76 | }
77 |
--------------------------------------------------------------------------------
/common/help/cos/uploadVideo.go:
--------------------------------------------------------------------------------
1 | package cos
2 |
3 | import (
4 | "context"
5 | "github.com/tencentyun/cos-go-sdk-v5"
6 | "github.com/zeromicro/go-zero/core/logx"
7 | "mime/multipart"
8 | "net/http"
9 | "net/url"
10 | "strconv"
11 | )
12 |
13 | type UploaderVideo struct {
14 | UserId int64
15 | MachineId uint16
16 | VideoBucket string
17 | SecretID string
18 | SecretKey string
19 | }
20 |
21 | func (l *UploaderVideo) UploadVideo(ctx context.Context, file multipart.File) (string, error) {
22 | u, _ := url.Parse(l.VideoBucket)
23 | b := &cos.BaseURL{BucketURL: u}
24 | c := cos.NewClient(b, &http.Client{
25 | Transport: &cos.AuthorizationTransport{
26 | SecretID: l.SecretID,
27 | SecretKey: l.SecretKey,
28 | },
29 | })
30 | genSnowFlake := new(GenSnowFlake)
31 | id, err := genSnowFlake.GenSnowFlake(l.MachineId)
32 | if err != nil {
33 | logx.Errorf("UploadVideo--->GenSnowFlake err : %v", err)
34 | return "", err
35 | }
36 | // 生成useId/id.mp4
37 | key := strconv.FormatInt(l.UserId, 10) + "/" + strconv.FormatInt(int64(id), 10)
38 | // 上传视频文件
39 | _, err = c.Object.Put(ctx, key+".mp4", file, nil)
40 | if err != nil {
41 | logx.Errorf("UploadVideo--->Put err : %v", err)
42 | return "", err
43 | }
44 |
45 | // 上传成功 返回key
46 | return key, nil
47 | }
48 |
--------------------------------------------------------------------------------
/common/help/token/common.go:
--------------------------------------------------------------------------------
1 | package token
2 |
3 | import "github.com/golang-jwt/jwt/v4"
4 |
5 | type Claims struct {
6 | UserId int64 `json:"user_id"`
7 | ExpireAt int64 `json:"expire_at"`
8 | Else jwt.MapClaims
9 | jwt.RegisteredClaims
10 | }
11 |
12 | type CurrentUserId string
13 |
14 | const (
15 | AccessSecret = "healerrrrr"
16 | AccessExpire = 86400
17 | )
18 |
--------------------------------------------------------------------------------
/common/help/token/genToken.go:
--------------------------------------------------------------------------------
1 | package token
2 |
3 | import (
4 | "github.com/golang-jwt/jwt/v4"
5 | "time"
6 | )
7 |
8 | type GenToken struct{}
9 |
10 | func (g *GenToken) GenToken(iat time.Time, userId int64, payloads map[string]interface{}) (string, error) {
11 | claims := Claims{
12 | UserId: userId,
13 | ExpireAt: iat.Add(time.Second * AccessExpire).Unix(),
14 | RegisteredClaims: jwt.RegisteredClaims{
15 | Issuer: "healer",
16 | },
17 | }
18 | for k, v := range payloads {
19 | claims.Else[k] = v
20 | }
21 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
22 | return token.SignedString([]byte(AccessSecret))
23 | }
24 |
--------------------------------------------------------------------------------
/common/help/token/parseToken.go:
--------------------------------------------------------------------------------
1 | package token
2 |
3 | import "github.com/golang-jwt/jwt/v4"
4 |
5 | type ParseToken struct{}
6 |
7 | func (*ParseToken) ParseToken(tokenString string) (*Claims, error) {
8 | // 解码token
9 | tokenClaims, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
10 | return []byte(AccessSecret), nil
11 | })
12 | // 如果不为空,解码成功
13 | if tokenClaims != nil {
14 | // 断言转换
15 | if claims, ok := tokenClaims.Claims.(*Claims); ok && tokenClaims.Valid {
16 | return claims, nil
17 | }
18 | }
19 | return nil, err
20 | }
21 |
--------------------------------------------------------------------------------
/common/messageTypes/message.go:
--------------------------------------------------------------------------------
1 | package messageTypes
2 |
3 | const (
4 | ActionADD int64 = 1
5 | ActionCancel int64 = 2
6 | ActionErr int64 = -99
7 | )
8 |
9 | // UserFavoriteOptMessage 点赞 / 取消点赞
10 | type UserFavoriteOptMessage struct {
11 | ActionType int64 `json:"action_type"`
12 | VideoId int64 `json:"video_id"`
13 | UserId int64 `json:"user_id"`
14 | }
15 |
16 | // UserFollowOptMessage 关注 / 取消关注
17 | type UserFollowOptMessage struct {
18 | ActionType int64 `json:"action_type"`
19 | FollowId int64 `json:"follow_id"`
20 | UserId int64 `json:"user_id"`
21 | }
22 |
23 | // UserCommentOptMessage 评论 / 删除评论
24 | type UserCommentOptMessage struct {
25 | VideoId int64 `json:"video_id"`
26 | CommentId int64 `json:"comment_id"`
27 | UserId int64 `json:"user_id"`
28 | ActionType int64 `json:"action_type"`
29 | CommentText string `json:"comment_text,omitempty"`
30 | CreateDate string `json:"create_date,omitempty"`
31 | }
32 |
--------------------------------------------------------------------------------
/common/xerr/errCode.go:
--------------------------------------------------------------------------------
1 | package xerr
2 |
3 | // 通用错误码成功返回
4 | const (
5 | OK int64 = 0
6 | ERR int64 = 1
7 | )
8 |
9 | // 全局错误码
10 | const (
11 | SERVER_COMMON_ERROR int64 = 100001
12 | REUQEST_PARAM_ERROR int64 = 100002
13 | TOKEN_EXPIRE_ERROR int64 = 100003
14 | TOKEN_GENERATE_ERROR int64 = 100004
15 | DB_ERROR int64 = 100005
16 | DB_UPDATE_AFFECTED_ZERO_ERROR int64 = 100006
17 | SECRET_ERROR int64 = 100007
18 | )
19 |
--------------------------------------------------------------------------------
/common/xerr/errMsg.go:
--------------------------------------------------------------------------------
1 | package xerr
2 |
3 | var message map[int64]string
4 |
5 | func init() {
6 | message = make(map[int64]string)
7 | message[OK] = "SUCCESS"
8 | message[REUQEST_PARAM_ERROR] = "参数错误"
9 | message[TOKEN_EXPIRE_ERROR] = "token失效,请重新登陆"
10 | message[TOKEN_GENERATE_ERROR] = "生成token失败"
11 | message[DB_ERROR] = "数据库繁忙,请稍后再试"
12 | message[SECRET_ERROR] = "密码错误"
13 | }
14 |
15 | func MapErrMsg(errcode int64) string {
16 | if msg, ok := message[errcode]; ok {
17 | return msg
18 | } else {
19 | return "服务器开小差啦,稍后再来试一试"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/common/xerr/errors.go:
--------------------------------------------------------------------------------
1 | package xerr
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | /**
8 | 常用通用固定错误
9 | */
10 |
11 | type CodeError struct {
12 | errCode int64
13 | errMsg string
14 | }
15 |
16 | // GetErrCode 返回给前端的错误码
17 | func (e *CodeError) GetErrCode() int64 {
18 | return e.errCode
19 | }
20 |
21 | // GetErrMsg 返回给前端显示端错误信息
22 | func (e *CodeError) GetErrMsg() string {
23 | return e.errMsg
24 | }
25 |
26 | func (e *CodeError) Error() string {
27 | return fmt.Sprintf("ErrCode:%d,ErrMsg:%s", e.errCode, e.errMsg)
28 | }
29 |
30 | func NewErrCode(errCode int64) *CodeError {
31 | return &CodeError{errCode: errCode, errMsg: MapErrMsg(errCode)}
32 | }
33 |
34 | func NewErrMsg(errMsg string) *CodeError {
35 | return &CodeError{errCode: SERVER_COMMON_ERROR, errMsg: errMsg}
36 | }
37 |
--------------------------------------------------------------------------------
/deploy/README.md:
--------------------------------------------------------------------------------
1 | 1. docker-compose.yml 部署的是Zookeeper和Kafka ```docker-compose up -d```启动
2 |
3 | 2. asynqUI 是Asynq配套的UI,可以在浏览器中查看任务的执行状态,他的镜像通过```docker pull hibiken/asynqmon```下载
4 |
5 | 3. 其他的Dockerfile都是service下的各个文件的镜像生成文件,使用时需要将对应的Dockerfile在项目根目录下进行```docker build .```构建镜像
--------------------------------------------------------------------------------
/deploy/api/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:alpine AS builder
2 |
3 | LABEL stage=gobuilder
4 |
5 | ENV CGO_ENABLED 0
6 | ENV GOPROXY https://goproxy.cn,direct
7 |
8 | RUN apk update --no-cache && apk add --no-cache tzdata
9 |
10 | WORKDIR /build
11 |
12 | ADD go.mod .
13 | ADD go.sum .
14 | RUN go mod download
15 | COPY ../../service/api .
16 | COPY service/api/etc /app/etc
17 | RUN go build -ldflags="-s -w" -o /app/user service/api/user.go
18 |
19 |
20 | FROM scratch
21 |
22 | COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
23 | COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /usr/share/zoneinfo/Asia/Shanghai
24 | ENV TZ Asia/Shanghai
25 |
26 | WORKDIR /app
27 | COPY --from=builder /app/user /app/user
28 | COPY --from=builder /app/etc /app/etc
29 |
30 | CMD ["./user", "-f", "etc/user-api.yaml"]
31 |
--------------------------------------------------------------------------------
/deploy/asynqJob/client-scheduler/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:alpine AS builder
2 |
3 | LABEL stage=gobuilder
4 |
5 | ENV CGO_ENABLED 0
6 | ENV GOPROXY https://goproxy.cn,direct
7 |
8 | RUN apk update --no-cache && apk add --no-cache tzdata
9 |
10 | WORKDIR /build
11 |
12 | ADD go.mod .
13 | ADD go.sum .
14 | RUN go mod download
15 | COPY ../../../service/asynqJob/client-scheduler .
16 | COPY service/asynqJob/client-scheduler/etc /app/etc
17 | RUN go build -ldflags="-s -w" -o /app/scheduler service/asynqJob/client-scheduler/scheduler.go
18 |
19 |
20 | FROM scratch
21 |
22 | COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
23 | COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /usr/share/zoneinfo/Asia/Shanghai
24 | ENV TZ Asia/Shanghai
25 |
26 | WORKDIR /app
27 | COPY --from=builder /app/scheduler /app/scheduler
28 | COPY --from=builder /app/etc /app/etc
29 |
30 | CMD ["./scheduler", "-f", "etc/scheduler.yaml"]
31 |
--------------------------------------------------------------------------------
/deploy/asynqJob/server/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:alpine AS builder
2 |
3 | LABEL stage=gobuilder
4 |
5 | ENV CGO_ENABLED 0
6 | ENV GOPROXY https://goproxy.cn,direct
7 |
8 | RUN apk update --no-cache && apk add --no-cache tzdata
9 |
10 | WORKDIR /build
11 |
12 | ADD go.mod .
13 | ADD go.sum .
14 | RUN go mod download
15 | COPY ../../../service/asynqJob/server .
16 | COPY service/asynqJob/server/etc /app/etc
17 | RUN go build -ldflags="-s -w" -o /app/server service/asynqJob/server/server.go
18 |
19 |
20 | FROM scratch
21 |
22 | COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
23 | COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /usr/share/zoneinfo/Asia/Shanghai
24 | ENV TZ Asia/Shanghai
25 |
26 | WORKDIR /app
27 | COPY --from=builder /app/server /app/server
28 | COPY --from=builder /app/etc /app/etc
29 |
30 | CMD ["./server", "-f", "etc/server.yaml"]
31 |
--------------------------------------------------------------------------------
/deploy/asynqUI.sh:
--------------------------------------------------------------------------------
1 | docker run --rm --name asynqmon \
2 | --network test-net \
3 | -p 8080:8080 \
4 | hibiken/asynqmon --redis-addr=localhost:6379 --redis-password=password
5 |
--------------------------------------------------------------------------------
/deploy/docker-compose.yml:
--------------------------------------------------------------------------------
1 | ---
2 | version: '3'
3 | services:
4 | zookeeper:
5 | image: 3f43f72cb283
6 | container_name: zookeeper
7 | environment:
8 | ZOOKEEPER_CLIENT_PORT: 2181
9 | ZOOKEEPER_TICK_TIME: 2000
10 | networks:
11 | - test-net
12 |
13 | broker:
14 | image: 2dd91ce2efe1
15 | container_name: kafka
16 | ports:
17 | - "29092:9092"
18 | depends_on:
19 | - zookeeper
20 | environment:
21 | KAFKA_BROKER_ID: 1
22 | KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181'
23 | KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092
24 | KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://172.18.85.218:29092
25 | KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
26 | KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
27 | KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
28 | networks:
29 | - test-net
30 | networks:
31 | test-net:
32 | external:
33 | name: test-net
34 |
--------------------------------------------------------------------------------
/deploy/mq/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:alpine AS builder
2 |
3 | LABEL stage=gobuilder
4 |
5 | ENV CGO_ENABLED 0
6 | ENV GOPROXY https://goproxy.cn,direct
7 |
8 | RUN apk update --no-cache && apk add --no-cache tzdata
9 |
10 | WORKDIR /build
11 |
12 | ADD go.mod .
13 | ADD go.sum .
14 | RUN go mod download
15 | COPY ../../service/mq .
16 | COPY service/mq/etc /app/etc
17 | RUN go build -ldflags="-s -w" -o /app/mq service/mq/mq.go
18 |
19 |
20 | FROM scratch
21 |
22 | COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
23 | COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /usr/share/zoneinfo/Asia/Shanghai
24 | ENV TZ Asia/Shanghai
25 |
26 | WORKDIR /app
27 | COPY --from=builder /app/mq /app/mq
28 | COPY --from=builder /app/etc /app/etc
29 |
30 | CMD ["./mq", "-f", "etc/mq.yaml"]
31 |
--------------------------------------------------------------------------------
/deploy/rpc-user-info/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:alpine AS builder
2 |
3 | LABEL stage=gobuilder
4 |
5 | ENV CGO_ENABLED 0
6 | ENV GOPROXY https://goproxy.cn,direct
7 |
8 | RUN apk update --no-cache && apk add --no-cache tzdata
9 |
10 | WORKDIR /build
11 |
12 | ADD go.mod .
13 | ADD go.sum .
14 | RUN go mod download
15 | COPY ../../service/rpc-user-info .
16 | COPY service/rpc-user-info/etc /app/etc
17 | RUN go build -ldflags="-s -w" -o /app/userInfoService service/rpc-user-info/userInfoService.go
18 |
19 |
20 | FROM scratch
21 |
22 | COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
23 | COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /usr/share/zoneinfo/Asia/Shanghai
24 | ENV TZ Asia/Shanghai
25 |
26 | WORKDIR /app
27 | COPY --from=builder /app/userInfoService /app/userInfoService
28 | COPY --from=builder /app/etc /app/etc
29 |
30 | CMD ["./userInfoService", "-f", "etc/userInfoService.yaml"]
31 |
--------------------------------------------------------------------------------
/deploy/rpc-user-operate/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:alpine AS builder
2 |
3 | LABEL stage=gobuilder
4 |
5 | ENV CGO_ENABLED 0
6 | ENV GOPROXY https://goproxy.cn,direct
7 |
8 | RUN apk update --no-cache && apk add --no-cache tzdata
9 |
10 | WORKDIR /build
11 |
12 | ADD go.mod .
13 | ADD go.sum .
14 | RUN go mod download
15 | COPY ../../service/rpc-user-operate .
16 | COPY service/rpc-user-operate/etc /app/etc
17 | RUN go build -ldflags="-s -w" -o /app/userOptService service/rpc-user-operate/userOptService.go
18 |
19 |
20 | FROM scratch
21 |
22 | COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
23 | COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /usr/share/zoneinfo/Asia/Shanghai
24 | ENV TZ Asia/Shanghai
25 |
26 | WORKDIR /app
27 | COPY --from=builder /app/userOptService /app/userOptService
28 | COPY --from=builder /app/etc /app/etc
29 |
30 | CMD ["./userOptService", "-f", "etc/userOptService.yaml"]
31 |
--------------------------------------------------------------------------------
/deploy/rpc-video-service/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:alpine AS builder
2 |
3 | LABEL stage=gobuilder
4 |
5 | ENV CGO_ENABLED 0
6 | ENV GOPROXY https://goproxy.cn,direct
7 |
8 | RUN apk update --no-cache && apk add --no-cache tzdata
9 |
10 | WORKDIR /build
11 |
12 | ADD go.mod .
13 | ADD go.sum .
14 | RUN go mod download
15 | COPY ../../service/rpc-video-service .
16 | COPY service/rpc-video-service/etc /app/etc
17 | RUN go build -ldflags="-s -w" -o /app/videoService service/rpc-video-service/videoService.go
18 |
19 |
20 | FROM scratch
21 |
22 | COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
23 | COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /usr/share/zoneinfo/Asia/Shanghai
24 | ENV TZ Asia/Shanghai
25 |
26 | WORKDIR /app
27 | COPY --from=builder /app/videoService /app/videoService
28 | COPY --from=builder /app/etc /app/etc
29 |
30 | CMD ["./videoService", "-f", "etc/videoService.yaml"]
31 |
--------------------------------------------------------------------------------
/service/api/api/build.sh:
--------------------------------------------------------------------------------
1 | # 通过goctl生成脚手架 --home 是你的模板(如果未对goctl的模板进行修改就不写,我这里修改了一些模板)
2 | # userOpt.api中 import了多个api文件 所以最后只需要这一个文件
3 | goctl api go -api userOpt.api -dir ../ --style=goZero --home=../../../tpl
--------------------------------------------------------------------------------
/service/api/api/user.api:
--------------------------------------------------------------------------------
1 | // api语法版本
2 | syntax = "v1"
3 |
4 | info(
5 | author: "healer"
6 | date: "2022-6-7"
7 | )
8 |
9 | // common 请求
10 | type UserReq {
11 | UserName string `form:"username"`
12 | Password string `form:"password"`
13 | }
14 |
15 | type IdWithTokenReq {
16 | UserId int64 `form:"user_id"`
17 | Token string `form:"token"`
18 | }
19 |
20 | // common 返回
21 | type Status {
22 | Code int64 `json:"status_code"`
23 | Msg string `json:"status_msg,omitempty"`
24 | }
25 |
26 | type IdWithTokenRes {
27 | UserId int64 `json:"user_id,omitempty"`
28 | Token string `json:"token,omitempty"`
29 | }
30 |
31 | // 每个成员的tag只能绑定一种 json,form,path header
32 | // 注册
33 | type (
34 | UserRegisterReq {
35 | UserReq
36 | }
37 | UserRegisterRes {
38 | Status
39 | IdWithTokenRes
40 | }
41 | )
42 |
43 | // 登录
44 | type (
45 | UserLoginReq {
46 | UserReq
47 | }
48 | UserLoginRes {
49 | Status
50 | IdWithTokenRes
51 | }
52 | )
53 |
54 | // 用户信息
55 | type User {
56 | UserId int64 `json:"id"`
57 | UserName string `json:"name"`
58 | FollowCount int64 `json:"follow_count"`
59 | FollowerCount int64 `json:"follower_count"`
60 | IsFollow bool `json:"is_follow"`
61 | }
62 |
63 | type (
64 | UserInfoReq {
65 | IdWithTokenReq
66 | }
67 | UserInfoRes {
68 | Status
69 | User *User `json:"user,omitempty"`
70 | }
71 | )
72 | @server (
73 | group : user
74 | prefix : /douyin/user
75 | )
76 | service user-api {
77 | @doc "用户注册"
78 | @handler UserRegister
79 | post /register (UserRegisterReq) returns (UserRegisterRes)
80 | @doc "用户登录"
81 | @handler UserLogin
82 | post /login (UserLoginReq) returns (UserLoginRes)
83 | }
84 |
85 | @server (
86 | group : user
87 | prefix : /douyin/user
88 | middleware: AuthJWT
89 | )
90 | service user-api {
91 | @doc "用户信息"
92 | @handler UserInfo
93 | get / (UserInfoReq) returns (UserInfoRes)
94 | }
--------------------------------------------------------------------------------
/service/api/api/userOpt.api:
--------------------------------------------------------------------------------
1 | syntax = "v1"
2 |
3 | import "user.api"
4 | import "video.api"
5 |
6 | type FavoriteOptReq {
7 | Token string `form:"token"`
8 | VideoId int64 `form:"video_id"`
9 | ActionType int64 `form:"action_type"`
10 | }
11 |
12 | type FavoriteOptRes {
13 | Status
14 | }
15 |
16 | type FollowOptReq {
17 | Token string `form:"token"`
18 | FollowId int64 `form:"to_user_id"`
19 | ActionType int64 `form:"action_type"`
20 | }
21 |
22 | type FollowOptRes {
23 | Status
24 | }
25 |
26 | type CommentOptReq {
27 | Token string `form:"token"`
28 | VideoId int64 `form:"video_id"`
29 | ActionType int64 `form:"action_type"`
30 | CommentText string `form:"comment_text,omitempty, optional"`
31 | CommentId int64 `form:"comment_id,omitempty, optional"`
32 | }
33 |
34 | type Comment {
35 | CommentId int64 `json:"id" copyier:"id"`
36 | User User `json:"user"`
37 | Content string `json:"content"`
38 | CreateTime string `json:"create_date"`
39 | }
40 |
41 | type CommentOptRes {
42 | Status
43 | Comment *Comment `json:"comment,omitempty"`
44 | }
45 |
46 | @server(
47 | group : userOpt
48 | prefix : /douyin
49 | middleware: AuthJWT
50 | )
51 |
52 | service user-api {
53 | @doc "用户点赞"
54 | @handler FavoriteOpt
55 | post /favorite/action (FavoriteOptReq) returns (FavoriteOptRes)
56 |
57 | @doc "用户评论"
58 | @handler CommentOpt
59 | post /comment/action (CommentOptReq) returns (CommentOptRes)
60 |
61 | @doc "用户关注"
62 | @handler FollowOpt
63 | post /relation/action (FollowOptReq) returns (FollowOptRes)
64 | }
65 |
66 | type FavoriteListReq {
67 | IdWithTokenReq
68 | }
69 |
70 | type FavoriteListRes {
71 | Status
72 | FavoriteList []*PubVideo `json:"video_list,omitempty"`
73 | }
74 |
75 | type FollowListReq {
76 | IdWithTokenReq
77 | }
78 |
79 | type FollowListRes {
80 | Status
81 | UserFollowlist []*User `json:"user_list,omitempty"`
82 | }
83 |
84 | type FollowerListReq {
85 | IdWithTokenReq
86 | }
87 |
88 | type FollowerListRes {
89 | Status
90 | UserFollowerlist []*User `json:"user_list,omitempty"`
91 | }
92 |
93 | type CommentListReq {
94 | Token string `form:"token"`
95 | VideoId int64 `form:"video_id"`
96 | }
97 |
98 | type CommentListRes {
99 | Status
100 | CommentList []*Comment `json:"comment_list,omitempty"`
101 | }
102 |
103 | @server(
104 | group : userOpt
105 | prefix : /douyin
106 | middleware: AuthJWT
107 | )
108 | service user-api {
109 | @doc "用户点赞列表"
110 | @handler GetFavoriteList
111 | get /favorite/list (FavoriteListReq) returns (FavoriteListRes)
112 |
113 | @doc "视频评论列表"
114 | @handler GetCommentList
115 | get /comment/list (CommentListReq) returns (CommentListRes)
116 |
117 | @doc "用户关注列表"
118 | @handler GetFollowList
119 | get /relation/follow/list (FollowListReq) returns (FollowListRes)
120 |
121 | @doc "用户粉丝列表"
122 | @handler GetFollowerList
123 | get /relation/follower/list (FollowerListReq) returns (FollowerListRes)
124 | }
--------------------------------------------------------------------------------
/service/api/api/video.api:
--------------------------------------------------------------------------------
1 | syntax = "v1"
2 |
3 | info(
4 | title: "video"
5 | author: "healer"
6 | )
7 |
8 | import "user.api"
9 |
10 | type PubVideoReq {
11 | Token string `form:"token"`
12 | Title string `form:"title"`
13 | }
14 |
15 | type PubVideoRes {
16 | Status
17 | }
18 |
19 | type GetPubVideoListReq {
20 | Token string `form:"token"`
21 | UserId int64 `form:"user_id"`
22 | }
23 |
24 | type PubVideo {
25 | Id int64 `json:"id"`
26 | User User `json:"author"`
27 | PlayURL string `json:"play_url"`
28 | CoverURL string `json:"cover_url"`
29 | FavoriteCount int `json:"favorite_count"`
30 | CommentCount int `json:"comment_count"`
31 | IsFavorite bool `json:"is_favorite"`
32 | Title string `json:"title"`
33 | }
34 |
35 | type GetPubVideoListRes {
36 | Status
37 | VideoPubList []*PubVideo `json:"video_list,omitempty"`
38 | }
39 |
40 | @server(
41 | group: publish
42 | prefix: /douyin/publish
43 | middleware: AuthJWT
44 | )
45 | service user-api {
46 | @doc "发布视频"
47 | @handler PublishVideo
48 | post /action (PubVideoReq) returns (PubVideoRes)
49 |
50 | @doc "获取发布视频列表"
51 | @handler GetPublishVideoList
52 | get /list (GetPubVideoListReq) returns (GetPubVideoListRes)
53 | }
54 |
55 | type FeedVideoListReq {
56 | Token string `form:"token,optional"`
57 | LastTime int64 `form:"last_time,optional"`
58 | }
59 |
60 | type FeedVideoListRes {
61 | Status
62 | NextTime int64 `json:"next_time,omitempty"`
63 | VideoList []*PubVideo `json:"video_list,omitempty"`
64 | }
65 |
66 | @server(
67 | group: feed
68 | prefix: /douyin/feed
69 | middleware: IsLogin
70 | )
71 | service user-api {
72 | @doc "获取视频流"
73 | @handler FeedVideoList
74 | get / (FeedVideoListReq) returns (FeedVideoListRes)
75 | }
--------------------------------------------------------------------------------
/service/api/etc/user-api.yaml:
--------------------------------------------------------------------------------
1 | Name: user-api
2 | Host: 0.0.0.0
3 | Port: 22222
4 |
5 | Mode: test
6 |
7 | Log:
8 | Encoding: plain
9 |
10 | # 腾讯云
11 | COSConf:
12 | SecretId: SecretId
13 | SecretKey: SecretKey
14 | MachineId: 1
15 | VideoBucket: https://xxxx.cos.ap-guangzhou.myqcloud.com
16 | CoverBucket: https://xxxx.cos.ap-guangzhou.myqcloud.com
17 | CommentBucket: https://xxxx.cos.ap-guangzhou.myqcloud.com
18 |
19 | # rpc
20 | UserInfoService:
21 | Etcd:
22 | Hosts:
23 | - 127.0.0.1:33333
24 | Key: userinfoservice.rpc
25 | NonBlock: true
26 |
27 | VideoService:
28 | Etcd:
29 | Hosts:
30 | - 127.0.0.1:33333
31 | Key: videoservice.rpc
32 | NonBlock: true
33 |
34 | UserOptService:
35 | Etcd:
36 | Hosts:
37 | - 127.0.0.1:33333
38 | Key: useroptservice.rpc
39 | NonBlock: true
40 |
41 | Cache:
42 | - Host: localhost:6379
43 | Pass: password
44 |
45 | # kafka
46 | UserFavoriteOptServiceConf:
47 | Brokers:
48 | - 127.0.0.1:9092
49 | Topic: UserFavoriteOptService-topic
50 |
51 | UserCommentOptServiceConf:
52 | Brokers:
53 | - 127.0.0.1:9092
54 | Topic: UserCommentOptService-topic
55 |
56 | UserFollowOptServiceConf:
57 | Brokers:
58 | - 127.0.0.1:9092
59 | Topic: UserFollowOptService-topic
--------------------------------------------------------------------------------
/service/api/internal/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "github.com/zeromicro/go-zero/core/stores/cache"
5 | "github.com/zeromicro/go-zero/rest"
6 | "github.com/zeromicro/go-zero/zrpc"
7 | )
8 |
9 | // KqConfig 用于发现kafka的Topic
10 | type KqConfig struct {
11 | Brokers []string
12 | Topic string
13 | }
14 |
15 | type Config struct {
16 | rest.RestConf
17 |
18 | Cache cache.CacheConf
19 | // rpc
20 | UserInfoService zrpc.RpcClientConf
21 | VideoService zrpc.RpcClientConf
22 | UserOptService zrpc.RpcClientConf
23 | // 腾讯云
24 | COSConf struct {
25 | SecretId string
26 | SecretKey string
27 | MachineId uint16
28 | VideoBucket string
29 | CoverBucket string
30 | }
31 | // kafka
32 | UserFavoriteOptServiceConf KqConfig
33 | UserCommentOptServiceConf KqConfig
34 | UserFollowOptServiceConf KqConfig
35 | }
36 |
--------------------------------------------------------------------------------
/service/api/internal/handler/feed/feedVideoListHandler.go:
--------------------------------------------------------------------------------
1 | package feed
2 |
3 | import (
4 | "net/http"
5 |
6 | "douyin/service/api/internal/logic/feed"
7 | "douyin/service/api/internal/svc"
8 | "douyin/service/api/internal/types"
9 | "github.com/zeromicro/go-zero/rest/httpx"
10 | )
11 |
12 | func FeedVideoListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
13 | return func(w http.ResponseWriter, r *http.Request) {
14 | var req types.FeedVideoListReq
15 | if err := httpx.Parse(r, &req); err != nil {
16 | httpx.Error(w, err)
17 | return
18 | }
19 |
20 | l := feed.NewFeedVideoListLogic(r.Context(), svcCtx)
21 |
22 | resp, err := l.FeedVideoList(&req)
23 |
24 | if err != nil {
25 | httpx.Error(w, err)
26 | } else {
27 | httpx.OkJson(w, resp)
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/service/api/internal/handler/publish/getPublishVideoListHandler.go:
--------------------------------------------------------------------------------
1 | package publish
2 |
3 | import (
4 | "net/http"
5 |
6 | "douyin/service/api/internal/logic/publish"
7 | "douyin/service/api/internal/svc"
8 | "douyin/service/api/internal/types"
9 | "github.com/zeromicro/go-zero/rest/httpx"
10 | )
11 |
12 | func GetPublishVideoListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
13 | return func(w http.ResponseWriter, r *http.Request) {
14 | var req types.GetPubVideoListReq
15 | if err := httpx.Parse(r, &req); err != nil {
16 | httpx.Error(w, err)
17 | return
18 | }
19 |
20 | l := publish.NewGetPublishVideoListLogic(r.Context(), svcCtx)
21 | resp, err := l.GetPublishVideoList(&req)
22 | if err != nil {
23 | httpx.Error(w, err)
24 | } else {
25 | httpx.OkJson(w, resp)
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/service/api/internal/handler/publish/publishVideoHandler.go:
--------------------------------------------------------------------------------
1 | package publish
2 |
3 | import (
4 | "douyin/service/api/internal/logic/publish"
5 | "douyin/service/api/internal/svc"
6 | "douyin/service/api/internal/types"
7 | "github.com/zeromicro/go-zero/rest/httpx"
8 | "net/http"
9 | )
10 |
11 | func PublishVideoHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
12 | return func(w http.ResponseWriter, r *http.Request) {
13 | var req types.PubVideoReq
14 | if err := httpx.Parse(r, &req); err != nil {
15 | httpx.Error(w, err)
16 | return
17 | }
18 |
19 | l := publish.NewPublishVideoLogic(r.Context(), svcCtx, r)
20 | resp, err := l.PublishVideo(&req)
21 | if err != nil {
22 | httpx.Error(w, err)
23 | } else {
24 | httpx.OkJson(w, resp)
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/service/api/internal/handler/routes.go:
--------------------------------------------------------------------------------
1 | // Code generated by goctl. DO NOT EDIT.
2 | package handler
3 |
4 | import (
5 | "net/http"
6 |
7 | feed "douyin/service/api/internal/handler/feed"
8 | publish "douyin/service/api/internal/handler/publish"
9 | user "douyin/service/api/internal/handler/user"
10 | userOpt "douyin/service/api/internal/handler/userOpt"
11 | "douyin/service/api/internal/svc"
12 |
13 | "github.com/zeromicro/go-zero/rest"
14 | )
15 |
16 | func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
17 | server.AddRoutes(
18 | rest.WithMiddlewares(
19 | []rest.Middleware{serverCtx.AuthJWT},
20 | []rest.Route{
21 | {
22 | Method: http.MethodPost,
23 | Path: "/favorite/action",
24 | Handler: userOpt.FavoriteOptHandler(serverCtx),
25 | },
26 | {
27 | Method: http.MethodPost,
28 | Path: "/comment/action",
29 | Handler: userOpt.CommentOptHandler(serverCtx),
30 | },
31 | {
32 | Method: http.MethodPost,
33 | Path: "/relation/action",
34 | Handler: userOpt.FollowOptHandler(serverCtx),
35 | },
36 | }...,
37 | ),
38 | rest.WithPrefix("/douyin"),
39 | )
40 |
41 | server.AddRoutes(
42 | rest.WithMiddlewares(
43 | []rest.Middleware{serverCtx.AuthJWT},
44 | []rest.Route{
45 | {
46 | Method: http.MethodGet,
47 | Path: "/favorite/list",
48 | Handler: userOpt.GetFavoriteListHandler(serverCtx),
49 | },
50 | {
51 | Method: http.MethodGet,
52 | Path: "/comment/list",
53 | Handler: userOpt.GetCommentListHandler(serverCtx),
54 | },
55 | {
56 | Method: http.MethodGet,
57 | Path: "/relation/follow/list",
58 | Handler: userOpt.GetFollowListHandler(serverCtx),
59 | },
60 | {
61 | Method: http.MethodGet,
62 | Path: "/relation/follower/list",
63 | Handler: userOpt.GetFollowerListHandler(serverCtx),
64 | },
65 | }...,
66 | ),
67 | rest.WithPrefix("/douyin"),
68 | )
69 |
70 | server.AddRoutes(
71 | []rest.Route{
72 | {
73 | Method: http.MethodPost,
74 | Path: "/register",
75 | Handler: user.UserRegisterHandler(serverCtx),
76 | },
77 | {
78 | Method: http.MethodPost,
79 | Path: "/login",
80 | Handler: user.UserLoginHandler(serverCtx),
81 | },
82 | },
83 | rest.WithPrefix("/douyin/user"),
84 | )
85 |
86 | server.AddRoutes(
87 | rest.WithMiddlewares(
88 | []rest.Middleware{serverCtx.AuthJWT},
89 | []rest.Route{
90 | {
91 | Method: http.MethodGet,
92 | Path: "/",
93 | Handler: user.UserInfoHandler(serverCtx),
94 | },
95 | }...,
96 | ),
97 | rest.WithPrefix("/douyin/user"),
98 | )
99 |
100 | server.AddRoutes(
101 | rest.WithMiddlewares(
102 | []rest.Middleware{serverCtx.AuthJWT},
103 | []rest.Route{
104 | {
105 | Method: http.MethodPost,
106 | Path: "/action",
107 | Handler: publish.PublishVideoHandler(serverCtx),
108 | },
109 | {
110 | Method: http.MethodGet,
111 | Path: "/list",
112 | Handler: publish.GetPublishVideoListHandler(serverCtx),
113 | },
114 | }...,
115 | ),
116 | rest.WithPrefix("/douyin/publish"),
117 | )
118 |
119 | server.AddRoutes(
120 | rest.WithMiddlewares(
121 | []rest.Middleware{serverCtx.IsLogin},
122 | []rest.Route{
123 | {
124 | Method: http.MethodGet,
125 | Path: "/",
126 | Handler: feed.FeedVideoListHandler(serverCtx),
127 | },
128 | }...,
129 | ),
130 | rest.WithPrefix("/douyin/feed"),
131 | )
132 | }
133 |
--------------------------------------------------------------------------------
/service/api/internal/handler/user/userInfoHandler.go:
--------------------------------------------------------------------------------
1 | package user
2 |
3 | import (
4 | "net/http"
5 |
6 | "douyin/service/api/internal/logic/user"
7 | "douyin/service/api/internal/svc"
8 | "douyin/service/api/internal/types"
9 | "github.com/zeromicro/go-zero/rest/httpx"
10 | )
11 |
12 | func UserInfoHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
13 | return func(w http.ResponseWriter, r *http.Request) {
14 | var req types.UserInfoReq
15 | if err := httpx.Parse(r, &req); err != nil {
16 | httpx.Error(w, err)
17 | return
18 | }
19 |
20 | l := user.NewUserInfoLogic(r.Context(), svcCtx)
21 | resp, err := l.UserInfo(&req)
22 | if err != nil {
23 | httpx.Error(w, err)
24 | } else {
25 | httpx.OkJson(w, resp)
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/service/api/internal/handler/user/userLoginHandler.go:
--------------------------------------------------------------------------------
1 | package user
2 |
3 | import (
4 | "net/http"
5 |
6 | "douyin/service/api/internal/logic/user"
7 | "douyin/service/api/internal/svc"
8 | "douyin/service/api/internal/types"
9 | "github.com/zeromicro/go-zero/rest/httpx"
10 | )
11 |
12 | func UserLoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
13 | return func(w http.ResponseWriter, r *http.Request) {
14 | var req types.UserLoginReq
15 | if err := httpx.Parse(r, &req); err != nil {
16 | httpx.Error(w, err)
17 | return
18 | }
19 |
20 | l := user.NewUserLoginLogic(r.Context(), svcCtx)
21 | resp, err := l.UserLogin(&req)
22 | if err != nil {
23 | httpx.Error(w, err)
24 | } else {
25 | httpx.OkJson(w, resp)
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/service/api/internal/handler/user/userRegisterHandler.go:
--------------------------------------------------------------------------------
1 | package user
2 |
3 | import (
4 | "net/http"
5 |
6 | "douyin/service/api/internal/logic/user"
7 | "douyin/service/api/internal/svc"
8 | "douyin/service/api/internal/types"
9 | "github.com/zeromicro/go-zero/rest/httpx"
10 | )
11 |
12 | func UserRegisterHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
13 | return func(w http.ResponseWriter, r *http.Request) {
14 | var req types.UserRegisterReq
15 | if err := httpx.Parse(r, &req); err != nil {
16 | httpx.Error(w, err)
17 | return
18 | }
19 |
20 | l := user.NewUserRegisterLogic(r.Context(), svcCtx)
21 | resp, err := l.UserRegister(&req)
22 | if err != nil {
23 | httpx.Error(w, err)
24 | } else {
25 | httpx.OkJson(w, resp)
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/service/api/internal/handler/userOpt/commentOptHandler.go:
--------------------------------------------------------------------------------
1 | package userOpt
2 |
3 | import (
4 | "net/http"
5 |
6 | "douyin/service/api/internal/logic/userOpt"
7 | "douyin/service/api/internal/svc"
8 | "douyin/service/api/internal/types"
9 | "github.com/zeromicro/go-zero/rest/httpx"
10 | )
11 |
12 | func CommentOptHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
13 | return func(w http.ResponseWriter, r *http.Request) {
14 | var req types.CommentOptReq
15 | if err := httpx.Parse(r, &req); err != nil {
16 | httpx.Error(w, err)
17 | return
18 | }
19 |
20 | l := userOpt.NewCommentOptLogic(r.Context(), svcCtx)
21 | resp, err := l.CommentOpt(&req)
22 | if err != nil {
23 | httpx.Error(w, err)
24 | } else {
25 | httpx.OkJson(w, resp)
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/service/api/internal/handler/userOpt/favoriteOptHandler.go:
--------------------------------------------------------------------------------
1 | package userOpt
2 |
3 | import (
4 | "net/http"
5 |
6 | "douyin/service/api/internal/logic/userOpt"
7 | "douyin/service/api/internal/svc"
8 | "douyin/service/api/internal/types"
9 | "github.com/zeromicro/go-zero/rest/httpx"
10 | )
11 |
12 | func FavoriteOptHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
13 | return func(w http.ResponseWriter, r *http.Request) {
14 | var req types.FavoriteOptReq
15 | if err := httpx.Parse(r, &req); err != nil {
16 | httpx.Error(w, err)
17 | return
18 | }
19 |
20 | l := userOpt.NewFavoriteOptLogic(r.Context(), svcCtx)
21 | resp, err := l.FavoriteOpt(&req)
22 | if err != nil {
23 | httpx.Error(w, err)
24 | } else {
25 | httpx.OkJson(w, resp)
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/service/api/internal/handler/userOpt/followOptHandler.go:
--------------------------------------------------------------------------------
1 | package userOpt
2 |
3 | import (
4 | "net/http"
5 |
6 | "douyin/service/api/internal/logic/userOpt"
7 | "douyin/service/api/internal/svc"
8 | "douyin/service/api/internal/types"
9 | "github.com/zeromicro/go-zero/rest/httpx"
10 | )
11 |
12 | func FollowOptHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
13 | return func(w http.ResponseWriter, r *http.Request) {
14 | var req types.FollowOptReq
15 | if err := httpx.Parse(r, &req); err != nil {
16 | httpx.Error(w, err)
17 | return
18 | }
19 |
20 | l := userOpt.NewFollowOptLogic(r.Context(), svcCtx)
21 | resp, err := l.FollowOpt(&req)
22 | if err != nil {
23 | httpx.Error(w, err)
24 | } else {
25 | httpx.OkJson(w, resp)
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/service/api/internal/handler/userOpt/getCommentListHandler.go:
--------------------------------------------------------------------------------
1 | package userOpt
2 |
3 | import (
4 | "net/http"
5 |
6 | "douyin/service/api/internal/logic/userOpt"
7 | "douyin/service/api/internal/svc"
8 | "douyin/service/api/internal/types"
9 | "github.com/zeromicro/go-zero/rest/httpx"
10 | )
11 |
12 | func GetCommentListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
13 | return func(w http.ResponseWriter, r *http.Request) {
14 | var req types.CommentListReq
15 | if err := httpx.Parse(r, &req); err != nil {
16 | httpx.Error(w, err)
17 | return
18 | }
19 |
20 | l := userOpt.NewGetCommentListLogic(r.Context(), svcCtx)
21 | resp, err := l.GetCommentList(&req)
22 | if err != nil {
23 | httpx.Error(w, err)
24 | } else {
25 | httpx.OkJson(w, resp)
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/service/api/internal/handler/userOpt/getFavoriteListHandler.go:
--------------------------------------------------------------------------------
1 | package userOpt
2 |
3 | import (
4 | "net/http"
5 |
6 | "douyin/service/api/internal/logic/userOpt"
7 | "douyin/service/api/internal/svc"
8 | "douyin/service/api/internal/types"
9 | "github.com/zeromicro/go-zero/rest/httpx"
10 | )
11 |
12 | func GetFavoriteListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
13 | return func(w http.ResponseWriter, r *http.Request) {
14 | var req types.FavoriteListReq
15 | if err := httpx.Parse(r, &req); err != nil {
16 | httpx.Error(w, err)
17 | return
18 | }
19 |
20 | l := userOpt.NewGetFavoriteListLogic(r.Context(), svcCtx)
21 | resp, err := l.GetFavoriteList(&req)
22 | if err != nil {
23 | httpx.Error(w, err)
24 | } else {
25 | httpx.OkJson(w, resp)
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/service/api/internal/handler/userOpt/getFollowListHandler.go:
--------------------------------------------------------------------------------
1 | package userOpt
2 |
3 | import (
4 | "net/http"
5 |
6 | "douyin/service/api/internal/logic/userOpt"
7 | "douyin/service/api/internal/svc"
8 | "douyin/service/api/internal/types"
9 | "github.com/zeromicro/go-zero/rest/httpx"
10 | )
11 |
12 | func GetFollowListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
13 | return func(w http.ResponseWriter, r *http.Request) {
14 | var req types.FollowListReq
15 | if err := httpx.Parse(r, &req); err != nil {
16 | httpx.Error(w, err)
17 | return
18 | }
19 |
20 | l := userOpt.NewGetFollowListLogic(r.Context(), svcCtx)
21 | resp, err := l.GetFollowList(&req)
22 | if err != nil {
23 | httpx.Error(w, err)
24 | } else {
25 | httpx.OkJson(w, resp)
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/service/api/internal/handler/userOpt/getFollowerListHandler.go:
--------------------------------------------------------------------------------
1 | package userOpt
2 |
3 | import (
4 | "net/http"
5 |
6 | "douyin/service/api/internal/logic/userOpt"
7 | "douyin/service/api/internal/svc"
8 | "douyin/service/api/internal/types"
9 | "github.com/zeromicro/go-zero/rest/httpx"
10 | )
11 |
12 | func GetFollowerListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
13 | return func(w http.ResponseWriter, r *http.Request) {
14 | var req types.FollowerListReq
15 | if err := httpx.Parse(r, &req); err != nil {
16 | httpx.Error(w, err)
17 | return
18 | }
19 |
20 | l := userOpt.NewGetFollowerListLogic(r.Context(), svcCtx)
21 | resp, err := l.GetFollowerList(&req)
22 | if err != nil {
23 | httpx.Error(w, err)
24 | } else {
25 | httpx.OkJson(w, resp)
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/service/api/internal/logic/publish/getPublishVideoListLogic.go:
--------------------------------------------------------------------------------
1 | package publish
2 |
3 | import (
4 | "context"
5 | "douyin/common/xerr"
6 | "douyin/service/api/internal/svc"
7 | "douyin/service/api/internal/types"
8 | "douyin/service/rpc-user-info/userInfoPb"
9 | "douyin/service/rpc-video-service/videoservice"
10 | "github.com/jinzhu/copier"
11 | "github.com/zeromicro/go-zero/core/logx"
12 | )
13 |
14 | type GetPublishVideoListLogic struct {
15 | logx.Logger
16 | ctx context.Context
17 | svcCtx *svc.ServiceContext
18 | }
19 |
20 | func NewGetPublishVideoListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetPublishVideoListLogic {
21 | return &GetPublishVideoListLogic{
22 | Logger: logx.WithContext(ctx),
23 | ctx: ctx,
24 | svcCtx: svcCtx,
25 | }
26 | }
27 |
28 | func (l *GetPublishVideoListLogic) GetPublishVideoList(req *types.GetPubVideoListReq) (resp *types.GetPubVideoListRes, err error) {
29 | // 获得本人发布列表
30 | res, err := l.svcCtx.VideoSvcRpcClient.GetVideoList(l.ctx, &videoservice.GetVideoListReq{
31 | AuthId: req.UserId,
32 | })
33 | if err != nil {
34 | logx.Errorf("get publish video list fail %s", err.Error())
35 | return &types.GetPubVideoListRes{
36 | Status: types.Status{
37 | Code: xerr.ERR,
38 | Msg: "get publish video list failed",
39 | },
40 | }, nil
41 | }
42 |
43 | // 如果没有视频直接返回
44 | if len(res.VideoPubList) == 0 {
45 | return &types.GetPubVideoListRes{
46 | Status: types.Status{
47 | Code: xerr.OK,
48 | },
49 | }, nil
50 | }
51 |
52 | // 获得本人的信息
53 | authInfo, err := l.svcCtx.UserInfoRpcClient.Info(l.ctx, &userInfoPb.UserInfoReq{
54 | UserId: req.UserId,
55 | })
56 | if err != nil {
57 | return &types.GetPubVideoListRes{
58 | Status: types.Status{
59 | Code: xerr.ERR,
60 | Msg: "get user info failed",
61 | },
62 | }, nil
63 | }
64 |
65 | // 整合数据
66 | // 将rpc返回的videoPubList转换为api返回的videoPubList
67 | var videos []*types.PubVideo
68 | for _, v := range res.VideoPubList {
69 | var video types.PubVideo
70 | _ = copier.Copy(&video, v)
71 | _ = copier.Copy(&video.Author, authInfo.User)
72 |
73 | videos = append(videos, &video)
74 | }
75 |
76 | return &types.GetPubVideoListRes{
77 | Status: types.Status{
78 | Code: xerr.OK,
79 | },
80 | VideoPubList: videos,
81 | }, nil
82 | }
83 |
--------------------------------------------------------------------------------
/service/api/internal/logic/publish/publishVideoLogic.go:
--------------------------------------------------------------------------------
1 | package publish
2 |
3 | import (
4 | "context"
5 | "douyin/common/help/cos"
6 | myToken "douyin/common/help/token"
7 | "douyin/common/xerr"
8 | "douyin/service/api/internal/svc"
9 | "douyin/service/api/internal/types"
10 | "douyin/service/rpc-video-service/videoservice"
11 | "github.com/zeromicro/go-zero/core/logx"
12 | "net/http"
13 | )
14 |
15 | type PublishVideoLogic struct {
16 | logx.Logger
17 | ctx context.Context
18 | svcCtx *svc.ServiceContext
19 | r *http.Request
20 | }
21 |
22 | func NewPublishVideoLogic(ctx context.Context, svcCtx *svc.ServiceContext, r *http.Request) *PublishVideoLogic {
23 | return &PublishVideoLogic{
24 | Logger: logx.WithContext(ctx),
25 | ctx: ctx,
26 | svcCtx: svcCtx,
27 | r: r,
28 | }
29 | }
30 |
31 | func (l *PublishVideoLogic) PublishVideo(req *types.PubVideoReq) (resp *types.PubVideoRes, err error) {
32 | // 从前端获取视频
33 | file, _, err := l.r.FormFile("data")
34 | authId := l.ctx.Value(myToken.CurrentUserId("CurrentUserId")).(int64)
35 |
36 | upLoader := cos.UploaderVideo{
37 | UserId: authId,
38 | MachineId: l.svcCtx.Config.COSConf.MachineId,
39 | VideoBucket: l.svcCtx.Config.COSConf.VideoBucket,
40 | SecretID: l.svcCtx.Config.COSConf.SecretId,
41 | SecretKey: l.svcCtx.Config.COSConf.SecretKey,
42 | }
43 | key, err := upLoader.UploadVideo(l.ctx, file)
44 | if err != nil {
45 | logx.Errorf("upload video failed: %s", err.Error())
46 | return &types.PubVideoRes{
47 | Status: types.Status{
48 | Code: xerr.ERR,
49 | Msg: "upload video failed",
50 | },
51 | }, nil
52 | }
53 |
54 | // 将视频信息存到数据库
55 | _, err = l.svcCtx.VideoSvcRpcClient.PubVideo(l.ctx, &videoservice.PubVideoReq{
56 | AuthId: authId,
57 | Title: req.Title,
58 | PlayURL: l.svcCtx.Config.COSConf.VideoBucket + "/" + key + ".mp4",
59 | CoverURL: l.svcCtx.Config.COSConf.CoverBucket + "/" + key + "_0.jpg",
60 | })
61 | if err != nil {
62 | logx.Errorf("publish video failed: %s", err.Error())
63 | return &types.PubVideoRes{
64 | Status: types.Status{
65 | Code: xerr.ERR,
66 | Msg: "server database err",
67 | },
68 | }, nil
69 | }
70 |
71 | return &types.PubVideoRes{
72 | Status: types.Status{
73 | Code: xerr.OK,
74 | },
75 | }, nil
76 | }
77 |
--------------------------------------------------------------------------------
/service/api/internal/logic/user/userInfoLogic.go:
--------------------------------------------------------------------------------
1 | package user
2 |
3 | import (
4 | "context"
5 | "douyin/common/xerr"
6 | "douyin/service/rpc-user-info/userinfoservice"
7 |
8 | "douyin/service/api/internal/svc"
9 | "douyin/service/api/internal/types"
10 |
11 | "github.com/zeromicro/go-zero/core/logx"
12 | )
13 |
14 | type UserInfoLogic struct {
15 | logx.Logger
16 | ctx context.Context
17 | svcCtx *svc.ServiceContext
18 | }
19 |
20 | func NewUserInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserInfoLogic {
21 | return &UserInfoLogic{
22 | Logger: logx.WithContext(ctx),
23 | ctx: ctx,
24 | svcCtx: svcCtx,
25 | }
26 | }
27 |
28 | // UserInfo 登陆后调用此接口拉取个人信息
29 | func (l *UserInfoLogic) UserInfo(req *types.UserInfoReq) (resp *types.UserInfoRes, err error) {
30 | info, err := l.svcCtx.UserInfoRpcClient.Info(l.ctx, &userinfoservice.UserInfoReq{
31 | UserId: req.UserId,
32 | })
33 | if err != nil {
34 | logx.Errorf("get user info failed: %v", err.Error())
35 | return &types.UserInfoRes{
36 | Status: types.Status{
37 | Code: xerr.ERR,
38 | Msg: "get user info failed",
39 | },
40 | }, nil
41 | }
42 |
43 | return &types.UserInfoRes{
44 | Status: types.Status{
45 | Code: xerr.OK,
46 | },
47 | User: &types.User{
48 | UserId: info.User.UserId,
49 | UserName: info.User.UserName,
50 | FollowCount: info.User.FollowCount,
51 | FollowerCount: info.User.FollowerCount,
52 | IsFollow: false,
53 | },
54 | }, nil
55 | }
56 |
--------------------------------------------------------------------------------
/service/api/internal/logic/user/userLoginLogic.go:
--------------------------------------------------------------------------------
1 | package user
2 |
3 | import (
4 | "context"
5 | "douyin/common/xerr"
6 | "douyin/service/api/internal/svc"
7 | "douyin/service/api/internal/types"
8 | "douyin/service/rpc-user-info/userinfoservice"
9 |
10 | "github.com/zeromicro/go-zero/core/logx"
11 | )
12 |
13 | type UserLoginLogic struct {
14 | logx.Logger
15 | ctx context.Context
16 | svcCtx *svc.ServiceContext
17 | }
18 |
19 | func NewUserLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserLoginLogic {
20 | return &UserLoginLogic{
21 | Logger: logx.WithContext(ctx),
22 | ctx: ctx,
23 | svcCtx: svcCtx,
24 | }
25 | }
26 |
27 | // UserLogin 用户登陆
28 | // 通过username获得用户密码,然后比对密码
29 | // token 先查redis 是否存在,如果存在,则直接返回,如果不存在,则生成token,并存入redis
30 | // 并返回userId,token
31 | func (l *UserLoginLogic) UserLogin(req *types.UserLoginReq) (resp *types.UserLoginRes, err error) {
32 | res, err := l.svcCtx.UserInfoRpcClient.Login(l.ctx, &userinfoservice.LoginReq{
33 | UserName: req.UserName,
34 | Password: req.Password,
35 | })
36 | if err != nil {
37 | logx.Errorf("login failed: %v", err.Error())
38 | return &types.UserLoginRes{
39 | Status: types.Status{
40 | Code: xerr.ERR,
41 | Msg: "login failed",
42 | },
43 | }, nil
44 | }
45 |
46 | return &types.UserLoginRes{
47 | Status: types.Status{
48 | Code: xerr.OK,
49 | },
50 | IdWithTokenRes: types.IdWithTokenRes{
51 | UserId: res.UserId,
52 | Token: res.Token,
53 | },
54 | }, nil
55 | }
56 |
--------------------------------------------------------------------------------
/service/api/internal/logic/user/userRegisterLogic.go:
--------------------------------------------------------------------------------
1 | package user
2 |
3 | import (
4 | "context"
5 | "douyin/common/xerr"
6 | "douyin/service/api/internal/svc"
7 | "douyin/service/api/internal/types"
8 | "douyin/service/rpc-user-info/userinfoservice"
9 | "github.com/zeromicro/go-zero/core/logx"
10 | )
11 |
12 | type UserRegisterLogic struct {
13 | logx.Logger
14 | ctx context.Context
15 | svcCtx *svc.ServiceContext
16 | }
17 |
18 | func NewUserRegisterLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserRegisterLogic {
19 | return &UserRegisterLogic{
20 | Logger: logx.WithContext(ctx),
21 | ctx: ctx,
22 | svcCtx: svcCtx,
23 | }
24 | }
25 |
26 | // UserRegister 对密码使用bcrypt加密
27 | // 生成token,并将token和userId存入redis
28 | // rpc调用Register服务保存用户信息
29 | func (l *UserRegisterLogic) UserRegister(req *types.UserRegisterReq) (resp *types.UserRegisterRes, err error) {
30 | res, err := l.svcCtx.UserInfoRpcClient.Register(l.ctx, &userinfoservice.RegisterReq{
31 | UserName: req.UserName,
32 | Password: req.Password,
33 | })
34 | if err != nil {
35 | logx.Errorf("register failed: %s", err.Error())
36 | return &types.UserRegisterRes{
37 | Status: types.Status{
38 | Code: xerr.SECRET_ERROR,
39 | Msg: "register failed" + err.Error(),
40 | },
41 | }, nil
42 | }
43 |
44 | return &types.UserRegisterRes{
45 | Status: types.Status{
46 | Code: xerr.OK,
47 | },
48 | IdWithTokenRes: types.IdWithTokenRes{
49 | UserId: res.UserId,
50 | Token: res.Token,
51 | },
52 | }, nil
53 | }
54 |
--------------------------------------------------------------------------------
/service/api/internal/logic/userOpt/commentOptLogic.go:
--------------------------------------------------------------------------------
1 | package userOpt
2 |
3 | import (
4 | "context"
5 | "douyin/common/help/cos"
6 | myToken "douyin/common/help/token"
7 | "douyin/common/messageTypes"
8 | "douyin/common/xerr"
9 | "douyin/service/api/internal/svc"
10 | "douyin/service/api/internal/types"
11 | "douyin/service/rpc-user-info/userInfoPb"
12 | "encoding/json"
13 | "github.com/jinzhu/copier"
14 | "github.com/pkg/errors"
15 | "time"
16 |
17 | "github.com/zeromicro/go-zero/core/logx"
18 | )
19 |
20 | type CommentOptLogic struct {
21 | logx.Logger
22 | ctx context.Context
23 | svcCtx *svc.ServiceContext
24 | }
25 |
26 | func NewCommentOptLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CommentOptLogic {
27 | return &CommentOptLogic{
28 | Logger: logx.WithContext(ctx),
29 | ctx: ctx,
30 | svcCtx: svcCtx,
31 | }
32 | }
33 |
34 | func (l *CommentOptLogic) CommentOpt(req *types.CommentOptReq) (resp *types.CommentOptRes, err error) {
35 |
36 | // 前端传入的是1,2表示评论取消评论,入口这里就将它转换成1,0表示评论取消评论
37 | msgTemp, status, err := l.getActionType(req)
38 |
39 | if msgTemp.ActionType == messageTypes.ActionErr || err != nil {
40 | logx.Errorf("CommentOptLogic CommentOpt err: %s", err.Error())
41 | return status, nil
42 | }
43 |
44 | if msgTemp.ActionType == 1 {
45 | // 拉取发布消息的用户信息
46 | userInfo, err := l.svcCtx.UserInfoRpcClient.Info(l.ctx, &userInfoPb.UserInfoReq{
47 | UserId: msgTemp.UserId,
48 | })
49 | if err != nil {
50 | logx.Errorf("CommentOptLogic CommentOpt err: %s", err.Error())
51 | return &types.CommentOptRes{
52 | Status: types.Status{
53 | Code: xerr.ERR,
54 | Msg: "get user info err",
55 | },
56 | }, nil
57 | }
58 | return &types.CommentOptRes{
59 | Status: types.Status{
60 | Code: xerr.OK,
61 | },
62 | Comment: &types.Comment{
63 | CommentId: msgTemp.CommentId,
64 | User: types.User{
65 | UserId: userInfo.User.UserId,
66 | UserName: userInfo.User.UserName,
67 | FollowCount: userInfo.User.FollowCount,
68 | FollowerCount: userInfo.User.FollowerCount,
69 | IsFollow: false,
70 | },
71 | Content: msgTemp.CommentText,
72 | CreateTime: msgTemp.CreateDate,
73 | },
74 | }, nil
75 | }
76 |
77 | if msgTemp.ActionType == 0 {
78 | return &types.CommentOptRes{
79 | Status: types.Status{
80 | Code: xerr.OK,
81 | },
82 | }, nil
83 | }
84 |
85 | return nil, nil
86 | }
87 |
88 | func (l *CommentOptLogic) getActionType(req *types.CommentOptReq) (*messageTypes.UserCommentOptMessage, *types.CommentOptRes, error) {
89 | var msgTemp messageTypes.UserCommentOptMessage
90 | _ = copier.Copy(&msgTemp, req)
91 |
92 | switch req.ActionType { // 方便扩展
93 | case messageTypes.ActionADD:
94 | msgTemp.UserId = l.ctx.Value(myToken.CurrentUserId("CurrentUserId")).(int64)
95 | msgTemp.CreateDate = time.Now().Format("01-01")
96 | msgTemp.ActionType = 1
97 | var genId cos.GenSnowFlake
98 | id, _ := genId.GenSnowFlake(1)
99 | msgTemp.CommentId = int64(id)
100 |
101 | case messageTypes.ActionCancel:
102 | msgTemp.UserId = l.ctx.Value(myToken.CurrentUserId("CurrentUserId")).(int64)
103 | msgTemp.ActionType = 0
104 | default:
105 | msgTemp.ActionType = -99
106 | return nil, &types.CommentOptRes{
107 | Status: types.Status{
108 | Code: xerr.ERR,
109 | Msg: "send message to CommentOptMsgConsumer ActionType err",
110 | },
111 | }, errors.New("operate error")
112 | }
113 |
114 | // 序列化
115 | msg, err := json.Marshal(msgTemp)
116 | if err != nil {
117 | return nil, &types.CommentOptRes{
118 | Status: types.Status{
119 | Code: xerr.ERR,
120 | Msg: "send message to CommentOptMsgConsumer json.Marshal err",
121 | },
122 | }, errors.Wrapf(err, " json.Marshal err")
123 | }
124 |
125 | // 向消息队列发送消息
126 | err = l.svcCtx.CommentOptMsgProducer.Push(string(msg))
127 | if err != nil {
128 | return nil, &types.CommentOptRes{
129 | Status: types.Status{
130 | Code: xerr.ERR,
131 | Msg: "send message to CommentOptMsgConsumer err",
132 | },
133 | }, errors.Wrapf(err, " json.Marshal err")
134 | }
135 | return &msgTemp, nil, nil
136 | }
137 |
--------------------------------------------------------------------------------
/service/api/internal/logic/userOpt/favoriteOptLogic.go:
--------------------------------------------------------------------------------
1 | package userOpt
2 |
3 | import (
4 | "context"
5 | myToken "douyin/common/help/token"
6 | "douyin/common/messageTypes"
7 | "douyin/common/xerr"
8 | "douyin/service/api/internal/svc"
9 | "douyin/service/api/internal/types"
10 | "encoding/json"
11 | "github.com/jinzhu/copier"
12 | "github.com/pkg/errors"
13 | "github.com/zeromicro/go-zero/core/logx"
14 | )
15 |
16 | type FavoriteOptLogic struct {
17 | logx.Logger
18 | ctx context.Context
19 | svcCtx *svc.ServiceContext
20 | }
21 |
22 | func NewFavoriteOptLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FavoriteOptLogic {
23 | return &FavoriteOptLogic{
24 | Logger: logx.WithContext(ctx),
25 | ctx: ctx,
26 | svcCtx: svcCtx,
27 | }
28 | }
29 |
30 | func (l *FavoriteOptLogic) FavoriteOpt(req *types.FavoriteOptReq) (resp *types.FavoriteOptRes, err error) {
31 |
32 | var msgTemp messageTypes.UserFavoriteOptMessage
33 | _ = copier.Copy(&msgTemp, req)
34 |
35 | // 前端传入的是1,2表示点赞与取消点赞,入口这里就将它转换成1,0表示点赞与取消点赞
36 | msgTemp.ActionType = l.getActionType(req.ActionType)
37 | if msgTemp.ActionType == -99 {
38 | logx.Errorf("error actionType : %d", req.ActionType)
39 | return &types.FavoriteOptRes{
40 | Status: types.Status{
41 | Code: xerr.ERR,
42 | Msg: "operate error",
43 | },
44 | }, nil
45 | }
46 |
47 | msgTemp.UserId = l.ctx.Value(myToken.CurrentUserId("CurrentUserId")).(int64)
48 |
49 | // 序列化
50 | msg, err := json.Marshal(msgTemp)
51 | if err != nil {
52 | logx.Errorf("FavoriteOpt json.Marshal err : %s", err.Error())
53 | return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), " json.Marshal err")
54 | }
55 |
56 | // 向消息队列发送消息
57 | err = l.svcCtx.FavoriteOptMsgProducer.Push(string(msg))
58 | if err != nil {
59 | logx.Errorf("FavoriteOpt msgProducer.Push err : %s", err.Error())
60 | return &types.FavoriteOptRes{
61 | Status: types.Status{
62 | Code: xerr.ERR,
63 | Msg: "send message to FavoriteOptMsgConsumer err",
64 | },
65 | }, nil
66 | }
67 |
68 | return &types.FavoriteOptRes{
69 | Status: types.Status{
70 | Code: xerr.OK,
71 | },
72 | }, nil
73 | }
74 |
75 | func (l *FavoriteOptLogic) getActionType(actionType int64) int64 {
76 |
77 | switch actionType { // 方便扩展
78 | case messageTypes.ActionADD:
79 | return 1
80 | case messageTypes.ActionCancel:
81 | return 0
82 | default:
83 | return -99
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/service/api/internal/logic/userOpt/followOptLogic.go:
--------------------------------------------------------------------------------
1 | package userOpt
2 |
3 | import (
4 | "context"
5 | myToken "douyin/common/help/token"
6 | "douyin/common/messageTypes"
7 | "douyin/common/xerr"
8 | "encoding/json"
9 | "github.com/jinzhu/copier"
10 | "github.com/pkg/errors"
11 |
12 | "douyin/service/api/internal/svc"
13 | "douyin/service/api/internal/types"
14 |
15 | "github.com/zeromicro/go-zero/core/logx"
16 | )
17 |
18 | type FollowOptLogic struct {
19 | logx.Logger
20 | ctx context.Context
21 | svcCtx *svc.ServiceContext
22 | }
23 |
24 | func NewFollowOptLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FollowOptLogic {
25 | return &FollowOptLogic{
26 | Logger: logx.WithContext(ctx),
27 | ctx: ctx,
28 | svcCtx: svcCtx,
29 | }
30 | }
31 |
32 | func (l *FollowOptLogic) FollowOpt(req *types.FollowOptReq) (resp *types.FollowOptRes, err error) {
33 | var msgTemp messageTypes.UserFollowOptMessage
34 | _ = copier.Copy(&msgTemp, req)
35 |
36 | msgTemp.UserId = l.ctx.Value(myToken.CurrentUserId("CurrentUserId")).(int64)
37 |
38 | // 前端传入的是1,2表示关注与取消关注,入口这里就将它转换成1,0表示点赞与取消关注
39 | msgTemp.ActionType = l.getActionType(req.ActionType)
40 |
41 | if msgTemp.ActionType == -99 {
42 | logx.Errorf("error actionType : %+v", req.ActionType)
43 | return &types.FollowOptRes{
44 | Status: types.Status{
45 | Code: xerr.ERR,
46 | Msg: "operate error",
47 | },
48 | }, nil
49 | }
50 |
51 | // 序列化
52 | msg, err := json.Marshal(msgTemp)
53 | if err != nil {
54 | logx.Errorf("json.Marshal err : %s", err.Error())
55 | return nil, errors.Wrapf(err, " json.Marshal err")
56 | }
57 |
58 | // 向消息队列发送消息
59 | err = l.svcCtx.FollowOptMsgProducer.Push(string(msg))
60 | if err != nil {
61 | logx.Errorf("FollowOptMsgProducer.Push err : %s", err.Error())
62 | return &types.FollowOptRes{
63 | Status: types.Status{
64 | Code: xerr.ERR,
65 | Msg: "send message to FollowOptMsgProducer err",
66 | },
67 | }, nil
68 | }
69 |
70 | return &types.FollowOptRes{
71 | Status: types.Status{
72 | Code: xerr.OK,
73 | Msg: "operate success",
74 | },
75 | }, nil
76 | }
77 |
78 | func (l *FollowOptLogic) getActionType(actionType int64) int64 {
79 |
80 | switch actionType { // 方便扩展
81 | case messageTypes.ActionADD:
82 | return 1
83 | case messageTypes.ActionCancel:
84 | return 0
85 | default:
86 | return -99
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/service/api/internal/logic/userOpt/getCommentListLogic.go:
--------------------------------------------------------------------------------
1 | package userOpt
2 |
3 | import (
4 | "context"
5 | myToken "douyin/common/help/token"
6 | "douyin/common/xerr"
7 | "douyin/service/rpc-user-info/userInfoPb"
8 | "douyin/service/rpc-user-operate/useroptservice"
9 | "github.com/jinzhu/copier"
10 | "github.com/zeromicro/go-zero/core/mr"
11 |
12 | "douyin/service/api/internal/svc"
13 | "douyin/service/api/internal/types"
14 |
15 | "github.com/zeromicro/go-zero/core/logx"
16 | )
17 |
18 | type GetCommentListLogic struct {
19 | logx.Logger
20 | ctx context.Context
21 | svcCtx *svc.ServiceContext
22 | }
23 |
24 | func NewGetCommentListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetCommentListLogic {
25 | return &GetCommentListLogic{
26 | Logger: logx.WithContext(ctx),
27 | ctx: ctx,
28 | svcCtx: svcCtx,
29 | }
30 | }
31 |
32 | func (l *GetCommentListLogic) GetCommentList(req *types.CommentListReq) (resp *types.CommentListRes, err error) {
33 | // 1.获得视频里所有的点赞内容
34 | // 2.获得点赞用的用户信息,follow情况
35 | comments, err := l.svcCtx.UserOptSvcRpcClient.GetVideoComment(l.ctx, &useroptservice.GetVideoCommentReq{
36 | VideoId: req.VideoId,
37 | })
38 | if err != nil {
39 | logx.Errorf("get video comment list fail %s", err.Error())
40 | return &types.CommentListRes{
41 | Status: types.Status{
42 | Code: xerr.ERR,
43 | Msg: "get video comment list fail",
44 | },
45 | }, nil
46 | }
47 |
48 | var commentList []*types.Comment // 最终返回的视频列表
49 |
50 | // 当前的用户id
51 | userId := l.ctx.Value(myToken.CurrentUserId("CurrentUserId")).(int64)
52 | if comments != nil {
53 | // 把视频里的作者的信息找出来 并去重
54 | var authIds = make([]int64, 0, len(comments.CommentList))
55 | for _, v := range comments.CommentList {
56 | var authIdsTemp = make(map[int64]interface{}, len(comments.CommentList))
57 |
58 | if _, ok := authIdsTemp[v.UserId]; !ok {
59 | authIdsTemp[v.UserId] = nil
60 | authIds = append(authIds, v.UserId)
61 | }
62 | }
63 |
64 | var authsInfo *userInfoPb.AuthsInfoResp
65 | var userFollowList *useroptservice.GetUserFollowResp
66 | err = mr.Finish(func() error {
67 | // 查询所有视频的的作者信息
68 | authsInfo, err = l.svcCtx.UserInfoRpcClient.AuthsInfo(l.ctx, &userInfoPb.AuthsInfoReq{ // 返回作者信息 按照作者id升序排列
69 | AuthIds: authIds,
70 | })
71 | if err != nil {
72 | resp = &types.CommentListRes{
73 | Status: types.Status{
74 | Code: xerr.ERR,
75 | Msg: "get author list fail",
76 | },
77 | }
78 | return err
79 | }
80 | return nil
81 | }, func() error {
82 | // 查找当前用户对作者的关注状态
83 | userFollowList, err = l.svcCtx.UserOptSvcRpcClient.GetUserFollow(l.ctx, &useroptservice.GetUserFollowReq{
84 | UserId: userId,
85 | AuthIds: authIds,
86 | })
87 | if err != nil {
88 | resp = &types.CommentListRes{
89 | Status: types.Status{
90 | Code: xerr.ERR,
91 | Msg: "get user follow list fail",
92 | },
93 | }
94 | return err
95 | }
96 | return nil
97 | })
98 | if err != nil {
99 | logx.Errorf("get user follow list fail %s", err.Error())
100 | return resp, nil
101 | }
102 |
103 | for _, v := range comments.CommentList {
104 | var comment types.Comment
105 | _ = copier.Copy(&comment, v)
106 | _ = copier.Copy(&comment.User, authsInfo.Auths[v.UserId])
107 | // 用户对该视频的作者是否关注
108 | comment.User.IsFollow = userFollowList.UserFollowList[v.UserId]
109 | comment.CreateTime = v.CreateDate
110 |
111 | commentList = append(commentList, &comment)
112 | }
113 |
114 | return &types.CommentListRes{
115 | Status: types.Status{
116 | Code: xerr.OK,
117 | },
118 | CommentList: commentList,
119 | }, nil
120 |
121 | } else { // 没有点赞视频
122 | return &types.CommentListRes{
123 | Status: types.Status{
124 | Code: xerr.OK,
125 | },
126 | }, nil
127 | }
128 |
129 | }
130 |
--------------------------------------------------------------------------------
/service/api/internal/logic/userOpt/getFavoriteListLogic.go:
--------------------------------------------------------------------------------
1 | package userOpt
2 |
3 | import (
4 | "context"
5 | "douyin/common/xerr"
6 | "douyin/service/rpc-user-info/userInfoPb"
7 | "douyin/service/rpc-user-operate/useroptservice"
8 | "douyin/service/rpc-video-service/videoSvcPb"
9 | "github.com/jinzhu/copier"
10 | "github.com/zeromicro/go-zero/core/mr"
11 |
12 | "douyin/service/api/internal/svc"
13 | "douyin/service/api/internal/types"
14 |
15 | "github.com/zeromicro/go-zero/core/logx"
16 | )
17 |
18 | type GetFavoriteListLogic struct {
19 | logx.Logger
20 | ctx context.Context
21 | svcCtx *svc.ServiceContext
22 | }
23 |
24 | func NewGetFavoriteListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetFavoriteListLogic {
25 | return &GetFavoriteListLogic{
26 | Logger: logx.WithContext(ctx),
27 | ctx: ctx,
28 | svcCtx: svcCtx,
29 | }
30 | }
31 |
32 | func (l *GetFavoriteListLogic) GetFavoriteList(req *types.FavoriteListReq) (resp *types.FavoriteListRes, err error) {
33 | videosId, err := l.svcCtx.UserOptSvcRpcClient.GetUserFavorite(l.ctx, &useroptservice.GetUserFavoriteReq{
34 | UserId: req.UserId,
35 | })
36 | if err != nil {
37 | logx.Errorf("GetFavoriteListLogic.GetFavoriteList error: %s", err.Error())
38 | return &types.FavoriteListRes{
39 | Status: types.Status{
40 | Code: xerr.ERR,
41 | Msg: "get user favorite list fail ",
42 | },
43 | }, nil
44 | }
45 |
46 | var videoList []*types.PubVideo // 最终返回的视频列表
47 |
48 | // 存在点赞的视频
49 | if videosId != nil {
50 | // 拿着这些视频id去查询视频信息
51 | videoArr, err := l.svcCtx.VideoSvcRpcClient.GetMyFavoriteVideos(l.ctx, &videoSvcPb.MyFavoriteVideosReq{
52 | VideoIdArr: videosId.UserFavoriteArr,
53 | })
54 | if err != nil {
55 | logx.Errorf("GetFavoriteListLogic.GetFavoriteList error: %s", err.Error())
56 | return &types.FavoriteListRes{
57 | Status: types.Status{
58 | Code: xerr.ERR,
59 | Msg: "get video list fail ",
60 | },
61 | }, nil
62 | }
63 |
64 | // 把视频里的作者的信息找出来 并去重
65 | var authIds = make([]int64, 0, len(videoArr.VideoPubList))
66 | for _, v := range videoArr.VideoPubList {
67 | var authIdsTemp = make(map[int64]interface{}, len(videoArr.VideoPubList))
68 |
69 | if _, ok := authIdsTemp[v.AuthId]; !ok {
70 | authIdsTemp[v.AuthId] = nil
71 | authIds = append(authIds, v.AuthId)
72 | }
73 | }
74 |
75 | var authsInfo *userInfoPb.AuthsInfoResp
76 | var userFollowList *useroptservice.GetUserFollowResp
77 |
78 | err = mr.Finish(func() error {
79 | // 查询所有视频的的作者信息
80 | authsInfo, err = l.svcCtx.UserInfoRpcClient.AuthsInfo(l.ctx, &userInfoPb.AuthsInfoReq{ // 返回作者信息 按照作者id升序排列
81 | AuthIds: authIds,
82 | })
83 | if err != nil {
84 | resp = &types.FavoriteListRes{
85 | Status: types.Status{
86 | Code: xerr.ERR,
87 | Msg: "get author list fail " + err.Error(),
88 | },
89 | }
90 | return err
91 | }
92 | return nil
93 | }, func() error {
94 | // 查找当前用户对作者的关注状态
95 | userFollowList, err = l.svcCtx.UserOptSvcRpcClient.GetUserFollow(l.ctx, &useroptservice.GetUserFollowReq{
96 | UserId: req.UserId,
97 | AuthIds: authIds,
98 | })
99 | if err != nil {
100 | resp = &types.FavoriteListRes{
101 | Status: types.Status{
102 | Code: xerr.ERR,
103 | Msg: "get user follow list fail " + err.Error(),
104 | },
105 | }
106 | return err
107 | }
108 | return nil
109 | })
110 | if err != nil {
111 | logx.Errorf("GetFavoriteListLogic error: %s", err.Error())
112 | return resp, err
113 | }
114 |
115 | for _, v := range videoArr.VideoPubList {
116 | var video types.PubVideo
117 | _ = copier.Copy(&video, v)
118 | _ = copier.Copy(&video.Author, authsInfo.Auths[v.AuthId])
119 | // 点赞列表里肯定点赞了
120 | video.IsFavorite = true
121 | // 用户对该视频的作者是否关注
122 | video.Author.IsFollow = userFollowList.UserFollowList[v.AuthId]
123 |
124 | videoList = append(videoList, &video)
125 | }
126 |
127 | return &types.FavoriteListRes{
128 | Status: types.Status{
129 | Code: xerr.OK,
130 | },
131 | FavoriteList: videoList,
132 | }, nil
133 |
134 | } else { // 没有点赞视频
135 | return &types.FavoriteListRes{
136 | Status: types.Status{
137 | Code: xerr.OK,
138 | },
139 | }, nil
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/service/api/internal/logic/userOpt/getFollowListLogic.go:
--------------------------------------------------------------------------------
1 | package userOpt
2 |
3 | import (
4 | "context"
5 | "douyin/common/xerr"
6 | "douyin/service/api/internal/svc"
7 | "douyin/service/api/internal/types"
8 | "douyin/service/rpc-user-info/userInfoPb"
9 | "douyin/service/rpc-user-operate/useroptservice"
10 | "github.com/jinzhu/copier"
11 |
12 | "github.com/zeromicro/go-zero/core/logx"
13 | )
14 |
15 | type GetFollowListLogic struct {
16 | logx.Logger
17 | ctx context.Context
18 | svcCtx *svc.ServiceContext
19 | }
20 |
21 | func NewGetFollowListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetFollowListLogic {
22 | return &GetFollowListLogic{
23 | Logger: logx.WithContext(ctx),
24 | ctx: ctx,
25 | svcCtx: svcCtx,
26 | }
27 | }
28 |
29 | func (l *GetFollowListLogic) GetFollowList(req *types.FollowListReq) (resp *types.FollowListRes, err error) {
30 |
31 | followsIdMap, err := l.svcCtx.UserOptSvcRpcClient.GetUserFollow(l.ctx, &useroptservice.GetUserFollowReq{
32 | UserId: req.UserId,
33 | })
34 | if err != nil {
35 | logx.Errorf("get user follow list fail %s", err.Error())
36 | return &types.FollowListRes{
37 | Status: types.Status{
38 | Code: xerr.ERR,
39 | Msg: "get user follow list fail ",
40 | },
41 | }, nil
42 | }
43 |
44 | var followsIdArr []int64
45 | for k := range followsIdMap.UserFollowList {
46 | followsIdArr = append(followsIdArr, k)
47 | }
48 |
49 | var userList []*types.User // 最终返回的关注者列表
50 |
51 | if followsIdMap != nil {
52 |
53 | followsInfo, err := l.svcCtx.UserInfoRpcClient.AuthsInfo(l.ctx, &userInfoPb.AuthsInfoReq{
54 | AuthIds: followsIdArr,
55 | })
56 | if err != nil {
57 | logx.Errorf("get user follow list fail %s", err.Error())
58 | return &types.FollowListRes{
59 | Status: types.Status{
60 | Code: xerr.ERR,
61 | Msg: "get AuthsInfo list fail " + err.Error(),
62 | },
63 | }, nil
64 | }
65 |
66 | for _, v := range followsInfo.Auths {
67 | var user types.User
68 | _ = copier.Copy(&user, v)
69 | user.IsFollow = true
70 |
71 | userList = append(userList, &user)
72 | }
73 |
74 | return &types.FollowListRes{
75 | Status: types.Status{
76 | Code: xerr.OK,
77 | },
78 | UserFollowlist: userList,
79 | }, nil
80 |
81 | } else { // 没有关注任何人
82 | return &types.FollowListRes{
83 | Status: types.Status{
84 | Code: xerr.OK,
85 | },
86 | }, nil
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/service/api/internal/logic/userOpt/getFollowerListLogic.go:
--------------------------------------------------------------------------------
1 | package userOpt
2 |
3 | import (
4 | "context"
5 | "douyin/common/xerr"
6 | "douyin/service/rpc-user-info/userInfoPb"
7 | "douyin/service/rpc-user-operate/useroptservice"
8 | "github.com/jinzhu/copier"
9 |
10 | "douyin/service/api/internal/svc"
11 | "douyin/service/api/internal/types"
12 |
13 | "github.com/zeromicro/go-zero/core/logx"
14 | )
15 |
16 | type GetFollowerListLogic struct {
17 | logx.Logger
18 | ctx context.Context
19 | svcCtx *svc.ServiceContext
20 | }
21 |
22 | func NewGetFollowerListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetFollowerListLogic {
23 | return &GetFollowerListLogic{
24 | Logger: logx.WithContext(ctx),
25 | ctx: ctx,
26 | svcCtx: svcCtx,
27 | }
28 | }
29 |
30 | func (l *GetFollowerListLogic) GetFollowerList(req *types.FollowerListReq) (resp *types.FollowerListRes, err error) {
31 |
32 | followersIdMap, err := l.svcCtx.UserOptSvcRpcClient.GetUserFollower(l.ctx, &useroptservice.GetUserFollowerReq{
33 | UserId: req.UserId,
34 | })
35 | if err != nil {
36 | return &types.FollowerListRes{
37 | Status: types.Status{
38 | Code: xerr.ERR,
39 | Msg: "get user follower list fail ",
40 | },
41 | }, nil
42 | }
43 |
44 | var followersIdArr []int64
45 | for k := range followersIdMap.UserFollowerList {
46 | followersIdArr = append(followersIdArr, k)
47 | }
48 |
49 | var userList []*types.User // 最终返回的关注者列表
50 |
51 | if followersIdMap != nil {
52 | // 查看我是否关注了粉丝
53 | followersInfo, err := l.svcCtx.UserInfoRpcClient.AuthsInfo(l.ctx, &userInfoPb.AuthsInfoReq{
54 | AuthIds: followersIdArr,
55 | })
56 | if err != nil {
57 | logx.Errorf("get AuthsInfo list fail %s", err.Error())
58 | return &types.FollowerListRes{
59 | Status: types.Status{
60 | Code: xerr.ERR,
61 | Msg: "get AuthsInfo list fail",
62 | },
63 | }, nil
64 | }
65 |
66 | allFollowersMap, err := l.svcCtx.UserOptSvcRpcClient.GetUserFollow(l.ctx, &useroptservice.GetUserFollowReq{
67 | UserId: req.UserId,
68 | AuthIds: followersIdArr,
69 | })
70 | if err != nil {
71 | logx.Errorf("get user follow list fail %s", err.Error())
72 | return &types.FollowerListRes{
73 | Status: types.Status{
74 | Code: xerr.ERR,
75 | Msg: "get user follow list fail",
76 | },
77 | }, nil
78 | }
79 |
80 | for _, v := range followersInfo.Auths {
81 | var user types.User
82 | _ = copier.Copy(&user, v)
83 | user.IsFollow = allFollowersMap.UserFollowList[v.UserId]
84 |
85 | userList = append(userList, &user)
86 | }
87 |
88 | return &types.FollowerListRes{
89 | Status: types.Status{
90 | Code: xerr.OK,
91 | },
92 | UserFollowerlist: userList,
93 | }, nil
94 |
95 | } else { // 没有关注任何人
96 | return &types.FollowerListRes{
97 | Status: types.Status{
98 | Code: xerr.OK,
99 | },
100 | }, nil
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/service/api/internal/middleware/authjwtMiddleware.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "context"
5 | myToken "douyin/common/help/token"
6 | "douyin/common/xerr"
7 | "douyin/service/api/internal/types"
8 | "encoding/json"
9 | "fmt"
10 | "net/http"
11 | "strconv"
12 | "time"
13 | )
14 |
15 | type AuthJWTMiddleware struct {
16 | }
17 |
18 | func NewAuthJWTMiddleware() *AuthJWTMiddleware {
19 | return &AuthJWTMiddleware{}
20 | }
21 |
22 | /*
23 | 这里前端有bug 用户信息、投稿接口、发布列表应该都带有user_id 才能对比token解析出来的user_id是否一致
24 | 但是前端的投稿接口没有user_id 只有token 所以这里没有办法判断
25 | */
26 |
27 | func (m *AuthJWTMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
28 | return func(w http.ResponseWriter, r *http.Request) {
29 | status := new(types.Status)
30 | token := r.FormValue("token")
31 |
32 | if token == "" {
33 | status.Code = xerr.REUQEST_PARAM_ERROR
34 | status.Msg = "no token"
35 | res, _ := json.Marshal(status)
36 | _, _ = w.Write(res)
37 | return
38 | }
39 | // 解析token 判断是否有效
40 | var parseClaims myToken.ParseToken
41 | claims, err := parseClaims.ParseToken(token)
42 | if err != nil {
43 | status.Code = xerr.REUQEST_PARAM_ERROR
44 | status.Msg = "param error " + err.Error()
45 | res, _ := json.Marshal(status)
46 | _, _ = w.Write(res)
47 | return
48 | }
49 |
50 | id := r.FormValue("user_id")
51 | var userId int64
52 | if id != "" {
53 | res, err := strconv.ParseInt(id, 10, 64)
54 | if err != nil {
55 | status.Code = xerr.REUQEST_PARAM_ERROR
56 | status.Msg = fmt.Sprintf("param error user_id : %d", res)
57 | res, _ := json.Marshal(status)
58 | _, _ = w.Write(res)
59 | return
60 | }
61 | userId = res
62 | }
63 |
64 | if userId != 0 && userId != claims.UserId {
65 | status.Code = xerr.REUQEST_PARAM_ERROR
66 | status.Msg = "user_id not match"
67 | res, _ := json.Marshal(status)
68 | _, _ = w.Write(res)
69 | return
70 | }
71 |
72 | // 过期时间点 小于当前时间 表示过期
73 | if claims.ExpireAt < time.Now().Unix() {
74 | status.Code = xerr.REUQEST_PARAM_ERROR
75 | status.Msg = "please login again"
76 | res, _ := json.Marshal(status)
77 | _, _ = w.Write(res)
78 | return
79 | }
80 | r = r.Clone(context.WithValue(r.Context(), myToken.CurrentUserId("CurrentUserId"), claims.UserId))
81 |
82 | next(w, r)
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/service/api/internal/middleware/isloginMiddleware.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "context"
5 | myToken "douyin/common/help/token"
6 | "douyin/common/xerr"
7 | "douyin/service/api/internal/types"
8 | "encoding/json"
9 | "net/http"
10 | "time"
11 | )
12 |
13 | type IsLoginMiddleware struct {
14 | }
15 |
16 | func NewIsLoginMiddleware() *IsLoginMiddleware {
17 | return &IsLoginMiddleware{}
18 | }
19 |
20 | // Handle 没有登录也是可以调用feed接口的
21 | func (m *IsLoginMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
22 | return func(w http.ResponseWriter, r *http.Request) {
23 | status := new(types.Status)
24 | token := r.FormValue("token")
25 | if token != "" {
26 | // 解析token 判断是否有效
27 | var parseClaims myToken.ParseToken
28 | claims, err := parseClaims.ParseToken(token)
29 | if err != nil {
30 | status.Code = xerr.REUQEST_PARAM_ERROR
31 | status.Msg = "param error " + err.Error()
32 | res, _ := json.Marshal(status)
33 | _, _ = w.Write(res)
34 | return
35 | }
36 |
37 | // 过期时间点 小于当前时间 表示过期
38 | if claims.ExpireAt < time.Now().Unix() {
39 | status.Code = xerr.REUQEST_PARAM_ERROR
40 | status.Msg = "please login again"
41 | res, _ := json.Marshal(status)
42 | _, _ = w.Write(res)
43 | return
44 | }
45 |
46 | r = r.Clone(context.WithValue(r.Context(), myToken.CurrentUserId("LoginUserId"), claims.UserId))
47 |
48 | // 把r传递给下一个handler
49 | next(w, r)
50 | } else {
51 | next(w, r)
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/service/api/internal/svc/serviceContext.go:
--------------------------------------------------------------------------------
1 | package svc
2 |
3 | import (
4 | "douyin/service/api/internal/config"
5 | "douyin/service/api/internal/middleware"
6 | "douyin/service/rpc-user-info/userinfoservice"
7 | "douyin/service/rpc-user-operate/useroptservice"
8 | "douyin/service/rpc-video-service/videoservice"
9 | "github.com/zeromicro/go-queue/kq"
10 | "github.com/zeromicro/go-zero/rest"
11 | "github.com/zeromicro/go-zero/zrpc"
12 | )
13 |
14 | type ServiceContext struct {
15 | Config config.Config
16 |
17 | UserInfoRpcClient userinfoservice.UserInfoService
18 | VideoSvcRpcClient videoservice.VideoService
19 | UserOptSvcRpcClient useroptservice.UserOptService
20 |
21 | AuthJWT rest.Middleware
22 | IsLogin rest.Middleware
23 |
24 | FavoriteOptMsgProducer *kq.Pusher
25 | CommentOptMsgProducer *kq.Pusher
26 | FollowOptMsgProducer *kq.Pusher
27 | }
28 |
29 | func NewServiceContext(c config.Config) *ServiceContext {
30 | return &ServiceContext{
31 | Config: c,
32 |
33 | UserInfoRpcClient: userinfoservice.NewUserInfoService(zrpc.MustNewClient(c.UserInfoService)),
34 | VideoSvcRpcClient: videoservice.NewVideoService(zrpc.MustNewClient(c.VideoService)),
35 | UserOptSvcRpcClient: useroptservice.NewUserOptService(zrpc.MustNewClient(c.UserOptService)),
36 |
37 | AuthJWT: middleware.NewAuthJWTMiddleware().Handle,
38 | IsLogin: middleware.NewIsLoginMiddleware().Handle,
39 |
40 | FavoriteOptMsgProducer: kq.NewPusher(c.UserFavoriteOptServiceConf.Brokers, c.UserFavoriteOptServiceConf.Topic),
41 | CommentOptMsgProducer: kq.NewPusher(c.UserCommentOptServiceConf.Brokers, c.UserCommentOptServiceConf.Topic),
42 | FollowOptMsgProducer: kq.NewPusher(c.UserFollowOptServiceConf.Brokers, c.UserFollowOptServiceConf.Topic),
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/service/api/internal/types/types.go:
--------------------------------------------------------------------------------
1 | // Code generated by goctl. DO NOT EDIT.
2 | package types
3 |
4 | type FavoriteOptReq struct {
5 | Token string `form:"token"`
6 | VideoId int64 `form:"video_id"`
7 | ActionType int64 `form:"action_type"`
8 | }
9 |
10 | type FavoriteOptRes struct {
11 | Status
12 | }
13 |
14 | type FollowOptReq struct {
15 | Token string `form:"token"`
16 | FollowId int64 `form:"to_user_id"`
17 | ActionType int64 `form:"action_type"`
18 | }
19 |
20 | type FollowOptRes struct {
21 | Status
22 | }
23 |
24 | type CommentOptReq struct {
25 | Token string `form:"token"`
26 | VideoId int64 `form:"video_id"`
27 | ActionType int64 `form:"action_type"`
28 | CommentText string `form:"comment_text,omitempty, optional"`
29 | CommentId int64 `form:"comment_id,omitempty, optional"`
30 | }
31 |
32 | type Comment struct {
33 | CommentId int64 `json:"id" copyier:"id"`
34 | User User `json:"user"`
35 | Content string `json:"content"`
36 | CreateTime string `json:"create_date"`
37 | }
38 |
39 | type CommentOptRes struct {
40 | Status
41 | Comment *Comment `json:"comment,omitempty"`
42 | }
43 |
44 | type FavoriteListReq struct {
45 | IdWithTokenReq
46 | }
47 |
48 | type FavoriteListRes struct {
49 | Status
50 | FavoriteList []*PubVideo `json:"video_list,omitempty"`
51 | }
52 |
53 | type FollowListReq struct {
54 | IdWithTokenReq
55 | }
56 |
57 | type FollowListRes struct {
58 | Status
59 | UserFollowlist []*User `json:"user_list,omitempty"`
60 | }
61 |
62 | type FollowerListReq struct {
63 | IdWithTokenReq
64 | }
65 |
66 | type FollowerListRes struct {
67 | Status
68 | UserFollowerlist []*User `json:"user_list,omitempty"`
69 | }
70 |
71 | type CommentListReq struct {
72 | Token string `form:"token"`
73 | VideoId int64 `form:"video_id"`
74 | }
75 |
76 | type CommentListRes struct {
77 | Status
78 | CommentList []*Comment `json:"comment_list,omitempty"`
79 | }
80 |
81 | type UserReq struct {
82 | UserName string `form:"username"`
83 | Password string `form:"password"`
84 | }
85 |
86 | type IdWithTokenReq struct {
87 | UserId int64 `form:"user_id"`
88 | Token string `form:"token"`
89 | }
90 |
91 | type Status struct {
92 | Code int64 `json:"status_code"`
93 | Msg string `json:"status_msg,omitempty"`
94 | }
95 |
96 | type IdWithTokenRes struct {
97 | UserId int64 `json:"user_id,omitempty"`
98 | Token string `json:"token,omitempty"`
99 | }
100 |
101 | type UserRegisterReq struct {
102 | UserReq
103 | }
104 |
105 | type UserRegisterRes struct {
106 | Status
107 | IdWithTokenRes
108 | }
109 |
110 | type UserLoginReq struct {
111 | UserReq
112 | }
113 |
114 | type UserLoginRes struct {
115 | Status
116 | IdWithTokenRes
117 | }
118 |
119 | type User struct {
120 | UserId int64 `json:"id"`
121 | UserName string `json:"name"`
122 | FollowCount int64 `json:"follow_count"`
123 | FollowerCount int64 `json:"follower_count"`
124 | IsFollow bool `json:"is_follow"`
125 | }
126 |
127 | type UserInfoReq struct {
128 | IdWithTokenReq
129 | }
130 |
131 | type UserInfoRes struct {
132 | Status
133 | User *User `json:"user,omitempty"`
134 | }
135 |
136 | type PubVideoReq struct {
137 | Token string `form:"token"`
138 | Title string `form:"title"`
139 | }
140 |
141 | type PubVideoRes struct {
142 | Status
143 | }
144 |
145 | type GetPubVideoListReq struct {
146 | Token string `form:"token"`
147 | UserId int64 `form:"user_id"`
148 | }
149 |
150 | type PubVideo struct {
151 | Id int64 `json:"id"`
152 | Author User `json:"author"`
153 | PlayURL string `json:"play_url"`
154 | CoverURL string `json:"cover_url"`
155 | FavoriteCount int `json:"favorite_count"`
156 | CommentCount int `json:"comment_count"`
157 | IsFavorite bool `json:"is_favorite"`
158 | Title string `json:"title"`
159 | }
160 |
161 | type GetPubVideoListRes struct {
162 | Status
163 | VideoPubList []*PubVideo `json:"video_list,omitempty"`
164 | }
165 |
166 | type FeedVideoListReq struct {
167 | Token string `form:"token,optional"`
168 | LastTime int64 `form:"last_time,optional"`
169 | }
170 |
171 | type FeedVideoListRes struct {
172 | Status
173 | NextTime int64 `json:"next_time,omitempty"`
174 | VideoList []*PubVideo `json:"video_list,omitempty"`
175 | }
176 |
--------------------------------------------------------------------------------
/service/api/user.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 |
7 | "douyin/service/api/internal/config"
8 | "douyin/service/api/internal/handler"
9 | "douyin/service/api/internal/svc"
10 |
11 | "github.com/zeromicro/go-zero/core/conf"
12 | "github.com/zeromicro/go-zero/rest"
13 | )
14 |
15 | var configFile = flag.String("f", "etc/user-api.yaml", "the config file")
16 |
17 | func main() {
18 | flag.Parse()
19 |
20 | var c config.Config
21 | conf.MustLoad(*configFile, &c)
22 |
23 | ctx := svc.NewServiceContext(c)
24 | server := rest.MustNewServer(c.RestConf)
25 | defer server.Stop()
26 |
27 | handler.RegisterHandlers(server, ctx)
28 |
29 | fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
30 | server.Start()
31 | }
32 |
--------------------------------------------------------------------------------
/service/asynqJob/README.md:
--------------------------------------------------------------------------------
1 | 文件夹下分为client与server
2 |
3 | client用来定义调度时间
4 |
5 | server是到了时间接受client的消息触发来执行我们写的业务的
6 |
7 | 实际业务我们应该写在server,client用来定义业务调度时间的
--------------------------------------------------------------------------------
/service/asynqJob/client-scheduler/etc/scheduler.yaml:
--------------------------------------------------------------------------------
1 | Name: client-scheduler
2 |
3 | Mode: dev
4 |
5 | Log:
6 | ServiceName: client-scheduler
7 | Encoding: plain
8 |
9 | Redis:
10 | Host: localhost:6379
11 | Type: node
12 | Pass: password
13 |
--------------------------------------------------------------------------------
/service/asynqJob/client-scheduler/internal/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "github.com/zeromicro/go-zero/core/service"
5 | "github.com/zeromicro/go-zero/core/stores/redis"
6 | )
7 |
8 | type Config struct {
9 | service.ServiceConf
10 |
11 | Redis redis.RedisConf
12 | }
13 |
--------------------------------------------------------------------------------
/service/asynqJob/client-scheduler/internal/logic/register.go:
--------------------------------------------------------------------------------
1 | package logic
2 |
3 | import (
4 | "context"
5 | "douyin/service/asynqJob/client-scheduler/internal/svc"
6 | )
7 |
8 | type MqueueScheduler struct {
9 | ctx context.Context
10 | svcCtx *svc.ServiceContext
11 | }
12 |
13 | func NewCronScheduler(ctx context.Context, svcCtx *svc.ServiceContext) *MqueueScheduler {
14 | return &MqueueScheduler{
15 | ctx: ctx,
16 | svcCtx: svcCtx,
17 | }
18 | }
19 |
20 | func (l *MqueueScheduler) Register() {
21 |
22 | l.GetUserFavoriteStatusScheduler()
23 |
24 | l.GetUserFollowStatusScheduler()
25 | }
26 |
--------------------------------------------------------------------------------
/service/asynqJob/client-scheduler/internal/logic/setScheduler.go:
--------------------------------------------------------------------------------
1 | package logic
2 |
3 | import (
4 | "douyin/service/asynqJob/server/jobtype"
5 | "github.com/hibiken/asynq"
6 | "github.com/zeromicro/go-zero/core/logx"
7 | )
8 |
9 | // GetUserFavoriteStatusScheduler 向Redis发送定时消息调用worker进行工作
10 | func (l *MqueueScheduler) GetUserFavoriteStatusScheduler() {
11 |
12 | task := asynq.NewTask(jobtype.ScheduleGetUserFavoriteStatus, nil)
13 | // every one minute exec
14 | entryID, err := l.svcCtx.Scheduler.Register("@every 10s", task)
15 | if err != nil {
16 | logx.WithContext(l.ctx).Errorf("!!!MqueueSchedulerErr!!! ====> 【ScheduleGetUserFavoriteStatus】 registered err:%+v , task:%+v", err, task)
17 | }
18 | logx.Infof("【ScheduleGetUserFavoriteStatus】 registered an entry: %q \n", entryID)
19 | }
20 |
21 | func (l *MqueueScheduler) GetUserFollowStatusScheduler() {
22 |
23 | task := asynq.NewTask(jobtype.ScheduleGetUserFollowStatus, nil)
24 | // every one minute exec
25 | entryID, err := l.svcCtx.Scheduler.Register("@every 10s", task)
26 | if err != nil {
27 | logx.WithContext(l.ctx).Errorf("!!!MqueueSchedulerErr!!! ====> 【ScheduleGetUserFollowStatus】 registered err:%+v , task:%+v", err, task)
28 | }
29 | logx.Infof("【ScheduleGetUserFollowStatus】 registered an entry: %q \n", entryID)
30 | }
31 |
--------------------------------------------------------------------------------
/service/asynqJob/client-scheduler/internal/svc/scheduler.go:
--------------------------------------------------------------------------------
1 | package svc
2 |
3 | import (
4 | "douyin/service/asynqJob/client-scheduler/internal/config"
5 | "github.com/hibiken/asynq"
6 | "github.com/zeromicro/go-zero/core/logx"
7 | "time"
8 | )
9 |
10 | // create client-scheduler
11 | func newScheduler(c config.Config) *asynq.Scheduler {
12 |
13 | location, _ := time.LoadLocation("Asia/Shanghai")
14 | return asynq.NewScheduler(
15 | asynq.RedisClientOpt{
16 | Addr: c.Redis.Host,
17 | Password: c.Redis.Pass,
18 | }, &asynq.SchedulerOpts{
19 | Location: location,
20 | EnqueueErrorHandler: func(task *asynq.Task, opts []asynq.Option, err error) {
21 | logx.Infof("Scheduler EnqueueErrorHandler <<<<<<<===>>>>> err : %+v , task : %+v", err, task)
22 | },
23 | })
24 | }
25 |
--------------------------------------------------------------------------------
/service/asynqJob/client-scheduler/internal/svc/serviceContext.go:
--------------------------------------------------------------------------------
1 | package svc
2 |
3 | import (
4 | "douyin/service/asynqJob/client-scheduler/internal/config"
5 | "github.com/hibiken/asynq"
6 | )
7 |
8 | type ServiceContext struct {
9 | Config config.Config
10 |
11 | Scheduler *asynq.Scheduler
12 | }
13 |
14 | func NewServiceContext(c config.Config) *ServiceContext {
15 | return &ServiceContext{
16 | Config: c,
17 | Scheduler: newScheduler(c),
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/service/asynqJob/client-scheduler/scheduler.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "douyin/service/asynqJob/client-scheduler/internal/config"
6 | "douyin/service/asynqJob/client-scheduler/internal/logic"
7 | "douyin/service/asynqJob/client-scheduler/internal/svc"
8 | "flag"
9 | "github.com/zeromicro/go-zero/core/logx"
10 | "os"
11 |
12 | "github.com/zeromicro/go-zero/core/conf"
13 | )
14 |
15 | var configFile = flag.String("f", "etc/scheduler.yaml", "Specify the config file")
16 |
17 | func main() {
18 | flag.Parse()
19 | var c config.Config
20 |
21 | conf.MustLoad(*configFile, &c)
22 |
23 | logx.DisableStat()
24 | // log、prometheus、trace、metricsUrl.
25 | if err := c.SetUp(); err != nil {
26 | panic(err)
27 | }
28 |
29 | svcContext := svc.NewServiceContext(c)
30 | ctx := context.Background()
31 | mqueueScheduler := logic.NewCronScheduler(ctx, svcContext)
32 | mqueueScheduler.Register()
33 |
34 | if err := svcContext.Scheduler.Run(); err != nil {
35 | logx.Errorf("!!!MqueueSchedulerErr!!! run err:%+v", err)
36 | os.Exit(1)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/service/asynqJob/server/etc/server.yaml:
--------------------------------------------------------------------------------
1 | Name: asynq-server
2 | Mode: dev
3 | Log:
4 | ServiceName: asynq-server
5 | Encoding: plain
6 |
7 | Redis:
8 | Host: localhost:6379
9 | Type: node
10 | Pass: password
11 |
12 | UserOptServiceConf:
13 | Etcd:
14 | Hosts:
15 | - 127.0.0.1:33333
16 | Key: useroptservice.rpc
17 | NonBlock: true
18 |
19 | RedisCacheConf:
20 | Host: localhost:6379
21 | Pass: password
--------------------------------------------------------------------------------
/service/asynqJob/server/internal/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "github.com/zeromicro/go-zero/core/service"
5 | "github.com/zeromicro/go-zero/core/stores/redis"
6 | "github.com/zeromicro/go-zero/zrpc"
7 | )
8 |
9 | type Config struct {
10 | service.ServiceConf
11 | Redis redis.RedisConf
12 |
13 | RedisCacheConf redis.RedisConf
14 |
15 | UserOptServiceConf zrpc.RpcClientConf
16 | }
17 |
--------------------------------------------------------------------------------
/service/asynqJob/server/internal/logic/jobs/GetUserFavoriteStatus.go:
--------------------------------------------------------------------------------
1 | package jobs
2 |
3 | import (
4 | "context"
5 | "douyin/common/globalkey"
6 | "douyin/service/asynqJob/server/internal/svc"
7 | "douyin/service/rpc-user-operate/userOptPb"
8 | "github.com/hibiken/asynq"
9 | "github.com/zeromicro/go-zero/core/logx"
10 | "github.com/zeromicro/go-zero/core/mr"
11 | "strconv"
12 | "strings"
13 | )
14 |
15 | type GetUserFavoriteStatusHandler struct {
16 | svcCtx *svc.ServiceContext
17 | }
18 |
19 | func NewGetUserFavoriteStatusHandler(svcCtx *svc.ServiceContext) *GetUserFavoriteStatusHandler {
20 | return &GetUserFavoriteStatusHandler{
21 | svcCtx: svcCtx,
22 | }
23 | }
24 |
25 | // ProcessTask every one minute exec : if return err != nil , asynq will retry
26 | func (l *GetUserFavoriteStatusHandler) ProcessTask(ctx context.Context, _ *asynq.Task) error {
27 |
28 | vals, err := l.svcCtx.RedisCache.SmembersCtx(ctx, globalkey.FavoriteSetKey)
29 | if err != nil {
30 | logx.Errorf("RedisCache.SmembersCtx error -----> %s", err.Error())
31 | return err
32 | }
33 | if len(vals) == 0 {
34 | logx.Infof("every 10s exec But not exist data in redis cache")
35 | return nil
36 | }
37 |
38 | // 持久化数据
39 | mr.ForEach(func(source chan<- interface{}) {
40 | for _, videoIdKey := range vals {
41 | source <- videoIdKey
42 | }
43 | }, func(item interface{}) {
44 | videoIdKey := item.(string)
45 |
46 | usersInfoTemp, err := l.svcCtx.RedisCache.EvalShaCtx(ctx, l.svcCtx.ScriptREMTag, []string{videoIdKey})
47 |
48 | if err != nil { // 获取赞了这个视频的所有的用户Id
49 | logx.Errorf("RedisCache.SmembersCtx error -----> %s", err.Error())
50 | return
51 | }
52 |
53 | // 切分出视频的Id
54 | _, videoIdStr, _ := strings.Cut(videoIdKey, ":")
55 | videoId, _ := strconv.ParseInt(videoIdStr, 10, 64)
56 |
57 | var usersInfo []interface{}
58 | usersInfo = usersInfoTemp.([]interface{})
59 |
60 | mr.ForEach(func(source chan<- interface{}) {
61 | for _, userId := range usersInfo {
62 | source <- userId
63 | }
64 | }, func(item interface{}) {
65 | // 切分出用户的Id 和 点赞状态 "%d:%d" 0 是未写入数据库 第二个是user_id 第三个是操作 点赞与未点赞
66 | members := strings.Split(item.(string), ":")
67 | userid, _ := strconv.ParseInt(members[0], 10, 64)
68 | actType, _ := strconv.ParseInt(members[1], 10, 64)
69 | _, _ = l.svcCtx.UserOptSvcRpcClient.UpdateFavoriteStatus(ctx, &userOptPb.UpdateFavoriteStatusReq{
70 | VideoId: videoId,
71 | UserId: userid,
72 | ActionType: actType,
73 | })
74 | })
75 | }, mr.WithWorkers(10))
76 |
77 | return nil
78 | }
79 |
--------------------------------------------------------------------------------
/service/asynqJob/server/internal/logic/jobs/GetUserFollowStatus.go:
--------------------------------------------------------------------------------
1 | package jobs
2 |
3 | import (
4 | "context"
5 | "douyin/common/globalkey"
6 | "douyin/service/asynqJob/server/internal/svc"
7 | "douyin/service/rpc-user-operate/userOptPb"
8 | "github.com/hibiken/asynq"
9 | "github.com/zeromicro/go-zero/core/logx"
10 | "github.com/zeromicro/go-zero/core/mr"
11 | "strconv"
12 | "strings"
13 | )
14 |
15 | type GetUserFollowStatusHandler struct {
16 | svcCtx *svc.ServiceContext
17 | }
18 |
19 | func NewGetUserFollowStatusHandler(svcCtx *svc.ServiceContext) *GetUserFollowStatusHandler {
20 | return &GetUserFollowStatusHandler{
21 | svcCtx: svcCtx,
22 | }
23 | }
24 |
25 | // ProcessTask if return err != nil , asynq will retry
26 | func (l *GetUserFollowStatusHandler) ProcessTask(ctx context.Context, _ *asynq.Task) error {
27 |
28 | vals, err := l.svcCtx.RedisCache.SmembersCtx(ctx, globalkey.FollowSetKey)
29 | if err != nil {
30 | logx.Errorf("RedisCache.SmembersCtx error -----> %s", err.Error())
31 | return err
32 | }
33 | if len(vals) == 0 {
34 | logx.Infof("every 10s exec But not exist data in redis cache")
35 | return nil
36 | }
37 |
38 | // 持久化数据
39 | mr.ForEach(func(source chan<- interface{}) {
40 | for _, followKey := range vals {
41 | source <- followKey
42 | }
43 | }, func(item interface{}) {
44 | followIdKey := item.(string)
45 | usersInfoTemp, err := l.svcCtx.RedisCache.EvalShaCtx(ctx, l.svcCtx.ScriptREMTag, []string{followIdKey})
46 |
47 | if err != nil { // 获取赞了这个视频的所有的用户Id
48 | logx.Errorf("RedisCache.SmembersCtx error -----> %s", err.Error())
49 | return
50 | }
51 |
52 | // 切分出视频的Id
53 | _, followIdStr, _ := strings.Cut(followIdKey, ":")
54 | followId, _ := strconv.ParseInt(followIdStr, 10, 64)
55 |
56 | var usersInfo []interface{}
57 | usersInfo = usersInfoTemp.([]interface{})
58 |
59 | mr.ForEach(func(source chan<- interface{}) {
60 | for _, userId := range usersInfo {
61 | source <- userId
62 | }
63 | }, func(item interface{}) {
64 | // 切分出用户的Id 和 关注状态 "%d:%d" 0 是未写入数据库 第二个是user_id 第三个是操作 关注与未关注
65 | members := strings.Split(item.(string), ":")
66 | userid, _ := strconv.ParseInt(members[0], 10, 64)
67 | actType, _ := strconv.ParseInt(members[1], 10, 64)
68 |
69 | _, err = l.svcCtx.UserOptSvcRpcClient.UpdateFollowStatus(ctx, &userOptPb.UpdateFollowStatusReq{
70 | FollowId: followId,
71 | UserId: userid,
72 | ActionType: actType,
73 | })
74 | if err != nil {
75 | logx.Errorf("UserOptSvcRpcClient.UpdateFollowStatus error -----> %s", err.Error())
76 | return
77 | }
78 | })
79 | }, mr.WithWorkers(10))
80 |
81 | return nil
82 | }
83 |
--------------------------------------------------------------------------------
/service/asynqJob/server/internal/logic/routes.go:
--------------------------------------------------------------------------------
1 | package logic
2 |
3 | import (
4 | "context"
5 | "douyin/service/asynqJob/server/internal/logic/jobs"
6 | "douyin/service/asynqJob/server/internal/svc"
7 | "douyin/service/asynqJob/server/jobtype"
8 | "github.com/hibiken/asynq"
9 | )
10 |
11 | type CronJob struct {
12 | ctx context.Context
13 | svcCtx *svc.ServiceContext
14 | }
15 |
16 | func NewCronJob(ctx context.Context, svcCtx *svc.ServiceContext) *CronJob {
17 | return &CronJob{
18 | ctx: ctx,
19 | svcCtx: svcCtx,
20 | }
21 | }
22 |
23 | // register server
24 | func (l *CronJob) Register() *asynq.ServeMux {
25 |
26 | mux := asynq.NewServeMux()
27 |
28 | // handle
29 | mux.Handle(jobtype.ScheduleGetUserFavoriteStatus, jobs.NewGetUserFavoriteStatusHandler(l.svcCtx))
30 | mux.Handle(jobtype.ScheduleGetUserFollowStatus, jobs.NewGetUserFollowStatusHandler(l.svcCtx))
31 |
32 | return mux
33 | }
34 |
--------------------------------------------------------------------------------
/service/asynqJob/server/internal/svc/asynqServer.go:
--------------------------------------------------------------------------------
1 | package svc
2 |
3 | import (
4 | "douyin/service/asynqJob/server/internal/config"
5 | "github.com/hibiken/asynq"
6 | "github.com/zeromicro/go-zero/core/logx"
7 | )
8 |
9 | func newAsynqServer(c config.Config) *asynq.Server {
10 |
11 | return asynq.NewServer(
12 | asynq.RedisClientOpt{Addr: c.Redis.Host, Password: c.Redis.Pass},
13 | asynq.Config{
14 | IsFailure: func(err error) bool {
15 | logx.Infof("asynq server exec task IsFailure ======== >>>>>>>>>>> err : %s", err.Error())
16 | return true
17 | },
18 | Concurrency: 10, //max concurrent process server task num
19 | },
20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/service/asynqJob/server/internal/svc/serviceContext.go:
--------------------------------------------------------------------------------
1 | package svc
2 |
3 | import (
4 | "douyin/service/asynqJob/server/internal/config"
5 | "douyin/service/rpc-user-operate/useroptservice"
6 | "github.com/hibiken/asynq"
7 | "github.com/zeromicro/go-zero/core/stores/redis"
8 | "github.com/zeromicro/go-zero/zrpc"
9 | )
10 |
11 | type ServiceContext struct {
12 | Config config.Config
13 | AsynqServer *asynq.Server
14 | RedisCache *redis.Redis
15 |
16 | UserOptSvcRpcClient useroptservice.UserOptService
17 |
18 | ScriptREMTag string
19 | }
20 |
21 | const scriptLoadREM = "local arr = redis.call('SMEMBERS', KEYS[1]) for i=1, #arr do redis.call('SREM', KEYS[1], arr[i] ) end return arr"
22 |
23 | func NewServiceContext(c config.Config) *ServiceContext {
24 | ServiceContext := &ServiceContext{
25 | Config: c,
26 | AsynqServer: newAsynqServer(c),
27 | RedisCache: c.RedisCacheConf.NewRedis(),
28 |
29 | UserOptSvcRpcClient: useroptservice.NewUserOptService(zrpc.MustNewClient(c.UserOptServiceConf)),
30 | }
31 |
32 | ServiceContext.ScriptREMTag, _ = ServiceContext.RedisCache.ScriptLoad(scriptLoadREM)
33 |
34 | return ServiceContext
35 | }
36 |
--------------------------------------------------------------------------------
/service/asynqJob/server/jobtype/jobtype.go:
--------------------------------------------------------------------------------
1 | package jobtype
2 |
3 | // ScheduleGetUserFavoriteStatus 结算给商家的结算记录
4 | const ScheduleGetUserFavoriteStatus = "schedule:get_user_favorite_status"
5 |
6 | const ScheduleGetUserFollowStatus = "schedule:get_user_follow_status"
7 |
--------------------------------------------------------------------------------
/service/asynqJob/server/server.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "douyin/service/asynqJob/server/internal/config"
6 | "douyin/service/asynqJob/server/internal/logic"
7 | "douyin/service/asynqJob/server/internal/svc"
8 | "flag"
9 | "github.com/zeromicro/go-zero/core/logx"
10 | "os"
11 |
12 | "github.com/zeromicro/go-zero/core/conf"
13 | )
14 |
15 | var configFile = flag.String("f", "etc/server.yaml", "Specify the config file")
16 |
17 | func main() {
18 | flag.Parse()
19 | var c config.Config
20 |
21 | conf.MustLoad(*configFile, &c)
22 |
23 | // log、prometheus、trace、metricsUrl
24 | if err := c.SetUp(); err != nil {
25 | panic(err)
26 | }
27 |
28 | //logx.DisableStat()
29 |
30 | svcContext := svc.NewServiceContext(c)
31 | ctx := context.Background()
32 | cronJob := logic.NewCronJob(ctx, svcContext)
33 | mux := cronJob.Register()
34 |
35 | if err := svcContext.AsynqServer.Run(mux); err != nil {
36 | logx.WithContext(ctx).Errorf("!!!CronJobErr!!! run err:%+v", err)
37 | os.Exit(1)
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/service/mq/etc/mq.yaml:
--------------------------------------------------------------------------------
1 | Name: user-opt-mq
2 | Log:
3 | Encoding: plain
4 | Mode: dev
5 |
6 | UserOptServiceConf:
7 | Etcd:
8 | Hosts:
9 | - 127.0.0.1:33333
10 | Key: useroptservice.rpc
11 | NonBlock: true
12 |
13 |
14 | VideoService:
15 | Etcd:
16 | Hosts:
17 | - 127.0.0.1:33333
18 | Key: videoservice.rpc
19 | NonBlock: true
20 |
21 |
22 | #kq
23 | UserFavoriteOptServiceConf:
24 | Name: kq
25 | Brokers:
26 | - 127.0.0.1:9092
27 | Group: UserFavoriteOptService-group
28 | Topic: UserFavoriteOptService-topic
29 | Offset: first
30 | Consumers: 1
31 | Processors: 1
32 |
33 | UserCommentOptServiceConf:
34 | Name: UserCommentOptService
35 | Brokers:
36 | - 127.0.0.1:9092
37 | Group: UserCommentOptService-group
38 | Topic: UserCommentOptService-topic
39 | Offset: first
40 | Consumers: 1
41 | Processors: 1
42 |
43 | UserFollowOptServiceConf:
44 | Name: UserFollowOptService
45 | Brokers:
46 | - 127.0.0.1:9092
47 | Group: UserFollowOptService-group
48 | Topic: UserFollowOptService-topic
49 | Offset: first
50 | Consumers: 1
51 | Processors: 1
52 |
53 | RedisCacheConf:
54 | Host: localhost:6379
55 | Pass: password
56 |
57 | COSConf:
58 | SecretId: SecretId
59 | SecretKey: SecretKey
60 | CommentBucket: https://xxxx.cos.ap-guangzhou.myqcloud.com
--------------------------------------------------------------------------------
/service/mq/internal/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "github.com/zeromicro/go-queue/kq"
5 | "github.com/zeromicro/go-zero/core/service"
6 | "github.com/zeromicro/go-zero/core/stores/redis"
7 | "github.com/zeromicro/go-zero/zrpc"
8 | )
9 |
10 | type Config struct {
11 | service.ServiceConf
12 |
13 | // kq : pub sub
14 | // 点赞 评论 关注
15 | UserFavoriteOptServiceConf kq.KqConf
16 | UserCommentOptServiceConf kq.KqConf
17 | UserFollowOptServiceConf kq.KqConf
18 |
19 | // 操作rpc
20 | UserOptServiceConf zrpc.RpcClientConf
21 | VideoService zrpc.RpcClientConf
22 |
23 | RedisCacheConf redis.RedisConf
24 |
25 | COSConf struct {
26 | SecretId string
27 | SecretKey string
28 | CommentBucket string
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/service/mq/internal/listen/kqMqs.go:
--------------------------------------------------------------------------------
1 | package listen
2 |
3 | import (
4 | "context"
5 | "douyin/service/mq/internal/config"
6 | "douyin/service/mq/internal/svc"
7 |
8 | kqMq "douyin/service/mq/internal/mqs/kq"
9 |
10 | "github.com/zeromicro/go-queue/kq"
11 | "github.com/zeromicro/go-zero/core/service"
12 | )
13 |
14 | // KqMqs pub sub use kq (kafka)
15 | func KqMqs(c config.Config, ctx context.Context, svcContext *svc.ServiceContext) []service.Service {
16 |
17 | return []service.Service{
18 | //Listening for changes in consumption flow status
19 | kq.MustNewQueue(c.UserFavoriteOptServiceConf, kqMq.NewUserFavoriteUpdateMq(ctx, svcContext)),
20 | kq.MustNewQueue(c.UserFollowOptServiceConf, kqMq.NewUserFollowUpdateMq(ctx, svcContext)),
21 | kq.MustNewQueue(c.UserCommentOptServiceConf, kqMq.NewUserCommentUpdateMq(ctx, svcContext)),
22 | // 配置
23 | //.....
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/service/mq/internal/listen/listen.go:
--------------------------------------------------------------------------------
1 | package listen
2 |
3 | import (
4 | "context"
5 | "douyin/service/mq/internal/config"
6 | "douyin/service/mq/internal/svc"
7 | "github.com/zeromicro/go-zero/core/logx"
8 |
9 | "github.com/zeromicro/go-zero/core/service"
10 | )
11 |
12 | const scriptLoadSADD = "redis.call('SADD', KEYS[1], ARGV[1]) redis.call('SADD', KEYS[2], ARGV[2]) redis.call('EXPIRE', KEYS[1], 60) redis.call('EXPIRE', KEYS[2], 60)"
13 |
14 | // Mqs back to all consumers
15 | func Mqs(c config.Config) []service.Service {
16 |
17 | svcContext := svc.NewServiceContext(c)
18 | ctx := context.Background()
19 |
20 | // 加载脚本
21 | tmp, err := svcContext.RedisCache.ScriptLoadCtx(ctx, scriptLoadSADD)
22 | if err != nil {
23 | logx.Errorf("load script err:%+v", err)
24 | return nil
25 | }
26 | svcContext.ScriptADD = tmp
27 |
28 | var services []service.Service
29 |
30 | //kq :pub sub
31 | services = append(services, KqMqs(c, ctx, svcContext)...)
32 |
33 | return services
34 | }
35 |
--------------------------------------------------------------------------------
/service/mq/internal/mqs/kq/userCommentUpdate.go:
--------------------------------------------------------------------------------
1 | package kq
2 |
3 | import (
4 | "context"
5 | "douyin/common/help/cos"
6 | "douyin/common/messageTypes"
7 | "douyin/service/mq/internal/svc"
8 | "douyin/service/rpc-user-operate/model"
9 | "douyin/service/rpc-user-operate/useroptservice"
10 | "encoding/json"
11 | "github.com/jinzhu/copier"
12 | "github.com/pkg/errors"
13 | "github.com/zeromicro/go-zero/core/logx"
14 | )
15 |
16 | /*
17 | Listening to the payment flow status change notification message queue
18 | */
19 | type UserCommentOpt struct {
20 | ctx context.Context
21 | svcCtx *svc.ServiceContext
22 |
23 | CommentOpt cos.UpdateComment
24 | }
25 |
26 | func NewUserCommentUpdateMq(ctx context.Context, svcCtx *svc.ServiceContext) *UserCommentOpt {
27 | return &UserCommentOpt{
28 | ctx: ctx,
29 | svcCtx: svcCtx,
30 | CommentOpt: cos.UpdateComment{},
31 | }
32 | }
33 |
34 | func (l *UserCommentOpt) Consume(_, val string) error {
35 | var message messageTypes.UserCommentOptMessage
36 | if err := json.Unmarshal([]byte(val), &message); err != nil {
37 | logx.WithContext(l.ctx).Error("UserCommentOptMessage->Consume Unmarshal err : %v , val : %s", err, val)
38 | return err
39 | }
40 |
41 | if err := l.execService(message); err != nil {
42 | logx.WithContext(l.ctx).Error("UserCommentOptMessage->execService err : %v , val : %s , message:%+v", err, val, message)
43 | return err
44 | }
45 |
46 | return nil
47 | }
48 |
49 | // 处理逻辑
50 | func (l *UserCommentOpt) execService(message messageTypes.UserCommentOptMessage) error {
51 |
52 | actionType, err := l.getActionType(message.ActionType, message)
53 | if actionType == -99 || err != nil {
54 | return errors.Wrap(err, "UserCommentOptMessage->execService getActionType err")
55 | }
56 |
57 | // 调用rpc 更新user_comment表
58 | _, err = l.svcCtx.UserOptSvcRpcClient.UpdateCommentStatus(l.ctx, &useroptservice.UpdateCommentStatusReq{
59 | VideoId: message.VideoId,
60 | UserId: message.UserId,
61 | CommentId: message.CommentId,
62 | ActionType: actionType,
63 | })
64 |
65 | logx.Error("UserCommentOptMessage->execService xxxxxxxxxxx")
66 |
67 | if err != nil {
68 | logx.Errorf("UserCommentOptMessage->execService err : %v , val : %s , message:%+v", err, message)
69 | return err
70 | }
71 |
72 | return nil
73 | }
74 |
75 | func (l *UserCommentOpt) getActionType(actionType int64, message messageTypes.UserCommentOptMessage) (int64, error) {
76 |
77 | _ = copier.Copy(&l.CommentOpt, &message)
78 |
79 | switch actionType { // 方便扩展
80 | case model.ActionADD:
81 | // 新增评论
82 | _, err := l.CommentOpt.UploadComment(l.ctx, l.svcCtx.Config.COSConf.CommentBucket,
83 | l.svcCtx.Config.COSConf.SecretId, l.svcCtx.Config.COSConf.SecretKey)
84 |
85 | if err != nil {
86 | logx.Errorf("UserCommentOptMessage->getActionType err : %v , val : %s , message:%+v", err, message)
87 | return 0, err
88 | }
89 |
90 | return model.ActionADD, nil
91 |
92 | case model.ActionCancel:
93 | // 取消评论
94 | _, err := l.CommentOpt.DeleteComment(l.ctx, l.svcCtx.Config.COSConf.CommentBucket,
95 | l.svcCtx.Config.COSConf.SecretId, l.svcCtx.Config.COSConf.SecretKey)
96 |
97 | if err != nil {
98 | logx.Errorf("UserCommentOptMessage->getActionType err : %v , val : %s , message:%+v", err, message)
99 | return 0, err
100 | }
101 |
102 | return model.ActionCancel, nil
103 |
104 | default:
105 | return -99, errors.New("UserCommentOptMessage->execService ActionType err")
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/service/mq/internal/mqs/kq/userFavoriteUpdate.go:
--------------------------------------------------------------------------------
1 | package kq
2 |
3 | import (
4 | "context"
5 | "douyin/common/globalkey"
6 | "douyin/common/messageTypes"
7 | "douyin/service/mq/internal/svc"
8 | "douyin/service/rpc-user-operate/userOptPb"
9 | "encoding/json"
10 | "fmt"
11 | "github.com/jinzhu/copier"
12 | "github.com/zeromicro/go-zero/core/logx"
13 | "github.com/zeromicro/go-zero/core/stores/redis"
14 | )
15 |
16 | /*
17 | Listening to the payment flow status change notification message queue
18 | */
19 | type UserFavoriteOpt struct {
20 | ctx context.Context
21 | svcCtx *svc.ServiceContext
22 | }
23 |
24 | func NewUserFavoriteUpdateMq(ctx context.Context, svcCtx *svc.ServiceContext) *UserFavoriteOpt {
25 | return &UserFavoriteOpt{
26 | ctx: ctx,
27 | svcCtx: svcCtx,
28 | }
29 | }
30 |
31 | func (l *UserFavoriteOpt) Consume(_, val string) error {
32 | var message messageTypes.UserFavoriteOptMessage
33 |
34 | if err := json.Unmarshal([]byte(val), &message); err != nil {
35 | logx.WithContext(l.ctx).Error("UserFavoriteOptMessage->Consume Unmarshal err : %v , val : %s", err, val)
36 | return err
37 | }
38 |
39 | if err := l.execService(message); err != nil {
40 | logx.WithContext(l.ctx).Error("UserFavoriteOptMessage->execService err : %v , val : %s , message:%+v", err, val, message)
41 | logx.Errorf("UserFavoriteOptMessage->execService err : %v , val : %s , message:%+v", err, val, message)
42 | return err
43 | }
44 | return nil
45 | }
46 |
47 | // 处理逻辑
48 | func (l *UserFavoriteOpt) execService(message messageTypes.UserFavoriteOptMessage) error {
49 |
50 | logx.Infof("UserFavoriteOptMessage message : %+v\n", message)
51 |
52 | var req userOptPb.UpdateFavoriteStatusReq
53 | _ = copier.Copy(&req, &message)
54 |
55 | // 构造redis的数据
56 | dataKey := fmt.Sprintf(globalkey.FavoriteSetValTpl, message.VideoId)
57 | favoriteSetVal := fmt.Sprintf(globalkey.FavoriteSetValTpl, message.VideoId)
58 | dataVal := fmt.Sprintf(globalkey.ExistDataValTpl, message.UserId, message.ActionType)
59 |
60 | // 消息取出来之后无非是点赞或者取消点赞 0,1,那么打到redis也是0,1
61 | _, err := l.svcCtx.RedisCache.EvalShaCtx(l.ctx, l.svcCtx.ScriptADD, []string{globalkey.FavoriteSetKey, dataKey}, []string{favoriteSetVal, dataVal})
62 | if err != redis.Nil {
63 | logx.Errorf("script exec err : %v", err)
64 | return err
65 | }
66 |
67 | return nil
68 | }
69 |
--------------------------------------------------------------------------------
/service/mq/internal/mqs/kq/userFollowUpdate.go:
--------------------------------------------------------------------------------
1 | package kq
2 |
3 | import (
4 | "context"
5 | "douyin/common/globalkey"
6 | "douyin/common/messageTypes"
7 | "douyin/service/mq/internal/svc"
8 | "douyin/service/rpc-user-operate/userOptPb"
9 | "encoding/json"
10 | "fmt"
11 | "github.com/jinzhu/copier"
12 | "github.com/zeromicro/go-zero/core/logx"
13 | "github.com/zeromicro/go-zero/core/stores/redis"
14 | )
15 |
16 | /*
17 | Listening to the payment flow status change notification message queue
18 | */
19 | type UserFollowOpt struct {
20 | ctx context.Context
21 | svcCtx *svc.ServiceContext
22 | }
23 |
24 | func NewUserFollowUpdateMq(ctx context.Context, svcCtx *svc.ServiceContext) *UserFollowOpt {
25 | return &UserFollowOpt{
26 | ctx: ctx,
27 | svcCtx: svcCtx,
28 | }
29 | }
30 |
31 | func (l *UserFollowOpt) Consume(_, val string) error {
32 | var message messageTypes.UserFollowOptMessage
33 | if err := json.Unmarshal([]byte(val), &message); err != nil {
34 | logx.WithContext(l.ctx).Error("UserFollowOptMessage->Consume Unmarshal err : %v , val : %s", err, val)
35 | return err
36 | }
37 |
38 | if err := l.execService(message); err != nil {
39 | logx.WithContext(l.ctx).Error("UserFollowOptMessage->execService err : %v , val : %s , message:%+v", err, val, message)
40 | return err
41 | }
42 |
43 | return nil
44 | }
45 |
46 | // 处理逻辑
47 | func (l *UserFollowOpt) execService(message messageTypes.UserFollowOptMessage) error {
48 |
49 | logx.Infof("UserFollowOptMessage message : %+v\n", message)
50 |
51 | var req userOptPb.UpdateFollowStatusReq
52 | _ = copier.Copy(&req, &message)
53 |
54 | // 构造redis的数据
55 | dataKey := fmt.Sprintf(globalkey.FollowSetValTpl, message.FollowId)
56 | followSetVal := fmt.Sprintf(globalkey.FollowSetValTpl, message.FollowId)
57 | dataVal := fmt.Sprintf(globalkey.ExistDataValTpl, message.UserId, message.ActionType)
58 |
59 | // 消息取出来之后无非是点赞或者取消点赞 0,1,那么打到redis也是0,1
60 | _, err := l.svcCtx.RedisCache.EvalShaCtx(l.ctx, l.svcCtx.ScriptADD, []string{globalkey.FollowSetKey, dataKey}, []string{followSetVal, dataVal})
61 | if err != redis.Nil {
62 | logx.Errorf("script exec err : %v", err)
63 | return err
64 | }
65 |
66 | return nil
67 | }
68 |
--------------------------------------------------------------------------------
/service/mq/internal/svc/serviceContext.go:
--------------------------------------------------------------------------------
1 | package svc
2 |
3 | import (
4 | "douyin/service/mq/internal/config"
5 | "douyin/service/rpc-user-operate/useroptservice"
6 | "douyin/service/rpc-video-service/videoservice"
7 | "github.com/zeromicro/go-zero/core/stores/redis"
8 | "github.com/zeromicro/go-zero/zrpc"
9 | )
10 |
11 | type ServiceContext struct {
12 | Config config.Config
13 |
14 | UserOptSvcRpcClient useroptservice.UserOptService
15 | VideoSvcRpcClient videoservice.VideoService
16 |
17 | RedisCache *redis.Redis
18 | ScriptADD string // 在Mqs中初始化
19 | }
20 |
21 | func NewServiceContext(c config.Config) *ServiceContext {
22 | return &ServiceContext{
23 | Config: c,
24 |
25 | UserOptSvcRpcClient: useroptservice.NewUserOptService(zrpc.MustNewClient(c.UserOptServiceConf)),
26 | VideoSvcRpcClient: videoservice.NewVideoService(zrpc.MustNewClient(c.VideoService)),
27 |
28 | RedisCache: c.RedisCacheConf.NewRedis(),
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/service/mq/mq.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "douyin/service/mq/internal/config"
5 | "douyin/service/mq/internal/listen"
6 | "flag"
7 | "fmt"
8 |
9 | "github.com/zeromicro/go-zero/core/conf"
10 | "github.com/zeromicro/go-zero/core/service"
11 | )
12 |
13 | var configFile = flag.String("f", "etc/mq.yaml", "Specify the config file")
14 |
15 | func main() {
16 | flag.Parse()
17 | var c config.Config
18 |
19 | conf.MustLoad(*configFile, &c)
20 |
21 | fmt.Printf("%+v\n", c.UserFavoriteOptServiceConf.Brokers)
22 |
23 | // log、prometheus、trace、metricsUrl.
24 | if err := c.SetUp(); err != nil {
25 | panic(err)
26 | }
27 |
28 | serviceGroup := service.NewServiceGroup()
29 | defer serviceGroup.Stop()
30 |
31 | for _, mq := range listen.Mqs(c) {
32 | serviceGroup.Add(mq)
33 | }
34 |
35 | serviceGroup.Start()
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/service/rpc-user-info/etc/userInfoService.yaml:
--------------------------------------------------------------------------------
1 | Name: userinfoservice.rpc
2 | ListenOn: 0.0.0.0:9997
3 | # Mode 使用 dev可以通过 grpcui -plaintext localhost:9999 进行调试
4 | Mode: dev
5 | #注册中心
6 | Etcd:
7 | Hosts:
8 | - 127.0.0.1:33333
9 | Key: userinfoservice.rpc
10 | Log:
11 | Encoding: plain
12 |
13 | DB:
14 | DataSource: username:password@tcp(localhost:3306)/douyin2?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
15 |
16 | CacheConf:
17 | - Host: localhost:6379
18 | Pass: password
19 |
20 | RedisCacheConf:
21 | Host: localhost:6379
22 | Pass: password
23 |
--------------------------------------------------------------------------------
/service/rpc-user-info/internal/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "github.com/zeromicro/go-zero/core/stores/cache"
5 | "github.com/zeromicro/go-zero/core/stores/redis"
6 | "github.com/zeromicro/go-zero/zrpc"
7 | )
8 |
9 | type Config struct {
10 | zrpc.RpcServerConf
11 | RedisCacheConf redis.RedisConf
12 | DB struct {
13 | DataSource string
14 | }
15 | CacheConf cache.CacheConf
16 | }
17 |
--------------------------------------------------------------------------------
/service/rpc-user-info/internal/logic/authsInfoLogic.go:
--------------------------------------------------------------------------------
1 | package logic
2 |
3 | import (
4 | "context"
5 | "douyin/common/xerr"
6 | "douyin/service/rpc-user-info/internal/svc"
7 | "douyin/service/rpc-user-info/userInfoPb"
8 | "github.com/Masterminds/squirrel"
9 | "github.com/jinzhu/copier"
10 | "github.com/pkg/errors"
11 | "github.com/zeromicro/go-zero/core/logx"
12 | )
13 |
14 | type AuthsInfoLogic struct {
15 | ctx context.Context
16 | svcCtx *svc.ServiceContext
17 | logx.Logger
18 | }
19 |
20 | func NewAuthsInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AuthsInfoLogic {
21 | return &AuthsInfoLogic{
22 | ctx: ctx,
23 | svcCtx: svcCtx,
24 | Logger: logx.WithContext(ctx),
25 | }
26 | }
27 |
28 | func (l *AuthsInfoLogic) AuthsInfo(in *userInfoPb.AuthsInfoReq) (*userInfoPb.AuthsInfoResp, error) {
29 | whereBuilder := l.svcCtx.UserModel.RowBuilder().Where(squirrel.Eq{"user_id": in.AuthIds})
30 | /*
31 | FindAll FindAll 里的降序 需要是user_id 而默认是id 需要改一下源文件
32 | */
33 | auths, err := l.svcCtx.UserModel.FindAll(l.ctx, whereBuilder, "user_id ASC")
34 |
35 | if err != nil {
36 | logx.Errorf("get auths info fail FindAll err : %v", err)
37 | return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "get auths info fail FindAll err : %v , authIds:%v", err, in.AuthIds)
38 | }
39 |
40 | var authsInfo map[int64]*userInfoPb.User
41 | if len(auths) > 0 {
42 | authsInfo = make(map[int64]*userInfoPb.User, len(auths))
43 | for _, v := range auths {
44 | var authInfo userInfoPb.User
45 | _ = copier.Copy(&authInfo, v)
46 |
47 | authsInfo[v.UserId] = &authInfo
48 | }
49 | }
50 |
51 | return &userInfoPb.AuthsInfoResp{
52 | Auths: authsInfo,
53 | }, nil
54 | }
55 |
--------------------------------------------------------------------------------
/service/rpc-user-info/internal/logic/infoLogic.go:
--------------------------------------------------------------------------------
1 | package logic
2 |
3 | import (
4 | "context"
5 | "douyin/common/xerr"
6 | "douyin/service/rpc-user-info/userInfoPb"
7 | "github.com/jinzhu/copier"
8 | "github.com/pkg/errors"
9 |
10 | "douyin/service/rpc-user-info/internal/svc"
11 | "github.com/zeromicro/go-zero/core/logx"
12 | )
13 |
14 | type InfoLogic struct {
15 | ctx context.Context
16 | svcCtx *svc.ServiceContext
17 | logx.Logger
18 | }
19 |
20 | func NewInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *InfoLogic {
21 | return &InfoLogic{
22 | ctx: ctx,
23 | svcCtx: svcCtx,
24 | Logger: logx.WithContext(ctx),
25 | }
26 | }
27 |
28 | func (l *InfoLogic) Info(in *userInfoPb.UserInfoReq) (*userInfoPb.UserInfoResp, error) {
29 | userInfo, err := l.svcCtx.UserModel.FindOne(l.ctx, in.UserId)
30 |
31 | if err != nil {
32 | logx.Errorf("get user info failed: %v", err.Error())
33 | return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "req: %+v", in)
34 | }
35 |
36 | var user userInfoPb.User
37 | _ = copier.Copy(&user, userInfo)
38 |
39 | return &userInfoPb.UserInfoResp{
40 | User: &user,
41 | }, nil
42 | }
43 |
--------------------------------------------------------------------------------
/service/rpc-user-info/internal/logic/loginLogic.go:
--------------------------------------------------------------------------------
1 | package logic
2 |
3 | import (
4 | "context"
5 | myToken "douyin/common/help/token"
6 | "douyin/common/xerr"
7 | "douyin/service/rpc-user-info/internal/svc"
8 | "douyin/service/rpc-user-info/userInfoPb"
9 | "github.com/pkg/errors"
10 | "golang.org/x/crypto/bcrypt"
11 | "strconv"
12 | "time"
13 |
14 | "github.com/zeromicro/go-zero/core/logx"
15 | )
16 |
17 | type LoginLogic struct {
18 | ctx context.Context
19 | svcCtx *svc.ServiceContext
20 | logx.Logger
21 | }
22 |
23 | func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic {
24 | return &LoginLogic{
25 | ctx: ctx,
26 | svcCtx: svcCtx,
27 | Logger: logx.WithContext(ctx),
28 | }
29 | }
30 |
31 | // Login 登录
32 | // 然后通过username获得用户密码,然后比对密码
33 | // 通过userId查 redis
34 | // 如果存在,则直接返回,如果不存在,则生成token,并存入redis
35 | func (l *LoginLogic) Login(in *userInfoPb.LoginReq) (*userInfoPb.LoginResp, error) {
36 | user, err := l.svcCtx.UserModel.FindOneByUserName(l.ctx, in.UserName)
37 | if err != nil {
38 | logx.Errorf("find user failed, err: %s", err.Error())
39 | return nil, errors.Wrap(err, "find user failed")
40 | }
41 |
42 | // 校验密码
43 | err = bcrypt.CompareHashAndPassword([]byte(user.PasswordDigest), []byte(in.Password))
44 | if err != nil {
45 | logx.Errorf("password not match, err: %s", err.Error())
46 | return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "password not match")
47 | }
48 |
49 | // 通过userId查 redis 是否有此token
50 | token, err := l.svcCtx.RedisCache.GetCtx(l.ctx, "token:"+strconv.FormatInt(user.UserId, 10))
51 | if err != nil {
52 | logx.Errorf("get token from redis failed, err: %s", err.Error())
53 | return nil, errors.Wrap(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "get token from redis failed")
54 | }
55 | // 如果存在,则直接返回
56 | if token != "" {
57 | return &userInfoPb.LoginResp{
58 | UserId: user.UserId,
59 | Token: token,
60 | }, nil
61 | }
62 |
63 | //如果不存在,则生成token,并存入redis
64 | var genToken myToken.GenToken
65 | now := time.Now()
66 | token, err = genToken.GenToken(now, user.UserId, nil)
67 | _, err = l.svcCtx.RedisCache.SetnxExCtx(l.ctx, "token:"+strconv.FormatInt(user.UserId, 10), token, myToken.AccessExpire)
68 | if err != nil {
69 | logx.Errorf("set token to redis failed, err: %s", err.Error())
70 | return nil, errors.Wrapf(xerr.NewErrCode(xerr.TOKEN_GENERATE_ERROR), "set token to redis error")
71 | }
72 |
73 | return &userInfoPb.LoginResp{
74 | UserId: user.UserId,
75 | Token: token,
76 | }, nil
77 | }
78 |
--------------------------------------------------------------------------------
/service/rpc-user-info/internal/logic/registerLogic.go:
--------------------------------------------------------------------------------
1 | package logic
2 |
3 | import (
4 | "context"
5 | "douyin/common/help/token"
6 | "douyin/common/xerr"
7 | "douyin/service/rpc-user-info/model"
8 | "douyin/service/rpc-user-info/userInfoPb"
9 | "github.com/pkg/errors"
10 | "golang.org/x/crypto/bcrypt"
11 | "strconv"
12 | "time"
13 |
14 | "douyin/service/rpc-user-info/internal/svc"
15 | "github.com/zeromicro/go-zero/core/logx"
16 | )
17 |
18 | type RegisterLogic struct {
19 | ctx context.Context
20 | svcCtx *svc.ServiceContext
21 | logx.Logger
22 | }
23 |
24 | func NewRegisterLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RegisterLogic {
25 | return &RegisterLogic{
26 | ctx: ctx,
27 | svcCtx: svcCtx,
28 | Logger: logx.WithContext(ctx),
29 | }
30 | }
31 |
32 | // Register -----------------------user-----------------------
33 | func (l *RegisterLogic) Register(in *userInfoPb.RegisterReq) (*userInfoPb.RegisterResp, error) {
34 |
35 | bytes, err := bcrypt.GenerateFromPassword([]byte(in.Password), 12)
36 | if err != nil {
37 | logx.Errorf("generate password failed, err:%s", err.Error())
38 | return nil, err
39 | }
40 | res, err := l.svcCtx.UserModel.Insert(l.ctx, nil, &model.User{
41 | UserName: in.UserName,
42 | PasswordDigest: string(bytes),
43 | })
44 | if err != nil {
45 | logx.Errorf("insert user failed, err: %s", err.Error())
46 | return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "insert user failed, user_name: %s", in.UserName)
47 | }
48 | userId, _ := res.LastInsertId()
49 |
50 | var genToken *token.GenToken
51 | now := time.Now()
52 | tokenString, err := genToken.GenToken(now, userId, nil)
53 | if err != nil {
54 | logx.Errorf("gen token error: %s", err.Error())
55 | return nil, errors.Wrapf(err, "genToken error")
56 | }
57 |
58 | _, err = l.svcCtx.RedisCache.SetnxExCtx(l.ctx, "token:"+strconv.FormatInt(userId, 10), tokenString, token.AccessExpire)
59 | if err != nil {
60 | logx.Errorf("set token to redis error: %s", err.Error())
61 | return nil, errors.Wrapf(xerr.NewErrCode(xerr.TOKEN_GENERATE_ERROR), "genToken error")
62 | }
63 |
64 | return &userInfoPb.RegisterResp{
65 | UserId: userId,
66 | Token: tokenString,
67 | }, nil
68 | }
69 |
--------------------------------------------------------------------------------
/service/rpc-user-info/internal/server/userInfoServiceServer.go:
--------------------------------------------------------------------------------
1 | // Code generated by goctl. DO NOT EDIT!
2 | // Source: UserInfoService.proto
3 |
4 | package server
5 |
6 | import (
7 | "context"
8 |
9 | "douyin/service/rpc-user-info/internal/logic"
10 | "douyin/service/rpc-user-info/internal/svc"
11 | "douyin/service/rpc-user-info/userInfoPb"
12 | )
13 |
14 | type UserInfoServiceServer struct {
15 | svcCtx *svc.ServiceContext
16 | userInfoPb.UnimplementedUserInfoServiceServer
17 | }
18 |
19 | func NewUserInfoServiceServer(svcCtx *svc.ServiceContext) *UserInfoServiceServer {
20 | return &UserInfoServiceServer{
21 | svcCtx: svcCtx,
22 | }
23 | }
24 |
25 | // -----------------------user-----------------------
26 | func (s *UserInfoServiceServer) Register(ctx context.Context, in *userInfoPb.RegisterReq) (*userInfoPb.RegisterResp, error) {
27 | l := logic.NewRegisterLogic(ctx, s.svcCtx)
28 | return l.Register(in)
29 | }
30 |
31 | func (s *UserInfoServiceServer) Login(ctx context.Context, in *userInfoPb.LoginReq) (*userInfoPb.LoginResp, error) {
32 | l := logic.NewLoginLogic(ctx, s.svcCtx)
33 | return l.Login(in)
34 | }
35 |
36 | func (s *UserInfoServiceServer) Info(ctx context.Context, in *userInfoPb.UserInfoReq) (*userInfoPb.UserInfoResp, error) {
37 | l := logic.NewInfoLogic(ctx, s.svcCtx)
38 | return l.Info(in)
39 | }
40 |
41 | func (s *UserInfoServiceServer) AuthsInfo(ctx context.Context, in *userInfoPb.AuthsInfoReq) (*userInfoPb.AuthsInfoResp, error) {
42 | l := logic.NewAuthsInfoLogic(ctx, s.svcCtx)
43 | return l.AuthsInfo(in)
44 | }
45 |
--------------------------------------------------------------------------------
/service/rpc-user-info/internal/svc/serviceContext.go:
--------------------------------------------------------------------------------
1 | package svc
2 |
3 | import (
4 | "douyin/service/rpc-user-info/internal/config"
5 | "douyin/service/rpc-user-info/model"
6 | "github.com/zeromicro/go-zero/core/stores/redis"
7 | "github.com/zeromicro/go-zero/core/stores/sqlx"
8 | )
9 |
10 | type ServiceContext struct {
11 | Config config.Config
12 | UserModel model.UserModel
13 | RedisCache *redis.Redis
14 | }
15 |
16 | func NewServiceContext(c config.Config) *ServiceContext {
17 | return &ServiceContext{
18 | Config: c,
19 | RedisCache: c.RedisCacheConf.NewRedis(),
20 | UserModel: model.NewUserModel(sqlx.NewMysql(c.DB.DataSource), c.CacheConf),
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/service/rpc-user-info/model/vars.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import "github.com/zeromicro/go-zero/core/stores/sqlx"
4 |
5 | var ErrNotFound = sqlx.ErrNotFound
6 |
--------------------------------------------------------------------------------
/service/rpc-user-info/userInfoPb/GenPb/UserInfoService.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | option go_package ="./userInfoPb";
4 |
5 | package userInfoPb;
6 |
7 | // ------------------------------------
8 | // Messages
9 | // ------------------------------------
10 | message RegisterReq {
11 | string userName = 1; //name
12 | string password = 2; //password
13 | }
14 | message RegisterResp {
15 | int64 userId = 1; //userId
16 | string token = 2; //token
17 | }
18 |
19 | message LoginReq {
20 | string userName = 1; //userName
21 | string password = 2; //password
22 | }
23 | message LoginResp {
24 | int64 user_id = 1;
25 | string token = 2; //token
26 | }
27 |
28 | message User {
29 | int64 userId = 1; //userId
30 | string userName = 2; //name
31 | int64 followCount = 3; //followCount
32 | int64 followerCount = 4; //followerCount
33 | }
34 |
35 | message UserInfoReq {
36 | int64 userId = 1; //userId
37 | }
38 |
39 | message UserInfoResp {
40 | User user = 1;
41 | }
42 |
43 | message AuthsInfoReq {
44 | repeated int64 authIds = 1; //authIds
45 | optional int64 curUserId = 2; //curUserId
46 | }
47 |
48 | message AuthsInfoResp {
49 | map auths = 1; //auths
50 | }
51 |
52 | service UserInfoService{
53 | //-----------------------user-----------------------
54 | rpc Register(RegisterReq) returns (RegisterResp);
55 | rpc Login(LoginReq) returns (LoginResp);
56 | rpc Info(UserInfoReq) returns (UserInfoResp);
57 | rpc AuthsInfo(AuthsInfoReq) returns (AuthsInfoResp);
58 | }
59 |
--------------------------------------------------------------------------------
/service/rpc-user-info/userInfoPb/GenPb/build.sh:
--------------------------------------------------------------------------------
1 | goctl rpc protoc UserInfoService.proto --go_out=../../ --go-grpc_out=../../ --zrpc_out=../../ --style=goZero --home=../../../../tpl
2 | # 当有多个proto文件时 需要先生成其他的文件 再生成主要的pb文件
--------------------------------------------------------------------------------
/service/rpc-user-info/userInfoService.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "douyin/service/rpc-user-info/userInfoPb"
5 | "flag"
6 | "fmt"
7 |
8 | "douyin/service/rpc-user-info/internal/config"
9 | "douyin/service/rpc-user-info/internal/server"
10 | "douyin/service/rpc-user-info/internal/svc"
11 | "github.com/zeromicro/go-zero/core/conf"
12 | "github.com/zeromicro/go-zero/core/service"
13 | "github.com/zeromicro/go-zero/zrpc"
14 | "google.golang.org/grpc"
15 | "google.golang.org/grpc/reflection"
16 | )
17 |
18 | var configFile = flag.String("f", "etc/userInfoService.yaml", "the config file")
19 |
20 | func main() {
21 | flag.Parse()
22 |
23 | var c config.Config
24 | conf.MustLoad(*configFile, &c)
25 | ctx := svc.NewServiceContext(c)
26 | svr := server.NewUserInfoServiceServer(ctx)
27 |
28 | s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
29 | userInfoPb.RegisterUserInfoServiceServer(grpcServer, svr)
30 |
31 | if c.Mode == service.DevMode || c.Mode == service.TestMode {
32 | reflection.Register(grpcServer)
33 | }
34 | })
35 | defer s.Stop()
36 |
37 | fmt.Printf("Starting rpc server at %s...\n", c.ListenOn)
38 | s.Start()
39 | }
40 |
--------------------------------------------------------------------------------
/service/rpc-user-info/userinfoservice/userInfoService.go:
--------------------------------------------------------------------------------
1 | // Code generated by goctl. DO NOT EDIT!
2 | // Source: UserInfoService.proto
3 |
4 | package userinfoservice
5 |
6 | import (
7 | "context"
8 |
9 | "douyin/service/rpc-user-info/userInfoPb"
10 |
11 | "github.com/zeromicro/go-zero/zrpc"
12 | "google.golang.org/grpc"
13 | )
14 |
15 | type (
16 | AuthsInfoReq = userInfoPb.AuthsInfoReq
17 | AuthsInfoResp = userInfoPb.AuthsInfoResp
18 | LoginReq = userInfoPb.LoginReq
19 | LoginResp = userInfoPb.LoginResp
20 | RegisterReq = userInfoPb.RegisterReq
21 | RegisterResp = userInfoPb.RegisterResp
22 | User = userInfoPb.User
23 | UserInfoReq = userInfoPb.UserInfoReq
24 | UserInfoResp = userInfoPb.UserInfoResp
25 |
26 | UserInfoService interface {
27 | // -----------------------user-----------------------
28 | Register(ctx context.Context, in *RegisterReq, opts ...grpc.CallOption) (*RegisterResp, error)
29 | Login(ctx context.Context, in *LoginReq, opts ...grpc.CallOption) (*LoginResp, error)
30 | Info(ctx context.Context, in *UserInfoReq, opts ...grpc.CallOption) (*UserInfoResp, error)
31 | AuthsInfo(ctx context.Context, in *AuthsInfoReq, opts ...grpc.CallOption) (*AuthsInfoResp, error)
32 | }
33 |
34 | defaultUserInfoService struct {
35 | cli zrpc.Client
36 | }
37 | )
38 |
39 | func NewUserInfoService(cli zrpc.Client) UserInfoService {
40 | return &defaultUserInfoService{
41 | cli: cli,
42 | }
43 | }
44 |
45 | // -----------------------user-----------------------
46 | func (m *defaultUserInfoService) Register(ctx context.Context, in *RegisterReq, opts ...grpc.CallOption) (*RegisterResp, error) {
47 | client := userInfoPb.NewUserInfoServiceClient(m.cli.Conn())
48 | return client.Register(ctx, in, opts...)
49 | }
50 |
51 | func (m *defaultUserInfoService) Login(ctx context.Context, in *LoginReq, opts ...grpc.CallOption) (*LoginResp, error) {
52 | client := userInfoPb.NewUserInfoServiceClient(m.cli.Conn())
53 | return client.Login(ctx, in, opts...)
54 | }
55 |
56 | func (m *defaultUserInfoService) Info(ctx context.Context, in *UserInfoReq, opts ...grpc.CallOption) (*UserInfoResp, error) {
57 | client := userInfoPb.NewUserInfoServiceClient(m.cli.Conn())
58 | return client.Info(ctx, in, opts...)
59 | }
60 |
61 | func (m *defaultUserInfoService) AuthsInfo(ctx context.Context, in *AuthsInfoReq, opts ...grpc.CallOption) (*AuthsInfoResp, error) {
62 | client := userInfoPb.NewUserInfoServiceClient(m.cli.Conn())
63 | return client.AuthsInfo(ctx, in, opts...)
64 | }
65 |
--------------------------------------------------------------------------------
/service/rpc-user-operate/etc/userOptService.yaml:
--------------------------------------------------------------------------------
1 | Name: useroptservice.rpc
2 | ListenOn: 0.0.0.0:9998
3 |
4 | Mode: dev
5 |
6 | Etcd:
7 | Hosts:
8 | - 127.0.0.1:33333
9 | Key: useroptservice.rpc
10 |
11 | Log:
12 | Encoding: plain
13 | DB:
14 | DataSource: username:password@tcp(localhost:3306)/douyin2?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
15 |
16 | CacheConf:
17 | - Host: localhost:6379
18 | Pass: password
19 |
20 | COSConf:
21 | SecretId: SecretId
22 | SecretKey: SecretKey
23 | CommentBucket: https://xxxx.cos.ap-guangzhou.myqcloud.com
--------------------------------------------------------------------------------
/service/rpc-user-operate/internal/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "github.com/zeromicro/go-zero/core/stores/cache"
5 | "github.com/zeromicro/go-zero/zrpc"
6 | )
7 |
8 | type Config struct {
9 | zrpc.RpcServerConf
10 | DB struct {
11 | DataSource string
12 | }
13 | CacheConf cache.CacheConf
14 |
15 | COSConf struct {
16 | SecretId string
17 | SecretKey string
18 | CommentBucket string
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/service/rpc-user-operate/internal/logic/getUserFavoriteLogic.go:
--------------------------------------------------------------------------------
1 | package logic
2 |
3 | import (
4 | "context"
5 | "douyin/common/xerr"
6 | "douyin/service/rpc-user-operate/internal/svc"
7 | "douyin/service/rpc-user-operate/userOptPb"
8 | "github.com/Masterminds/squirrel"
9 | "github.com/pkg/errors"
10 |
11 | "github.com/zeromicro/go-zero/core/logx"
12 | )
13 |
14 | type GetUserFavoriteLogic struct {
15 | ctx context.Context
16 | svcCtx *svc.ServiceContext
17 | logx.Logger
18 | }
19 |
20 | func NewGetUserFavoriteLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserFavoriteLogic {
21 | return &GetUserFavoriteLogic{
22 | ctx: ctx,
23 | svcCtx: svcCtx,
24 | Logger: logx.WithContext(ctx),
25 | }
26 | }
27 |
28 | // GetUserFavorite 获取用户对视频的点赞
29 | func (l *GetUserFavoriteLogic) GetUserFavorite(in *userOptPb.GetUserFavoriteReq) (*userOptPb.GetUserFavoriteResp, error) {
30 |
31 | if in.VideoIds != nil { // 给 feed 接口使用 查看用户对以下videoids的点赞状态
32 | whereBuilder := l.svcCtx.UserFavoriteModel.RowBuilder().Where(squirrel.Eq{"user_id": in.UserId, "video_id": in.VideoIds}).Where(squirrel.NotEq{"is_favorite": 0})
33 |
34 | res, err := l.svcCtx.UserFavoriteModel.FindAll(l.ctx, whereBuilder, "")
35 | if err != nil {
36 | logx.Errorf("GetUserFavorite err , id:%d , err:%s", in.UserId, err.Error())
37 | return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "GetUserFavorite err")
38 | }
39 |
40 | // 该用户对视频的点赞映射
41 | var respUserFavoriteList map[int64]bool
42 | if len(res) > 0 {
43 | respUserFavoriteList = make(map[int64]bool, len(res))
44 | for _, v := range res {
45 | respUserFavoriteList[v.VideoId] = true
46 | }
47 | }
48 |
49 | return &userOptPb.GetUserFavoriteResp{
50 | UserFavoriteList: respUserFavoriteList,
51 | }, nil
52 |
53 | } else { // 给拉取点赞列表使用 查询的是用户对哪些视频进行了点赞
54 | whereBuilder := l.svcCtx.UserFavoriteModel.RowBuilder().Where("user_id = ? and is_favorite != 0 ", in.UserId)
55 |
56 | res, err := l.svcCtx.UserFavoriteModel.FindAll(l.ctx, whereBuilder, "user_id ASC")
57 | if err != nil {
58 | logx.Errorf("GetUserFavorite err , id:%d , err:%s", in.UserId, err.Error())
59 | return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "GetUserFavorite err")
60 | }
61 |
62 | /*
63 | 这里用数组是因为拉取点赞列表的接口返回的是数组 可以直接返回 比较方便
64 | */
65 | var respUserFavoriteArr []int64
66 | if len(res) > 0 {
67 | respUserFavoriteArr = make([]int64, len(res))
68 | for _, v := range res {
69 | respUserFavoriteArr = append(respUserFavoriteArr, v.VideoId)
70 | }
71 | }
72 | return &userOptPb.GetUserFavoriteResp{
73 | UserFavoriteArr: respUserFavoriteArr,
74 | }, nil
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/service/rpc-user-operate/internal/logic/getUserFollowLogic.go:
--------------------------------------------------------------------------------
1 | package logic
2 |
3 | import (
4 | "context"
5 | "douyin/common/xerr"
6 | "douyin/service/rpc-user-operate/internal/svc"
7 | "douyin/service/rpc-user-operate/userOptPb"
8 | "github.com/Masterminds/squirrel"
9 | "github.com/pkg/errors"
10 | "github.com/zeromicro/go-zero/core/logx"
11 | )
12 |
13 | type GetUserFollowLogic struct {
14 | ctx context.Context
15 | svcCtx *svc.ServiceContext
16 | logx.Logger
17 | }
18 |
19 | func NewGetUserFollowLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserFollowLogic {
20 | return &GetUserFollowLogic{
21 | ctx: ctx,
22 | svcCtx: svcCtx,
23 | Logger: logx.WithContext(ctx),
24 | }
25 | }
26 |
27 | func (l *GetUserFollowLogic) GetUserFollow(in *userOptPb.GetUserFollowReq) (*userOptPb.GetUserFollowResp, error) {
28 |
29 | if in.AuthIds != nil { // 查看用户是否关注了这些作者
30 | whereBuilder := l.svcCtx.UserFollowModel.RowBuilder().Where(squirrel.Eq{"user_id": in.UserId, "follow_id": in.AuthIds}).Where(squirrel.NotEq{"is_follow": 0})
31 | res, err := l.svcCtx.UserFollowModel.FindAll(l.ctx, whereBuilder, "")
32 | if err != nil {
33 | logx.Errorf("GetUserFollow err:%s", err.Error())
34 | return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "GetUserFollow err , id:%d , err:%v", in.UserId, err)
35 | }
36 |
37 | var userFollowList map[int64]bool
38 | if len(res) > 0 {
39 | userFollowList = make(map[int64]bool, len(res))
40 | for _, v := range res {
41 | userFollowList[v.FollowId] = true
42 | }
43 | }
44 |
45 | return &userOptPb.GetUserFollowResp{
46 | UserFollowList: userFollowList,
47 | }, nil
48 |
49 | } else { // 把该用户的所有关注者找出来
50 | whereBuilder := l.svcCtx.UserFollowModel.RowBuilder().Where("user_id = ? and is_follow != 0 ", in.UserId)
51 |
52 | res, err := l.svcCtx.UserFollowModel.FindAll(l.ctx, whereBuilder, "user_id ASC")
53 | if err != nil {
54 | logx.Errorf("GetUserFollowLogic GetUserFollow err: %s", err.Error())
55 | return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "GetUserFollow err , id:%d , err:%v", in.UserId, err)
56 | }
57 |
58 | var userFollowList map[int64]bool
59 | if len(res) > 0 {
60 | userFollowList = make(map[int64]bool, len(res))
61 | for _, v := range res {
62 | userFollowList[v.FollowId] = true
63 | }
64 | }
65 |
66 | return &userOptPb.GetUserFollowResp{
67 | UserFollowList: userFollowList,
68 | }, nil
69 |
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/service/rpc-user-operate/internal/logic/getUserFollowerLogic.go:
--------------------------------------------------------------------------------
1 | package logic
2 |
3 | import (
4 | "context"
5 | "douyin/common/xerr"
6 | "github.com/pkg/errors"
7 |
8 | "douyin/service/rpc-user-operate/internal/svc"
9 | "douyin/service/rpc-user-operate/userOptPb"
10 |
11 | "github.com/zeromicro/go-zero/core/logx"
12 | )
13 |
14 | type GetUserFollowerLogic struct {
15 | ctx context.Context
16 | svcCtx *svc.ServiceContext
17 | logx.Logger
18 | }
19 |
20 | func NewGetUserFollowerLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserFollowerLogic {
21 | return &GetUserFollowerLogic{
22 | ctx: ctx,
23 | svcCtx: svcCtx,
24 | Logger: logx.WithContext(ctx),
25 | }
26 | }
27 |
28 | func (l *GetUserFollowerLogic) GetUserFollower(in *userOptPb.GetUserFollowerReq) (*userOptPb.GetUserFollowerResp, error) {
29 | whereBuilder := l.svcCtx.UserFollowModel.RowBuilder().Where("follow_id = ? and is_follow != 0 ", in.UserId)
30 |
31 | res, err := l.svcCtx.UserFollowModel.FindAll(l.ctx, whereBuilder, "user_id ASC")
32 | if err != nil {
33 | logx.Errorf("GetUserFollowerLogic GetUserFollower err: %s", err.Error())
34 | return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "GetUserFollower err , id:%d , err:%v", in.UserId, err)
35 | }
36 |
37 | /*
38 | 这里用数组是因为拉取点赞列表的接口返回的是数组 可以直接返回 比较方便
39 | */
40 | var userFollowerList map[int64]bool
41 | if len(res) > 0 {
42 | userFollowerList = make(map[int64]bool, len(res))
43 | for _, v := range res {
44 | userFollowerList[v.UserId] = true
45 | }
46 | }
47 |
48 | return &userOptPb.GetUserFollowerResp{
49 | UserFollowerList: userFollowerList,
50 | }, nil
51 | }
52 |
--------------------------------------------------------------------------------
/service/rpc-user-operate/internal/logic/getVideoCommentLogic.go:
--------------------------------------------------------------------------------
1 | package logic
2 |
3 | import (
4 | "context"
5 | "douyin/common/help/cos"
6 | "douyin/service/rpc-user-operate/internal/svc"
7 | "douyin/service/rpc-user-operate/userOptPb"
8 | "fmt"
9 | "github.com/jinzhu/copier"
10 | "github.com/zeromicro/go-zero/core/logx"
11 | )
12 |
13 | type GetVideoCommentLogic struct {
14 | ctx context.Context
15 | svcCtx *svc.ServiceContext
16 | logx.Logger
17 | }
18 |
19 | func NewGetVideoCommentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetVideoCommentLogic {
20 | return &GetVideoCommentLogic{
21 | ctx: ctx,
22 | svcCtx: svcCtx,
23 | Logger: logx.WithContext(ctx),
24 | }
25 | }
26 |
27 | func (l *GetVideoCommentLogic) GetVideoComment(in *userOptPb.GetVideoCommentReq) (*userOptPb.GetVideoCommentReqResp, error) {
28 |
29 | key := fmt.Sprintf("video_id_%d/", in.VideoId)
30 | downloadHelper := cos.DownloadComment{
31 | Key: key,
32 | CommentBucket: l.svcCtx.Config.COSConf.CommentBucket,
33 | SecretID: l.svcCtx.Config.COSConf.SecretId,
34 | SecretKey: l.svcCtx.Config.COSConf.SecretKey,
35 | }
36 | comments, err := downloadHelper.DownloadComment(l.ctx)
37 |
38 | var commentList []*userOptPb.Comment
39 | for _, v := range comments {
40 | var comment userOptPb.Comment
41 | _ = copier.Copy(&comment, v)
42 | comment.Content = v.Content
43 | commentList = append(commentList, &comment)
44 | }
45 |
46 | if err != nil {
47 | logx.Errorf("get video comment list fail %s", err.Error())
48 | return nil, err
49 | }
50 |
51 | return &userOptPb.GetVideoCommentReqResp{
52 | CommentList: commentList,
53 | }, nil
54 | }
55 |
--------------------------------------------------------------------------------
/service/rpc-user-operate/internal/logic/updateCommentStatusLogic.go:
--------------------------------------------------------------------------------
1 | package logic
2 |
3 | import (
4 | "context"
5 | "douyin/service/rpc-user-operate/model"
6 | "github.com/zeromicro/go-zero/core/stores/sqlx"
7 | "strings"
8 |
9 | "douyin/service/rpc-user-operate/internal/svc"
10 | "douyin/service/rpc-user-operate/userOptPb"
11 |
12 | "github.com/zeromicro/go-zero/core/logx"
13 | )
14 |
15 | type UpdateCommentStatusLogic struct {
16 | ctx context.Context
17 | svcCtx *svc.ServiceContext
18 | logx.Logger
19 | }
20 |
21 | func NewUpdateCommentStatusLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateCommentStatusLogic {
22 | return &UpdateCommentStatusLogic{
23 | ctx: ctx,
24 | svcCtx: svcCtx,
25 | Logger: logx.WithContext(ctx),
26 | }
27 | }
28 |
29 | func (l *UpdateCommentStatusLogic) UpdateCommentStatus(in *userOptPb.UpdateCommentStatusReq) (*userOptPb.UpdateCommentStatusResp, error) {
30 | tmp := []string{"video_id", "comment_id", "user_id", "del_state"}
31 | field := strings.Join(tmp, ",")
32 | var action int64
33 | if in.ActionType == model.ActionADD { // 1是添加评论 0是删除评论
34 | action = 0 // del_state 0 是存在 1是已删除
35 | } else if in.ActionType == model.ActionCancel {
36 | action = 1
37 | }
38 |
39 | err := l.svcCtx.UserCommentModel.Trans(l.ctx, func(context context.Context, session sqlx.Session) error {
40 |
41 | // 这里有点不一样 要是model文件里的内容变了 这里也要变
42 | // InsertOrUpdate(ctx context.Context, session sqlx.Session, field string, setStatus string, videoId, objId, userId, opt int64)
43 | _, err := l.svcCtx.UserCommentModel.InsertOrUpdate(l.ctx, session, field, "del_state", in.VideoId, in.CommentId, in.UserId, action)
44 | if err != nil {
45 | logx.Errorf("UpdateCommentStatus------->InsertOrUpdate err : %s", err.Error())
46 | return err
47 | }
48 |
49 | // 消息中传来的 in.action是 0 1 写入video comment_count 就需要变成 -1 / +1
50 | actionType := l.getActionType(in.ActionType)
51 | _, err = l.svcCtx.VideoModel.UpdateStatus(l.ctx, session, "comment_count", "id", actionType, in.VideoId)
52 | if err != nil {
53 | logx.Errorf("UpdateCommentStatus------->UpdateStatus err : %s", err.Error())
54 | return err
55 | }
56 |
57 | return nil
58 | })
59 |
60 | if err != nil {
61 | logx.Error("UpdateCommentStatus-------> trans fail")
62 | return &userOptPb.UpdateCommentStatusResp{}, err
63 | }
64 |
65 | return &userOptPb.UpdateCommentStatusResp{}, nil
66 | }
67 |
68 | func (l *UpdateCommentStatusLogic) getActionType(actionType int64) int64 {
69 |
70 | switch actionType { // 方便扩展
71 | case model.ActionADD:
72 | return 1
73 | case model.ActionCancel:
74 | return -1
75 | default:
76 | return -99
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/service/rpc-user-operate/internal/logic/updateFavoriteStatusLogic.go:
--------------------------------------------------------------------------------
1 | package logic
2 |
3 | import (
4 | "context"
5 | "douyin/service/rpc-user-operate/internal/svc"
6 | "douyin/service/rpc-user-operate/model"
7 | "douyin/service/rpc-user-operate/userOptPb"
8 | "github.com/zeromicro/go-zero/core/stores/sqlx"
9 | "strings"
10 |
11 | "github.com/zeromicro/go-zero/core/logx"
12 | )
13 |
14 | type UpdateFavoriteStatusLogic struct {
15 | ctx context.Context
16 | svcCtx *svc.ServiceContext
17 | logx.Logger
18 | }
19 |
20 | func NewUpdateFavoriteStatusLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateFavoriteStatusLogic {
21 | return &UpdateFavoriteStatusLogic{
22 | ctx: ctx,
23 | svcCtx: svcCtx,
24 | Logger: logx.WithContext(ctx),
25 | }
26 | }
27 |
28 | // export logic "INSERT INTO user_favorite_list(user_id,video_id,is_favorite) VALUES(1,6,?) ON DUPLICATE KEY UPDATE is_favorite=?"
29 | func (l *UpdateFavoriteStatusLogic) UpdateFavoriteStatus(in *userOptPb.UpdateFavoriteStatusReq) (*userOptPb.UpdateFavoriteStatusResp, error) {
30 | tmp := []string{"user_id", "video_id", "is_favorite"}
31 | field := strings.Join(tmp, ",")
32 | err := l.svcCtx.UserFavoriteModel.Trans(l.ctx, func(context context.Context, session sqlx.Session) error {
33 | _, err := l.svcCtx.UserFavoriteModel.InsertOrUpdate(l.ctx, session, field, "is_favorite", in.UserId, in.VideoId, in.ActionType)
34 | if err != nil {
35 | logx.Errorf("UpdateFavoriteStatusLogic------->InsertOrUpdate err : %v", err.Error())
36 | return err
37 | }
38 |
39 | // 消息中传来的 in.action是 0 1 写入user favorite_count 就需要变成 -1 1
40 | action := l.getActionType(in.ActionType)
41 | _, err = l.svcCtx.VideoModel.UpdateStatus(l.ctx, session, "favorite_count", "id", action, in.VideoId)
42 | if err != nil {
43 | logx.Errorf("UpdateFavoriteStatusLogic------->UpdateStatus err : %v", err.Error())
44 | return err
45 | }
46 | return nil
47 | })
48 | if err != nil {
49 | logx.Error("UpdateFavoriteStatusLogic-------> trans fail")
50 | return &userOptPb.UpdateFavoriteStatusResp{}, err
51 | }
52 |
53 | return &userOptPb.UpdateFavoriteStatusResp{}, nil
54 | }
55 | func (l *UpdateFavoriteStatusLogic) getActionType(actionType int64) int64 {
56 |
57 | switch actionType { // 方便扩展
58 | case model.ActionADD:
59 | return 1
60 | case model.ActionCancel:
61 | return -1
62 | default:
63 | return -99
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/service/rpc-user-operate/internal/logic/updateFollowStatusLogic.go:
--------------------------------------------------------------------------------
1 | package logic
2 |
3 | import (
4 | "context"
5 | "douyin/service/rpc-user-operate/model"
6 | "github.com/zeromicro/go-zero/core/stores/sqlx"
7 | "strings"
8 |
9 | "douyin/service/rpc-user-operate/internal/svc"
10 | "douyin/service/rpc-user-operate/userOptPb"
11 |
12 | "github.com/zeromicro/go-zero/core/logx"
13 | )
14 |
15 | type UpdateFollowStatusLogic struct {
16 | ctx context.Context
17 | svcCtx *svc.ServiceContext
18 | logx.Logger
19 | }
20 |
21 | func NewUpdateFollowStatusLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateFollowStatusLogic {
22 | return &UpdateFollowStatusLogic{
23 | ctx: ctx,
24 | svcCtx: svcCtx,
25 | Logger: logx.WithContext(ctx),
26 | }
27 | }
28 |
29 | func (l *UpdateFollowStatusLogic) UpdateFollowStatus(in *userOptPb.UpdateFollowStatusReq) (*userOptPb.UpdateFollowStatusResp, error) {
30 |
31 | tmp := []string{"user_id", "follow_id", "is_follow"}
32 | field := strings.Join(tmp, ",")
33 | err := l.svcCtx.UserFollowModel.Trans(l.ctx, func(context context.Context, session sqlx.Session) error {
34 | _, err := l.svcCtx.UserFollowModel.InsertOrUpdate(l.ctx, session, field, "is_follow", in.UserId, in.FollowId, in.ActionType)
35 | if err != nil {
36 | logx.Errorf("UpdateFollowStatus------->InsertOrUpdate err : %s", err.Error())
37 | return err
38 | }
39 |
40 | // 消息中传来的 in.action是 0 1 写入user follow_count 就需要变成 -1 / +1
41 | action := l.getActionType(in.ActionType)
42 | _, err = l.svcCtx.UserModel.UpdateStatus(l.ctx, session, "follow_count", "user_id", action, in.UserId)
43 | if err != nil {
44 | logx.Errorf("UpdateFollowStatus------->UpdateStatus err : %s", err.Error())
45 | return err
46 | }
47 |
48 | // 更新被关注者的粉丝数
49 | _, err = l.svcCtx.UserModel.UpdateStatus(l.ctx, session, "follower_count", "user_id", action, in.FollowId)
50 | if err != nil {
51 | logx.Errorf("UpdateFollowStatus------->Update follower Status err : %v", err.Error())
52 | return err
53 | }
54 | return nil
55 | })
56 | if err != nil {
57 | logx.Error("UpdateFollowStatus-------> trans fail")
58 | return &userOptPb.UpdateFollowStatusResp{}, err
59 | }
60 |
61 | return &userOptPb.UpdateFollowStatusResp{}, nil
62 | }
63 |
64 | func (l *UpdateFollowStatusLogic) getActionType(actionType int64) int64 {
65 |
66 | switch actionType { // 方便扩展
67 | case model.ActionADD:
68 | return 1
69 | case model.ActionCancel:
70 | return -1
71 | default:
72 | return -99
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/service/rpc-user-operate/internal/server/userOptServiceServer.go:
--------------------------------------------------------------------------------
1 | // Code generated by goctl. DO NOT EDIT!
2 | // Source: UserOptService.proto
3 |
4 | package server
5 |
6 | import (
7 | "context"
8 |
9 | "douyin/service/rpc-user-operate/internal/logic"
10 | "douyin/service/rpc-user-operate/internal/svc"
11 | "douyin/service/rpc-user-operate/userOptPb"
12 | )
13 |
14 | type UserOptServiceServer struct {
15 | svcCtx *svc.ServiceContext
16 | userOptPb.UnimplementedUserOptServiceServer
17 | }
18 |
19 | func NewUserOptServiceServer(svcCtx *svc.ServiceContext) *UserOptServiceServer {
20 | return &UserOptServiceServer{
21 | svcCtx: svcCtx,
22 | }
23 | }
24 |
25 | // -----------------------userFavoriteList-----------------------
26 | func (s *UserOptServiceServer) GetUserFavorite(ctx context.Context, in *userOptPb.GetUserFavoriteReq) (*userOptPb.GetUserFavoriteResp, error) {
27 | l := logic.NewGetUserFavoriteLogic(ctx, s.svcCtx)
28 | return l.GetUserFavorite(in)
29 | }
30 |
31 | func (s *UserOptServiceServer) GetUserFollow(ctx context.Context, in *userOptPb.GetUserFollowReq) (*userOptPb.GetUserFollowResp, error) {
32 | l := logic.NewGetUserFollowLogic(ctx, s.svcCtx)
33 | return l.GetUserFollow(in)
34 | }
35 |
36 | func (s *UserOptServiceServer) UpdateFavoriteStatus(ctx context.Context, in *userOptPb.UpdateFavoriteStatusReq) (*userOptPb.UpdateFavoriteStatusResp, error) {
37 | l := logic.NewUpdateFavoriteStatusLogic(ctx, s.svcCtx)
38 | return l.UpdateFavoriteStatus(in)
39 | }
40 |
41 | func (s *UserOptServiceServer) UpdateFollowStatus(ctx context.Context, in *userOptPb.UpdateFollowStatusReq) (*userOptPb.UpdateFollowStatusResp, error) {
42 | l := logic.NewUpdateFollowStatusLogic(ctx, s.svcCtx)
43 | return l.UpdateFollowStatus(in)
44 | }
45 |
46 | func (s *UserOptServiceServer) UpdateCommentStatus(ctx context.Context, in *userOptPb.UpdateCommentStatusReq) (*userOptPb.UpdateCommentStatusResp, error) {
47 | l := logic.NewUpdateCommentStatusLogic(ctx, s.svcCtx)
48 | return l.UpdateCommentStatus(in)
49 | }
50 |
51 | func (s *UserOptServiceServer) GetVideoComment(ctx context.Context, in *userOptPb.GetVideoCommentReq) (*userOptPb.GetVideoCommentReqResp, error) {
52 | l := logic.NewGetVideoCommentLogic(ctx, s.svcCtx)
53 | return l.GetVideoComment(in)
54 | }
55 |
56 | func (s *UserOptServiceServer) GetUserFollower(ctx context.Context, in *userOptPb.GetUserFollowerReq) (*userOptPb.GetUserFollowerResp, error) {
57 | l := logic.NewGetUserFollowerLogic(ctx, s.svcCtx)
58 | return l.GetUserFollower(in)
59 | }
60 |
--------------------------------------------------------------------------------
/service/rpc-user-operate/internal/svc/serviceContext.go:
--------------------------------------------------------------------------------
1 | package svc
2 |
3 | import (
4 | usermodel "douyin/service/rpc-user-info/model"
5 | "douyin/service/rpc-user-operate/internal/config"
6 | "douyin/service/rpc-user-operate/model"
7 | videomodel "douyin/service/rpc-video-service/model"
8 | "github.com/zeromicro/go-zero/core/stores/sqlx"
9 | )
10 |
11 | type ServiceContext struct {
12 | Config config.Config
13 | UserFavoriteModel model.UserFavoriteListModel
14 | UserFollowModel model.UserFollowListModel
15 | UserCommentModel model.UserCommentListModel
16 | UserModel usermodel.UserModel
17 | VideoModel videomodel.VideoModel
18 | }
19 |
20 | func NewServiceContext(c config.Config) *ServiceContext {
21 | return &ServiceContext{
22 | Config: c,
23 | UserFavoriteModel: model.NewUserFavoriteListModel(sqlx.NewMysql(c.DB.DataSource), c.CacheConf),
24 | UserFollowModel: model.NewUserFollowListModel(sqlx.NewMysql(c.DB.DataSource), c.CacheConf),
25 | UserModel: usermodel.NewUserModel(sqlx.NewMysql(c.DB.DataSource), c.CacheConf),
26 | VideoModel: videomodel.NewVideoModel(sqlx.NewMysql(c.DB.DataSource), c.CacheConf),
27 | UserCommentModel: model.NewUserCommentListModel(sqlx.NewMysql(c.DB.DataSource), c.CacheConf),
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/service/rpc-user-operate/model/vars.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import "github.com/zeromicro/go-zero/core/stores/sqlx"
4 |
5 | var ErrNotFound = sqlx.ErrNotFound
6 |
7 | const ( // 增加是1 取消是0
8 | ActionADD int64 = 1
9 | ActionCancel int64 = 0
10 | )
11 |
--------------------------------------------------------------------------------
/service/rpc-user-operate/userOptPb/GenPb/UserOptService.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | option go_package ="./userOptPb";
4 |
5 | package pb;
6 |
7 | // ------------------------------------
8 | // Messages
9 | // ------------------------------------
10 |
11 | //--------------------------------userFavoriteList--------------------------------
12 |
13 | // 用户给视频点赞
14 | message UpdateFavoriteStatusReq {
15 | int64 videoId = 1; //videoId
16 | int64 userId = 2; //userId
17 | int64 actionType = 3; //点赞
18 | }
19 |
20 | message UpdateFavoriteStatusResp {
21 | }
22 |
23 | // 用户给用户关注
24 | message UpdateFollowStatusReq {
25 | int64 followId = 1; //videoId
26 | int64 userId = 2; //userId
27 | int64 actionType = 3; //点赞
28 | }
29 |
30 | message UpdateFollowStatusResp {
31 | }
32 |
33 | // 用户对视频评论
34 | message UpdateCommentStatusReq {
35 | int64 videoId = 1; //videoId
36 | int64 userId = 2; //userId
37 | int64 commentId = 3; //commentId
38 | int64 actionType = 4; //评论 取消评论
39 | }
40 |
41 | message UpdateCommentStatusResp {
42 | }
43 |
44 |
45 |
46 | // 获得用户和视频的点赞关系
47 | message GetUserFavoriteReq {
48 | int64 userId = 1; //userId
49 | repeated int64 videoIds = 2; //videoId
50 | }
51 |
52 | message GetUserFavoriteResp {
53 | map userFavoriteList = 1; //userFavoriteList
54 | repeated int64 userFavoriteArr = 2; //userFavoriteArr
55 | }
56 |
57 | // 获得用户的关注列表
58 | message GetUserFollowReq {
59 | int64 userId = 1; //user_id
60 | repeated int64 authIds = 2; //user_id
61 | }
62 | message GetUserFollowResp {
63 | map userFollowList = 1; //用户和视频的关注关系
64 | }
65 |
66 | // 获得用户的粉丝列表
67 | message GetUserFollowerReq {
68 | int64 userId = 1; //user_id
69 | }
70 | message GetUserFollowerResp {
71 | map userFollowerList = 1; //用户和视频的关注关系
72 | }
73 |
74 |
75 | // 获得视频的评论列表
76 | message GetVideoCommentReq {
77 | int64 videoId = 1; //video_id
78 | }
79 | message Comment {
80 | int64 commentId = 1;
81 | int64 userId = 2;
82 | string content = 3;
83 | string createDate = 4;
84 | }
85 |
86 | message GetVideoCommentReqResp {
87 | repeated Comment commentList = 1;//comment_list
88 | }
89 |
90 | // ------------------------------------
91 | // Rpc Func
92 | // ------------------------------------
93 |
94 | service UserOptService{
95 | //-----------------------userFavoriteList-----------------------
96 | rpc GetUserFavorite(GetUserFavoriteReq) returns (GetUserFavoriteResp);
97 | rpc GetUserFollow(GetUserFollowReq) returns (GetUserFollowResp);
98 | rpc UpdateFavoriteStatus(UpdateFavoriteStatusReq) returns (UpdateFavoriteStatusResp);
99 | rpc UpdateFollowStatus(UpdateFollowStatusReq) returns (UpdateFollowStatusResp);
100 | rpc UpdateCommentStatus(UpdateCommentStatusReq) returns (UpdateCommentStatusResp);
101 | rpc GetVideoComment(GetVideoCommentReq) returns (GetVideoCommentReqResp);
102 | rpc GetUserFollower(GetUserFollowerReq) returns (GetUserFollowerResp);
103 | }
104 |
--------------------------------------------------------------------------------
/service/rpc-user-operate/userOptPb/GenPb/build.sh:
--------------------------------------------------------------------------------
1 | goctl rpc protoc UserOptService.proto --go_out=../../ --go-grpc_out=../../ --zrpc_out=../../ --style=goZero --home=../../../../tpl
2 | # 当有多个proto文件时 需要先生成其他的文件 再生成主要的pb文件
--------------------------------------------------------------------------------
/service/rpc-user-operate/userOptService.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 |
7 | "douyin/service/rpc-user-operate/internal/config"
8 | "douyin/service/rpc-user-operate/internal/server"
9 | "douyin/service/rpc-user-operate/internal/svc"
10 | "douyin/service/rpc-user-operate/userOptPb"
11 |
12 | "github.com/zeromicro/go-zero/core/conf"
13 | "github.com/zeromicro/go-zero/core/service"
14 | "github.com/zeromicro/go-zero/zrpc"
15 | "google.golang.org/grpc"
16 | "google.golang.org/grpc/reflection"
17 | )
18 |
19 | var configFile = flag.String("f", "etc/userOptService.yaml", "the config file")
20 |
21 | func main() {
22 | flag.Parse()
23 |
24 | var c config.Config
25 | conf.MustLoad(*configFile, &c)
26 | ctx := svc.NewServiceContext(c)
27 | svr := server.NewUserOptServiceServer(ctx)
28 |
29 | s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
30 | userOptPb.RegisterUserOptServiceServer(grpcServer, svr)
31 |
32 | if c.Mode == service.DevMode || c.Mode == service.TestMode {
33 | reflection.Register(grpcServer)
34 | }
35 | })
36 | defer s.Stop()
37 |
38 | fmt.Printf("Starting rpc server at %s...\n", c.ListenOn)
39 | s.Start()
40 | }
41 |
--------------------------------------------------------------------------------
/service/rpc-user-operate/useroptservice/userOptService.go:
--------------------------------------------------------------------------------
1 | // Code generated by goctl. DO NOT EDIT!
2 | // Source: UserOptService.proto
3 |
4 | package useroptservice
5 |
6 | import (
7 | "context"
8 |
9 | "douyin/service/rpc-user-operate/userOptPb"
10 |
11 | "github.com/zeromicro/go-zero/zrpc"
12 | "google.golang.org/grpc"
13 | )
14 |
15 | type (
16 | Comment = userOptPb.Comment
17 | GetUserFavoriteReq = userOptPb.GetUserFavoriteReq
18 | GetUserFavoriteResp = userOptPb.GetUserFavoriteResp
19 | GetUserFollowReq = userOptPb.GetUserFollowReq
20 | GetUserFollowResp = userOptPb.GetUserFollowResp
21 | GetUserFollowerReq = userOptPb.GetUserFollowerReq
22 | GetUserFollowerResp = userOptPb.GetUserFollowerResp
23 | GetVideoCommentReq = userOptPb.GetVideoCommentReq
24 | GetVideoCommentReqResp = userOptPb.GetVideoCommentReqResp
25 | UpdateCommentStatusReq = userOptPb.UpdateCommentStatusReq
26 | UpdateCommentStatusResp = userOptPb.UpdateCommentStatusResp
27 | UpdateFavoriteStatusReq = userOptPb.UpdateFavoriteStatusReq
28 | UpdateFavoriteStatusResp = userOptPb.UpdateFavoriteStatusResp
29 | UpdateFollowStatusReq = userOptPb.UpdateFollowStatusReq
30 | UpdateFollowStatusResp = userOptPb.UpdateFollowStatusResp
31 |
32 | UserOptService interface {
33 | // -----------------------userFavoriteList-----------------------
34 | GetUserFavorite(ctx context.Context, in *GetUserFavoriteReq, opts ...grpc.CallOption) (*GetUserFavoriteResp, error)
35 | GetUserFollow(ctx context.Context, in *GetUserFollowReq, opts ...grpc.CallOption) (*GetUserFollowResp, error)
36 | UpdateFavoriteStatus(ctx context.Context, in *UpdateFavoriteStatusReq, opts ...grpc.CallOption) (*UpdateFavoriteStatusResp, error)
37 | UpdateFollowStatus(ctx context.Context, in *UpdateFollowStatusReq, opts ...grpc.CallOption) (*UpdateFollowStatusResp, error)
38 | UpdateCommentStatus(ctx context.Context, in *UpdateCommentStatusReq, opts ...grpc.CallOption) (*UpdateCommentStatusResp, error)
39 | GetVideoComment(ctx context.Context, in *GetVideoCommentReq, opts ...grpc.CallOption) (*GetVideoCommentReqResp, error)
40 | GetUserFollower(ctx context.Context, in *GetUserFollowerReq, opts ...grpc.CallOption) (*GetUserFollowerResp, error)
41 | }
42 |
43 | defaultUserOptService struct {
44 | cli zrpc.Client
45 | }
46 | )
47 |
48 | func NewUserOptService(cli zrpc.Client) UserOptService {
49 | return &defaultUserOptService{
50 | cli: cli,
51 | }
52 | }
53 |
54 | // -----------------------userFavoriteList-----------------------
55 | func (m *defaultUserOptService) GetUserFavorite(ctx context.Context, in *GetUserFavoriteReq, opts ...grpc.CallOption) (*GetUserFavoriteResp, error) {
56 | client := userOptPb.NewUserOptServiceClient(m.cli.Conn())
57 | return client.GetUserFavorite(ctx, in, opts...)
58 | }
59 |
60 | func (m *defaultUserOptService) GetUserFollow(ctx context.Context, in *GetUserFollowReq, opts ...grpc.CallOption) (*GetUserFollowResp, error) {
61 | client := userOptPb.NewUserOptServiceClient(m.cli.Conn())
62 | return client.GetUserFollow(ctx, in, opts...)
63 | }
64 |
65 | func (m *defaultUserOptService) UpdateFavoriteStatus(ctx context.Context, in *UpdateFavoriteStatusReq, opts ...grpc.CallOption) (*UpdateFavoriteStatusResp, error) {
66 | client := userOptPb.NewUserOptServiceClient(m.cli.Conn())
67 | return client.UpdateFavoriteStatus(ctx, in, opts...)
68 | }
69 |
70 | func (m *defaultUserOptService) UpdateFollowStatus(ctx context.Context, in *UpdateFollowStatusReq, opts ...grpc.CallOption) (*UpdateFollowStatusResp, error) {
71 | client := userOptPb.NewUserOptServiceClient(m.cli.Conn())
72 | return client.UpdateFollowStatus(ctx, in, opts...)
73 | }
74 |
75 | func (m *defaultUserOptService) UpdateCommentStatus(ctx context.Context, in *UpdateCommentStatusReq, opts ...grpc.CallOption) (*UpdateCommentStatusResp, error) {
76 | client := userOptPb.NewUserOptServiceClient(m.cli.Conn())
77 | return client.UpdateCommentStatus(ctx, in, opts...)
78 | }
79 |
80 | func (m *defaultUserOptService) GetVideoComment(ctx context.Context, in *GetVideoCommentReq, opts ...grpc.CallOption) (*GetVideoCommentReqResp, error) {
81 | client := userOptPb.NewUserOptServiceClient(m.cli.Conn())
82 | return client.GetVideoComment(ctx, in, opts...)
83 | }
84 |
85 | func (m *defaultUserOptService) GetUserFollower(ctx context.Context, in *GetUserFollowerReq, opts ...grpc.CallOption) (*GetUserFollowerResp, error) {
86 | client := userOptPb.NewUserOptServiceClient(m.cli.Conn())
87 | return client.GetUserFollower(ctx, in, opts...)
88 | }
89 |
--------------------------------------------------------------------------------
/service/rpc-video-service/etc/videoService.yaml:
--------------------------------------------------------------------------------
1 | Name: videoservice.rpc
2 | ListenOn: 0.0.0.0:9999
3 |
4 | Mode: dev
5 |
6 | Etcd:
7 | Hosts:
8 | - 127.0.0.1:33333
9 | Key: videoservice.rpc
10 | Log:
11 | Encoding: plain
12 | DB:
13 | DataSource: username:password@tcp(localhost:3306)/douyin2?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
14 |
15 | CacheConf:
16 | - Host: localhost:6379
17 | Pass: password
18 |
--------------------------------------------------------------------------------
/service/rpc-video-service/internal/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "github.com/zeromicro/go-zero/core/stores/cache"
5 | "github.com/zeromicro/go-zero/zrpc"
6 | )
7 |
8 | type Config struct {
9 | zrpc.RpcServerConf
10 | DB struct {
11 | DataSource string
12 | }
13 | CacheConf cache.CacheConf
14 | }
15 |
--------------------------------------------------------------------------------
/service/rpc-video-service/internal/logic/feedVideosLogic.go:
--------------------------------------------------------------------------------
1 | package logic
2 |
3 | import (
4 | "context"
5 | "douyin/common/xerr"
6 | "douyin/service/rpc-video-service/internal/svc"
7 | "douyin/service/rpc-video-service/videoSvcPb"
8 | "github.com/jinzhu/copier"
9 | "github.com/pkg/errors"
10 | "github.com/zeromicro/go-zero/core/logx"
11 | "time"
12 | )
13 |
14 | type FeedVideosLogic struct {
15 | ctx context.Context
16 | svcCtx *svc.ServiceContext
17 | logx.Logger
18 | }
19 |
20 | func NewFeedVideosLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FeedVideosLogic {
21 | return &FeedVideosLogic{
22 | ctx: ctx,
23 | svcCtx: svcCtx,
24 | Logger: logx.WithContext(ctx),
25 | }
26 | }
27 |
28 | func (l *FeedVideosLogic) FeedVideos(in *videoSvcPb.FeedVideosReq) (*videoSvcPb.FeedVideosResp, error) {
29 |
30 | // 转换为数据库里的时间格式
31 | reqTime := time.Unix(in.LastTime, 0)
32 |
33 | logx.Errorf("小于这个时间的视频reqTime: %v", reqTime)
34 |
35 | whereBuilder := l.svcCtx.VideoModel.RowBuilder().Where("create_time < ?", reqTime)
36 | list, err := l.svcCtx.VideoModel.FindPageListByPage(l.ctx, whereBuilder, 1, 10, "create_time DESC")
37 | if err != nil {
38 | return nil, errors.Wrapf(xerr.NewErrMsg("get video list fail"), "get video list fail FindPageListByIdDESC err : %v , lastTime:%d", err, in.LastTime)
39 | }
40 | /*
41 | 如果想重构这里可以用泛型
42 | */
43 | var videos []*videoSvcPb.Video
44 | videos = make([]*videoSvcPb.Video, 0, len(list))
45 | var NextTime int64
46 | if len(list) > 0 {
47 | for _, v := range list {
48 | var video videoSvcPb.Video
49 | _ = copier.Copy(&video, v)
50 | videos = append(videos, &video)
51 | }
52 | // 最早的视频的时间 时间戳最小的
53 | NextTime = list[len(list)-1].CreateTime.Unix()
54 | }
55 |
56 | logx.Errorf("下次请求的视频小于这个时间的视频: %v", list[len(list)-1].CreateTime)
57 |
58 | return &videoSvcPb.FeedVideosResp{
59 | VideoPubList: videos,
60 | NextTime: NextTime,
61 | }, nil
62 | }
63 |
--------------------------------------------------------------------------------
/service/rpc-video-service/internal/logic/getMyFavoriteVideosLogic.go:
--------------------------------------------------------------------------------
1 | package logic
2 |
3 | import (
4 | "context"
5 | "douyin/common/xerr"
6 | "douyin/service/rpc-video-service/model"
7 | "github.com/Masterminds/squirrel"
8 | "github.com/jinzhu/copier"
9 | "github.com/pkg/errors"
10 |
11 | "douyin/service/rpc-video-service/internal/svc"
12 | "douyin/service/rpc-video-service/videoSvcPb"
13 |
14 | "github.com/zeromicro/go-zero/core/logx"
15 | )
16 |
17 | type GetMyFavoriteVideosLogic struct {
18 | ctx context.Context
19 | svcCtx *svc.ServiceContext
20 | logx.Logger
21 | }
22 |
23 | func NewGetMyFavoriteVideosLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetMyFavoriteVideosLogic {
24 | return &GetMyFavoriteVideosLogic{
25 | ctx: ctx,
26 | svcCtx: svcCtx,
27 | Logger: logx.WithContext(ctx),
28 | }
29 | }
30 |
31 | func (l *GetMyFavoriteVideosLogic) GetMyFavoriteVideos(in *videoSvcPb.MyFavoriteVideosReq) (*videoSvcPb.MyFavoriteVideosResp, error) {
32 | whereBuilder := l.svcCtx.VideoModel.RowBuilder().Where(squirrel.Eq{"id": in.VideoIdArr})
33 | list, err := l.svcCtx.VideoModel.FindAll(l.ctx, whereBuilder, "")
34 | if err != nil && err != model.ErrNotFound {
35 | return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "Failed to get video list err : %v , in :%+v", err, in)
36 | }
37 |
38 | /*
39 | 如果想重构这里可以用泛型
40 | */
41 | var resp []*videoSvcPb.Video
42 | if len(list) > 0 {
43 | for _, video := range list {
44 | var pbVideo videoSvcPb.Video
45 | _ = copier.Copy(&pbVideo, video)
46 |
47 | resp = append(resp, &pbVideo)
48 | }
49 | }
50 |
51 | return &videoSvcPb.MyFavoriteVideosResp{
52 | VideoPubList: resp,
53 | }, nil
54 | }
55 |
--------------------------------------------------------------------------------
/service/rpc-video-service/internal/logic/getVideoListLogic.go:
--------------------------------------------------------------------------------
1 | package logic
2 |
3 | import (
4 | "context"
5 | "douyin/common/xerr"
6 | "douyin/service/rpc-video-service/internal/svc"
7 | "douyin/service/rpc-video-service/model"
8 | "douyin/service/rpc-video-service/videoSvcPb"
9 | "github.com/Masterminds/squirrel"
10 | "github.com/jinzhu/copier"
11 | "github.com/pkg/errors"
12 | "github.com/zeromicro/go-zero/core/logx"
13 | )
14 |
15 | type GetVideoListLogic struct {
16 | ctx context.Context
17 | svcCtx *svc.ServiceContext
18 | logx.Logger
19 | }
20 |
21 | func NewGetVideoListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetVideoListLogic {
22 | return &GetVideoListLogic{
23 | ctx: ctx,
24 | svcCtx: svcCtx,
25 | Logger: logx.WithContext(ctx),
26 | }
27 | }
28 |
29 | func (l *GetVideoListLogic) GetVideoList(in *videoSvcPb.GetVideoListReq) (*videoSvcPb.GetVideoListResp, error) {
30 | whereBuilder := l.svcCtx.VideoModel.RowBuilder().Where(squirrel.Eq{"auth_id": in.AuthId})
31 | list, err := l.svcCtx.VideoModel.FindAll(l.ctx, whereBuilder, "")
32 | if err != nil && err != model.ErrNotFound {
33 | return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "Failed to get video list err : %v , in :%+v", err, in)
34 | }
35 |
36 | /*
37 | 如果想重构这里可以用泛型
38 | */
39 | var resp []*videoSvcPb.Video
40 | if len(list) > 0 {
41 | for _, video := range list {
42 | var pbVideo videoSvcPb.Video
43 | _ = copier.Copy(&pbVideo, video)
44 | resp = append(resp, &pbVideo)
45 | }
46 | }
47 |
48 | return &videoSvcPb.GetVideoListResp{
49 | VideoPubList: resp,
50 | }, nil
51 | }
52 |
--------------------------------------------------------------------------------
/service/rpc-video-service/internal/logic/pubVideoLogic.go:
--------------------------------------------------------------------------------
1 | package logic
2 |
3 | import (
4 | "context"
5 | "douyin/service/rpc-video-service/internal/svc"
6 | "douyin/service/rpc-video-service/model"
7 | "douyin/service/rpc-video-service/videoSvcPb"
8 | "github.com/pkg/errors"
9 | "github.com/zeromicro/go-zero/core/logx"
10 | )
11 |
12 | type PubVideoLogic struct {
13 | ctx context.Context
14 | svcCtx *svc.ServiceContext
15 | logx.Logger
16 | }
17 |
18 | func NewPubVideoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PubVideoLogic {
19 | return &PubVideoLogic{
20 | ctx: ctx,
21 | svcCtx: svcCtx,
22 | Logger: logx.WithContext(ctx),
23 | }
24 | }
25 |
26 | // PubVideo -----------------------video-----------------------
27 | func (l *PubVideoLogic) PubVideo(in *videoSvcPb.PubVideoReq) (*videoSvcPb.PubVideoResp, error) {
28 |
29 | _, err := l.svcCtx.VideoModel.Insert(l.ctx, nil, &model.Video{
30 | AuthId: in.AuthId,
31 | Title: in.Title,
32 | PlayURL: in.PlayURL,
33 | CoverURL: in.CoverURL,
34 | })
35 | if err != nil {
36 | return nil, errors.Wrapf(err, " PubVideo insert video fail")
37 | }
38 | return &videoSvcPb.PubVideoResp{}, nil
39 | }
40 |
--------------------------------------------------------------------------------
/service/rpc-video-service/internal/server/videoServiceServer.go:
--------------------------------------------------------------------------------
1 | // Code generated by goctl. DO NOT EDIT!
2 | // Source: VideoService.proto
3 |
4 | package server
5 |
6 | import (
7 | "context"
8 |
9 | "douyin/service/rpc-video-service/internal/logic"
10 | "douyin/service/rpc-video-service/internal/svc"
11 | "douyin/service/rpc-video-service/videoSvcPb"
12 | )
13 |
14 | type VideoServiceServer struct {
15 | svcCtx *svc.ServiceContext
16 | videoSvcPb.UnimplementedVideoServiceServer
17 | }
18 |
19 | func NewVideoServiceServer(svcCtx *svc.ServiceContext) *VideoServiceServer {
20 | return &VideoServiceServer{
21 | svcCtx: svcCtx,
22 | }
23 | }
24 |
25 | // -----------------------video-----------------------
26 | func (s *VideoServiceServer) PubVideo(ctx context.Context, in *videoSvcPb.PubVideoReq) (*videoSvcPb.PubVideoResp, error) {
27 | l := logic.NewPubVideoLogic(ctx, s.svcCtx)
28 | return l.PubVideo(in)
29 | }
30 |
31 | func (s *VideoServiceServer) GetVideoList(ctx context.Context, in *videoSvcPb.GetVideoListReq) (*videoSvcPb.GetVideoListResp, error) {
32 | l := logic.NewGetVideoListLogic(ctx, s.svcCtx)
33 | return l.GetVideoList(in)
34 | }
35 |
36 | func (s *VideoServiceServer) FeedVideos(ctx context.Context, in *videoSvcPb.FeedVideosReq) (*videoSvcPb.FeedVideosResp, error) {
37 | l := logic.NewFeedVideosLogic(ctx, s.svcCtx)
38 | return l.FeedVideos(in)
39 | }
40 |
41 | func (s *VideoServiceServer) GetMyFavoriteVideos(ctx context.Context, in *videoSvcPb.MyFavoriteVideosReq) (*videoSvcPb.MyFavoriteVideosResp, error) {
42 | l := logic.NewGetMyFavoriteVideosLogic(ctx, s.svcCtx)
43 | return l.GetMyFavoriteVideos(in)
44 | }
45 |
--------------------------------------------------------------------------------
/service/rpc-video-service/internal/svc/serviceContext.go:
--------------------------------------------------------------------------------
1 | package svc
2 |
3 | import (
4 | "douyin/service/rpc-video-service/internal/config"
5 | videoModel "douyin/service/rpc-video-service/model"
6 | "github.com/zeromicro/go-zero/core/stores/sqlx"
7 | )
8 |
9 | type ServiceContext struct {
10 | Config config.Config
11 | VideoModel videoModel.VideoModel
12 | }
13 |
14 | func NewServiceContext(c config.Config) *ServiceContext {
15 | return &ServiceContext{
16 | Config: c,
17 | VideoModel: videoModel.NewVideoModel(sqlx.NewMysql(c.DB.DataSource), c.CacheConf),
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/service/rpc-video-service/model/vars.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import "github.com/zeromicro/go-zero/core/stores/sqlx"
4 |
5 | var ErrNotFound = sqlx.ErrNotFound
6 |
--------------------------------------------------------------------------------
/service/rpc-video-service/videoService.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 |
7 | "douyin/service/rpc-video-service/internal/config"
8 | "douyin/service/rpc-video-service/internal/server"
9 | "douyin/service/rpc-video-service/internal/svc"
10 | "douyin/service/rpc-video-service/videoSvcPb"
11 |
12 | "github.com/zeromicro/go-zero/core/conf"
13 | "github.com/zeromicro/go-zero/core/service"
14 | "github.com/zeromicro/go-zero/zrpc"
15 | "google.golang.org/grpc"
16 | "google.golang.org/grpc/reflection"
17 | )
18 |
19 | var configFile = flag.String("f", "etc/videoService.yaml", "the config file")
20 |
21 | func main() {
22 | flag.Parse()
23 |
24 | var c config.Config
25 | conf.MustLoad(*configFile, &c)
26 | ctx := svc.NewServiceContext(c)
27 | svr := server.NewVideoServiceServer(ctx)
28 |
29 | s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
30 | videoSvcPb.RegisterVideoServiceServer(grpcServer, svr)
31 |
32 | if c.Mode == service.DevMode || c.Mode == service.TestMode {
33 | reflection.Register(grpcServer)
34 | }
35 | })
36 | defer s.Stop()
37 |
38 | fmt.Printf("Starting rpc server at %s...\n", c.ListenOn)
39 | s.Start()
40 | }
41 |
--------------------------------------------------------------------------------
/service/rpc-video-service/videoSvcPb/GenPb/VideoService.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | option go_package ="./videoSvcPb";
4 |
5 | package pb;
6 |
7 | // ------------------------------------
8 | // Messages
9 | // ------------------------------------
10 |
11 | //--------------------------------video--------------------------------
12 | message PubVideoReq {
13 | int64 AuthId = 1; //authId
14 | string Title = 2; //title
15 | string PlayURL = 3; //play_url
16 | string CoverURL = 4; //cover_url
17 | }
18 |
19 | message PubVideoResp {
20 | }
21 |
22 | message Video {
23 | int64 Id = 1; //id
24 | string Title = 2; //title
25 | string PlayURL = 3; //play_url
26 | string CoverURL = 4; //cover_url
27 | int64 FavoriteCount = 5; //favorite_count
28 | int64 CommentCount = 6; //comment_count
29 | bool IsFavorite = 7; //is_favorite
30 | int64 AuthId = 8; // auth_id
31 | }
32 |
33 | message GetVideoListReq {
34 | int64 AuthId = 1; //authId
35 | }
36 |
37 | message GetVideoListResp {
38 | repeated Video VideoPubList = 1; //video_list
39 | }
40 |
41 | message FeedVideosReq {
42 | int64 LastTime = 1; // last_time
43 | }
44 | message FeedVideosResp {
45 | repeated Video VideoPubList = 1; //video_list
46 | int64 NextTime = 2; // next_time
47 | }
48 |
49 | message MyFavoriteVideosReq {
50 | repeated int64 VideoIdArr = 1; //videos_id
51 | }
52 | message MyFavoriteVideosResp {
53 | repeated Video VideoPubList = 1; //video_list
54 | }
55 |
56 | // ------------------------------------
57 | // Rpc Func
58 | // ------------------------------------
59 |
60 | service VideoService{
61 | //-----------------------video-----------------------
62 | rpc PubVideo(PubVideoReq) returns (PubVideoResp);
63 | rpc GetVideoList(GetVideoListReq) returns (GetVideoListResp);
64 | rpc FeedVideos(FeedVideosReq) returns (FeedVideosResp);
65 | rpc GetMyFavoriteVideos(MyFavoriteVideosReq) returns (MyFavoriteVideosResp);
66 | }
67 |
--------------------------------------------------------------------------------
/service/rpc-video-service/videoSvcPb/GenPb/build.sh:
--------------------------------------------------------------------------------
1 | goctl rpc protoc VideoService.proto --go_out=../../ --go-grpc_out=../../ --zrpc_out=../../ --style=goZero --home=../../../../tpl
2 | # 当有多个proto文件时 需要先生成其他的文件 再生成主要的pb文件
--------------------------------------------------------------------------------
/service/rpc-video-service/videoservice/videoService.go:
--------------------------------------------------------------------------------
1 | // Code generated by goctl. DO NOT EDIT!
2 | // Source: VideoService.proto
3 |
4 | package videoservice
5 |
6 | import (
7 | "context"
8 |
9 | "douyin/service/rpc-video-service/videoSvcPb"
10 |
11 | "github.com/zeromicro/go-zero/zrpc"
12 | "google.golang.org/grpc"
13 | )
14 |
15 | type (
16 | FeedVideosReq = videoSvcPb.FeedVideosReq
17 | FeedVideosResp = videoSvcPb.FeedVideosResp
18 | GetVideoListReq = videoSvcPb.GetVideoListReq
19 | GetVideoListResp = videoSvcPb.GetVideoListResp
20 | MyFavoriteVideosReq = videoSvcPb.MyFavoriteVideosReq
21 | MyFavoriteVideosResp = videoSvcPb.MyFavoriteVideosResp
22 | PubVideoReq = videoSvcPb.PubVideoReq
23 | PubVideoResp = videoSvcPb.PubVideoResp
24 | Video = videoSvcPb.Video
25 |
26 | VideoService interface {
27 | // -----------------------video-----------------------
28 | PubVideo(ctx context.Context, in *PubVideoReq, opts ...grpc.CallOption) (*PubVideoResp, error)
29 | GetVideoList(ctx context.Context, in *GetVideoListReq, opts ...grpc.CallOption) (*GetVideoListResp, error)
30 | FeedVideos(ctx context.Context, in *FeedVideosReq, opts ...grpc.CallOption) (*FeedVideosResp, error)
31 | GetMyFavoriteVideos(ctx context.Context, in *MyFavoriteVideosReq, opts ...grpc.CallOption) (*MyFavoriteVideosResp, error)
32 | }
33 |
34 | defaultVideoService struct {
35 | cli zrpc.Client
36 | }
37 | )
38 |
39 | func NewVideoService(cli zrpc.Client) VideoService {
40 | return &defaultVideoService{
41 | cli: cli,
42 | }
43 | }
44 |
45 | // -----------------------video-----------------------
46 | func (m *defaultVideoService) PubVideo(ctx context.Context, in *PubVideoReq, opts ...grpc.CallOption) (*PubVideoResp, error) {
47 | client := videoSvcPb.NewVideoServiceClient(m.cli.Conn())
48 | return client.PubVideo(ctx, in, opts...)
49 | }
50 |
51 | func (m *defaultVideoService) GetVideoList(ctx context.Context, in *GetVideoListReq, opts ...grpc.CallOption) (*GetVideoListResp, error) {
52 | client := videoSvcPb.NewVideoServiceClient(m.cli.Conn())
53 | return client.GetVideoList(ctx, in, opts...)
54 | }
55 |
56 | func (m *defaultVideoService) FeedVideos(ctx context.Context, in *FeedVideosReq, opts ...grpc.CallOption) (*FeedVideosResp, error) {
57 | client := videoSvcPb.NewVideoServiceClient(m.cli.Conn())
58 | return client.FeedVideos(ctx, in, opts...)
59 | }
60 |
61 | func (m *defaultVideoService) GetMyFavoriteVideos(ctx context.Context, in *MyFavoriteVideosReq, opts ...grpc.CallOption) (*MyFavoriteVideosResp, error) {
62 | client := videoSvcPb.NewVideoServiceClient(m.cli.Conn())
63 | return client.GetMyFavoriteVideos(ctx, in, opts...)
64 | }
65 |
--------------------------------------------------------------------------------
/tpl/1.3.5/api/config.tpl:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import {{.authImport}}
4 |
5 | type Config struct {
6 | rest.RestConf
7 | {{.auth}}
8 | {{.jwtTrans}}
9 | }
10 |
--------------------------------------------------------------------------------
/tpl/1.3.5/api/context.tpl:
--------------------------------------------------------------------------------
1 | package svc
2 |
3 | import (
4 | {{.configImport}}
5 | )
6 |
7 | type ServiceContext struct {
8 | Config {{.config}}
9 | {{.middleware}}
10 | }
11 |
12 | func NewServiceContext(c {{.config}}) *ServiceContext {
13 | return &ServiceContext{
14 | Config: c,
15 | {{.middlewareAssignment}}
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/tpl/1.3.5/api/etc.tpl:
--------------------------------------------------------------------------------
1 | Name: {{.serviceName}}
2 | Host: {{.host}}
3 | Port: {{.port}}
4 |
5 | Log:
6 | Encoding: plain
7 |
8 | DB:
9 | DataSource: username:password@tcp(localhost:3306)/dbname?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
10 | CacheConf:
11 | - Host: localhost:6379
12 | Pass: Pass
13 | # ----- 一键生成proto文件 sql2pb-----
14 | #sql2pb -go_package ./pb -package pb -host localhost -user user -password password -port 3306 \
15 | # -schema tablename -service_name xxx > xxx.proto
16 | # --- docker ----
17 | # goctl docker -go user.go
18 |
19 | # ---- model ----
20 | # modeldir=..
21 | # # 数据库配置
22 | # host=localhost
23 | # port=3306
24 | # dbname=douyin
25 | # tablename1=user
26 | # tablename2=user_data
27 | # username=username
28 | # passwd=passwd
29 | # echo "开始连接库:$dbname 的表:$tablename1"
30 | # goctl model mysql datasource -url="${username}:${passwd}@tcp(${host}:${port})/${dbname}" -table="${tablename1}" -dir="${modeldir}"
--------------------------------------------------------------------------------
/tpl/1.3.5/api/handler.tpl:
--------------------------------------------------------------------------------
1 | package {{.PkgName}}
2 |
3 | import (
4 | "net/http"
5 |
6 | "github.com/zeromicro/go-zero/rest/httpx"
7 | {{.ImportPackages}}
8 | )
9 |
10 | func {{.HandlerName}}(svcCtx *svc.ServiceContext) http.HandlerFunc {
11 | return func(w http.ResponseWriter, r *http.Request) {
12 | {{if .HasRequest}}var req types.{{.RequestType}}
13 | if err := httpx.Parse(r, &req); err != nil {
14 | httpx.Error(w, err)
15 | return
16 | }
17 |
18 | {{end}}l := {{.LogicName}}.New{{.LogicType}}(r.Context(), svcCtx)
19 | {{if .HasResp}}resp, {{end}}err := l.{{.Call}}({{if .HasRequest}}&req{{end}})
20 | if err != nil {
21 | httpx.Error(w, err)
22 | } else {
23 | {{if .HasResp}}httpx.OkJson(w, resp){{else}}httpx.Ok(w){{end}}
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/tpl/1.3.5/api/logic.tpl:
--------------------------------------------------------------------------------
1 | package {{.pkgName}}
2 |
3 | import (
4 | {{.imports}}
5 | )
6 |
7 | type {{.logic}} struct {
8 | logx.Logger
9 | ctx context.Context
10 | svcCtx *svc.ServiceContext
11 | }
12 |
13 | func New{{.logic}}(ctx context.Context, svcCtx *svc.ServiceContext) *{{.logic}} {
14 | return &{{.logic}}{
15 | Logger: logx.WithContext(ctx),
16 | ctx: ctx,
17 | svcCtx: svcCtx,
18 | }
19 | }
20 |
21 | func (l *{{.logic}}) {{.function}}({{.request}}) {{.responseType}} {
22 | // todo: add your logic here and delete this line
23 |
24 | {{.returnString}}
25 | }
26 |
--------------------------------------------------------------------------------
/tpl/1.3.5/api/main.tpl:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 |
7 | {{.importPackages}}
8 | )
9 |
10 | var configFile = flag.String("f", "etc/{{.serviceName}}.yaml", "the config file")
11 |
12 | func main() {
13 | flag.Parse()
14 |
15 | var c config.Config
16 | conf.MustLoad(*configFile, &c)
17 |
18 | ctx := svc.NewServiceContext(c)
19 | server := rest.MustNewServer(c.RestConf)
20 | defer server.Stop()
21 |
22 | handler.RegisterHandlers(server, ctx)
23 |
24 | fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
25 | server.Start()
26 | }
27 |
--------------------------------------------------------------------------------
/tpl/1.3.5/api/middleware.tpl:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import "net/http"
4 |
5 | type {{.name}} struct {
6 | }
7 |
8 | func New{{.name}}() *{{.name}} {
9 | return &{{.name}}{}
10 | }
11 |
12 | func (m *{{.name}})Handle(next http.HandlerFunc) http.HandlerFunc {
13 | return func(w http.ResponseWriter, r *http.Request) {
14 | // TODO generate middleware implement function, delete after code implementation
15 |
16 | // Passthrough to next handler if need
17 | next(w, r)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/tpl/1.3.5/api/route-addition.tpl:
--------------------------------------------------------------------------------
1 |
2 | server.AddRoutes(
3 | {{.routes}} {{.jwt}}{{.signature}} {{.prefix}} {{.timeout}}
4 | )
5 |
--------------------------------------------------------------------------------
/tpl/1.3.5/api/routes.tpl:
--------------------------------------------------------------------------------
1 | // Code generated by goctl. DO NOT EDIT.
2 | package handler
3 |
4 | import (
5 | "net/http"{{if .hasTimeout}}
6 | "time"{{end}}
7 |
8 | {{.importPackages}}
9 | )
10 |
11 | func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
12 | {{.routesAdditions}}
13 | }
14 |
--------------------------------------------------------------------------------
/tpl/1.3.5/api/template.tpl:
--------------------------------------------------------------------------------
1 | syntax = "v1"
2 |
3 | info (
4 | title: // TODO: add title
5 | desc: // TODO: add description
6 | author: "{{.gitUser}}"
7 | email: "{{.gitEmail}}"
8 | )
9 |
10 | type request {
11 | // TODO: add members here and delete this comment
12 | }
13 |
14 | type response {
15 | // TODO: add members here and delete this comment
16 | }
17 |
18 | service {{.serviceName}} {
19 | @handler GetUser // TODO: set handler name and delete this comment
20 | get /usersInfo/id/:userId(request) returns(response)
21 |
22 | @handler CreateUser // TODO: set handler name and delete this comment
23 | post /usersInfo/create(request)
24 | }
25 |
--------------------------------------------------------------------------------
/tpl/1.3.5/api/types.tpl:
--------------------------------------------------------------------------------
1 | // Code generated by goctl. DO NOT EDIT.
2 | package types{{if .containsTime}}
3 | import (
4 | "time"
5 | ){{end}}
6 | {{.types}}
7 |
--------------------------------------------------------------------------------
/tpl/1.3.5/docker/docker.tpl:
--------------------------------------------------------------------------------
1 | FROM golang:{{.Version}}alpine AS builder
2 |
3 | LABEL stage=gobuilder
4 |
5 | ENV CGO_ENABLED 0
6 | {{if .Chinese}}ENV GOPROXY https://goproxy.cn,direct
7 | {{end}}{{if .HasTimezone}}
8 | RUN apk update --no-cache && apk add --no-cache tzdata
9 | {{end}}
10 | WORKDIR /build
11 |
12 | ADD go.mod .
13 | ADD go.sum .
14 | RUN go mod download
15 | COPY . .
16 | {{if .Argument}}COPY {{.GoRelPath}}/etc /app/etc
17 | {{end}}RUN go build -ldflags="-s -w" -o /app/{{.ExeFile}} {{.GoRelPath}}/{{.GoFile}}
18 |
19 |
20 | FROM {{.BaseImage}}
21 |
22 | COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
23 | {{if .HasTimezone}}COPY --from=builder /usr/share/zoneinfo/{{.Timezone}} /usr/share/zoneinfo/{{.Timezone}}
24 | ENV TZ {{.Timezone}}
25 | {{end}}
26 | WORKDIR /app
27 | COPY --from=builder /app/{{.ExeFile}} /app/{{.ExeFile}}{{if .Argument}}
28 | COPY --from=builder /app/etc /app/etc{{end}}
29 | {{if .HasPort}}
30 | EXPOSE {{.Port}}
31 | {{end}}
32 | CMD ["./{{.ExeFile}}"{{.Argument}}]
33 |
--------------------------------------------------------------------------------
/tpl/1.3.5/kube/deployment.tpl:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: {{.Name}}
5 | namespace: {{.Namespace}}
6 | labels:
7 | app: {{.Name}}
8 | spec:
9 | replicas: {{.Replicas}}
10 | revisionHistoryLimit: {{.Revisions}}
11 | selector:
12 | matchLabels:
13 | app: {{.Name}}
14 | template:
15 | metadata:
16 | labels:
17 | app: {{.Name}}
18 | spec:{{if .ServiceAccount}}
19 | serviceAccountName: {{.ServiceAccount}}{{end}}
20 | containers:
21 | - name: {{.Name}}
22 | image: {{.Image}}
23 | lifecycle:
24 | preStop:
25 | exec:
26 | command: ["sh","-c","sleep 5"]
27 | ports:
28 | - containerPort: {{.Port}}
29 | readinessProbe:
30 | tcpSocket:
31 | port: {{.Port}}
32 | initialDelaySeconds: 5
33 | periodSeconds: 10
34 | livenessProbe:
35 | tcpSocket:
36 | port: {{.Port}}
37 | initialDelaySeconds: 15
38 | periodSeconds: 20
39 | resources:
40 | requests:
41 | cpu: {{.RequestCpu}}m
42 | memory: {{.RequestMem}}Mi
43 | limits:
44 | cpu: {{.LimitCpu}}m
45 | memory: {{.LimitMem}}Mi
46 | volumeMounts:
47 | - name: timezone
48 | mountPath: /etc/localtime
49 | {{if .Secret}}imagePullSecrets:
50 | - name: {{.Secret}}
51 | {{end}}volumes:
52 | - name: timezone
53 | hostPath:
54 | path: /usr/share/zoneinfo/Asia/Shanghai
55 |
56 | ---
57 |
58 | apiVersion: v1
59 | kind: Service
60 | metadata:
61 | name: {{.Name}}-svc
62 | namespace: {{.Namespace}}
63 | spec:
64 | ports:
65 | {{if .UseNodePort}}- nodePort: {{.NodePort}}
66 | port: {{.Port}}
67 | protocol: TCP
68 | targetPort: {{.Port}}
69 | type: NodePort{{else}}- port: {{.Port}}{{end}}
70 | selector:
71 | app: {{.Name}}
72 |
73 | ---
74 |
75 | apiVersion: autoscaling/v2beta1
76 | kind: HorizontalPodAutoscaler
77 | metadata:
78 | name: {{.Name}}-hpa-c
79 | namespace: {{.Namespace}}
80 | labels:
81 | app: {{.Name}}-hpa-c
82 | spec:
83 | scaleTargetRef:
84 | apiVersion: apps/v1
85 | kind: Deployment
86 | name: {{.Name}}
87 | minReplicas: {{.MinReplicas}}
88 | maxReplicas: {{.MaxReplicas}}
89 | metrics:
90 | - type: Resource
91 | resource:
92 | name: cpu
93 | targetAverageUtilization: 80
94 |
95 | ---
96 |
97 | apiVersion: autoscaling/v2beta1
98 | kind: HorizontalPodAutoscaler
99 | metadata:
100 | name: {{.Name}}-hpa-m
101 | namespace: {{.Namespace}}
102 | labels:
103 | app: {{.Name}}-hpa-m
104 | spec:
105 | scaleTargetRef:
106 | apiVersion: apps/v1
107 | kind: Deployment
108 | name: {{.Name}}
109 | minReplicas: {{.MinReplicas}}
110 | maxReplicas: {{.MaxReplicas}}
111 | metrics:
112 | - type: Resource
113 | resource:
114 | name: memory
115 | targetAverageUtilization: 80
116 |
--------------------------------------------------------------------------------
/tpl/1.3.5/kube/job.tpl:
--------------------------------------------------------------------------------
1 | apiVersion: batch/v1
2 | kind: CronJob
3 | metadata:
4 | name: {{.Name}}
5 | namespace: {{.Namespace}}
6 | spec:
7 | successfulJobsHistoryLimit: {{.SuccessfulJobsHistoryLimit}}
8 | schedule: "{{.Schedule}}"
9 | jobTemplate:
10 | spec:
11 | template:
12 | spec:{{if .ServiceAccount}}
13 | serviceAccountName: {{.ServiceAccount}}{{end}}
14 | {{end}}containers:
15 | - name: {{.Name}}
16 | image: # todo image url
17 | resources:
18 | requests:
19 | cpu: {{.RequestCpu}}m
20 | memory: {{.RequestMem}}Mi
21 | limits:
22 | cpu: {{.LimitCpu}}m
23 | memory: {{.LimitMem}}Mi
24 | command:
25 | - ./{{.ServiceName}}
26 | - -f
27 | - ./{{.Name}}.yaml
28 | volumeMounts:
29 | - name: timezone
30 | mountPath: /etc/localtime
31 | imagePullSecrets:
32 | - name: # registry secret, if no, remove this
33 | restartPolicy: OnFailure
34 | volumes:
35 | - name: timezone
36 | hostPath:
37 | path: /usr/share/zoneinfo/Asia/Shanghai
38 |
--------------------------------------------------------------------------------
/tpl/1.3.5/model/delete.tpl:
--------------------------------------------------------------------------------
1 | func (m *default{{.upperStartCamelObject}}Model) Delete(ctx context.Context, session sqlx.Session, {{.lowerStartCamelPrimaryKey}} {{.dataType}}) error {
2 | {{if .withCache}}{{if .containsIndexCache}}data, err:=m.FindOne(ctx, {{.lowerStartCamelPrimaryKey}})
3 | if err!=nil{
4 | return err
5 | }
6 |
7 | {{end}} {{.keys}}
8 | _, err {{if .containsIndexCache}}={{else}}:={{end}} m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
9 | query := fmt.Sprintf("delete from %s where {{.originalPrimaryKey}} = {{if .postgreSql}}$1{{else}}?{{end}}", m.table)
10 | if session!=nil{
11 | return session.ExecCtx(ctx,query, {{.lowerStartCamelPrimaryKey}})
12 | }
13 | return conn.ExecCtx(ctx, query, {{.lowerStartCamelPrimaryKey}})
14 | }, {{.keyValues}}){{else}}query := fmt.Sprintf("delete from %s where {{.originalPrimaryKey}} = {{if .postgreSql}}$1{{else}}?{{end}}", m.table)
15 | if session!=nil{
16 | _,err:= session.ExecCtx(ctx,query, {{.lowerStartCamelPrimaryKey}})
17 | return err
18 | }
19 | _,err:=m.conn.ExecCtx(ctx, query, {{.lowerStartCamelPrimaryKey}}){{end}}
20 | return err
21 | }
22 |
--------------------------------------------------------------------------------
/tpl/1.3.5/model/err.tpl:
--------------------------------------------------------------------------------
1 | package {{.pkg}}
2 |
3 | import "github.com/zeromicro/go-zero/core/stores/sqlx"
4 |
5 | var ErrNotFound = sqlx.ErrNotFound
6 |
7 | const ( // 增加是1 取消是0
8 | ActionADD int64 = 1
9 | ActionCancel int64 = 0
10 | )
11 |
--------------------------------------------------------------------------------
/tpl/1.3.5/model/field.tpl:
--------------------------------------------------------------------------------
1 | {{.name}} {{.type}} {{.tag}} {{if .hasComment}}// {{.comment}}{{end}}
--------------------------------------------------------------------------------
/tpl/1.3.5/model/find-one-by-field-extra-method.tpl:
--------------------------------------------------------------------------------
1 | func (m *default{{.upperStartCamelObject}}Model) formatPrimary(primary interface{}) string {
2 | return fmt.Sprintf("%s%v", {{.primaryKeyLeft}}, primary)
3 | }
4 | func (m *default{{.upperStartCamelObject}}Model) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error {
5 | query := fmt.Sprintf("select %s from %s where {{.originalPrimaryField}} = {{if .postgreSql}}$1{{else}}?{{end}} and del_state = ? limit 1", {{.lowerStartCamelObject}}Rows, m.table )
6 | return conn.QueryRowCtx(ctx, v, query, primary,globalkey.DelStateNo)
7 | }
8 |
--------------------------------------------------------------------------------
/tpl/1.3.5/model/find-one-by-field.tpl:
--------------------------------------------------------------------------------
1 |
2 | func (m *default{{.upperStartCamelObject}}Model) FindOneBy{{.upperField}}(ctx context.Context, {{.in}}) (*{{.upperStartCamelObject}}, error) {
3 | {{if .withCache}}{{.cacheKey}}
4 | var resp {{.upperStartCamelObject}}
5 | err := m.QueryRowIndexCtx(ctx, &resp, {{.cacheKeyVariable}}, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
6 | query := fmt.Sprintf("select %s from %s where {{.originalField}} and del_state = ? limit 1", {{.lowerStartCamelObject}}Rows, m.table)
7 | if err := conn.QueryRowCtx(ctx, &resp, query, {{.lowerStartCamelField}},globalkey.DelStateNo); err != nil {
8 | return nil, err
9 | }
10 | return resp.{{.upperStartCamelPrimaryKey}}, nil
11 | }, m.queryPrimary)
12 | switch err {
13 | case nil:
14 | return &resp, nil
15 | case sqlc.ErrNotFound:
16 | return nil, ErrNotFound
17 | default:
18 | return nil, err
19 | }
20 | }{{else}}var resp {{.upperStartCamelObject}}
21 | query := fmt.Sprintf("select %s from %s where {{.originalField}} and del_state = ? limit 1", {{.lowerStartCamelObject}}Rows, m.table )
22 | err := m.conn.QueryRowCtx(ctx, &resp, query, {{.lowerStartCamelField}},globalkey.DelStateNo)
23 | switch err {
24 | case nil:
25 | return &resp, nil
26 | case sqlc.ErrNotFound:
27 | return nil, ErrNotFound
28 | default:
29 | return nil, err
30 | }
31 | }{{end}}
32 |
33 |
--------------------------------------------------------------------------------
/tpl/1.3.5/model/find-one.tpl:
--------------------------------------------------------------------------------
1 | func (m *default{{.upperStartCamelObject}}Model) FindOne(ctx context.Context, {{.lowerStartCamelPrimaryKey}} {{.dataType}}) (*{{.upperStartCamelObject}}, error) {
2 | {{if .withCache}}{{.cacheKey}}
3 | var resp {{.upperStartCamelObject}}
4 | err := m.QueryRowCtx(ctx, &resp, {{.cacheKeyVariable}}, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error {
5 | query := fmt.Sprintf("select %s from %s where {{.originalPrimaryKey}} = {{if .postgreSql}}$1{{else}}?{{end}} and del_state = ? limit 1", {{.lowerStartCamelObject}}Rows, m.table)
6 | return conn.QueryRowCtx(ctx, v, query, {{.lowerStartCamelPrimaryKey}},globalkey.DelStateNo)
7 | })
8 | switch err {
9 | case nil:
10 | return &resp, nil
11 | case sqlc.ErrNotFound:
12 | return nil, ErrNotFound
13 | default:
14 | return nil, err
15 | }{{else}}query := fmt.Sprintf("select %s from %s where {{.originalPrimaryKey}} = {{if .postgreSql}}$1{{else}}?{{end}} and del_state = ? limit 1", {{.lowerStartCamelObject}}Rows, m.table)
16 | var resp {{.upperStartCamelObject}}
17 | err := m.conn.QueryRowCtx(ctx, &resp, query, {{.lowerStartCamelPrimaryKey}},globalkey.DelStateNo)
18 | switch err {
19 | case nil:
20 | return &resp, nil
21 | case sqlc.ErrNotFound:
22 | return nil, ErrNotFound
23 | default:
24 | return nil, err
25 | }{{end}}
26 | }
27 |
--------------------------------------------------------------------------------
/tpl/1.3.5/model/import-no-cache.tpl:
--------------------------------------------------------------------------------
1 | import (
2 | "context"
3 | "database/sql"
4 | "fmt"
5 | "strings"
6 | {{if .time}}"time"{{end}}
7 |
8 | "douyin/common/globalkey"
9 |
10 | "github.com/zeromicro/go-zero/core/stores/builder"
11 | "github.com/zeromicro/go-zero/core/stores/sqlc"
12 | "github.com/zeromicro/go-zero/core/stores/sqlx"
13 | "github.com/zeromicro/go-zero/core/stringx"
14 | )
15 |
--------------------------------------------------------------------------------
/tpl/1.3.5/model/import.tpl:
--------------------------------------------------------------------------------
1 | import (
2 | "context"
3 | "database/sql"
4 | "fmt"
5 | "strings"
6 | {{if .time}}"time"{{end}}
7 |
8 | "douyin/common/globalkey"
9 |
10 | "github.com/zeromicro/go-zero/core/stores/builder"
11 | "github.com/zeromicro/go-zero/core/stores/cache"
12 | "github.com/zeromicro/go-zero/core/stores/sqlc"
13 | "github.com/zeromicro/go-zero/core/stores/sqlx"
14 | "github.com/zeromicro/go-zero/core/stringx"
15 | )
16 |
--------------------------------------------------------------------------------
/tpl/1.3.5/model/insert.tpl:
--------------------------------------------------------------------------------
1 |
2 | func (m *default{{.upperStartCamelObject}}Model) Insert(ctx context.Context,session sqlx.Session, data *{{.upperStartCamelObject}}) (sql.Result,error) {
3 | // data.DeletedTime = time.Unix(0,0)
4 | {{if .withCache}}{{.keys}}
5 | return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
6 | query := fmt.Sprintf("insert into %s (%s) values ({{.expression}})", m.table, {{.lowerStartCamelObject}}RowsExpectAutoSet)
7 | if session != nil{
8 | return session.ExecCtx(ctx,query,{{.expressionValues}})
9 | }
10 | return conn.ExecCtx(ctx, query, {{.expressionValues}})
11 | }, {{.keyValues}}){{else}}
12 | query := fmt.Sprintf("insert into %s (%s) values ({{.expression}})", m.table, {{.lowerStartCamelObject}}RowsExpectAutoSet)
13 | if session != nil{
14 | return session.ExecCtx(ctx,query,{{.expressionValues}})
15 | }
16 | return m.conn.ExecCtx(ctx, query, {{.expressionValues}}){{end}}
17 | }
18 |
19 | func (m *default{{.upperStartCamelObject}}Model) InsertOrUpdate(ctx context.Context, session sqlx.Session, field string, setStatus string, userId, objId, opt int64) (sql.Result, error) {
20 | return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
21 | query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%d,%d,?) ON DUPLICATE KEY UPDATE %s=?", m.table, field, userId, objId, setStatus)
22 | if session != nil {
23 | return session.ExecCtx(ctx, query, opt, opt)
24 | }
25 | return conn.ExecCtx(ctx, query, opt, opt)
26 | })
27 | }
--------------------------------------------------------------------------------
/tpl/1.3.5/model/interface-delete.tpl:
--------------------------------------------------------------------------------
1 | Delete(ctx context.Context,session sqlx.Session, {{.lowerStartCamelPrimaryKey}} {{.dataType}}) error
2 |
--------------------------------------------------------------------------------
/tpl/1.3.5/model/interface-find-one-by-field.tpl:
--------------------------------------------------------------------------------
1 | FindOneBy{{.upperField}}(ctx context.Context, {{.in}}) (*{{.upperStartCamelObject}}, error)
--------------------------------------------------------------------------------
/tpl/1.3.5/model/interface-find-one.tpl:
--------------------------------------------------------------------------------
1 | FindOne(ctx context.Context, {{.lowerStartCamelPrimaryKey}} {{.dataType}}) (*{{.upperStartCamelObject}}, error)
--------------------------------------------------------------------------------
/tpl/1.3.5/model/interface-insert.tpl:
--------------------------------------------------------------------------------
1 | Insert(ctx context.Context, session sqlx.Session,data *{{.upperStartCamelObject}}) (sql.Result,error)
2 | InsertOrUpdate(ctx context.Context, session sqlx.Session, field string, setStatus string, userId, objId, opt int64) (sql.Result, error)
--------------------------------------------------------------------------------
/tpl/1.3.5/model/interface-update.tpl:
--------------------------------------------------------------------------------
1 | Update(ctx context.Context,session sqlx.Session, data *{{.upperStartCamelObject}}) (sql.Result, error)
2 | UpdateStatus(ctx context.Context,session sqlx.Session, key string, idx string,actionType,id int64) (sql.Result,error)
3 |
--------------------------------------------------------------------------------
/tpl/1.3.5/model/model-gen.tpl:
--------------------------------------------------------------------------------
1 | // Code generated by goctl. DO NOT EDIT!
2 |
3 | package {{.pkg}}
4 | {{.imports}}
5 | {{.vars}}
6 | {{.types}}
7 | {{.new}}
8 | {{.insert}}
9 | {{.find}}
10 | {{.update}}
11 | {{.delete}}
12 | {{.extraMethod}}
13 | {{.tableName}}
14 |
--------------------------------------------------------------------------------
/tpl/1.3.5/model/model-new.tpl:
--------------------------------------------------------------------------------
1 |
2 | func new{{.upperStartCamelObject}}Model(conn sqlx.SqlConn{{if .withCache}}, c cache.CacheConf{{end}}) *default{{.upperStartCamelObject}}Model {
3 | return &default{{.upperStartCamelObject}}Model{
4 | {{if .withCache}}CachedConn: sqlc.NewConn(conn, c){{else}}conn:conn{{end}},
5 | table: {{.table}},
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/tpl/1.3.5/model/table-name.tpl:
--------------------------------------------------------------------------------
1 |
2 | func (m *default{{.upperStartCamelObject}}Model) tableName() string {
3 | return m.table
4 | }
5 |
--------------------------------------------------------------------------------
/tpl/1.3.5/model/tag.tpl:
--------------------------------------------------------------------------------
1 | `db:"{{.field}}"`
--------------------------------------------------------------------------------
/tpl/1.3.5/model/types.tpl:
--------------------------------------------------------------------------------
1 |
2 | type (
3 | {{.lowerStartCamelObject}}Model interface{
4 | {{.method}}
5 | }
6 |
7 | default{{.upperStartCamelObject}}Model struct {
8 | {{if .withCache}}sqlc.CachedConn{{else}}conn sqlx.SqlConn{{end}}
9 | table string
10 | }
11 |
12 | {{.upperStartCamelObject}} struct {
13 | {{.fields}}
14 | }
15 | )
16 |
--------------------------------------------------------------------------------
/tpl/1.3.5/model/update.tpl:
--------------------------------------------------------------------------------
1 |
2 | func (m *default{{.upperStartCamelObject}}Model) Update(ctx context.Context,session sqlx.Session, data *{{.upperStartCamelObject}}) (sql.Result,error) {
3 | {{if .withCache}}{{.keys}}
4 | return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
5 | query := fmt.Sprintf("update %s set %s where {{.originalPrimaryKey}} = {{if .postgreSql}}$1{{else}}?{{end}}", m.table, {{.lowerStartCamelObject}}RowsWithPlaceHolder)
6 | if session != nil{
7 | return session.ExecCtx(ctx,query, {{.expressionValues}})
8 | }
9 | return conn.ExecCtx(ctx, query, {{.expressionValues}})
10 | }, {{.keyValues}}){{else}}query := fmt.Sprintf("update %s set %s where {{.originalPrimaryKey}} = {{if .postgreSql}}$1{{else}}?{{end}}", m.table, {{.lowerStartCamelObject}}RowsWithPlaceHolder)
11 | if session != nil{
12 | return session.ExecCtx(ctx,query, {{.expressionValues}})
13 | }
14 | return m.conn.ExecCtx(ctx, query, {{.expressionValues}}){{end}}
15 | }
16 |
17 | func (m *default{{.upperStartCamelObject}}Model) UpdateStatus(ctx context.Context,session sqlx.Session, key string, idx string,actionType,id int64) (sql.Result,error) {
18 |
19 | return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
20 | query := fmt.Sprintf("UPDATE %s SET %s=%s+? WHERE %s=?", m.table, key, key, idx)
21 | if session != nil {
22 | return session.ExecCtx(ctx, query, actionType, id)
23 | }
24 | return conn.ExecCtx(ctx, query, actionType, id)
25 | })
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/tpl/1.3.5/model/var.tpl:
--------------------------------------------------------------------------------
1 |
2 | var (
3 | {{.lowerStartCamelObject}}FieldNames = builder.RawFieldNames(&{{.upperStartCamelObject}}{}{{if .postgreSql}},true{{end}})
4 | {{.lowerStartCamelObject}}Rows = strings.Join({{.lowerStartCamelObject}}FieldNames, ",")
5 | {{.lowerStartCamelObject}}RowsExpectAutoSet = {{if .postgreSql}}strings.Join(stringx.Remove({{.lowerStartCamelObject}}FieldNames, {{if .autoIncrement}}"{{.originalPrimaryKey}}",{{end}} "create_time", "update_time"), ","){{else}}strings.Join(stringx.Remove({{.lowerStartCamelObject}}FieldNames, {{if .autoIncrement}}"{{.originalPrimaryKey}}",{{end}} "`create_time`", "`update_time`"), ","){{end}}
6 | {{.lowerStartCamelObject}}RowsWithPlaceHolder = {{if .postgreSql}}builder.PostgreSqlJoin(stringx.Remove({{.lowerStartCamelObject}}FieldNames, "{{.originalPrimaryKey}}", "create_time", "update_time")){{else}}strings.Join(stringx.Remove({{.lowerStartCamelObject}}FieldNames, "{{.originalPrimaryKey}}", "`create_time`", "`update_time`"), "=?,") + "=?"{{end}}
7 |
8 | {{if .withCache}}{{.cacheKeys}}{{end}}
9 | )
10 |
11 |
--------------------------------------------------------------------------------
/tpl/1.3.5/mongo/err.tpl:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import "errors"
4 |
5 | var ErrNotFound = errors.New("not found")
6 | var ErrInvalidObjectId = errors.New("invalid objectId")
7 |
--------------------------------------------------------------------------------
/tpl/1.3.5/mongo/model.tpl:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/globalsign/mgo/bson"
7 | {{if .CacheConf}}cachec "github.com/zeromicro/go-zero/core/stores/cache"
8 | "github.com/zeromicro/go-zero/core/stores/mongoc"{{else}}"github.com/zeromicro/go-zero/core/stores/mongo"{{end}}
9 | )
10 |
11 | {{if .CacheConf}}var prefix{{.Type}}CacheKey = "cache:{{.Type}}:"{{end}}
12 |
13 | type {{.Type}}Model interface{
14 | Insert(ctx context.Context,data *{{.Type}}) error
15 | FindOne(ctx context.Context,id string) (*{{.Type}}, error)
16 | Update(ctx context.Context,data *{{.Type}}) error
17 | Delete(ctx context.Context,id string) error
18 | }
19 |
20 | type default{{.Type}}Model struct {
21 | {{if .CacheConf}}*mongoc.Model{{else}}*mongo.Model{{end}}
22 | }
23 |
24 | func New{{.Type}}Model(url, collection string{{if .CacheConf}}, c cachec.CacheConf{{end}}) {{.Type}}Model {
25 | return &default{{.Type}}Model{
26 | Model: {{if .CacheConf}}mongoc.MustNewModel(url, collection, c){{else}}mongo.MustNewModel(url, collection){{end}},
27 | }
28 | }
29 |
30 |
31 | func (m *default{{.Type}}Model) Insert(ctx context.Context, data *{{.Type}}) error {
32 | if !data.ID.Valid() {
33 | data.ID = bson.NewObjectId()
34 | }
35 |
36 | session, err := m.TakeSession()
37 | if err != nil {
38 | return err
39 | }
40 |
41 | defer m.PutSession(session)
42 | return m.GetCollection(session).Insert(data)
43 | }
44 |
45 | func (m *default{{.Type}}Model) FindOne(ctx context.Context, id string) (*{{.Type}}, error) {
46 | if !bson.IsObjectIdHex(id) {
47 | return nil, ErrInvalidObjectId
48 | }
49 |
50 | session, err := m.TakeSession()
51 | if err != nil {
52 | return nil, err
53 | }
54 |
55 | defer m.PutSession(session)
56 | var data {{.Type}}
57 | {{if .CacheConf}}key := prefix{{.Type}}CacheKey + id
58 | err = m.GetCollection(session).FindOneId(&data, key, bson.ObjectIdHex(id))
59 | {{- else}}
60 | err = m.GetCollection(session).FindId(bson.ObjectIdHex(id)).One(&data)
61 | {{- end}}
62 | switch err {
63 | case nil:
64 | return &data,nil
65 | case {{if .CacheConf}}mongoc.ErrNotFound{{else}}mongo.ErrNotFound{{end}}:
66 | return nil,ErrNotFound
67 | default:
68 | return nil,err
69 | }
70 | }
71 |
72 | func (m *default{{.Type}}Model) Update(ctx context.Context, data *{{.Type}}) error {
73 | session, err := m.TakeSession()
74 | if err != nil {
75 | return err
76 | }
77 |
78 | defer m.PutSession(session)
79 | {{if .CacheConf}}key := prefix{{.Type}}CacheKey + data.ID.Hex()
80 | return m.GetCollection(session).UpdateId(data.ID, data, key)
81 | {{- else}}
82 | return m.GetCollection(session).UpdateId(data.ID, data)
83 | {{- end}}
84 | }
85 |
86 | func (m *default{{.Type}}Model) Delete(ctx context.Context, id string) error {
87 | session, err := m.TakeSession()
88 | if err != nil {
89 | return err
90 | }
91 |
92 | defer m.PutSession(session)
93 | {{if .CacheConf}}key := prefix{{.Type}}CacheKey + id
94 | return m.GetCollection(session).RemoveId(bson.ObjectIdHex(id), key)
95 | {{- else}}
96 | return m.GetCollection(session).RemoveId(bson.ObjectIdHex(id))
97 | {{- end}}
98 | }
99 |
--------------------------------------------------------------------------------
/tpl/1.3.5/newapi/newtemplate.tpl:
--------------------------------------------------------------------------------
1 | type Request {
2 | Name string `path:"name,options=you|me"`
3 | }
4 |
5 | type Response {
6 | Message string `json:"message"`
7 | }
8 |
9 | service {{.name}}-api {
10 | @handler {{.handler}}Handler
11 | get /from/:name(Request) returns (Response)
12 | }
13 |
--------------------------------------------------------------------------------
/tpl/1.3.5/rpc/call-func.tpl:
--------------------------------------------------------------------------------
1 |
2 | {{if .hasComment}}{{.comment}}{{end}}
3 | func (m *default{{.serviceName}}) {{.method}}(ctx context.Context{{if .hasReq}}, in *{{.pbRequest}}{{end}}, opts ...grpc.CallOption) ({{if .notStream}}*{{.pbResponse}}, {{else}}{{.streamBody}},{{end}} error) {
4 | client := {{if .isCallPkgSameToGrpcPkg}}{{else}}{{.package}}.{{end}}New{{.rpcServiceName}}Client(m.cli.Conn())
5 | return client.{{.method}}(ctx{{if .hasReq}}, in{{end}}, opts...)
6 | }
7 |
--------------------------------------------------------------------------------
/tpl/1.3.5/rpc/call-interface-func.tpl:
--------------------------------------------------------------------------------
1 | {{if .hasComment}}{{.comment}}
2 | {{end}}{{.method}}(ctx context.Context{{if .hasReq}}, in *{{.pbRequest}}{{end}}, opts ...grpc.CallOption) ({{if .notStream}}*{{.pbResponse}}, {{else}}{{.streamBody}},{{end}} error)
--------------------------------------------------------------------------------
/tpl/1.3.5/rpc/call.tpl:
--------------------------------------------------------------------------------
1 | {{.head}}
2 |
3 | package {{.filePackage}}
4 |
5 | import (
6 | "context"
7 |
8 | {{.pbPackage}}
9 | {{if ne .pbPackage .protoGoPackage}}{{.protoGoPackage}}{{end}}
10 |
11 | "github.com/zeromicro/go-zero/zrpc"
12 | "google.golang.org/grpc"
13 | )
14 |
15 | type (
16 | {{.alias}}
17 |
18 | {{.serviceName}} interface {
19 | {{.interface}}
20 | }
21 |
22 | default{{.serviceName}} struct {
23 | cli zrpc.Client
24 | }
25 | )
26 |
27 | func New{{.serviceName}}(cli zrpc.Client) {{.serviceName}} {
28 | return &default{{.serviceName}}{
29 | cli: cli,
30 | }
31 | }
32 |
33 | {{.functions}}
34 |
--------------------------------------------------------------------------------
/tpl/1.3.5/rpc/config.tpl:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import "github.com/zeromicro/go-zero/zrpc"
4 |
5 | type Config struct {
6 | zrpc.RpcServerConf
7 | }
8 |
--------------------------------------------------------------------------------
/tpl/1.3.5/rpc/etc.tpl:
--------------------------------------------------------------------------------
1 | Name: {{.serviceName}}.rpc
2 | ListenOn: 0.0.0.0:9999
3 | # Mode 使用 dev可以通过 grpcui -plaintext localhost:9999 进行调试
4 | Mode: dev
5 | #注册中心
6 | Etcd:
7 | Hosts:
8 | - 127.0.0.1:2379
9 | Key: {{.serviceName}}.rpc
10 | #如果api不通过etcd,而是直接连接rpc服务
11 | #注意是rpc客户端不是rpc服务端需要配置 Endpoints:
12 | # - 127.0.0.1:9999
13 | Log:
14 | Encoding: plain
15 |
16 | DB:
17 | DataSource: username:password@tcp(localhost:3306)/dbname?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
18 | CacheConf:
19 | - Host: localhost:6379
20 | Pass: Pass
21 | # ----- 一键生成proto文件 sql2pb-----
22 | #sql2pb -go_package ./pb -package pb -host localhost -user user -password password -port 3306 \
23 | # -schema tablename -service_name xxx > xxx.proto
24 | # --- docker ----
25 | # goctl docker -go user.go
26 |
27 | # ---- model ----
28 | # modeldir=..
29 | # # 数据库配置
30 | # host=localhost
31 | # port=3306
32 | # dbname=douyin
33 | # tablename=user
34 | # username=username
35 | # passwd=passwd
36 | # echo "开始连接库:$dbname 的表:$tablename"
37 | # goctl model mysql datasource -url="${username}:${passwd}@tcp(${host}:${port})/${dbname}" -table="${tablename}" -dir="${modeldir}"
38 |
--------------------------------------------------------------------------------
/tpl/1.3.5/rpc/logic-func.tpl:
--------------------------------------------------------------------------------
1 | {{if .hasComment}}{{.comment}}{{end}}
2 | func (l *{{.logicName}}) {{.method}} ({{if .hasReq}}in {{.request}}{{if .stream}},stream {{.streamBody}}{{end}}{{else}}stream {{.streamBody}}{{end}}) ({{if .hasReply}}{{.response}},{{end}} error) {
3 | // todo: add your logic here and delete this line
4 |
5 | return {{if .hasReply}}&{{.responseType}}{},{{end}} nil
6 | }
7 |
--------------------------------------------------------------------------------
/tpl/1.3.5/rpc/logic.tpl:
--------------------------------------------------------------------------------
1 | package logic
2 |
3 | import (
4 | "context"
5 |
6 | {{.imports}}
7 |
8 | "github.com/zeromicro/go-zero/core/logx"
9 | )
10 |
11 | type {{.logicName}} struct {
12 | ctx context.Context
13 | svcCtx *svc.ServiceContext
14 | logx.Logger
15 | }
16 |
17 | func New{{.logicName}}(ctx context.Context,svcCtx *svc.ServiceContext) *{{.logicName}} {
18 | return &{{.logicName}}{
19 | ctx: ctx,
20 | svcCtx: svcCtx,
21 | Logger: logx.WithContext(ctx),
22 | }
23 | }
24 | {{.functions}}
25 |
--------------------------------------------------------------------------------
/tpl/1.3.5/rpc/main.tpl:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 |
7 | {{.imports}}
8 |
9 | "github.com/zeromicro/go-zero/core/conf"
10 | "github.com/zeromicro/go-zero/core/service"
11 | "github.com/zeromicro/go-zero/zrpc"
12 | "google.golang.org/grpc"
13 | "google.golang.org/grpc/reflection"
14 | )
15 |
16 | var configFile = flag.String("f", "etc/{{.serviceName}}.yaml", "the config file")
17 |
18 | func main() {
19 | flag.Parse()
20 |
21 | var c config.Config
22 | conf.MustLoad(*configFile, &c)
23 | ctx := svc.NewServiceContext(c)
24 | svr := server.New{{.serviceNew}}Server(ctx)
25 |
26 | s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
27 | {{.pkg}}.Register{{.service}}Server(grpcServer, svr)
28 |
29 | if c.Mode == service.DevMode || c.Mode == service.TestMode {
30 | reflection.Register(grpcServer)
31 | }
32 | })
33 | defer s.Stop()
34 |
35 | fmt.Printf("Starting rpc server at %s...\n", c.ListenOn)
36 | s.Start()
37 | }
38 |
--------------------------------------------------------------------------------
/tpl/1.3.5/rpc/server-func.tpl:
--------------------------------------------------------------------------------
1 |
2 | {{if .hasComment}}{{.comment}}{{end}}
3 | func (s *{{.server}}Server) {{.method}} ({{if .notStream}}ctx context.Context,{{if .hasReq}} in {{.request}}{{end}}{{else}}{{if .hasReq}} in {{.request}},{{end}}stream {{.streamBody}}{{end}}) ({{if .notStream}}{{.response}},{{end}}error) {
4 | l := logic.New{{.logicName}}({{if .notStream}}ctx,{{else}}stream.Context(),{{end}}s.svcCtx)
5 | return l.{{.method}}({{if .hasReq}}in{{if .stream}} ,stream{{end}}{{else}}{{if .stream}}stream{{end}}{{end}})
6 | }
7 |
--------------------------------------------------------------------------------
/tpl/1.3.5/rpc/server.tpl:
--------------------------------------------------------------------------------
1 | {{.head}}
2 |
3 | package server
4 |
5 | import (
6 | {{if .notStream}}"context"{{end}}
7 |
8 | {{.imports}}
9 | )
10 |
11 | type {{.server}}Server struct {
12 | svcCtx *svc.ServiceContext
13 | {{.unimplementedServer}}
14 | }
15 |
16 | func New{{.server}}Server(svcCtx *svc.ServiceContext) *{{.server}}Server {
17 | return &{{.server}}Server{
18 | svcCtx: svcCtx,
19 | }
20 | }
21 |
22 | {{.funcs}}
23 |
--------------------------------------------------------------------------------
/tpl/1.3.5/rpc/svc.tpl:
--------------------------------------------------------------------------------
1 | package svc
2 |
3 | import {{.imports}}
4 |
5 | type ServiceContext struct {
6 | Config config.Config
7 | }
8 |
9 | func NewServiceContext(c config.Config) *ServiceContext {
10 | return &ServiceContext{
11 | Config:c,
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/tpl/1.3.5/rpc/template.tpl:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package {{.package}};
4 | option go_package="./{{.package}}";
5 |
6 | message Request {
7 | string ping = 1;
8 | }
9 |
10 | message Response {
11 | string pong = 1;
12 | }
13 |
14 | service {{.serviceName}} {
15 | rpc Ping(Request) returns(Response);
16 | }
17 |
--------------------------------------------------------------------------------