├── .gitignore ├── LICENSE ├── README.md ├── deploy └── config │ ├── db_init │ └── init.sql │ ├── logstash.conf │ ├── logstash.yml │ ├── otel-collector-config.yaml │ └── prometheus.yml ├── docker-compose.yaml ├── docs ├── award.png ├── nacos.png └── project.png ├── scripts ├── build-bin.sh ├── build-dockerfile.sh ├── build-volumes.sh └── init_go_work.sh ├── start.sh ├── test ├── filetype_test.go └── sentinel_test.go ├── toktik-api ├── Dockerfile ├── config │ ├── bootstrap.yaml.template │ └── config.yaml.template ├── go.mod ├── go.sum ├── internal │ ├── api │ │ ├── api.go │ │ ├── chat │ │ │ ├── chat.go │ │ │ ├── route.go │ │ │ └── rpc_client.go │ │ ├── comment │ │ │ ├── comment.go │ │ │ ├── route.go │ │ │ └── rpc_client.go │ │ ├── favor │ │ │ ├── favor.go │ │ │ ├── route.go │ │ │ └── rpc_client.go │ │ ├── interaction │ │ │ ├── interaction.go │ │ │ ├── route.go │ │ │ └── rpc_client.go │ │ ├── user │ │ │ ├── route.go │ │ │ ├── rpc_client.go │ │ │ └── user.go │ │ └── video │ │ │ ├── route.go │ │ │ ├── rpc_client.go │ │ │ └── video.go │ ├── global │ │ ├── root.go │ │ └── var.go │ └── model │ │ ├── auto │ │ ├── base.go │ │ ├── chat.go │ │ ├── comment.go │ │ ├── favorite.go │ │ ├── relation.go │ │ ├── user.go │ │ ├── user_count.go │ │ └── video.go │ │ ├── config │ │ └── config.go │ │ ├── constants.go │ │ ├── request │ │ ├── chat.go │ │ ├── comment.go │ │ ├── favor.go │ │ ├── interaction.go │ │ ├── user.go │ │ └── video.go │ │ └── response │ │ ├── chat.go │ │ ├── comment.go │ │ ├── favor.go │ │ ├── interaction.go │ │ ├── user.go │ │ └── video.go ├── main.go ├── pkg │ ├── middleware │ │ ├── auth.go │ │ └── limiter.go │ ├── myerr │ │ └── myerr.go │ ├── router │ │ └── router.go │ ├── sentinel │ │ └── sentinel.go │ ├── setting │ │ ├── config.go │ │ ├── enter.go │ │ ├── logger.go │ │ ├── maker.go │ │ └── nacos.go │ └── tracing │ │ └── jaeger.go └── storage │ └── applogs │ ├── error.log │ └── info.log ├── toktik-chat ├── Dockerfile ├── config │ ├── bootstrap.yaml.template │ └── config.yaml.template ├── go.mod ├── internal │ ├── cache │ │ ├── chat.go │ │ └── redis.go │ ├── dao │ │ ├── cron │ │ │ └── chatcron.go │ │ ├── enter.go │ │ └── mysql │ │ │ ├── chat.go │ │ │ ├── db.go │ │ │ └── tran.go │ ├── global │ │ ├── root.go │ │ └── var.go │ ├── model │ │ ├── auto │ │ │ ├── base.go │ │ │ └── chat.go │ │ ├── config │ │ │ └── config.go │ │ ├── constants.go │ │ └── handler_resp.go │ ├── repo │ │ ├── cache.go │ │ ├── chat.go │ │ └── handler_resp.go │ └── service │ │ └── handler.go ├── main.go └── pkg │ ├── myerr │ └── myerr.go │ ├── rpc │ ├── client │ │ ├── client.go │ │ └── interaction.go │ └── register.go │ └── setting │ ├── config.go │ ├── dao.go │ ├── enter.go │ ├── logger.go │ └── nacos.go ├── toktik-comment ├── Dockerfile ├── config │ ├── bootstrap.yaml.template │ └── config.yaml.template ├── go.mod ├── internal │ ├── cache │ │ ├── redis.go │ │ └── video.go │ ├── dao │ │ ├── enter.go │ │ └── mysql │ │ │ ├── db.go │ │ │ ├── tran.go │ │ │ └── video.go │ ├── global │ │ ├── root.go │ │ └── var.go │ ├── model │ │ ├── auto │ │ │ ├── base.go │ │ │ └── comment.go │ │ ├── config │ │ │ └── config.go │ │ ├── constants.go │ │ └── handler_resp.go │ ├── repo │ │ ├── cache.go │ │ ├── handler_resp.go │ │ └── video.go │ └── service │ │ └── handler.go ├── main.go └── pkg │ ├── myerr │ └── myerr.go │ ├── rpc │ ├── client │ │ ├── client.go │ │ ├── user.go │ │ └── video.go │ └── register.go │ └── setting │ ├── config.go │ ├── dao.go │ ├── enter.go │ ├── logger.go │ ├── nacos.go │ └── snowflake.go ├── toktik-common ├── errcode │ ├── common.go │ └── errcode.go ├── go.mod ├── go.sum ├── kk │ ├── kafka_test.go │ ├── receiver.go │ └── sender.go ├── limiter │ ├── api │ │ ├── limit.go │ │ └── limit_test.go │ └── bucket │ │ ├── limiter.go │ │ ├── prefixTree.go │ │ ├── prefix_limiter.go │ │ └── prefix_limiter_test.go ├── logger │ └── logger.go ├── oss │ ├── aliyun │ │ ├── aliyun.go │ │ ├── objectKey.go │ │ ├── upload.go │ │ └── url.go │ └── oss.go ├── page │ └── page.go ├── response │ └── response.go ├── rpc-middleware │ └── rpc-middleware.go ├── serveHTTP │ └── run.go ├── setting │ └── setting.go ├── timing_job │ └── gocron.go ├── token │ ├── maker.go │ ├── model.go │ ├── paseto_maker.go │ └── payload.go └── utils │ ├── convent.go │ ├── ffmpeg.go │ ├── ip.go │ ├── password.go │ └── snowflake.go ├── toktik-favor ├── Dockerfile ├── config │ ├── bootstrap.yaml.template │ └── config.yaml.template ├── go.mod ├── go.sum ├── internal │ ├── cache │ │ ├── favor.go │ │ └── redis.go │ ├── dao │ │ ├── cron │ │ │ └── favor.go │ │ ├── enter.go │ │ └── mysql │ │ │ ├── db.go │ │ │ ├── favor.go │ │ │ └── tran.go │ ├── global │ │ ├── root.go │ │ └── var.go │ ├── model │ │ ├── auto │ │ │ ├── base.go │ │ │ └── favorite.go │ │ ├── config │ │ │ └── config.go │ │ ├── constants.go │ │ └── handler_resp.go │ ├── repo │ │ ├── cache.go │ │ ├── favor.go │ │ └── handler_resp.go │ └── service │ │ └── handler.go ├── main.go └── pkg │ ├── myerr │ └── myerr.go │ ├── rpc │ ├── client │ │ ├── client.go │ │ ├── user.go │ │ └── video.go │ └── register.go │ └── setting │ ├── config.go │ ├── dao.go │ ├── enter.go │ ├── logger.go │ └── nacos.go ├── toktik-interaction ├── Dockerfile ├── config │ ├── bootstrap.yaml.template │ └── config.yaml.template ├── go.mod ├── internal │ ├── cache │ │ ├── intseraction.go │ │ └── redis.go │ ├── dao │ │ ├── enter.go │ │ └── mysql │ │ │ ├── db.go │ │ │ ├── interaction.go │ │ │ └── tran.go │ ├── global │ │ ├── root.go │ │ └── var.go │ ├── model │ │ ├── auto │ │ │ ├── base.go │ │ │ └── relation.go │ │ ├── config │ │ │ └── config.go │ │ ├── constants.go │ │ └── handler_resp.go │ ├── repo │ │ ├── cache.go │ │ ├── handler_resp.go │ │ └── interaction.go │ └── service │ │ └── handler.go ├── main.go ├── pkg │ ├── myerr │ │ └── myerr.go │ ├── rpc │ │ ├── client │ │ │ ├── chat.go │ │ │ ├── client.go │ │ │ └── user.go │ │ └── register.go │ └── setting │ │ ├── config.go │ │ ├── dao.go │ │ ├── enter.go │ │ ├── logger.go │ │ └── nacos.go └── storage │ └── applogs │ └── error.log ├── toktik-rpc ├── go.mod ├── idl │ ├── chat.proto │ ├── comment.proto │ ├── favor.proto │ ├── interaction.proto │ ├── user.proto │ └── video.proto ├── kitex_gen │ ├── chat │ │ ├── chat.pb.fast.go │ │ ├── chat.pb.go │ │ └── chatservice │ │ │ ├── chatservice.go │ │ │ ├── client.go │ │ │ ├── invoker.go │ │ │ └── server.go │ ├── comment │ │ ├── comment.pb.fast.go │ │ ├── comment.pb.go │ │ └── commentservice │ │ │ ├── client.go │ │ │ ├── commentservice.go │ │ │ ├── invoker.go │ │ │ └── server.go │ ├── favor │ │ ├── favor.pb.fast.go │ │ ├── favor.pb.go │ │ └── favorservice │ │ │ ├── client.go │ │ │ ├── favorservice.go │ │ │ ├── invoker.go │ │ │ └── server.go │ ├── interaction │ │ ├── interaction.pb.fast.go │ │ ├── interaction.pb.go │ │ └── interactionservice │ │ │ ├── client.go │ │ │ ├── interactionservice.go │ │ │ ├── invoker.go │ │ │ └── server.go │ ├── user │ │ ├── user.pb.fast.go │ │ ├── user.pb.go │ │ └── userservice │ │ │ ├── client.go │ │ │ ├── invoker.go │ │ │ ├── server.go │ │ │ └── userservice.go │ └── video │ │ ├── video.pb.fast.go │ │ ├── video.pb.go │ │ └── videoservice │ │ ├── client.go │ │ ├── invoker.go │ │ ├── server.go │ │ └── videoservice.go └── zgen.sh ├── toktik-user ├── Dockerfile ├── config │ ├── bootstrap.yaml.template │ └── config.yaml.template ├── go.mod ├── go.sum ├── internal │ ├── cache │ │ ├── redis.go │ │ └── user.go │ ├── dao │ │ ├── cron │ │ │ └── usercron.go │ │ ├── enter.go │ │ └── mysql │ │ │ ├── db.go │ │ │ ├── tran.go │ │ │ └── user.go │ ├── global │ │ ├── root.go │ │ └── var.go │ ├── model │ │ ├── auto │ │ │ ├── base.go │ │ │ ├── user.go │ │ │ ├── user_count.go │ │ │ └── user_test.go │ │ ├── config │ │ │ └── config.go │ │ ├── constants.go │ │ └── handler_resp.go │ ├── repo │ │ ├── cache.go │ │ ├── handler_resp.go │ │ └── user.go │ └── service │ │ ├── handler.go │ │ └── token.go ├── main.go ├── pkg │ ├── myerr │ │ └── myerr.go │ ├── rpc │ │ ├── client │ │ │ ├── client.go │ │ │ ├── interaction.go │ │ │ └── video.go │ │ └── register.go │ └── setting │ │ ├── config.go │ │ ├── dao.go │ │ ├── enter.go │ │ ├── logger.go │ │ ├── maker.go │ │ ├── nacos.go │ │ └── snowflake.go └── storage │ └── applogs │ └── error.log └── toktik-video ├── Dockerfile ├── config ├── bootstrap.yaml.template └── config.yaml.template ├── go.mod ├── internal ├── cache │ ├── redis.go │ └── video.go ├── dao │ ├── cron │ │ └── videocron.go │ ├── enter.go │ └── mysql │ │ ├── db.go │ │ ├── tran.go │ │ └── video.go ├── global │ ├── root.go │ └── var.go ├── model │ ├── auto │ │ ├── base.go │ │ └── video.go │ ├── config │ │ └── config.go │ ├── constants.go │ └── handler_resp.go ├── repo │ ├── cache.go │ ├── handler_resp.go │ └── video.go └── service │ └── handler.go ├── main.go ├── pkg ├── myerr │ └── myerr.go ├── rpc │ ├── client │ │ ├── client.go │ │ ├── favor.go │ │ ├── interaction.go │ │ └── user.go │ └── register.go └── setting │ ├── config.go │ ├── dao.go │ ├── enter.go │ ├── logger.go │ ├── nacos.go │ ├── oss.go │ └── snowflake.go └── storage ├── applogs └── error.log └── videos ├── 兰亭序.mp4 ├── 可爱女人.mp4 ├── 星狗.mp4 └── 胡歌.mp4 /.gitignore: -------------------------------------------------------------------------------- 1 | # If you prefer the allow list template instead of the deny list, see community template: 2 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 3 | # 4 | # Binaries for programs and plugins 5 | *.exe 6 | *.exe~ 7 | *.dll 8 | *.so 9 | *.dylib 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Dependency directories (remove the comment below to include it) 18 | # vendor/ 19 | 20 | # Go workspace file 21 | go.work 22 | go.work.sum 23 | 24 | *.mp4 25 | 26 | .idea 27 | 28 | ./toktik-video/config/config.yaml 29 | ./toktik-api/config/config.yaml 30 | ./toktik-user/config/config.yaml 31 | ./toktik-interaction/config/config.yaml 32 | ./toktik-chat/config/config.yaml 33 | 34 | ./deploy/data -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 小猫战士 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 | # toktik 2 | ## 项目介绍 3 | - 基于 Hertz + Kitex + Gorm 的微服务架构的精简抖音后端。 4 | - 项目服务地址:http://120.78.138.217:8080/ping 5 | - 项目文档地址:https://swuqw5z89yq.feishu.cn/docx/U2LxdtEoNo77K3xlammcTZO5nDe 6 | - 项目地址:https://github.com/Happy-Why/toktik 7 | - Apifox文档地址:https://qn6pcbbdek.apifox.cn/ 8 | 9 | *** 10 | 11 | ## 快速启动 12 | - 确保机器已经安装go1.19环境 13 | - 确保机器已经安装docker和docker-compose 14 | - 执行 chmod 777 start.sh 15 | - 执行 sh start.sh 16 | - 确保以上操作无误后,执行 docker-compose up -d 17 | - 进入每个toktik服务中,修改config目录下的配置文件 18 | - 前往nacos配置中心, 地址:http://xxx:8848/ 19 | - 如此配置 ![nacos.png](docs/nacos.png) 20 | - 修改好配置后,回到代码目录下,执行 docker-compose up -d 21 | 22 | *** 23 | ## 项目架构: 24 | ![project.png](docs/project.png) 25 | 26 | *** 27 | ## 奖项: 28 | ![img.png](docs/award.png) 29 | -------------------------------------------------------------------------------- /deploy/config/logstash.conf: -------------------------------------------------------------------------------- 1 | input { 2 | kafka { 3 | topics => "toktik_log" #kafka的topic 4 | bootstrap_servers => ["kafka:29092"] #服务器地址 5 | codec => "json" #以Json格式取数据 6 | } 7 | } 8 | output { 9 | elasticsearch { 10 | hosts => ["es:9200"] #ES地址 11 | index => "toktik_log-%{+YYYY.MM.dd}" #ES index,必须使用小写字母 12 | #user => "elastic" #这里建议使用 elastic 用户 13 | #password => "**********" 14 | } 15 | } -------------------------------------------------------------------------------- /deploy/config/logstash.yml: -------------------------------------------------------------------------------- 1 | http.host: "0.0.0.0" 2 | #ES地址 3 | xpack.monitoring.elasticsearch.hosts: ["http://es:9200"] 4 | xpack.monitoring.enabled: true 5 | #ES中的内置账户和密码,在ES中配置 6 | #xpack.monitoring.elasticsearch.username: logstash_system 7 | #xpack.monitoring.elasticsearch.password: ***************** -------------------------------------------------------------------------------- /deploy/config/otel-collector-config.yaml: -------------------------------------------------------------------------------- 1 | receivers: 2 | otlp: 3 | protocols: 4 | grpc: 5 | 6 | exporters: 7 | prometheusremotewrite: 8 | endpoint: "http://victoriametrics:8428/api/v1/write" 9 | 10 | logging: 11 | 12 | jaeger: 13 | endpoint: jaeger-all-in-one:14250 14 | tls: 15 | insecure: true 16 | 17 | processors: 18 | batch: 19 | 20 | extensions: 21 | health_check: 22 | pprof: 23 | endpoint: :1888 24 | zpages: 25 | endpoint: :55679 26 | 27 | service: 28 | extensions: [ pprof, zpages, health_check ] 29 | pipelines: 30 | traces: 31 | receivers: [ otlp ] 32 | processors: [ batch ] 33 | exporters: [ logging, jaeger ] 34 | metrics: 35 | receivers: [ otlp ] 36 | processors: [ batch ] 37 | exporters: [ logging, prometheusremotewrite ] -------------------------------------------------------------------------------- /deploy/config/prometheus.yml: -------------------------------------------------------------------------------- 1 | global: 2 | scrape_interval: 15s 3 | evaluation_interval: 15s 4 | scrape_configs: 5 | - job_name: "prometheus" 6 | scrape_interval: 5s 7 | static_configs: 8 | - targets: ['192.168.30.135:9090'] 9 | - job_name: "toktik-api" 10 | scrape_interval: 5s 11 | static_configs: 12 | - targets: ['192.168.30.135:9190'] 13 | - job_name: "toktik-user" 14 | scrape_interval: 5s 15 | static_configs: 16 | - targets: [ '192.168.30.135:9191' ] 17 | - job_name: "toktik-interaction" 18 | scrape_interval: 5s 19 | static_configs: 20 | - targets: [ '192.168.30.135:9192' ] 21 | - job_name: "toktik-video" 22 | scrape_interval: 5s 23 | static_configs: 24 | - targets: [ '192.168.30.135:9193' ] 25 | - job_name: "toktik-chat" 26 | scrape_interval: 5s 27 | static_configs: 28 | - targets: [ '192.168.30.135:9194' ] 29 | - job_name: "toktik-favor" 30 | scrape_interval: 5s 31 | static_configs: 32 | - targets: [ '192.168.30.135:9195' ] 33 | - job_name: "toktik-comment" 34 | scrape_interval: 5s 35 | static_configs: 36 | - targets: [ '192.168.30.135:9196' ] -------------------------------------------------------------------------------- /docs/award.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Happy-why/toktik/1cd3d85a6af72dca83e47994b196d6b40836668b/docs/award.png -------------------------------------------------------------------------------- /docs/nacos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Happy-why/toktik/1cd3d85a6af72dca83e47994b196d6b40836668b/docs/nacos.png -------------------------------------------------------------------------------- /docs/project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Happy-why/toktik/1cd3d85a6af72dca83e47994b196d6b40836668b/docs/project.png -------------------------------------------------------------------------------- /scripts/build-bin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "编译Linux版本64位" 4 | export CGO_ENABLED=0 5 | export GOOS=linux 6 | export GOARCH=amd64 7 | 8 | # 编译各个服务的主程序 9 | go build -o toktik-api/bin/toktik-api toktik-api/main.go 10 | go build -o toktik-user/bin/toktik-user toktik-user/main.go 11 | go build -o toktik-interaction/bin/toktik-interaction toktik-interaction/main.go 12 | go build -o toktik-video/bin/toktik-video toktik-video/main.go 13 | go build -o toktik-chat/bin/toktik-chat toktik-chat/main.go 14 | go build -o toktik-favor/bin/toktik-favor toktik-favor/main.go 15 | go build -o toktik-comment/bin/toktik-comment toktik-comment/main.go 16 | 17 | echo "可执行文件编译完成" 18 | -------------------------------------------------------------------------------- /scripts/build-dockerfile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 定义一个函数用于构建 Docker 镜像 4 | build_docker_image() { 5 | local service_name="$1" 6 | local tag="$2" 7 | 8 | echo "构建 Docker 镜像:$service_name:$tag" 9 | cd "$service_name" 10 | docker build -f Dockerfile -t "$service_name:$tag" . 11 | # shellcheck disable=SC2103 12 | cd .. 13 | } 14 | 15 | # 构建各个服务的 Docker 镜像 16 | build_docker_image "toktik-api" "0.1" 17 | build_docker_image "toktik-user" "0.1" 18 | build_docker_image "toktik-interaction" "0.1" 19 | build_docker_image "toktik-video" "0.1" 20 | build_docker_image "toktik-chat" "0.1" 21 | build_docker_image "toktik-favor" "0.1" 22 | build_docker_image "toktik-comment" "0.1" 23 | 24 | # 完成提示 25 | echo "所有 Docker 镜像构建完成。" 26 | -------------------------------------------------------------------------------- /scripts/build-volumes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 创建目录函数 4 | create_dir() { 5 | if [ ! -d "$1" ]; then 6 | mkdir -p "$1" 7 | if [ $? -eq 0 ]; then 8 | echo "目录创建成功:$1" 9 | else 10 | echo "目录创建失败:$1" 11 | exit 1 12 | fi 13 | else 14 | echo "目录已经存在:$1" 15 | fi 16 | } 17 | 18 | # 在deploy下创建目录 19 | cd deploy 20 | 21 | # 创建deploy/data/目录 22 | create_dir "data" 23 | 24 | # 创建mysql目录结构 25 | create_dir "data/mysql/data" 26 | create_dir "data/mysql/conf" 27 | create_dir "data/mysql/logs" 28 | 29 | # 创建redis目录结构 30 | create_dir "data/redis/data" 31 | create_dir "data/redis/conf" 32 | 33 | # 创建redis.conf文件 34 | if [ ! -f "data/redis/conf/redis.conf" ]; then 35 | touch "data/redis/conf/redis.conf" 36 | fi 37 | 38 | # 创建etcd目录结构 39 | create_dir "data/etcd/data" 40 | chmod 777 data/etcd/data 41 | # 创建nacos目录结构 42 | create_dir "data/nacos/data" 43 | 44 | # 创建elasticsearch目录结构 45 | create_dir "data/es/data" 46 | create_dir "data/es/logs" 47 | create_dir "data/es/plugins" 48 | 49 | # 添加 es权限 50 | chmod 777 data/es/data 51 | chmod 777 data/es/logs 52 | chmod 777 data/es/plugins 53 | 54 | # 创建logstash目录结构 55 | create_dir "data/logstash/log" 56 | 57 | cd .. 58 | -------------------------------------------------------------------------------- /scripts/init_go_work.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 初始化 go.work 文件 4 | go work init 5 | 6 | # 添加服务模块 7 | go work use ./toktik-api 8 | go work use ./toktik-chat 9 | go work use ./toktik-comment 10 | go work use ./toktik-common 11 | go work use ./toktik-favor 12 | go work use ./toktik-interaction 13 | go work use ./toktik-rpc 14 | go work use ./toktik-user 15 | go work use ./toktik-video 16 | -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 创建go_work目录 4 | chmod 777 init_go_work.sh 5 | ./scripts/init_go_work.sh 6 | 7 | # 创建容器卷目录 8 | chmod 777 scripts/build-volumes.sh 9 | ./scripts/build-volumes.sh 10 | 11 | # 创建每个服务的可执行文件 12 | chmod 777 scripts/build-bin.sh 13 | ./scripts/build-bin.sh 14 | 15 | # 构建每个服务的Docker镜像 16 | chmod 777 scripts/build-dockerfile.sh 17 | ./scripts/build-dockerfile.sh 18 | -------------------------------------------------------------------------------- /test/filetype_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/h2non/filetype" 6 | "io/ioutil" 7 | "testing" 8 | ) 9 | 10 | func TestFileType(t *testing.T) { 11 | buf, _ := ioutil.ReadFile("E:\\Go\\goproject\\src\\my_project\\toktik\\toktik-video\\storage\\videos\\兰亭序.mp4") 12 | 13 | if filetype.IsVideo(buf) { 14 | fmt.Println("File is an image") 15 | } else { 16 | fmt.Println("Not an image") 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/sentinel_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "fmt" 5 | sentinel "github.com/alibaba/sentinel-golang/api" 6 | "github.com/alibaba/sentinel-golang/core/flow" 7 | "github.com/alibaba/sentinel-golang/util" 8 | "log" 9 | "math/rand" 10 | "testing" 11 | "time" 12 | ) 13 | 14 | func TestSentinel(t *testing.T) { 15 | // 务必先进行初始化 16 | err := sentinel.InitDefault() 17 | if err != nil { 18 | log.Fatal(err) 19 | } 20 | 21 | // 配置一条限流规则 22 | _, err = flow.LoadRules([]*flow.Rule{ 23 | { 24 | Resource: "some-test", 25 | Threshold: 1, 26 | TokenCalculateStrategy: flow.Direct, 27 | ControlBehavior: flow.Reject, 28 | }, 29 | }) 30 | if err != nil { 31 | fmt.Println(err) 32 | return 33 | } 34 | 35 | ch := make(chan struct{}) 36 | for i := 0; i < 10; i++ { 37 | go func() { 38 | for { 39 | // 埋点逻辑,埋点资源名为 some-test 40 | e, b := sentinel.Entry("some-test") 41 | if b != nil { 42 | // 请求被拒绝,在此处进行处理 43 | time.Sleep(time.Duration(rand.Uint64()%10) * time.Millisecond) 44 | } else { 45 | // 请求允许通过,此处编写业务逻辑 46 | fmt.Println(util.CurrentTimeMillis(), "Passed") 47 | time.Sleep(time.Duration(rand.Uint64()%10) * time.Millisecond) 48 | 49 | // 务必保证业务结束后调用 Exit 50 | e.Exit() 51 | } 52 | 53 | } 54 | }() 55 | } 56 | <-ch 57 | } 58 | -------------------------------------------------------------------------------- /toktik-api/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine 2 | WORKDIR /app 3 | COPY ./bin/toktik-api . 4 | COPY ./config . 5 | RUN mkdir config && mv config.yaml config/config.yaml && mv bootstrap.yaml config/bootstrap.yaml 6 | 7 | RUN chmod +x ./toktik-api 8 | 9 | EXPOSE 8080 9190 10 | ENTRYPOINT ["./toktik-api"] 11 | -------------------------------------------------------------------------------- /toktik-api/config/bootstrap.yaml.template: -------------------------------------------------------------------------------- 1 | Nacos: 2 | Namespace: "xxx" # 命名空间id 3 | Group: "api" # group名称 4 | Addr: "xxx.xxx.xxx.xxx" # 地址 5 | Port: 8848 6 | Scheme: "http" 7 | ContextPath: "/nacos" -------------------------------------------------------------------------------- /toktik-api/config/config.yaml.template: -------------------------------------------------------------------------------- 1 | Server: 2 | Name: "toktik-api" 3 | Addr: "0.0.0.0:8080" 4 | 5 | RPC: 6 | Name: "rpc-api" 7 | Addr: "0.0.0.0:8880" 8 | ServerAddrs: 9 | rpc-user: xxx:8881 10 | rpc-video: xxx:8882 11 | rpc-interaction: xxx:8883 12 | rpc-chat: xxx:8884 13 | rpc-favor: xxx:8885 14 | rpc-comment: xxx:8886 15 | 16 | Jaeger: 17 | HTTPEndpoint: "http://xxx:14268/api/traces" 18 | RPCExportEndpoint: "xxx:4317" 19 | ServerName: 20 | toktik-api: "toktik-api" 21 | toktik-user: "toktik-user" 22 | toktik-video: "toktik-video" 23 | toktik-interaction: "toktik-interaction" 24 | toktik-chat: "toktik-chat" 25 | toktik-favor: "toktik-favor" 26 | toktik-comment: "toktik-comment" 27 | 28 | Prometheus: 29 | Post: ":9190" 30 | Path: "/metrics" 31 | 32 | Logger: 33 | Level: "debug" 34 | LogSavePath: "storage/applogs/" 35 | LogFileExt: ".log" 36 | MaxSize: 10 37 | MaxBackups: 7 38 | MaxAge: 30 39 | Compress: false 40 | LowLevelFile: "info" 41 | HighLevelFile: "error" 42 | 43 | Etcd: 44 | Addr: 45 | - "xxx:2379" 46 | 47 | Token: 48 | Key: "xxx" 49 | UserTokenExp: 168h # 小时 50 | AuthorizationKey: xxx -------------------------------------------------------------------------------- /toktik-api/internal/api/api.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "toktik-rpc/kitex_gen/chat/chatservice" 5 | "toktik-rpc/kitex_gen/comment/commentservice" 6 | "toktik-rpc/kitex_gen/favor/favorservice" 7 | 8 | "toktik-rpc/kitex_gen/interaction/interactionservice" 9 | "toktik-rpc/kitex_gen/user/userservice" 10 | "toktik-rpc/kitex_gen/video/videoservice" 11 | ) 12 | 13 | var ( 14 | UserClient userservice.Client 15 | InteractionClient interactionservice.Client 16 | VideoClient videoservice.Client 17 | ChatClient chatservice.Client 18 | FavorClient favorservice.Client 19 | Comment commentservice.Client 20 | ) 21 | -------------------------------------------------------------------------------- /toktik-api/internal/api/chat/route.go: -------------------------------------------------------------------------------- 1 | package chat 2 | 3 | import ( 4 | "github.com/cloudwego/hertz/pkg/app/server" 5 | "log" 6 | "toktik-api/pkg/middleware" 7 | "toktik-api/pkg/router" 8 | ) 9 | 10 | type RouterChat struct { 11 | } 12 | 13 | func init() { 14 | log.Println("init chat router success") 15 | rc := &RouterChat{} 16 | router.Register(rc) 17 | } 18 | 19 | func (*RouterChat) Route(r *server.Hertz) { 20 | InitRpcChatClient() 21 | //初始化grpc的客户端连接 22 | h := NewHandlerInteraction() 23 | g := r.Group("/douyin/message", middleware.MustUser()) 24 | { 25 | g.GET("/chat/", h.MessageList) 26 | g.POST("/action/", h.ChatAction) 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /toktik-api/internal/api/chat/rpc_client.go: -------------------------------------------------------------------------------- 1 | package chat 2 | 3 | import ( 4 | "github.com/cloudwego/kitex/client" 5 | "github.com/kitex-contrib/obs-opentelemetry/provider" 6 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 7 | etcd "github.com/kitex-contrib/registry-etcd" 8 | "go.uber.org/zap" 9 | "toktik-api/internal/api" 10 | "toktik-api/internal/global" 11 | "toktik-api/internal/model" 12 | "toktik-rpc/kitex_gen/chat/chatservice" 13 | ) 14 | 15 | func InitRpcChatClient() { 16 | r, err := etcd.NewEtcdResolver(global.Settings.Etcd.Addr) 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | provider.NewOpenTelemetryProvider( 22 | provider.WithServiceName(global.Settings.Jaeger.ServerName[model.TokTikChat]), 23 | provider.WithExportEndpoint(global.Settings.Jaeger.RPCExportEndpoint), 24 | provider.WithInsecure(), 25 | ) 26 | c, err := chatservice.NewClient( 27 | model.RpcChat, 28 | client.WithSuite(tracing.NewClientSuite()), 29 | //client.WithHostPorts(global.Settings.Rpc.ServerAddrs[model.RpcInteraction]), 30 | //client.WithMiddleware(rpcmiddleware.CommonMiddleware), 31 | //client.WithInstanceMW(rpcmiddleware.ClientMiddleware), 32 | client.WithResolver(r), 33 | ) 34 | if err != nil { 35 | zap.L().Error("apiServer InitRpcChatClient err:", zap.Error(err)) 36 | panic(err) 37 | } 38 | api.ChatClient = c 39 | } 40 | -------------------------------------------------------------------------------- /toktik-api/internal/api/comment/route.go: -------------------------------------------------------------------------------- 1 | package video 2 | 3 | import ( 4 | "github.com/cloudwego/hertz/pkg/app/server" 5 | "log" 6 | "toktik-api/pkg/middleware" 7 | "toktik-api/pkg/router" 8 | ) 9 | 10 | type RouterComment struct { 11 | } 12 | 13 | func init() { 14 | log.Println("init Comment router success") 15 | rc := &RouterComment{} 16 | router.Register(rc) 17 | } 18 | 19 | func (*RouterComment) Route(r *server.Hertz) { 20 | InitRpcCommentClient() 21 | //初始化grpc的客户端连接 22 | h := NewHandlerVideo() 23 | r.GET("/douyin/comment/list/", h.CommentList) 24 | g := r.Group("/douyin", middleware.MustUser()) 25 | { 26 | g.POST("/comment/action/", h.CommentAction) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /toktik-api/internal/api/comment/rpc_client.go: -------------------------------------------------------------------------------- 1 | package video 2 | 3 | import ( 4 | "github.com/cloudwego/kitex/client" 5 | "github.com/kitex-contrib/obs-opentelemetry/provider" 6 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 7 | etcd "github.com/kitex-contrib/registry-etcd" 8 | "go.uber.org/zap" 9 | "toktik-api/internal/api" 10 | "toktik-api/internal/global" 11 | "toktik-api/internal/model" 12 | "toktik-rpc/kitex_gen/comment/commentservice" 13 | ) 14 | 15 | func InitRpcCommentClient() { 16 | r, err := etcd.NewEtcdResolver(global.Settings.Etcd.Addr) 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | provider.NewOpenTelemetryProvider( 22 | provider.WithServiceName(global.Settings.Jaeger.ServerName[model.TokTikComment]), 23 | provider.WithExportEndpoint(global.Settings.Jaeger.RPCExportEndpoint), 24 | provider.WithInsecure(), 25 | ) 26 | 27 | c, err := commentservice.NewClient( 28 | model.RpcComment, 29 | client.WithSuite(tracing.NewClientSuite()), 30 | //client.WithHostPorts(global.Settings.Rpc.ServerAddrs[model.RpcVideo]), 31 | //client.WithMiddleware(rpcmiddleware.CommonMiddleware), 32 | //client.WithInstanceMW(rpcmiddleware.ClientMiddleware), 33 | client.WithResolver(r), 34 | ) 35 | if err != nil { 36 | zap.L().Error("apiServer InitRpc Comment Client err:", zap.Error(err)) 37 | panic(err) 38 | } 39 | api.Comment = c 40 | } 41 | -------------------------------------------------------------------------------- /toktik-api/internal/api/favor/route.go: -------------------------------------------------------------------------------- 1 | package video 2 | 3 | import ( 4 | "github.com/cloudwego/hertz/pkg/app/server" 5 | "log" 6 | "toktik-api/pkg/middleware" 7 | "toktik-api/pkg/router" 8 | ) 9 | 10 | type RouterFavor struct { 11 | } 12 | 13 | func init() { 14 | log.Println("init Favor router success") 15 | rf := &RouterFavor{} 16 | router.Register(rf) 17 | } 18 | 19 | func (*RouterFavor) Route(r *server.Hertz) { 20 | InitRpcFavorClient() 21 | //初始化grpc的客户端连接 22 | h := NewHandlerVideo() 23 | r.GET("/douyin/favorite/list/", h.FavoriteList) 24 | g := r.Group("/douyin", middleware.MustUser()) 25 | { 26 | g.POST("/favorite/action/", h.FavoriteAction) 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /toktik-api/internal/api/favor/rpc_client.go: -------------------------------------------------------------------------------- 1 | package video 2 | 3 | import ( 4 | "github.com/cloudwego/kitex/client" 5 | "github.com/kitex-contrib/obs-opentelemetry/provider" 6 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 7 | etcd "github.com/kitex-contrib/registry-etcd" 8 | "go.uber.org/zap" 9 | "toktik-api/internal/api" 10 | "toktik-api/internal/global" 11 | "toktik-api/internal/model" 12 | "toktik-rpc/kitex_gen/favor/favorservice" 13 | ) 14 | 15 | func InitRpcFavorClient() { 16 | r, err := etcd.NewEtcdResolver(global.Settings.Etcd.Addr) 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | provider.NewOpenTelemetryProvider( 22 | provider.WithServiceName(global.Settings.Jaeger.ServerName[model.TokTikFavor]), 23 | provider.WithExportEndpoint(global.Settings.Jaeger.RPCExportEndpoint), 24 | provider.WithInsecure(), 25 | ) 26 | 27 | c, err := favorservice.NewClient( 28 | model.RpcFavor, 29 | client.WithSuite(tracing.NewClientSuite()), 30 | //client.WithHostPorts(global.Settings.Rpc.ServerAddrs[model.RpcVideo]), 31 | //client.WithMiddleware(rpcmiddleware.CommonMiddleware), 32 | //client.WithInstanceMW(rpcmiddleware.ClientMiddleware), 33 | client.WithResolver(r), 34 | ) 35 | if err != nil { 36 | zap.L().Error("apiServer InitRpc Favor Client err:", zap.Error(err)) 37 | panic(err) 38 | } 39 | api.FavorClient = c 40 | } 41 | -------------------------------------------------------------------------------- /toktik-api/internal/api/interaction/route.go: -------------------------------------------------------------------------------- 1 | package interaction 2 | 3 | import ( 4 | "github.com/cloudwego/hertz/pkg/app/server" 5 | "log" 6 | "toktik-api/pkg/middleware" 7 | "toktik-api/pkg/router" 8 | ) 9 | 10 | type RouterInteraction struct { 11 | } 12 | 13 | func init() { 14 | log.Println("init Interaction router success") 15 | ri := &RouterInteraction{} 16 | router.Register(ri) 17 | } 18 | 19 | func (*RouterInteraction) Route(r *server.Hertz) { 20 | InitRpcInteractionClient() 21 | //初始化grpc的客户端连接 22 | h := NewHandlerInteraction() 23 | g := r.Group("/douyin/relation", middleware.MustUser()) 24 | { 25 | g.POST("/action/", h.FollowSB) 26 | g.GET("/follow/list/", h.FollowList) 27 | g.GET("/follower/list/", h.FansList) 28 | g.GET("/friend/list/", h.FriendList) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /toktik-api/internal/api/interaction/rpc_client.go: -------------------------------------------------------------------------------- 1 | package interaction 2 | 3 | import ( 4 | "github.com/cloudwego/kitex/client" 5 | "github.com/kitex-contrib/obs-opentelemetry/provider" 6 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 7 | etcd "github.com/kitex-contrib/registry-etcd" 8 | "go.uber.org/zap" 9 | "toktik-api/internal/api" 10 | "toktik-api/internal/global" 11 | "toktik-api/internal/model" 12 | "toktik-rpc/kitex_gen/interaction/interactionservice" 13 | ) 14 | 15 | func InitRpcInteractionClient() { 16 | r, err := etcd.NewEtcdResolver(global.Settings.Etcd.Addr) 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | provider.NewOpenTelemetryProvider( 22 | provider.WithServiceName(global.Settings.Jaeger.ServerName[model.TokTikInteraction]), 23 | provider.WithExportEndpoint(global.Settings.Jaeger.RPCExportEndpoint), 24 | provider.WithInsecure(), 25 | ) 26 | 27 | c, err := interactionservice.NewClient( 28 | model.RpcInteraction, 29 | client.WithSuite(tracing.NewClientSuite()), 30 | //client.WithHostPorts(global.Settings.Rpc.ServerAddrs[model.RpcInteraction]), 31 | //client.WithMiddleware(rpcmiddleware.CommonMiddleware), 32 | //client.WithInstanceMW(rpcmiddleware.ClientMiddleware), 33 | client.WithResolver(r), 34 | ) 35 | if err != nil { 36 | zap.L().Error("apiServer InitRpcInteractionClient err:", zap.Error(err)) 37 | panic(err) 38 | } 39 | api.InteractionClient = c 40 | } 41 | -------------------------------------------------------------------------------- /toktik-api/internal/api/user/route.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "github.com/cloudwego/hertz/pkg/app/server" 5 | "log" 6 | "toktik-api/pkg/middleware" 7 | "toktik-api/pkg/router" 8 | ) 9 | 10 | type RouterUser struct { 11 | } 12 | 13 | func init() { 14 | log.Println("init User router success") 15 | ru := &RouterUser{} 16 | router.Register(ru) 17 | } 18 | 19 | func (*RouterUser) Route(r *server.Hertz) { 20 | InitRpcUserClient() 21 | //初始化grpc的客户端连接 22 | h := NewHandlerUser() 23 | r.POST("/douyin/user/register/", h.Register) 24 | r.POST("/douyin/user/login/", h.Login) 25 | 26 | g := r.Group("", middleware.MustUser()) 27 | { 28 | g.GET("/douyin/user/", h.UserIndex) 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /toktik-api/internal/api/user/rpc_client.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "github.com/cloudwego/kitex/client" 5 | "github.com/kitex-contrib/obs-opentelemetry/provider" 6 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 7 | etcd "github.com/kitex-contrib/registry-etcd" 8 | "go.uber.org/zap" 9 | "toktik-api/internal/api" 10 | "toktik-api/internal/global" 11 | "toktik-api/internal/model" 12 | "toktik-rpc/kitex_gen/user/userservice" 13 | ) 14 | 15 | func InitRpcUserClient() { 16 | r, err := etcd.NewEtcdResolver(global.Settings.Etcd.Addr) 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | provider.NewOpenTelemetryProvider( 22 | provider.WithServiceName(global.Settings.Jaeger.ServerName[model.TokTikUser]), 23 | provider.WithExportEndpoint(global.Settings.Jaeger.RPCExportEndpoint), 24 | provider.WithInsecure(), 25 | ) 26 | 27 | c, err := userservice.NewClient( 28 | model.RpcUser, 29 | client.WithSuite(tracing.NewClientSuite()), 30 | //client.WithHostPorts(global.Settings.Rpc.ServerAddrs[model.RpcUser]), 31 | //client.WithMiddleware(rpcmiddleware.CommonMiddleware), 32 | //client.WithInstanceMW(rpcmiddleware.ClientMiddleware), 33 | client.WithResolver(r), 34 | ) 35 | if err != nil { 36 | zap.L().Error("apiServer InitRpc User Client err:", zap.Error(err)) 37 | panic(err) 38 | } 39 | api.UserClient = c 40 | } 41 | -------------------------------------------------------------------------------- /toktik-api/internal/api/video/route.go: -------------------------------------------------------------------------------- 1 | package video 2 | 3 | import ( 4 | "github.com/cloudwego/hertz/pkg/app/server" 5 | "log" 6 | "toktik-api/pkg/middleware" 7 | "toktik-api/pkg/router" 8 | ) 9 | 10 | type RouterVideo struct { 11 | } 12 | 13 | func init() { 14 | log.Println("init video router success") 15 | rv := &RouterVideo{} 16 | router.Register(rv) 17 | } 18 | 19 | func (*RouterVideo) Route(r *server.Hertz) { 20 | InitRpcVideoClient() 21 | //初始化grpc的客户端连接 22 | h := NewHandlerVideo() 23 | r.GET("/douyin/feed/", h.VideoFeed) 24 | r.GET("/douyin//publish/list/", h.PublishList) 25 | g := r.Group("/douyin", middleware.MustUser()) 26 | { 27 | g.POST("/publish/action/", h.VideoPublish) 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /toktik-api/internal/api/video/rpc_client.go: -------------------------------------------------------------------------------- 1 | package video 2 | 3 | import ( 4 | "github.com/cloudwego/kitex/client" 5 | "github.com/kitex-contrib/obs-opentelemetry/provider" 6 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 7 | etcd "github.com/kitex-contrib/registry-etcd" 8 | "go.uber.org/zap" 9 | "toktik-api/internal/api" 10 | "toktik-api/internal/global" 11 | "toktik-api/internal/model" 12 | "toktik-rpc/kitex_gen/video/videoservice" 13 | ) 14 | 15 | func InitRpcVideoClient() { 16 | r, err := etcd.NewEtcdResolver(global.Settings.Etcd.Addr) 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | provider.NewOpenTelemetryProvider( 22 | provider.WithServiceName(global.Settings.Jaeger.ServerName[model.TokTikVideo]), 23 | provider.WithExportEndpoint(global.Settings.Jaeger.RPCExportEndpoint), 24 | provider.WithInsecure(), 25 | ) 26 | 27 | c, err := videoservice.NewClient( 28 | model.RpcVideo, 29 | client.WithSuite(tracing.NewClientSuite()), 30 | //client.WithHostPorts(global.Settings.Rpc.ServerAddrs[model.RpcVideo]), 31 | //client.WithMiddleware(rpcmiddleware.CommonMiddleware), 32 | //client.WithInstanceMW(rpcmiddleware.ClientMiddleware), 33 | client.WithResolver(r), 34 | ) 35 | if err != nil { 36 | zap.L().Error("apiServer InitRpc Video Client err:", zap.Error(err)) 37 | panic(err) 38 | } 39 | api.VideoClient = c 40 | } 41 | -------------------------------------------------------------------------------- /toktik-api/internal/global/root.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | "path/filepath" 7 | "sync" 8 | ) 9 | 10 | /* 11 | 用于推断当前项目根路径 12 | */ 13 | 14 | var ( 15 | RootDir string // 项目根路径 16 | once = new(sync.Once) 17 | ) 18 | 19 | func exist(filePath string) bool { 20 | _, err := os.Stat(filePath) 21 | return err == nil || errors.Is(err, os.ErrExist) 22 | } 23 | 24 | // 计算项目路径 25 | func inferRootDir() string { 26 | cwd, err := os.Getwd() 27 | if err != nil { 28 | panic(err) 29 | } 30 | var infer func(path string) string 31 | infer = func(path string) string { 32 | if exist(path + "/config") { 33 | return path 34 | } 35 | return infer(filepath.Dir(path)) 36 | } 37 | return infer(cwd) 38 | } 39 | 40 | func init() { 41 | once.Do(func() { 42 | RootDir = inferRootDir() 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /toktik-api/internal/global/var.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "toktik-api/internal/model/config" 5 | "toktik-common/logger" 6 | "toktik-common/token" 7 | ) 8 | 9 | var ( 10 | Settings config.Config // Public配置 11 | Maker token.Maker 12 | Logger *logger.Log // 日志 13 | ) 14 | -------------------------------------------------------------------------------- /toktik-api/internal/model/auto/base.go: -------------------------------------------------------------------------------- 1 | package auto 2 | 3 | import ( 4 | "gorm.io/gorm" 5 | "time" 6 | ) 7 | 8 | type BaseModel struct { 9 | ID uint `gorm:"primarykey" json:"id,string"` 10 | CreatedAt time.Time `json:"created_at,string"` 11 | UpdatedAt time.Time `json:"updated_at,string"` 12 | DeletedAt gorm.DeletedAt `json:"deleted_at,string"` 13 | } 14 | -------------------------------------------------------------------------------- /toktik-api/internal/model/auto/chat.go: -------------------------------------------------------------------------------- 1 | package auto 2 | 3 | type Message struct { 4 | BaseModel 5 | UserId uint `json:"from_user_id" gorm:"index:idx_message;not null"` 6 | ToUserId uint `json:"to_user_id" gorm:"index:idx_message;not null"` 7 | Content string `json:"content" gorm:"not null"` 8 | } 9 | 10 | func (*Message) TableName() string { 11 | return "messages" 12 | } 13 | -------------------------------------------------------------------------------- /toktik-api/internal/model/auto/comment.go: -------------------------------------------------------------------------------- 1 | package auto 2 | 3 | // Comment 评论表 / 4 | type Comment struct { 5 | BaseModel 6 | VideoId uint `json:"video_id,string" gorm:"not null;index:comment_video"` 7 | UserId uint `json:"user_id,string" gorm:"not null"` 8 | Content string `json:"content,string" gorm:"not null"` 9 | } 10 | 11 | func (*Comment) TableName() string { 12 | return "comment" 13 | } 14 | -------------------------------------------------------------------------------- /toktik-api/internal/model/auto/favorite.go: -------------------------------------------------------------------------------- 1 | package auto 2 | 3 | // Favorite 点赞表 /* 4 | type Favorite struct { 5 | ID uint `json:"id,string" gorm:"primarykey"` 6 | UserId uint `json:"user_id,string" gorm:"uniqueIndex:user_video_id,not null;"` 7 | VideoId uint `json:"video_id,string" gorm:"uniqueIndex:user_video_id,not null;"` 8 | } 9 | 10 | func (*Favorite) TableName() string { 11 | return "favorite" 12 | } 13 | -------------------------------------------------------------------------------- /toktik-api/internal/model/auto/relation.go: -------------------------------------------------------------------------------- 1 | package auto 2 | 3 | type Relation struct { 4 | BaseModel 5 | UserId uint `json:"user_id,string" gorm:"index:idx_relation,not null"` // 用户ID 6 | TargetId uint `json:"target_id,string" gorm:"index:idx_relation,not null"` // 目标ID,添加复合索引 7 | //IsFriend int `json:"is_friend" gorm:"not null"` // 如果需要保证 relation_id 唯一,可以使用该字段 8 | } 9 | 10 | func (*Relation) TableName() string { 11 | return "relation" 12 | } 13 | -------------------------------------------------------------------------------- /toktik-api/internal/model/auto/user.go: -------------------------------------------------------------------------------- 1 | package auto 2 | 3 | type User struct { 4 | BaseModel 5 | Username string `json:"username" gorm:"uniqueIndex:username;size:40;not null"` // 设置唯一索引,判断用户名是否重复 6 | Password string `json:"password" gorm:"type:varchar(50);not null"` 7 | Avatar string `json:"avatar" gorm:"type:varchar(255);not null"` // 用户头像 8 | BackgroundImage string `json:"background_image" gorm:"type:varchar(255);not null"` //背景图片 9 | IsFollow bool `json:"is_follow,string" gorm:"not null"` // true-已关注,false-未关注 在数据库中这个字段没用 10 | Signature string `json:"signature" gorm:"type:varchar(255);"` // 个人简介 11 | } 12 | 13 | func (*User) TableName() string { 14 | return "user" 15 | } 16 | -------------------------------------------------------------------------------- /toktik-api/internal/model/auto/user_count.go: -------------------------------------------------------------------------------- 1 | package auto 2 | 3 | type UserCount struct { 4 | BaseModel 5 | UserId uint `json:"user_id,string" gorm:"uniqueIndex,not null"` 6 | User User `gorm:"ForeignKey:UserId"` 7 | FollowCount int64 `json:"follow_count,string" ` // 关注总数 8 | FollowerCount int64 `json:"follower_count,string" ` // 粉丝总数 9 | TotalFavorited int64 `json:"total_favorited,string" ` // 获赞数量 10 | WorkCount int64 `json:"work_count,string" ` // 作品数 11 | FavoriteCount int64 `json:"favorite_count,string" ` // 点赞总数 12 | } 13 | 14 | func (*UserCount) TableName() string { 15 | return "user_count" 16 | } 17 | -------------------------------------------------------------------------------- /toktik-api/internal/model/auto/video.go: -------------------------------------------------------------------------------- 1 | package auto 2 | 3 | // Video 视频表 /* 4 | type Video struct { 5 | BaseModel 6 | UserId uint `json:"user_id,string" gorm:"index:user_id,not null"` 7 | Title string `json:"title" gorm:"type:varchar(255);not null"` // 视频标题 8 | PlayURL string `json:"play_url" gorm:"type:varchar(255);not null"` // 视频播放地址 9 | CoverURL string `json:"cover_url" gorm:"type:varchar(255);not null"` // 视频封面地址 10 | FavoriteCount int64 `json:"favorite_count,string" ` // 视频的点赞总数 11 | CommentCount int64 `json:"comment_count,string"` // 视频的评论总数 12 | } 13 | 14 | func (*Video) TableName() string { 15 | return "video" 16 | } 17 | -------------------------------------------------------------------------------- /toktik-api/internal/model/constants.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | const ( 4 | RpcUser = "rpc-user" 5 | RpcVideo = "rpc-video" 6 | RpcInteraction = "rpc-interaction" 7 | RpcChat = "rpc-chat" 8 | RpcFavor = "rpc-favor" 9 | RpcComment = "rpc-comment" 10 | ) 11 | 12 | const ( 13 | TokTikApi = "toktik-api" 14 | TokTikUser = "toktik-user" 15 | TokTikInteraction = "toktik-interaction" 16 | TokTikVideo = "toktik-video" 17 | TokTikChat = "toktik-chat" 18 | TokTikFavor = "toktik-favor" 19 | TokTikComment = "toktik-comment" 20 | ) 21 | 22 | const ( 23 | SentinelApi = "api" 24 | ) 25 | -------------------------------------------------------------------------------- /toktik-api/internal/model/request/chat.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | type ChatActionRequest struct { 4 | Token string `json:"token" form:"token" query:"token"` 5 | UserId int64 `json:"user_id" form:"user_id" query:"user_id"` 6 | ToUserId int64 `json:"to_user_id" form:"to_user_id" query:"to_user_id"` 7 | ActionType int32 `json:"action_type" form:"action_type" query:"action_type"` 8 | Content string `json:"content" form:"content" query:"content"` 9 | } 10 | 11 | func (r *ChatActionRequest) Verify() bool { 12 | return r.UserId == r.ToUserId 13 | } 14 | 15 | type MessageListRequest struct { 16 | UserId int64 `json:"user_id" form:"user_id" query:"user_id"` 17 | Token string `json:"token" form:"token" query:"token"` 18 | ToUserId int64 `json:"to_user_id" form:"to_user_id" query:"to_user_id"` 19 | PreMsgTime int64 `json:"pre_msg_time" form:"pre_msg_time" query:"pre_msg_time"` 20 | } 21 | -------------------------------------------------------------------------------- /toktik-api/internal/model/request/comment.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | type CommentActionRequest struct { 4 | UserId int64 `json:"user_id" form:"user_id" query:"user_id"` 5 | Token string `json:"token" form:"token" query:"token"` 6 | VideoId int64 `json:"video_id" form:"video_id" query:"video_id"` 7 | ActionType int32 `json:"action_type" form:"action_type" query:"action_type"` 8 | CommentText string `json:"comment_text" form:"comment_text" query:"comment_text"` 9 | CommentId int64 `json:"comment_id" form:"comment_id" query:"comment_id"` 10 | } 11 | 12 | type CommentListRequest struct { 13 | Token string `json:"token" form:"token" query:"token"` 14 | VideoId int64 `json:"video_id,omitempty" form:"video_id" query:"video_id"` 15 | UserId int64 `json:"user_id" form:"user_id" query:"user_id"` 16 | } 17 | -------------------------------------------------------------------------------- /toktik-api/internal/model/request/favor.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | type FavoriteActionRequest struct { 4 | Token string `json:"token" form:"token" query:"token"` 5 | VideoId int64 `json:"video_id" form:"video_id" query:"video_id"` 6 | ActionType int32 `json:"action_type" form:"action_type" query:"action_type"` 7 | UserId int64 `json:"user_id" form:"user_id" query:"user_id"` 8 | } 9 | 10 | type FavoriteListRequest struct { 11 | UserId int64 `json:"user_id" form:"user_id" query:"user_id"` 12 | MyUserId int64 `json:"my_user_id" form:"my_user_id" query:"my_user_id"` 13 | Token string `json:"token" form:"token" query:"token"` 14 | } 15 | -------------------------------------------------------------------------------- /toktik-api/internal/model/request/interaction.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | type FollowActionRequest struct { 4 | Token string `json:"token" form:"token" query:"token"` 5 | ToUserId int64 `json:"to_user_id" form:"to_user_id" query:"to_user_id"` 6 | ActionType int32 `json:"action_type" form:"action_type" query:"action_type"` 7 | MyUserId int64 `json:"my_user_id" form:"my_user_id" query:"my_user_id"` 8 | } 9 | 10 | type FollowListRequest struct { 11 | UserId int64 `json:"user_id" form:"user_id" query:"user_id"` 12 | MyUserId int64 `json:"my_user_id" form:"my_user_id" query:"my_user_id"` 13 | Token string `json:"token" form:"token" query:"token"` 14 | } 15 | 16 | type FansListRequest struct { 17 | UserId int64 `json:"user_id" form:"user_id" query:"user_id"` 18 | MyUserId int64 `json:"my_user_id" form:"my_user_id" query:"my_user_id"` 19 | Token string `json:"token" form:"token" query:"token"` 20 | } 21 | 22 | type FriendListRequest struct { 23 | UserId int64 `json:"user_id" form:"user_id" query:"user_id"` 24 | MyUserId int64 `json:"my_user_id" form:"my_user_id" query:"my_user_id"` 25 | Token string `json:"token" form:"token" query:"token"` 26 | } 27 | -------------------------------------------------------------------------------- /toktik-api/internal/model/request/user.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | import ( 4 | "toktik-api/pkg/myerr" 5 | "toktik-common/errcode" 6 | ) 7 | 8 | type RegisterRequest struct { 9 | Username string `json:"username" form:"username" query:"username"` 10 | Password string `json:"password" form:"password" query:"password"` 11 | } 12 | 13 | func (r RegisterRequest) Verify() errcode.Err { 14 | if r.Username == "" || len(r.Username) > 32 || r.Password == "" || len(r.Password) > 32 { 15 | return myerr.ErrUserNameORPassWord 16 | } 17 | return nil 18 | } 19 | 20 | type LoginRequest struct { 21 | Username string `json:"username" form:"username" query:"username"` 22 | Password string `json:"password" form:"password" query:"password"` 23 | } 24 | 25 | func (r LoginRequest) Verify() errcode.Err { 26 | if r.Username == "" || len(r.Username) > 32 || r.Password == "" || len(r.Password) > 32 { 27 | return myerr.ErrUserNameORPassWord 28 | } 29 | return nil 30 | } 31 | 32 | type UserIndexRequest struct { 33 | UserId int64 `json:"user_id" form:"user_id" query:"user_id"` 34 | Token string `json:"token" form:"token" query:"token"` 35 | MyUserId int64 `json:"my_user_id" form:"my_user_id" query:"my_user_id"` 36 | } 37 | -------------------------------------------------------------------------------- /toktik-api/internal/model/request/video.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "github.com/h2non/filetype" 7 | "io" 8 | "mime/multipart" 9 | "time" 10 | ) 11 | 12 | type VideoFeedRequest struct { 13 | LatestTime int64 `json:"latest_time" form:"latest_time" query:"latest_time"` 14 | Token string `json:"token" form:"token" query:"token"` 15 | UserId int64 `json:"user_id" form:"user_id" query:"user_id"` 16 | } 17 | 18 | func (v *VideoFeedRequest) VerifyFeed() { 19 | if v.LatestTime == 0 { 20 | v.LatestTime = time.Now().Unix() 21 | } 22 | } 23 | 24 | type VideoPublishRequest struct { 25 | Data *multipart.FileHeader `json:"data" form:"data"` 26 | Token string `json:"token" form:"token"` 27 | Title string `json:"title" form:"title"` 28 | UserId int64 `json:"user_id" form:"user_id"` 29 | } 30 | 31 | func (v *VideoPublishRequest) VerifyFeed() (*bytes.Buffer, error) { 32 | 33 | if v.Data == nil { 34 | return nil, fmt.Errorf("找不到上传的文件呀~") 35 | } 36 | src, _ := v.Data.Open() 37 | buf := bytes.NewBuffer(nil) 38 | _, err := io.Copy(buf, src) 39 | if !filetype.IsVideo(buf.Bytes()) { 40 | return nil, fmt.Errorf("文件格式不对啊") 41 | } 42 | return buf, err 43 | } 44 | 45 | type PublishListRequest struct { 46 | Token string `json:"token" form:"token" query:"token"` 47 | UserId int64 `json:"user_id" form:"user_id" query:"user_id"` 48 | MyUserId int64 `json:"my_user_id" form:"my_user_id" query:"my_user_id"` 49 | } 50 | -------------------------------------------------------------------------------- /toktik-api/internal/model/response/chat.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | type ChatActionResponse struct { 4 | StatusCode int64 `json:"status_code"` 5 | StatusMsg string `json:"status_msg"` 6 | } 7 | 8 | type MessageListResponse struct { 9 | StatusCode int64 `json:"status_code"` 10 | StatusMsg string `json:"status_msg"` 11 | MessageList []*MessageResponse `json:"message_list"` 12 | } 13 | 14 | type MessageResponse struct { 15 | ID uint `gorm:"primarykey" json:"id,string"` 16 | CreateTime int64 `json:"create_time"` 17 | FromUserId uint `json:"from_user_id"` 18 | ToUserId uint `json:"to_user_id"` 19 | Content string `json:"content"` 20 | } 21 | -------------------------------------------------------------------------------- /toktik-api/internal/model/response/comment.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | type CommentActionResponse struct { 4 | StatusCode int64 `json:"status_code"` 5 | StatusMsg string `json:"status_msg"` 6 | Comment *Comment `json:"comment"` 7 | } 8 | 9 | type CommentListResponse struct { 10 | StatusCode int64 `json:"status_code"` 11 | StatusMsg string `json:"status_msg"` 12 | CommentList []*Comment `json:"comment_list"` 13 | } 14 | 15 | type Comment struct { 16 | Id int64 `json:"id"` // 视频评论id 17 | User *User `json:"user"` // 评论用户信息 18 | Content string `json:"content"` // 评论内容 19 | CreateDate string `json:"create_date"` // 评论发布日期,格式 mm-dd 20 | } 21 | -------------------------------------------------------------------------------- /toktik-api/internal/model/response/favor.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | type FavoriteActionResponse struct { 4 | StatusCode int64 `json:"status_code"` 5 | StatusMsg string `json:"status_msg"` 6 | } 7 | 8 | type FavoriteListResponse struct { 9 | StatusCode int64 `json:"status_code"` 10 | StatusMsg string `json:"status_msg"` 11 | VideoList []*Video `json:"video_list"` 12 | } 13 | -------------------------------------------------------------------------------- /toktik-api/internal/model/response/interaction.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | type FollowActionResponse struct { 4 | StatusCode int64 `json:"status_code"` 5 | StatusMsg string `json:"status_msg"` 6 | } 7 | 8 | type FollowListResponse struct { 9 | StatusCode int64 `json:"status_code"` 10 | StatusMsg string `json:"status_msg"` 11 | UserList []*User `json:"user_list"` 12 | } 13 | 14 | type FansListResponse struct { 15 | StatusCode int64 `json:"status_code"` 16 | StatusMsg string `json:"status_msg"` 17 | UserList []*User `json:"user_list"` 18 | } 19 | 20 | type FriendListResponse struct { 21 | StatusCode int64 `json:"status_code"` 22 | StatusMsg string `json:"status_msg"` 23 | UserList []*FriendUser `json:"user_list"` 24 | } 25 | 26 | type FriendUser struct { 27 | Id int64 `json:"id"` 28 | Name string `json:"name"` 29 | FollowCount *int64 `json:"follow_count"` 30 | FollowerCount *int64 `json:"follower_count"` 31 | IsFollow bool `json:"is_follow"` 32 | Avatar *string `json:"avatar"` 33 | BackgroundImage *string `json:"background_image"` 34 | Signature *string `json:"signature"` 35 | TotalFavorited *int64 `json:"total_favorited"` 36 | WorkCount *int64 `json:"work_count"` 37 | FavoriteCount *int64 `json:"favorite_count"` 38 | ChatMessage string `json:"message"` 39 | MsgType int32 `json:"msgType"` 40 | } 41 | -------------------------------------------------------------------------------- /toktik-api/internal/model/response/user.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | type RegisterResponse struct { 4 | StatusCode int64 `json:"status_code"` 5 | StatusMsg string `json:"status_msg"` 6 | UserId uint `json:"user_id"` 7 | Token string `json:"token"` 8 | } 9 | 10 | type LoginResponse struct { 11 | StatusCode int64 ` json:"status_code"` 12 | StatusMsg string ` json:"status_msg"` 13 | UserId int64 `json:"user_id"` 14 | Token string ` json:"token"` 15 | } 16 | 17 | type UserIndexResponse struct { 18 | StatusCode int64 `json:"status_code"` 19 | StatusMsg string `json:"status_msg"` 20 | User *User `json:"user"` 21 | } 22 | 23 | type User struct { 24 | Id int64 `json:"id"` 25 | Name string `json:"name"` 26 | FollowCount *int64 `json:"follow_count"` 27 | FollowerCount *int64 `json:"follower_count"` 28 | IsFollow bool `json:"is_follow"` 29 | Avatar *string `json:"avatar"` 30 | BackgroundImage *string `json:"background_image"` 31 | Signature *string `json:"signature"` 32 | TotalFavorited *int64 `json:"total_favorited"` 33 | WorkCount *int64 `json:"work_count"` 34 | FavoriteCount *int64 `json:"favorite_count"` 35 | } 36 | -------------------------------------------------------------------------------- /toktik-api/internal/model/response/video.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | type VideoFeedResponse struct { 4 | StatusCode int64 `json:"status_code"` 5 | StatusMsg string `json:"status_msg"` 6 | NextTime int64 `json:"next_time"` 7 | VideoList []*Video `json:"video_list"` 8 | } 9 | 10 | type VideoPublishResponse struct { 11 | StatusCode int64 `json:"status_code"` 12 | StatusMsg string `json:"status_msg"` 13 | } 14 | 15 | type PublishListResponse struct { 16 | StatusCode int64 `json:"status_code"` 17 | StatusMsg string `json:"status_msg"` 18 | VideoList []*Video `json:"video_list"` 19 | } 20 | 21 | type Video struct { 22 | Id int64 `json:"id"` // 视频唯一标识 23 | Author *User `json:"author"` // 视频作者信息 24 | PlayUrl string `json:"play_url"` // 视频播放地址 25 | CoverUrl string `json:"cover_url"` // 视频封面地址 26 | FavoriteCount int64 `json:"favorite_count"` // 视频的点赞总数 27 | CommentCount int64 `json:"comment_count"` // 视频的评论总数 28 | IsFavorite bool `json:"is_favorite"` // true-已点赞,false-未点赞 29 | Title string `json:"title"` // 视频标题 30 | } 31 | -------------------------------------------------------------------------------- /toktik-api/pkg/middleware/limiter.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "context" 5 | sentinel "github.com/alibaba/sentinel-golang/api" 6 | "github.com/alibaba/sentinel-golang/core/base" 7 | "github.com/cloudwego/hertz/pkg/app" 8 | "github.com/cloudwego/hertz/pkg/common/utils" 9 | ) 10 | 11 | // LimitIP 对 ip 进行限流 12 | func LimitIP() app.HandlerFunc { 13 | return func(c context.Context, ctx *app.RequestContext) { 14 | ip := ctx.ClientIP() 15 | //fmt.Println("ip:", ip) 16 | entry, err := sentinel.Entry( 17 | "limit_ip", 18 | sentinel.WithResourceType(base.ResTypeWeb), 19 | sentinel.WithTrafficType(base.Inbound), 20 | sentinel.WithArgs(ip), 21 | ) 22 | if err != nil { 23 | ctx.AbortWithStatusJSON(400, utils.H{ 24 | "err": "too many request; the quota used up", 25 | "code": 10222, 26 | }) 27 | return 28 | } 29 | defer entry.Exit() 30 | ctx.Next(c) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /toktik-api/pkg/myerr/myerr.go: -------------------------------------------------------------------------------- 1 | package myerr 2 | 3 | import "toktik-common/errcode" 4 | 5 | var ( 6 | RegisterFailed = errcode.NewErr(100001, "用户注册失败") 7 | ErrUserNameORPassWord = errcode.NewErr(100002, "账号或密码格式错误") 8 | CanNotChatSelf = errcode.NewErr(100003, "不能自言自语哦~") 9 | ) 10 | -------------------------------------------------------------------------------- /toktik-api/pkg/router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "context" 5 | "github.com/cloudwego/hertz/pkg/app" 6 | "github.com/cloudwego/hertz/pkg/app/server" 7 | "github.com/cloudwego/hertz/pkg/common/utils" 8 | ) 9 | 10 | var routers []Router 11 | 12 | type Router interface { 13 | Route(r *server.Hertz) 14 | } 15 | 16 | func Register(root ...Router) { 17 | routers = append(routers, root...) 18 | } 19 | 20 | func InitRouter(r *server.Hertz) { 21 | r.GET("/ping", func(ctx context.Context, c *app.RequestContext) { 22 | c.JSON(200, utils.H{"msg": "pong"}) 23 | }) 24 | for _, root := range routers { 25 | root.Route(r) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /toktik-api/pkg/sentinel/sentinel.go: -------------------------------------------------------------------------------- 1 | package sentinel 2 | 3 | import ( 4 | sentinel "github.com/alibaba/sentinel-golang/api" 5 | "github.com/alibaba/sentinel-golang/core/flow" 6 | "github.com/alibaba/sentinel-golang/core/hotspot" 7 | "go.uber.org/zap" 8 | ) 9 | 10 | func InitSentinel() { 11 | err := sentinel.InitDefault() 12 | if err != nil { 13 | zap.L().Error("sentinel.InitDefault(),err:", zap.Error(err)) 14 | // log.Fatalf("Unexpected error: %+v", err) 15 | } 16 | _, err = flow.LoadRules([]*flow.Rule{ 17 | { 18 | Resource: "api", 19 | Threshold: 100, 20 | TokenCalculateStrategy: flow.Direct, 21 | ControlBehavior: flow.Reject, 22 | StatIntervalInMs: 1000, 23 | }, 24 | }) 25 | if err != nil { 26 | zap.L().Error("flow.LoadRules([]*flow.Rule,err:", zap.Error(err)) 27 | // log.Fatalf("Unexpected error: %+v", err) 28 | return 29 | } 30 | 31 | _, err = hotspot.LoadRules([]*hotspot.Rule{ 32 | { 33 | Resource: "limit_ip", 34 | MetricType: hotspot.QPS, 35 | ControlBehavior: hotspot.Reject, 36 | ParamIndex: 15, 37 | Threshold: 2, 38 | BurstCount: 0, // 控制令牌 39 | DurationInSec: 3, 40 | }, 41 | }) 42 | if err != nil { 43 | zap.L().Error("hotspot.LoadRules([]*hotspot.Rule,err:", zap.Error(err)) 44 | // log.Fatalf("Unexpected error: %+v", err) 45 | return 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /toktik-api/pkg/setting/enter.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | var Settings []InitSettings 4 | 5 | type InitSettings interface { 6 | InitSetting() 7 | } 8 | 9 | func InitAllSetting() { 10 | InitBootStrap() 11 | for _, setting := range Settings { 12 | setting.InitSetting() 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /toktik-api/pkg/setting/logger.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "go.uber.org/zap" 5 | "toktik-api/internal/global" 6 | "toktik-common/logger" 7 | ) 8 | 9 | func init() { 10 | l := log{} 11 | Settings = append(Settings, l) 12 | } 13 | 14 | type log struct { 15 | } 16 | 17 | // InitSetting 日志初始化 18 | func (log) InitSetting() { 19 | global.Logger = logger.NewLogger(&logger.InitStruct{ 20 | LogSavePath: global.Settings.Logger.LogSavePath, 21 | LogFileExt: global.Settings.Logger.LogFileExt, 22 | MaxSize: global.Settings.Logger.MaxSize, 23 | MaxBackups: global.Settings.Logger.MaxBackups, 24 | MaxAge: global.Settings.Logger.MaxAge, 25 | Compress: global.Settings.Logger.Compress, 26 | LowLevelFile: global.Settings.Logger.LowLevelFile, 27 | HighLevelFile: global.Settings.Logger.HighLevelFile, 28 | }, global.Settings.Logger.Level) 29 | zap.ReplaceGlobals(global.Logger.Logger) 30 | } 31 | -------------------------------------------------------------------------------- /toktik-api/pkg/setting/maker.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "toktik-api/internal/global" 5 | "toktik-common/token" 6 | ) 7 | 8 | func init() { 9 | m := maker{} 10 | Settings = append(Settings, m) 11 | } 12 | 13 | type maker struct { 14 | } 15 | 16 | // InitSetting 初始化 17 | func (maker) InitSetting() { 18 | var err error 19 | global.Maker, err = token.NewPasetoMaker(global.Settings.Token.Key) 20 | if err != nil { 21 | panic(err) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /toktik-api/pkg/setting/nacos.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "github.com/nacos-group/nacos-sdk-go/v2/clients" 5 | "github.com/nacos-group/nacos-sdk-go/v2/clients/config_client" 6 | "github.com/nacos-group/nacos-sdk-go/v2/common/constant" 7 | "github.com/nacos-group/nacos-sdk-go/v2/vo" 8 | log2 "log" 9 | "toktik-api/internal/global" 10 | ) 11 | 12 | type NacosClient struct { 13 | confClient config_client.IConfigClient 14 | } 15 | 16 | func InitNacos() *NacosClient { 17 | clientConfig := constant.ClientConfig{ 18 | NamespaceId: global.Settings.Nacos.Namespace, //we can create multiple clients with different namespaceId to support multiple namespace.When namespace is public, fill in the blank string here. 19 | TimeoutMs: 5000, 20 | NotLoadCacheAtStart: true, 21 | LogDir: "/tmp/nacos/log", 22 | CacheDir: "/tmp/nacos/cache", 23 | LogLevel: "debug", 24 | } 25 | serverConfigs := []constant.ServerConfig{ 26 | { 27 | IpAddr: global.Settings.Nacos.Addr, 28 | ContextPath: global.Settings.Nacos.ContextPath, 29 | Port: uint64(global.Settings.Nacos.Port), 30 | Scheme: global.Settings.Nacos.Scheme, 31 | }, 32 | } 33 | configClient, err := clients.NewConfigClient( 34 | vo.NacosClientParam{ 35 | ClientConfig: &clientConfig, 36 | ServerConfigs: serverConfigs, 37 | }, 38 | ) 39 | if err != nil { 40 | log2.Fatalln(err) 41 | } 42 | nc := &NacosClient{ 43 | confClient: configClient, 44 | } 45 | return nc 46 | } 47 | -------------------------------------------------------------------------------- /toktik-api/pkg/tracing/jaeger.go: -------------------------------------------------------------------------------- 1 | package tracing 2 | 3 | import ( 4 | "go.opentelemetry.io/otel/exporters/jaeger" 5 | "go.opentelemetry.io/otel/sdk/resource" 6 | sdktrace "go.opentelemetry.io/otel/sdk/trace" 7 | semconv "go.opentelemetry.io/otel/semconv/v1.4.0" 8 | "toktik-api/internal/global" 9 | "toktik-api/internal/model" 10 | ) 11 | 12 | func JaegerTraceProvider() (*sdktrace.TracerProvider, error) { 13 | exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(global.Settings.Jaeger.HTTPEndpoint))) 14 | if err != nil { 15 | return nil, err 16 | } 17 | tp := sdktrace.NewTracerProvider( 18 | sdktrace.WithBatcher(exp), 19 | sdktrace.WithResource(resource.NewWithAttributes( 20 | semconv.SchemaURL, 21 | semconv.ServiceNameKey.String(global.Settings.Jaeger.ServerName[model.TokTikApi]), 22 | semconv.DeploymentEnvironmentKey.String("dev"), 23 | )), 24 | ) 25 | return tp, nil 26 | } 27 | -------------------------------------------------------------------------------- /toktik-chat/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine 2 | WORKDIR /app 3 | 4 | COPY ./bin/toktik-chat . 5 | COPY ./config . 6 | RUN mkdir config && mv config.yaml config/config.yaml && mv bootstrap.yaml config/bootstrap.yaml 7 | 8 | RUN chmod +x ./toktik-chat 9 | 10 | EXPOSE 8084 8884 9194 11 | ENTRYPOINT ["./toktik-chat"] 12 | -------------------------------------------------------------------------------- /toktik-chat/config/bootstrap.yaml.template: -------------------------------------------------------------------------------- 1 | Nacos: 2 | Namespace: "xxx" # 命名空间id 3 | Group: "api" # group名称 4 | Addr: "xxx.xxx.xxx.xxx" # 地址 5 | Port: 8848 6 | Scheme: "http" 7 | ContextPath: "/nacos" -------------------------------------------------------------------------------- /toktik-chat/config/config.yaml.template: -------------------------------------------------------------------------------- 1 | Server: 2 | Name: "toktik-chat" 3 | RunMode: "DEBUG" 4 | Addr: "0.0.0.0:8084" 5 | DefaultContextTimeout: 10s 6 | 7 | Rules: 8 | DefaultUserAvatar: "https://q1.qlogo.cn/g?b=qq&nk=1780006511&s=640" 9 | DefaultUserSignature: "这里什么有没有捏" 10 | VideoInfoCacheExpire: 720h 11 | FavoriteInfoCacheExpire: 168h 12 | 13 | Jaeger: 14 | RPCExportEndpoint: "xxx:4317" 15 | ServerName: 16 | toktik-api: "toktik-api" 17 | toktik-user: "toktik-user" 18 | toktik-video: "toktik-video" 19 | toktik-interaction: "toktik-interaction" 20 | toktik-chat: "toktik-chat" 21 | toktik-favor: "toktik-favor" 22 | toktik-comment: "toktik-comment" 23 | 24 | Prometheus: 25 | Post: ":9194" 26 | Path: "/metrics" 27 | 28 | RPC: 29 | Name: "rpc-chat" 30 | Addr: "0.0.0.0:8884" 31 | 32 | Logger: 33 | Level: "debug" 34 | LogSavePath: "storage/applogs/" 35 | LogFileExt: ".log" 36 | MaxSize: 10 37 | MaxBackups: 7 38 | MaxAge: 30 39 | Compress: false 40 | LowLevelFile: "info" 41 | HighLevelFile: "error" 42 | 43 | Mysql: 44 | Username: "root" 45 | Password: "xxx" 46 | Host: "xxx" 47 | Port: "3309" 48 | DB: "toktik_chat" 49 | 50 | Redis: 51 | Host: "xxx" 52 | Port: "6379" 53 | Password: "" 54 | DB: 4 55 | 56 | Etcd: 57 | Addr: 58 | - "xxx:2379" 59 | 60 | -------------------------------------------------------------------------------- /toktik-chat/go.mod: -------------------------------------------------------------------------------- 1 | module toktik-chat 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /toktik-chat/internal/dao/enter.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "github.com/go-redis/redis/v8" 5 | "gorm.io/gorm" 6 | ) 7 | 8 | type group struct { 9 | Mdb *gorm.DB 10 | Rdb *redis.Client 11 | } 12 | 13 | var Group = new(group) 14 | -------------------------------------------------------------------------------- /toktik-chat/internal/dao/mysql/chat.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import ( 4 | "context" 5 | "time" 6 | "toktik-chat/internal/model/auto" 7 | ) 8 | 9 | type ChatDao struct { 10 | conn *GormConn 11 | } 12 | 13 | func NewChatDao() *ChatDao { 14 | return &ChatDao{ 15 | conn: NewGormConn(), 16 | } 17 | } 18 | 19 | // 按升序取消息 20 | 21 | func (cd *ChatDao) GetMessageList(c context.Context, userId, targetId int64, preMsgTime int64) ([]*auto.Message, error) { 22 | messageList := make([]*auto.Message, 0) 23 | preMsgTime = preMsgTime / 100 24 | err := cd.conn.Session(c).Model(&auto.Message{}). 25 | Where("user_id = ? AND to_user_id = ?", userId, targetId). 26 | Where("created_at > ?", time.Unix(preMsgTime, 0)). 27 | Order("created_at asc").Find(&messageList).Error 28 | return nil, err 29 | } 30 | -------------------------------------------------------------------------------- /toktik-chat/internal/dao/mysql/tran.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // Transaction 事务的操作 一定跟数据库有关 注入数据库的连接 gorm.db 4 | type Transaction interface { 5 | Action(func(conn DbConn) error) error 6 | } 7 | 8 | type DbConn interface { 9 | Begin() 10 | Rollback() 11 | Commit() 12 | } 13 | 14 | type TransactionImpl struct { 15 | conn DbConn 16 | } 17 | 18 | func (t TransactionImpl) Action(f func(conn DbConn) error) error { 19 | t.conn.Begin() 20 | err := f(t.conn) 21 | if err != nil { 22 | t.conn.Rollback() 23 | return err 24 | } 25 | t.conn.Commit() 26 | return nil 27 | } 28 | 29 | func NewTransaction() *TransactionImpl { 30 | return &TransactionImpl{ 31 | conn: NewTran(), 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /toktik-chat/internal/global/root.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | "path/filepath" 7 | "sync" 8 | ) 9 | 10 | /* 11 | 用于推断当前项目根路径 12 | */ 13 | 14 | var ( 15 | RootDir string // 项目根路径 16 | once = new(sync.Once) 17 | ) 18 | 19 | func exist(filePath string) bool { 20 | _, err := os.Stat(filePath) 21 | return err == nil || errors.Is(err, os.ErrExist) 22 | } 23 | 24 | // 计算项目路径 25 | func inferRootDir() string { 26 | cwd, err := os.Getwd() 27 | if err != nil { 28 | panic(err) 29 | } 30 | var infer func(path string) string 31 | infer = func(path string) string { 32 | if exist(path + "/config") { 33 | return path 34 | } 35 | return infer(filepath.Dir(path)) 36 | } 37 | return infer(cwd) 38 | } 39 | 40 | func init() { 41 | once.Do(func() { 42 | RootDir = inferRootDir() 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /toktik-chat/internal/global/var.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "toktik-chat/internal/model/config" 5 | "toktik-common/logger" 6 | "toktik-common/utils" 7 | ) 8 | 9 | var ( 10 | Settings config.Config // Public配置 11 | Logger *logger.Log // 日志 12 | SnowFlake *utils.SnowFlake 13 | //RdbClient *redis.RdbCache 14 | ) 15 | -------------------------------------------------------------------------------- /toktik-chat/internal/model/auto/base.go: -------------------------------------------------------------------------------- 1 | package auto 2 | 3 | import ( 4 | "gorm.io/gorm" 5 | "time" 6 | ) 7 | 8 | type BaseModel struct { 9 | ID uint `gorm:"primarykey" json:"id,string"` 10 | CreatedAt time.Time `json:"created_at,string"` 11 | UpdatedAt time.Time `json:"updated_at,string"` 12 | DeletedAt gorm.DeletedAt `json:"deleted_at,string"` 13 | } 14 | -------------------------------------------------------------------------------- /toktik-chat/internal/model/auto/chat.go: -------------------------------------------------------------------------------- 1 | package auto 2 | 3 | import ( 4 | "strconv" 5 | ) 6 | 7 | type Message struct { 8 | BaseModel 9 | UserId uint `json:"from_user_id" gorm:"index:idx_message;not null"` 10 | ToUserId uint `json:"to_user_id" gorm:"index:idx_message;not null"` 11 | Content string `json:"content" gorm:"not null"` 12 | } 13 | 14 | func CreateChatHistoryKey(userId, targetId int64) string { 15 | if userId < targetId { 16 | return "chat::history::" + strconv.FormatInt(userId, 10) + "+" + strconv.FormatInt(targetId, 10) 17 | } 18 | return "chat::history::" + strconv.FormatInt(targetId, 10) + "+" + strconv.FormatInt(userId, 10) 19 | } 20 | 21 | func CreateChatMessageKey(userId, targetId int64) string { 22 | if userId < targetId { 23 | return "chat::message::" + strconv.FormatInt(userId, 10) + "+" + strconv.FormatInt(targetId, 10) 24 | } 25 | return "chat::message::" + strconv.FormatInt(targetId, 10) + "+" + strconv.FormatInt(userId, 10) 26 | } 27 | 28 | func CreateMessageContent(userId, targetId int64, content string) string { 29 | return strconv.FormatInt(userId, 10) + "+" + strconv.FormatInt(targetId, 10) + "+" + content 30 | } 31 | -------------------------------------------------------------------------------- /toktik-chat/internal/model/constants.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | const ( 4 | MsgNil = "" 5 | ) 6 | 7 | const ( 8 | RpcUser = "rpc-user" 9 | RpcVideo = "rpc-video" 10 | RpcInteraction = "rpc-interaction" 11 | RpcChat = "rpc-chat" 12 | ) 13 | const ( 14 | RpcSuccess = 0 15 | ) 16 | const ( 17 | TokTikApi = "toktik-api" 18 | TokTikUser = "toktik-user" 19 | TokTikInteraction = "toktik-interaction" 20 | TokTikVideo = "toktik-video" 21 | TokTikChat = "toktik-chat" 22 | TokTikFavor = "toktik-favor" 23 | TokTikComment = "toktik-comment" 24 | ) 25 | -------------------------------------------------------------------------------- /toktik-chat/internal/model/handler_resp.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "toktik-common/errcode" 5 | "toktik-rpc/kitex_gen/chat" 6 | ) 7 | 8 | type ChatActionHandler struct { 9 | } 10 | 11 | func (ChatActionHandler) ChatActionResponse(err errcode.Err, msg string, resp *chat.ChatActionResponse) *chat.ChatActionResponse { 12 | resp.StatusCode = err.ECode() 13 | resp.StatusMsg = err.Error() + ":" + msg 14 | return resp 15 | } 16 | 17 | type MessageListHandler struct { 18 | } 19 | 20 | func (MessageListHandler) MessageListResponse(err errcode.Err, msg string, resp *chat.MessageListResponse) *chat.MessageListResponse { 21 | resp.StatusCode = err.ECode() 22 | resp.StatusMsg = err.Error() + ":" + msg 23 | return resp 24 | } 25 | 26 | type GetFriendLatestMessageHandler struct { 27 | } 28 | 29 | func (MessageListHandler) GetFriendLatestMessageResponse(err errcode.Err, msg string, resp *chat.GetFriendLatestMessageResponse) *chat.GetFriendLatestMessageResponse { 30 | resp.StatusCode = err.ECode() 31 | resp.StatusMsg = err.Error() + ":" + msg 32 | return resp 33 | } 34 | -------------------------------------------------------------------------------- /toktik-chat/internal/repo/cache.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "context" 5 | "toktik-chat/internal/model/auto" 6 | ) 7 | 8 | type RCacheRepo interface { 9 | KeyExist(c context.Context, key string) (bool, error) 10 | PushHistoryMessage(c context.Context, key string, time float64, content string) error 11 | PushDBMessage(c context.Context, key string, time float64, content string) error 12 | ZRangeMessageList(c context.Context, key string, preMsgTime int64) ([]*auto.Message, error) 13 | PushManyHistoryMessage(c context.Context, key string, messageInfos []*auto.Message) error 14 | ZGetFriendLatestMessage(c context.Context, key string, myUserId int64) (string, int32, error) 15 | } 16 | -------------------------------------------------------------------------------- /toktik-chat/internal/repo/chat.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "context" 5 | "toktik-chat/internal/model/auto" 6 | ) 7 | 8 | type ChatRepo interface { 9 | GetMessageList(c context.Context, userId, targetId int64, preMsgTime int64) ([]*auto.Message, error) 10 | } 11 | -------------------------------------------------------------------------------- /toktik-chat/internal/repo/handler_resp.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "toktik-chat/internal/model" 5 | "toktik-common/errcode" 6 | "toktik-rpc/kitex_gen/chat" 7 | ) 8 | 9 | type HandlerResp interface { 10 | ChatActionResponse(err errcode.Err, msg string, resp *chat.ChatActionResponse) *chat.ChatActionResponse 11 | MessageListResponse(err errcode.Err, msg string, resp *chat.MessageListResponse) *chat.MessageListResponse 12 | GetFriendLatestMessageResponse(err errcode.Err, msg string, resp *chat.GetFriendLatestMessageResponse) *chat.GetFriendLatestMessageResponse 13 | } 14 | 15 | type HandlerResps struct { 16 | model.ChatActionHandler 17 | model.MessageListHandler 18 | model.GetFriendLatestMessageHandler 19 | } 20 | 21 | func NewHandlerResps() *HandlerResps { 22 | return &HandlerResps{} 23 | } 24 | -------------------------------------------------------------------------------- /toktik-chat/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "toktik-chat/internal/global" 6 | "toktik-chat/pkg/rpc" 7 | "toktik-chat/pkg/rpc/client" 8 | "toktik-chat/pkg/setting" 9 | ) 10 | 11 | func main() { 12 | // 初始化 13 | setting.InitAllSetting() 14 | fmt.Printf("config:%#v\n", global.Settings) 15 | 16 | // RPC 注册 17 | client.NewRpcClient() 18 | fmt.Println("-----Chat Service Start ! ! !-----") 19 | rpc.RegisterRPC() 20 | } 21 | -------------------------------------------------------------------------------- /toktik-chat/pkg/myerr/myerr.go: -------------------------------------------------------------------------------- 1 | package myerr 2 | 3 | import "toktik-common/errcode" 4 | 5 | var ( 6 | IsNotFriend = errcode.NewErr(400001, "不是朋友关系!") 7 | MessageCanNotEmpty = errcode.NewErr(400002, "不能发空消息哦~!") 8 | ) 9 | -------------------------------------------------------------------------------- /toktik-chat/pkg/rpc/client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | func NewRpcClient() { 4 | InitRpcInteractionClient() 5 | } 6 | -------------------------------------------------------------------------------- /toktik-chat/pkg/rpc/client/interaction.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/cloudwego/kitex/client" 5 | "github.com/kitex-contrib/obs-opentelemetry/provider" 6 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 7 | etcd "github.com/kitex-contrib/registry-etcd" 8 | "go.uber.org/zap" 9 | "toktik-chat/internal/global" 10 | "toktik-chat/internal/model" 11 | "toktik-rpc/kitex_gen/interaction/interactionservice" 12 | ) 13 | 14 | var InteractionClient interactionservice.Client 15 | 16 | func InitRpcInteractionClient() { 17 | r, err := etcd.NewEtcdResolver(global.Settings.Etcd.Addr) 18 | if err != nil { 19 | panic(err) 20 | } 21 | provider.NewOpenTelemetryProvider( 22 | provider.WithServiceName(global.Settings.Jaeger.ServerName[model.TokTikInteraction]), 23 | provider.WithExportEndpoint(global.Settings.Jaeger.RPCExportEndpoint), 24 | provider.WithInsecure(), 25 | ) 26 | 27 | c, err := interactionservice.NewClient( 28 | model.RpcInteraction, 29 | client.WithSuite(tracing.NewClientSuite()), 30 | //client.WithHostPorts(global.PbSettings.Rpc.ServerAddrs[model.RpcInteraction]), 31 | //client.WithMiddleware(rpcmiddleware.CommonMiddleware), 32 | //client.WithMiddleware(rpcmiddleware.ClientMiddleware), 33 | client.WithResolver(r), 34 | ) 35 | if err != nil { 36 | zap.L().Error("InitRpcInteractionClient err:", zap.Error(err)) 37 | panic(err) 38 | } 39 | InteractionClient = c 40 | } 41 | -------------------------------------------------------------------------------- /toktik-chat/pkg/setting/dao.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "toktik-chat/internal/cache" 5 | "toktik-chat/internal/dao" 6 | "toktik-chat/internal/dao/cron" 7 | "toktik-chat/internal/dao/mysql" 8 | ) 9 | 10 | func init() { 11 | d := Dao{} 12 | Settings = append(Settings, d) 13 | } 14 | 15 | type Dao struct { 16 | } 17 | 18 | func (Dao) InitSetting() { 19 | mysql.InitMysql() 20 | //global.RdbClient = redis.InitRedis() 21 | dao.Group.Rdb = cache.InitRedis() 22 | go cron.TimingJob() 23 | } 24 | -------------------------------------------------------------------------------- /toktik-chat/pkg/setting/enter.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | var Settings []InitSettings 4 | 5 | type InitSettings interface { 6 | InitSetting() 7 | } 8 | 9 | func InitAllSetting() { 10 | InitBootStrap() 11 | for _, setting := range Settings { 12 | setting.InitSetting() 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /toktik-chat/pkg/setting/logger.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "go.uber.org/zap" 5 | "toktik-chat/internal/global" 6 | "toktik-common/logger" 7 | ) 8 | 9 | func init() { 10 | l := log{} 11 | Settings = append(Settings, l) 12 | } 13 | 14 | type log struct { 15 | } 16 | 17 | // InitSetting 日志初始化 18 | func (log) InitSetting() { 19 | global.Logger = logger.NewLogger(&logger.InitStruct{ 20 | LogSavePath: global.Settings.Logger.LogSavePath, 21 | LogFileExt: global.Settings.Logger.LogFileExt, 22 | MaxSize: global.Settings.Logger.MaxSize, 23 | MaxBackups: global.Settings.Logger.MaxBackups, 24 | MaxAge: global.Settings.Logger.MaxAge, 25 | Compress: global.Settings.Logger.Compress, 26 | LowLevelFile: global.Settings.Logger.LowLevelFile, 27 | HighLevelFile: global.Settings.Logger.HighLevelFile, 28 | }, global.Settings.Logger.Level) 29 | zap.ReplaceGlobals(global.Logger.Logger) 30 | } 31 | -------------------------------------------------------------------------------- /toktik-chat/pkg/setting/nacos.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "github.com/nacos-group/nacos-sdk-go/v2/clients" 5 | "github.com/nacos-group/nacos-sdk-go/v2/clients/config_client" 6 | "github.com/nacos-group/nacos-sdk-go/v2/common/constant" 7 | "github.com/nacos-group/nacos-sdk-go/v2/vo" 8 | log2 "log" 9 | "toktik-chat/internal/global" 10 | ) 11 | 12 | type NacosClient struct { 13 | confClient config_client.IConfigClient 14 | } 15 | 16 | func InitNacos() *NacosClient { 17 | clientConfig := constant.ClientConfig{ 18 | NamespaceId: global.Settings.Nacos.Namespace, //we can create multiple clients with different namespaceId to support multiple namespace.When namespace is public, fill in the blank string here. 19 | TimeoutMs: 5000, 20 | NotLoadCacheAtStart: true, 21 | LogDir: "/tmp/nacos/log", 22 | CacheDir: "/tmp/nacos/cache", 23 | LogLevel: "debug", 24 | } 25 | serverConfigs := []constant.ServerConfig{ 26 | { 27 | IpAddr: global.Settings.Nacos.Addr, 28 | ContextPath: global.Settings.Nacos.ContextPath, 29 | Port: uint64(global.Settings.Nacos.Port), 30 | Scheme: global.Settings.Nacos.Scheme, 31 | }, 32 | } 33 | configClient, err := clients.NewConfigClient( 34 | vo.NacosClientParam{ 35 | ClientConfig: &clientConfig, 36 | ServerConfigs: serverConfigs, 37 | }, 38 | ) 39 | if err != nil { 40 | log2.Fatalln(err) 41 | } 42 | nc := &NacosClient{ 43 | confClient: configClient, 44 | } 45 | return nc 46 | } 47 | -------------------------------------------------------------------------------- /toktik-comment/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine 2 | WORKDIR /app 3 | COPY ./bin/toktik-comment . 4 | COPY ./config . 5 | RUN mkdir config && mv config.yaml config/config.yaml && mv bootstrap.yaml config/bootstrap.yaml 6 | 7 | RUN chmod +x ./toktik-comment 8 | 9 | EXPOSE 8086 8886 9196 10 | ENTRYPOINT ["./toktik-comment"] 11 | -------------------------------------------------------------------------------- /toktik-comment/config/bootstrap.yaml.template: -------------------------------------------------------------------------------- 1 | Nacos: 2 | Namespace: "xxx" # 命名空间id 3 | Group: "api" # group名称 4 | Addr: "xxx.xxx.xxx.xxx" # 地址 5 | Port: 8848 6 | Scheme: "http" 7 | ContextPath: "/nacos" -------------------------------------------------------------------------------- /toktik-comment/config/config.yaml.template: -------------------------------------------------------------------------------- 1 | Server: 2 | Name: "toktik-comment" 3 | RunMode: "DEBUG" 4 | Addr: "0.0.0.0:8086" 5 | DefaultContextTimeout: 10s 6 | 7 | Rules: 8 | 9 | Jaeger: 10 | RPCExportEndpoint: "xxx:4317" 11 | ServerName: 12 | toktik-api: "toktik-api" 13 | toktik-user: "toktik-user" 14 | toktik-video: "toktik-video" 15 | toktik-interaction: "toktik-interaction" 16 | toktik-chat: "toktik-chat" 17 | toktik-favor: "toktik-favor" 18 | toktik-comment: "toktik-comment" 19 | 20 | Prometheus: 21 | Post: ":9196" 22 | Path: "/metrics" 23 | 24 | RPC: 25 | Name: "rpc-comment" 26 | Addr: "0.0.0.0:8886" 27 | 28 | Logger: 29 | Level: "debug" 30 | LogSavePath: "storage/applogs/" 31 | LogFileExt: ".log" 32 | MaxSize: 10 33 | MaxBackups: 7 34 | MaxAge: 30 35 | Compress: false 36 | LowLevelFile: "info" 37 | HighLevelFile: "error" 38 | 39 | Mysql: 40 | Username: "root" 41 | Password: "xxx" 42 | Host: "xxx.135" 43 | Port: "3309" 44 | DB: "toktik_comment" 45 | 46 | Redis: 47 | Host: "xxx" 48 | Port: "6379" 49 | Password: "" 50 | DB: 6 51 | 52 | Etcd: 53 | Addr: 54 | - "xxx:2379" 55 | -------------------------------------------------------------------------------- /toktik-comment/internal/dao/enter.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "github.com/go-redis/redis/v8" 5 | "gorm.io/gorm" 6 | ) 7 | 8 | type group struct { 9 | Mdb *gorm.DB 10 | Rdb *redis.Client 11 | } 12 | 13 | var Group = new(group) 14 | -------------------------------------------------------------------------------- /toktik-comment/internal/dao/mysql/tran.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // Transaction 事务的操作 一定跟数据库有关 注入数据库的连接 gorm.db 4 | type Transaction interface { 5 | Action(func(conn DbConn) error) error 6 | } 7 | 8 | type DbConn interface { 9 | Begin() 10 | Rollback() 11 | Commit() 12 | } 13 | 14 | type TransactionImpl struct { 15 | conn DbConn 16 | } 17 | 18 | func (t TransactionImpl) Action(f func(conn DbConn) error) error { 19 | t.conn.Begin() 20 | err := f(t.conn) 21 | if err != nil { 22 | t.conn.Rollback() 23 | return err 24 | } 25 | t.conn.Commit() 26 | return nil 27 | } 28 | 29 | func NewTransaction() *TransactionImpl { 30 | return &TransactionImpl{ 31 | conn: NewTran(), 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /toktik-comment/internal/dao/mysql/video.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import ( 4 | "context" 5 | "toktik-comment/internal/model/auto" 6 | ) 7 | 8 | type CommentDao struct { 9 | conn *GormConn 10 | } 11 | 12 | func NewCommentDao() *CommentDao { 13 | return &CommentDao{ 14 | conn: NewGormConn(), 15 | } 16 | } 17 | 18 | func (v *CommentDao) CreateComment(c context.Context, commentInfo *auto.Comment) error { 19 | return v.conn.Session(c).Create(commentInfo).Error 20 | } 21 | 22 | func (v *CommentDao) DeleteComment(c context.Context, commentInfo *auto.Comment) error { 23 | return v.conn.Session(c).Model(&auto.Comment{}).Where("id = ?", commentInfo.ID).Unscoped().Delete(commentInfo).Error 24 | } 25 | 26 | func (v *CommentDao) GetCommentAuthorIds(c context.Context, videoId int64) ([]int64, error) { 27 | userIds := make([]int64, 0) 28 | err := v.conn.Session(c).Model(&auto.Comment{}). 29 | Where("video_id = ?", videoId). 30 | Order("created_at desc"). 31 | Pluck("user_id", &userIds).Error 32 | return userIds, err 33 | } 34 | 35 | func (v *CommentDao) GetCommentList(c context.Context, videoId int64) ([]*auto.Comment, error) { 36 | commentInfos := make([]*auto.Comment, 0) 37 | err := v.conn.Session(c).Model(&auto.Comment{}). 38 | Where("video_id = ?", videoId). 39 | Order("created_at desc"). 40 | Find(&commentInfos).Error 41 | return commentInfos, err 42 | } 43 | -------------------------------------------------------------------------------- /toktik-comment/internal/global/root.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | "path/filepath" 7 | "sync" 8 | ) 9 | 10 | /* 11 | 用于推断当前项目根路径 12 | */ 13 | 14 | var ( 15 | RootDir string // 项目根路径 16 | once = new(sync.Once) 17 | ) 18 | 19 | func exist(filePath string) bool { 20 | _, err := os.Stat(filePath) 21 | return err == nil || errors.Is(err, os.ErrExist) 22 | } 23 | 24 | // 计算项目路径 25 | func inferRootDir() string { 26 | cwd, err := os.Getwd() 27 | if err != nil { 28 | panic(err) 29 | } 30 | var infer func(path string) string 31 | infer = func(path string) string { 32 | if exist(path + "/config") { 33 | return path 34 | } 35 | return infer(filepath.Dir(path)) 36 | } 37 | return infer(cwd) 38 | } 39 | 40 | func init() { 41 | once.Do(func() { 42 | RootDir = inferRootDir() 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /toktik-comment/internal/global/var.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "toktik-comment/internal/model/config" 5 | "toktik-common/logger" 6 | "toktik-common/utils" 7 | ) 8 | 9 | var ( 10 | Settings config.Config // Public配置 11 | Logger *logger.Log // 日志 12 | SnowFlake *utils.SnowFlake 13 | ) 14 | -------------------------------------------------------------------------------- /toktik-comment/internal/model/auto/base.go: -------------------------------------------------------------------------------- 1 | package auto 2 | 3 | import ( 4 | "gorm.io/gorm" 5 | "strconv" 6 | "time" 7 | ) 8 | 9 | type BaseModel struct { 10 | ID uint `gorm:"primarykey" json:"id,string"` 11 | CreatedAt time.Time `json:"created_at,string"` 12 | UpdatedAt time.Time `json:"updated_at,string"` 13 | DeletedAt gorm.DeletedAt `json:"deleted_at,string"` 14 | } 15 | 16 | func CreateUserKey(userId uint) string { 17 | return "user_info::" + strconv.FormatInt(int64(userId), 10) 18 | } 19 | 20 | func CreatePublishKey() string { 21 | return "video_publish" 22 | } 23 | 24 | func CreateUserVideoKey(userId uint) string { 25 | return "user_video::" + strconv.FormatInt(int64(userId), 10) 26 | } 27 | -------------------------------------------------------------------------------- /toktik-comment/internal/model/auto/comment.go: -------------------------------------------------------------------------------- 1 | package auto 2 | 3 | import "strconv" 4 | 5 | // Comment 评论表 / 6 | type Comment struct { 7 | BaseModel 8 | VideoId uint `json:"video_id,string" gorm:"not null;index:comment_video"` 9 | UserId uint `json:"user_id,string" gorm:"not null"` 10 | Content string `json:"content,string" gorm:"not null"` 11 | } 12 | 13 | func (*Comment) TableName() string { 14 | return "comment" 15 | } 16 | 17 | // Key Value: comment_id+content 18 | 19 | func CreateCommentKey(videoId int64) string { 20 | return "video_comment::" + strconv.FormatInt(videoId, 10) 21 | } 22 | 23 | func CreateCommentValue(commentId, userId int64, content string) string { 24 | return strconv.FormatInt(commentId, 10) + "+" + strconv.FormatInt(userId, 10) + "+" + content 25 | } 26 | -------------------------------------------------------------------------------- /toktik-comment/internal/model/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import "time" 4 | 5 | type Config struct { 6 | Logger Logger 7 | Server Server 8 | Rpc Rpc 9 | Rules Rules 10 | Mysql Mysql 11 | Redis Redis 12 | Etcd Etcd 13 | Nacos Nacos 14 | Jaeger Jaeger 15 | Prometheus Prometheus 16 | } 17 | 18 | type Nacos struct { 19 | Namespace string `json:"namespace"` 20 | Group string `json:"group"` 21 | Addr string `json:"addr"` 22 | Port int `json:"port"` 23 | Scheme string `json:"scheme"` 24 | ContextPath string `json:"context_path"` 25 | } 26 | 27 | type Server struct { 28 | Name string 29 | RunMode string 30 | Addr string 31 | DefaultContextTimeout time.Duration 32 | } 33 | 34 | type Rules struct { 35 | } 36 | 37 | type Jaeger struct { 38 | RPCExportEndpoint string 39 | ServerName map[string]string 40 | } 41 | 42 | type Prometheus struct { 43 | Post string 44 | Path string 45 | } 46 | 47 | type Rpc struct { 48 | Name string 49 | Addr string 50 | } 51 | 52 | type Etcd struct { 53 | Addr []string 54 | } 55 | 56 | type Logger struct { 57 | Level string 58 | LogSavePath string // 保存路径 59 | LogFileExt string // 日志文件后缀 60 | MaxSize int // 备份的大小(M) 61 | MaxBackups int // 最大备份数 62 | MaxAge int // 最大备份天数 63 | Compress bool // 是否压缩过期日志 64 | LowLevelFile string // 低级别文件名 65 | HighLevelFile string // 高级别文件名 66 | } 67 | 68 | type Mysql struct { 69 | Username string 70 | Password string 71 | Host string 72 | Port string 73 | DB string 74 | } 75 | 76 | type Redis struct { 77 | Host string 78 | Port string 79 | Password string 80 | DB int 81 | } 82 | -------------------------------------------------------------------------------- /toktik-comment/internal/model/constants.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | const ( 4 | MsgNil = "" 5 | ) 6 | 7 | type FileType string 8 | 9 | const ( 10 | RpcSuccess = 0 11 | ) 12 | 13 | const ( 14 | COMMENT = iota + 1 15 | CANCELCOMMENT 16 | ) 17 | const ( 18 | RpcUser = "rpc-user" 19 | RpcVideo = "rpc-video" 20 | RpcInteraction = "rpc-interaction" 21 | RpcChat = "rpc-chat" 22 | RpcFavor = "rpc-favor" 23 | RpcComment = "rpc-comment" 24 | ) 25 | const ( 26 | TokTikApi = "toktik-api" 27 | TokTikUser = "toktik-user" 28 | TokTikInteraction = "toktik-interaction" 29 | TokTikVideo = "toktik-video" 30 | TokTikChat = "toktik-chat" 31 | TokTikFavor = "toktik-favor" 32 | TokTikComment = "toktik-comment" 33 | ) 34 | -------------------------------------------------------------------------------- /toktik-comment/internal/model/handler_resp.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "toktik-common/errcode" 5 | "toktik-rpc/kitex_gen/comment" 6 | ) 7 | 8 | type CommentActionHandler struct { 9 | } 10 | 11 | func (CommentActionHandler) CommentActionResponse(err errcode.Err, msg string, resp *comment.CommentActionResponse) *comment.CommentActionResponse { 12 | resp.StatusCode = err.ECode() 13 | resp.StatusMsg = err.Error() + ":" + msg 14 | return resp 15 | } 16 | 17 | type CommentListHandler struct { 18 | } 19 | 20 | func (CommentListHandler) CommentListResponse(err errcode.Err, msg string, resp *comment.CommentListResponse) *comment.CommentListResponse { 21 | resp.StatusCode = err.ECode() 22 | resp.StatusMsg = err.Error() + ":" + msg 23 | return resp 24 | } 25 | -------------------------------------------------------------------------------- /toktik-comment/internal/repo/cache.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "context" 5 | "toktik-comment/internal/model/auto" 6 | ) 7 | 8 | type RCacheRepo interface { 9 | GetKeys(c context.Context, keyPattern string) ([]string, error) 10 | KeyExist(c context.Context, key string) (bool, error) 11 | ZSAddCommentInfo(c context.Context, key string, time float64, content string) error 12 | DelComment(c context.Context, key string) error 13 | ZGetCommentList(c context.Context, key string) ([]int64, []*auto.Comment, error) 14 | } 15 | -------------------------------------------------------------------------------- /toktik-comment/internal/repo/handler_resp.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "toktik-comment/internal/model" 5 | "toktik-common/errcode" 6 | "toktik-rpc/kitex_gen/comment" 7 | ) 8 | 9 | type HandlerResp interface { 10 | CommentActionResponse(err errcode.Err, msg string, resp *comment.CommentActionResponse) *comment.CommentActionResponse 11 | CommentListResponse(err errcode.Err, msg string, resp *comment.CommentListResponse) *comment.CommentListResponse 12 | } 13 | 14 | type HandlerResps struct { 15 | model.CommentActionHandler 16 | model.CommentListHandler 17 | } 18 | 19 | func NewHandlerResps() *HandlerResps { 20 | return &HandlerResps{} 21 | } 22 | -------------------------------------------------------------------------------- /toktik-comment/internal/repo/video.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "context" 5 | "toktik-comment/internal/model/auto" 6 | ) 7 | 8 | type VideoRepo interface { 9 | CreateComment(c context.Context, commentInfo *auto.Comment) error 10 | DeleteComment(c context.Context, commentInfo *auto.Comment) error 11 | GetCommentAuthorIds(c context.Context, videoId int64) ([]int64, error) 12 | GetCommentList(c context.Context, videoId int64) ([]*auto.Comment, error) 13 | } 14 | -------------------------------------------------------------------------------- /toktik-comment/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "toktik-comment/internal/global" 6 | "toktik-comment/pkg/rpc" 7 | "toktik-comment/pkg/rpc/client" 8 | "toktik-comment/pkg/setting" 9 | ) 10 | 11 | func main() { 12 | // 初始化 13 | setting.InitAllSetting() 14 | fmt.Printf("config:%#v\n", global.Settings) 15 | 16 | // RPC 注册 17 | client.NewRpcClient() 18 | fmt.Println("-----Comment Service Start ! ! !-----") 19 | rpc.RegisterRPC() 20 | } 21 | -------------------------------------------------------------------------------- /toktik-comment/pkg/myerr/myerr.go: -------------------------------------------------------------------------------- 1 | package myerr 2 | 3 | import "toktik-common/errcode" 4 | 5 | var ( 6 | OperationErr = errcode.NewErr(600001, "没有这个功能哦~") 7 | ) 8 | -------------------------------------------------------------------------------- /toktik-comment/pkg/rpc/client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | func NewRpcClient() { 4 | InitRpcUserClient() 5 | InitRpcVideoClient() 6 | } 7 | -------------------------------------------------------------------------------- /toktik-comment/pkg/rpc/client/user.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/cloudwego/kitex/client" 5 | "github.com/kitex-contrib/obs-opentelemetry/provider" 6 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 7 | etcd "github.com/kitex-contrib/registry-etcd" 8 | "go.uber.org/zap" 9 | "toktik-comment/internal/global" 10 | "toktik-comment/internal/model" 11 | "toktik-rpc/kitex_gen/user/userservice" 12 | ) 13 | 14 | var UserClient userservice.Client 15 | 16 | func InitRpcUserClient() { 17 | r, err := etcd.NewEtcdResolver(global.Settings.Etcd.Addr) 18 | if err != nil { 19 | panic(err) 20 | } 21 | provider.NewOpenTelemetryProvider( 22 | provider.WithServiceName(global.Settings.Jaeger.ServerName[model.TokTikUser]), 23 | provider.WithExportEndpoint(global.Settings.Jaeger.RPCExportEndpoint), 24 | provider.WithInsecure(), 25 | ) 26 | 27 | c, err := userservice.NewClient( 28 | model.RpcUser, 29 | client.WithSuite(tracing.NewClientSuite()), 30 | //client.WithHostPorts(global.Settings.Rpc.ServerAddrs[model.RpcUser]), 31 | //client.WithMiddleware(rpcmiddleware.CommonMiddleware), 32 | //client.WithInstanceMW(rpcmiddleware.ClientMiddleware), 33 | client.WithResolver(r), 34 | ) 35 | if err != nil { 36 | zap.L().Error("InitRpcUserClient err:", zap.Error(err)) 37 | panic(err) 38 | } 39 | UserClient = c 40 | } 41 | -------------------------------------------------------------------------------- /toktik-comment/pkg/rpc/client/video.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/cloudwego/kitex/client" 5 | "github.com/kitex-contrib/obs-opentelemetry/provider" 6 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 7 | etcd "github.com/kitex-contrib/registry-etcd" 8 | "go.uber.org/zap" 9 | "toktik-comment/internal/global" 10 | "toktik-comment/internal/model" 11 | "toktik-rpc/kitex_gen/video/videoservice" 12 | ) 13 | 14 | var VideoClient videoservice.Client 15 | 16 | func InitRpcVideoClient() { 17 | r, err := etcd.NewEtcdResolver(global.Settings.Etcd.Addr) 18 | if err != nil { 19 | panic(err) 20 | } 21 | provider.NewOpenTelemetryProvider( 22 | provider.WithServiceName(global.Settings.Jaeger.ServerName[model.TokTikVideo]), 23 | provider.WithExportEndpoint(global.Settings.Jaeger.RPCExportEndpoint), 24 | provider.WithInsecure(), 25 | ) 26 | c, err := videoservice.NewClient( 27 | model.RpcVideo, 28 | client.WithSuite(tracing.NewClientSuite()), 29 | //client.WithHostPorts(global.Settings.Rpc.ServerAddrs[model.RpcUser]), 30 | //client.WithMiddleware(rpcmiddleware.CommonMiddleware), 31 | //client.WithInstanceMW(rpcmiddleware.ClientMiddleware), 32 | client.WithResolver(r), 33 | ) 34 | if err != nil { 35 | zap.L().Error("InitRpcInteractionClient err:", zap.Error(err)) 36 | panic(err) 37 | } 38 | VideoClient = c 39 | } 40 | -------------------------------------------------------------------------------- /toktik-comment/pkg/setting/dao.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "toktik-comment/internal/cache" 5 | "toktik-comment/internal/dao" 6 | "toktik-comment/internal/dao/mysql" 7 | ) 8 | 9 | func init() { 10 | d := Dao{} 11 | Settings = append(Settings, d) 12 | } 13 | 14 | type Dao struct { 15 | } 16 | 17 | func (Dao) InitSetting() { 18 | mysql.InitMysql() 19 | dao.Group.Rdb = cache.InitRedis() 20 | //go cron.TimingJob() 21 | } 22 | -------------------------------------------------------------------------------- /toktik-comment/pkg/setting/enter.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | var Settings []InitSettings 4 | 5 | type InitSettings interface { 6 | InitSetting() 7 | } 8 | 9 | func InitAllSetting() { 10 | InitBootStrap() 11 | for _, setting := range Settings { 12 | setting.InitSetting() 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /toktik-comment/pkg/setting/logger.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "go.uber.org/zap" 5 | "toktik-comment/internal/global" 6 | "toktik-common/logger" 7 | ) 8 | 9 | func init() { 10 | l := log{} 11 | Settings = append(Settings, l) 12 | } 13 | 14 | type log struct { 15 | } 16 | 17 | // InitSetting 日志初始化 18 | func (log) InitSetting() { 19 | global.Logger = logger.NewLogger(&logger.InitStruct{ 20 | LogSavePath: global.Settings.Logger.LogSavePath, 21 | LogFileExt: global.Settings.Logger.LogFileExt, 22 | MaxSize: global.Settings.Logger.MaxSize, 23 | MaxBackups: global.Settings.Logger.MaxBackups, 24 | MaxAge: global.Settings.Logger.MaxAge, 25 | Compress: global.Settings.Logger.Compress, 26 | LowLevelFile: global.Settings.Logger.LowLevelFile, 27 | HighLevelFile: global.Settings.Logger.HighLevelFile, 28 | }, global.Settings.Logger.Level) 29 | zap.ReplaceGlobals(global.Logger.Logger) 30 | } 31 | -------------------------------------------------------------------------------- /toktik-comment/pkg/setting/nacos.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "github.com/nacos-group/nacos-sdk-go/v2/clients" 5 | "github.com/nacos-group/nacos-sdk-go/v2/clients/config_client" 6 | "github.com/nacos-group/nacos-sdk-go/v2/common/constant" 7 | "github.com/nacos-group/nacos-sdk-go/v2/vo" 8 | log2 "log" 9 | "toktik-comment/internal/global" 10 | ) 11 | 12 | type NacosClient struct { 13 | confClient config_client.IConfigClient 14 | } 15 | 16 | func InitNacos() *NacosClient { 17 | clientConfig := constant.ClientConfig{ 18 | NamespaceId: global.Settings.Nacos.Namespace, //we can create multiple clients with different namespaceId to support multiple namespace.When namespace is public, fill in the blank string here. 19 | TimeoutMs: 5000, 20 | NotLoadCacheAtStart: true, 21 | LogDir: "/tmp/nacos/log", 22 | CacheDir: "/tmp/nacos/cache", 23 | LogLevel: "debug", 24 | } 25 | serverConfigs := []constant.ServerConfig{ 26 | { 27 | IpAddr: global.Settings.Nacos.Addr, 28 | ContextPath: global.Settings.Nacos.ContextPath, 29 | Port: uint64(global.Settings.Nacos.Port), 30 | Scheme: global.Settings.Nacos.Scheme, 31 | }, 32 | } 33 | configClient, err := clients.NewConfigClient( 34 | vo.NacosClientParam{ 35 | ClientConfig: &clientConfig, 36 | ServerConfigs: serverConfigs, 37 | }, 38 | ) 39 | if err != nil { 40 | log2.Fatalln(err) 41 | } 42 | nc := &NacosClient{ 43 | confClient: configClient, 44 | } 45 | return nc 46 | } 47 | -------------------------------------------------------------------------------- /toktik-comment/pkg/setting/snowflake.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "toktik-comment/internal/global" 5 | "toktik-common/utils" 6 | ) 7 | 8 | func init() { 9 | s := sf{} 10 | Settings = append(Settings, s) 11 | } 12 | 13 | type sf struct { 14 | } 15 | 16 | func (sf) InitSetting() { 17 | var err error 18 | global.SnowFlake, err = utils.NewSnowFlake(0, 0) 19 | if err != nil { 20 | panic(err) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /toktik-common/errcode/common.go: -------------------------------------------------------------------------------- 1 | package errcode 2 | 3 | var ( 4 | StatusOK = NewErr(0, "成功") 5 | ErrParamsNotValid = NewErr(0001, "参数有误") 6 | ErrNotFound = NewErr(0002, "未找到资源") 7 | ErrServer = NewErr(0003, "系统错误") 8 | ErrTooManyRequests = NewErr(0004, "请求过多") 9 | ErrTimeOut = NewErr(0005, "请求超时") 10 | ErrDB = NewErr(0006, "db错误") 11 | ErrRedis = NewErr(0007, "redis错误") 12 | ErrAuth = NewErr(0010, "身份鉴权失败") 13 | ErrAuthExp = NewErr(0011, "身份过期") 14 | ) 15 | -------------------------------------------------------------------------------- /toktik-common/errcode/errcode.go: -------------------------------------------------------------------------------- 1 | package errcode 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "sync" 7 | 8 | "github.com/jinzhu/copier" 9 | ) 10 | 11 | // 编写常用的一些错误处理公共方法,标准化我们的错误输出 12 | 13 | type Err interface { 14 | Error() string 15 | ECode() int64 16 | WithDetails(details ...string) Err 17 | } 18 | 19 | type RespErr struct { 20 | Err Err 21 | ErrStr string 22 | } 23 | 24 | var globalMap map[int64]Err 25 | var once sync.Once 26 | 27 | func CreateErr(code int64, msg string) Err { 28 | return &myErr{Code: code, Msg: msg} 29 | } 30 | 31 | func NewErr(code int64, msg string) Err { 32 | once.Do(func() { 33 | globalMap = make(map[int64]Err) 34 | }) 35 | if _, ok := globalMap[code]; ok { 36 | panic("错误码已存在") 37 | } 38 | err := &myErr{Code: code, Msg: msg} 39 | globalMap[code] = err 40 | return err 41 | } 42 | 43 | type myErr struct { 44 | Code int64 `json:"status_code"` // 状态码,0-成功,其他值-失败 45 | Msg string `json:"status_msg"` // 返回状态描述 46 | Details []string `json:"-"` // 详细信息 47 | } 48 | 49 | func (m *myErr) ECode() int64 { 50 | return m.Code 51 | } 52 | 53 | func (m *myErr) Error() string { 54 | return fmt.Sprintf("%v", m.Msg) 55 | } 56 | 57 | func (m *myErr) WithDetails(details ...string) Err { 58 | var newErr = &myErr{} 59 | _ = copier.Copy(newErr, m) 60 | msgs := strings.Split(m.Msg, ",") 61 | m.Msg = msgs[0] + "," + details[0] 62 | //newErr.Details = append(newErr.Details, details...) 63 | return newErr 64 | } 65 | -------------------------------------------------------------------------------- /toktik-common/kk/kafka_test.go: -------------------------------------------------------------------------------- 1 | package kk 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestProducer(t *testing.T) { 10 | w := GetWriter("192.168.30.134:9092") 11 | m := make(map[string]string) 12 | m["projectCode"] = "1200" 13 | bytes, _ := json.Marshal(m) 14 | w.Send(LogData{ 15 | Topic: "log", 16 | Data: bytes, 17 | }) 18 | time.Sleep(2 * time.Second) 19 | } 20 | 21 | func TestConsumer(t *testing.T) { 22 | GetReader([]string{"192.168.30.134:9092"}, "group1", "log") 23 | for { 24 | 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /toktik-common/kk/receiver.go: -------------------------------------------------------------------------------- 1 | package kk 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/segmentio/kafka-go" 7 | log2 "log" 8 | ) 9 | 10 | type KafkaReader struct { 11 | r *kafka.Reader 12 | } 13 | 14 | func (r *KafkaReader) readMsg() { 15 | for { 16 | m, err := r.r.ReadMessage(context.Background()) 17 | if err != nil { 18 | log2.Printf("kafka readMsg err %s \n", err.Error()) 19 | continue 20 | } 21 | fmt.Printf("message at topic/partition/offset %v/%v/%v: %s = %s\n", m.Topic, m.Partition, m.Offset, string(m.Key), string(m.Value)) 22 | } 23 | } 24 | 25 | func GetReader(brokers []string, groupId, topic string) *KafkaReader { 26 | r := kafka.NewReader(kafka.ReaderConfig{ 27 | Brokers: brokers, 28 | GroupID: groupId, //同一个组下的consumer 协同工作 共同消费topic队列中的内容 29 | Topic: topic, 30 | MinBytes: 10e3, // 10KB 31 | MaxBytes: 10e6, // 10MB 32 | }) 33 | k := &KafkaReader{r: r} 34 | go k.readMsg() 35 | return k 36 | } 37 | -------------------------------------------------------------------------------- /toktik-common/kk/sender.go: -------------------------------------------------------------------------------- 1 | package kk 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "github.com/segmentio/kafka-go" 7 | log2 "log" 8 | "time" 9 | ) 10 | 11 | type LogData struct { 12 | Topic string 13 | //json数据 14 | Data []byte 15 | } 16 | type KafkaWriter struct { 17 | w *kafka.Writer 18 | data chan LogData 19 | } 20 | 21 | func GetWriter(addr string) *KafkaWriter { 22 | w := &kafka.Writer{ 23 | Addr: kafka.TCP(addr), 24 | Balancer: &kafka.LeastBytes{}, 25 | } 26 | k := &KafkaWriter{ 27 | w: w, 28 | data: make(chan LogData, 100), 29 | } 30 | go k.sendKafka() 31 | return k 32 | } 33 | 34 | func (w *KafkaWriter) Send(data LogData) { 35 | w.data <- data 36 | } 37 | 38 | func (w *KafkaWriter) Close() { 39 | if w.w != nil { 40 | w.w.Close() 41 | } 42 | } 43 | 44 | func (w *KafkaWriter) sendKafka() { 45 | for { 46 | select { 47 | case data := <-w.data: 48 | messages := []kafka.Message{ 49 | { 50 | Topic: data.Topic, 51 | Key: []byte("logMsg"), 52 | Value: data.Data, 53 | }, 54 | } 55 | var err error 56 | const retries = 3 57 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 58 | defer cancel() 59 | for i := 0; i < retries; i++ { 60 | // attempt to create topic prior to publishing the message 61 | err = w.w.WriteMessages(ctx, messages...) 62 | if err == nil { 63 | break 64 | } 65 | if errors.Is(err, kafka.LeaderNotAvailable) || errors.Is(err, context.DeadlineExceeded) { 66 | time.Sleep(time.Millisecond * 250) 67 | continue 68 | } 69 | if err != nil { 70 | log2.Printf("kafka send writemessage err %s \n", err.Error()) 71 | } 72 | } 73 | } 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /toktik-common/limiter/api/limit.go: -------------------------------------------------------------------------------- 1 | package limit 2 | 3 | import ( 4 | "context" 5 | "sort" 6 | "time" 7 | 8 | "golang.org/x/time/rate" 9 | ) 10 | 11 | // RateLimiter 限流接口 12 | type RateLimiter interface { 13 | Wait(ctx context.Context) error // 阻塞等待 14 | Limit() rate.Limit 15 | } 16 | 17 | type multiLimiter struct { 18 | limiters []RateLimiter 19 | } 20 | 21 | // Wait 阻塞等待直到获取令牌或者超时 22 | func (m *multiLimiter) Wait(ctx context.Context) error { 23 | for _, l := range m.limiters { 24 | if err := l.Wait(ctx); err != nil { 25 | return err 26 | } 27 | } 28 | return nil 29 | } 30 | 31 | // Limit 返回当前限制速率 32 | func (m *multiLimiter) Limit() rate.Limit { 33 | return m.limiters[0].Limit() // 直接返回限制最多的元素 34 | } 35 | 36 | // MultiLimiter 混合多个限流桶 37 | func MultiLimiter(limiters ...RateLimiter) *multiLimiter { 38 | byLimit := func(i, j int) bool { return limiters[i].Limit() < limiters[j].Limit() } // 细粒度在前 39 | sort.Slice(limiters, byLimit) 40 | return &multiLimiter{limiters: limiters} 41 | } 42 | 43 | // Per 返回速率为 每duration,eventCount个请求 44 | func Per(eventCount int, duration time.Duration) rate.Limit { 45 | return rate.Every(duration / time.Duration(eventCount)) 46 | } 47 | -------------------------------------------------------------------------------- /toktik-common/limiter/bucket/limiter.go: -------------------------------------------------------------------------------- 1 | package bucket 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/gin-gonic/gin" 7 | "github.com/juju/ratelimit" 8 | ) 9 | 10 | type Iface interface { 11 | Key(c *gin.Context) string // 获取对应的限流器的键值对名称。 12 | GetBucket(key string) (*ratelimit.Bucket, bool) // 获取对应的限流器的键值对名称。 13 | AddBuckets(rules ...BucketRule) Iface // 新增多个令牌桶规则。 14 | } 15 | 16 | // Limier 存储令牌桶与键值对名称的映射关系 17 | type Limier struct { 18 | limiterBuckets map[string]*ratelimit.Bucket 19 | } 20 | 21 | type BucketRule struct { 22 | Key string // 自定义键值对名称 23 | FillInterval time.Duration // 增加新桶的间隔时间 24 | Cap int64 // 桶的最大容量 25 | Quantum int64 // 每次到达间隔时间之后存放的桶数量 26 | } 27 | -------------------------------------------------------------------------------- /toktik-common/limiter/bucket/prefixTree.go: -------------------------------------------------------------------------------- 1 | package bucket 2 | 3 | // 前缀树 4 | 5 | type PrefixTree struct { 6 | suffix map[string]*PrefixTree 7 | result interface{} 8 | } 9 | 10 | func NewPrefixTree() *PrefixTree { 11 | return &PrefixTree{suffix: make(map[string]*PrefixTree)} 12 | } 13 | 14 | func (t *PrefixTree) Put(prefix []string, v interface{}) { 15 | root := t 16 | for _, s := range prefix { 17 | if root.suffix[s] == nil { 18 | root.suffix[s] = NewPrefixTree() 19 | } 20 | root = root.suffix[s] 21 | } 22 | root.result = v 23 | } 24 | 25 | func (t *PrefixTree) Get(prefix []string) interface{} { 26 | root := t 27 | for _, s := range prefix { 28 | if root.suffix[s] != nil { 29 | root = root.suffix[s] 30 | } else { 31 | break 32 | } 33 | } 34 | return root.result 35 | } 36 | -------------------------------------------------------------------------------- /toktik-common/limiter/bucket/prefix_limiter.go: -------------------------------------------------------------------------------- 1 | package bucket 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/gin-gonic/gin" 7 | "github.com/juju/ratelimit" 8 | ) 9 | 10 | type PrefixLimiter struct { 11 | *Limier 12 | *PrefixTree 13 | } 14 | 15 | func NewPrefixLimiter() *PrefixLimiter { 16 | return &PrefixLimiter{&Limier{limiterBuckets: map[string]*ratelimit.Bucket{}}, NewPrefixTree()} 17 | } 18 | 19 | func (p *PrefixLimiter) Key(c *gin.Context) string { 20 | uri := c.Request.RequestURI 21 | prefix := strings.Split(uri, "/") 22 | if result := p.Get(prefix); result != nil { 23 | return result.(string) 24 | } 25 | return "" 26 | } 27 | 28 | func (p *PrefixLimiter) testKey(uri string) string { 29 | prefix := strings.Split(uri, "/") 30 | result := p.Get(prefix) 31 | if result != nil { 32 | return result.(string) 33 | } 34 | return "" 35 | } 36 | 37 | func (p *PrefixLimiter) GetBucket(key string) (*ratelimit.Bucket, bool) { 38 | bucket, ok := p.limiterBuckets[key] 39 | return bucket, ok 40 | } 41 | 42 | func (p *PrefixLimiter) AddBuckets(rules ...BucketRule) Iface { 43 | for _, rule := range rules { 44 | if _, ok := p.limiterBuckets[rule.Key]; !ok { 45 | p.limiterBuckets[rule.Key] = ratelimit.NewBucketWithQuantum(rule.FillInterval, rule.Cap, rule.Quantum) 46 | p.Put(strings.Split(rule.Key, "/"), rule.Key) 47 | } 48 | } 49 | return p 50 | } 51 | -------------------------------------------------------------------------------- /toktik-common/limiter/bucket/prefix_limiter_test.go: -------------------------------------------------------------------------------- 1 | package bucket 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestNewPrefixLimiter(t *testing.T) { 11 | p := NewPrefixLimiter() 12 | p.AddBuckets(BucketRule{ 13 | Key: "/post/test1", 14 | FillInterval: time.Second, 15 | Cap: 100, 16 | Quantum: 100, 17 | }, BucketRule{ 18 | Key: "/post/test2", 19 | FillInterval: time.Second, 20 | Cap: 10, 21 | Quantum: 10, 22 | }, BucketRule{ 23 | Key: "/user/test3", 24 | FillInterval: time.Second, 25 | Cap: 20, 26 | Quantum: 20, 27 | }) 28 | key := p.testKey("/post/test1") 29 | bucket, ok := p.GetBucket(key) 30 | require.True(t, ok) 31 | require.NotEmpty(t, bucket) 32 | key = p.testKey("/post/test2") 33 | bucket, ok = p.GetBucket(key) 34 | require.True(t, ok) 35 | require.NotEmpty(t, bucket) 36 | key = p.testKey("/post/test3") 37 | bucket, ok = p.GetBucket(key) 38 | require.False(t, ok) 39 | require.Empty(t, bucket) 40 | } 41 | -------------------------------------------------------------------------------- /toktik-common/oss/aliyun/aliyun.go: -------------------------------------------------------------------------------- 1 | package aliyun 2 | 3 | import ( 4 | "github.com/aliyun/aliyun-oss-go-sdk/oss" 5 | ) 6 | 7 | type Config struct { 8 | Endpoint string 9 | AccessKeyId string 10 | AccessKeySecret string 11 | BucketName string 12 | } 13 | 14 | type OSS struct { 15 | config Config 16 | } 17 | 18 | func Init(config Config) *OSS { 19 | return &OSS{config: config} 20 | } 21 | 22 | func (o *OSS) newBucket() (*oss.Bucket, error) { 23 | // 创建OSSClient实例。 24 | client, err := oss.New(o.config.Endpoint, o.config.AccessKeyId, o.config.AccessKeySecret) 25 | if err != nil { 26 | return nil, err 27 | } 28 | // 获取存储空间。 29 | bucket, err := client.Bucket(o.config.BucketName) 30 | if err != nil { 31 | return nil, err 32 | } 33 | return bucket, nil 34 | } 35 | -------------------------------------------------------------------------------- /toktik-common/oss/aliyun/objectKey.go: -------------------------------------------------------------------------------- 1 | package aliyun 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | func (o *OSS) CreateObjectKey(suffix string, directories ...string) string { 8 | var builder strings.Builder 9 | for i, v := range directories { 10 | builder.WriteString(v) 11 | if i != len(directories)-1 { //TODO 这里绝对可以优化,懒得测性能,下次看到一定优化 12 | builder.WriteString("/") 13 | } 14 | } 15 | builder.WriteString(suffix) 16 | return builder.String() 17 | } 18 | 19 | // bucket_name:why_bucket 视频目录:video/user_id/video_name 封面目录:cover/user_id/cover_name 20 | // video_name:video_id + title.mp4 cover:video_id + title.jpg 21 | -------------------------------------------------------------------------------- /toktik-common/oss/aliyun/upload.go: -------------------------------------------------------------------------------- 1 | package aliyun 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "io" 7 | ) 8 | 9 | func (o *OSS) UploadFile(objectKey string, reader io.Reader) (string, error) { 10 | bucket, err := o.newBucket() 11 | if err != nil { 12 | return "", errors.New("function OSS.NewBucket() Failed, err:" + err.Error()) 13 | } 14 | // 上传文件的bytes。 15 | err = bucket.PutObject(objectKey, reader) 16 | if err != nil { 17 | return "", errors.New("function formUploader.Put() Failed, err:" + err.Error()) 18 | } 19 | url := o.CreateFileURL(o.config.BucketName, o.config.Endpoint, objectKey) 20 | return url, nil 21 | } 22 | 23 | // UploadByteFile 上传文件 objectKey:目录+objectName fileBuf:文件流.buf 24 | // 返回 访问地址,文件key,error 25 | func (o *OSS) UploadByteFile(objectKey string, fileBuf []byte) (string, error) { 26 | bucket, err := o.newBucket() 27 | if err != nil { 28 | return "", errors.New("function OSS.NewBucket() Failed, err:" + err.Error()) 29 | } 30 | // 上传文件的bytes。 31 | err = bucket.PutObject(objectKey, bytes.NewReader(fileBuf)) 32 | if err != nil { 33 | return "", errors.New("function formUploader.Put() Failed, err:" + err.Error()) 34 | } 35 | url := o.CreateFileURL(o.config.BucketName, o.config.Endpoint, objectKey) 36 | return url, nil 37 | } 38 | 39 | // DeleteFile 删除文件 40 | func (o *OSS) DeleteFile() error { 41 | return nil 42 | } 43 | -------------------------------------------------------------------------------- /toktik-common/oss/aliyun/url.go: -------------------------------------------------------------------------------- 1 | package aliyun 2 | 3 | import ( 4 | "net/url" 5 | "strings" 6 | ) 7 | 8 | //构建 URL https://why-bucket.oss-cn-beijing.aliyuncs.com/test1/123.jpg 9 | // URL 由 https:// + bucket_name. + endpoint + object_name 10 | 11 | func (o *OSS) CreateFileURL(bucketName, endpoint, objectName string) string { 12 | // 注意: OSS自己构建的 httpURL,会自动将 objectName转码一次,所以需要在自己构建的URL中进行转码,不能提前对objectName转码 13 | objectName = url.QueryEscape(objectName) 14 | objectName = strings.Replace(objectName, "%2F", "/", -1) // "/"转码后是"%2F",让他变回去 15 | str := "https://" + bucketName + "." + endpoint + "/" + objectName 16 | // 对url的中文 进行url编码 17 | return str 18 | } 19 | 20 | //data := url.QueryEscape("asd") 21 | //fmt.Println(data) 22 | //ax, _ := url.QueryUnescape("%E5%85%B0%E4%BA%AD%E5%BA%8F") 23 | //fmt.Println(ax) 24 | -------------------------------------------------------------------------------- /toktik-common/oss/oss.go: -------------------------------------------------------------------------------- 1 | package oss 2 | 3 | import "io" 4 | 5 | // OSS 对象存储接口 6 | type OSS interface { 7 | UploadFile(objectKey string, reader io.Reader) (string, error) 8 | UploadByteFile(objectKey string, fileBuf []byte) (string, error) 9 | DeleteFile() error // 未实现 10 | CreateFileURL(bucketName, endpoint, objectName string) string 11 | CreateObjectKey(suffix string, directories ...string) string 12 | } 13 | 14 | // CreateObjectKey(后缀 string,...string])string 15 | // 这里只是构建视频和封面的key,后续可以完善这个方法,参数为...string 16 | // 每个string都是一级目录,然后用字符'/'拼接这个切片。 17 | -------------------------------------------------------------------------------- /toktik-common/page/page.go: -------------------------------------------------------------------------------- 1 | package page 2 | 3 | import ( 4 | "net/http" 5 | "toktik-common/utils" 6 | ) 7 | 8 | // 分页处理 9 | 10 | type Page struct { 11 | DefaultPageSize int64 12 | MaxPageSize int64 13 | PageKey string // url中page关键字 14 | PageSizeKey string // pagesize关键字 15 | } 16 | 17 | // InitPage 初始化默认页数大小和最大页数限制以及查询的关键字 18 | func InitPage(defaultPageSize, maxPageSize int64, pageKey, pageSizeKey string) *Page { 19 | return &Page{ 20 | DefaultPageSize: defaultPageSize, 21 | MaxPageSize: maxPageSize, 22 | PageKey: pageKey, 23 | PageSizeKey: pageSizeKey, 24 | } 25 | } 26 | 27 | // GetPageSizeAndOffset 从请求中获取偏移值和页尺寸 28 | func (p *Page) GetPageSizeAndOffset(r *http.Request) (limit, offset int64) { 29 | page := utils.StrTo(r.FormValue(p.PageKey)).MustInt64() 30 | if page <= 0 { 31 | page = 1 32 | } 33 | limit = utils.StrTo(r.FormValue(p.PageSizeKey)).MustInt64() 34 | if limit <= 0 { 35 | limit = p.DefaultPageSize 36 | } 37 | if limit > p.MaxPageSize { 38 | limit = p.MaxPageSize 39 | } 40 | offset = (page - 1) * limit 41 | return 42 | } 43 | 44 | // CulOffset 计算偏移值 45 | func CulOffset(page, pageSize int32) (offset int32) { 46 | return (page - 1) * pageSize 47 | } 48 | -------------------------------------------------------------------------------- /toktik-common/response/response.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import ( 4 | "github.com/cloudwego/hertz/pkg/app" 5 | "net/http" 6 | "toktik-common/errcode" 7 | ) 8 | 9 | type Response struct { 10 | c *app.RequestContext 11 | } 12 | 13 | // State 状态码 14 | type State struct { 15 | Code int64 `json:"status_code"` // 状态码,0-成功,其他值-失败 16 | Msg string `json:"status_msg"` // 返回状态描述 17 | Data interface{} `json:"data,omitempty"` // 失败时返回空 18 | } 19 | 20 | type List struct { 21 | List interface{} `json:"list"` 22 | Total int64 `json:"total"` 23 | } 24 | 25 | func NewResponse(ctx *app.RequestContext) *Response { 26 | return &Response{c: ctx} 27 | } 28 | 29 | // Reply 响应单个数据 30 | func (r *Response) Reply(err errcode.Err, datas ...any) { //err errcode.Err 31 | var data interface{} 32 | if len(datas) > 0 { 33 | data = datas[0] 34 | } 35 | if err == nil { 36 | err = errcode.StatusOK 37 | } else { 38 | data = nil 39 | r.c.JSON(http.StatusOK, State{ 40 | Code: err.ECode(), 41 | Msg: err.Error(), 42 | }) 43 | return 44 | } 45 | r.c.JSON(http.StatusOK, data) 46 | } 47 | 48 | // ReplyList 响应列表数据 49 | func (r *Response) ReplyList(err errcode.Err, total int64, data interface{}) { 50 | if err == nil { 51 | err = errcode.StatusOK 52 | } else { 53 | data = nil 54 | } 55 | r.c.JSON(http.StatusOK, State{ 56 | Code: err.ECode(), 57 | Msg: err.Error(), 58 | Data: List{List: data, Total: total}, 59 | }) 60 | } 61 | -------------------------------------------------------------------------------- /toktik-common/serveHTTP/run.go: -------------------------------------------------------------------------------- 1 | package serveHTTP 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/gin-gonic/gin" 7 | "log" 8 | "net/http" 9 | "os" 10 | "os/signal" 11 | "syscall" 12 | "time" 13 | ) 14 | 15 | func Run(r *gin.Engine, srvName string, addr string, stop func()) { 16 | server := &http.Server{ 17 | Addr: addr, 18 | Handler: r, 19 | } 20 | 21 | // 保证下面的优雅启停 22 | go func() { 23 | log.Printf("%s server running in %s \n", srvName, server.Addr) 24 | if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { 25 | fmt.Println("hah") 26 | log.Fatalln(err) 27 | } 28 | }() 29 | 30 | fmt.Println("----------start----------") 31 | quit := make(chan os.Signal) 32 | // SIGINT 用户发送INTR字符(Ctrl+C)触发 kill -2 33 | // SIGTERM 结束程序(可以被捕获、阻塞或忽略) 34 | signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) 35 | <-quit 36 | log.Printf("Shutting Down project %s...\n", srvName) 37 | 38 | ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) 39 | defer cancel() 40 | if stop != nil { 41 | stop() 42 | } 43 | if err := server.Shutdown(ctx); err != nil { 44 | log.Fatalf("%s server Shutdown,causer by %v: \n", srvName, err) 45 | } else if err == context.DeadlineExceeded { 46 | log.Fatalln("服务器关闭超时") 47 | } 48 | select { 49 | case <-ctx.Done(): 50 | log.Println("wait timeout...") 51 | } 52 | log.Printf("%s server stop success...\n", srvName) 53 | } 54 | -------------------------------------------------------------------------------- /toktik-common/setting/setting.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "io" 5 | "log" 6 | 7 | "github.com/fsnotify/fsnotify" 8 | "github.com/spf13/viper" 9 | ) 10 | 11 | // 使用viper进行配置文件的读取和热加载 12 | 13 | type Setting struct { 14 | vp *viper.Viper 15 | all interface{} 16 | } 17 | 18 | // NewSetting 初始化本项目的配置的基础属性 19 | // 设定配置文件的名称为 config,配置类型为 yaml,并且设置其配置路径为相对路径 configs/ 20 | func NewSetting(configName, configType string, configPaths ...string) (*Setting, error) { 21 | vp := viper.New() 22 | vp.SetConfigName(configName) 23 | vp.SetConfigType(configType) // 设置配置文件类型 24 | for _, config := range configPaths { 25 | if config != "" { 26 | vp.AddConfigPath(config) // 可以设置多个配置路径,解决路径查找问题 27 | } 28 | } 29 | err := vp.ReadInConfig() // 加载配置文件 30 | if err != nil { 31 | return nil, err 32 | } 33 | s := &Setting{vp: vp} 34 | s.vp.WatchConfig() 35 | s.vp.OnConfigChange(func(in fsnotify.Event) { 36 | log.Println("更新配置") 37 | err := s.vp.Unmarshal(s.all) 38 | if err != nil { 39 | log.Fatalln("更新配置失败:" + err.Error()) 40 | } 41 | }) 42 | return s, nil 43 | } 44 | 45 | // ReadConfigFromBuf 从文件流中读取配置 46 | func ReadConfigFromBuf(configType string, fileBuf io.Reader) (*Setting, error) { 47 | vp := viper.New() 48 | vp.SetConfigType(configType) // 设置配置文件类型 49 | err := vp.ReadConfig(fileBuf) // 加载配置文件 50 | if err != nil { 51 | return nil, err 52 | } 53 | s := &Setting{vp: vp} 54 | return s, nil 55 | } 56 | 57 | // BindAll 绑定配置文件 58 | func (s *Setting) BindAll(v interface{}) error { 59 | // 绑定 60 | err := s.vp.Unmarshal(v) 61 | if err != nil { 62 | return err 63 | } 64 | s.all = v 65 | return nil 66 | } 67 | -------------------------------------------------------------------------------- /toktik-common/timing_job/gocron.go: -------------------------------------------------------------------------------- 1 | package timing_job 2 | 3 | import ( 4 | "github.com/go-co-op/gocron" 5 | "time" 6 | ) 7 | 8 | // scheduleJob 定时任务 gocron 9 | type scheduleJob struct { 10 | schedule *gocron.Scheduler 11 | } 12 | 13 | func NewSchedule() *scheduleJob { 14 | return &scheduleJob{gocron.NewScheduler(time.Local)} 15 | } 16 | 17 | func StartMinuteJob(job interface{}, frequency int, tag ...string) { 18 | s := gocron.NewScheduler(time.Local) 19 | s.Every(frequency).Tag(tag...).Minute().Do(job) 20 | s.StartAsync() 21 | } 22 | -------------------------------------------------------------------------------- /toktik-common/token/maker.go: -------------------------------------------------------------------------------- 1 | package token 2 | 3 | import ( 4 | "errors" 5 | "time" 6 | ) 7 | 8 | const minSecretKeySize = 32 9 | 10 | var ErrSecretLen = errors.New("密钥长度不合法") 11 | 12 | // Maker is an interface for managing tokens 13 | type Maker interface { 14 | // CreateToken creates a new token for a specific username and duration 15 | CreateToken(content []byte, duration time.Duration) (string, *Payload, error) 16 | // VerifyToken checks if the token is valid or not 17 | VerifyToken(token string) (*Payload, error) 18 | } 19 | -------------------------------------------------------------------------------- /toktik-common/token/model.go: -------------------------------------------------------------------------------- 1 | package token 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/cloudwego/hertz/pkg/app" 6 | ) 7 | 8 | type ( 9 | TokenType string 10 | ) 11 | 12 | const ( 13 | UserToken TokenType = "user" 14 | AuthKey = "token-Content" 15 | ) 16 | 17 | type Content struct { 18 | ID int64 `json:"id"` 19 | Type TokenType `json:"type"` 20 | } 21 | 22 | // NewTokenContent 新建一种类型的token 23 | func NewTokenContent(t TokenType, userID int64) *Content { 24 | return &Content{Type: t, ID: userID} 25 | } 26 | 27 | func (c *Content) Marshal() ([]byte, error) { 28 | // 返回json编码 29 | return json.Marshal(c) 30 | } 31 | 32 | func (c *Content) Unmarshal(data []byte) error { 33 | if err := json.Unmarshal(data, &c); err != nil { 34 | return err 35 | } 36 | return nil 37 | } 38 | 39 | // Token 结合token.Payload和Token 40 | type Token struct { 41 | AccessToken string 42 | Payload *Payload 43 | Content *Content 44 | } 45 | 46 | // GetTokenContent 从当前上下文中获取保存的content内容 47 | func GetTokenContent(c *app.RequestContext) (*Content, bool) { 48 | val, ok := c.Get(AuthKey) 49 | if !ok { 50 | return nil, false 51 | } 52 | return val.(*Content), true 53 | } 54 | -------------------------------------------------------------------------------- /toktik-common/token/paseto_maker.go: -------------------------------------------------------------------------------- 1 | package token 2 | 3 | import ( 4 | "github.com/o1egl/paseto" 5 | "golang.org/x/crypto/chacha20poly1305" 6 | "time" 7 | ) 8 | 9 | type PasetoMaker struct { 10 | paseto *paseto.V2 11 | Key []byte //symmetric对称 12 | } 13 | 14 | func NewPasetoMaker(Key string) (Maker, error) { 15 | if len(Key) != chacha20poly1305.KeySize { 16 | return nil, ErrSecretLen 17 | } 18 | 19 | maker := &PasetoMaker{ 20 | paseto: paseto.NewV2(), 21 | Key: []byte(Key), 22 | } 23 | return maker, nil 24 | } 25 | 26 | // CreateToken 构造token 27 | func (maker *PasetoMaker) CreateToken(content []byte, duration time.Duration) (string, *Payload, error) { 28 | payload, err := NewPayload(content, duration) 29 | if err != nil { 30 | return "", nil, err 31 | } 32 | 33 | token, err := maker.paseto.Encrypt(maker.Key, payload, nil) 34 | return token, payload, nil 35 | } 36 | 37 | // VerifyToken checks if the token is valid or not 38 | func (maker *PasetoMaker) VerifyToken(token string) (*Payload, error) { 39 | payload := &Payload{} 40 | 41 | // 验证token是否有效 42 | err := maker.paseto.Decrypt(token, maker.Key, payload, nil) 43 | if err != nil { 44 | return nil, ErrInvalidToken 45 | } 46 | 47 | // 验证token是否过期 48 | err = payload.Valid() 49 | if err != nil { 50 | return nil, ErrExpiredToken 51 | } 52 | 53 | return payload, nil 54 | } 55 | -------------------------------------------------------------------------------- /toktik-common/token/payload.go: -------------------------------------------------------------------------------- 1 | package token 2 | 3 | import ( 4 | "errors" 5 | "github.com/google/uuid" 6 | "time" 7 | ) 8 | 9 | var ( 10 | ErrExpiredToken = errors.New("token has expired") 11 | ErrInvalidToken = errors.New("token is invalided") 12 | ) 13 | 14 | type User struct { // 伪造一个Payload的Content 15 | UserID int64 `json:"userid"` 16 | UserName string `json:"username"` 17 | } 18 | 19 | // Payload contains the payload data of the token 20 | type Payload struct { 21 | ID uuid.UUID `json:"id"` 22 | Content []byte `json:"content"` // 可以是任意内容 23 | IssuedAt time.Time `json:"issued_at"` 24 | ExpiredAt time.Time `json:"expired_at"` 25 | } 26 | 27 | func NewPayload(content []byte, duration time.Duration) (*Payload, error) { 28 | // NewRandom return a random uuid.UUID 29 | tokenID, err := uuid.NewRandom() 30 | if err != nil { 31 | return nil, err 32 | } 33 | 34 | payload := &Payload{ 35 | ID: tokenID, 36 | Content: content, 37 | IssuedAt: time.Now(), 38 | ExpiredAt: time.Now().Add(duration), 39 | } 40 | 41 | return payload, nil 42 | } 43 | 44 | func (payload *Payload) Valid() error { 45 | if time.Now().After(payload.ExpiredAt) { 46 | return ErrExpiredToken 47 | } 48 | return nil 49 | } 50 | -------------------------------------------------------------------------------- /toktik-common/utils/ffmpeg.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | ffmpeg "github.com/u2takey/ffmpeg-go" 7 | "io" 8 | "os" 9 | ) 10 | 11 | func ReadFrameAsJpeg(inFileName string, frameNum int) (io.Reader, error) { 12 | buf := bytes.NewBuffer(nil) 13 | err := ffmpeg.Input(inFileName). 14 | Filter("select", ffmpeg.Args{fmt.Sprintf("gte(n,%d)", frameNum)}). 15 | Output("pipe:", ffmpeg.KwArgs{"vframes": 1, "format": "image2", "vcodec": "mjpeg"}). 16 | WithOutput(buf, os.Stdout). 17 | Run() 18 | if err != nil { 19 | return nil, err 20 | } 21 | return buf, nil 22 | } 23 | -------------------------------------------------------------------------------- /toktik-common/utils/ip.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "net" 5 | "os" 6 | "strings" 7 | ) 8 | 9 | func GetIp(key string) string { 10 | ip := os.Getenv(key) 11 | if ip == "" { 12 | ip = "localhost" 13 | } 14 | return ip 15 | } 16 | 17 | func GetOutBoundIP() (ip string, err error) { 18 | conn, err := net.Dial("udp", "8.8.8.8:53") 19 | if err != nil { 20 | return 21 | } 22 | localAddr := conn.LocalAddr().(*net.UDPAddr) 23 | ip = strings.Split(localAddr.String(), ":")[0] 24 | return 25 | } 26 | -------------------------------------------------------------------------------- /toktik-common/utils/password.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "golang.org/x/crypto/bcrypt" 6 | ) 7 | 8 | /* 9 | 原理: 10 | 是一种加盐的加密方法,MD5加密时候,同一个密码经过hash的时候生成的是同一个hash值,在大数据的情况下,有些经过md5加密的方法将会被破解. 11 | 使用BCrypt进行加密,同一个密码每次生成的hash值都是不相同的。 12 | 每次加密的时候首先会生成一个随机数就是盐,之后将这个随机数与密码进行hash,得到一个hash值存到数据库。 13 | 当用户在登陆的时候,输入的是明文的密码, 14 | 从数据库中取出保存密码对其hash值进行分离,前面的22位就是加的盐,之后将随机数与前端输入的密码进行组合求hash值判断是否相同 15 | */ 16 | 17 | // HashPassword 计算bcrypt哈希字符串 18 | func HashPassword(password string) (string, error) { 19 | hashPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) 20 | if err != nil { 21 | return "", fmt.Errorf("未能包装密码:%v", err) 22 | } 23 | return string(hashPassword), nil 24 | } 25 | 26 | // CheckPassword 检查输入的密码和哈希字符串是否匹配 27 | func CheckPassword(password, hashPassword string) error { 28 | return bcrypt.CompareHashAndPassword([]byte(hashPassword), []byte(password)) 29 | } 30 | -------------------------------------------------------------------------------- /toktik-favor/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine 2 | WORKDIR /app 3 | COPY ./bin/toktik-favor . 4 | COPY ./config . 5 | RUN mkdir config && mv config.yaml config/config.yaml && mv bootstrap.yaml config/bootstrap.yaml 6 | 7 | RUN chmod +x ./toktik-favor 8 | 9 | EXPOSE 8085 8885 9195 10 | ENTRYPOINT ["./toktik-favor"] 11 | -------------------------------------------------------------------------------- /toktik-favor/config/bootstrap.yaml.template: -------------------------------------------------------------------------------- 1 | Nacos: 2 | Namespace: "xxx" # 命名空间id 3 | Group: "api" # group名称 4 | Addr: "xxx.xxx.xxx.xxx" # 地址 5 | Port: 8848 6 | Scheme: "http" 7 | ContextPath: "/nacos" -------------------------------------------------------------------------------- /toktik-favor/config/config.yaml.template: -------------------------------------------------------------------------------- 1 | Server: 2 | Name: "toktik-favor" 3 | RunMode: "DEBUG" 4 | Addr: "0.0.0.0:8085" 5 | DefaultContextTimeout: 10s 6 | 7 | Rules: 8 | FavoriteInfoCacheExpire: 168h 9 | 10 | Jaeger: 11 | RPCExportEndpoint: "xxx:4317" 12 | ServerName: 13 | toktik-api: "toktik-api" 14 | toktik-user: "toktik-user" 15 | toktik-video: "toktik-video" 16 | toktik-interaction: "toktik-interaction" 17 | toktik-chat: "toktik-chat" 18 | toktik-favor: "toktik-favor" 19 | toktik-comment: "toktik-comment" 20 | 21 | Prometheus: 22 | Post: ":9195" 23 | Path: "/metrics" 24 | 25 | RPC: 26 | Name: "rpc-favor" 27 | Addr: "0.0.0.0:8885" 28 | 29 | Logger: 30 | Level: "debug" 31 | LogSavePath: "storage/applogs/" 32 | LogFileExt: ".log" 33 | MaxSize: 10 34 | MaxBackups: 7 35 | MaxAge: 30 36 | Compress: false 37 | LowLevelFile: "info" 38 | HighLevelFile: "error" 39 | 40 | Mysql: 41 | Username: "root" 42 | Password: "xxx" 43 | Host: "xxx" 44 | Port: "3309" 45 | DB: "toktik_favor" 46 | 47 | Redis: 48 | Host: "xxx" 49 | Port: "6379" 50 | Password: "" 51 | DB: 5 52 | 53 | Etcd: 54 | Addr: 55 | - "xxx:2379" 56 | -------------------------------------------------------------------------------- /toktik-favor/internal/dao/enter.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "github.com/go-redis/redis/v8" 5 | "gorm.io/gorm" 6 | ) 7 | 8 | type group struct { 9 | Mdb *gorm.DB 10 | Rdb *redis.Client 11 | } 12 | 13 | var Group = new(group) 14 | -------------------------------------------------------------------------------- /toktik-favor/internal/dao/mysql/favor.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import ( 4 | "context" 5 | "toktik-favor/internal/model/auto" 6 | ) 7 | 8 | type FavorDao struct { 9 | conn *GormConn 10 | } 11 | 12 | func NewFavorDao() *FavorDao { 13 | return &FavorDao{ 14 | conn: NewGormConn(), 15 | } 16 | } 17 | 18 | func (v *FavorDao) IsFavoriteVideo(c context.Context, userId, videoId int64) (bool, error) { 19 | var count int64 20 | err := v.conn.Session(c).Model(&auto.Favorite{}).Where("user_id = ? AND video_id = ?", userId, videoId).Count(&count).Error 21 | return count > 0, err 22 | } 23 | 24 | func (v *FavorDao) IsFavoriteRecordExist(c context.Context, userId, videoId int64) (bool, error) { 25 | var count int64 26 | err := v.conn.Session(c).Model(&auto.Favorite{}). 27 | Where("user_id = ? AND video_id = ?", userId, videoId).Count(&count).Error 28 | return count > 0, err 29 | } 30 | 31 | func (v *FavorDao) CreateFavoriteRecord(c context.Context, conn DbConn, favoriteInfo *auto.Favorite) error { 32 | v.conn = conn.(*GormConn) 33 | return v.conn.Tx(c).Create(favoriteInfo).Error 34 | } 35 | 36 | func (v *FavorDao) DeleteFavoriteRecord(c context.Context, conn DbConn, favoriteInfo *auto.Favorite) error { 37 | v.conn = conn.(*GormConn) 38 | return v.conn.Tx(c).Model(&auto.Favorite{}). 39 | Where("user_id = ? AND video_id = ?", favoriteInfo.UserId, favoriteInfo.VideoId). 40 | Unscoped().Delete(favoriteInfo).Error 41 | } 42 | -------------------------------------------------------------------------------- /toktik-favor/internal/dao/mysql/tran.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // Transaction 事务的操作 一定跟数据库有关 注入数据库的连接 gorm.db 4 | type Transaction interface { 5 | Action(func(conn DbConn) error) error 6 | } 7 | 8 | type DbConn interface { 9 | Begin() 10 | Rollback() 11 | Commit() 12 | } 13 | 14 | type TransactionImpl struct { 15 | conn DbConn 16 | } 17 | 18 | func (t TransactionImpl) Action(f func(conn DbConn) error) error { 19 | t.conn.Begin() 20 | err := f(t.conn) 21 | if err != nil { 22 | t.conn.Rollback() 23 | return err 24 | } 25 | t.conn.Commit() 26 | return nil 27 | } 28 | 29 | func NewTransaction() *TransactionImpl { 30 | return &TransactionImpl{ 31 | conn: NewTran(), 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /toktik-favor/internal/global/root.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | "path/filepath" 7 | "sync" 8 | ) 9 | 10 | /* 11 | 用于推断当前项目根路径 12 | */ 13 | 14 | var ( 15 | RootDir string // 项目根路径 16 | once = new(sync.Once) 17 | ) 18 | 19 | func exist(filePath string) bool { 20 | _, err := os.Stat(filePath) 21 | return err == nil || errors.Is(err, os.ErrExist) 22 | } 23 | 24 | // 计算项目路径 25 | func inferRootDir() string { 26 | cwd, err := os.Getwd() 27 | if err != nil { 28 | panic(err) 29 | } 30 | var infer func(path string) string 31 | infer = func(path string) string { 32 | if exist(path + "/config") { 33 | return path 34 | } 35 | return infer(filepath.Dir(path)) 36 | } 37 | return infer(cwd) 38 | } 39 | 40 | func init() { 41 | once.Do(func() { 42 | RootDir = inferRootDir() 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /toktik-favor/internal/global/var.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "toktik-common/logger" 5 | "toktik-favor/internal/model/config" 6 | ) 7 | 8 | var ( 9 | Settings config.Config // Public配置 10 | Logger *logger.Log // 日志 11 | ) 12 | -------------------------------------------------------------------------------- /toktik-favor/internal/model/auto/base.go: -------------------------------------------------------------------------------- 1 | package auto 2 | 3 | import ( 4 | "gorm.io/gorm" 5 | "strconv" 6 | "time" 7 | ) 8 | 9 | type BaseModel struct { 10 | ID uint `gorm:"primarykey" json:"id,string"` 11 | CreatedAt time.Time `json:"created_at,string"` 12 | UpdatedAt time.Time `json:"updated_at,string"` 13 | DeletedAt gorm.DeletedAt `json:"deleted_at,string"` 14 | } 15 | 16 | func CreateUserKey(userId uint) string { 17 | return "user_info::" + strconv.FormatInt(int64(userId), 10) 18 | } 19 | 20 | func CreatePublishKey() string { 21 | return "video_publish" 22 | } 23 | 24 | func CreateUserVideoKey(userId uint) string { 25 | return "user_video::" + strconv.FormatInt(int64(userId), 10) 26 | } 27 | -------------------------------------------------------------------------------- /toktik-favor/internal/model/auto/favorite.go: -------------------------------------------------------------------------------- 1 | package auto 2 | 3 | import "strconv" 4 | 5 | // Favorite 点赞表 /* 6 | type Favorite struct { 7 | ID uint `json:"id,string" gorm:"primarykey"` 8 | UserId uint `json:"user_id,string" gorm:"uniqueIndex:user_video_id,not null;"` 9 | VideoId uint `json:"video_id,string" gorm:"uniqueIndex:user_video_id,not null;"` 10 | } 11 | 12 | func (*Favorite) TableName() string { 13 | return "favorite" 14 | } 15 | 16 | func CreateFavKey(userId uint) string { 17 | favStr := strconv.Itoa(int(userId)) 18 | return "user_favorite::" + favStr 19 | } 20 | -------------------------------------------------------------------------------- /toktik-favor/internal/model/constants.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | const ( 4 | MsgNil = "" 5 | ) 6 | 7 | type FileType string 8 | 9 | const ( 10 | RpcSuccess = 0 11 | ) 12 | 13 | const ( 14 | FAVORITE = iota + 1 15 | CANCELFAVORITE 16 | ) 17 | 18 | const ( 19 | RpcUser = "rpc-user" 20 | RpcVideo = "rpc-video" 21 | RpcInteraction = "rpc-interaction" 22 | RpcChat = "rpc-chat" 23 | RpcFavor = "rpc-favor" 24 | RpcComment = "rpc-comment" 25 | ) 26 | const ( 27 | TokTikApi = "toktik-api" 28 | TokTikUser = "toktik-user" 29 | TokTikInteraction = "toktik-interaction" 30 | TokTikVideo = "toktik-video" 31 | TokTikChat = "toktik-chat" 32 | TokTikFavor = "toktik-favor" 33 | TokTikComment = "toktik-comment" 34 | ) 35 | -------------------------------------------------------------------------------- /toktik-favor/internal/model/handler_resp.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "toktik-common/errcode" 5 | 6 | "toktik-rpc/kitex_gen/favor" 7 | ) 8 | 9 | type FavoriteListHandler struct { 10 | } 11 | 12 | func (FavoriteListHandler) FavoriteListResponse(err errcode.Err, msg string, resp *favor.FavoriteListResponse) *favor.FavoriteListResponse { 13 | resp.StatusCode = err.ECode() 14 | resp.StatusMsg = err.Error() + ":" + msg 15 | return resp 16 | } 17 | 18 | type FavoriteActionHandler struct { 19 | } 20 | 21 | func (FavoriteActionHandler) FavoriteActionResponse(err errcode.Err, msg string, resp *favor.FavoriteActionResponse) *favor.FavoriteActionResponse { 22 | resp.StatusCode = err.ECode() 23 | resp.StatusMsg = err.Error() + ":" + msg 24 | return resp 25 | } 26 | 27 | type IsFavoriteVideoHandler struct { 28 | } 29 | 30 | func (IsFavoriteVideoHandler) IsFavoriteVideoResponse(err errcode.Err, msg string, resp *favor.IsFavoriteVideosResponse) *favor.IsFavoriteVideosResponse { 31 | resp.StatusCode = err.ECode() 32 | resp.StatusMsg = err.Error() + ":" + msg 33 | return resp 34 | } 35 | -------------------------------------------------------------------------------- /toktik-favor/internal/repo/cache.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | type RCacheRepo interface { 8 | GetKeys(c context.Context, keyPattern string) ([]string, error) 9 | KeyExist(c context.Context, key string) (bool, error) 10 | CreateFavorite(c context.Context, key string, video int64) error 11 | DelFavorite(c context.Context, key string, videoId int64) error 12 | IsFavRecordExist(c context.Context, key string, videoId int64) (bool, error) 13 | GetFavoriteVideoIds(c context.Context, key string) ([]int64, error) 14 | } 15 | -------------------------------------------------------------------------------- /toktik-favor/internal/repo/favor.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "context" 5 | "toktik-favor/internal/dao/mysql" 6 | "toktik-favor/internal/model/auto" 7 | ) 8 | 9 | type FavorRepo interface { 10 | IsFavoriteVideo(c context.Context, userId, videoId int64) (bool, error) 11 | IsFavoriteRecordExist(c context.Context, userId, videoId int64) (bool, error) 12 | CreateFavoriteRecord(c context.Context, conn mysql.DbConn, favoriteInfo *auto.Favorite) error 13 | DeleteFavoriteRecord(c context.Context, conn mysql.DbConn, favoriteInfo *auto.Favorite) error 14 | } 15 | -------------------------------------------------------------------------------- /toktik-favor/internal/repo/handler_resp.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "toktik-common/errcode" 5 | "toktik-favor/internal/model" 6 | "toktik-rpc/kitex_gen/favor" 7 | ) 8 | 9 | type HandlerResp interface { 10 | FavoriteListResponse(err errcode.Err, msg string, resp *favor.FavoriteListResponse) *favor.FavoriteListResponse 11 | FavoriteActionResponse(err errcode.Err, msg string, resp *favor.FavoriteActionResponse) *favor.FavoriteActionResponse 12 | IsFavoriteVideoResponse(err errcode.Err, msg string, resp *favor.IsFavoriteVideosResponse) *favor.IsFavoriteVideosResponse 13 | } 14 | 15 | type HandlerResps struct { 16 | model.FavoriteListHandler 17 | model.FavoriteActionHandler 18 | model.IsFavoriteVideoHandler 19 | } 20 | 21 | func NewHandlerResps() *HandlerResps { 22 | return &HandlerResps{} 23 | } 24 | -------------------------------------------------------------------------------- /toktik-favor/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "toktik-favor/internal/global" 6 | "toktik-favor/pkg/rpc" 7 | "toktik-favor/pkg/rpc/client" 8 | "toktik-favor/pkg/setting" 9 | ) 10 | 11 | func main() { 12 | // 初始化 13 | setting.InitAllSetting() 14 | fmt.Printf("config:%#v\n", global.Settings) 15 | 16 | // RPC 注册 17 | client.NewRpcClient() 18 | fmt.Println("-----Favor Service Start ! ! !-----") 19 | rpc.RegisterRPC() 20 | 21 | } 22 | -------------------------------------------------------------------------------- /toktik-favor/pkg/myerr/myerr.go: -------------------------------------------------------------------------------- 1 | package myerr 2 | 3 | import "toktik-common/errcode" 4 | 5 | var ( 6 | AlreadyFavorite = errcode.NewErr(500001, "已经点过赞了呦~") 7 | IsNotFavorite = errcode.NewErr(500002, "未对该视频点过赞哦~") 8 | OperationErr = errcode.NewErr(500003, "没有这个功能哦~") 9 | ) 10 | -------------------------------------------------------------------------------- /toktik-favor/pkg/rpc/client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | func NewRpcClient() { 4 | InitRpcUserClient() 5 | InitRpcVideoClient() 6 | } 7 | -------------------------------------------------------------------------------- /toktik-favor/pkg/rpc/client/user.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/cloudwego/kitex/client" 5 | "github.com/kitex-contrib/obs-opentelemetry/provider" 6 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 7 | etcd "github.com/kitex-contrib/registry-etcd" 8 | "go.uber.org/zap" 9 | "toktik-favor/internal/global" 10 | "toktik-favor/internal/model" 11 | "toktik-rpc/kitex_gen/user/userservice" 12 | ) 13 | 14 | var UserClient userservice.Client 15 | 16 | func InitRpcUserClient() { 17 | r, err := etcd.NewEtcdResolver(global.Settings.Etcd.Addr) 18 | if err != nil { 19 | panic(err) 20 | } 21 | provider.NewOpenTelemetryProvider( 22 | provider.WithServiceName(global.Settings.Jaeger.ServerName[model.TokTikUser]), 23 | provider.WithExportEndpoint(global.Settings.Jaeger.RPCExportEndpoint), 24 | provider.WithInsecure(), 25 | ) 26 | c, err := userservice.NewClient( 27 | model.RpcUser, 28 | client.WithSuite(tracing.NewClientSuite()), 29 | //client.WithHostPorts(global.Settings.Rpc.ServerAddrs[model.RpcUser]), 30 | //client.WithMiddleware(rpcmiddleware.CommonMiddleware), 31 | //client.WithInstanceMW(rpcmiddleware.ClientMiddleware), 32 | client.WithResolver(r), 33 | ) 34 | if err != nil { 35 | zap.L().Error("InitRpcUserClient err:", zap.Error(err)) 36 | panic(err) 37 | } 38 | UserClient = c 39 | } 40 | -------------------------------------------------------------------------------- /toktik-favor/pkg/rpc/client/video.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/cloudwego/kitex/client" 5 | "github.com/kitex-contrib/obs-opentelemetry/provider" 6 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 7 | etcd "github.com/kitex-contrib/registry-etcd" 8 | "go.uber.org/zap" 9 | "toktik-favor/internal/global" 10 | "toktik-favor/internal/model" 11 | "toktik-rpc/kitex_gen/video/videoservice" 12 | ) 13 | 14 | var VideoClient videoservice.Client 15 | 16 | func InitRpcVideoClient() { 17 | r, err := etcd.NewEtcdResolver(global.Settings.Etcd.Addr) 18 | if err != nil { 19 | panic(err) 20 | } 21 | provider.NewOpenTelemetryProvider( 22 | provider.WithServiceName(global.Settings.Jaeger.ServerName[model.TokTikVideo]), 23 | provider.WithExportEndpoint(global.Settings.Jaeger.RPCExportEndpoint), 24 | provider.WithInsecure(), 25 | ) 26 | c, err := videoservice.NewClient( 27 | model.RpcVideo, 28 | client.WithSuite(tracing.NewClientSuite()), 29 | //client.WithHostPorts(global.Settings.Rpc.ServerAddrs[model.RpcUser]), 30 | //client.WithMiddleware(rpcmiddleware.CommonMiddleware), 31 | //client.WithInstanceMW(rpcmiddleware.ClientMiddleware), 32 | client.WithResolver(r), 33 | ) 34 | if err != nil { 35 | zap.L().Error("InitRpcInteractionClient err:", zap.Error(err)) 36 | panic(err) 37 | } 38 | VideoClient = c 39 | } 40 | -------------------------------------------------------------------------------- /toktik-favor/pkg/setting/dao.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "toktik-favor/internal/cache" 5 | "toktik-favor/internal/dao" 6 | "toktik-favor/internal/dao/cron" 7 | "toktik-favor/internal/dao/mysql" 8 | ) 9 | 10 | func init() { 11 | d := Dao{} 12 | Settings = append(Settings, d) 13 | } 14 | 15 | type Dao struct { 16 | } 17 | 18 | func (Dao) InitSetting() { 19 | mysql.InitMysql() 20 | dao.Group.Rdb = cache.InitRedis() 21 | go cron.TimingJob() 22 | } 23 | -------------------------------------------------------------------------------- /toktik-favor/pkg/setting/enter.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | var Settings []InitSettings 4 | 5 | type InitSettings interface { 6 | InitSetting() 7 | } 8 | 9 | func InitAllSetting() { 10 | InitBootStrap() 11 | for _, setting := range Settings { 12 | setting.InitSetting() 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /toktik-favor/pkg/setting/logger.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "go.uber.org/zap" 5 | "toktik-common/logger" 6 | "toktik-favor/internal/global" 7 | ) 8 | 9 | func init() { 10 | l := log{} 11 | Settings = append(Settings, l) 12 | } 13 | 14 | type log struct { 15 | } 16 | 17 | // InitSetting 日志初始化 18 | func (log) InitSetting() { 19 | global.Logger = logger.NewLogger(&logger.InitStruct{ 20 | LogSavePath: global.Settings.Logger.LogSavePath, 21 | LogFileExt: global.Settings.Logger.LogFileExt, 22 | MaxSize: global.Settings.Logger.MaxSize, 23 | MaxBackups: global.Settings.Logger.MaxBackups, 24 | MaxAge: global.Settings.Logger.MaxAge, 25 | Compress: global.Settings.Logger.Compress, 26 | LowLevelFile: global.Settings.Logger.LowLevelFile, 27 | HighLevelFile: global.Settings.Logger.HighLevelFile, 28 | }, global.Settings.Logger.Level) 29 | zap.ReplaceGlobals(global.Logger.Logger) 30 | } 31 | -------------------------------------------------------------------------------- /toktik-favor/pkg/setting/nacos.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "github.com/nacos-group/nacos-sdk-go/v2/clients" 5 | "github.com/nacos-group/nacos-sdk-go/v2/clients/config_client" 6 | "github.com/nacos-group/nacos-sdk-go/v2/common/constant" 7 | "github.com/nacos-group/nacos-sdk-go/v2/vo" 8 | log2 "log" 9 | "toktik-favor/internal/global" 10 | ) 11 | 12 | type NacosClient struct { 13 | confClient config_client.IConfigClient 14 | } 15 | 16 | func InitNacos() *NacosClient { 17 | clientConfig := constant.ClientConfig{ 18 | NamespaceId: global.Settings.Nacos.Namespace, //we can create multiple clients with different namespaceId to support multiple namespace.When namespace is public, fill in the blank string here. 19 | TimeoutMs: 5000, 20 | NotLoadCacheAtStart: true, 21 | LogDir: "/tmp/nacos/log", 22 | CacheDir: "/tmp/nacos/cache", 23 | LogLevel: "debug", 24 | } 25 | serverConfigs := []constant.ServerConfig{ 26 | { 27 | IpAddr: global.Settings.Nacos.Addr, 28 | ContextPath: global.Settings.Nacos.ContextPath, 29 | Port: uint64(global.Settings.Nacos.Port), 30 | Scheme: global.Settings.Nacos.Scheme, 31 | }, 32 | } 33 | configClient, err := clients.NewConfigClient( 34 | vo.NacosClientParam{ 35 | ClientConfig: &clientConfig, 36 | ServerConfigs: serverConfigs, 37 | }, 38 | ) 39 | if err != nil { 40 | log2.Fatalln(err) 41 | } 42 | nc := &NacosClient{ 43 | confClient: configClient, 44 | } 45 | return nc 46 | } 47 | -------------------------------------------------------------------------------- /toktik-interaction/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine 2 | WORKDIR /app 3 | COPY ./bin/toktik-interaction . 4 | COPY ./config . 5 | RUN mkdir config && mv config.yaml config/config.yaml && mv bootstrap.yaml config/bootstrap.yaml 6 | 7 | RUN chmod +x ./toktik-interaction 8 | 9 | EXPOSE 8082 8882 9192 10 | ENTRYPOINT ["./toktik-interaction"] 11 | -------------------------------------------------------------------------------- /toktik-interaction/config/bootstrap.yaml.template: -------------------------------------------------------------------------------- 1 | Nacos: 2 | Namespace: "xxx" # 命名空间id 3 | Group: "api" # group名称 4 | Addr: "xxx.xxx.xxx.xxx" # 地址 5 | Port: 8848 6 | Scheme: "http" 7 | ContextPath: "/nacos" -------------------------------------------------------------------------------- /toktik-interaction/config/config.yaml.template: -------------------------------------------------------------------------------- 1 | Server: 2 | Name: "toktik-interaction" 3 | RunMode: "DEBUG" 4 | Addr: "0.0.0.0:8082" 5 | DefaultContextTimeout: 10s 6 | 7 | Rules: 8 | DefaultUserAvatar: "https://q1.qlogo.cn/g?b=qq&nk=1780006511&s=640" 9 | DefaultUserSignature: "这里什么有没有捏" 10 | 11 | Jaeger: 12 | RPCExportEndpoint: "xxx:4317" 13 | ServerName: 14 | toktik-api: "toktik-api" 15 | toktik-user: "toktik-user" 16 | toktik-video: "toktik-video" 17 | toktik-interaction: "toktik-interaction" 18 | toktik-chat: "toktik-chat" 19 | toktik-favor: "toktik-favor" 20 | toktik-comment: "toktik-comment" 21 | 22 | Prometheus: 23 | Post: ":9192" 24 | Path: "/metrics" 25 | 26 | RPC: 27 | Name: "rpc-interaction" 28 | Addr: "0.0.0.0:8882" 29 | 30 | Logger: 31 | Level: "debug" 32 | LogSavePath: "storage/applogs/" 33 | LogFileExt: ".log" 34 | MaxSize: 10 35 | MaxBackups: 7 36 | MaxAge: 30 37 | Compress: false 38 | LowLevelFile: "info" 39 | HighLevelFile: "error" 40 | 41 | Mysql: 42 | Username: "root" 43 | Password: "xxx" 44 | Host: "xxx" 45 | Port: "3309" 46 | DB: "toktik_interaction" 47 | 48 | Redis: 49 | Host: "xxx" 50 | Port: "6379" 51 | Password: "" 52 | DB: 2 53 | 54 | Etcd: 55 | Addr: 56 | - "xxx:2379" 57 | -------------------------------------------------------------------------------- /toktik-interaction/internal/dao/enter.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "github.com/go-redis/redis/v8" 5 | "gorm.io/gorm" 6 | ) 7 | 8 | type group struct { 9 | Mdb *gorm.DB 10 | Rdb *redis.Client 11 | } 12 | 13 | var Group = new(group) 14 | -------------------------------------------------------------------------------- /toktik-interaction/internal/dao/mysql/tran.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // Transaction 事务的操作 一定跟数据库有关 注入数据库的连接 gorm.db 4 | type Transaction interface { 5 | Action(func(conn DbConn) error) error 6 | } 7 | 8 | type DbConn interface { 9 | Begin() 10 | Rollback() 11 | Commit() 12 | } 13 | 14 | type TransactionImpl struct { 15 | conn DbConn 16 | } 17 | 18 | func (t TransactionImpl) Action(f func(conn DbConn) error) error { 19 | t.conn.Begin() 20 | err := f(t.conn) 21 | if err != nil { 22 | t.conn.Rollback() 23 | return err 24 | } 25 | t.conn.Commit() 26 | return nil 27 | } 28 | 29 | func NewTransaction() *TransactionImpl { 30 | return &TransactionImpl{ 31 | conn: NewTran(), 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /toktik-interaction/internal/global/root.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | "path/filepath" 7 | "sync" 8 | ) 9 | 10 | /* 11 | 用于推断当前项目根路径 12 | */ 13 | 14 | var ( 15 | RootDir string // 项目根路径 16 | once = new(sync.Once) 17 | ) 18 | 19 | func exist(filePath string) bool { 20 | _, err := os.Stat(filePath) 21 | return err == nil || errors.Is(err, os.ErrExist) 22 | } 23 | 24 | // 计算项目路径 25 | func inferRootDir() string { 26 | cwd, err := os.Getwd() 27 | if err != nil { 28 | panic(err) 29 | } 30 | var infer func(path string) string 31 | infer = func(path string) string { 32 | if exist(path + "/config") { 33 | return path 34 | } 35 | return infer(filepath.Dir(path)) 36 | } 37 | return infer(cwd) 38 | } 39 | 40 | func init() { 41 | once.Do(func() { 42 | RootDir = inferRootDir() 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /toktik-interaction/internal/global/var.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "toktik-common/logger" 5 | "toktik-interaction/internal/model/config" 6 | ) 7 | 8 | var ( 9 | Settings config.Config // Public配置 10 | Logger *logger.Log // 日志 11 | ) 12 | -------------------------------------------------------------------------------- /toktik-interaction/internal/model/auto/base.go: -------------------------------------------------------------------------------- 1 | package auto 2 | 3 | import ( 4 | "gorm.io/gorm" 5 | "strconv" 6 | "time" 7 | ) 8 | 9 | type BaseModel struct { 10 | ID uint `gorm:"primarykey" json:"id,string"` 11 | CreatedAt time.Time `json:"created_at,string"` 12 | UpdatedAt time.Time `json:"updated_at,string"` 13 | DeletedAt gorm.DeletedAt `json:"deleted_at,string"` 14 | } 15 | 16 | func CreateUserKey(userId uint) string { 17 | userStr := strconv.Itoa(int(userId)) 18 | return "user_info::" + userStr 19 | } 20 | -------------------------------------------------------------------------------- /toktik-interaction/internal/model/auto/relation.go: -------------------------------------------------------------------------------- 1 | package auto 2 | 3 | import "strconv" 4 | 5 | type Relation struct { 6 | BaseModel 7 | UserId uint `json:"user_id,string" gorm:"index:idx_relation,not null"` // 用户ID 8 | TargetId uint `json:"target_id,string" gorm:"index:idx_relation,not null"` // 目标ID,添加复合索引 9 | //IsFriend int `json:"is_friend" gorm:"not null"` // 如果需要保证 relation_id 唯一,可以使用该字段 10 | } 11 | 12 | func (*Relation) TableName() string { 13 | return "relation" 14 | } 15 | 16 | func CreateFollowKey(userId int64) string { 17 | return "follow::" + strconv.FormatInt(userId, 10) 18 | } 19 | 20 | func CreateFanKey(userId int64) string { 21 | return "fan::" + strconv.FormatInt(userId, 10) 22 | } 23 | 24 | func CreateFriendKey(userId int64) string { 25 | return "friend::" + strconv.FormatInt(userId, 10) 26 | } 27 | -------------------------------------------------------------------------------- /toktik-interaction/internal/model/constants.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | const ( 4 | MsgNil = "" 5 | ) 6 | 7 | const ( 8 | Follow int32 = 1 9 | CancelFollow int32 = 2 10 | ) 11 | 12 | const ( 13 | RpcUser = "rpc-user" 14 | RpcVideo = "rpc-video" 15 | RpcInteraction = "rpc-interaction" 16 | RpcChat = "rpc-chat" 17 | ) 18 | const ( 19 | RpcSuccess = 0 20 | ) 21 | const ( 22 | TokTikApi = "toktik-api" 23 | TokTikUser = "toktik-user" 24 | TokTikInteraction = "toktik-interaction" 25 | TokTikVideo = "toktik-video" 26 | TokTikChat = "toktik-chat" 27 | TokTikFavor = "toktik-favor" 28 | TokTikComment = "toktik-comment" 29 | ) 30 | -------------------------------------------------------------------------------- /toktik-interaction/internal/repo/cache.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | type RCacheRepo interface { 8 | KeyExist(c context.Context, key string) (bool, error) 9 | SAddFollow(c context.Context, key string, targetId int64) error 10 | SAddManyIds(c context.Context, key string, followIds []int64) error 11 | SAddFriend(c context.Context, key string, targetId int64) error 12 | DelFollow(c context.Context, key string) error 13 | DelFan(c context.Context, key string) error 14 | DelFriend(c context.Context, key string) error 15 | IsFollow(c context.Context, key string, targetId int64) (bool, error) 16 | SGetAllIds(c context.Context, key string) ([]int64, error) 17 | IsFriend(c context.Context, key string, targetId int64) (bool, error) 18 | } 19 | -------------------------------------------------------------------------------- /toktik-interaction/internal/repo/handler_resp.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "toktik-common/errcode" 5 | "toktik-interaction/internal/model" 6 | inter "toktik-rpc/kitex_gen/interaction" 7 | ) 8 | 9 | type HandlerResp interface { 10 | FollowSBResponse(err errcode.Err, msg string, resp *inter.FollowActionResponse) *inter.FollowActionResponse 11 | FollowListResponse(err errcode.Err, msg string, resp *inter.FollowListResponse) *inter.FollowListResponse 12 | FansListResponse(err errcode.Err, msg string, resp *inter.FansListResponse) *inter.FansListResponse 13 | FriendListResponse(err errcode.Err, msg string, resp *inter.FriendListResponse) *inter.FriendListResponse 14 | IsFollowTargetResponse(err errcode.Err, msg string, resp *inter.IsFollowTargetResponse) *inter.IsFollowTargetResponse 15 | IsFollowManyTargetsResponse(err errcode.Err, msg string, resp *inter.IsFollowManyTargetsResponse) *inter.IsFollowManyTargetsResponse 16 | IsFriendResponse(err errcode.Err, msg string, resp *inter.IsFriendResponse) *inter.IsFriendResponse 17 | } 18 | 19 | type HandlerResps struct { 20 | model.FollowSBHandler 21 | model.FollowListHandler 22 | model.FansListHandler 23 | model.FriendListHandler 24 | model.IsFollowTargetHandler 25 | model.IsFollowManyTargetsHandler 26 | model.IsFriendHandler 27 | } 28 | 29 | func NewHandlerResps() *HandlerResps { 30 | return &HandlerResps{} 31 | } 32 | -------------------------------------------------------------------------------- /toktik-interaction/internal/repo/interaction.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "context" 5 | "toktik-interaction/internal/dao/mysql" 6 | "toktik-interaction/internal/model/auto" 7 | ) 8 | 9 | type InteractionRepo interface { 10 | IsRelationExist(c context.Context, myUserID, targetID int64) (uint, bool, error) 11 | FollowUserAction(c context.Context, conn mysql.DbConn, relationInfo *auto.Relation) error 12 | CancelFollowUser(c context.Context, conn mysql.DbConn, relationInfo *auto.Relation) error 13 | GetFollowIDs(c context.Context, userID uint) ([]int64, error) 14 | GetFansIDs(c context.Context, userID uint) ([]int64, error) 15 | GetFriendIDs(c context.Context, userID uint) ([]int64, error) 16 | IsFollowUser(c context.Context, myUserID, targetUserID int64) (bool, error) 17 | IsFriend(c context.Context, userId, targetId int64) (bool, error) 18 | } 19 | -------------------------------------------------------------------------------- /toktik-interaction/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "toktik-interaction/internal/global" 6 | "toktik-interaction/pkg/rpc" 7 | "toktik-interaction/pkg/rpc/client" 8 | "toktik-interaction/pkg/setting" 9 | ) 10 | 11 | func main() { 12 | // 初始化 13 | setting.InitAllSetting() 14 | fmt.Printf("config:%#v\n", global.Settings) 15 | 16 | // RPC 注册 17 | client.NewRpcClient() 18 | fmt.Println("-----Interaction Service Start ! ! !-----") 19 | rpc.RegisterRPC() 20 | } 21 | -------------------------------------------------------------------------------- /toktik-interaction/pkg/myerr/myerr.go: -------------------------------------------------------------------------------- 1 | package myerr 2 | 3 | import "toktik-common/errcode" 4 | 5 | var ( 6 | UserAlreadyFollowed = errcode.NewErr(200001, "已经关注该用户") 7 | UserNotFollowed = errcode.NewErr(200002, "该用户未被关注") 8 | NotCanFollowSelf = errcode.NewErr(200003, "不可以对自己操作哦~") 9 | OperationErr = errcode.NewErr(200004, "错误的操作") 10 | ) 11 | -------------------------------------------------------------------------------- /toktik-interaction/pkg/rpc/client/chat.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/cloudwego/kitex/client" 5 | "github.com/kitex-contrib/obs-opentelemetry/provider" 6 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 7 | etcd "github.com/kitex-contrib/registry-etcd" 8 | "go.uber.org/zap" 9 | "toktik-interaction/internal/global" 10 | "toktik-interaction/internal/model" 11 | "toktik-rpc/kitex_gen/chat/chatservice" 12 | ) 13 | 14 | var ChatCache chatservice.Client 15 | 16 | func InitRpcChatCache() { 17 | r, err := etcd.NewEtcdResolver(global.Settings.Etcd.Addr) 18 | if err != nil { 19 | panic(err) 20 | } 21 | provider.NewOpenTelemetryProvider( 22 | provider.WithServiceName(global.Settings.Jaeger.ServerName[model.TokTikChat]), 23 | provider.WithExportEndpoint(global.Settings.Jaeger.RPCExportEndpoint), 24 | provider.WithInsecure(), 25 | ) 26 | c, err := chatservice.NewClient( 27 | model.RpcChat, 28 | client.WithSuite(tracing.NewClientSuite()), 29 | //client.WithHostPorts(global.Settings.Rpc.ServerAddrs[model.RpcUser]), 30 | //client.WithMiddleware(rpcmiddleware.CommonMiddleware), 31 | //client.WithInstanceMW(rpcmiddleware.ClientMiddleware), 32 | client.WithResolver(r), 33 | ) 34 | if err != nil { 35 | zap.L().Error("InitRpcUserCache err:", zap.Error(err)) 36 | panic(err) 37 | } 38 | ChatCache = c 39 | } 40 | -------------------------------------------------------------------------------- /toktik-interaction/pkg/rpc/client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | func NewRpcClient() { 4 | InitRpcUserCache() 5 | InitRpcChatCache() 6 | } 7 | -------------------------------------------------------------------------------- /toktik-interaction/pkg/rpc/client/user.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/cloudwego/kitex/client" 5 | "github.com/kitex-contrib/obs-opentelemetry/provider" 6 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 7 | etcd "github.com/kitex-contrib/registry-etcd" 8 | "go.uber.org/zap" 9 | "toktik-interaction/internal/global" 10 | "toktik-interaction/internal/model" 11 | "toktik-rpc/kitex_gen/user/userservice" 12 | ) 13 | 14 | var UserCache userservice.Client 15 | 16 | func InitRpcUserCache() { 17 | r, err := etcd.NewEtcdResolver(global.Settings.Etcd.Addr) 18 | if err != nil { 19 | panic(err) 20 | } 21 | provider.NewOpenTelemetryProvider( 22 | provider.WithServiceName(global.Settings.Jaeger.ServerName[model.TokTikUser]), 23 | provider.WithExportEndpoint(global.Settings.Jaeger.RPCExportEndpoint), 24 | provider.WithInsecure(), 25 | ) 26 | c, err := userservice.NewClient( 27 | model.RpcUser, 28 | client.WithSuite(tracing.NewClientSuite()), 29 | //client.WithHostPorts(global.Settings.Rpc.ServerAddrs[model.RpcUser]), 30 | //client.WithMiddleware(rpcmiddleware.CommonMiddleware), 31 | //client.WithInstanceMW(rpcmiddleware.ClientMiddleware), 32 | client.WithResolver(r), 33 | ) 34 | if err != nil { 35 | zap.L().Error("InitRpcUserCache err:", zap.Error(err)) 36 | panic(err) 37 | } 38 | UserCache = c 39 | } 40 | -------------------------------------------------------------------------------- /toktik-interaction/pkg/setting/dao.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "toktik-interaction/internal/cache" 5 | "toktik-interaction/internal/dao" 6 | "toktik-interaction/internal/dao/mysql" 7 | ) 8 | 9 | func init() { 10 | d := Dao{} 11 | Settings = append(Settings, d) 12 | } 13 | 14 | type Dao struct { 15 | } 16 | 17 | func (Dao) InitSetting() { 18 | mysql.InitMysql() 19 | dao.Group.Rdb = cache.InitRedis() 20 | } 21 | -------------------------------------------------------------------------------- /toktik-interaction/pkg/setting/enter.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | var Settings []InitSettings 4 | 5 | type InitSettings interface { 6 | InitSetting() 7 | } 8 | 9 | func InitAllSetting() { 10 | InitBootStrap() 11 | for _, setting := range Settings { 12 | setting.InitSetting() 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /toktik-interaction/pkg/setting/logger.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "go.uber.org/zap" 5 | "toktik-common/logger" 6 | "toktik-interaction/internal/global" 7 | ) 8 | 9 | func init() { 10 | l := log{} 11 | Settings = append(Settings, l) 12 | } 13 | 14 | type log struct { 15 | } 16 | 17 | // InitSetting 日志初始化 18 | func (log) InitSetting() { 19 | global.Logger = logger.NewLogger(&logger.InitStruct{ 20 | LogSavePath: global.Settings.Logger.LogSavePath, 21 | LogFileExt: global.Settings.Logger.LogFileExt, 22 | MaxSize: global.Settings.Logger.MaxSize, 23 | MaxBackups: global.Settings.Logger.MaxBackups, 24 | MaxAge: global.Settings.Logger.MaxAge, 25 | Compress: global.Settings.Logger.Compress, 26 | LowLevelFile: global.Settings.Logger.LowLevelFile, 27 | HighLevelFile: global.Settings.Logger.HighLevelFile, 28 | }, global.Settings.Logger.Level) 29 | zap.ReplaceGlobals(global.Logger.Logger) 30 | } 31 | -------------------------------------------------------------------------------- /toktik-interaction/pkg/setting/nacos.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "github.com/nacos-group/nacos-sdk-go/v2/clients" 5 | "github.com/nacos-group/nacos-sdk-go/v2/clients/config_client" 6 | "github.com/nacos-group/nacos-sdk-go/v2/common/constant" 7 | "github.com/nacos-group/nacos-sdk-go/v2/vo" 8 | log2 "log" 9 | "toktik-interaction/internal/global" 10 | ) 11 | 12 | type NacosClient struct { 13 | confClient config_client.IConfigClient 14 | } 15 | 16 | func InitNacos() *NacosClient { 17 | clientConfig := constant.ClientConfig{ 18 | NamespaceId: global.Settings.Nacos.Namespace, //we can create multiple clients with different namespaceId to support multiple namespace.When namespace is public, fill in the blank string here. 19 | TimeoutMs: 5000, 20 | NotLoadCacheAtStart: true, 21 | LogDir: "/tmp/nacos/log", 22 | CacheDir: "/tmp/nacos/cache", 23 | LogLevel: "debug", 24 | } 25 | serverConfigs := []constant.ServerConfig{ 26 | { 27 | IpAddr: global.Settings.Nacos.Addr, 28 | ContextPath: global.Settings.Nacos.ContextPath, 29 | Port: uint64(global.Settings.Nacos.Port), 30 | Scheme: global.Settings.Nacos.Scheme, 31 | }, 32 | } 33 | configClient, err := clients.NewConfigClient( 34 | vo.NacosClientParam{ 35 | ClientConfig: &clientConfig, 36 | ServerConfigs: serverConfigs, 37 | }, 38 | ) 39 | if err != nil { 40 | log2.Fatalln(err) 41 | } 42 | nc := &NacosClient{ 43 | confClient: configClient, 44 | } 45 | return nc 46 | } 47 | -------------------------------------------------------------------------------- /toktik-rpc/idl/chat.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package chat; 3 | 4 | option go_package = "chat"; 5 | 6 | message MessageListRequest { 7 | int64 user_id = 1; 8 | int64 to_user_id = 2; // 对方用户id 9 | int64 pre_msg_time=3;//上次最新消息的时间(新增字段-apk更新中) 10 | } 11 | 12 | message MessageListResponse { 13 | int64 status_code = 1; // 状态码,0-成功,其他值-失败 14 | string status_msg = 2; // 返回状态描述 15 | repeated Message message_list = 3; // 消息列表 16 | } 17 | 18 | message Message { 19 | int64 id = 1; // 消息id 20 | int64 to_user_id = 2; // 该消息接收者的id 21 | int64 from_user_id =3; // 该消息发送者的id 22 | string content = 4; // 消息内容 23 | int64 create_time = 5; // 消息创建时间 24 | } 25 | 26 | message ChatActionRequest { 27 | int64 user_id = 1; 28 | int64 to_user_id = 2; // 对方用户id 29 | int32 action_type = 3; // 1-发送消息 30 | string content = 4; // 消息内容 31 | } 32 | 33 | message ChatActionResponse { 34 | int64 status_code = 1; // 状态码,0-成功,其他值-失败 35 | string status_msg = 2; // 返回状态描述 36 | } 37 | 38 | message GetFriendLatestMessageRequest{ 39 | int64 user_id = 1; 40 | repeated int64 friend_ids = 2; 41 | } 42 | 43 | message GetFriendLatestMessageResponse{ 44 | int64 status_code = 1; // 状态码,0-成功,其他值-失败 45 | string status_msg = 2; // 返回状态描述 46 | repeated string message_list = 3; 47 | repeated int32 msg_type_list = 4; 48 | } 49 | 50 | service ChatService{ 51 | rpc MessageList(MessageListRequest)returns(MessageListResponse); 52 | rpc ChatAction(ChatActionRequest)returns(ChatActionResponse); 53 | rpc GetFriendLatestMessage(GetFriendLatestMessageRequest)returns(GetFriendLatestMessageResponse); 54 | } -------------------------------------------------------------------------------- /toktik-rpc/idl/comment.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | import "user.proto"; 3 | package comment; 4 | 5 | option go_package = "comment"; 6 | 7 | message CommentActionRequest { 8 | int64 user_id = 1; 9 | int64 video_id = 2; // 视频id 10 | int32 action_type = 3; // 1-发布评论,2-删除评论 11 | optional string comment_text = 4; // 用户填写的评论内容,在action_type=1的时候使用 12 | optional int64 comment_id = 5; // 要删除的评论id,在action_type=2的时候使用 13 | } 14 | 15 | message CommentActionResponse { 16 | int64 status_code = 1; // 状态码,0-成功,其他值-失败 17 | string status_msg = 2; // 返回状态描述 18 | optional Comment comment = 3; // 评论成功返回评论内容,不需要重新拉取整个列表 19 | } 20 | 21 | message Comment { 22 | int64 id = 1; // 视频评论id 23 | user.User user =2; // 评论用户信息 24 | string content = 3; // 评论内容 25 | string create_date = 4; // 评论发布日期,格式 mm-dd 26 | } 27 | 28 | message CommentListRequest { 29 | int64 user_id = 1; 30 | int64 video_id = 2; // 视频id 31 | } 32 | 33 | message CommentListResponse { 34 | int64 status_code = 1; // 状态码,0-成功,其他值-失败 35 | string status_msg = 2; // 返回状态描述 36 | repeated Comment comment_list = 3; // 评论列表 37 | } 38 | 39 | service CommentService{ 40 | rpc CommentAction(CommentActionRequest)returns(CommentActionResponse); 41 | rpc CommentList(CommentListRequest)returns(CommentListResponse); 42 | } 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /toktik-rpc/idl/favor.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | import "video.proto"; 3 | package favor; 4 | 5 | option go_package = "favor"; 6 | 7 | message FavoriteListRequest { 8 | int64 user_id = 1; // 用户id 9 | int64 my_user_id = 2; // 当前登录用户 10 | } 11 | 12 | message FavoriteListResponse { 13 | int64 status_code = 1; // 状态码,0-成功,其他值-失败 14 | string status_msg = 2; // 返回状态描述 15 | repeated video.Video video_list = 3; // 用户点赞视频列表 16 | } 17 | 18 | message FavoriteActionRequest { 19 | int64 user_id = 1; 20 | int64 video_id = 2; // 视频id 21 | int32 action_type = 3; // 1-点赞,2-取消点赞 22 | } 23 | 24 | message FavoriteActionResponse { 25 | int64 status_code = 1; // 状态码,0-成功,其他值-失败 26 | string status_msg = 2; // 返回状态描述 27 | } 28 | 29 | message IsFavoriteVideosRequest{ 30 | int64 user_id = 1; 31 | repeated int64 video_ids = 2; // 视频id 32 | } 33 | 34 | message IsFavoriteVideosResponse{ 35 | int64 status_code = 1; // 状态码,0-成功,其他值-失败 36 | string status_msg = 2; // 返回状态描述 37 | repeated bool many_is_favorite = 3; // 很多个是否点赞 38 | } 39 | 40 | service FavorService{ 41 | rpc FavoriteList(FavoriteListRequest)returns(FavoriteListResponse); 42 | rpc FavoriteAction(FavoriteActionRequest)returns(FavoriteActionResponse); 43 | rpc IsFavoriteVideos(IsFavoriteVideosRequest)returns(IsFavoriteVideosResponse); 44 | } 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /toktik-rpc/kitex_gen/chat/chatservice/invoker.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.6.2. DO NOT EDIT. 2 | 3 | package chatservice 4 | 5 | import ( 6 | server "github.com/cloudwego/kitex/server" 7 | chat "toktik-rpc/kitex_gen/chat" 8 | ) 9 | 10 | // NewInvoker creates a server.Invoker with the given handler and options. 11 | func NewInvoker(handler chat.ChatService, opts ...server.Option) server.Invoker { 12 | var options []server.Option 13 | 14 | options = append(options, opts...) 15 | 16 | s := server.NewInvoker(options...) 17 | if err := s.RegisterService(serviceInfo(), handler); err != nil { 18 | panic(err) 19 | } 20 | if err := s.Init(); err != nil { 21 | panic(err) 22 | } 23 | return s 24 | } 25 | -------------------------------------------------------------------------------- /toktik-rpc/kitex_gen/chat/chatservice/server.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.6.2. DO NOT EDIT. 2 | package chatservice 3 | 4 | import ( 5 | server "github.com/cloudwego/kitex/server" 6 | chat "toktik-rpc/kitex_gen/chat" 7 | ) 8 | 9 | // NewServer creates a server.Server with the given handler and options. 10 | func NewServer(handler chat.ChatService, opts ...server.Option) server.Server { 11 | var options []server.Option 12 | 13 | options = append(options, opts...) 14 | 15 | svr := server.NewServer(options...) 16 | if err := svr.RegisterService(serviceInfo(), handler); err != nil { 17 | panic(err) 18 | } 19 | return svr 20 | } 21 | -------------------------------------------------------------------------------- /toktik-rpc/kitex_gen/comment/commentservice/invoker.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.6.2. DO NOT EDIT. 2 | 3 | package commentservice 4 | 5 | import ( 6 | server "github.com/cloudwego/kitex/server" 7 | comment "toktik-rpc/kitex_gen/comment" 8 | ) 9 | 10 | // NewInvoker creates a server.Invoker with the given handler and options. 11 | func NewInvoker(handler comment.CommentService, opts ...server.Option) server.Invoker { 12 | var options []server.Option 13 | 14 | options = append(options, opts...) 15 | 16 | s := server.NewInvoker(options...) 17 | if err := s.RegisterService(serviceInfo(), handler); err != nil { 18 | panic(err) 19 | } 20 | if err := s.Init(); err != nil { 21 | panic(err) 22 | } 23 | return s 24 | } 25 | -------------------------------------------------------------------------------- /toktik-rpc/kitex_gen/comment/commentservice/server.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.6.2. DO NOT EDIT. 2 | package commentservice 3 | 4 | import ( 5 | server "github.com/cloudwego/kitex/server" 6 | comment "toktik-rpc/kitex_gen/comment" 7 | ) 8 | 9 | // NewServer creates a server.Server with the given handler and options. 10 | func NewServer(handler comment.CommentService, opts ...server.Option) server.Server { 11 | var options []server.Option 12 | 13 | options = append(options, opts...) 14 | 15 | svr := server.NewServer(options...) 16 | if err := svr.RegisterService(serviceInfo(), handler); err != nil { 17 | panic(err) 18 | } 19 | return svr 20 | } 21 | -------------------------------------------------------------------------------- /toktik-rpc/kitex_gen/favor/favorservice/invoker.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.6.2. DO NOT EDIT. 2 | 3 | package favorservice 4 | 5 | import ( 6 | server "github.com/cloudwego/kitex/server" 7 | favor "toktik-rpc/kitex_gen/favor" 8 | ) 9 | 10 | // NewInvoker creates a server.Invoker with the given handler and options. 11 | func NewInvoker(handler favor.FavorService, opts ...server.Option) server.Invoker { 12 | var options []server.Option 13 | 14 | options = append(options, opts...) 15 | 16 | s := server.NewInvoker(options...) 17 | if err := s.RegisterService(serviceInfo(), handler); err != nil { 18 | panic(err) 19 | } 20 | if err := s.Init(); err != nil { 21 | panic(err) 22 | } 23 | return s 24 | } 25 | -------------------------------------------------------------------------------- /toktik-rpc/kitex_gen/favor/favorservice/server.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.6.2. DO NOT EDIT. 2 | package favorservice 3 | 4 | import ( 5 | server "github.com/cloudwego/kitex/server" 6 | favor "toktik-rpc/kitex_gen/favor" 7 | ) 8 | 9 | // NewServer creates a server.Server with the given handler and options. 10 | func NewServer(handler favor.FavorService, opts ...server.Option) server.Server { 11 | var options []server.Option 12 | 13 | options = append(options, opts...) 14 | 15 | svr := server.NewServer(options...) 16 | if err := svr.RegisterService(serviceInfo(), handler); err != nil { 17 | panic(err) 18 | } 19 | return svr 20 | } 21 | -------------------------------------------------------------------------------- /toktik-rpc/kitex_gen/interaction/interactionservice/invoker.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.6.2. DO NOT EDIT. 2 | 3 | package interactionservice 4 | 5 | import ( 6 | server "github.com/cloudwego/kitex/server" 7 | interaction "toktik-rpc/kitex_gen/interaction" 8 | ) 9 | 10 | // NewInvoker creates a server.Invoker with the given handler and options. 11 | func NewInvoker(handler interaction.InteractionService, opts ...server.Option) server.Invoker { 12 | var options []server.Option 13 | 14 | options = append(options, opts...) 15 | 16 | s := server.NewInvoker(options...) 17 | if err := s.RegisterService(serviceInfo(), handler); err != nil { 18 | panic(err) 19 | } 20 | if err := s.Init(); err != nil { 21 | panic(err) 22 | } 23 | return s 24 | } 25 | -------------------------------------------------------------------------------- /toktik-rpc/kitex_gen/interaction/interactionservice/server.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.6.2. DO NOT EDIT. 2 | package interactionservice 3 | 4 | import ( 5 | server "github.com/cloudwego/kitex/server" 6 | interaction "toktik-rpc/kitex_gen/interaction" 7 | ) 8 | 9 | // NewServer creates a server.Server with the given handler and options. 10 | func NewServer(handler interaction.InteractionService, opts ...server.Option) server.Server { 11 | var options []server.Option 12 | 13 | options = append(options, opts...) 14 | 15 | svr := server.NewServer(options...) 16 | if err := svr.RegisterService(serviceInfo(), handler); err != nil { 17 | panic(err) 18 | } 19 | return svr 20 | } 21 | -------------------------------------------------------------------------------- /toktik-rpc/kitex_gen/user/userservice/invoker.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.6.2. DO NOT EDIT. 2 | 3 | package userservice 4 | 5 | import ( 6 | server "github.com/cloudwego/kitex/server" 7 | user "toktik-rpc/kitex_gen/user" 8 | ) 9 | 10 | // NewInvoker creates a server.Invoker with the given handler and options. 11 | func NewInvoker(handler user.UserService, opts ...server.Option) server.Invoker { 12 | var options []server.Option 13 | 14 | options = append(options, opts...) 15 | 16 | s := server.NewInvoker(options...) 17 | if err := s.RegisterService(serviceInfo(), handler); err != nil { 18 | panic(err) 19 | } 20 | if err := s.Init(); err != nil { 21 | panic(err) 22 | } 23 | return s 24 | } 25 | -------------------------------------------------------------------------------- /toktik-rpc/kitex_gen/user/userservice/server.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.6.2. DO NOT EDIT. 2 | package userservice 3 | 4 | import ( 5 | server "github.com/cloudwego/kitex/server" 6 | user "toktik-rpc/kitex_gen/user" 7 | ) 8 | 9 | // NewServer creates a server.Server with the given handler and options. 10 | func NewServer(handler user.UserService, opts ...server.Option) server.Server { 11 | var options []server.Option 12 | 13 | options = append(options, opts...) 14 | 15 | svr := server.NewServer(options...) 16 | if err := svr.RegisterService(serviceInfo(), handler); err != nil { 17 | panic(err) 18 | } 19 | return svr 20 | } 21 | -------------------------------------------------------------------------------- /toktik-rpc/kitex_gen/video/videoservice/invoker.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.6.2. DO NOT EDIT. 2 | 3 | package videoservice 4 | 5 | import ( 6 | server "github.com/cloudwego/kitex/server" 7 | video "toktik-rpc/kitex_gen/video" 8 | ) 9 | 10 | // NewInvoker creates a server.Invoker with the given handler and options. 11 | func NewInvoker(handler video.VideoService, opts ...server.Option) server.Invoker { 12 | var options []server.Option 13 | 14 | options = append(options, opts...) 15 | 16 | s := server.NewInvoker(options...) 17 | if err := s.RegisterService(serviceInfo(), handler); err != nil { 18 | panic(err) 19 | } 20 | if err := s.Init(); err != nil { 21 | panic(err) 22 | } 23 | return s 24 | } 25 | -------------------------------------------------------------------------------- /toktik-rpc/kitex_gen/video/videoservice/server.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.6.2. DO NOT EDIT. 2 | package videoservice 3 | 4 | import ( 5 | server "github.com/cloudwego/kitex/server" 6 | video "toktik-rpc/kitex_gen/video" 7 | ) 8 | 9 | // NewServer creates a server.Server with the given handler and options. 10 | func NewServer(handler video.VideoService, opts ...server.Option) server.Server { 11 | var options []server.Option 12 | 13 | options = append(options, opts...) 14 | 15 | svr := server.NewServer(options...) 16 | if err := svr.RegisterService(serviceInfo(), handler); err != nil { 17 | panic(err) 18 | } 19 | return svr 20 | } 21 | -------------------------------------------------------------------------------- /toktik-rpc/zgen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | kitex -module toktik-rpc -service chat -I E:\\Go\\goproject\\src\\my_project\\toktik\\toktik-rpc\\idl chat.proto 4 | 5 | kitex -module toktik-rpc -service interaction -I E:\\Go\\goproject\\src\\my_project\\toktik\\toktik-rpc\\idl interaction.proto 6 | 7 | kitex -module toktik-rpc -service user -I E:\\Go\\goproject\\src\\my_project\\toktik\\toktik-rpc\\idl user.proto 8 | 9 | kitex -module toktik-rpc -service video -I E:\\Go\\goproject\\src\\my_project\\toktik\\toktik-rpc\\idl video.proto 10 | 11 | kitex -module toktik-rpc -service favor -I E:\\Go\\goproject\\src\\my_project\\toktik\\toktik-rpc\\idl favor.proto 12 | 13 | kitex -module toktik-rpc -service comment -I E:\\Go\\goproject\\src\\my_project\\toktik\\toktik-rpc\\idl comment.proto -------------------------------------------------------------------------------- /toktik-user/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine 2 | WORKDIR /app 3 | COPY ./bin/toktik-user . 4 | COPY ./config . 5 | RUN mkdir config && mv config.yaml config/config.yaml && mv bootstrap.yaml config/bootstrap.yaml 6 | 7 | RUN chmod +x ./toktik-user 8 | 9 | EXPOSE 8081 8881 9191 10 | ENTRYPOINT ["./toktik-user"] 11 | -------------------------------------------------------------------------------- /toktik-user/config/bootstrap.yaml.template: -------------------------------------------------------------------------------- 1 | Nacos: 2 | Namespace: "xxx" # 命名空间id 3 | Group: "api" # group名称 4 | Addr: "xxx.xxx.xxx.xxx" # 地址 5 | Port: 8848 6 | Scheme: "http" 7 | ContextPath: "/nacos" -------------------------------------------------------------------------------- /toktik-user/config/config.yaml.template: -------------------------------------------------------------------------------- 1 | Server: 2 | Name: "toktik-user" 3 | RunMode: "DEBUG" 4 | Addr: "0.0.0.0:8081" 5 | DefaultContextTimeout: 10s 6 | 7 | Rules: 8 | DefaultUserAvatar: "https://q1.qlogo.cn/g?b=qq&nk=1780006511&s=640" 9 | DefaultUserSignature: "这里什么有没有捏" 10 | DefaultUserBackGroundImage: "https://why-bucket.oss-cn-beijing.aliyuncs.com/back.jpg" 11 | 12 | Jaeger: 13 | RPCExportEndpoint: "xxx:4317" 14 | ServerName: 15 | toktik-api: "toktik-api" 16 | toktik-user: "toktik-user" 17 | toktik-video: "toktik-video" 18 | toktik-interaction: "toktik-interaction" 19 | toktik-chat: "toktik-chat" 20 | toktik-favor: "toktik-favor" 21 | toktik-comment: "toktik-comment" 22 | 23 | Prometheus: 24 | Post: ":9191" 25 | Path: "/metrics" 26 | 27 | RPC: 28 | Name: "rpc-user" 29 | Addr: "0.0.0.0:8881" 30 | 31 | Logger: 32 | Level: "debug" 33 | LogSavePath: "storage/applogs/" 34 | LogFileExt: ".log" 35 | MaxSize: 10 36 | MaxBackups: 7 37 | MaxAge: 30 38 | Compress: false 39 | LowLevelFile: "info" 40 | HighLevelFile: "error" 41 | 42 | Mysql: 43 | Username: "root" 44 | Password: "xxx" 45 | Host: "xxx" 46 | Port: "3309" 47 | DB: "toktik_user" 48 | 49 | Redis: 50 | Host: "xxx" 51 | Port: "6379" 52 | Password: "" 53 | DB: 1 54 | 55 | Etcd: 56 | Addr: 57 | - "xxx:2379" 58 | 59 | Token: 60 | Key: "xxx" 61 | UserTokenExp: 168h # 小时 62 | AuthorizationKey: xxx -------------------------------------------------------------------------------- /toktik-user/internal/dao/cron/usercron.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import ( 4 | "context" 5 | "go.uber.org/zap" 6 | "toktik-common/timing_job" 7 | "toktik-user/internal/cache" 8 | "toktik-user/internal/dao/mysql" 9 | "toktik-user/internal/model/auto" 10 | ) 11 | 12 | func TimingJob() { 13 | go timing_job.StartMinuteJob(UserInfoMoveToDB, 30, "userInfo") 14 | select {} 15 | } 16 | 17 | func UserInfoMoveToDB() { 18 | ctx := context.Background() 19 | rdb := cache.GetRdbCache() 20 | dbConn := mysql.NewGormConn() 21 | // 从数据库取 user_ids 22 | userIds := make([]int64, 0) 23 | err := dbConn.Session(ctx).Model(&auto.User{}).Pluck("id", &userIds).Error 24 | if err != nil { 25 | zap.L().Error("TimingJob UserInfoMoveToDB GetUserIds err:", zap.Error(err)) 26 | return 27 | } 28 | // 去 redis中获取user_count 29 | for _, v := range userIds { 30 | userCntKey := auto.CreateUserCountKey(uint(v)) 31 | userCntMap, err := rdb.HGetAll(ctx, userCntKey) 32 | if err != nil { 33 | zap.L().Error("TimingJob UserInfoMoveToDB rdb.HGetAll err:", zap.Error(err)) 34 | return 35 | } 36 | userCntInfo, err := auto.CreateUserCountInfo(userCntMap) 37 | if err != nil { 38 | zap.L().Error("TimingJob UserInfoMoveToDB auto.CreateUserCountInfo err:", zap.Error(err)) 39 | return 40 | } 41 | // 更新数据库 42 | err = dbConn.Session(ctx).Model(&auto.UserCount{}).Where("user_id = ?", v).Updates(&userCntInfo).Error 43 | if err != nil { 44 | zap.L().Error("TimingJob UserInfoMoveToDB DB err:", zap.Error(err)) 45 | return 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /toktik-user/internal/dao/enter.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "github.com/go-redis/redis/v8" 5 | "gorm.io/gorm" 6 | ) 7 | 8 | type group struct { 9 | Mdb *gorm.DB 10 | Rdb *redis.Client 11 | } 12 | 13 | var Group = new(group) 14 | -------------------------------------------------------------------------------- /toktik-user/internal/dao/mysql/tran.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // Transaction 事务的操作 一定跟数据库有关 注入数据库的连接 gorm.db 4 | type Transaction interface { 5 | Action(func(conn DbConn) error) error 6 | } 7 | 8 | type DbConn interface { 9 | Begin() 10 | Rollback() 11 | Commit() 12 | } 13 | 14 | type TransactionImpl struct { 15 | conn DbConn 16 | } 17 | 18 | func (t TransactionImpl) Action(f func(conn DbConn) error) error { 19 | t.conn.Begin() 20 | err := f(t.conn) 21 | if err != nil { 22 | t.conn.Rollback() 23 | return err 24 | } 25 | t.conn.Commit() 26 | return nil 27 | } 28 | 29 | func NewTransaction() *TransactionImpl { 30 | return &TransactionImpl{ 31 | conn: NewTran(), 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /toktik-user/internal/global/root.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | "path/filepath" 7 | "sync" 8 | ) 9 | 10 | /* 11 | 用于推断当前项目根路径 12 | */ 13 | 14 | var ( 15 | RootDir string // 项目根路径 16 | once = new(sync.Once) 17 | ) 18 | 19 | func exist(filePath string) bool { 20 | _, err := os.Stat(filePath) 21 | return err == nil || errors.Is(err, os.ErrExist) 22 | } 23 | 24 | // 计算项目路径 25 | func inferRootDir() string { 26 | cwd, err := os.Getwd() 27 | if err != nil { 28 | panic(err) 29 | } 30 | var infer func(path string) string 31 | infer = func(path string) string { 32 | if exist(path + "/config") { 33 | return path 34 | } 35 | return infer(filepath.Dir(path)) 36 | } 37 | return infer(cwd) 38 | } 39 | 40 | func init() { 41 | once.Do(func() { 42 | RootDir = inferRootDir() 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /toktik-user/internal/global/var.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "toktik-common/logger" 5 | "toktik-common/token" 6 | "toktik-common/utils" 7 | "toktik-user/internal/model/config" 8 | ) 9 | 10 | var ( 11 | Settings config.Config // Public配置 12 | Logger *logger.Log // 日志 13 | Maker token.Maker 14 | SnowFlake *utils.SnowFlake 15 | //RdbClient *redis.RdbCache 16 | ) 17 | -------------------------------------------------------------------------------- /toktik-user/internal/model/auto/base.go: -------------------------------------------------------------------------------- 1 | package auto 2 | 3 | import ( 4 | "gorm.io/gorm" 5 | "time" 6 | ) 7 | 8 | type BaseModel struct { 9 | ID uint `gorm:"primarykey" json:"id,string"` 10 | CreatedAt time.Time `json:"created_at,string"` 11 | UpdatedAt time.Time `json:"updated_at,string"` 12 | DeletedAt gorm.DeletedAt `json:"deleted_at,string"` 13 | } 14 | -------------------------------------------------------------------------------- /toktik-user/internal/model/auto/user_count.go: -------------------------------------------------------------------------------- 1 | package auto 2 | 3 | type UserCount struct { 4 | BaseModel 5 | UserId uint `json:"user_id,string" gorm:"uniqueIndex,not null"` 6 | User User `gorm:"ForeignKey:UserId"` 7 | FollowCount int64 `json:"follow_count,string" ` // 关注总数 8 | FollowerCount int64 `json:"follower_count,string" ` // 粉丝总数 9 | TotalFavorited int64 `json:"total_favorited,string" ` // 获赞数量 10 | WorkCount int64 `json:"work_count,string" ` // 作品数 11 | FavoriteCount int64 `json:"favorite_count,string" ` // 点赞总数 12 | } 13 | 14 | func (*UserCount) TableName() string { 15 | return "user_count" 16 | } 17 | 18 | const ( 19 | FollowCount = "follow_count" 20 | FollowerCount = "follower_count" 21 | TotalFavorited = "total_favorited" 22 | WorkCount = "work_count" 23 | FavoriteCount = "favorite_count" 24 | ) 25 | -------------------------------------------------------------------------------- /toktik-user/internal/model/constants.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | const ( 4 | MsgNil = "" 5 | ) 6 | 7 | const ( 8 | RpcSuccess = 0 9 | ) 10 | 11 | var TypeUserToken = "user_token::" 12 | 13 | const ( 14 | RpcUser = "rpc-user" 15 | RpcVideo = "rpc-video" 16 | RpcInteraction = "rpc-interaction" 17 | RpcChat = "rpc-chat" 18 | ) 19 | 20 | const ( 21 | FAVORITE = iota + 1 22 | CANCELFAVORITE 23 | ) 24 | const ( 25 | TokTikApi = "toktik-api" 26 | TokTikUser = "toktik-user" 27 | TokTikInteraction = "toktik-interaction" 28 | TokTikVideo = "toktik-video" 29 | TokTikChat = "toktik-chat" 30 | TokTikFavor = "toktik-favor" 31 | TokTikComment = "toktik-comment" 32 | ) 33 | -------------------------------------------------------------------------------- /toktik-user/internal/repo/cache.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "context" 5 | "time" 6 | "toktik-user/internal/model/auto" 7 | ) 8 | 9 | type RCacheRepo interface { 10 | SetToken(c context.Context, key, value string, expire time.Duration) error 11 | GetToken(c context.Context, key string) (string, error) 12 | HSetUserInfo(c context.Context, key string, value map[string]interface{}) error 13 | HSetUserCountInfo(c context.Context, key string, value map[string]interface{}) error 14 | HGetUserInfo(c context.Context, key string) (*auto.User, error) 15 | HGetUserCountInfo(c context.Context, key string) (*auto.UserCount, error) 16 | AddFollowCount(c context.Context, key string) error 17 | AddFollowerCount(c context.Context, key string) error 18 | SubFollowCount(c context.Context, key string) error 19 | SubFollowerCount(c context.Context, key string) error 20 | AddWorkCount(c context.Context, key string) error 21 | AddFavoriteCount(c context.Context, key string) error 22 | AddTotalFavoriteCount(c context.Context, key string) error 23 | SubFavoriteCount(c context.Context, key string) error 24 | SubTotalFavoriteCount(c context.Context, key string) error 25 | } 26 | -------------------------------------------------------------------------------- /toktik-user/internal/repo/handler_resp.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "toktik-common/errcode" 5 | "toktik-rpc/kitex_gen/user" 6 | "toktik-user/internal/model" 7 | ) 8 | 9 | type HandlerResp interface { 10 | RegisterResponse(err errcode.Err, msg string, resp *user.RegisterResponse) *user.RegisterResponse 11 | LoginResponse(err errcode.Err, msg string, resp *user.LoginResponse) *user.LoginResponse 12 | UserIndexResponse(err errcode.Err, msg string, resp *user.UserIndexResponse) *user.UserIndexResponse 13 | AddFollowCountResponse(err errcode.Err, msg string, resp *user.AddFollowCountResponse) *user.AddFollowCountResponse 14 | SubFollowCountResponse(err errcode.Err, msg string, resp *user.SubFollowCountResponse) *user.SubFollowCountResponse 15 | GetUserListResponse(err errcode.Err, msg string, resp *user.GetUserListResponse) *user.GetUserListResponse 16 | AddUserWorkCountResponse(err errcode.Err, msg string, resp *user.AddUserWorkCountResponse) *user.AddUserWorkCountResponse 17 | UpdateUserFavoriteCountResponse(err errcode.Err, msg string, resp *user.UpdateUserFavoriteCountResponse) *user.UpdateUserFavoriteCountResponse 18 | } 19 | 20 | type HandlerResps struct { 21 | model.RegisterHandler 22 | model.LoginHandler 23 | model.UserIndexHandler 24 | model.AddFollowCountHandler 25 | model.SubFollowCountHandler 26 | model.GutUserListHandler 27 | model.AddUserWorkCountHandler 28 | model.UpdateUserFavoriteCountHandler 29 | } 30 | 31 | func NewHandlerResps() *HandlerResps { 32 | return &HandlerResps{} 33 | } 34 | -------------------------------------------------------------------------------- /toktik-user/internal/repo/user.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "context" 5 | "toktik-user/internal/dao/mysql" 6 | "toktik-user/internal/model/auto" 7 | ) 8 | 9 | type UserRepo interface { 10 | GetUserByUsername(c context.Context, username string) (bool, error) 11 | SetUserInfo(c context.Context, conn mysql.DbConn, userInfo *auto.User) error 12 | SetUserCountInfo(c context.Context, conn mysql.DbConn, userCountInfo *auto.UserCount) error 13 | 14 | GetUserInfoByUsername(c context.Context, username string) (*auto.User, error) 15 | GetUserInfoByUserID(c context.Context, userID int64) (*auto.User, error) 16 | GetUserCountInfoByUserID(c context.Context, userID int64) (*auto.UserCount, error) 17 | AddFollowCount(c context.Context, conn mysql.DbConn, userID uint) error 18 | AddFollowerCount(c context.Context, conn mysql.DbConn, userID uint) error 19 | SubFollowCount(c context.Context, conn mysql.DbConn, userID uint) error 20 | SubFollowerCount(c context.Context, conn mysql.DbConn, userID uint) error 21 | GetUserList(c context.Context, userIDs []int64) ([]*auto.User, error) 22 | GetUserCountList(c context.Context, userIDs []int64) ([]*auto.UserCount, error) 23 | // IsFollowUser(c context.Context, myUserID, targetUserID int64) (bool, error) 24 | } 25 | -------------------------------------------------------------------------------- /toktik-user/internal/service/token.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | token "toktik-common/token" 5 | "toktik-user/internal/global" 6 | ) 7 | 8 | func CreateToken(userID int64) (string, string, error) { 9 | duration := global.Settings.Token.UserTokenExp 10 | data, err := token.NewTokenContent(token.UserToken, userID).Marshal() 11 | if err != nil { 12 | return "", "", err 13 | } 14 | tokenStr, _, err := global.Maker.CreateToken(data, duration) 15 | if err != nil { 16 | return "", "", err 17 | } 18 | return tokenStr, string(data), nil 19 | } 20 | -------------------------------------------------------------------------------- /toktik-user/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "toktik-user/internal/global" 6 | "toktik-user/pkg/rpc" 7 | "toktik-user/pkg/rpc/client" 8 | "toktik-user/pkg/setting" 9 | ) 10 | 11 | func main() { 12 | // 初始化 13 | setting.InitAllSetting() 14 | fmt.Printf("config:%#v\n", global.Settings) 15 | client.NewRpcClient() 16 | fmt.Println("-----User Service Start ! ! !-----") 17 | // RPC 注册 18 | rpc.RegisterRPC() 19 | 20 | } 21 | -------------------------------------------------------------------------------- /toktik-user/pkg/myerr/myerr.go: -------------------------------------------------------------------------------- 1 | package myerr 2 | 3 | import "toktik-common/errcode" 4 | 5 | var ( 6 | UsernameExist = errcode.NewErr(100001, "用户名已存在") 7 | CreateTokenErr = errcode.NewErr(100002, "生成Token失败") 8 | PasswordErr = errcode.NewErr(100003, "密码错误") 9 | UserNotFound = errcode.NewErr(100004, "用户名不存在!") 10 | IsFollowManyTargetsErr = errcode.NewErr(100005, "查询是否关注很多用户,失败!") 11 | ) 12 | -------------------------------------------------------------------------------- /toktik-user/pkg/rpc/client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | //func NewRpcClientRepo() *RpcClient { 4 | // return &RpcClient{InteractionClient,VideoClient} 5 | //} 6 | 7 | //type RpcClient struct { 8 | // interactionservice.Client 9 | // videoservice.Client 10 | //} 11 | 12 | func NewRpcClient() { 13 | InitRpcInteractionClient() 14 | } 15 | -------------------------------------------------------------------------------- /toktik-user/pkg/rpc/client/interaction.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/cloudwego/kitex/client" 5 | "github.com/kitex-contrib/obs-opentelemetry/provider" 6 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 7 | etcd "github.com/kitex-contrib/registry-etcd" 8 | "go.uber.org/zap" 9 | "toktik-rpc/kitex_gen/interaction/interactionservice" 10 | "toktik-user/internal/global" 11 | "toktik-user/internal/model" 12 | ) 13 | 14 | var InteractionClient interactionservice.Client 15 | 16 | func InitRpcInteractionClient() { 17 | r, err := etcd.NewEtcdResolver(global.Settings.Etcd.Addr) 18 | if err != nil { 19 | panic(err) 20 | } 21 | provider.NewOpenTelemetryProvider( 22 | provider.WithServiceName(global.Settings.Jaeger.ServerName[model.TokTikInteraction]), 23 | provider.WithExportEndpoint(global.Settings.Jaeger.RPCExportEndpoint), 24 | provider.WithInsecure(), 25 | ) 26 | 27 | c, err := interactionservice.NewClient( 28 | model.RpcInteraction, 29 | client.WithSuite(tracing.NewClientSuite()), 30 | //client.WithHostPorts(global.PbSettings.Rpc.ServerAddrs[model.RpcInteraction]), 31 | //client.WithMiddleware(rpcmiddleware.CommonMiddleware), 32 | //client.WithMiddleware(rpcmiddleware.ClientMiddleware), 33 | client.WithResolver(r), 34 | ) 35 | if err != nil { 36 | zap.L().Error("InitRpcInteractionClient err:", zap.Error(err)) 37 | panic(err) 38 | } 39 | InteractionClient = c 40 | } 41 | -------------------------------------------------------------------------------- /toktik-user/pkg/rpc/client/video.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/cloudwego/kitex/client" 5 | "github.com/kitex-contrib/obs-opentelemetry/provider" 6 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 7 | etcd "github.com/kitex-contrib/registry-etcd" 8 | "go.uber.org/zap" 9 | "toktik-rpc/kitex_gen/interaction/interactionservice" 10 | "toktik-rpc/kitex_gen/video/videoservice" 11 | "toktik-user/internal/global" 12 | "toktik-user/internal/model" 13 | ) 14 | 15 | var VideoClient videoservice.Client 16 | 17 | func InitRpcVideoClient() { 18 | r, err := etcd.NewEtcdResolver(global.Settings.Etcd.Addr) 19 | if err != nil { 20 | panic(err) 21 | } 22 | provider.NewOpenTelemetryProvider( 23 | provider.WithServiceName(global.Settings.Jaeger.ServerName[model.TokTikVideo]), 24 | provider.WithExportEndpoint(global.Settings.Jaeger.RPCExportEndpoint), 25 | provider.WithInsecure(), 26 | ) 27 | c, err := interactionservice.NewClient( 28 | model.RpcVideo, 29 | client.WithSuite(tracing.NewClientSuite()), 30 | //client.WithHostPorts(global.PbSettings.Rpc.ServerAddrs[model.RpcInteraction]), 31 | //client.WithMiddleware(rpcmiddleware.CommonMiddleware), 32 | //client.WithInstanceMW(rpcmiddleware.ClientMiddleware), 33 | client.WithResolver(r), 34 | ) 35 | if err != nil { 36 | zap.L().Error("InitRpcInteractionClient err:", zap.Error(err)) 37 | panic(err) 38 | } 39 | InteractionClient = c 40 | } 41 | -------------------------------------------------------------------------------- /toktik-user/pkg/setting/dao.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "toktik-user/internal/cache" 5 | "toktik-user/internal/dao" 6 | "toktik-user/internal/dao/cron" 7 | "toktik-user/internal/dao/mysql" 8 | ) 9 | 10 | func init() { 11 | d := Dao{} 12 | Settings = append(Settings, d) 13 | } 14 | 15 | type Dao struct { 16 | } 17 | 18 | func (Dao) InitSetting() { 19 | mysql.InitMysql() 20 | //global.RdbClient = redis.InitRedis() 21 | dao.Group.Rdb = cache.InitRedis() 22 | // 开启定时任务 23 | go cron.TimingJob() 24 | } 25 | -------------------------------------------------------------------------------- /toktik-user/pkg/setting/enter.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | var Settings []InitSettings 4 | 5 | type InitSettings interface { 6 | InitSetting() 7 | } 8 | 9 | func InitAllSetting() { 10 | InitBootStrap() 11 | for _, setting := range Settings { 12 | setting.InitSetting() 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /toktik-user/pkg/setting/logger.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "go.uber.org/zap" 5 | "toktik-common/logger" 6 | "toktik-user/internal/global" 7 | ) 8 | 9 | func init() { 10 | l := log{} 11 | Settings = append(Settings, l) 12 | } 13 | 14 | type log struct { 15 | } 16 | 17 | // InitSetting 日志初始化 18 | func (log) InitSetting() { 19 | global.Logger = logger.NewLogger(&logger.InitStruct{ 20 | LogSavePath: global.Settings.Logger.LogSavePath, 21 | LogFileExt: global.Settings.Logger.LogFileExt, 22 | MaxSize: global.Settings.Logger.MaxSize, 23 | MaxBackups: global.Settings.Logger.MaxBackups, 24 | MaxAge: global.Settings.Logger.MaxAge, 25 | Compress: global.Settings.Logger.Compress, 26 | LowLevelFile: global.Settings.Logger.LowLevelFile, 27 | HighLevelFile: global.Settings.Logger.HighLevelFile, 28 | }, global.Settings.Logger.Level) 29 | zap.ReplaceGlobals(global.Logger.Logger) 30 | } 31 | -------------------------------------------------------------------------------- /toktik-user/pkg/setting/maker.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "toktik-common/token" 5 | "toktik-user/internal/global" 6 | ) 7 | 8 | func init() { 9 | m := maker{} 10 | Settings = append(Settings, m) 11 | } 12 | 13 | type maker struct { 14 | } 15 | 16 | // InitSetting 初始化 17 | func (maker) InitSetting() { 18 | var err error 19 | global.Maker, err = token.NewPasetoMaker(global.Settings.Token.Key) 20 | if err != nil { 21 | panic(err) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /toktik-user/pkg/setting/nacos.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "github.com/nacos-group/nacos-sdk-go/v2/clients" 5 | "github.com/nacos-group/nacos-sdk-go/v2/clients/config_client" 6 | "github.com/nacos-group/nacos-sdk-go/v2/common/constant" 7 | "github.com/nacos-group/nacos-sdk-go/v2/vo" 8 | log2 "log" 9 | "toktik-user/internal/global" 10 | ) 11 | 12 | type NacosClient struct { 13 | confClient config_client.IConfigClient 14 | } 15 | 16 | func InitNacos() *NacosClient { 17 | clientConfig := constant.ClientConfig{ 18 | NamespaceId: global.Settings.Nacos.Namespace, //we can create multiple clients with different namespaceId to support multiple namespace.When namespace is public, fill in the blank string here. 19 | TimeoutMs: 5000, 20 | NotLoadCacheAtStart: true, 21 | LogDir: "/tmp/nacos/log", 22 | CacheDir: "/tmp/nacos/cache", 23 | LogLevel: "debug", 24 | } 25 | serverConfigs := []constant.ServerConfig{ 26 | { 27 | IpAddr: global.Settings.Nacos.Addr, 28 | ContextPath: global.Settings.Nacos.ContextPath, 29 | Port: uint64(global.Settings.Nacos.Port), 30 | Scheme: global.Settings.Nacos.Scheme, 31 | }, 32 | } 33 | configClient, err := clients.NewConfigClient( 34 | vo.NacosClientParam{ 35 | ClientConfig: &clientConfig, 36 | ServerConfigs: serverConfigs, 37 | }, 38 | ) 39 | if err != nil { 40 | log2.Fatalln(err) 41 | } 42 | nc := &NacosClient{ 43 | confClient: configClient, 44 | } 45 | return nc 46 | } 47 | -------------------------------------------------------------------------------- /toktik-user/pkg/setting/snowflake.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "toktik-common/utils" 5 | "toktik-user/internal/global" 6 | ) 7 | 8 | func init() { 9 | s := sf{} 10 | Settings = append(Settings, s) 11 | } 12 | 13 | type sf struct { 14 | } 15 | 16 | func (sf) InitSetting() { 17 | var err error 18 | global.SnowFlake, err = utils.NewSnowFlake(0, 0) 19 | if err != nil { 20 | panic(err) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /toktik-video/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine 2 | WORKDIR /app 3 | 4 | RUN apk update && apk add ffmpeg 5 | 6 | COPY ./bin/toktik-video . 7 | COPY ./config . 8 | RUN mkdir config && mv config.yaml config/config.yaml && mv bootstrap.yaml config/bootstrap.yaml 9 | 10 | RUN chmod +x ./toktik-video 11 | 12 | EXPOSE 8083 8883 9193 13 | ENTRYPOINT ["./toktik-video"] 14 | -------------------------------------------------------------------------------- /toktik-video/config/bootstrap.yaml.template: -------------------------------------------------------------------------------- 1 | Nacos: 2 | Namespace: "xxx" # 命名空间id 3 | Group: "api" # group名称 4 | Addr: "xxx.xxx.xxx.xxx" # 地址 5 | Port: 8848 6 | Scheme: "http" 7 | ContextPath: "/nacos" -------------------------------------------------------------------------------- /toktik-video/config/config.yaml.template: -------------------------------------------------------------------------------- 1 | Server: 2 | Name: "toktik-video" 3 | RunMode: "DEBUG" 4 | Addr: "0.0.0.0:8083" 5 | DefaultContextTimeout: 10s 6 | 7 | Rules: 8 | DefaultUserAvatar: "https://q1.qlogo.cn/g?b=qq&nk=1780006511&s=640" 9 | DefaultUserSignature: "这里什么有没有捏" 10 | VideoInfoCacheExpire: 720h 11 | FavoriteInfoCacheExpire: 168h 12 | 13 | Jaeger: 14 | RPCExportEndpoint: "xxx:4317" 15 | ServerName: 16 | toktik-api: "toktik-api" 17 | toktik-user: "toktik-user" 18 | toktik-video: "toktik-video" 19 | toktik-interaction: "toktik-interaction" 20 | toktik-chat: "toktik-chat" 21 | toktik-favor: "toktik-favor" 22 | toktik-comment: "toktik-comment" 23 | 24 | Prometheus: 25 | Post: ":9193" 26 | Path: "/metrics" 27 | 28 | RPC: 29 | Name: "rpc-video" 30 | Addr: "0.0.0.0:8883" 31 | 32 | Logger: 33 | Level: "debug" 34 | LogSavePath: "storage/applogs/" 35 | LogFileExt: ".log" 36 | MaxSize: 10 37 | MaxBackups: 7 38 | MaxAge: 30 39 | Compress: false 40 | LowLevelFile: "info" 41 | HighLevelFile: "error" 42 | 43 | Mysql: 44 | Username: "root" 45 | Password: "xxx" 46 | Host: "xxx" 47 | Port: "3309" 48 | DB: "toktik_video" 49 | 50 | Redis: 51 | Host: "xxx" 52 | Port: "6379" 53 | Password: "" 54 | DB: 3 55 | 56 | Etcd: 57 | Addr: 58 | - "xxx:2379" 59 | 60 | AliyunOSS: 61 | Endpoint: "xxx" 62 | AccessKeyId: "xxx" 63 | AccessKeySecret: "xxx" 64 | BucketName: "xxx" 65 | -------------------------------------------------------------------------------- /toktik-video/internal/dao/enter.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "github.com/go-redis/redis/v8" 5 | "gorm.io/gorm" 6 | ) 7 | 8 | type group struct { 9 | Mdb *gorm.DB 10 | Rdb *redis.Client 11 | } 12 | 13 | var Group = new(group) 14 | -------------------------------------------------------------------------------- /toktik-video/internal/dao/mysql/tran.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // Transaction 事务的操作 一定跟数据库有关 注入数据库的连接 gorm.db 4 | type Transaction interface { 5 | Action(func(conn DbConn) error) error 6 | } 7 | 8 | type DbConn interface { 9 | Begin() 10 | Rollback() 11 | Commit() 12 | } 13 | 14 | type TransactionImpl struct { 15 | conn DbConn 16 | } 17 | 18 | func (t TransactionImpl) Action(f func(conn DbConn) error) error { 19 | t.conn.Begin() 20 | err := f(t.conn) 21 | if err != nil { 22 | t.conn.Rollback() 23 | return err 24 | } 25 | t.conn.Commit() 26 | return nil 27 | } 28 | 29 | func NewTransaction() *TransactionImpl { 30 | return &TransactionImpl{ 31 | conn: NewTran(), 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /toktik-video/internal/global/root.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | "path/filepath" 7 | "sync" 8 | ) 9 | 10 | /* 11 | 用于推断当前项目根路径 12 | */ 13 | 14 | var ( 15 | RootDir string // 项目根路径 16 | once = new(sync.Once) 17 | ) 18 | 19 | func exist(filePath string) bool { 20 | _, err := os.Stat(filePath) 21 | return err == nil || errors.Is(err, os.ErrExist) 22 | } 23 | 24 | // 计算项目路径 25 | func inferRootDir() string { 26 | cwd, err := os.Getwd() 27 | if err != nil { 28 | panic(err) 29 | } 30 | var infer func(path string) string 31 | infer = func(path string) string { 32 | if exist(path + "/config") { 33 | return path 34 | } 35 | return infer(filepath.Dir(path)) 36 | } 37 | return infer(cwd) 38 | } 39 | 40 | func init() { 41 | once.Do(func() { 42 | RootDir = inferRootDir() 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /toktik-video/internal/global/var.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "toktik-common/logger" 5 | "toktik-common/oss" 6 | "toktik-common/utils" 7 | "toktik-video/internal/model/config" 8 | ) 9 | 10 | var ( 11 | Settings config.Config // Public配置 12 | Logger *logger.Log // 日志 13 | OSS oss.OSS 14 | SnowFlake *utils.SnowFlake 15 | ) 16 | -------------------------------------------------------------------------------- /toktik-video/internal/model/auto/base.go: -------------------------------------------------------------------------------- 1 | package auto 2 | 3 | import ( 4 | "gorm.io/gorm" 5 | "strconv" 6 | "time" 7 | ) 8 | 9 | type BaseModel struct { 10 | ID uint `gorm:"primarykey" json:"id,string"` 11 | CreatedAt time.Time `json:"created_at,string"` 12 | UpdatedAt time.Time `json:"updated_at,string"` 13 | DeletedAt gorm.DeletedAt `json:"deleted_at,string"` 14 | } 15 | 16 | func CreatePublishKey() string { 17 | return "video_publish" 18 | } 19 | 20 | func CreateUserVideoKey(userId uint) string { 21 | return "user_video::" + strconv.FormatInt(int64(userId), 10) 22 | } 23 | -------------------------------------------------------------------------------- /toktik-video/internal/model/constants.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | const ( 4 | MsgNil = "" 5 | ) 6 | 7 | type FileType string 8 | 9 | const ( 10 | RpcSuccess = 0 11 | ) 12 | 13 | const ( 14 | MP4 = ".mp4" 15 | JPG = ".jpg" 16 | ) 17 | 18 | const ( 19 | VIDEO = "video" 20 | COVER = "cover" 21 | ) 22 | 23 | const ( 24 | RpcUser = "rpc-user" 25 | RpcVideo = "rpc-video" 26 | RpcInteraction = "rpc-interaction" 27 | RpcChat = "rpc-chat" 28 | RpcFavor = "rpc-favor" 29 | RpcComment = "rpc-comment" 30 | ) 31 | 32 | const ( 33 | TokTikApi = "toktik-api" 34 | TokTikUser = "toktik-user" 35 | TokTikInteraction = "toktik-interaction" 36 | TokTikVideo = "toktik-video" 37 | TokTikChat = "toktik-chat" 38 | TokTikFavor = "toktik-favor" 39 | TokTikComment = "toktik-comment" 40 | ) 41 | -------------------------------------------------------------------------------- /toktik-video/internal/repo/cache.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "context" 5 | "toktik-video/internal/model/auto" 6 | ) 7 | 8 | type RCacheRepo interface { 9 | GetKeys(c context.Context, keyPattern string) ([]string, error) 10 | KeyExist(c context.Context, key string) (bool, error) 11 | HSetVideoInfo(c context.Context, key string, value map[string]interface{}) error 12 | AddVideoFavoriteCount(c context.Context, key string) error 13 | SubVideoFavoriteCount(c context.Context, key string) error 14 | HGetVideoInfo(c context.Context, key string) (*auto.Video, error) 15 | PublishVideo(c context.Context, key string, time float64, videoId int64) error 16 | ZGetVideoIds(c context.Context, key string, latestTime int64) (int64, []int64, error) 17 | AddVideoCommentCount(c context.Context, key string) error 18 | SubVideoCommentCount(c context.Context, key string) error 19 | SDelUserVideo(c context.Context, key string) error 20 | SGetUserVideoIds(c context.Context, key string) ([]int64, error) 21 | SAddUserVideoIds(c context.Context, key string, videoId []int64) error 22 | } 23 | -------------------------------------------------------------------------------- /toktik-video/internal/repo/handler_resp.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "toktik-common/errcode" 5 | "toktik-rpc/kitex_gen/video" 6 | "toktik-video/internal/model" 7 | ) 8 | 9 | type HandlerResp interface { 10 | VideoFeedResponse(err errcode.Err, msg string, resp *video.VideoFeedResponse) *video.VideoFeedResponse 11 | VideoPublishResponse(err errcode.Err, msg string, resp *video.VideoPublishResponse) *video.VideoPublishResponse 12 | PublishListResponse(err errcode.Err, msg string, resp *video.PublishListResponse) *video.PublishListResponse 13 | GetVideoInfoResponse(err errcode.Err, msg string, resp *video.GetVideoInfoResponse) *video.GetVideoInfoResponse 14 | GetManyVideoInfosResponse(err errcode.Err, msg string, resp *video.GetManyVideoInfosResponse) *video.GetManyVideoInfosResponse 15 | AddVideoFavoriteCountResponse(err errcode.Err, msg string, resp *video.AddVideoFavoriteCountResponse) *video.AddVideoFavoriteCountResponse 16 | SubVideoFavoriteCountResponse(err errcode.Err, msg string, resp *video.SubVideoFavoriteCountResponse) *video.SubVideoFavoriteCountResponse 17 | AddVideoCommentCountResponse(err errcode.Err, msg string, resp *video.AddVideoCommentCountResponse) *video.AddVideoCommentCountResponse 18 | SubVideoCommentCountResponse(err errcode.Err, msg string, resp *video.SubVideoCommentCountResponse) *video.SubVideoCommentCountResponse 19 | } 20 | 21 | type HandlerResps struct { 22 | model.VideoFeedHandler 23 | model.VideoPublishHandler 24 | model.PublishListHandler 25 | model.GetVideoInfoHandler 26 | model.GetManyVideoInfosHandler 27 | model.AddVideoFavoriteCountHandler 28 | model.SubVideoFavoriteCountHandler 29 | model.AddVideoCommentCountHandler 30 | model.SubVideoCommentCountHandler 31 | } 32 | 33 | func NewHandlerResps() *HandlerResps { 34 | return &HandlerResps{} 35 | } 36 | -------------------------------------------------------------------------------- /toktik-video/internal/repo/video.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "context" 5 | "toktik-video/internal/dao/mysql" 6 | "toktik-video/internal/model/auto" 7 | ) 8 | 9 | type VideoRepo interface { 10 | CreateVideo(c context.Context, conn mysql.DbConn, videoInfo *auto.Video) error 11 | GetVideoInfoByVideoId(c context.Context, videoId int64) (*auto.Video, error) 12 | GetVideosByTime(c context.Context, LatestTime int64) ([]*auto.Video, error) 13 | GetVideosByUserId(c context.Context, userId int64) ([]*auto.Video, error) 14 | GetVideoIdsByUserId(c context.Context, userId int64) ([]int64, error) 15 | IsVideoExist(c context.Context, videoId int64) (bool, error) 16 | } 17 | -------------------------------------------------------------------------------- /toktik-video/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "toktik-video/internal/global" 6 | "toktik-video/pkg/rpc" 7 | "toktik-video/pkg/rpc/client" 8 | "toktik-video/pkg/setting" 9 | ) 10 | 11 | func main() { 12 | // 初始化 13 | setting.InitAllSetting() 14 | fmt.Printf("config:%#v\n", global.Settings) 15 | 16 | // RPC 客户端注册 17 | client.NewRpcClient() 18 | fmt.Println("-----Video Service Start ! ! !-----") 19 | // RPC 服务注册 20 | rpc.RegisterRPC() 21 | } 22 | -------------------------------------------------------------------------------- /toktik-video/pkg/myerr/myerr.go: -------------------------------------------------------------------------------- 1 | package myerr 2 | 3 | import "toktik-common/errcode" 4 | 5 | var ( 6 | UploadVideoErr = errcode.NewErr(300001, "上传视频失败") 7 | GetCoverErr = errcode.NewErr(300002, "获取视频封面失败") 8 | UploadCoverErr = errcode.NewErr(300003, "上传视频封面失败") 9 | SubmitVideoErr = errcode.NewErr(300004, "投稿视频失败") 10 | 11 | VideoNotExist = errcode.NewErr(300007, "该视频不存在") 12 | CanNotSearchVideo = errcode.NewErr(300008, "not search shipin") 13 | OperationErr = errcode.NewErr(300009, "错误的操作") 14 | ) 15 | -------------------------------------------------------------------------------- /toktik-video/pkg/rpc/client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | func NewRpcClient() { 4 | InitRpcUserCache() 5 | InitRpcInteractionClient() 6 | InitRpcFavorCache() 7 | } 8 | -------------------------------------------------------------------------------- /toktik-video/pkg/rpc/client/favor.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/cloudwego/kitex/client" 5 | "github.com/kitex-contrib/obs-opentelemetry/provider" 6 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 7 | etcd "github.com/kitex-contrib/registry-etcd" 8 | "go.uber.org/zap" 9 | "toktik-rpc/kitex_gen/favor/favorservice" 10 | "toktik-video/internal/global" 11 | "toktik-video/internal/model" 12 | ) 13 | 14 | var FavorClient favorservice.Client 15 | 16 | func InitRpcFavorCache() { 17 | r, err := etcd.NewEtcdResolver(global.Settings.Etcd.Addr) 18 | if err != nil { 19 | panic(err) 20 | } 21 | provider.NewOpenTelemetryProvider( 22 | provider.WithServiceName(global.Settings.Jaeger.ServerName[model.TokTikFavor]), 23 | provider.WithExportEndpoint(global.Settings.Jaeger.RPCExportEndpoint), 24 | provider.WithInsecure(), 25 | ) 26 | c, err := favorservice.NewClient( 27 | model.RpcFavor, 28 | client.WithSuite(tracing.NewClientSuite()), 29 | //client.WithHostPorts(global.Settings.Rpc.ServerAddrs[model.RpcUser]), 30 | //client.WithMiddleware(rpcmiddleware.CommonMiddleware), 31 | //client.WithInstanceMW(rpcmiddleware.ClientMiddleware), 32 | client.WithResolver(r), 33 | ) 34 | if err != nil { 35 | zap.L().Error("InitRpcFavorCache err:", zap.Error(err)) 36 | panic(err) 37 | } 38 | FavorClient = c 39 | } 40 | -------------------------------------------------------------------------------- /toktik-video/pkg/rpc/client/interaction.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/cloudwego/kitex/client" 5 | "github.com/kitex-contrib/obs-opentelemetry/provider" 6 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 7 | etcd "github.com/kitex-contrib/registry-etcd" 8 | "go.uber.org/zap" 9 | "toktik-rpc/kitex_gen/interaction/interactionservice" 10 | "toktik-video/internal/global" 11 | "toktik-video/internal/model" 12 | ) 13 | 14 | var InteractionClient interactionservice.Client 15 | 16 | func InitRpcInteractionClient() { 17 | r, err := etcd.NewEtcdResolver(global.Settings.Etcd.Addr) 18 | if err != nil { 19 | panic(err) 20 | } 21 | provider.NewOpenTelemetryProvider( 22 | provider.WithServiceName(global.Settings.Jaeger.ServerName[model.TokTikInteraction]), 23 | provider.WithExportEndpoint(global.Settings.Jaeger.RPCExportEndpoint), 24 | provider.WithInsecure(), 25 | ) 26 | c, err := interactionservice.NewClient( 27 | model.RpcInteraction, 28 | client.WithSuite(tracing.NewClientSuite()), 29 | //client.WithHostPorts(global.Settings.Rpc.ServerAddrs[model.RpcUser]), 30 | //client.WithMiddleware(rpcmiddleware.CommonMiddleware), 31 | //client.WithInstanceMW(rpcmiddleware.ClientMiddleware), 32 | client.WithResolver(r), 33 | ) 34 | if err != nil { 35 | zap.L().Error("InitRpcInteractionClient err:", zap.Error(err)) 36 | panic(err) 37 | } 38 | InteractionClient = c 39 | } 40 | -------------------------------------------------------------------------------- /toktik-video/pkg/rpc/client/user.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/cloudwego/kitex/client" 5 | "github.com/kitex-contrib/obs-opentelemetry/provider" 6 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 7 | etcd "github.com/kitex-contrib/registry-etcd" 8 | "go.uber.org/zap" 9 | "toktik-rpc/kitex_gen/user/userservice" 10 | "toktik-video/internal/global" 11 | "toktik-video/internal/model" 12 | ) 13 | 14 | var UserClient userservice.Client 15 | 16 | func InitRpcUserCache() { 17 | r, err := etcd.NewEtcdResolver(global.Settings.Etcd.Addr) 18 | if err != nil { 19 | panic(err) 20 | } 21 | provider.NewOpenTelemetryProvider( 22 | provider.WithServiceName(global.Settings.Jaeger.ServerName[model.TokTikUser]), 23 | provider.WithExportEndpoint(global.Settings.Jaeger.RPCExportEndpoint), 24 | provider.WithInsecure(), 25 | ) 26 | c, err := userservice.NewClient( 27 | model.RpcUser, 28 | client.WithSuite(tracing.NewClientSuite()), 29 | //client.WithHostPorts(global.Settings.Rpc.ServerAddrs[model.RpcUser]), 30 | //client.WithMiddleware(rpcmiddleware.CommonMiddleware), 31 | //client.WithInstanceMW(rpcmiddleware.ClientMiddleware), 32 | client.WithResolver(r), 33 | ) 34 | if err != nil { 35 | zap.L().Error("InitRpcUserCache err:", zap.Error(err)) 36 | panic(err) 37 | } 38 | UserClient = c 39 | } 40 | -------------------------------------------------------------------------------- /toktik-video/pkg/setting/dao.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "toktik-video/internal/cache" 5 | "toktik-video/internal/dao" 6 | "toktik-video/internal/dao/cron" 7 | "toktik-video/internal/dao/mysql" 8 | ) 9 | 10 | func init() { 11 | d := Dao{} 12 | Settings = append(Settings, d) 13 | } 14 | 15 | type Dao struct { 16 | } 17 | 18 | func (Dao) InitSetting() { 19 | mysql.InitMysql() 20 | dao.Group.Rdb = cache.InitRedis() 21 | go cron.TimingJob() 22 | } 23 | -------------------------------------------------------------------------------- /toktik-video/pkg/setting/enter.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | var Settings []InitSettings 4 | 5 | type InitSettings interface { 6 | InitSetting() 7 | } 8 | 9 | func InitAllSetting() { 10 | InitBootStrap() 11 | for _, setting := range Settings { 12 | setting.InitSetting() 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /toktik-video/pkg/setting/logger.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "go.uber.org/zap" 5 | "toktik-common/logger" 6 | "toktik-video/internal/global" 7 | ) 8 | 9 | func init() { 10 | l := log{} 11 | Settings = append(Settings, l) 12 | } 13 | 14 | type log struct { 15 | } 16 | 17 | // InitSetting 日志初始化 18 | func (log) InitSetting() { 19 | global.Logger = logger.NewLogger(&logger.InitStruct{ 20 | LogSavePath: global.Settings.Logger.LogSavePath, 21 | LogFileExt: global.Settings.Logger.LogFileExt, 22 | MaxSize: global.Settings.Logger.MaxSize, 23 | MaxBackups: global.Settings.Logger.MaxBackups, 24 | MaxAge: global.Settings.Logger.MaxAge, 25 | Compress: global.Settings.Logger.Compress, 26 | LowLevelFile: global.Settings.Logger.LowLevelFile, 27 | HighLevelFile: global.Settings.Logger.HighLevelFile, 28 | }, global.Settings.Logger.Level) 29 | zap.ReplaceGlobals(global.Logger.Logger) 30 | } 31 | -------------------------------------------------------------------------------- /toktik-video/pkg/setting/nacos.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "github.com/nacos-group/nacos-sdk-go/v2/clients" 5 | "github.com/nacos-group/nacos-sdk-go/v2/clients/config_client" 6 | "github.com/nacos-group/nacos-sdk-go/v2/common/constant" 7 | "github.com/nacos-group/nacos-sdk-go/v2/vo" 8 | log2 "log" 9 | "toktik-video/internal/global" 10 | ) 11 | 12 | type NacosClient struct { 13 | confClient config_client.IConfigClient 14 | } 15 | 16 | func InitNacos() *NacosClient { 17 | clientConfig := constant.ClientConfig{ 18 | NamespaceId: global.Settings.Nacos.Namespace, //we can create multiple clients with different namespaceId to support multiple namespace.When namespace is public, fill in the blank string here. 19 | TimeoutMs: 5000, 20 | NotLoadCacheAtStart: true, 21 | LogDir: "/tmp/nacos/log", 22 | CacheDir: "/tmp/nacos/cache", 23 | LogLevel: "debug", 24 | } 25 | serverConfigs := []constant.ServerConfig{ 26 | { 27 | IpAddr: global.Settings.Nacos.Addr, 28 | ContextPath: global.Settings.Nacos.ContextPath, 29 | Port: uint64(global.Settings.Nacos.Port), 30 | Scheme: global.Settings.Nacos.Scheme, 31 | }, 32 | } 33 | configClient, err := clients.NewConfigClient( 34 | vo.NacosClientParam{ 35 | ClientConfig: &clientConfig, 36 | ServerConfigs: serverConfigs, 37 | }, 38 | ) 39 | if err != nil { 40 | log2.Fatalln(err) 41 | } 42 | nc := &NacosClient{ 43 | confClient: configClient, 44 | } 45 | return nc 46 | } 47 | -------------------------------------------------------------------------------- /toktik-video/pkg/setting/oss.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "toktik-common/oss/aliyun" 5 | "toktik-video/internal/global" 6 | ) 7 | 8 | func init() { 9 | o := oss{} 10 | Settings = append(Settings, o) 11 | } 12 | 13 | type oss struct { 14 | } 15 | 16 | func (oss) InitSetting() { 17 | global.OSS = aliyun.Init(aliyun.Config{ 18 | Endpoint: global.Settings.AliyunOSS.Endpoint, 19 | AccessKeyId: global.Settings.AliyunOSS.AccessKeyId, 20 | AccessKeySecret: global.Settings.AliyunOSS.AccessKeySecret, 21 | BucketName: global.Settings.AliyunOSS.BucketName, 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /toktik-video/pkg/setting/snowflake.go: -------------------------------------------------------------------------------- 1 | package setting 2 | 3 | import ( 4 | "toktik-common/utils" 5 | "toktik-video/internal/global" 6 | ) 7 | 8 | func init() { 9 | s := sf{} 10 | Settings = append(Settings, s) 11 | } 12 | 13 | type sf struct { 14 | } 15 | 16 | func (sf) InitSetting() { 17 | var err error 18 | global.SnowFlake, err = utils.NewSnowFlake(0, 0) 19 | if err != nil { 20 | panic(err) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /toktik-video/storage/videos/兰亭序.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Happy-why/toktik/1cd3d85a6af72dca83e47994b196d6b40836668b/toktik-video/storage/videos/兰亭序.mp4 -------------------------------------------------------------------------------- /toktik-video/storage/videos/可爱女人.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Happy-why/toktik/1cd3d85a6af72dca83e47994b196d6b40836668b/toktik-video/storage/videos/可爱女人.mp4 -------------------------------------------------------------------------------- /toktik-video/storage/videos/星狗.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Happy-why/toktik/1cd3d85a6af72dca83e47994b196d6b40836668b/toktik-video/storage/videos/星狗.mp4 -------------------------------------------------------------------------------- /toktik-video/storage/videos/胡歌.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Happy-why/toktik/1cd3d85a6af72dca83e47994b196d6b40836668b/toktik-video/storage/videos/胡歌.mp4 --------------------------------------------------------------------------------