├── .idea
├── .gitignore
├── modules.xml
├── tiktok.iml
└── workspace.xml
├── .vscode
└── launch.json
├── go.mod
├── go.sum
├── go
├── config
│ ├── DataSource.go
│ ├── SecretKeyForJwt.go
│ ├── datasource_test.go
│ ├── parameter.go
│ ├── readme.md
│ ├── redis.go
│ ├── redis_test.go
│ └── tencent_cos.go
├── controller
│ ├── commentController.go
│ ├── followController.go
│ ├── likeController.go
│ ├── messageController.go
│ ├── readme.md
│ ├── userController.go
│ └── videoController.go
├── main.go
├── middle
│ ├── base.go
│ └── jwt
│ │ ├── Verify.go
│ │ ├── jwt_test.go
│ │ └── token.go
├── model
│ ├── InitDb.go
│ ├── base.go
│ ├── comment.go
│ ├── comment_test.go
│ ├── follow.go
│ ├── follow_test.go
│ ├── like.go
│ ├── like_test.go
│ ├── message.go
│ ├── message_test.go
│ ├── readme.md
│ ├── user.go
│ ├── video.go
│ └── video_test.go
├── route
│ └── load.go
├── service
│ ├── baseService.go
│ ├── commentService.go
│ ├── followService.go
│ ├── likeService.go
│ ├── messageService.go
│ ├── readme.md
│ ├── userService.go
│ └── videoService.go
└── util
│ ├── checkFile.go
│ ├── checkFile_test.go
│ ├── filter.go
│ ├── filter_test.go
│ ├── key.txt
│ ├── log.go
│ └── log_test.go
├── readme.md
└── resources
├── initial.sql
└── insertData.sql
/.idea/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WenTesla/tiktok/140f0d6625698620d47a6d6badced4e425377a14/.idea/.gitignore
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/tiktok.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
21 |
22 |
23 |
24 |
25 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | {
47 | "keyToString": {
48 | "DefaultGoTemplateProperty": "Go File",
49 | "RunOnceActivity.OpenProjectViewOnStart": "true",
50 | "RunOnceActivity.ShowReadmeOnStart": "true",
51 | "RunOnceActivity.go.formatter.settings.were.checked": "true",
52 | "RunOnceActivity.go.migrated.go.modules.settings": "true",
53 | "RunOnceActivity.go.modules.go.list.on.any.changes.was.set": "true",
54 | "WebServerToolWindowFactoryState": "false",
55 | "configurable..is.expanded": "false",
56 | "configurable.GoLibrariesConfigurable.is.expanded": "true",
57 | "go.import.settings.migrated": "true",
58 | "last_opened_file_path": "H:/redis_course",
59 | "node.js.detected.package.eslint": "true",
60 | "node.js.selected.package.eslint": "(autodetect)",
61 | "nodejs_package_manager_path": "npm",
62 | "settings.editor.selected.configurable": "preferences.intentionPowerPack"
63 | },
64 | "keyToStringList": {
65 | "DatabaseDriversLRU": [
66 | "mysql",
67 | "redis"
68 | ],
69 | "RunConfigurationTargetLRU": [
70 | "f8ed14ed-78ef-4118-87b7-1ecf6c80c7b5"
71 | ],
72 | "com.intellij.ide.scratch.ScratchImplUtil$2/新建临时文件": [
73 | "go"
74 | ]
75 | }
76 | }
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 | true
217 |
218 |
219 |
220 |
221 |
222 | file://$PROJECT_DIR$/go/util/filter.go
223 | 61
224 |
225 |
226 |
227 | file://$PROJECT_DIR$/go/model/follow.go
228 | 92
229 |
230 |
231 |
232 | file://$PROJECT_DIR$/go/util/checkFile_test.go
233 | 16
234 |
235 |
236 |
237 | file://$PROJECT_DIR$/go/util/checkFile.go
238 | 95
239 |
240 |
241 |
242 | file://$PROJECT_DIR$/go/middle/jwt/jwt_test.go
243 | 33
244 |
245 |
246 |
247 | file://$APPLICATION_CONFIG_DIR$/scratches/scratch_1.go
248 | 8
249 |
250 |
251 |
252 | file://$PROJECT_DIR$/go/controller/videoController.go
253 | 63
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // 使用 IntelliSense 了解相关属性。
3 | // 悬停以查看现有属性的描述。
4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Launch Package",
9 | "type": "go",
10 | "request": "launch",
11 | "mode": "auto",
12 | "program": "${fileDirname}"
13 | },
14 | {
15 | "name": "Attach to Process",
16 | "type": "go",
17 | "request": "attach",
18 | "mode": "local",
19 | "processId": 0
20 | },
21 | {
22 | "name": "Launch Chrome",
23 | "request": "launch",
24 | "type": "chrome",
25 | "url": "http://localhost:8080",
26 | "webRoot": "${workspaceFolder}"
27 | },
28 | {
29 | "name": "Launch Package",
30 | "type": "go",
31 | "request": "launch",
32 | "mode": "auto",
33 | "program": "${fileDirname}"
34 | }
35 | ]
36 | }
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module tiktok
2 |
3 | go 1.19
4 |
5 | // 引入gin框架
6 | require (
7 | github.com/gin-contrib/sse v0.1.0 // indirect
8 | github.com/gin-gonic/gin v1.8.2
9 | github.com/go-playground/locales v0.14.1 // indirect
10 | github.com/go-playground/universal-translator v0.18.0 // indirect
11 | github.com/go-playground/validator/v10 v10.11.1 // indirect
12 | github.com/goccy/go-json v0.10.0 // indirect
13 | github.com/json-iterator/go v1.1.12 // indirect
14 | github.com/leodido/go-urn v1.2.1 // indirect
15 | github.com/mattn/go-isatty v0.0.17 // indirect
16 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
17 | github.com/modern-go/reflect2 v1.0.2 // indirect
18 | github.com/pelletier/go-toml/v2 v2.0.6 // indirect
19 | github.com/ugorji/go/codec v1.2.8 // indirect
20 | golang.org/x/crypto v0.5.0 // indirect
21 | golang.org/x/net v0.5.0 // indirect
22 | golang.org/x/sys v0.4.0 // indirect
23 | golang.org/x/text v0.6.0 // indirect
24 | google.golang.org/protobuf v1.28.1 // indirect
25 | gopkg.in/yaml.v2 v2.4.0 // indirect
26 | )
27 |
28 | // 引入数据库连接驱动和gorm
29 | require (
30 | gorm.io/driver/mysql v1.4.5
31 | gorm.io/gorm v1.24.3
32 | )
33 |
34 | // 引入
35 | require (
36 | github.com/go-sql-driver/mysql v1.7.0 // indirect
37 | github.com/jinzhu/inflection v1.0.0 // indirect
38 | github.com/jinzhu/now v1.1.5 // indirect
39 | )
40 |
41 | // 引入go的jwt
42 | require github.com/golang-jwt/jwt/v4 v4.4.3
43 |
44 | // 引入腾讯云的依赖
45 | require (
46 | github.com/clbanning/mxj v1.8.4 // indirect
47 | github.com/google/go-querystring v1.1.0 // indirect
48 | github.com/mitchellh/mapstructure v1.5.0 // indirect
49 | github.com/mozillazg/go-httpheader v0.3.1 // indirect
50 | github.com/tencentyun/cos-go-sdk-v5 v0.7.41 // indirect
51 | )
52 |
53 | // 引入redis
54 | require github.com/go-redis/redis v6.15.9+incompatible // indirect
55 |
56 | require golang.org/x/time v0.3.0 // indirect
57 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
2 | github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I=
3 | github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
4 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
6 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
7 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
8 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
9 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
10 | github.com/gin-gonic/gin v1.8.2 h1:UzKToD9/PoFj/V4rvlKqTRKnQYyz8Sc1MJlv4JHPtvY=
11 | github.com/gin-gonic/gin v1.8.2/go.mod h1:qw5AYuDrzRTnhvusDsrov+fDIxp9Dleuu12h8nfB398=
12 | github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
13 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
14 | github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
15 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
16 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
17 | github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
18 | github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
19 | github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
20 | github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
21 | github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
22 | github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
23 | github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
24 | github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
25 | github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA=
26 | github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
27 | github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU=
28 | github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
29 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
30 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
31 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
32 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
33 | github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
34 | github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
35 | github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
36 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
37 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
38 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
39 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
40 | github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
41 | github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
42 | github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
43 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
44 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
45 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
46 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
47 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
48 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
49 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
50 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
51 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
52 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
53 | github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
54 | github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
55 | github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
56 | github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
57 | github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
58 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
59 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
60 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
61 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
62 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
63 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
64 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
65 | github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60=
66 | github.com/mozillazg/go-httpheader v0.3.1 h1:IRP+HFrMX2SlwY9riuio7raffXUpzAosHtZu25BSJok=
67 | github.com/mozillazg/go-httpheader v0.3.1/go.mod h1:PuT8h0pw6efvp8ZeUec1Rs7dwjK08bt6gKSReGMqtdA=
68 | github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
69 | github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
70 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
71 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
72 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
73 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
74 | github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
75 | github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
76 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
77 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
78 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
79 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
80 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
81 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
82 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
83 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
84 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
85 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
86 | github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.194/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
87 | github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.194/go.mod h1:yrBKWhChnDqNz1xuXdSbWXG56XawEq0G5j1lg4VwBD4=
88 | github.com/tencentyun/cos-go-sdk-v5 v0.7.41 h1:iU0Li/Np78H4SBna0ECQoF3mpgi6ImLXU+doGzPFXGc=
89 | github.com/tencentyun/cos-go-sdk-v5 v0.7.41/go.mod h1:4dCEtLHGh8QPxHEkgq+nFaky7yZxQuYwgSJM87icDaw=
90 | github.com/ugorji/go/codec v1.2.8 h1:sgBJS6COt0b/P40VouWKdseidkDgHxYGm0SAglUHfP0=
91 | github.com/ugorji/go/codec v1.2.8/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
92 | golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
93 | golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
94 | golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
95 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
96 | golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
97 | golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
98 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
99 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
100 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
101 | golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
102 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
103 | golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
104 | golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
105 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
106 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
107 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
108 | golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
109 | golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
110 | golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
111 | golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
112 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
113 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
114 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
115 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
116 | google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
117 | google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
118 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
119 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
120 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
121 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
122 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
123 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
124 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
125 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
126 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
127 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
128 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
129 | gorm.io/driver/mysql v1.4.5 h1:u1lytId4+o9dDaNcPCFzNv7h6wvmc92UjNk3z8enSBU=
130 | gorm.io/driver/mysql v1.4.5/go.mod h1:SxzItlnT1cb6e1e4ZRpgJN2VYtcqJgqnHxWr4wsP8oc=
131 | gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
132 | gorm.io/gorm v1.24.3 h1:WL2ifUmzR/SLp85CSURAfybcHnGZ+yLSGSxgYXlFBHg=
133 | gorm.io/gorm v1.24.3/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
134 |
--------------------------------------------------------------------------------
/go/config/DataSource.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "gorm.io/driver/mysql"
5 | "gorm.io/gorm"
6 | "tiktok/go/util"
7 | )
8 |
9 | //var Db *gorm.DB
10 |
11 | // 初始化并返回数据链接
12 | func InitDataSource() *gorm.DB {
13 | // 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
14 | // 本地数据库-推荐-自己建表 -自己替换
15 | dsn := "root:zhang134679@tcp(127.0.0.1:3306)/tiktok?charset=utf8mb4&parseTime=True&loc=Local"
16 | // 阿里云数据库 非常慢
17 | //dsn := "tiktok:tiktok@tcp(rm-2ze62585lf96k7285mo.mysql.rds.aliyuncs.com)/tiktok?charset=utf8mb4&parseTime=True&loc=Local"
18 | Db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
19 | PrepareStmt: true, //缓存预编译语句
20 | })
21 | if err != nil {
22 | util.LogFatal(err.Error())
23 | panic(err)
24 | return nil
25 | }
26 | println("连接成功")
27 | return Db
28 | }
29 |
--------------------------------------------------------------------------------
/go/config/SecretKeyForJwt.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | const Secret = "this_is_a_secret_key"
4 |
--------------------------------------------------------------------------------
/go/config/datasource_test.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import "testing"
4 |
5 | func TestInitDataSource(t *testing.T) {
6 | InitDataSource()
7 | }
8 |
--------------------------------------------------------------------------------
/go/config/parameter.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | // 假数据
4 |
5 | const MockAvatarUrl = "https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/12640/20230206133334.png"
6 |
7 | const VideoMinCount = 5
8 |
9 | const VideoCount = 10
10 |
11 | const VideoMaxCount = 30
12 |
13 | const CommentCount = 20
14 |
15 | const MessageCount = 10
16 |
17 | const TokenIsNotExist = "Token不存在-用户未登录"
18 |
19 | const TokenIsNotMatchUserId = "Token与用户不匹配"
20 |
21 | const TokenIsExpire = "Token过期"
22 |
23 | const TokenParseErr = "Token解析错误"
24 |
25 | const Success = "成功!"
26 |
27 | const Fail = "失败"
28 |
29 | const RequestFail = "请求参数错误"
30 |
31 | const RequestTooFast = "请求过于频繁"
32 |
33 | const RequestParameterIsNull = "请求参数为空"
34 |
35 | const DatabaseError = "内部数据库错误"
36 |
37 | const SuccessWithEnglish = "Success"
38 |
39 | const FailWithEnglish = "Fail"
40 |
41 | const RequestError = "请求错误"
42 |
--------------------------------------------------------------------------------
/go/config/readme.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WenTesla/tiktok/140f0d6625698620d47a6d6badced4e425377a14/go/config/readme.md
--------------------------------------------------------------------------------
/go/config/redis.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "github.com/go-redis/redis"
5 | "tiktok/go/util"
6 | )
7 |
8 | // 声明一个全局的redisDb变量
9 | //var redisDb *redis.Client
10 |
11 | // 根据redis配置初始化一个客户端
12 |
13 | func InitRedisClient() (redisDb *redis.Client, err error) {
14 | // 替换你的账号密码
15 | redisDb = redis.NewClient(&redis.Options{
16 | Addr: "43.138.126.75:6388", // redis地址
17 | Password: "redis", // redis密码,没有则留空
18 | DB: 0, // 默认数据库,默认是0
19 | })
20 |
21 | //通过 *redis.Client.Ping() 来检查是否成功连接到了redis服务器
22 | _, err = redisDb.Ping().Result()
23 | if err != nil {
24 | util.LogFatal(err.Error())
25 | panic(err)
26 | return nil, err
27 | }
28 | util.Log("redis 初始化成功")
29 | return redisDb, nil
30 | }
31 |
32 | // 选择redis的数据库 -> i
33 |
34 | func RedisClient(i int) (*redis.Client, error) {
35 | redisDb := redis.NewClient(&redis.Options{
36 | Addr: "43.138.126.75:6388", // redis地址
37 | Password: "redis", // redis密码,没有则留空
38 | DB: i, // 默认数据库,默认是0
39 | })
40 | //通过 *redis.Client.Ping() 来检查是否成功连接到了redis服务器
41 | _, err := redisDb.Ping().Result()
42 | if err != nil {
43 | util.LogFatal(err.Error())
44 | panic(err)
45 | return nil, err
46 | }
47 | return redisDb, nil
48 | }
49 |
--------------------------------------------------------------------------------
/go/config/redis_test.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "testing"
7 | "time"
8 | )
9 |
10 | type Comment struct {
11 | ID int64 // 评论id
12 | UserId int64 // 用户Id
13 | VideoId int64 //视频Id
14 | Text string // 评论内容
15 | IsCancel int64 // 是否取消
16 | CreateTime time.Time `gorm:"column:createTime"` // 创建时间
17 | }
18 |
19 | // CommentInfo 评论详细信息
20 | type CommentInfo struct {
21 | Content string `json:"content"` // 评论内容
22 | CreateDate string `json:"create_date"` // 评论发布日期,格式 mm-dd
23 | ID int64 `json:"id"` // 评论id
24 | UserInfo UserInfo `json:"user"` // 评论用户的具体信息
25 | }
26 |
27 | // UserInfo 最详细的信息 不与数据库模型对应
28 | type UserInfo struct {
29 | Id int64 `json:"id,omitempty"` //主键
30 | Name string `json:"name,omitempty"` //昵称
31 | FollowCount int64 `json:"follow_count"` //关注总数
32 | FollowerCount int64 `json:"follower_count"` //粉丝总数
33 | IsFollow bool `json:"is_follow"` //是否关注
34 | AvatarUrl string `json:"avatar,omitempty"` //用户的url
35 | TotalFavorited int64 `json:"total_favorited,omitempty"` //获赞数量
36 | WorkCount int64 `json:"work_count,omitempty"` //作品数量
37 | FavoriteCount int64 `json:"favorite_count,omitempty"` //点赞数量
38 | }
39 |
40 | func TestRedis(t *testing.T) {
41 |
42 | _, err := InitRedisClient()
43 | if err != nil {
44 | fmt.Println(err)
45 | }
46 | }
47 |
48 | type guo struct {
49 | Name string
50 | Age int
51 | }
52 |
53 | //func (g *guo) MarshalBinary() (data []byte, err error) {
54 | // return json.Marshal(g)
55 | //}
56 | //
57 | //func (g *guo) UnmarshalBinary(data []byte) (err error) {
58 | // return json.Unmarshal(data, g)
59 | //}
60 |
61 | func TestAddRedis(t *testing.T) {
62 | db, _ := InitRedisClient()
63 | comment := CommentInfo{
64 | Content: "111",
65 | CreateDate: time.Now().String(),
66 | ID: 1,
67 | UserInfo: UserInfo{
68 | Id: 2,
69 | Name: "22",
70 | FollowCount: 3,
71 | FollowerCount: 4,
72 | IsFollow: false,
73 | AvatarUrl: "132",
74 | TotalFavorited: 1,
75 | WorkCount: 2,
76 | FavoriteCount: 3,
77 | },
78 | }
79 | bytes, err := json.Marshal(comment)
80 | if err != nil {
81 | fmt.Println(err)
82 | }
83 | db.LPush("commentInfo", bytes)
84 |
85 | }
86 |
87 | func TestQueryRedis(t *testing.T) {
88 | db, _ := InitRedisClient()
89 | push := db.LRange("commentInfo", 0, -1)
90 | result, err := push.Result()
91 | if err != nil {
92 | fmt.Println(err)
93 | }
94 | s := result[3]
95 | bytes := []byte(s)
96 | g := CommentInfo{}
97 | json.Unmarshal(bytes, &g)
98 | fmt.Printf("%v", g)
99 | }
100 |
101 | func TestTimeRedis(t *testing.T) {
102 | db, _ := RedisClient(10)
103 | //i, _ := db.Get("111").Int()
104 | db.Incr("111")
105 | //fmt.Println(i)
106 | }
107 |
108 | func TestIs(t *testing.T) {
109 | client, _ := RedisClient(1)
110 | client.SAdd("1", "2", "3", "4")
111 | member, _ := client.SIsMember("1", "1").Result()
112 | fmt.Println(member)
113 | }
114 |
115 | func TestCount(t *testing.T) {
116 | client, _ := RedisClient(4)
117 | card, _ := client.SCard("423").Result()
118 | fmt.Printf("%T", card)
119 | }
120 |
--------------------------------------------------------------------------------
/go/config/tencent_cos.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | // 替换Id
4 |
5 | const SecretId = "xxxxxxxxxxxxxxxxxxx"
6 |
7 | // 替换Key
8 |
9 | const SecretKey = "xxxxxxxxxxxxxxxxxxx"
10 |
11 | // 替换你的oss的域名
12 |
13 | const CosUrl = "https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com"
14 |
15 | const ReplaceSuffix = ".jpg"
16 |
17 | const CosUrlAnd = "https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/"
18 |
--------------------------------------------------------------------------------
/go/controller/commentController.go:
--------------------------------------------------------------------------------
1 | package controller
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "log"
6 | "net/http"
7 | "strconv"
8 | "tiktok/go/config"
9 | "tiktok/go/model"
10 | "tiktok/go/service"
11 | )
12 |
13 | type CommentListResponse struct {
14 | model.BaseResponse
15 | CommentList []model.CommentInfo `json:"comment_list"` // 评论列表
16 | }
17 | type CommentResponse struct {
18 | model.BaseResponse
19 | CommentInfo model.CommentInfo `json:"comment"` // 评论成功返回评论内容,不需要重新拉取整个列表
20 | }
21 |
22 | // 用户评论 登录用户对视频进行评论
23 |
24 | func CommentVideo(c *gin.Context) {
25 | // 获取登录用户
26 | user_id, exists := c.Get("Id")
27 | if !exists {
28 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.FailMsg(config.TokenIsNotExist))
29 | return
30 | }
31 | //
32 | userId := int64(user_id.(float64))
33 | video_id := c.Query("video_id")
34 | videoId, _ := strconv.ParseInt(video_id, 10, 64)
35 | action_type := c.Query("action_type")
36 |
37 | switch action_type {
38 | case "1":
39 | log.Printf("在%d的视频上发布评论", video_id)
40 | content := c.Query("comment_text")
41 | commentInfo, err := service.CreateCommentService(userId, videoId, content)
42 | if err != nil {
43 | c.JSON(http.StatusBadRequest, CommentResponse{
44 | BaseResponse: model.BaseResponseInstance.FailMsg(err.Error()),
45 | CommentInfo: model.CommentInfo{},
46 | })
47 | return
48 | } else {
49 | c.JSON(http.StatusOK, CommentResponse{
50 | BaseResponse: model.BaseResponseInstance.Success(),
51 | CommentInfo: commentInfo,
52 | })
53 | return
54 | }
55 | case "2":
56 | log.Printf("在%d的视频上删除评论", video_id)
57 | comment_id := c.Query("comment_id")
58 | commentId, _ := strconv.ParseInt(comment_id, 10, 64)
59 | isDelete, err := service.CancelCommentService(commentId, userId, videoId)
60 | if err != nil {
61 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.FailMsg(err.Error()))
62 | return
63 | } else {
64 | if !isDelete {
65 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.Fail())
66 | return
67 | } else {
68 | c.JSON(http.StatusOK, model.BaseResponseInstance.Success())
69 | return
70 | }
71 | }
72 | }
73 |
74 | println(userId)
75 | }
76 |
77 | // 评论列表
78 |
79 | func CommentList(c *gin.Context) {
80 | // 获取视频的id
81 | video_id := c.Query("video_id")
82 | // 判空
83 | if video_id == "" {
84 | c.JSON(http.StatusOK, CommentListResponse{
85 | BaseResponse: model.BaseResponseInstance.FailMsg(config.RequestParameterIsNull),
86 | CommentList: nil,
87 | })
88 | return
89 | }
90 | // 转换
91 | videoId, err := strconv.ParseInt(video_id, 10, 64)
92 | if err != nil {
93 | c.JSON(http.StatusBadRequest, CommentListResponse{
94 | BaseResponse: model.BaseResponseInstance.Fail(),
95 | CommentList: nil,
96 | })
97 | return
98 | }
99 | // 调用服务
100 | commentInfos, err := service.CommentListService(videoId)
101 | if err != nil {
102 | c.JSON(http.StatusBadRequest, CommentListResponse{
103 | BaseResponse: model.BaseResponseInstance.FailMsg(err.Error()),
104 | CommentList: nil,
105 | })
106 | return
107 | }
108 | c.JSON(http.StatusOK, CommentListResponse{
109 | BaseResponse: model.BaseResponseInstance.Success(),
110 | CommentList: commentInfos,
111 | })
112 | return
113 | }
114 |
--------------------------------------------------------------------------------
/go/controller/followController.go:
--------------------------------------------------------------------------------
1 | package controller
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | "strconv"
7 | "tiktok/go/config"
8 | "tiktok/go/model"
9 | "tiktok/go/service"
10 | )
11 |
12 | type FollowListResponse struct {
13 | model.BaseResponse
14 | UserList []model.UserInfo `json:"user_list"` // 用户信息列表
15 | }
16 |
17 | type FollowerListResponse struct {
18 | model.BaseResponse
19 | UserList []model.UserInfo `json:"user_list"` // 用户信息列表
20 | }
21 |
22 | // 关注操作
23 |
24 | func FollowUser(c *gin.Context) {
25 | // 对方用户的id
26 | to_user_id := c.Query("to_user_id")
27 | // 取token
28 | user_id, exists := c.Get("Id")
29 | if !exists {
30 | c.JSON(http.StatusBadRequest, model.BaseResponse{
31 | StatusCode: -1,
32 | StatusMsg: config.TokenIsNotExist,
33 | })
34 | return
35 | }
36 | // 取类型
37 | action_type := c.Query("action_type")
38 | // 判断为空
39 | if to_user_id == "" || user_id == "" || action_type == "" {
40 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.FailMsg(config.RequestParameterIsNull))
41 | return
42 | }
43 | // 转换
44 | toUserId, _ := strconv.ParseInt(to_user_id, 10, 64)
45 | userId := int64(user_id.(float64))
46 | if userId == toUserId {
47 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.FailMsg("不能自己关注自己"))
48 | return
49 | }
50 | var actionType bool
51 | // 关注
52 | if action_type == "1" {
53 | actionType = true
54 | } else if action_type == "2" {
55 | actionType = false
56 | } else {
57 | c.JSON(http.StatusNotFound, model.BaseResponseInstance.FailMsg(config.RequestFail))
58 | return
59 | }
60 | pass, err := service.FollowUserService(userId, toUserId, actionType)
61 | if err != nil {
62 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.FailMsg(err.Error()))
63 | return
64 | }
65 | if !pass {
66 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.Fail())
67 | return
68 | } else {
69 | c.JSON(http.StatusOK, model.BaseResponseInstance.Success())
70 | return
71 | }
72 | }
73 |
74 | // 关注列表
75 |
76 | func FollowList(c *gin.Context) {
77 | user_id := c.Query("user_id")
78 | userId, _ := strconv.ParseInt(user_id, 10, 64)
79 | // 取token
80 | loginUserId, exists := c.Get("Id")
81 | // 判空
82 | if user_id == "" || loginUserId == "" {
83 | c.JSON(http.StatusBadRequest, FollowListResponse{
84 | BaseResponse: model.BaseResponseInstance.FailMsg(config.RequestParameterIsNull),
85 | UserList: nil,
86 | })
87 | return
88 | }
89 | if !exists {
90 | // 不存在即未登录
91 | userInfos, err := service.FollowListService(userId)
92 | if err != nil {
93 | c.JSON(http.StatusBadRequest, FollowListResponse{
94 | BaseResponse: model.BaseResponseInstance.FailMsg(err.Error()),
95 | UserList: nil,
96 | })
97 | return
98 | }
99 | c.JSON(http.StatusOK, FollowListResponse{
100 | BaseResponse: model.BaseResponseInstance.Success(),
101 | UserList: userInfos,
102 | })
103 | return
104 | } else {
105 | loginUserId := int64(loginUserId.(float64))
106 | userInfos, err := service.FollowListServiceWithUserId(userId, loginUserId)
107 | if err != nil {
108 | c.JSON(http.StatusBadRequest, FollowListResponse{
109 | BaseResponse: model.BaseResponseInstance.FailMsg(err.Error()),
110 | UserList: nil,
111 | })
112 | return
113 | }
114 | c.JSON(http.StatusOK, FollowListResponse{
115 | BaseResponse: model.BaseResponseInstance.Success(),
116 | UserList: userInfos,
117 | })
118 | return
119 | }
120 |
121 | }
122 |
123 | // 粉丝列表
124 |
125 | func FollowerList(c *gin.Context) {
126 | user_id := c.Query("user_id")
127 | userId, _ := strconv.ParseInt(user_id, 10, 64)
128 | if user_id == "" {
129 | c.JSON(http.StatusBadRequest, FollowerListResponse{
130 | BaseResponse: model.BaseResponseInstance.FailMsg(config.RequestParameterIsNull),
131 | UserList: nil,
132 | })
133 | return
134 | }
135 | // 取token
136 | loginUserId, exists := c.Get("Id")
137 | if !exists {
138 | userInfos, err := service.FollowerListService(userId)
139 | if err != nil {
140 | c.JSON(http.StatusBadRequest, FollowerListResponse{
141 | BaseResponse: model.BaseResponseInstance.FailMsg(err.Error()),
142 | UserList: nil,
143 | })
144 | return
145 | }
146 | c.JSON(http.StatusOK, FollowerListResponse{
147 | BaseResponse: model.BaseResponseInstance.Success(),
148 | UserList: userInfos,
149 | })
150 | return
151 | } else {
152 | loginUserId := int64(loginUserId.(float64))
153 | userInfos, err := service.FollowerListServiceWithUserId(userId, loginUserId)
154 | if err != nil {
155 | c.JSON(http.StatusBadRequest, FollowerListResponse{
156 | BaseResponse: model.BaseResponseInstance.FailMsg(err.Error()),
157 | UserList: nil,
158 | })
159 | return
160 | }
161 | c.JSON(http.StatusOK, FollowerListResponse{
162 | BaseResponse: model.BaseResponseInstance.Success(),
163 | UserList: userInfos,
164 | })
165 | return
166 | }
167 |
168 | }
169 |
--------------------------------------------------------------------------------
/go/controller/likeController.go:
--------------------------------------------------------------------------------
1 | package controller
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | "strconv"
7 | "tiktok/go/config"
8 | "tiktok/go/model"
9 | "tiktok/go/service"
10 | )
11 |
12 | type userFavoriteListResponse struct {
13 | model.BaseResponse
14 | VideoList []model.Video `json:"video_list"`
15 | }
16 |
17 | // 点赞行为: 1-点赞,2-取消点赞
18 |
19 | func LikeVideoByUserID(c *gin.Context) {
20 | video_id := c.Query("video_id")
21 | action_type := c.Query("action_type")
22 | // 判断是否为空
23 | if video_id == "" || action_type == "" {
24 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.FailMsg(config.RequestParameterIsNull))
25 | return
26 | }
27 | // 转类型
28 | id, _ := strconv.ParseInt(video_id, 10, 64)
29 | Type, _ := strconv.ParseInt(action_type, 10, 64)
30 | // 用于同步数据库
31 | switch Type {
32 | case 1:
33 | //设置为0
34 | Type = 0
35 | case 2:
36 | Type = 1
37 | default:
38 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.FailMsg(config.RequestFail))
39 | return
40 | }
41 | // 提取用户Id
42 | user_id, exists := c.Get("Id")
43 | if !exists {
44 | c.JSON(http.StatusNotFound, model.BaseResponseInstance.FailMsg(config.TokenIsNotExist))
45 | return
46 | }
47 | userId := int64(user_id.(float64))
48 | // 点赞Type为1 取消为2
49 | flag, err := service.LikeVideoByUserIDService(userId, id, Type)
50 | if err != nil {
51 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.FailMsg(err.Error()))
52 | return
53 | }
54 | if flag {
55 | c.JSON(http.StatusOK, model.BaseResponseInstance.Success())
56 | return
57 | }
58 | }
59 |
60 | // 用户点赞列表
61 |
62 | func UserFavoriteList(c *gin.Context) {
63 | user_id := c.Query("user_id")
64 | // 判空
65 | if user_id == "" {
66 | c.JSON(http.StatusBadRequest, userFavoriteListResponse{
67 | BaseResponse: model.BaseResponseInstance.FailMsg(config.RequestParameterIsNull),
68 | VideoList: []model.Video{},
69 | })
70 | return
71 | }
72 | userId, err := strconv.ParseInt(user_id, 10, 64)
73 | if err != nil {
74 | c.JSON(http.StatusBadRequest, userFavoriteListResponse{
75 | BaseResponse: model.BaseResponseInstance.FailMsg(err.Error()),
76 | VideoList: []model.Video{},
77 | })
78 | return
79 | }
80 | userFavoriteList, err := service.UserFavoriteListService(userId)
81 | if err != nil {
82 | c.JSON(http.StatusBadRequest, userFavoriteListResponse{
83 | BaseResponse: model.BaseResponseInstance.FailMsg(err.Error()),
84 | VideoList: []model.Video{},
85 | })
86 | return
87 | } else {
88 | c.JSON(http.StatusOK, userFavoriteListResponse{
89 | BaseResponse: model.BaseResponseInstance.Success(),
90 | VideoList: userFavoriteList,
91 | })
92 | return
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/go/controller/messageController.go:
--------------------------------------------------------------------------------
1 | package controller
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "log"
6 | "net/http"
7 | "strconv"
8 | "tiktok/go/config"
9 | "tiktok/go/model"
10 | "tiktok/go/service"
11 | )
12 |
13 | type FriendListResponse struct {
14 | model.BaseResponse
15 | UserList []model.FriendUser `json:"user_list"` // 用户列表
16 | }
17 |
18 | type MessageListResponse struct {
19 | model.BaseResponse
20 | MessageList []model.Message `json:"message_list"`
21 | }
22 |
23 | // 好友列表
24 |
25 | func FriendList(c *gin.Context) {
26 | user_id := c.Query("user_id")
27 | //userId, _ := strconv.ParseInt(user_id, 10, 64)
28 | if user_id == "" {
29 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.FailMsg(config.RequestParameterIsNull))
30 | return
31 | }
32 | log.Println(user_id)
33 | // 提取用户Id
34 | userid, exists := c.Get("Id")
35 | if !exists {
36 | c.JSON(http.StatusNotFound, model.BaseResponseInstance.FailMsg(config.TokenIsNotExist))
37 | return
38 | }
39 | userId := int64(userid.(float64))
40 | //
41 | log.Printf("%v", userId)
42 | friendUsers, err := service.FriendListService(userId)
43 | if err != nil {
44 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.FailMsg(err.Error()))
45 | return
46 | }
47 | c.JSON(http.StatusOK, FriendListResponse{
48 | BaseResponse: model.BaseResponseInstance.Success(),
49 | UserList: friendUsers,
50 | })
51 | return
52 | }
53 |
54 | // 聊天记录 点进去才能看到
55 |
56 | func MessageChat(c *gin.Context) {
57 | to_user_id := c.Query("to_user_id")
58 | if to_user_id == "" {
59 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.FailMsg(config.RequestParameterIsNull))
60 | return
61 | }
62 | toUserId, _ := strconv.ParseInt(to_user_id, 10, 64)
63 | log.Println(to_user_id)
64 | // 提取用户Id
65 | userid, exists := c.Get("Id")
66 | if !exists {
67 | c.JSON(http.StatusNotFound, model.BaseResponseInstance.FailMsg(config.TokenIsNotExist))
68 | return
69 | }
70 | userId := int64(userid.(float64))
71 | // 提取上次最新消息的时间
72 | pre_msg_time := c.Query("pre_msg_time")
73 | preMsgTime, err := strconv.ParseInt(pre_msg_time, 10, 64)
74 | if err != nil {
75 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.FailMsg(err.Error()))
76 | return
77 | }
78 |
79 | messages, err := service.MessageChatService(userId, toUserId, preMsgTime)
80 | if err != nil {
81 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.FailMsg(err.Error()))
82 | return
83 | }
84 | c.JSON(http.StatusOK, MessageListResponse{
85 | BaseResponse: model.BaseResponseInstance.Success(),
86 | MessageList: messages,
87 | })
88 | return
89 | }
90 |
91 | // MessageAction 发送消息
92 | func MessageAction(c *gin.Context) {
93 | // 提取用户Id
94 | userid, exists := c.Get("Id")
95 | if !exists {
96 | c.JSON(http.StatusNotFound, model.BaseResponseInstance.FailMsg(config.TokenIsNotExist))
97 | return
98 | }
99 | userId := int64(userid.(float64))
100 | // 获取toUser
101 | to_user_id := c.Query("to_user_id")
102 | toUserId, err := strconv.ParseInt(to_user_id, 10, 64)
103 | content := c.Query("content")
104 | actionType := c.Query("action_type")
105 | // 参数错误
106 | if actionType != "1" {
107 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.FailMsg(config.RequestFail))
108 | return
109 | }
110 | pass, err := service.MessageActionService(userId, toUserId, content)
111 | if err != nil {
112 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.FailMsg(err.Error()))
113 | return
114 | }
115 | if !pass {
116 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.Fail())
117 | return
118 | } else {
119 | c.JSON(http.StatusOK, model.BaseResponseInstance.Success())
120 | return
121 | }
122 |
123 | }
124 |
--------------------------------------------------------------------------------
/go/controller/readme.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WenTesla/tiktok/140f0d6625698620d47a6d6badced4e425377a14/go/controller/readme.md
--------------------------------------------------------------------------------
/go/controller/userController.go:
--------------------------------------------------------------------------------
1 | package controller
2 |
3 | import (
4 | "fmt"
5 | "strconv"
6 | "tiktok/go/config"
7 | "tiktok/go/model"
8 |
9 | "github.com/gin-gonic/gin"
10 |
11 | "net/http"
12 | "tiktok/go/service"
13 | )
14 |
15 | // 要大写,不然无法传入json成功
16 | type userRegisterResponse struct {
17 | model.BaseResponse
18 | UserId int64 `json:"user_id"`
19 | Token string `json:"token"`
20 | }
21 | type userLoginResponse struct {
22 | model.BaseResponse
23 | UserId int64 `json:"user_id"`
24 | Token string `json:"token"`
25 | }
26 | type douyinUserResponse struct {
27 | model.BaseResponse
28 | UserInfo *model.UserInfo `json:"user"`
29 | }
30 |
31 | // 用户注册
32 |
33 | func Register(c *gin.Context) {
34 | username := c.Query("username")
35 | password := c.Query("password")
36 | // 先判空
37 | if username == "" || password == "" {
38 | c.JSON(http.StatusBadRequest, userRegisterResponse{
39 | BaseResponse: model.BaseResponseInstance.FailMsg("账号密码为空"),
40 | UserId: -1,
41 | Token: "",
42 | })
43 | return
44 | }
45 | // 先校验参数长度
46 | if len(password) > 32 || len(password) <= 5 || len(username) > 32 {
47 | c.JSON(http.StatusBadRequest, userRegisterResponse{
48 | BaseResponse: model.BaseResponseInstance.FailMsg("参数长度不正确"),
49 | UserId: -1,
50 | Token: "",
51 | })
52 | return
53 | }
54 | password = service.Encryption(password)
55 | //先校验合法性
56 | Id, err := service.RegisterService(username, password)
57 | if err != nil {
58 | fmt.Println(err)
59 | c.JSON(http.StatusBadRequest, userRegisterResponse{
60 | BaseResponse: model.BaseResponseInstance.FailMsg(err.Error()),
61 | UserId: -1,
62 | Token: "",
63 | })
64 | return
65 | } else {
66 | // 颁发token
67 | token, _ := service.GenerateTokenByName(username)
68 | // 成功返回
69 | c.JSON(http.StatusOK, userRegisterResponse{
70 | BaseResponse: model.BaseResponseInstance.Success(),
71 | UserId: Id,
72 | Token: token,
73 | })
74 | return
75 | }
76 | }
77 |
78 | // 用户登录
79 |
80 | func Login(c *gin.Context) {
81 | username := c.Query("username")
82 | password := c.Query("password")
83 | // 先判空
84 | if username == "" || password == "" {
85 | c.JSON(http.StatusBadRequest, userRegisterResponse{
86 | BaseResponse: model.BaseResponseInstance.FailMsg("账号密码为空"),
87 | UserId: -1,
88 | Token: "",
89 | })
90 | return
91 | }
92 |
93 | // 先校验参数
94 | if len(username) > 32 || len(password) > 32 || len(password) <= 5 {
95 | c.JSON(http.StatusBadRequest, userLoginResponse{
96 | BaseResponse: model.BaseResponseInstance.FailMsg("参数长度不正确"),
97 | UserId: -1,
98 | Token: "",
99 | })
100 | return
101 | }
102 | password = service.Encryption(password)
103 | fmt.Println(username, password)
104 | Id, err := service.LoginService(username, password)
105 | if err != nil {
106 | c.JSON(http.StatusBadRequest, userLoginResponse{
107 | BaseResponse: model.BaseResponseInstance.FailMsg(err.Error()),
108 | UserId: -1,
109 | Token: "",
110 | })
111 | } else {
112 | // 颁发token
113 | token, _ := service.GenerateTokenByName(username)
114 | c.JSON(http.StatusOK, userLoginResponse{
115 | BaseResponse: model.BaseResponseInstance.Success(),
116 | UserId: Id,
117 | Token: token,
118 | })
119 | }
120 |
121 | }
122 |
123 | // 用户信息
124 |
125 | func UserInfo(c *gin.Context) {
126 | user_Id := c.Query("user_id")
127 | // 转换Id的类型
128 | userId, _ := strconv.ParseInt(user_Id, 10, 64)
129 | Id, exists := c.Get("Id")
130 | if exists != true {
131 | c.JSON(http.StatusNotFound, douyinUserResponse{
132 | BaseResponse: model.BaseResponseInstance.FailMsg(config.TokenIsNotExist),
133 | })
134 | return
135 | }
136 | if userId != int64(Id.(float64)) {
137 | c.JSON(http.StatusNotFound, douyinUserResponse{
138 | BaseResponse: model.BaseResponseInstance.FailMsg(config.TokenIsNotMatchUserId),
139 | })
140 | return
141 | }
142 | userInfo, err := service.UserService(userId)
143 | if err != nil {
144 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.FailMsg(err.Error()))
145 | return
146 | } else {
147 | c.JSON(http.StatusOK, douyinUserResponse{
148 | BaseResponse: model.BaseResponseInstance.Success(),
149 | UserInfo: &userInfo,
150 | })
151 | }
152 |
153 | }
154 |
--------------------------------------------------------------------------------
/go/controller/videoController.go:
--------------------------------------------------------------------------------
1 | package controller
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "log"
6 | "net/http"
7 | "strconv"
8 | "tiktok/go/config"
9 | "tiktok/go/middle/jwt"
10 | "tiktok/go/model"
11 | "tiktok/go/service"
12 | "time"
13 | )
14 |
15 | type VideoStreamModel struct {
16 | model.BaseResponse
17 | NextTime int64 `json:"next_time,omitempty"` // 本次返回的视频中,发布最早的时间,作为下次请求时的latest_time
18 | VideoList []model.Video `json:"video_list"` // 视频列表
19 | }
20 |
21 | type VideoPublishListResponse struct {
22 | model.BaseResponse
23 | VideoList []model.Video `json:"video_list"` // 用户发布的视频列表
24 | }
25 |
26 | type Stream struct {
27 | model.BaseResponse
28 | }
29 |
30 | // 定义变量
31 |
32 | // 视频流接口
33 |
34 | func VideoStream(c *gin.Context) {
35 | // 传入的参数
36 | input_time := c.Query("latest_time")
37 | log.Printf("获取的参数 %s", input_time)
38 | var last_time time.Time
39 | if len(input_time) != 0 {
40 | // 处理传入的时间戳(这里是毫秒的)
41 | temp, _ := strconv.ParseInt(input_time, 10, 64)
42 | temp /= 1000
43 | last_time = time.Unix(temp, 0)
44 | } else {
45 | last_time = time.Now()
46 | }
47 | log.Printf("获取的时间戳 %v", last_time)
48 | // 定义变量
49 | var err error
50 | var videos []model.Video
51 | //userId := 20053
52 | // 获取token的数据
53 | token := c.Query("token")
54 | // 未登录的情况下
55 | if len(token) == 0 {
56 | videos, err = service.VideoStreamService(last_time, -1)
57 | if err != nil {
58 | c.JSON(http.StatusBadRequest, VideoStreamModel{
59 | BaseResponse: model.BaseResponseInstance.FailMsg(err.Error()),
60 | VideoList: []model.Video{},
61 | })
62 | }
63 | // 获取发布最早的时间 作为下一条next参数
64 | nextTime, err := model.QueryNextTimeByVideoId(videos[len(videos)-1].ID)
65 | if err != nil {
66 | c.JSON(http.StatusBadRequest, VideoStreamModel{
67 | BaseResponse: model.BaseResponseInstance.FailMsg(err.Error()),
68 | VideoList: []model.Video{},
69 | })
70 | return
71 | }
72 | log.Printf("%v", videos)
73 | c.JSON(http.StatusOK, VideoStreamModel{
74 | BaseResponse: model.BaseResponseInstance.Success(),
75 | NextTime: nextTime.UnixNano() / 1e6,
76 | VideoList: videos,
77 | })
78 | return
79 | }
80 | // 解析token
81 | parseToken, _ := jwt.ParseToken(token)
82 | userId := int64(parseToken.(float64))
83 | videos, err = service.VideoStreamService(last_time, userId)
84 | if err != nil {
85 | c.JSON(http.StatusBadRequest, VideoStreamModel{
86 | BaseResponse: model.BaseResponseInstance.FailMsg(err.Error()),
87 | VideoList: []model.Video{},
88 | })
89 | return
90 | }
91 | // 获取发布最早的时间 作为下一条next参数 这里有问题
92 | nextTime, err := model.QueryNextTimeByVideoId(videos[len(videos)-1].ID)
93 | if err != nil {
94 | c.JSON(http.StatusBadRequest, VideoStreamModel{
95 | BaseResponse: model.BaseResponseInstance.FailMsg(err.Error()),
96 | VideoList: []model.Video{},
97 | })
98 | return
99 | }
100 | c.JSON(http.StatusOK, VideoStreamModel{
101 | NextTime: nextTime.UnixNano() / 1e6,
102 | BaseResponse: model.BaseResponseInstance.Success(),
103 | VideoList: videos,
104 | })
105 | }
106 |
107 | // 登录用户选择视频上传
108 |
109 | func VideoPublish(c *gin.Context) {
110 | file, err := c.FormFile("data")
111 | if err != nil {
112 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.FailMsg(config.RequestFail))
113 | return
114 | }
115 | // 参数判断空
116 | if file.Size == 0 {
117 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.FailMsg(config.RequestParameterIsNull))
118 | return
119 | }
120 | // 获取登录用户的id
121 | user_id, exists := c.Get("Id")
122 | if !exists {
123 | c.JSON(http.StatusNotFound, model.BaseResponseInstance.FailMsg(config.RequestFail))
124 | return
125 | }
126 | // 转换
127 | //user_id.(float64)
128 | // 获取标题
129 | title := c.PostForm("title")
130 | // 上传视频
131 | err = service.PublishVideoService(file, int64(user_id.(float64)), title)
132 | if err != nil {
133 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.FailMsg(err.Error()))
134 | return
135 | } else {
136 | c.JSON(http.StatusOK, model.BaseResponseInstance.Success())
137 | return
138 | }
139 |
140 | }
141 |
142 | // 发布列表 用户的视频发布列表,直接列出用户所有投稿过的视频
143 |
144 | func VideoList(c *gin.Context) {
145 | user_id := c.Query("user_id")
146 | Id, err := strconv.ParseInt(user_id, 10, 64)
147 | if err != nil {
148 | c.JSON(http.StatusBadRequest, VideoPublishListResponse{
149 | BaseResponse: model.BaseResponseInstance.FailMsg(config.RequestFail),
150 | VideoList: nil,
151 | })
152 | return
153 | }
154 | videos, err := service.VideoInfoByUserId(int(Id))
155 | if err != nil {
156 | c.JSON(http.StatusNotFound, VideoPublishListResponse{
157 | BaseResponse: model.BaseResponseInstance.FailMsg(config.DatabaseError),
158 | VideoList: nil,
159 | })
160 | return
161 | }
162 | c.JSON(http.StatusOK, VideoPublishListResponse{
163 | BaseResponse: model.BaseResponseInstance.Success(),
164 | VideoList: videos,
165 | })
166 | }
167 |
--------------------------------------------------------------------------------
/go/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "errors"
5 | "io"
6 | "os"
7 | "tiktok/go/config"
8 | "tiktok/go/util"
9 |
10 | "github.com/gin-gonic/gin"
11 |
12 | // "tiktok/go/config"
13 | "tiktok/go/route"
14 | )
15 |
16 | /*
17 | 启动类
18 | */
19 | func main() {
20 | //初始化项目
21 | initProject()
22 | // 1.创建路由
23 | r := gin.Default()
24 | // 2.绑定路由规则,执行的函数
25 | route.LoadRouter(r)
26 | // 3.监听端口,默认在8080
27 | // Run("里面不指定端口号默认为8080")
28 | err := r.Run(":8000")
29 | if err != nil {
30 | panic(errors.New("项目启动失败"))
31 | }
32 | }
33 |
34 | func initProject() {
35 | // mysql 初始化
36 | config.InitDataSource()
37 | // redis 初始化
38 | config.InitRedisClient()
39 | // 过滤器
40 | util.InitSensitiveFilter()
41 |
42 | // 设置日志 --取消注释即可创建日志文件
43 | f, _ := os.Create("resources/gin.log") // // 如果文件已存在,会将文件清空。
44 | gin.DefaultWriter = io.MultiWriter(f)
45 | //gin.DebugPrintRouteFunc()
46 |
47 | // 如果需要同时将日志写入文件和控制台,请使用以下代码。
48 | //gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
49 | util.Log("服务器开启成功!")
50 | }
51 |
--------------------------------------------------------------------------------
/go/middle/base.go:
--------------------------------------------------------------------------------
1 | package middle
2 |
3 | import "tiktok/go/config"
4 |
5 | // 用于限速和防止大量请求
6 | var FlowLimitRedisDbByIp, _ = config.RedisClient(10)
7 |
8 | var FlowLimitRedisDbByUserId, _ = config.RedisClient(11)
9 |
--------------------------------------------------------------------------------
/go/middle/jwt/Verify.go:
--------------------------------------------------------------------------------
1 | package jwt
2 |
3 | import (
4 | "fmt"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | "strconv"
8 | "tiktok/go/config"
9 | "tiktok/go/middle"
10 | "tiktok/go/model"
11 | "time"
12 | )
13 |
14 | // 核实token,用于中间件的验证
15 | func VerifyToken(c *gin.Context) {
16 | token := c.Query("token")
17 | fmt.Printf("%v \t \n", token)
18 | if len(token) == 0 {
19 | ////错误 直接
20 | //c.Abort()
21 | ////返回json
22 | //c.JSON(http.StatusBadRequest, BasicResponse{
23 | // StatusCode: -1,
24 | // StatusMsg: "未携带token",
25 | //})
26 |
27 | // 未登录开启IP限速器
28 | remoteIP := c.RemoteIP()
29 | result, _ := middle.FlowLimitRedisDbByIp.Get(remoteIP).Int()
30 | if result == 0 {
31 | // 设置
32 | middle.FlowLimitRedisDbByIp.Set(remoteIP, 1, time.Minute)
33 | } else if result <= 60 {
34 | // +1
35 | middle.FlowLimitRedisDbByIp.Incr(remoteIP)
36 | } else {
37 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.FailMsg(config.RequestTooFast))
38 | c.Abort()
39 | }
40 | c.Next()
41 | return
42 | }
43 | Id, err := ParseToken(token)
44 | if err != nil {
45 | // 解析错误
46 | c.Abort()
47 | // 返回json
48 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.FailMsg(config.TokenIsNotExist))
49 | } else {
50 | // 开启用户限速器
51 | //temp := int64(Id.(float64))
52 | userId := strconv.FormatFloat(Id.(float64), 'f', 0, 64)
53 | // 登录开启Id限速器
54 | result, _ := middle.FlowLimitRedisDbByIp.Get(userId).Int()
55 | if result == 0 {
56 | // 设置
57 | middle.FlowLimitRedisDbByIp.Set(userId, 1, time.Minute)
58 | } else if result <= 60 {
59 | // +1
60 | middle.FlowLimitRedisDbByIp.Incr(userId)
61 | } else {
62 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.FailMsg(config.RequestTooFast))
63 | c.Abort()
64 | }
65 |
66 | // 解析签发时间
67 | tokenTime, err := ParseTokenTime(token)
68 | if err != nil {
69 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.FailMsg(config.TokenParseErr))
70 | }
71 | // 判断时间
72 | if GetDays(int64(tokenTime.(float64)), time.Now().Unix()) > 30 {
73 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.FailMsg(config.TokenIsExpire))
74 | }
75 | // 解析正确
76 | //str := strconv.FormatFloat(Id, 'E', -1, 64)
77 | //strconv.ParseInt(str, 10, 64)
78 | c.Set("Id", Id)
79 | c.Next()
80 | }
81 | }
82 |
83 | // 通过post请求获取token
84 |
85 | func VerifyTokenByPost(c *gin.Context) {
86 | token := c.PostForm("token")
87 | fmt.Printf("%v \t \n", token)
88 | if len(token) == 0 {
89 | //错误 直接
90 | c.Abort()
91 | //返回json
92 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.FailMsg(config.TokenIsNotExist))
93 | return
94 | }
95 | Id, err := ParseToken(token)
96 | if err != nil {
97 | // 解析错误
98 | c.Abort()
99 | // 返回json
100 | c.JSON(http.StatusBadRequest, model.BaseResponseInstance.FailMsg(config.TokenParseErr))
101 | } else {
102 | // 解析正确
103 | //str := strconv.FormatFloat(Id, 'E', -1, 64)
104 | //strconv.ParseInt(str, 10, 64)
105 | c.Set("Id", Id)
106 | c.Next()
107 | }
108 | }
109 |
110 | func GetDays(start, end int64) int {
111 | startTime := time.Unix(start, 0)
112 | endTime := time.Unix(end, 0)
113 | sub := int(endTime.Sub(startTime).Hours())
114 | days := sub / 24
115 | if (sub % 24) > 0 {
116 | days = days + 1
117 | }
118 | return days
119 | }
120 |
--------------------------------------------------------------------------------
/go/middle/jwt/jwt_test.go:
--------------------------------------------------------------------------------
1 | package jwt
2 |
3 | import (
4 | "fmt"
5 | "golang.org/x/time/rate"
6 | "log"
7 | "net/http"
8 | "testing"
9 | "time"
10 | )
11 |
12 | // 测试token的生成
13 | func TestSignToken(t *testing.T) {
14 | token := TestSignTokenFunction("1111")
15 | log.Println(token)
16 | }
17 |
18 | //func TestParseToken(t *testing.T) {
19 | //
20 | //}
21 |
22 | func TestToken(t *testing.T) {
23 | token := TestSignTokenFunction("1111")
24 | log.Println(token)
25 | parseToken, err := TestParseToken(token)
26 | fmt.Println(parseToken, err)
27 | }
28 |
29 | func TestParseTokenTime(t *testing.T) {
30 | time, err := ParseTokenTime("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaXNzIjoidGlrdG9rIiwibmJmIjoxNjc1MTU5ODM4LCJzdWIiOiLnlKjmiLdJZCJ9.sPKBddA3foAeVVH19QFNoDNIckUIIipfZOrGvKLHxQk")
31 | if err != nil {
32 | t.Fail()
33 | }
34 | fmt.Println(time)
35 | }
36 |
37 | func TestGetDays(t *testing.T) {
38 | days := GetDays(1676775698, 1675998098)
39 | fmt.Println(days)
40 | }
41 |
42 | func TestLimit(t *testing.T) {
43 | r := rate.Every(1 * time.Millisecond)
44 | limit := rate.NewLimiter(r, 10)
45 | http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
46 | if limit.Allow() {
47 | fmt.Printf("请求成功,当前时间:%s\n", time.Now().Format("2006-01-02 15:04:05"))
48 | } else {
49 | fmt.Printf("请求成功,但是被限流了。。。\n")
50 | }
51 | })
52 |
53 | _ = http.ListenAndServe(":8081", nil)
54 | }
55 |
56 | func GetApi() {
57 | api := "http://localhost:8081/"
58 | res, err := http.Get(api)
59 | if err != nil {
60 | panic(err)
61 | }
62 | defer res.Body.Close()
63 |
64 | if res.StatusCode == http.StatusOK {
65 | fmt.Printf("get api success\n")
66 | }
67 | }
68 |
69 | func Benchmark_Main(b *testing.B) {
70 | for i := 0; i < b.N; i++ {
71 | GetApi()
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/go/middle/jwt/token.go:
--------------------------------------------------------------------------------
1 | package jwt
2 |
3 | import (
4 | "fmt"
5 | "github.com/golang-jwt/jwt/v4"
6 | "tiktok/go/config"
7 | "tiktok/go/model"
8 | "time"
9 | )
10 |
11 | // 提取密钥
12 | var jwtSecret = []byte(config.Secret)
13 |
14 | // 测试签名token
15 | func TestSignTokenFunction(Id string) string {
16 | // test
17 |
18 | // Create a new token object, specifying signing method and the claims
19 | // you would like it to contain.
20 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
21 | // sub (subject): 主题
22 | "sub": "用户Id来签发token",
23 | //
24 | "foo": "bar",
25 | //nbf (Not Before): 生效时间
26 | //"nbf": time.Date(2015, 10, 10, 12, 0, 0, 0, time.UTC).Unix(),
27 | "nbf": time.Now().Unix(),
28 | })
29 | // 提取密钥
30 | var jwtSecret = []byte(config.Secret)
31 | // Sign and get the complete encoded token as a string using the secret
32 | tokenString, err := token.SignedString(jwtSecret)
33 | // 如果签名失败
34 | if err != nil {
35 | return ""
36 | }
37 | fmt.Println(tokenString, err)
38 | return tokenString
39 | }
40 |
41 | // 签名token
42 | func SignToken(user model.User) string {
43 | // dev
44 | Id := user.Id
45 | // Create a new token object, specifying signing method and the claims
46 | // you would like it to contain.
47 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
48 | // iss (issuer): 签发人
49 | "iss": "tiktok",
50 | // sub (subject): 主题
51 | "sub": "用户Id",
52 | // Id
53 | "id": Id,
54 | //nbf (Not Before): 生效时间
55 | "nbf": time.Now().Unix(),
56 | })
57 |
58 | // Sign and get the complete encoded token as a string using the secret
59 | tokenString, err := token.SignedString(jwtSecret)
60 | // 如果签名失败
61 | if err != nil {
62 | return "签名失败"
63 | }
64 | fmt.Println(tokenString, err)
65 | return tokenString
66 | }
67 |
68 | // 测试解析token
69 | func TestParseToken(tokenString string) (interface{}, error) {
70 | // sample token string taken from the New example
71 | //tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9.u1riaD1rW97opCoAuRCTy4w58Br-Zk-bh7vLiRIsrpU"
72 |
73 | // Parse takes the token string and a function for looking up the key. The latter is especially
74 | // useful if you use multiple keys for your application. The standard is to use 'kid' in the
75 | // head of the token to identify which key to use, but the parsed token (head and claims) is provided
76 | // to the callback, providing flexibility.
77 | token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
78 | // Don't forget to validate the alg is what you expect:
79 | if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
80 | return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
81 | }
82 |
83 | // hmacSampleSecret is a []byte containing your secret, e.g. []byte("my_secret_key")
84 | return jwtSecret, nil
85 | })
86 |
87 | if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
88 | fmt.Println(claims["foo"], claims["nbf"])
89 | } else {
90 | fmt.Println(err)
91 | return false, err
92 | }
93 | return true, err
94 | }
95 |
96 | // 解析token 返回用户Id
97 |
98 | func ParseToken(tokenString string) (interface{}, error) {
99 |
100 | // sample token string taken from the New example
101 | //tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9.u1riaD1rW97opCoAuRCTy4w58Br-Zk-bh7vLiRIsrpU"
102 |
103 | // Parse takes the token string and a function for looking up the key. The latter is especially
104 | // useful if you use multiple keys for your application. The standard is to use 'kid' in the
105 | // head of the token to identify which key to use, but the parsed token (head and claims) is provided
106 | // to the callback, providing flexibility.
107 | token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
108 | // Don't forget to validate the alg is what you expect:
109 | if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
110 | return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
111 | }
112 |
113 | // hmacSampleSecret is a []byte containing your secret, e.g. []byte("my_secret_key")
114 | return jwtSecret, nil
115 | })
116 |
117 | if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
118 | //fmt.Println(claims["foo"], claims["nbf"])
119 | return claims["id"], nil
120 | } else {
121 | fmt.Println(err)
122 | return nil, err
123 | }
124 | //return true, err
125 | }
126 |
127 | // 返回token中的签发日期
128 |
129 | func ParseTokenTime(tokenString string) (any, error) {
130 | // sample token string taken from the New example
131 | //tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9.u1riaD1rW97opCoAuRCTy4w58Br-Zk-bh7vLiRIsrpU"
132 |
133 | // Parse takes the token string and a function for looking up the key. The latter is especially
134 | // useful if you use multiple keys for your application. The standard is to use 'kid' in the
135 | // head of the token to identify which key to use, but the parsed token (head and claims) is provided
136 | // to the callback, providing flexibility.
137 | token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
138 | // Don't forget to validate the alg is what you expect:
139 | if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
140 | return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
141 | }
142 |
143 | // hmacSampleSecret is a []byte containing your secret, e.g. []byte("my_secret_key")
144 | return jwtSecret, nil
145 | })
146 |
147 | if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
148 | //fmt.Println(claims["foo"], claims["nbf"])
149 | return claims["nbf"], nil
150 | } else {
151 | fmt.Println(err)
152 | return nil, err
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/go/model/InitDb.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "gorm.io/driver/mysql"
5 | "gorm.io/gorm"
6 | )
7 |
8 | //var db *gorm.DB
9 |
10 | // 初始化并返回数据链接
11 | func Init() *gorm.DB {
12 | // 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
13 | dsn := "root:zhang134679@tcp(127.0.0.1:3306)/tiktok?charset=utf8mb4&parseTime=True&loc=Local"
14 | //dsn := "tiktok:tiktok@tcp(47.115.218.216:3306)/tiktok?charset=utf8mb4&parseTime=True&loc=Local"
15 | Db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
16 | PrepareStmt: true, //缓存预编译语句
17 | })
18 | if err != nil {
19 | panic(err)
20 | return nil
21 | }
22 | println("连接成功")
23 | return Db
24 | }
25 |
--------------------------------------------------------------------------------
/go/model/base.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import "tiktok/go/config"
4 |
5 | // 数据库连接池
6 | var db = config.InitDataSource()
7 |
8 | type BaseResponse struct {
9 | StatusCode int32 `json:"status_code"`
10 | StatusMsg string `json:"status_msg"`
11 | }
12 |
13 | var BaseResponseInstance = BaseResponse{}
14 |
15 | func (baseResponse *BaseResponse) Success() BaseResponse {
16 | baseResponse.StatusCode = 0
17 | baseResponse.StatusMsg = config.Success
18 | return BaseResponseInstance
19 | }
20 |
21 | func (baseResponse *BaseResponse) Fail() BaseResponse {
22 | baseResponse.StatusCode = -1
23 | baseResponse.StatusMsg = config.Fail
24 | return BaseResponseInstance
25 | }
26 |
27 | func (baseResponse *BaseResponse) SuccessMsg(msg string) BaseResponse {
28 | baseResponse.StatusCode = 0
29 | baseResponse.StatusMsg = msg
30 | return BaseResponseInstance
31 | }
32 | func (baseResponse *BaseResponse) FailMsg(msg string) BaseResponse {
33 | baseResponse.StatusCode = -1
34 | baseResponse.StatusMsg = msg
35 | return BaseResponseInstance
36 | }
37 |
--------------------------------------------------------------------------------
/go/model/comment.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "errors"
5 | "tiktok/go/util"
6 | "time"
7 | )
8 |
9 | type Comment struct {
10 | ID int64 // 评论id
11 | UserId int64 // 用户Id
12 | VideoId int64 //视频Id
13 | Text string // 评论内容
14 | IsCancel int64 // 是否取消
15 | CreateTime time.Time `gorm:"column:createTime"` // 创建时间
16 | }
17 |
18 | // CommentInfo 评论详细信息
19 | type CommentInfo struct {
20 | Content string `json:"content"` // 评论内容
21 | CreateDate string `json:"create_date"` // 评论发布日期,格式 mm-dd
22 | ID int64 `json:"id"` // 评论id
23 | UserInfo UserInfo `json:"user"` // 评论用户的具体信息
24 | }
25 |
26 | // QueryCommentByUserId 根据用户id获取评论
27 | func QueryCommentByUserId(userId int64) ([]Comment, error) {
28 | var comments []Comment
29 | // 获取
30 | result := db.Debug().Where("user_id = ? AND is_cancel = ?", userId, 0).Order("createTime desc").Find(&comments)
31 | if result.Error != nil {
32 | util.LogError(result.Error.Error())
33 | return nil, result.Error
34 | }
35 | return comments, nil
36 | }
37 |
38 | // QueryCommentByVideoId 根据视频id获取评论
39 | func QueryCommentByVideoId(videoId int64) ([]Comment, error) {
40 | var comments []Comment
41 | // 获取
42 | // SELECT * FROM `comments` WHERE video_id = 43 AND is_cancel = 0 ORDER BY createTime desc
43 | result := db.Where("video_id = ? AND is_cancel = ?", videoId, 0).Order("createTime desc").Find(&comments)
44 | if result.Error != nil {
45 | return nil, result.Error
46 | }
47 | return comments, nil
48 |
49 | }
50 |
51 | // QueryCommentCountByVideoId 根据视频的id获取视频的评论数
52 | func QueryCommentCountByVideoId(videoId int64) (int64, error) {
53 | var count int64
54 | // SELECT count(*) FROM `comments` WHERE video_id = 43 AND is_cancel = 0
55 | result := db.Model(&Comment{}).Where("video_id = ? AND is_cancel = ?", videoId, 0).Count(&count)
56 | if result.Error != nil {
57 | util.LogError(result.Error.Error())
58 | return -1, result.Error
59 | }
60 | return count, nil
61 | }
62 |
63 | // InsertComment 插入评论 这有问题 传入的参数
64 | func InsertComment(userId int64, videoId int64, content string) (Comment, error) {
65 | comment := Comment{
66 | UserId: userId,
67 | VideoId: videoId,
68 | Text: content,
69 | CreateTime: time.Now(),
70 | }
71 | result := db.Debug().Select("user_id", "video_id", "text").Create(&comment)
72 | if result.Error != nil {
73 | util.LogError(result.Error.Error())
74 | return comment, result.Error
75 | }
76 | return comment, nil
77 | }
78 |
79 | // DeleteComment 删除评论
80 | func DeleteComment(id int64) (bool, error) {
81 | result := db.Debug().Delete(&Comment{}, id)
82 | if result.Error != nil {
83 | util.LogError(result.Error.Error())
84 | return false, result.Error
85 | }
86 | if result.RowsAffected == 0 {
87 | return false, errors.New("该评论不存在")
88 | }
89 | return true, nil
90 | }
91 |
92 | // 取消评论 gorm有bug还是设计问题,不能通过主键直接更新,必须先查询数据
93 |
94 | func CancelComment(id int64, userId int64, videoId int64) (bool, error) {
95 | comment := Comment{
96 | ID: id,
97 | }
98 | //UPDATE `comments` SET `is_cancel`=1 WHERE (user_id = 1 AND video_id = 15) AND `id` = 48
99 | result := db.Debug().Model(&comment).Where("user_id = ? AND video_id = ?", userId, videoId).Update("is_cancel", 1)
100 | //// SELECT * FROM `comments` WHERE `comments`.`id` = 10 ORDER BY `comments`.`id` LIMIT 1
101 | //db.First(&comment, id)
102 | //comment.IsCancel = 1
103 | //result := db.Debug().Save(&comment)
104 | //if result.Error != nil {
105 | // util.LogError(result.Error.Error())
106 | // return false, result.Error
107 | //}
108 | //if result.RowsAffected == 0 {
109 | // return false, errors.New("该评论不存在")
110 | //}
111 | if result.Error != nil {
112 | util.LogError(result.Error.Error())
113 | return false, result.Error
114 | }
115 | return true, nil
116 | }
117 |
--------------------------------------------------------------------------------
/go/model/comment_test.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestQueryCommentByUserId(t *testing.T) {
9 | comments, err := QueryCommentByUserId(2)
10 | fmt.Printf("%v", comments)
11 | fmt.Printf("%v", err)
12 | }
13 | func TestQueryCommentByVideoId(t *testing.T) {
14 | comments, err := QueryCommentByVideoId(2)
15 | fmt.Printf("%v", comments)
16 | fmt.Printf("%v", err)
17 | }
18 | func TestCancelComment(t *testing.T) {
19 | CancelComment(48, 1, 15)
20 | }
21 |
--------------------------------------------------------------------------------
/go/model/follow.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "tiktok/go/util"
5 | "time"
6 | )
7 |
8 | // Follow 用户关系结构,对应用户关系表。
9 | type Follow struct {
10 | Id int64
11 | UserId int64
12 | FollowerId int64
13 | Cancel int8
14 | CreatedAt time.Time `gorm:"-"`
15 | }
16 |
17 | // GetFollowingById 通过id获取自己关注的数目 同时筛选出cancel
18 | func GetFollowingById(id int64) (int64, error) {
19 | var count int64
20 | // SELECT count(1) FROM users WHERE name = 'jinzhu'; (count)
21 | result := db.Model(&Follow{}).Where("user_id = ? AND cancel = ?", id, 0).Count(&count)
22 | if result.Error != nil {
23 | util.LogError(result.Error.Error())
24 | return 0, result.Error
25 | }
26 | return count, nil
27 | }
28 |
29 | // GetFansById 通过id获取自己的粉丝数 同时筛选出cancel
30 | func GetFansById(id int64) (int64, error) {
31 | var count int64
32 | // SELECT count(1) FROM users WHERE name = 'jinzhu'; (count)
33 | result := db.Model(&Follow{}).Where("follower_id = ? AND cancel = ?", id, 0).Count(&count)
34 | if result.Error != nil {
35 | util.LogError(result.Error.Error())
36 | return 0, result.Error
37 | }
38 | return count, nil
39 | }
40 |
41 | // IsFollowingById 通过id查看是否关注
42 | func IsFollowingById(id int64) (bool, error) {
43 | //var count int64
44 |
45 | return true, nil
46 | }
47 |
48 | // InsertFollow 插入关注
49 | func InsertFollow(userId int64, toUserID int64) (bool, error) {
50 | follow := Follow{
51 | UserId: userId,
52 | FollowerId: toUserID,
53 | }
54 | // INSERT INTO `follows` (`user_id`,`follower_id`,`cancel`) VALUES (7,9,0)
55 | result := db.Create(&follow)
56 | if result.Error != nil {
57 | util.LogError(result.Error.Error())
58 | return false, result.Error
59 | }
60 | return true, nil
61 | }
62 |
63 | // QueryFollowByUserIdAndToUserID 根据用户id和关注的id查询是否存在
64 | func QueryFollowByUserIdAndToUserID(userId int64, toUserID int64) (bool, error) {
65 | //SELECT * FROM `follows` WHERE user_id = 1 AND follower_id = 5
66 | result := db.Where("user_id = ? AND follower_id = ?", userId, toUserID).Find(&Follow{})
67 | if result.Error != nil {
68 | util.LogError(result.Error.Error())
69 | return false, result.Error
70 | }
71 | if result.RowsAffected == 0 {
72 | return false, nil
73 | }
74 | return true, nil
75 | }
76 |
77 | // CancelFollow 取消关注
78 | func CancelFollow(userId int64, toUserID int64) error {
79 | // 更新单列
80 | //UPDATE `follows` SET `cancel`=1 WHERE user_id = 1 AND follower_id = 5
81 | result := db.Debug().Model(&Follow{}).Where("user_id = ? AND follower_id = ?", userId, toUserID).Update("cancel", 1)
82 | if result.Error != nil {
83 | util.LogError(result.Error.Error())
84 | return result.Error
85 | }
86 | return nil
87 | }
88 |
89 | // RefocusUser 重新关注用户
90 | func RefocusUser(userId int64, toUserID int64) error {
91 | // 更新单列
92 | // UPDATE `follows` SET `cancel`=0 WHERE user_id = 1 AND follower_id = 5
93 | result := db.Model(&Follow{}).Where("user_id = ? AND follower_id = ?", userId, toUserID).Update("cancel", 0)
94 | if result.Error != nil {
95 | util.LogError(result.Error.Error())
96 | return result.Error
97 | }
98 | return nil
99 | }
100 |
101 | // 根据用户id查询用户关注的用户id切片 比较绕
102 |
103 | func QueryFollowUsersByUserId(userId int64) ([]User, error) {
104 | var users []User
105 | //SELECT * FROM `users` WHERE id IN (SELECT `follower_id` FROM `follows` WHERE user_id = 1 AND cancel = 0)
106 | result := db.Where("id IN (?)", db.Where("user_id = ? AND cancel = ?", userId, 0).Select("follower_id").Find(&Follow{})).Find(&users)
107 | if result.Error != nil {
108 | util.LogError(result.Error.Error())
109 | return nil, result.Error
110 | }
111 | return users, nil
112 | }
113 |
114 | // 根据用户id查询当前用户的粉丝id切片 比较绕
115 |
116 | func QueryFansUsersByUserId(userId int64) ([]User, error) {
117 | var users []User
118 | //SELECT * FROM `users` WHERE id IN (SELECT `user_id` FROM `follows` WHERE follower_id = 1 AND cancel = 0)
119 | result := db.Where("id IN (?)", db.Where("follower_id = ? AND cancel = ?", userId, 0).Select("user_id").Find(&Follow{})).Find(&users)
120 | if result.Error != nil {
121 | util.LogError(result.Error.Error())
122 | return nil, result.Error
123 | }
124 | return users, nil
125 | }
126 |
127 | /*
128 | 查询互相关注的用户列表
129 | */
130 |
131 | func QueryMutualFollowListByUserId(userId int64) ([]User, error) {
132 |
133 | // inner join
134 | //db.InnerJoins("Company").Find(&users)
135 | // SELECT `users`.`id`,`users`.`name`,`users`.`age`,`Company`.`id` AS `Company__id`,`Company`.`name` AS `Company__name` FROM `users` INNER JOIN `companies` AS `Company` ON `users`.`company_id` = `Company`.`id`
136 |
137 | var MutualFollowList []User
138 | // SELECT a.follower_id FROM follows a join follows b on a.user_id=b.follower_id AND a.follower_id = b.user_id WHERE a.cancel = 0 AND b.cancel = 0 AND a.user_id = ? AND b.follower_id = ?
139 | //result := db.Debug().Table("follows a").Select("a.follower_id").Joins("join follows b on a.user_id=b.follower_id AND a.follower_id = b.user_id").Where("a.cancel = ? AND b.cancel = ? AND a.user_id = ? AND b.follower_id = ?", 0, 0, userId, userId).Find(&MutualFollowList)
140 | // SELECT `id` `name` FROM `users` WHERE id in (SELECT a.follower_id FROM follows a join follows b on a.user_id=b.follower_id AND a.follower_id = b.user_id WHERE a.cancel = 0 AND b.cancel = 0 AND a.user_id = 1 AND b.follower_id = 1)
141 | result := db.Where("id in (?)", db.Table("follows a").Select("a.follower_id").Joins("join follows b on a.user_id=b.follower_id AND a.follower_id = b.user_id").Where("a.cancel = ? AND b.cancel = ? AND a.user_id = ? AND b.follower_id = ?", 0, 0, userId, userId).Find(&Follow{})).Select("id", "name").Find(&MutualFollowList)
142 | if result.Error != nil {
143 | util.LogError(result.Error.Error())
144 | return nil, result.Error
145 | }
146 | return MutualFollowList, nil
147 | }
148 |
149 | // 查询是否关注 第一个参数为当前用户的id,第二个参数为要关注的用户Id
150 |
151 | func QueryIsFollow(userId int64, toUserId int64) (bool, error) {
152 | // 自己不能关注自己
153 | if userId == toUserId {
154 | return true, nil
155 | }
156 | var count int64
157 | //SELECT count(*) FROM `follows` WHERE user_id = ? AND follower_id = ? AND cancel = 0
158 | result := db.Model(&Follow{}).Where("user_id = ? AND follower_id = ? AND cancel = ?", userId, toUserId, 0).Count(&count)
159 | if result.Error != nil {
160 | util.LogError(result.Error.Error())
161 | return false, result.Error
162 | }
163 | if count != 0 {
164 | return true, nil
165 | }
166 | return false, nil
167 | }
168 |
169 | // 查询粉丝的Id
170 |
171 | func QueryFansId(userId int64) ([]int64, error) {
172 |
173 | return nil, nil
174 | }
175 |
--------------------------------------------------------------------------------
/go/model/follow_test.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "testing"
7 | )
8 |
9 | func TestGetFollowingById(t *testing.T) {
10 | id, err := GetFollowingById(2)
11 | log.Println(err)
12 | log.Println(id)
13 | }
14 |
15 | func TestGetFansById(t *testing.T) {
16 | id, err := GetFansById(2)
17 | log.Println(id)
18 | log.Println(err)
19 | }
20 | func TestInsertFollow(t *testing.T) {
21 | InsertFollow(4, 2)
22 | }
23 | func TestCancelFollow(t *testing.T) {
24 | CancelFollow(1, 2)
25 | }
26 | func TestQueryFollowUsersByUserId(t *testing.T) {
27 | users, err := QueryFollowUsersByUserId(1)
28 | if err != nil {
29 | log.Println(err)
30 | }
31 | log.Println(users)
32 | }
33 | func TestQueryIsFollow(t *testing.T) {
34 | isFollow, _ := QueryIsFollow(1, 50)
35 | fmt.Println(isFollow)
36 | }
37 |
38 | func TestQueryMutualFollowListByUserId(t *testing.T) {
39 | QueryMutualFollowListByUserId(1)
40 | }
41 |
--------------------------------------------------------------------------------
/go/model/like.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "tiktok/go/config"
5 | "tiktok/go/util"
6 | )
7 |
8 | // Like 表的结构,不需要json化
9 | type Like struct {
10 | Id int64 //自增主键
11 | UserId int64 //点赞用户的id
12 | VideoId int64 //视频的id
13 | IsCancel int8 //是否点赞,0为点赞,1为取消赞
14 | }
15 |
16 | // 更新点赞数据
17 | func UpdateLikeVideoByUserId(userId int64, videoId int64, action int64) error {
18 | //更新当前用户观看视频的点赞状态“cancel”,返回错误结果
19 | // UPDATE `likes` SET `is_cancel`=0 WHERE `user_id` = 9 AND `video_id` = 39
20 | result := db.Model(Like{}).Where(map[string]interface{}{"user_id": userId, "video_id": videoId}).
21 | Update("is_cancel", action)
22 | if result.Error != nil {
23 | util.LogError(result.Error.Error())
24 | return result.Error
25 | }
26 | return nil
27 | }
28 |
29 | // 插入数据
30 | func InsertLikeData(userId int64, videoId int64) (bool, error) {
31 | like := Like{
32 | UserId: userId,
33 | VideoId: videoId,
34 | }
35 | result := db.Create(&like)
36 | if result.Error != nil {
37 | util.LogError(result.Error.Error())
38 | return false, result.Error
39 | }
40 | return true, nil
41 | }
42 |
43 | // QueryDuplicateLikeData 查询是否有重复数据
44 | func QueryDuplicateLikeData(userId int64, videoId int64) (bool, error) {
45 | like := Like{}
46 | // SELECT * FROM `likes` WHERE `user_id` = 9 AND `video_id` = 39
47 | result := db.Where(map[string]interface{}{"user_id": userId, "video_id": videoId}).Find(&like)
48 | if result.Error != nil {
49 | util.LogError(result.Error.Error())
50 | // 查询错误
51 | return false, result.Error
52 | }
53 | if like.Id == 0 {
54 | return false, nil
55 | }
56 | return true, nil
57 | }
58 |
59 | // QueryVideoByUserId 根据用户id查询视频的信息
60 | func QueryVideoByUserId(userId int64) ([]TableVideo, error) {
61 | tableVideos := make([]TableVideo, config.VideoMaxCount) //
62 | // SELECT
63 | // *
64 | //FROM
65 | // videos
66 | //WHERE
67 | // id IN ( SELECT video_id FROM likes WHERE user_id =? AND is_cancel = 0);
68 | result := db.Where("id IN (?)", db.Where("user_id = ? AND is_cancel = ?", userId, 0).Select("video_id").Find(&Like{})).Find(&tableVideos)
69 | //log.Printf("%v", tableVideos)
70 | if result.Error != nil {
71 | util.LogError(result.Error.Error())
72 | return nil, result.Error
73 | }
74 | return tableVideos, nil
75 | }
76 |
77 | // QueryLikeByVideoId 根据id获取视频被点赞的总数
78 | func QueryLikeByVideoId(videoId int64) (int64, error) {
79 | var count int64
80 | // SELECT count(*) FROM `likes` WHERE video_id = ? AND is_cancel = 0
81 | result := db.Model(&Like{}).Where("video_id = ? AND is_cancel = ?", videoId, 0).Count(&count)
82 | if result.Error != nil {
83 | util.LogError(result.Error.Error())
84 | return -1, result.Error
85 | }
86 | return count, nil
87 | }
88 |
89 | // 查询是否有点赞
90 |
91 | func QueryIsLike(userId int64, videoId int64) (bool, error) {
92 | like := Like{}
93 | // SELECT * FROM `likes` WHERE `user_id` = 9 AND `video_id` = 39 AND is_cancel = 0
94 | result := db.Where(map[string]interface{}{"user_id": userId, "video_id": videoId, "is_cancel": 0}).Find(&like)
95 | if result.Error != nil {
96 | util.LogError(result.Error.Error())
97 | // 查询错误
98 | return false, result.Error
99 | }
100 | if like.Id == 0 {
101 | return false, nil
102 | }
103 | return true, nil
104 | }
105 |
106 | // 查询点赞数量
107 |
108 | func QueryFavoriteCountByUserId(userId int64) (int64, error) {
109 | var count int64
110 | // SELECT count(*) FROM `likes` WHERE user_id = 1 AND is_cancel = 0
111 | result := db.Model(&Like{}).Where("user_id = ? AND is_cancel = ?", userId, 0).Count(&count)
112 | if result.Error != nil {
113 | util.LogError(result.Error.Error())
114 | return -1, result.Error
115 | }
116 | return count, nil
117 | }
118 |
119 | // 获取获赞数量
120 |
121 | func QueryTotalFavorited(userId int64) (int64, error) {
122 | var count int64
123 | // SELECT count(*) FROM `likes` WHERE video_id in (SELECT `id` FROM `videos` WHERE author_id = 1) AND is_cancel = 0
124 | db.Model(&Like{}).Where("video_id in (?) AND is_cancel = ?", db.Model(&TableVideo{}).Where("author_id = ?", userId).Select("id"), 0).Count(&count)
125 | return count, nil
126 | }
127 |
--------------------------------------------------------------------------------
/go/model/like_test.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestLikeVideoByUserId(t *testing.T) {
9 |
10 | }
11 |
12 | func TestUnLikeVideoByUserId(t *testing.T) {
13 | err := UpdateLikeVideoByUserId(2, 1, 1)
14 | fmt.Printf("%v", err)
15 | }
16 | func TestQueryDuplicateLikeData(t *testing.T) {
17 | data, err := QueryDuplicateLikeData(1, 10)
18 | fmt.Printf("%v %v", data, err)
19 | }
20 | func TestQueryVideoByUserId(t *testing.T) {
21 | QueryVideoByUserId(2)
22 | }
23 | func TestQueryLikeByVideoId(t *testing.T) {
24 | count, _ := QueryLikeByVideoId(3)
25 | fmt.Print(count)
26 | }
27 | func TestQueryTotalFavorited(t *testing.T) {
28 | QueryTotalFavorited(1)
29 | }
30 |
--------------------------------------------------------------------------------
/go/model/message.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "tiktok/go/config"
5 | "tiktok/go/util"
6 | "time"
7 | )
8 |
9 | type Message struct {
10 | ID int64 `json:"id"`
11 | UserId int64 `json:"from_user_id"`
12 | ToUserId int64 `json:"to_user_id"`
13 | Content string `json:"content"`
14 | IsWithdraw int8 `json:"is_withdraw,omitempty"`
15 | CreateTime int64 `json:"create_time" gorm:"column:createTime"` // 创建时间
16 | }
17 |
18 | // 插入数据
19 |
20 | func InsertMessage(userId int64, toUserId int64, content string) (bool, error) {
21 | messageInfo := Message{
22 | UserId: userId,
23 | ToUserId: toUserId,
24 | Content: content,
25 | CreateTime: time.Now().Unix(),
26 | }
27 | // INSERT INTO `messages` (`user_id`,`to_user_id`,`content`,`is_withdraw`,`createTime`) VALUES (5,1,'111',0,'2023-02-08 19:21:15.017')
28 | result := db.Create(&messageInfo)
29 | if result.Error != nil {
30 | util.LogError(result.Error.Error())
31 | return false, result.Error
32 | }
33 | return true, nil
34 | }
35 |
36 | // 根据用户Id查询聊天记录
37 |
38 | func QueryMessageByUserId(userId int64) ([]Message, error) {
39 | var messages []Message
40 | //SELECT * FROM `messages` WHERE user_id = 1 AND is_withdraw = 0 LIMIT 10
41 | result := db.Where("user_id = ? AND is_withdraw = ?", userId, 0).Limit(config.MessageCount).Find(&messages)
42 | if result.Error != nil {
43 | util.LogError(result.Error.Error())
44 | return nil, result.Error
45 | }
46 | return messages, nil
47 | }
48 |
49 | // 通过用户Id查询最新的聊天记录 0-接受 1-发送 有点问题
50 |
51 | func QueryNewestMessageByUserId(userId int64) (string, int8, error) {
52 | message := Message{}
53 | // SELECT * FROM `messages` WHERE (user_id = 1 Or to_user_id = 1) AND is_withdraw = 0 ORDER BY createTime desc LIMIT 1
54 | result := db.Where("(user_id = ? Or to_user_id = ?) AND is_withdraw = ?", userId, userId, 0).Order("createTime desc").Limit(1).Find(&message)
55 | if result.Error != nil {
56 | util.LogError(result.Error.Error())
57 | return "", -1, result.Error
58 | }
59 | if userId == message.UserId {
60 | return message.Content, 1, nil
61 | } else {
62 | return message.Content, 0, nil
63 | }
64 | }
65 |
66 | // 通过两者的用户Id查询最新最新的两者之间的聊天记录 0-接受 1-发送
67 |
68 | func QueryNewestMessageByUserIdAndToUserID(userId int64, toUserId int64) (string, int8, error) {
69 | message := Message{}
70 | // SELECT `content`,`createTime`,`user_id`,`to_user_id` FROM `messages` WHERE (user_id = 2 AND to_user_id = 7 AND is_withdraw = 0) OR (user_id = 7 AND to_user_id = 2 AND is_withdraw = 0) ORDER BY createTime desc LIMIT 1
71 | result := db.Where("user_id = ? AND to_user_id = ? AND is_withdraw = ?", userId, toUserId, 0).Or("user_id = ? AND to_user_id = ? AND is_withdraw = ?", toUserId, userId, 0).Order("createTime desc").Limit(1).Select("content", "createTime", "user_id", "to_user_id").Find(&message)
72 | if result.Error != nil {
73 | util.LogError(result.Error.Error())
74 | return "", -1, result.Error
75 | }
76 | if userId == message.UserId {
77 | return message.Content, 1, nil
78 | } else {
79 | return message.Content, 0, nil
80 | }
81 | }
82 |
83 | // 查询两者的全部聊天记录
84 |
85 | func QueryMessageByUserIdAndToUserId(userId int64, toUserId int64) ([]Message, error) {
86 | var messages []Message
87 | // SELECT * FROM `messages` WHERE (user_id = 1 AND to_user_id = 2 AND is_withdraw = 0) OR (user_id = 2 AND to_user_id = 1 AND is_withdraw = 0) ORDER BY createTime asc
88 | result := db.Where("user_id = ? AND to_user_id = ? AND is_withdraw = ?", userId, toUserId, 0).Or("user_id = ? AND to_user_id = ? AND is_withdraw = ?", toUserId, userId, 0).Order("createTime asc").Find(&messages)
89 | if result.Error != nil {
90 | util.LogError(result.Error.Error())
91 | return messages, result.Error
92 | }
93 | //for _, message := range messages {
94 | // message.CreateTime = message.CreateTime.Unix()
95 | //}
96 | return messages, nil
97 | }
98 |
99 | // 查询消息记录的最大值
100 |
101 | func QueryMessageMaxCount(userId int64, toUserId int64) (int64, error) {
102 | var count int64
103 | // SELECT count(*) FROM `messages` WHERE (user_id = 1 AND to_user_id = 2 ) OR (user_id = 2 AND to_user_id = 1 )
104 | result := db.Model(&Message{}).Where("user_id = ? AND to_user_id = ? ", userId, toUserId).Or("user_id = ? AND to_user_id = ? ", toUserId, userId).Count(&count)
105 | if result.Error != nil {
106 | util.LogError(result.Error.Error())
107 | return -1, result.Error
108 | }
109 | return count, nil
110 | }
111 |
--------------------------------------------------------------------------------
/go/model/message_test.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestInsertMessage(t *testing.T) {
9 | InsertMessage(3, 2, "1311")
10 | }
11 |
12 | func TestQueryMessageByUserId(t *testing.T) {
13 | QueryMessageByUserId(1)
14 | }
15 | func TestQert(t *testing.T) {
16 | _, i, _ := QueryNewestMessageByUserId(1)
17 | fmt.Println(i)
18 | }
19 | func TestQueryNewestMessageByUserIdAndToUserID(t *testing.T) {
20 | QueryNewestMessageByUserIdAndToUserID(1, 2)
21 | }
22 | func TestQueryMessageByUserIdAndToUserId(t *testing.T) {
23 | QueryMessageByUserIdAndToUserId(1, 2)
24 | }
25 |
26 | func TestQueryMessageMaxCount(t *testing.T) {
27 | count, _ := QueryMessageMaxCount(1, 2)
28 | fmt.Println(count)
29 | }
30 |
--------------------------------------------------------------------------------
/go/model/readme.md:
--------------------------------------------------------------------------------
1 | ### 定义数据类型与数据库交互接口
2 |
--------------------------------------------------------------------------------
/go/model/user.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "tiktok/go/util"
5 | )
6 |
7 | // User 对应数据库User表结构的结构体
8 | type User struct {
9 | Id int64 //主键
10 | Name string //昵称
11 | Password string //密码
12 | }
13 |
14 | // UserInfo 最详细的信息 不与数据库模型对应
15 | type UserInfo struct {
16 | Id int64 `json:"id,omitempty"` //主键
17 | Name string `json:"name,omitempty"` //昵称
18 | FollowCount int64 `json:"follow_count"` //关注总数
19 | FollowerCount int64 `json:"follower_count"` //粉丝总数
20 | IsFollow bool `json:"is_follow"` //是否关注
21 | AvatarUrl string `json:"avatar,omitempty"` //用户的url
22 | TotalFavorited int64 `json:"total_favorited,omitempty"` //获赞数量
23 | WorkCount int64 `json:"work_count,omitempty"` //作品数量
24 | FavoriteCount int64 `json:"favorite_count,omitempty"` //点赞数量
25 | }
26 |
27 | type FriendUser struct {
28 | UserInfo
29 | Message string `json:"message"` //聊天信息
30 | MsgType int8 `json:"msgType"` //message信息的类型,0=>请求用户接受信息,1=>当前请求用户发送的信息
31 | }
32 |
33 | // InsertUser 插入用户
34 | func InsertUser(name string, password string) (User, error) {
35 | //CreateUser()
36 | user := User{
37 | Name: name,
38 | Password: password,
39 | }
40 | result := db.Create(&user)
41 | return user, result.Error
42 | // return true, nil
43 | }
44 |
45 | // GetUserById 根据id(主键)获取用户
46 | func GetUserById(id int64) (User, error) {
47 | user := User{}
48 | // SELECT * FROM `users` WHERE Id = 9 ORDER BY `users`.`id` LIMIT 1
49 | if err := db.Where("Id = ?", id).First(&user).Error; err != nil {
50 | return user, err
51 | }
52 | //Db.Close()
53 | return user, nil
54 | }
55 |
56 | // GetUserByName 根据用户名(唯一)查询用户
57 | func GetUserByName(name string) (User, error) {
58 | user := User{}
59 | //Db := config.InitDataSource()
60 | // 查数据表
61 | // SELECT * FROM `users` WHERE name = '周子豪' LIMIT 1
62 | if err := db.Where("name = ?", name).Limit(1).Find(&user).Error; err != nil {
63 | //log.Println(err.Error())
64 | util.LogError(err.Error())
65 | return user, err
66 | }
67 | return user, nil
68 | }
69 |
70 | // PackageUserToUserInfo
71 | //
72 | // FollowCount
73 | // FollowerCount
74 | // IsFollow
75 | func PackageUserToUserInfo(user User) (UserInfo, error) {
76 | userInfo := UserInfo{}
77 | //var err error
78 | // 查询关注总数
79 | followingsCount, err := GetFollowingById(user.Id)
80 | if err != nil {
81 | util.LogError(err.Error())
82 |
83 | return userInfo, err
84 | }
85 | // 查询粉丝总数
86 | fansCount, err := GetFansById(user.Id)
87 | if err != nil {
88 | util.LogError(err.Error())
89 | return userInfo, err
90 | }
91 | // to-do 查询是否关注
92 |
93 | // 合并
94 | userInfo.Id = user.Id
95 | userInfo.Name = user.Name
96 | userInfo.FollowCount = followingsCount
97 | userInfo.FollowerCount = fansCount
98 | userInfo.IsFollow = false
99 |
100 | return userInfo, nil
101 | }
102 |
103 | //
104 | //
105 | // IsFollow -todo 点赞和作品总数
106 |
107 | func PackageUserToSimpleUserInfo(user User, userId int64) (UserInfo, error) {
108 | userInfo := UserInfo{}
109 | // 查询是否关注
110 | isFollow, err := QueryIsFollow(userId, user.Id)
111 | if err != nil {
112 | util.LogError(err.Error())
113 | return userInfo, err
114 | }
115 | // 合并
116 | userInfo.Id = user.Id
117 | userInfo.Name = user.Name
118 | userInfo.FollowCount = 0
119 | userInfo.FollowerCount = 0
120 | userInfo.IsFollow = isFollow
121 | return userInfo, nil
122 | }
123 |
124 | // PackageUserToUserInfoByUserId 根据id将user包装成userInfo
125 | func PackageUserToUserInfoByUserId(id int64) (UserInfo, error) {
126 | userInfo := UserInfo{}
127 |
128 | return userInfo, nil
129 | }
130 |
131 | // 直接将user转换为userInfo,不查询数据库,只是包装
132 |
133 | func PackageUserToDirectUserInfo(user User) (UserInfo, error) {
134 | userInfo := UserInfo{}
135 | userInfo.Id = user.Id
136 | userInfo.Name = user.Name
137 | userInfo.IsFollow = true
138 | return userInfo, nil
139 | }
140 |
--------------------------------------------------------------------------------
/go/model/video.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "tiktok/go/config"
5 | "time"
6 | )
7 |
8 | type ApifoxModel struct {
9 | NextTime *int64 `json:"next_time"` // 本次返回的视频中,发布最早的时间,作为下次请求时的latest_time
10 | StatusCode int64 `json:"status_code"` // 状态码,0-成功,其他值-失败
11 | StatusMsg *string `json:"status_msg"` // 返回状态描述
12 | VideoList []Video `json:"video_list"` // 视频列表
13 | }
14 |
15 | // 对应sql表格的实体 更加简单的配置
16 | type TableVideo struct {
17 | Id int64 `json:"id"`
18 | AuthorId int64
19 | PlayUrl string `json:"play_url"`
20 | CoverUrl string `json:"cover_url"`
21 | PublishTime time.Time
22 | Title string `json:"title"` //视频名,5.23添加
23 | }
24 |
25 | // Video
26 | type Video struct {
27 | Author UserInfo `json:"author"` // 视频作者信息
28 | CommentCount int64 `json:"comment_count"` // 视频的评论总数
29 | CoverURL string `json:"cover_url"` // 视频封面地址
30 | FavoriteCount int64 `json:"favorite_count"` // 视频的点赞总数
31 | ID int64 `json:"id"` // 视频唯一标识
32 | IsFavorite bool `json:"is_favorite"` // true-已点赞,false-未点赞
33 | PlayURL string `json:"play_url"` // 视频播放地址
34 | Title string `json:"title"` // 视频标题
35 | }
36 |
37 | func (TableVideo) TableName() string {
38 | return "videos"
39 | }
40 |
41 | //var db = config.InitDataSource()
42 |
43 | // 获取时间戳之前的视频
44 |
45 | func GetVideoByLastTime(lastTime time.Time) ([]TableVideo, error) {
46 | tableVideos := make([]TableVideo, config.VideoCount)
47 | // SELECT * FROM `videos` WHERE publish_time <= '2023-02-11 18:37:18.326' ORDER BY publish_time desc LIMIT 10
48 | result := db.Where("publish_time <= ?", lastTime).Order("publish_time desc").Limit(config.VideoCount).Find(&tableVideos)
49 | if result.Error != nil {
50 | return nil, result.Error
51 | }
52 | return tableVideos, nil
53 | }
54 |
55 | // 通过用户id获取视频
56 |
57 | func GetVideoByUserId(userId int) ([]TableVideo, error) {
58 | tableVideos := make([]TableVideo, config.VideoMaxCount)
59 | // SELECT * FROM `videos` WHERE author_id = 13 ORDER BY publish_time desc LIMIT 30
60 | db.Where("author_id = ?", userId).Order("publish_time desc").Limit(config.VideoMaxCount).Find(&tableVideos)
61 | return tableVideos, nil
62 | }
63 |
64 | // 获取发布最早的视频的时间戳,作为下次请求的时间戳 废弃
65 |
66 | func GetVideoNextTime(lastTime time.Time) (time.Time, error) {
67 | tableVideo := TableVideo{}
68 | result := db.Debug().Where("publish_time <= ?", lastTime).Order("publish_time asc").Limit(1).Select("publish_time").Find(&tableVideo)
69 | if result.Error != nil {
70 | return time.Time{}, result.Error
71 | }
72 | return tableVideo.PublishTime, nil
73 | }
74 |
75 | // 获取
76 |
77 | func QueryNextTimeByVideoId(videoId int64) (time.Time, error) {
78 | tableVideo := TableVideo{}
79 | // SELECT `publish_time` FROM `videos` WHERE id = 6 LIMIT 1
80 | result := db.Where("id = ? ", videoId).Limit(1).Select("publish_time").Find(&tableVideo)
81 | if result.Error != nil {
82 | return time.Time{}, result.Error
83 | }
84 | return tableVideo.PublishTime, nil
85 | }
86 |
87 | // 插入数据库
88 |
89 | func InsertVideo(userId int64, play_url string, cover_url string, title string) error {
90 | tableVideo := TableVideo{
91 | AuthorId: userId,
92 | PlayUrl: play_url,
93 | CoverUrl: cover_url,
94 | Title: title,
95 | PublishTime: time.Now(),
96 | }
97 | result := db.Debug().Create(&tableVideo)
98 | if result.Error != nil {
99 | return result.Error
100 | }
101 | return nil
102 | }
103 |
104 | // 查询作品的数量
105 |
106 | func QueryWorkCountByUserId(userId int64) (int64, error) {
107 | var count int64
108 | // SELECT count(*) FROM `videos` WHERE author_id = 1
109 | //result := db.Debug().Table("videos").Where("author_id = ?", userId).Count(&count)
110 | result := db.Model(&TableVideo{}).Where("author_id = ?", userId).Count(&count)
111 | if result.Error != nil {
112 | return -1, result.Error
113 | }
114 | return count, nil
115 | }
116 |
117 | // 查询视频的Id是否存在
118 |
119 | func QueryIsExistVideoId(Id int64) (bool, error) {
120 | result := db.Find(&TableVideo{}, Id)
121 | if result.Error != nil {
122 | return false, result.Error
123 | }
124 | if result.RowsAffected == 0 {
125 | return false, nil
126 | }
127 | return true, nil
128 | }
129 |
--------------------------------------------------------------------------------
/go/model/video_test.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "testing"
7 | "time"
8 | )
9 |
10 | func TestGetVideoByLastTime(t *testing.T) {
11 | now := time.Now()
12 | videos, err := GetVideoByLastTime(now)
13 | log.Printf("%v", videos)
14 | log.Printf("%v", err)
15 | }
16 | func TestGetVideoNextTime(t *testing.T) {
17 | now := time.Now()
18 | lastTime, err := GetVideoNextTime(now)
19 | log.Printf("%v", lastTime)
20 | log.Printf("%v", err)
21 | }
22 |
23 | func TestQueryIsExitsVideoId(t *testing.T) {
24 | id, _ := QueryIsExistVideoId(11)
25 | fmt.Println(id)
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/go/route/load.go:
--------------------------------------------------------------------------------
1 | package route
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | "tiktok/go/controller"
7 | "tiktok/go/middle/jwt"
8 | "tiktok/go/model"
9 | )
10 |
11 | // LoadRouter
12 | // 路由分组/*
13 | func LoadRouter(r *gin.Engine) {
14 | apiRouter := r.Group("/douyin")
15 | // basic apis
16 | // 注册接口
17 | apiRouter.POST("/user/register/", controller.Register)
18 | // 登录接口
19 | apiRouter.POST("/user/login/", controller.Login)
20 | // 用户信息接口
21 | apiRouter.GET("/user/", jwt.VerifyToken, controller.UserInfo)
22 | // 视频流接口
23 | apiRouter.GET("/feed/", controller.VideoStream)
24 | // 发布接口(视频上传)
25 | apiRouter.POST("/publish/action/", jwt.VerifyTokenByPost, controller.VideoPublish)
26 | // 发布列表接口
27 | apiRouter.GET("/publish/list/", controller.VideoList)
28 |
29 | // extra apis - I
30 | // 用户点赞接口
31 | apiRouter.POST("/favorite/action/", jwt.VerifyToken, controller.LikeVideoByUserID)
32 | // 喜欢列表接口
33 | apiRouter.GET("/favorite/list/", controller.UserFavoriteList)
34 | // 用户评论接口
35 | apiRouter.POST("/comment/action/", jwt.VerifyToken, controller.CommentVideo)
36 | // 评论列表接口
37 | apiRouter.GET("/comment/list/", controller.CommentList)
38 | // extra apis - II
39 | // 关注操作
40 | apiRouter.POST("/relation/action/", jwt.VerifyToken, controller.FollowUser)
41 | // 关注列表
42 | apiRouter.GET("/relation/follow/list/", jwt.VerifyToken, controller.FollowList)
43 | // 粉丝列表
44 | apiRouter.GET("/relation/follower/list/", jwt.VerifyToken, controller.FollowerList)
45 | // 用户好友列表
46 | apiRouter.GET("/relation/friend/list/", jwt.VerifyToken, controller.FriendList)
47 | // 聊天记录
48 | apiRouter.GET("/message/chat/", jwt.VerifyToken, controller.MessageChat)
49 | // 发送消息
50 | apiRouter.POST("/message/action/", jwt.VerifyToken, controller.MessageAction)
51 | // test
52 | //apiRouter.GET("/test/token",jwt.SignToken)
53 |
54 | // other
55 | r.NoRoute(func(context *gin.Context) {
56 | context.JSON(http.StatusNotFound, model.BaseResponseInstance.FailMsg("页面不存在"))
57 | })
58 | }
59 |
--------------------------------------------------------------------------------
/go/service/baseService.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "errors"
5 | "tiktok/go/config"
6 | )
7 |
8 | // 自定义数据库错误 用于返回上层
9 | var dataSourceErr = errors.New(config.DatabaseError)
10 |
11 | // 提供基本服务
12 |
13 | var redisDb, _ = config.InitRedisClient()
14 |
15 | var userRedisDb, _ = config.RedisClient(1)
16 |
17 | var videoRedisDb, _ = config.RedisClient(2)
18 |
19 | var followRedisDb, _ = config.RedisClient(3)
20 |
21 | var likeRedisDb, _ = config.RedisClient(4)
22 |
23 | var commentRedisDb, _ = config.RedisClient(5)
24 |
25 | var messageRedisDb, _ = config.RedisClient(6)
26 |
--------------------------------------------------------------------------------
/go/service/commentService.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "errors"
5 | "tiktok/go/model"
6 | "tiktok/go/util"
7 | )
8 |
9 | func CommentListService(videoId int64) ([]model.CommentInfo, error) {
10 | comments, err := model.QueryCommentByVideoId(videoId)
11 | if err != nil {
12 | return nil, dataSourceErr
13 | }
14 | commentInfos, err := PackageComments(comments)
15 | return commentInfos, nil
16 | }
17 |
18 | // 包装切片
19 |
20 | func PackageComments(comments []model.Comment) ([]model.CommentInfo, error) {
21 | // 提前定义切片
22 | var commentInfos []model.CommentInfo
23 | for _, comment := range comments {
24 | commentInfo, err := PackageComment(comment)
25 | if err != nil {
26 | return nil, err
27 | }
28 | commentInfos = append(commentInfos, commentInfo)
29 | }
30 | return commentInfos, nil
31 | }
32 |
33 | // 包装一个结构体 传入userInfo值
34 |
35 | func PackageComment(comment model.Comment) (model.CommentInfo, error) {
36 | // 定义
37 | commentInfo := model.CommentInfo{}
38 | // 填入Id
39 | commentInfo.ID = comment.ID
40 | // 填入评论内容
41 | commentInfo.Content = comment.Text
42 | // 填入创建时间 "2006-01-02 15:04:05.999999999 -0700 MST"
43 | commentInfo.CreateDate = comment.CreateTime.Format("01-02")
44 | // 根据用户id查询
45 | user, err := model.GetUserById(comment.UserId)
46 | if err != nil {
47 | return commentInfo, dataSourceErr
48 | }
49 | userInfo, err := model.PackageUserToUserInfo(user)
50 | if err != nil {
51 | return commentInfo, err
52 | }
53 | commentInfo.UserInfo = userInfo
54 | return commentInfo, nil
55 | }
56 |
57 | // 创建评论
58 |
59 | func CreateCommentService(userId int64, videoId int64, content string) (model.CommentInfo, error) {
60 | // 敏感词处理
61 | content, err := replaceSensitive(content)
62 | commentInfo := model.CommentInfo{}
63 | isExistVideoId, err := model.QueryIsExistVideoId(videoId)
64 | if isExistVideoId != true {
65 | return commentInfo, errors.New("视频不存在")
66 | }
67 | // 插入评论
68 | comment, err := model.InsertComment(userId, videoId, content)
69 | if err != nil {
70 | return commentInfo, dataSourceErr
71 | }
72 | commentInfo, err = PackageComment(comment)
73 | if err != nil {
74 | return commentInfo, dataSourceErr
75 | }
76 | return commentInfo, nil
77 | }
78 |
79 | // 删除评论
80 |
81 | func CancelCommentService(id int64, userId int64, videoId int64) (bool, error) {
82 |
83 | isDelete, err := model.CancelComment(id, userId, videoId)
84 | if err != nil {
85 | return false, dataSourceErr
86 | }
87 | return isDelete, nil
88 | }
89 |
90 | // checkSensitive 检验敏感词
91 | func checkSensitive(content string) bool {
92 |
93 | return false
94 | }
95 |
96 | // 替换敏感词 优化-》传递指针
97 |
98 | func replaceSensitive(content string) (string, error) {
99 | content, err := util.SensitiveWordsFilter(content)
100 | return content, err
101 | }
102 |
--------------------------------------------------------------------------------
/go/service/followService.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "errors"
5 | "tiktok/go/model"
6 | )
7 |
8 | // 关注用户服务
9 |
10 | func FollowUserService(userId int64, toUserId int64, actionType bool) (bool, error) {
11 | if actionType {
12 | // 先查询数据
13 | isExist, err := model.QueryFollowByUserIdAndToUserID(userId, toUserId)
14 | if err != nil {
15 | return false, dataSourceErr
16 | }
17 | if isExist {
18 | // 修改数据
19 | err = model.RefocusUser(userId, toUserId)
20 | if err != nil {
21 | return false, dataSourceErr
22 | }
23 | return true, nil
24 | } else {
25 | // 插入数据
26 | pass, err := model.InsertFollow(userId, toUserId)
27 | if err != nil {
28 | return false, dataSourceErr
29 | }
30 | if !pass {
31 | return false, errors.New("关注失败")
32 | }
33 | return true, nil
34 | }
35 | } else {
36 | // 取消关注
37 | err := model.CancelFollow(userId, toUserId)
38 | if err != nil {
39 | return false, dataSourceErr
40 | }
41 | return true, nil
42 | }
43 | return false, nil
44 | }
45 |
46 | // 未登录状态下的关注列表服务
47 |
48 | func FollowListService(userId int64) ([]model.UserInfo, error) {
49 | // 先根据用户Id取用户关注
50 | users, err := model.QueryFollowUsersByUserId(userId)
51 | if err != nil {
52 | return nil, dataSourceErr
53 | }
54 | // 定义userInfos 切片
55 | var userInfos []model.UserInfo
56 | // 循环
57 | for _, user := range users {
58 | userInfo, err := model.PackageUserToUserInfo(user)
59 | if err != nil {
60 | return nil, err
61 | }
62 | userInfos = append(userInfos, userInfo)
63 | }
64 |
65 | return userInfos, nil
66 | }
67 |
68 | // 未登录状态下的粉丝列表服务
69 |
70 | func FollowerListService(userId int64) ([]model.UserInfo, error) {
71 | // 先根据用户Id取用户关注
72 | users, err := model.QueryFansUsersByUserId(userId)
73 | if err != nil {
74 | return nil, dataSourceErr
75 | }
76 | // 定义userInfos 切片
77 | var userInfos []model.UserInfo
78 | // 循环
79 | for _, user := range users {
80 | userInfo, err := model.PackageUserToUserInfo(user)
81 | userInfo.IsFollow = false
82 | if err != nil {
83 | return nil, err
84 | }
85 | userInfos = append(userInfos, userInfo)
86 | }
87 |
88 | return userInfos, nil
89 | }
90 |
91 | // 登录状态下的关注列表 第一个参数为 第二个参数为登录用户的Id
92 |
93 | func FollowListServiceWithUserId(userId int64, loginUserId int64) ([]model.UserInfo, error) {
94 | // 先根据用户Id取用户关注
95 | users, err := model.QueryFollowUsersByUserId(userId)
96 | if err != nil {
97 | return nil, dataSourceErr
98 | }
99 | // 定义userInfos 切片
100 | var userInfos []model.UserInfo
101 | // 循环
102 | for _, user := range users {
103 | // 判断对方是否关注
104 |
105 | userInfo, err := model.PackageUserToSimpleUserInfo(user, loginUserId)
106 | if err != nil {
107 | return nil, err
108 | }
109 | userInfos = append(userInfos, userInfo)
110 | }
111 |
112 | return userInfos, nil
113 | }
114 |
115 | // 登录状态下的粉丝列表
116 |
117 | func FollowerListServiceWithUserId(userId int64, loginUserId int64) ([]model.UserInfo, error) {
118 | // 先根据用户Id取用户关注
119 | users, err := model.QueryFansUsersByUserId(userId)
120 | if err != nil {
121 | return nil, dataSourceErr
122 | }
123 | // 定义userInfos 切片
124 | var userInfos []model.UserInfo
125 | // 循环
126 | for _, user := range users {
127 | userInfo, err := model.PackageUserToSimpleUserInfo(user, loginUserId)
128 | if err != nil {
129 | return nil, err
130 | }
131 | userInfos = append(userInfos, userInfo)
132 | }
133 |
134 | return userInfos, nil
135 | }
136 |
137 | // 互相关注的好友列表
138 |
139 | func MutualFollowListService(userId int64) ([]model.UserInfo, error) {
140 | // 先根据用户Id取用户关注
141 | users, err := model.QueryMutualFollowListByUserId(userId)
142 | if err != nil {
143 | return nil, dataSourceErr
144 | }
145 | // 定义userInfos 切片
146 | var userInfos []model.UserInfo
147 | // 循环
148 | for _, user := range users {
149 | userInfo, _ := model.PackageUserToDirectUserInfo(user)
150 | userInfos = append(userInfos, userInfo)
151 | }
152 | return userInfos, nil
153 | }
154 |
--------------------------------------------------------------------------------
/go/service/likeService.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "errors"
5 | "strconv"
6 | "tiktok/go/model"
7 | )
8 |
9 | // 用户点赞服务
10 |
11 | func LikeVideoByUserIDService(userId int64, videoId int64, actionType int64) (bool, error) {
12 | var err error = nil
13 | var isDuplicate bool
14 | videoIdString := strconv.FormatInt(videoId, 10)
15 | //// 点赞 -- 没有数据的话创建数据,存在数据的话插入数据
16 |
17 | //// 从redis判断是否点过赞
18 | //isMember, err := likeRedisDb.SIsMember(videoIdString, userId).Result()
19 | //// redis 正常
20 | //if isMember {
21 | // // 查询到数据
22 | // // 如果是点赞,直接返回
23 | // if actionType == 0 {
24 | // return true, nil
25 | // } else if actionType == 1 {
26 | // // 如果是取消点赞
27 | // // 删除redis的操作
28 | // likeRedisDb.SRem(videoIdString, userId)
29 | // // 操作数据库
30 | // go model.UpdateLikeVideoByUserId(userId, videoId, actionType)
31 | // return true, nil
32 | // }
33 | //} else {
34 | // // 未查询到数据
35 | // // 如果是点赞
36 | // if actionType == 0 {
37 | // // 添加数据到redis
38 | // likeRedisDb.SAdd(videoIdString, userId)
39 | // // 添加数据到数据库
40 | // go model.InsertLikeData(userId, videoId)
41 | // return true, nil
42 | // } else if actionType == 1 {
43 | // // 如果是取消点赞
44 | // go model.UpdateLikeVideoByUserId(userId, videoId, actionType)
45 | // return true, nil
46 | // }
47 | //}
48 | // redis 操作异常 只操作数据库的做法
49 | // 检查是否存在当前的重复值
50 | isDuplicate, err = model.QueryDuplicateLikeData(userId, videoId)
51 | // 为查询相关数据或者数据查询错误
52 | if err != nil {
53 | return false, dataSourceErr
54 | }
55 | // 包含相关数据
56 | if isDuplicate {
57 | // 包含相关数据-更新数据
58 | err = model.UpdateLikeVideoByUserId(userId, videoId, actionType)
59 | if err != nil {
60 | return false, dataSourceErr
61 | }
62 | if actionType == 0 {
63 | // 添加redis
64 | likeRedisDb.SAdd(videoIdString, userId)
65 | } else if actionType == 1 {
66 | // 移除redis
67 | likeRedisDb.SRem(videoIdString, userId)
68 | }
69 | } else {
70 | if actionType == 0 {
71 | // 插入数据
72 | // 先检查视频的id是否存在
73 | IsExist, err := model.QueryIsExistVideoId(videoId)
74 | if IsExist != true {
75 | return false, errors.New("视频不存在")
76 | }
77 | _, err = model.InsertLikeData(userId, videoId)
78 | if err != nil {
79 | return false, dataSourceErr
80 | }
81 | // 添加到redis
82 | likeRedisDb.SAdd(videoIdString, userId)
83 | }
84 | }
85 | return true, err
86 | }
87 |
88 | // 用户喜欢列表服务
89 |
90 | func UserFavoriteListService(userId int64) ([]model.Video, error) {
91 | // 根据用户查询点赞的视频
92 | tableVideos, err := model.QueryVideoByUserId(userId)
93 | if err != nil {
94 | return nil, dataSourceErr
95 | }
96 | videos, err := packageVideos(tableVideos, -1)
97 | if err != nil {
98 | return nil, err
99 | }
100 | return videos, nil
101 | }
102 |
--------------------------------------------------------------------------------
/go/service/messageService.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "encoding/json"
5 | "errors"
6 | "github.com/go-redis/redis"
7 | "strconv"
8 | "sync"
9 | "tiktok/go/config"
10 | "tiktok/go/model"
11 | "tiktok/go/util"
12 | "time"
13 | )
14 |
15 | // 定义键值对维护消息记录 用户的Id->用户目前的消息记录索引
16 | // var userCommentIndex = make(map[int64]int64)
17 | var userCommentIndex sync.Map
18 |
19 | // 定义键值对维护消息记录 用户的Id->用户目前的消息记录最大值
20 | // var userMessageMaxIndex = make(map[int64]int64)
21 | var userMessageMaxIndex sync.Map
22 |
23 | var NowTime string
24 |
25 | func FriendListService(userId int64) ([]model.FriendUser, error) {
26 | var FriendUsers []model.FriendUser
27 | var err error
28 | FriendUsers, err = PackageFriendLists(userId)
29 | if err != nil {
30 | return nil, err
31 | }
32 | return FriendUsers, nil
33 | }
34 |
35 | func MessageService() {
36 |
37 | }
38 |
39 | // 通过userId查询粉丝的数据,再包装加入消息
40 |
41 | func PackageFriendLists(userId int64) ([]model.FriendUser, error) {
42 | var FriendLists []model.FriendUser
43 | var message string
44 | var msgType int8
45 | userInfos, err := MutualFollowListService(userId)
46 | if err != nil {
47 | return nil, err
48 | }
49 | for _, userInfo := range userInfos {
50 | // 查询Message和MsgType
51 | message, msgType, err = model.QueryNewestMessageByUserIdAndToUserID(userId, userInfo.Id)
52 | if err != nil {
53 | return nil, err
54 | }
55 | userInfo.AvatarUrl = config.MockAvatarUrl
56 | FriendLists = append(FriendLists, model.FriendUser{
57 | UserInfo: userInfo,
58 | Message: message,
59 | MsgType: msgType,
60 | })
61 | }
62 |
63 | return FriendLists, nil
64 | }
65 |
66 | // 包装单个请求
67 |
68 | func PackageFriendList(userInfo model.FriendUser) (model.UserInfo, error) {
69 | // test
70 | userInfo.AvatarUrl = "https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/12640/20230206171653.png"
71 | return model.UserInfo{}, nil
72 | }
73 |
74 | func MessageChatService(userId int64, toUserId int64, preMsgTime int64) ([]model.Message, error) {
75 | var messages []model.Message
76 | var err error
77 | // 第一次查询
78 | if preMsgTime == 0 {
79 | // 第一次使用
80 | // 查询userid和toUserId的表将全部内容返回
81 | messages, err = model.QueryMessageByUserIdAndToUserId(userId, toUserId)
82 | if err != nil {
83 | return nil, err
84 | }
85 | // 添加所有数据进入redis
86 | //AllMessageListAddRedis(userId, toUserId, messages)
87 | return messages, err
88 | }
89 | // 不是第一次查询 查询redis
90 | messages, err = ParseAllMessageListFromRedis(userId, toUserId, preMsgTime)
91 | if err != nil {
92 | return nil, err
93 | }
94 |
95 | return messages, nil
96 | }
97 |
98 | func MessageActionService(userId int64, toUserId int64, content string) (bool, error) {
99 | // 敏感词替换
100 | content, _ = util.SensitiveWordsFilter(content)
101 |
102 | // 添加数据进入redis
103 | MessageActionRedis(userId, toUserId, content)
104 | // 添加数据库
105 | pass, err := model.InsertMessage(userId, toUserId, content)
106 | if err != nil {
107 | return false, dataSourceErr
108 | }
109 | if !pass {
110 | return false, errors.New("发送消息失败")
111 | }
112 | return true, nil
113 | }
114 |
115 | func MessageActionRedis(userId int64, toUserId int64, content string) error {
116 | timeUnix := time.Now().Unix()
117 | message := model.Message{
118 | UserId: userId,
119 | ToUserId: toUserId,
120 | Content: content,
121 | IsWithdraw: 0,
122 | CreateTime: timeUnix,
123 | }
124 | // 序列化
125 | bytes, err := json.Marshal(message)
126 | if err != nil {
127 | return err
128 | }
129 | var messageRedisName = strconv.FormatInt(userId, 10) + "-" + strconv.FormatInt(toUserId, 10)
130 | // 添加到redis中hash
131 | messageRedisDb.RPush(messageRedisName, bytes)
132 | return nil
133 | }
134 |
135 | func AllMessageListAddRedis(userId int64, toUserId int64, messages []model.Message) {
136 | var messageRedisName = strconv.FormatInt(userId, 10) + "-" + strconv.FormatInt(toUserId, 10)
137 | // 添加到redis中hash
138 | for _, message := range messages {
139 | bytes, _ := json.Marshal(message)
140 | messageRedisDb.ZAdd(messageRedisName, redis.Z{
141 | Score: float64(message.CreateTime),
142 | Member: bytes,
143 | })
144 | }
145 | }
146 |
147 | // 查询时间戳之前的记录 并删除
148 |
149 | func ParseAllMessageListFromRedis(userId int64, toUserId int64, msgTime int64) ([]model.Message, error) {
150 | var messages []model.Message
151 | var messageRedisName = strconv.FormatInt(userId, 10) + "-" + strconv.FormatInt(toUserId, 10)
152 | for {
153 | bytes, _ := messageRedisDb.LPop(messageRedisName).Result()
154 | if bytes == "" {
155 | break
156 | }
157 | message := model.Message{}
158 | json.Unmarshal([]byte(bytes), &message)
159 | if message.IsWithdraw != 0 || message.CreateTime >= msgTime {
160 | continue
161 | }
162 | messages = append(messages, message)
163 | }
164 | return messages, nil
165 | }
166 |
--------------------------------------------------------------------------------
/go/service/readme.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WenTesla/tiktok/140f0d6625698620d47a6d6badced4e425377a14/go/service/readme.md
--------------------------------------------------------------------------------
/go/service/userService.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "crypto/md5"
5 | "encoding/hex"
6 | "errors"
7 | "log"
8 | "tiktok/go/config"
9 | "tiktok/go/middle/jwt"
10 |
11 | // "log"
12 | "regexp"
13 | "tiktok/go/model"
14 | )
15 |
16 | // SALT 盐值
17 | const SALT = "TikTok"
18 |
19 | // email verify
20 | func VerifyEmailFormat(email string) bool {
21 | //pattern := `\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*` //匹配电子邮箱
22 | pattern := `^[0-9a-z][_.0-9a-z-]{0,31}@([0-9a-z][0-9a-z-]{0,30}[0-9a-z]\.){1,4}[a-z]{2,4}$`
23 | reg := regexp.MustCompile(pattern)
24 | return reg.MatchString(email)
25 | }
26 |
27 | // mobile verify
28 | func VerifyMobileFormat(mobileNum string) bool {
29 | regular := "^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|166|198|199|(147))\\d{8}$"
30 | reg := regexp.MustCompile(regular)
31 | return reg.MatchString(mobileNum)
32 | }
33 |
34 | // GenerateTokenByName 根据name生成token
35 | func GenerateTokenByName(username string) (string, error) {
36 | user, _ := model.GetUserByName(username)
37 | token := jwt.SignToken(user)
38 | return token, nil
39 | }
40 |
41 | // Encryption md5加盐加密
42 | func Encryption(password string) string {
43 | password += SALT
44 | hash := md5.New()
45 | hash.Write([]byte(password))
46 | hash_password := hex.EncodeToString(hash.Sum(nil))
47 | return hash_password
48 | }
49 |
50 | // RegisterService 注册服务
51 | func RegisterService(username string, password string) (int64, error) {
52 | log.Println(username, "---", password)
53 |
54 | // 查表,是否存在id
55 | user, err := model.GetUserByName(username)
56 | if err != nil {
57 | return 0, errors.New("数据库查询错误")
58 | }
59 | if username == user.Name {
60 | return 0, errors.New("用户名已经存在")
61 | }
62 | // 插入
63 | user, _ = model.InsertUser(username, password)
64 | return user.Id, nil
65 | }
66 |
67 | // LoginService 登录服务
68 | func LoginService(username string, password string) (int64, error) {
69 |
70 | user, err := model.GetUserByName(username)
71 | if err != nil {
72 | return 0, dataSourceErr
73 | }
74 | if username != user.Name {
75 | return 0, errors.New("用户名不存在")
76 | }
77 | if password != user.Password {
78 | return 0, errors.New("用户或密码不正确")
79 | }
80 |
81 | return user.Id, nil
82 | }
83 |
84 | // 用户服务 先封装小的,再封装大的 根据用户Id查询用户的具体信息
85 |
86 | func UserService(Id int64) (model.UserInfo, error) {
87 | user, err := model.GetUserById(Id)
88 | if err != nil {
89 | return model.UserInfo{}, dataSourceErr
90 | }
91 | // 脱密
92 | user.Password = ""
93 | // 查询自己的关注数目
94 | followingCount, _ := model.GetFollowingById(Id)
95 | // 查询自己的粉丝
96 | fanCount, err := model.GetFansById(Id)
97 | // 查询点赞数量
98 | favoriteCount, err := model.QueryFavoriteCountByUserId(Id)
99 | if err != nil {
100 | return model.UserInfo{}, dataSourceErr
101 | }
102 | // 查询作品的数量
103 | workCount, err := model.QueryWorkCountByUserId(Id)
104 | if err != nil {
105 | return model.UserInfo{}, dataSourceErr
106 | }
107 | // 查询获赞数量
108 | totalFavorited, err := model.QueryTotalFavorited(Id)
109 | if err != nil {
110 | return model.UserInfo{}, dataSourceErr
111 | }
112 | // 以下为假数据 -avator
113 |
114 | // 关注一定为false
115 | userInfo := model.UserInfo{
116 | Id: user.Id,
117 | Name: user.Name,
118 | FollowCount: followingCount,
119 | FollowerCount: fanCount,
120 | IsFollow: false,
121 | AvatarUrl: config.MockAvatarUrl,
122 | TotalFavorited: totalFavorited,
123 | WorkCount: workCount,
124 | FavoriteCount: favoriteCount,
125 | }
126 | return userInfo, nil
127 | }
128 |
129 | // 用户服务 先封装小的,再封装大的 此时为登录状态,需要查询是否登录
130 |
131 | func UserInfoService(Id int64, userId int64) (model.UserInfo, error) {
132 | user, err := model.GetUserById(Id)
133 | if err != nil {
134 | return model.UserInfo{}, err
135 | }
136 | user.Password = ""
137 | // 查询关注数目
138 | followingCount, _ := model.GetFollowingById(Id)
139 | // 查询粉丝
140 | fanCount, err := model.GetFansById(Id)
141 | if err != nil {
142 | return model.UserInfo{}, dataSourceErr
143 | }
144 | // 查询是否关注
145 | isFollow, err := model.QueryIsFollow(userId, Id)
146 | if err != nil {
147 | return model.UserInfo{}, dataSourceErr
148 | }
149 | // 查询作品数量
150 | favoriteCount, err := model.QueryFavoriteCountByUserId(Id)
151 | if err != nil {
152 | return model.UserInfo{}, dataSourceErr
153 | }
154 | // 查询点赞数量
155 | workCount, err := model.QueryWorkCountByUserId(Id)
156 | if err != nil {
157 | return model.UserInfo{}, dataSourceErr
158 | }
159 | // 查询获赞数量
160 | totalFavorited, err := model.QueryTotalFavorited(Id)
161 | if err != nil {
162 | return model.UserInfo{}, dataSourceErr
163 | }
164 | userInfo := model.UserInfo{
165 | Id: user.Id,
166 | Name: user.Name,
167 | FollowCount: followingCount,
168 | FollowerCount: fanCount,
169 | IsFollow: isFollow,
170 | AvatarUrl: config.CosUrl,
171 | TotalFavorited: totalFavorited,
172 | WorkCount: workCount,
173 | FavoriteCount: favoriteCount,
174 | }
175 | return userInfo, nil
176 | }
177 |
178 | // 脱密后的信息
179 |
180 | func SimpleUserService(Id int64, userId int64) (model.UserInfo, error) {
181 | user, err := model.GetUserById(Id)
182 | if err != nil {
183 | return model.UserInfo{}, dataSourceErr
184 | }
185 | user.Password = ""
186 | // 关注一定为true
187 | userInfo := model.UserInfo{
188 | Id: user.Id,
189 | Name: user.Name,
190 | FollowCount: 0,
191 | FollowerCount: 0,
192 | IsFollow: false,
193 | }
194 | return userInfo, nil
195 | }
196 |
--------------------------------------------------------------------------------
/go/service/videoService.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "errors"
5 | "github.com/tencentyun/cos-go-sdk-v5"
6 | "golang.org/x/net/context"
7 | "log"
8 | "mime/multipart"
9 | "net/http"
10 | "net/url"
11 | "os"
12 | "strconv"
13 | "strings"
14 | "tiktok/go/config"
15 | "tiktok/go/model"
16 | "tiktok/go/util"
17 | "time"
18 | )
19 |
20 | var nowTime string
21 |
22 | var newFileName string
23 |
24 | // 通过传入时间戳,当前用户的id,返回对应的视频数组,以及视频数组中最早的发布时间
25 |
26 | func VideoStreamService(lastTime time.Time, userId int64) ([]model.Video, error) {
27 | tableVideos, err := model.GetVideoByLastTime(lastTime)
28 | if err != nil {
29 | log.Printf("失败 %v", err)
30 | util.LogError(err.Error())
31 | return nil, dataSourceErr
32 | }
33 | log.Printf("获取成功")
34 | videos, err := packageVideos(tableVideos, userId)
35 | if err != nil {
36 | return nil, err
37 | }
38 |
39 | return videos, nil
40 | }
41 | func VideoInfoByUserId(id int) ([]model.Video, error) {
42 | tableVideos, err := model.GetVideoByUserId(id)
43 | if err != nil {
44 | log.Printf("失败%v", err)
45 | util.LogError(err.Error())
46 | return nil, dataSourceErr
47 | }
48 | videos, err := packageVideos(tableVideos, -1)
49 | if err != nil {
50 | return nil, err
51 | }
52 | return videos, nil
53 | }
54 |
55 | // 包装视频流,填入内容如下
56 | //
57 | // author
58 | // "favorite_count": 0,
59 | // "comment_count": 0,
60 | // "is_favorite": true,
61 | //
62 | // user
63 | func packageVideos(tableVideos []model.TableVideo, userId int64) ([]model.Video, error) {
64 | // 创建video模型
65 | videos := make([]model.Video, 0, config.VideoCount)
66 | if userId == -1 {
67 | // 填入author
68 | for _, tableVideo := range tableVideos {
69 | video, err := packageVideo(&tableVideo)
70 | if err != nil {
71 | return nil, err
72 | }
73 | videos = append(videos, video)
74 | }
75 | } else {
76 | // 填入author
77 | for _, tableVideo := range tableVideos {
78 | video, err := packageVideoWithUserId(&tableVideo, userId)
79 | if err != nil {
80 | return nil, err
81 | }
82 | videos = append(videos, video)
83 | }
84 | }
85 |
86 | return videos, nil
87 | }
88 |
89 | // 包装简单的视频列表
90 |
91 | func packageSimpleVideos(tableVideos []model.TableVideo, userId int64) ([]model.Video, error) {
92 | // 创建video模型
93 | videos := make([]model.Video, 0, config.VideoCount)
94 | if userId == -1 {
95 | // 填入author
96 | for _, tableVideo := range tableVideos {
97 | video, err := PackSimpleVideoService(&tableVideo)
98 | if err != nil {
99 | return nil, err
100 | }
101 | videos = append(videos, video)
102 | }
103 | } else {
104 | // 填入author
105 | for _, tableVideo := range tableVideos {
106 | video, err := packageVideoWithUserId(&tableVideo, userId)
107 | if err != nil {
108 | return nil, err
109 | }
110 | videos = append(videos, video)
111 | }
112 | }
113 |
114 | return videos, nil
115 | }
116 |
117 | // 包装单个视频,不返回是否关注的信息-即未登录状态的信息
118 |
119 | func packageVideo(tableVideo *model.TableVideo) (model.Video, error) {
120 | // 创建video单例
121 | video := model.Video{}
122 | // 获取作者信息
123 | userInfo, err := UserService(tableVideo.AuthorId)
124 | if err != nil {
125 | return model.Video{}, err
126 | }
127 | log.Printf("%v", userInfo)
128 | //video.Author=user
129 | video.Author = userInfo
130 | // 填充Videos的
131 | video.ID = tableVideo.Id
132 | video.PlayURL = tableVideo.PlayUrl
133 | video.CoverURL = tableVideo.CoverUrl
134 | video.Title = tableVideo.Title
135 | // 获取 favorite_count
136 | // 先查询redis
137 | favoriteCount, err := likeRedisDb.SCard(strconv.FormatInt(video.ID, 10)).Result()
138 | if err != nil {
139 | //// 出错查询数据库
140 | favoriteCount, err = model.QueryLikeByVideoId(tableVideo.Id)
141 | if err != nil {
142 | return video, dataSourceErr
143 | }
144 | }
145 | video.FavoriteCount = favoriteCount
146 | // 获取"commentCount"
147 | commentCount, err := model.QueryCommentCountByVideoId(tableVideo.Id)
148 | if err != nil {
149 | return video, dataSourceErr
150 | }
151 | video.CommentCount = commentCount
152 | video.IsFavorite = false
153 | return video, nil
154 | }
155 |
156 | // 包装单个视频,返回是否关注的信息
157 |
158 | func packageVideoWithUserId(tableVideo *model.TableVideo, id int64) (model.Video, error) {
159 | // 创建video单例
160 | video := model.Video{}
161 | // 获取作者信息
162 | userInfo, err := UserInfoService(tableVideo.AuthorId, id)
163 | if err != nil {
164 | return model.Video{}, err
165 | }
166 | log.Printf("%v", userInfo)
167 | //video.Author=user
168 | video.Author = userInfo
169 | // 填充Videos的
170 | video.ID = tableVideo.Id
171 | video.PlayURL = tableVideo.PlayUrl
172 | video.CoverURL = tableVideo.CoverUrl
173 | video.Title = tableVideo.Title
174 | // 获取 favorite_count
175 | // 先查询redis
176 | favoriteCount, err := likeRedisDb.SCard(strconv.FormatInt(video.ID, 10)).Result()
177 | if err != nil {
178 | //// 出错查询数据库
179 | favoriteCount, err = model.QueryLikeByVideoId(tableVideo.Id)
180 | if err != nil {
181 | return video, dataSourceErr
182 | }
183 | }
184 | video.FavoriteCount = favoriteCount
185 | // 获取"commentCount"
186 | commentCount, err := model.QueryCommentCountByVideoId(tableVideo.Id)
187 | if err != nil {
188 | return video, dataSourceErr
189 | }
190 | video.CommentCount = commentCount
191 | // 获取是否点赞 先查询redis
192 | is_favorite, err := likeRedisDb.SIsMember(strconv.FormatInt(video.ID, 10), id).Result()
193 | // redis查询失败查询数据库
194 | if err != nil {
195 | is_favorite, err = model.QueryIsLike(id, tableVideo.Id)
196 | if err != nil {
197 | return video, err
198 | }
199 | }
200 | video.IsFavorite = is_favorite
201 | return video, nil
202 | }
203 |
204 | // 包装最简单的视频信息 作者只包含
205 |
206 | func PackSimpleVideoService(tableVideo *model.TableVideo) (model.Video, error) {
207 | // 创建video单例
208 | video := model.Video{}
209 | // 获取作者信息
210 | userInfo, err := SimpleUserService(tableVideo.AuthorId, -1)
211 | if err != nil {
212 | return model.Video{}, err
213 | }
214 | log.Printf("%v", userInfo)
215 | //video.Author=user
216 | video.Author = userInfo
217 | // 填充Videos的
218 | video.ID = tableVideo.Id
219 | video.PlayURL = tableVideo.PlayUrl
220 | video.CoverURL = tableVideo.CoverUrl
221 | video.Title = tableVideo.Title
222 | video.IsFavorite = false
223 | return video, nil
224 | }
225 |
226 | // PublishVideo 可以优化
227 |
228 | func PublishVideoService(file *multipart.FileHeader, userId int64, title string) error {
229 | src, err := file.Open()
230 | if err != nil {
231 | return errors.New("不能打开文件")
232 | }
233 | defer src.Close()
234 | // 获取视频文件名称
235 | nowTime = strconv.FormatInt(time.Now().Unix(), 10)
236 | // 转换视频名称
237 | newFileName, err = fileNameToTimeCurrentFileName(file.Filename, nowTime)
238 | // 上传到cos
239 | err = publishVideoByTencentCos(src, newFileName)
240 | if err != nil {
241 | return err
242 | }
243 | // 提取封面url
244 | play_cover, err := parseFileName(newFileName)
245 | if err != nil {
246 | return err
247 | }
248 | // 添加数据库
249 | err = model.InsertVideo(userId, config.CosUrl+"/"+newFileName, config.CosUrl+"/"+play_cover, title)
250 | if err != nil {
251 | return dataSourceErr
252 | }
253 | return nil
254 | }
255 |
256 | // publishVideoByTencentCos
257 | func publishVideoByTencentCos(file multipart.File, fileName string) error {
258 | // 将 examplebucket-1250000000 和 COS_REGION 修改为真实的信息
259 | // 存储桶名称,由 bucketname-appid 组成,appid 必须填入,可以在 COS 控制台查看存储桶名称。https://console.cloud.tencent.com/cos5/bucket
260 | // COS_REGION 可以在控制台查看,https://console.cloud.tencent.com/cos5/bucket, 关于地域的详情见 https://cloud.tencent.com/document/product/436/6224
261 | u, _ := url.Parse(config.CosUrl)
262 | b := &cos.BaseURL{BucketURL: u}
263 | c := cos.NewClient(b, &http.Client{
264 | Transport: &cos.AuthorizationTransport{
265 | SecretID: os.Getenv(config.SecretId), // 用户的 SecretId,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参考 https://cloud.tencent.com/document/product/598/37140
266 | SecretKey: os.Getenv(config.SecretKey), // 用户的 SecretKey,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参考 https://cloud.tencent.com/document/product/598/37140
267 | },
268 | })
269 | // 对象键(Key)是对象在存储桶中的唯一标识。
270 | // 例如,在对象的访问域名 `examplebucket-1250000000.cos.COS_REGION.myqcloud.com/test/objectPut.go` 中,对象键为 test/objectPut.go
271 | _, err := c.Object.Put(context.Background(), fileName, file, nil)
272 | if err != nil {
273 | return errors.New("文件上传失败")
274 | }
275 | //os.Open()
276 | return nil
277 | }
278 |
279 | // parseFileName parseFileName 解析文件名称,去除文件后缀并加上文件格式jpg
280 | func parseFileName(fileName string) (string, error) {
281 | //
282 | lastIndex := strings.LastIndex(fileName, ".")
283 | if lastIndex == -1 {
284 | return "", errors.New("解析错误")
285 | }
286 | replaced := fileName[lastIndex:]
287 | // 判断文件后缀是否为要求的后缀
288 | if replaced != ".mp4" {
289 | return "", errors.New("文件格式不符合要求")
290 | }
291 | return strings.Replace(fileName, replaced, config.ReplaceSuffix, 1), nil
292 | }
293 |
294 | // fileNameToTimeCurrentFileName 将文件名称转化为时间戳并返回
295 | func fileNameToTimeCurrentFileName(oldFileName string, newFileName string) (string, error) {
296 | // 提取文件后缀
297 | lastIndex := strings.LastIndex(oldFileName, ".")
298 | if lastIndex == -1 {
299 | return "", errors.New("文件名称转换错误")
300 | }
301 | replaced := oldFileName[:lastIndex]
302 | return strings.Replace(oldFileName, replaced, newFileName, 1), nil
303 | }
304 |
305 | // CheckFile -todo 检查文件内容的合法性
306 | func CheckFile() {
307 | // 检查文件后缀
308 | //path.Ext()
309 | }
310 |
--------------------------------------------------------------------------------
/go/util/checkFile.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "bytes"
5 | "encoding/hex"
6 | "strconv"
7 | "strings"
8 | "sync"
9 | )
10 |
11 | var fileTypeMap sync.Map
12 |
13 | func init() {
14 | fileTypeMap.Store("ffd8ffe000104a464946", "jpg") //JPEG (jpg)
15 | fileTypeMap.Store("89504e470d0a1a0a0000", "png") //PNG (png)
16 | fileTypeMap.Store("47494638396126026f01", "gif") //GIF (gif)
17 | fileTypeMap.Store("49492a00227105008037", "tif") //TIFF (tif)
18 | fileTypeMap.Store("424d228c010000000000", "bmp") //16色位图(bmp)
19 | fileTypeMap.Store("424d8240090000000000", "bmp") //24位位图(bmp)
20 | fileTypeMap.Store("424d8e1b030000000000", "bmp") //256色位图(bmp)
21 | fileTypeMap.Store("41433130313500000000", "dwg") //CAD (dwg)
22 | fileTypeMap.Store("3c21444f435459504520", "html") //HTML (html) 3c68746d6c3e0 3c68746d6c3e0
23 | fileTypeMap.Store("3c68746d6c3e0", "html") //HTML (html) 3c68746d6c3e0 3c68746d6c3e0
24 | fileTypeMap.Store("3c21646f637479706520", "htm") //HTM (htm)
25 | fileTypeMap.Store("48544d4c207b0d0a0942", "css") //css
26 | fileTypeMap.Store("696b2e71623d696b2e71", "js") //js
27 | fileTypeMap.Store("7b5c727466315c616e73", "rtf") //Rich Text Format (rtf)
28 | fileTypeMap.Store("38425053000100000000", "psd") //Photoshop (psd)
29 | fileTypeMap.Store("46726f6d3a203d3f6762", "eml") //Email [Outlook Express 6] (eml)
30 | fileTypeMap.Store("d0cf11e0a1b11ae10000", "doc") //MS Excel 注意:word、msi 和 excel的文件头一样
31 | fileTypeMap.Store("d0cf11e0a1b11ae10000", "vsd") //Visio 绘图
32 | fileTypeMap.Store("5374616E64617264204A", "mdb") //MS Access (mdb)
33 | fileTypeMap.Store("252150532D41646F6265", "ps")
34 | fileTypeMap.Store("255044462d312e350d0a", "pdf") //Adobe Acrobat (pdf)
35 | fileTypeMap.Store("2e524d46000000120001", "rmvb") //rmvb/rm相同
36 | fileTypeMap.Store("464c5601050000000900", "flv") //flv与f4v相同
37 | fileTypeMap.Store("00000020667479706d70", "mp4") // 0000001c667479706d70 00 00 00 18 66 74 79 70 69 73 6F 6D
38 | fileTypeMap.Store("49443303000000002176", "mp3")
39 | fileTypeMap.Store("000001ba210001000180", "mpg") //
40 | fileTypeMap.Store("3026b2758e66cf11a6d9", "wmv") //wmv与asf相同
41 | fileTypeMap.Store("52494646e27807005741", "wav") //Wave (wav)
42 | fileTypeMap.Store("52494646d07d60074156", "avi")
43 | fileTypeMap.Store("4d546864000000060001", "mid") //MIDI (mid)
44 | fileTypeMap.Store("504b0304140000000800", "zip")
45 | fileTypeMap.Store("526172211a0700cf9073", "rar")
46 | fileTypeMap.Store("235468697320636f6e66", "ini")
47 | fileTypeMap.Store("504b03040a0000000000", "jar")
48 | fileTypeMap.Store("4d5a9000030000000400", "exe") //可执行文件
49 | fileTypeMap.Store("3c25402070616765206c", "jsp") //jsp文件
50 | fileTypeMap.Store("4d616e69666573742d56", "mf") //MF文件
51 | fileTypeMap.Store("3c3f786d6c2076657273", "xml") //xml文件
52 | fileTypeMap.Store("494e5345525420494e54", "sql") //xml文件
53 | fileTypeMap.Store("7061636b616765207765", "java") //java文件
54 | fileTypeMap.Store("406563686f206f66660d", "bat") //bat文件
55 | fileTypeMap.Store("1f8b0800000000000000", "gz") //gz文件
56 | fileTypeMap.Store("6c6f67346a2e726f6f74", "properties") //bat文件
57 | fileTypeMap.Store("cafebabe0000002e0041", "class") //bat文件
58 | fileTypeMap.Store("49545346030000006000", "chm") //bat文件
59 | fileTypeMap.Store("04000000010000001300", "mxp") //bat文件
60 | fileTypeMap.Store("504b0304140006000800", "docx") //docx文件
61 | fileTypeMap.Store("d0cf11e0a1b11ae10000", "wps") //WPS文字wps、表格et、演示dps都是一样的
62 | fileTypeMap.Store("6431303a637265617465", "torrent")
63 | fileTypeMap.Store("6D6F6F76", "mov") //Quicktime (mov)
64 | fileTypeMap.Store("FF575043", "wpd") //WordPerfect (wpd)
65 | fileTypeMap.Store("CFAD12FEC5FD746F", "dbx") //Outlook Express (dbx)
66 | fileTypeMap.Store("2142444E", "pst") //Outlook (pst)
67 | fileTypeMap.Store("AC9EBD8F", "qdf") //Quicken (qdf)
68 | fileTypeMap.Store("E3828596", "pwl") //Windows Password (pwl)
69 | fileTypeMap.Store("2E7261FD", "ram") //Real Audio (ram)
70 | }
71 |
72 | // 获取前面结果字节的二进制
73 | func bytesToHexString(src []byte) string {
74 | res := bytes.Buffer{}
75 | if src == nil || len(src) <= 0 {
76 | return ""
77 | }
78 | temp := make([]byte, 0)
79 | for _, v := range src {
80 | sub := v & 0xFF
81 | hv := hex.EncodeToString(append(temp, sub))
82 | if len(hv) < 2 {
83 | res.WriteString(strconv.FormatInt(int64(0), 10))
84 | }
85 | res.WriteString(hv)
86 | }
87 | return res.String()
88 | }
89 |
90 | // 用文件前面几个字节来判断
91 | // fSrc: 文件字节流(就用前面几个字节)
92 | func GetFileType(fSrc []byte) string {
93 | var fileType string
94 | fileCode := bytesToHexString(fSrc)
95 | // 0000001c667479706d70
96 | fileTypeMap.Range(func(key, value interface{}) bool {
97 | k := key.(string)
98 | v := value.(string)
99 | // 如果包含前后缀 0000001c667479706d70
100 | if strings.HasPrefix(fileCode, strings.ToLower(k)) ||
101 | strings.HasPrefix(k, strings.ToLower(fileCode)) {
102 | fileType = v
103 | return false
104 | }
105 | return true
106 | })
107 | return fileType
108 | }
109 |
--------------------------------------------------------------------------------
/go/util/checkFile_test.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "io/ioutil"
5 | "log"
6 | "os"
7 | "testing"
8 | )
9 |
10 | func TestGetFileType(t *testing.T) {
11 | //f, err := os.Open("C:\\Users\\Administrator\\Desktop\\api.html")
12 | f, err := os.Open("C:\\Users\\WenTe\\Desktop\\video\\664bc4e86cfae46338056e7ec016555e.mp4")
13 | if err != nil {
14 | t.Logf("open error: %v", err)
15 | }
16 | fSrc, err := ioutil.ReadAll(f)
17 | fileType := GetFileType(fSrc[:10])
18 | log.Println(fileType)
19 | }
20 |
--------------------------------------------------------------------------------
/go/util/filter.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "regexp"
7 | "strings"
8 | )
9 |
10 | const SensitiveWordFilePath = "go/util/key.txt"
11 |
12 | var sensitiveWords []string
13 |
14 | //用于过滤
15 |
16 | var wordReg *regexp.Regexp
17 |
18 | func InitSensitiveFilter() {
19 | //sensitiveWords := []string{
20 | // "傻逼",
21 | // "傻叉",
22 | // "垃圾",
23 | // "妈的",
24 | // "sb",
25 | //}
26 | //text := "什么垃圾打野,傻逼一样,叫你来开龙不来,sb"
27 | //
28 | //// 构造正则匹配字符
29 | //regStr := strings.Join(sensitiveWords, "|")
30 | //println("regStr -> ", regStr)
31 | //傻逼|傻叉|垃圾|妈的|sb
32 | //wordReg := regexp.MustCompile(regStr)
33 | //text = wordReg.ReplaceAllString(text, "*")
34 | //
35 | //println("text -> ", text)
36 | //func ReadFile(name string) ([]byte, error) {}
37 | content, err := os.ReadFile(SensitiveWordFilePath)
38 | if err != nil {
39 | LogError(err.Error())
40 | panic(err)
41 | }
42 | s := string(content)
43 | wordReg = regexp.MustCompile(s)
44 | }
45 |
46 | func SensitiveWordsFilter(context string) (string, error) {
47 | test := wordReg.ReplaceAllString(context, "*")
48 | fmt.Printf("%s", test)
49 | return test, nil
50 | }
51 |
52 | func Test() {
53 | sensitiveWords := []string{
54 | "傻逼",
55 | "傻叉",
56 | "垃圾",
57 | "妈的",
58 | "sb",
59 | }
60 | text := "什么垃圾打野,傻逼一样,叫你来开龙不来,sb"
61 | // 构造正则匹配字符
62 | regStr := strings.Join(sensitiveWords, "|")
63 | println("regStr -> ", regStr)
64 | wordReg := regexp.MustCompile(regStr)
65 | text = wordReg.ReplaceAllString(text, "*")
66 |
67 | println("text -> ", text)
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/go/util/filter_test.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestInitSensitiveFilter(t *testing.T) {
9 | InitSensitiveFilter()
10 | }
11 |
12 | func TestInitSensitiveFilter2(t *testing.T) {
13 | InitSensitiveFilter()
14 | filter, _ := SensitiveWordsFilter("傻逼")
15 | fmt.Printf("%s", filter)
16 | }
17 |
18 | func TestTest(t *testing.T) {
19 | Test()
20 | }
21 |
--------------------------------------------------------------------------------
/go/util/log.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "fmt"
5 | "github.com/gin-gonic/gin"
6 | "time"
7 | )
8 |
9 | // 日志插件,封装了一层gin的日志
10 |
11 | var now string
12 |
13 | // Log 普通log
14 | func Log(format string, values ...any) {
15 | now = time.Now().Format("2006/01/02 - 15:04:05")
16 | f := fmt.Sprintf("[DEV] %s %s\n", now, format)
17 | fmt.Fprintf(gin.DefaultWriter, f, values...)
18 | }
19 |
20 | // LogError 带错误信息的log(服务器的错误)
21 | func LogError(ErrorInfo string, values ...any) {
22 | now = time.Now().Format("2006/01/02 - 15:04:05")
23 | f := fmt.Sprintf("[DEV] [Error] %s %s %v \n", now, ErrorInfo, values)
24 | fmt.Fprintf(gin.DefaultWriter, f)
25 | }
26 |
27 | // LogFatal 严重的错误
28 | func LogFatal(ErrorInfo string, values ...any) {
29 | now = time.Now().Format("2006/01/02 - 15:04:05")
30 | f := fmt.Sprintf("[DEV] [Fatal] %s %s %v\n", now, ErrorInfo, values)
31 | fmt.Fprintf(gin.DefaultWriter, f)
32 | }
33 |
--------------------------------------------------------------------------------
/go/util/log_test.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import "testing"
4 |
5 | func TestLog(t *testing.T) {
6 | LogFatal("111")
7 | }
8 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # 简易版抖音项目
2 |
3 | ## 项目答辩文档与App地址
4 | [飞书文档](https://xp8kgipb5a.feishu.cn/docx/RPAvdVcqpoc6DzxRPlnc5f3VnMb)
5 | ## 项目演示视频
6 | [青训营演示视频](https://www.bilibili.com/video/BV1uT411S79U/?share_source=copy_web&vd_source=fe55b12bbf1a3c973a095834d9f2ba6d)
7 |
8 | ## 项目启动
9 | ### 建表
10 | resources/initial.sql
11 | resources/insertData.sql
12 |
13 | ### 替换redis地址
14 | config/redis.go
15 |
16 | ### 替换腾讯云oss服务密钥
17 | config/tencent_oss
18 |
19 |
20 | ### 直接启动
21 | ```shell
22 | cd go
23 | ```
24 | ```shell
25 | go run main.go
26 | ```
27 |
28 | ## APP 操作
29 | 设置服务端地址
30 | 为方便测试登录和注册,及修改网络请求的服务器地址,提供了退出登录和高级设置两个能力。
31 | 1. 点击退出登录会自动重启
32 | 2. 在高级设置中可以配置自己的服务端项目的前缀地址,如下配置的http://192.168.1.7:8080
33 | 在app中访问上述某个接口时就会拼接该前缀地址,例如访问 http://192.168.1.7:8080/douyin/feed/ 拉取视频列表
34 | 
35 |
36 | ## 表数据
37 |
38 | ## 使用mvc分层结构
39 | 参考文献 (不懂的可以参考这篇文章)
40 | https://juejin.cn/post/7152299022017888286
41 |
42 |
43 |
44 | ## 版本
45 | * go版本 1.19
46 | * mysql 8.0+
47 | * redis驱动
48 | ## 使用到的框架与依赖
49 | + gin框架
50 | + gorm框架
51 | + mysql驱动
52 | + golang的jwt框架
53 | + 腾讯云的oss存储(设置了工作流用于截取视频的第一帧(.jpg)并储存在相同的桶中)文献: https://juejin.cn/post/7195857732846567485
54 | + redis驱动
55 | ## 已知错误
56 |
57 |
58 | ### 日志(resources/gin.log)
59 | ### 注意
60 | * 启动服务会自动生成日志文件
61 | * 每次重启会覆盖日志
62 | * 同时封装了log日志
63 |
64 |
65 | ## 待优化地方:
66 | * 建表为了省事使用自增Id,安全性缺乏 (懒得优化了)
67 | * 上传文件相同文件名称的处理(目前将文件名改为时间戳后处理,好像也可以)
68 | * 未设置读写分离
69 | * 一些地方可以用到指针(javer 的问题)
70 | * 服务更加细致,只返回对应的必要的json数据
71 | * 定时任务
72 |
73 |
74 | ## 注意
75 | * **目前上传文件接口只支持mp4格式**
76 |
77 | ## to-do
78 | * 使用docker部署
79 | * 自动执行sql语句
80 |
81 | ### 获奖证书
82 | 
83 |
84 | 一个人完成,只拿到了结营证书,拿到优秀学员证书都是使用微服务
85 |
86 | ### 作者:
87 | bowen https://www.github.com/WenTesla
88 | ### 最后修改时间
89 | 2023/5/18
90 |
--------------------------------------------------------------------------------
/resources/initial.sql:
--------------------------------------------------------------------------------
1 | -- create
2 | -- database tiktok;
3 |
4 | use
5 | tiktok;
6 | -- ----------------------------
7 | -- Table structure for users
8 | -- ----------------------------
9 | DROP TABLE IF EXISTS `users`;
10 | CREATE TABLE `users`
11 | (
12 | `id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户id,自增主键',
13 | `name` varchar(255) NOT NULL COMMENT '用户名',
14 | `password` varchar(255) NOT NULL COMMENT '用户密码',
15 | PRIMARY KEY (`id`),
16 | KEY `name-password` (`name`,`password`) USING BTREE COMMENT '用户名-密码索引\r\n'
17 | ) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8mb3 COMMENT='用户表';
18 |
19 | -- ----------------------------
20 | -- Table structure for videos
21 | -- ----------------------------
22 | DROP TABLE IF EXISTS `videos`;
23 | CREATE TABLE `videos`
24 | (
25 | `id` bigint NOT NULL AUTO_INCREMENT COMMENT '自增主键,视频唯一id',
26 | `author_id` bigint NOT NULL COMMENT '视频作者id',
27 | `play_url` varchar(255) NOT NULL COMMENT '播放url',
28 | `cover_url` varchar(255) NOT NULL COMMENT '封面的url',
29 | `title` varchar(255) DEFAULT NULL COMMENT '视频名称',
30 | `publish_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '发布时间戳',
31 | PRIMARY KEY (`id`) USING BTREE,
32 | KEY `publish_time` (`publish_time`) COMMENT '发布时间索引'
33 | ) ENGINE=InnoDB AUTO_INCREMENT=47 DEFAULT CHARSET=utf8mb3 COMMENT='\r\n视频表';
34 |
35 | DROP TABLE IF EXISTS `likes`;
36 | CREATE TABLE `likes`
37 | (
38 | `id` bigint NOT NULL AUTO_INCREMENT COMMENT '自增主键',
39 | `user_id` int DEFAULT NULL COMMENT '点赞用户的id',
40 | `video_id` int DEFAULT NULL COMMENT '视频作者的id',
41 | `is_cancel` tinyint DEFAULT '0' COMMENT '是否点赞 0-点赞 1-未点赞',
42 | `createTime` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
43 | `updateTime` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
44 | PRIMARY KEY (`id`),
45 | KEY `user_id` (`user_id`),
46 | KEY `video_id` (`video_id`),
47 | KEY `user_id_video_id` (`user_id`,`video_id`)
48 | ) ENGINE=InnoDB AUTO_INCREMENT=56 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='点赞列表';
49 |
50 |
51 | DROP TABLE IF EXISTS `comments`;
52 | CREATE TABLE `comments`
53 | (
54 | `id` bigint NOT NULL AUTO_INCREMENT COMMENT '自增id',
55 | `user_id` int DEFAULT NULL COMMENT '用户的id',
56 | `video_id` int DEFAULT NULL COMMENT '视频的id',
57 | `text` varchar(255) DEFAULT NULL COMMENT '评论的内容',
58 | `is_cancel` int DEFAULT '0' COMMENT '是否取消评论',
59 | `createTime` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
60 | PRIMARY KEY (`id`),
61 | KEY `user_id` (`user_id`),
62 | KEY `video_id` (`video_id`),
63 | KEY `user_id_video_id` (`user_id`,`video_id`)
64 | ) ENGINE=InnoDB AUTO_INCREMENT=50 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='评论表-用户评论视频';
65 |
66 | DROP TABLE IF EXISTS `follows`;
67 | CREATE TABLE `follows`
68 | (
69 | `id` bigint NOT NULL AUTO_INCREMENT COMMENT '自增id',
70 | `user_id` int DEFAULT NULL COMMENT '用户的id',
71 | `follower_id` int DEFAULT NULL COMMENT '关注的用户',
72 | `cancel` tinyint DEFAULT '0' COMMENT '是否关注',
73 | `createTime` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
74 | PRIMARY KEY (`id`),
75 | KEY `user_id` (`user_id`),
76 | KEY `follower_id` (`follower_id`),
77 | KEY `user_id_follower_id` (`user_id`,`follower_id`)
78 | ) ENGINE=InnoDB AUTO_INCREMENT=46 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户关注列表';
79 |
80 | DROP TABLE IF EXISTS `messages`;
81 | CREATE TABLE `messages`
82 | (
83 | `id` bigint NOT NULL AUTO_INCREMENT COMMENT '自增id',
84 | `user_id` bigint NOT NULL COMMENT '用户的Id',
85 | `to_user_id` bigint NOT NULL COMMENT '接受消息的用户Id',
86 | `content` varchar(256) NOT NULL COMMENT '消息内容',
87 | `is_withdraw` tinyint DEFAULT '0' COMMENT '是否撤回 0-不撤回,1-撤回',
88 | `createTime` bigint DEFAULT NULL COMMENT '时间戳',
89 | PRIMARY KEY (`id`),
90 | KEY `用户索引` (`user_id`) USING BTREE COMMENT '发送信息的用户Id索引',
91 | KEY `接受信息的用户索引` (`to_user_id`) USING BTREE COMMENT '接受用户的用户Id索引'
92 | ) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='消息表';
93 |
--------------------------------------------------------------------------------
/resources/insertData.sql:
--------------------------------------------------------------------------------
1 | INSERT INTO `tiktok`.`users` (`id`, `name`, `password`)
2 | VALUES (3, 'bowenzhang', '77a90868207689664f244ad398a871fc');
3 | INSERT INTO `tiktok`.`users` (`id`, `name`, `password`)
4 | VALUES (4, 'lichangyuan', '77a90868207689664f244ad398a871fc');
5 | INSERT INTO `tiktok`.`users` (`id`, `name`, `password`)
6 | VALUES (11, 'rantong', '77a90868207689664f244ad398a871fc');
7 | INSERT INTO `tiktok`.`users` (`id`, `name`, `password`)
8 | VALUES (5, 'sunshixin', 'de9ae573b41776f624526219666336d2');
9 | INSERT INTO `tiktok`.`users` (`id`, `name`, `password`)
10 | VALUES (6, 'tandonghang', '77a90868207689664f244ad398a871fc');
11 | INSERT INTO `tiktok`.`users` (`id`, `name`, `password`)
12 | VALUES (14, 'test', '77a90868207689664f244ad398a871fc');
13 | INSERT INTO `tiktok`.`users` (`id`, `name`, `password`)
14 | VALUES (15, 'test2', '77a90868207689664f244ad398a871fc');
15 | INSERT INTO `tiktok`.`users` (`id`, `name`, `password`)
16 | VALUES (7, 'tuzhuangzhuang', '77a90868207689664f244ad398a871fc');
17 | INSERT INTO `tiktok`.`users` (`id`, `name`, `password`)
18 | VALUES (1, 'zhangbowen', '77a90868207689664f244ad398a871fc');
19 | INSERT INTO `tiktok`.`users` (`id`, `name`, `password`)
20 | VALUES (2, 'zhangbowen1', '77a90868207689664f244ad398a871fc');
21 | INSERT INTO `tiktok`.`users` (`id`, `name`, `password`)
22 | VALUES (8, 'zhangchangyueyan', '77a90868207689664f244ad398a871fc');
23 | INSERT INTO `tiktok`.`users` (`id`, `name`, `password`)
24 | VALUES (12, '周子豪', '77a90868207689664f244ad398a871fc');
25 | INSERT INTO `tiktok`.`users` (`id`, `name`, `password`)
26 | VALUES (10, '张博文', '77a90868207689664f244ad398a871fc');
27 | INSERT INTO `tiktok`.`users` (`id`, `name`, `password`)
28 | VALUES (17, '张常越岩', '77a90868207689664f244ad398a871fc');
29 | INSERT INTO `tiktok`.`users` (`id`, `name`, `password`)
30 | VALUES (9, '李常远', '77a90868207689664f244ad398a871fc');
31 | INSERT INTO `tiktok`.`users` (`id`, `name`, `password`)
32 | VALUES (13, '邢政', '77a90868207689664f244ad398a871fc');
33 |
34 | INSERT INTO `tiktok`.`videos` (`id`, `author_id`, `play_url`, `cover_url`, `title`, `publish_time`)
35 | VALUES (1, 1, 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/36924f94fdf64cb3e3cfca3956fa6d9c.mp4',
36 | 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/36924f94fdf64cb3e3cfca3956fa6d9c.jpg', 'test',
37 | '2023-02-02 19:49:36');
38 | INSERT INTO `tiktok`.`videos` (`id`, `author_id`, `play_url`, `cover_url`, `title`, `publish_time`)
39 | VALUES (2, 2, 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/664bc4e86cfae46338056e7ec016555e.mp4',
40 | 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/664bc4e86cfae46338056e7ec016555e.jpg', 'test',
41 | '2023-02-02 19:50:58');
42 | INSERT INTO `tiktok`.`videos` (`id`, `author_id`, `play_url`, `cover_url`, `title`, `publish_time`)
43 | VALUES (3, 3, 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/aefa322953ccc87ffe59525ad1b4e1c0.mp4',
44 | 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/aefa322953ccc87ffe59525ad1b4e1c0.jpg', 'test',
45 | '2023-02-02 19:51:42');
46 | INSERT INTO `tiktok`.`videos` (`id`, `author_id`, `play_url`, `cover_url`, `title`, `publish_time`)
47 | VALUES (4, 4, 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/b31512aa2916f3ab484f8a4be569a0fa.mp4',
48 | 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/b31512aa2916f3ab484f8a4be569a0fa.jpg', 'test',
49 | '2023-02-02 19:51:49');
50 | INSERT INTO `tiktok`.`videos` (`id`, `author_id`, `play_url`, `cover_url`, `title`, `publish_time`)
51 | VALUES (5, 5, 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/a1da2b17a124aa88d0134d75d3983b5f.mp4',
52 | 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/a1da2b17a124aa88d0134d75d3983b5f.jpg', 'test',
53 | '2023-02-02 19:51:56');
54 | INSERT INTO `tiktok`.`videos` (`id`, `author_id`, `play_url`, `cover_url`, `title`, `publish_time`)
55 | VALUES (6, 2, 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/dad18708c59804da1f3abb996cb56770.mp4',
56 | 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/dad18708c59804da1f3abb996cb56770.jpg', 'test',
57 | '2023-02-02 19:52:00');
58 | INSERT INTO `tiktok`.`videos` (`id`, `author_id`, `play_url`, `cover_url`, `title`, `publish_time`)
59 | VALUES (7, 1, 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/1675682358.mp4',
60 | 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/1675682358.jpg', 'test', '2023-02-02 19:52:04');
61 | INSERT INTO `tiktok`.`videos` (`id`, `author_id`, `play_url`, `cover_url`, `title`, `publish_time`)
62 | VALUES (8, 2, 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/bea309f6840bee5d95c233616b3f1f34.mp4',
63 | 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/bea309f6840bee5d95c233616b3f1f34.jpg', 'test',
64 | '2023-02-02 19:52:09');
65 | INSERT INTO `tiktok`.`videos` (`id`, `author_id`, `play_url`, `cover_url`, `title`, `publish_time`)
66 | VALUES (9, 3, 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/dbd19a6cba6bcf02027613b4caefdce8.mp4',
67 | 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/dbd19a6cba6bcf02027613b4caefdce8.jpg', 'test',
68 | '2023-02-02 19:52:38');
69 | INSERT INTO `tiktok`.`videos` (`id`, `author_id`, `play_url`, `cover_url`, `title`, `publish_time`)
70 | VALUES (10, 5, 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/b29672e25430abc8eb31daecda52b8cb.mp4',
71 | 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/b29672e25430abc8eb31daecda52b8cb.jpg', 'test',
72 | '2023-02-02 19:52:43');
73 | INSERT INTO `tiktok`.`videos` (`id`, `author_id`, `play_url`, `cover_url`, `title`, `publish_time`)
74 | VALUES (11, 1, 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/1671fadf2ac23fed56996c3dc935ce92.mp4',
75 | 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/1671fadf2ac23fed56996c3dc935ce92.jpg', 'test',
76 | '2023-02-02 19:52:47');
77 | INSERT INTO `tiktok`.`videos` (`id`, `author_id`, `play_url`, `cover_url`, `title`, `publish_time`)
78 | VALUES (12, 11, 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/V30203-123620.mp4',
79 | 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/V30203-123620.jpg', '124578',
80 | '2023-02-03 12:45:58');
81 | INSERT INTO `tiktok`.`videos` (`id`, `author_id`, `play_url`, `cover_url`, `title`, `publish_time`)
82 | VALUES (13, 4, 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/安欣霸凌高启强-哔哩哔哩_302651035.mp4',
83 | 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/安欣霸凌高启强-哔哩哔哩_302651035.jpg', '高启强',
84 | '2023-02-04 18:36:05');
85 | INSERT INTO `tiktok`.`videos` (`id`, `author_id`, `play_url`, `cover_url`, `title`, `publish_time`)
86 | VALUES (14, 9, 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/1675658873.mp4',
87 | 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/1675658873.jpg', '维利维亚',
88 | '2023-02-06 12:47:55');
89 | INSERT INTO `tiktok`.`videos` (`id`, `author_id`, `play_url`, `cover_url`, `title`, `publish_time`)
90 | VALUES (15, 9, 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/1675685888.mp4',
91 | 'https://tiktok-video-1313520634.cos.ap-beijing.myqcloud.com/1675685888.jpg', 'girl', '2023-02-06 20:18:09');
92 |
93 | -- like 表与 redis 缓存交互 这里不插入数据
94 |
95 | INSERT INTO `tiktok`.`follows` (`id`, `user_id`, `follower_id`, `cancel`, `createTime`)
96 | VALUES (34, 1, 9, 0, '2023-02-21 17:16:26');
97 | INSERT INTO `tiktok`.`follows` (`id`, `user_id`, `follower_id`, `cancel`, `createTime`)
98 | VALUES (35, 1, 2, 0, '2023-02-21 17:44:27');
99 | INSERT INTO `tiktok`.`follows` (`id`, `user_id`, `follower_id`, `cancel`, `createTime`)
100 | VALUES (36, 9, 1, 0, '2023-02-21 17:45:20');
101 | INSERT INTO `tiktok`.`follows` (`id`, `user_id`, `follower_id`, `cancel`, `createTime`)
102 | VALUES (37, 9, 5, 0, '2023-02-21 17:50:01');
103 | INSERT INTO `tiktok`.`follows` (`id`, `user_id`, `follower_id`, `cancel`, `createTime`)
104 | VALUES (38, 9, 4, 0, '2023-02-21 17:50:21');
105 | INSERT INTO `tiktok`.`follows` (`id`, `user_id`, `follower_id`, `cancel`, `createTime`)
106 | VALUES (39, 11, 9, 0, '2023-02-21 17:52:01');
107 | INSERT INTO `tiktok`.`follows` (`id`, `user_id`, `follower_id`, `cancel`, `createTime`)
108 | VALUES (40, 11, 5, 0, '2023-02-21 17:52:13');
109 | INSERT INTO `tiktok`.`follows` (`id`, `user_id`, `follower_id`, `cancel`, `createTime`)
110 | VALUES (41, 11, 4, 0, '2023-02-21 17:52:13');
111 | INSERT INTO `tiktok`.`follows` (`id`, `user_id`, `follower_id`, `cancel`, `createTime`)
112 | VALUES (42, 14, 2, 0, '2023-02-21 17:53:19');
113 | INSERT INTO `tiktok`.`follows` (`id`, `user_id`, `follower_id`, `cancel`, `createTime`)
114 | VALUES (43, 14, 1, 0, '2023-02-21 17:53:32');
115 | INSERT INTO `tiktok`.`follows` (`id`, `user_id`, `follower_id`, `cancel`, `createTime`)
116 | VALUES (44, 14, 3, 0, '2023-02-21 17:53:38');
117 | INSERT INTO `tiktok`.`follows` (`id`, `user_id`, `follower_id`, `cancel`, `createTime`)
118 | VALUES (45, 10, 9, 0, '2023-02-21 18:42:49');
119 |
120 | INSERT INTO `tiktok`.`comments` (`id`, `user_id`, `video_id`, `text`, `is_cancel`, `createTime`)
121 | VALUES (48, 1, 15, '太美了哈哈', 0, '2023-02-21 17:19:31');
122 | INSERT INTO `tiktok`.`comments` (`id`, `user_id`, `video_id`, `text`, `is_cancel`, `createTime`)
123 | VALUES (49, 9, 15, '自己发的*哈哈', 0, '2023-02-21 17:48:08');
124 |
125 | INSERT INTO `tiktok`.`messages` (`id`, `user_id`, `to_user_id`, `content`, `is_withdraw`, `createTime`)
126 | VALUES (22, 1, 9, '测试', 0, 1676976129);
127 |
--------------------------------------------------------------------------------