这是我参与「第三届青训营 -后端场」笔记创作活动的的第六篇笔记
本次笔记记录我们组的项目:抖声后端开发的要点。
项目结构
-
cmd/ 存放项目启动相关
-
bin/ 本地调试时存放项目生成的二进制可执行文件
-
runtime/ 存放项目运行时产生的文件
- logs/ 存放项目运行时日志
-
-
common/
- config/ 项目的配置读取
- cron/ 定时任务
- db/ 数据库支持
- log/ 日志配置
- model/ 数据表
- oss/ OSS,配置了 minio 和 aliyun 两种
- result/ 规范了全局的错误码和返回值
-
controller/ 控制层实现
-
doc/ 存放项目文档
- imgs/ 存放文档所需的图片
-
middlewire/ 中间件
-
service/ 服务层实现
-
test/ 项目单元测试 -
util/ 存放封装了项目通用逻辑的小工具
-
app.example.yaml 项目配置文件示例(远程服务器配置ignore,不上传至仓库)
数据库设计
注:该文档为最初始的文档,实际数据表以 common 中的 model.go 为准
用户表
| 字段 | 类型 | 注释 |
|---|---|---|
| id | int64 | id,主键 |
| name | string | 用户昵称,index |
| username | string | 登录用户名 |
| password | string | 登录密码 |
| follow_count | int64 | 关注总数 |
| follower_count | int64 | 粉丝总数 |
视频表
| 字段 | 类型 | 注释 |
|---|---|---|
| id | int64 | id,主键 |
| publish_id | int64 | 发布者id,外键对应于用户表id |
| play_url | string | 视频播放地址 |
| cover_url | string | 视频封面地址 |
| create_time | string | 创建时间,精确到秒 |
| favorite_count | int64 | 视频点赞总数 |
| comment_count | int64 | 视频评论总数 |
| title | string | 视频标题 |
评论表
| 字段 | 类型 | 注释 |
|---|---|---|
| id | int64 | 评论id,主键 |
| user_id | int64 | 评论人id,外键 |
| content | string | 评论内容 |
| create_time | string | 评论时间,mm-dd |
| video_id | int64 | 评论视频id,外键 |
关注表
| 字段 | 类型 | 注释 |
|---|---|---|
| id | int64 | id,主键 |
| user_id | int64 | 被关注人id,外键 |
| follow_id | int64 | 关注人id,外键 |
| create_time | string | 关注时间,精确到秒 |
视频点赞表
| 字段 | 类型 | 注释 |
|---|---|---|
| id | int64 | id,主键 |
| user_id | int64 | 用户id,外键 |
| video_id | int64 | 视频id,外键 |
| create_time | int64 | 点赞时间 |
项目初始化
func init() {
once.Do(func() {
config.ReadCfg()
config.Init()
log.Init()
db.Init()
oss.Init()
cron.Init()
})
}
func main() {
r := gin.Default()
handle(r)
r.Run(fmt.Sprintf("%s:%s", config.AppCfg.Host, config.AppCfg.Port))
}
路由
路由在 router.go 中,注意鉴权中间件的使用。
路由转到相应的controller,再执行相关service,并返回responce或错误
service为具体功能实现
鉴权
本项目采用jwt鉴权
注册
-
*gin.Context.ShouldBindWith判断长度是否合法 -
数据库查询是否存在用户名
err = db.MySQL.Debug().Model(&model.User{}).Where("username = ?", req.Username).Select("id").Count(&count).Error注意没查到也返回err,要有
err != gorm.ErrRecordNotFound -
采用bcrypt加盐加密,保护在数据库的密码安全
-
通过user.id创建
token,和refreshToken,前者2h,后者30d,并存入Redis set
登录
- 解析参数
- 数据库查询用户名是否存在
- 密码校验
bcrypt.CompareHashAndPassword - 通过user.id创建
token,和refreshToken,前者2h,后者30d,并存入Redis set
拦截鉴权
所有服务均要get或post当前用户的token,暂不清楚前端存储token方式。
查询refreshToken,不存则代表超三十天未登录,需重新登录;若未过期,在redis更新两个token,并给login发送post。这种鉴权方法可以有效防止token被窃取,token频繁刷新,提高了web项目的安全性。
日志
采用zap
对象存储
阿里云oss存储视频,返回url
文件类型判断用filetype.IsVideo
注意需分两次独立open multipart.file类型的文件,因为同一打开的文件经过校验后会损坏,会上传损坏的视频
通用result设计
res.Success(c, res.R{
"userid": data.UserId,
"token": data.Token,
})