master 09a12ca6caf1 cached
52 files
92.4 KB
34.3k tokens
280 symbols
1 requests
Download .txt
Repository: ACking-you/byte_douyin_project
Branch: master
Commit: 09a12ca6caf1
Files: 52
Total size: 92.4 KB

Directory structure:
gitextract_cvanrqe9/

├── .gitignore
├── LICENSE
├── README.md
└── deprecated/
    ├── cache/
    │   ├── index_map.go
    │   └── indexmap_test.go
    ├── config/
    │   ├── conf.go
    │   └── config.toml
    ├── go.mod
    ├── go.sum
    ├── handlers/
    │   ├── comment/
    │   │   ├── post_comment_handler.go
    │   │   └── query_commentlist_handler.go
    │   ├── user_info/
    │   │   ├── post_follow_action_handler.go
    │   │   ├── query_follow_list_handler.go
    │   │   ├── query_follower_handler.go
    │   │   └── userinfo_handler.go
    │   ├── user_login/
    │   │   ├── user_login_handler.go
    │   │   └── user_register_handler.go
    │   └── video/
    │       ├── feed_videolist_handler.go
    │       ├── post_favor_handler.go
    │       ├── publish_video_handler.go
    │       ├── query_favor_videolist_handler.go
    │       └── query_videolist_handler.go
    ├── main.go
    ├── middleware/
    │   ├── jwt.go
    │   ├── jwt_test.go
    │   ├── normal.go
    │   ├── password.go
    │   └── password_test.go
    ├── models/
    │   ├── comment.go
    │   ├── common.go
    │   ├── init_db.go
    │   ├── user_info.go
    │   ├── user_info_test.go
    │   ├── user_login.go
    │   ├── video.go
    │   └── video_test.go
    ├── router/
    │   └── router_douyin.go
    ├── service/
    │   ├── comment/
    │   │   ├── post_comment.go
    │   │   └── query_comment_list.go
    │   ├── user_info/
    │   │   ├── post_follow_action.go
    │   │   ├── query_follow_list.go
    │   │   └── query_follower_list.go
    │   ├── user_login/
    │   │   ├── post_user_login.go
    │   │   └── query_user_login.go
    │   └── video/
    │       ├── feed_videolist.go
    │       ├── post_favor_state.go
    │       ├── post_video.go
    │       ├── query_favor_videolist.go
    │       └── query_videolist.go
    └── util/
        ├── comment.go
        ├── ffmpeg.go
        └── video.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.idea
!deprecated/static/1-1.jpg
!deprecated/static/1-1.mp4
deprecated/static/*

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2022 L_B__

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
[写给后续参加字节跳动青训营的同学](https://github.com/ACking-you/byte_douyin_project/issues/10)
# 抖音极简版
<!-- PROJECT SHIELDS -->

[![Contributors][contributors-shield]][contributors-url]
[![Forks][forks-shield]][forks-url]
[![Stargazers][stars-shield]][stars-url]
[![Issues][issues-shield]][issues-url]
[![MIT License][license-shield]][license-url]

<!-- links -->
[your-project-path]:ACking-you/byte_douyin_project
[contributors-shield]: https://img.shields.io/github/contributors/ACking-you/byte_douyin_project.svg?style=flat-square
[contributors-url]: https://github.com/ACking-you/byte_douyin_project/graphs/contributors
[forks-shield]: https://img.shields.io/github/forks/ACking-you/byte_douyin_project.svg?style=flat-square
[forks-url]: https://github.com/ACking-you/byte_douyin_project/network/members
[stars-shield]: https://img.shields.io/github/stars/ACking-you/byte_douyin_project.svg?style=flat-square
[stars-url]: https://github.com/ACking-you/byte_douyin_project/stargazers
[issues-shield]: https://img.shields.io/github/issues/ACking-you/byte_douyin_project.svg?style=flat-square
[issues-url]: https://img.shields.io/github/issues/ACking-you/byte_douyin_project.svg
[license-shield]: https://img.shields.io/github/license/ACking-you/byte_douyin_project?style=flat-square
[license-url]: https://github.com/ACking-you/byte_douyin_project/blob/master/LICENSE



* [数据库说明](#数据库说明)
    * [数据库关系说明](#数据库关系说明)
    * [数据库建立说明](#数据库建立说明)
* [架构说明](#架构说明)
    * [各模块代码详细说明](#各模块代码详细说明)
        * [Handlers](#handlers)
        * [Service](#service)
        * [Models](#models)
* [遇到的问题及对应解决方案](#遇到的问题及对应解决方案)
    * [返回json数据的完整性和前端要求的一致性](#返回json数据的完整性和前端要求的一致性)
    * [is\_favorite和is\_follow字段的更新](#is_favorite和is_follow字段的更新)
    * [视频的保存和封面的切片](#视频的保存和封面的切片)
        * [视频的保存](#视频的保存)
        * [封面的截取](#封面的截取)
* [可改进的地方](#可改进的地方)
* [项目运行](#项目运行)

## 数据库说明


![database.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/892fbbe46695467ebe4fb4a12ebd412e~tplv-k3u1fbpfcp-watermark.image?)

> 单纯看上面的图会感觉很混乱,现在我们来将关系拆解。

### 数据库关系说明

**关系图如下:**


![database_relation.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f08918db1ea84126bc21d23fe9401a75~tplv-k3u1fbpfcp-watermark.image?)

> 所有的表都有自己的id主键为唯一的标识。

user_logins:存下用户的用户名和密码

user_infos:存下用户的基本信息

videos:存下视频的基本信息

comment:存下每个评论的基本信息

**具体的关系索引:**

所有的一对一和一对多关系,只需要在一个表中加入对方的id索引。

* 比如user_infos和user_logins的一对一关系,在user_logins中加入user_id字段设为外键存储user_infos中对应的行的id信息。
* 比如user_infos和和videos的一对多关系,在videos中加入user_id字段设为外键存储user_infos中对应的行的id信息。

所有的多对多关系,需要多建立一张表,用该表作为媒介存储两个对象的id作为关系的产生,而它们各自表中不需要再存下额外的字段。

* 比如user_infos和videos的多对多关系,创建一张user_favor_videos中间表,然后将该表的字段均设为外键,分别存下user_infos和videos对应行的id。如id为1的用户对id为2的视频点了个赞,那么就把这个1和2存入中间表user_favor_videos即可。

### 数据库建立说明

数据库各表的建立无需自己实现额外的建表操作,一切都由gorm框架自动建表,具体逻辑在models层的代码中。

> gorm官方文档链接:[链接](https://gorm.io/zh_CN/docs/index.html)

建表和初始化操作由init_db.go来执行:

```go
func InitDB() {
	var err error
	DB, err = gorm.Open(mysql.Open(config.DBConnectString()), &gorm.Config{
		PrepareStmt:            true, //缓存预编译命令
		SkipDefaultTransaction: true, //禁用默认事务操作
		//Logger:                 logger.Default.LogMode(logger.Info), //打印sql语句
	})
	if err != nil {
		panic(err)
	}
	err = DB.AutoMigrate(&UserInfo{}, &Video{}, &Comment{}, &UserLogin{})
	if err != nil {
		panic(err)
	}
}
```

## 架构说明


![architecture.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ae11d82b8de74787a258ef36f4cf2557~tplv-k3u1fbpfcp-watermark.image?)

> 以用户登录为例共需要经过以下过程:

1. 进入中间件SHAMiddleWare内的函数逻辑,得到password明文加密后再设置password。具体需要调用gin.Context的Set方法设置password。随后调用next()方法继续下层路由。
2. 进入UserLoginHandler函数逻辑,获取username,并调用gin.Context的Get方法得到中间件设置的password。再调用service层的QueryUserLogin函数。
3. 进入QueryUserLogin函数逻辑,执行三个过程:checkNum,prepareData,packData。也就是检查参数、准备数据、打包数据,准备数据的过程中会调用models层的UserLoginDAO。
4. 进入UserLoginDAO的逻辑,执行最终的数据库请求过程,返回给上层。

### 各模块代码详细说明

我开发的过程中是以单个函数为单个文件进行开发,所以代码会比较长,故我根据数据库内的模型对函数文件进行了如下分包:


![handlers.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6dc222793d6f4038b1bf2435053bfee4~tplv-k3u1fbpfcp-watermark.image?)

service层的分包也是一样的。

#### Handlers

对于handlers层级的所有函数实现有如下规范:

所有的逻辑由代理对象进行,完成以下两个逻辑

1. 解析得到参数。
2. 开始调用下层逻辑。

例如一个关注动作触发的逻辑:

```go
NewProxyPostFollowAction().Do()
//其中Do主要包含以下两个逻辑,对应两个方法
p.parseNum() //解析参数
p.startAction() //开始调用下层逻辑
```

#### Service

对于service层级的函数实现由如下规范:

同样由一个代理对象进行,完成以下三个或两个逻辑

当上层需要返回数据信息,则进行三个逻辑:

1. 检查参数。
2. 准备数据。
3. 打包数据。

当上层不需要返回数据信息,则进行两个逻辑:

1. 检查参数。
2. 执行上层指定的动作。

例如关注动作在service层的逻辑属于第二类:

```go
NewPostFollowActionFlow(...).Do()
//Do中包含以下两个逻辑
p.checkNum() //检查参数
p.publish() //执行动作
```

#### Models

对于models层的各个操作,没有像service和handler层针对前端发来的请求就行对应的处理,models层是面向于数据库的增删改查,不需要考虑和上层的交互。

而service层根据上层的需要来调用models层的不同代码请求数据库内的内容。

## 遇到的问题及对应解决方案

### 返回json数据的完整性和前端要求的一致性

由于数据库内的一对一、一对多、多对多关系是根据id进行映射,所以models层请求得到的字段并不包含前端所需要的直接数据,比如前端要求Comment结构中需要包含UserInfo,而我的Comment结构如下:

```go
type Comment struct {
	Id         int64     `json:"id"`
	UserInfoId int64     `json:"-"` //用于一对多关系的id
	VideoId    int64     `json:"-"` //一对多,视频对评论
	User       UserInfo  `json:"user" gorm:"-"`
	Content    string    `json:"content"`
	CreatedAt  time.Time `json:"-"`
	CreateDate string    `json:"create_date" gorm:"-"`
}
```

很明显,为了与数据库中设计的表一一对应,在原数据的基础上加了几个字段,且在gorm屏蔽了User字段,所以service调用models层得到是Comment数据中User字段还未被填充,还需再填充这部分内容,好在由对应的UserId,故可以正确填充该字段。

为了重用以及不破坏代码的一致性,将填充逻辑写入util包内,比如以上的字段填充函数,同时前端要求的日期格式也能够按要求设置:

```go
func FillCommentListFields(comments *[]*models.Comment) error {
	size := len(*comments)
	if comments == nil || size == 0 {
		return errors.New("util.FillCommentListFields comments为空")
	}
	dao := models.NewUserInfoDAO()
	for _, v := range *comments {
		_ = dao.QueryUserInfoById(v.UserInfoId, &v.User) //填充这条评论的作者信息
		v.CreateDate = v.CreatedAt.Format("1-2")         //转为前端要求的日期格式
	}
	return nil
}
```

这里举了Comment这一个例子,其他的Video也是同理。

### is_favorite和is_follow字段的更新

每次为视频点赞都会在数据库的user_favor_videos表中加入用户的id和视频的id,很明显is_favorite字段是针对每个用户来判断的,而我所设计的数据库中的videos表也是包含这个字段的,但这个字段很明显不能直接进行复用,而是需要每次判断用户和此视频的关系来重新更新。

这个更新过程放入util包的填充函数中即可,为了点赞过程的迅速响应,我采取了nosql的方式存储了这个点赞的映射,也就是userId和videoId的映射,也就是用nosql代替了这个中间表的功效。

具体代码逻辑在cache包内。

### 视频的保存和封面的切片

#### 视频的保存

在本地建立static文件夹存储视频和封面图片。

具体逻辑如下:

1. 检查视频格式
2. 根据userId和该作者发布的视频数量产生唯一的名称,如id为1的用户发布了0个视频,那么本次上传的名称为1-0.mp4
3. 截取第一帧画面作为封面
4. 保存视频基本信息到数据库(包括视频链接和封面链接

#### 封面的截取

使用ffmpeg调用命令行对视频进行截取。

设计ffmpeg请求类Video2Image,通过对它内部的参数设置来构造对应的命令行字符串。具体请看util包内的ffmpeg.go的实现。

由于我设计的命令请求字符串是直接的一行字符串,而go语言exec包里面的Command函数执行所需的仅仅是一个个参数。

所以此处我想到用cgo直接调用 system(args)来解决。

代码如下:

```go
//#include <stdlib.h>
//int startCmd(const char* cmd){
//	  return system(cmd);
//}
import "C"

func (v *Video2Image) ExecCommand(cmd string) error {
	if v.debug {
		log.Println(cmd)
	}
	cCmd := C.CString(cmd)
	defer C.free(unsafe.Pointer(cCmd))
	status := C.startCmd(cCmd)
	if status != 0 {
		return errors.New("视频切截图失败")
	}
	return nil
}
```

## 可改进的地方

1. 写到后面发现很多mysql的数据可以用redis优化。
2. 很多执行逻辑可以通过并行优化。
3. 路由分组可以更为详实。
4. ...

## 项目运行

> 本项目运行不需要手动建表,项目启动后会自动建表。

**运行所需环境**:

* mysql 5.7及以上
* redis 5.0.14及以上
* ffmepg(已放入lib自带,用于对视频切片得到封面
* 需要gcc环境(主要用于cgo,windows请将mingw-w64设置到环境变量

**运行需要更改配置**:

> 进入config目录更改对应的mysql、redis、server、path信息。

* mysql:mysql相关的配置信息
* redis:redis相关配置信息
* server:当前服务器(当前启动的机器)的配置信息,用于生成对应的视频和图片链接
* path:其中ffmpeg_path为lib里的文件路径,static_source_path为本项目的static目录,这里请根据本地的绝对路径进行更改

> 完成config配置文件的更改后,需要再更改conf.go里的解析文件路径为config.toml文件的绝对路径,内容如下:
>
> ```go
> if _, err := toml.DecodeFile("你的绝对路径\\config.toml", &Info); err != nil {
> 		panic(err)
> 	}
> ```
>
>

**运行所需命令**:

```shell
cd .\byte_douyin_project\
go run main.go
```


================================================
FILE: deprecated/cache/index_map.go
================================================
package cache

import (
	"context"
	"fmt"
	"github.com/ACking-you/byte_douyin_project/config"
	"github.com/go-redis/redis/v8"
)

// 用户id->被点赞的视频id集合->是否含有该视频id

var ctx = context.Background()
var rdb *redis.Client

const (
	favor    = "favor"
	relation = "relation"
)

func init() {
	rdb = redis.NewClient(
		&redis.Options{
			Addr:     fmt.Sprintf("%s:%d", config.Global.RDB.IP, config.Global.RDB.Port),
			Password: "", //没有设置密码
			DB:       config.Global.RDB.Database,
		})
}

var (
	proxyIndexOperation ProxyIndexMap
)

type ProxyIndexMap struct {
}

func NewProxyIndexMap() *ProxyIndexMap {
	return &proxyIndexOperation
}

// UpdateVideoFavorState 更新点赞状态,state:true为点赞,false为取消点赞
func (i *ProxyIndexMap) UpdateVideoFavorState(userId int64, videoId int64, state bool) {
	key := fmt.Sprintf("%s:%d", favor, userId)
	if state {
		rdb.SAdd(ctx, key, videoId)
		return
	}
	rdb.SRem(ctx, key, videoId)
}

// GetVideoFavorState 得到点赞状态
func (i *ProxyIndexMap) GetVideoFavorState(userId int64, videoId int64) bool {
	key := fmt.Sprintf("%s:%d", favor, userId)
	ret := rdb.SIsMember(ctx, key, videoId)
	return ret.Val()
}

// UpdateUserRelation 更新点赞状态,state:true为点关注,false为取消关注
func (i *ProxyIndexMap) UpdateUserRelation(userId int64, followId int64, state bool) {
	key := fmt.Sprintf("%s:%d", relation, userId)
	if state {
		rdb.SAdd(ctx, key, followId)
		return
	}
	rdb.SRem(ctx, key, followId)
}

// GetUserRelation 得到关注状态
func (i *ProxyIndexMap) GetUserRelation(userId int64, followId int64) bool {
	key := fmt.Sprintf("%s:%d", relation, userId)
	ret := rdb.SIsMember(ctx, key, followId)
	return ret.Val()
}


================================================
FILE: deprecated/cache/indexmap_test.go
================================================
package cache

import (
	"fmt"
	"testing"
)

func TestProxyIndexMap_UpdateUserRelation(t *testing.T) {
	NewProxyIndexMap().UpdateUserRelation(1, 2, true)
	fmt.Println(NewProxyIndexMap().GetUserRelation(1, 3))
}

func TestProxyIndexMap_GetVideoFavorState(t *testing.T) {
	fmt.Println(NewProxyIndexMap().GetVideoFavorState(1, 2))
	NewProxyIndexMap().UpdateVideoFavorState(1, 2, true)
	fmt.Println(NewProxyIndexMap().GetVideoFavorState(1, 2))
}


================================================
FILE: deprecated/config/conf.go
================================================
package config

import (
	"fmt"
	"github.com/BurntSushi/toml"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

type Mysql struct {
	Host      string
	Port      int
	Database  string
	Username  string
	Password  string
	Charset   string
	ParseTime bool `toml:"parse_time"`
	Loc       string
}

type Redis struct {
	IP       string
	Port     int
	Database int
}

type Server struct {
	IP   string
	Port int
}

type Path struct {
	FfmpegPath       string `toml:"ffmpeg_path"`
	StaticSourcePath string `toml:"static_source_path"`
}

type Config struct {
	DB     Mysql `toml:"mysql"`
	RDB    Redis `toml:"redis"`
	Server `toml:"server"`
	Path   `toml:"path"`
}

var Global Config

func ensurePathValid() {
	var err error
	if _, err = os.Stat(Global.StaticSourcePath); os.IsNotExist(err) {
		if err = os.Mkdir(Global.StaticSourcePath, 0755); err != nil {
			log.Fatalf("mkdir error:path %s", Global.StaticSourcePath)
		}
	}
	if _, err = os.Stat(Global.FfmpegPath); os.IsNotExist(err) {
		if _, err = exec.Command("ffmpeg", "-version").Output(); err != nil {
			log.Fatalf("ffmpeg not valid %s", Global.FfmpegPath)
		} else {
			Global.FfmpegPath = "ffmpeg"
		}
	} else {
		Global.FfmpegPath, err = filepath.Abs(Global.FfmpegPath)
		if err != nil {
			log.Fatalln("get abs path failed:", Global.FfmpegPath)
		}
	}
	//把资源路径转化为绝对路径,防止调用ffmpeg命令失效
	Global.StaticSourcePath, err = filepath.Abs(Global.StaticSourcePath)
	if err != nil {
		log.Fatalln("get abs path failed:", Global.StaticSourcePath)
	}
}

//包初始化加载时候会调用的函数
func init() {
	if _, err := toml.DecodeFile("./config/config.toml", &Global); err != nil {
		panic(err)
	}
	//去除左右的空格
	strings.Trim(Global.Server.IP, " ")
	strings.Trim(Global.RDB.IP, " ")
	strings.Trim(Global.DB.Host, " ")
	//保证路径正常
	ensurePathValid()
}

// DBConnectString 填充得到数据库连接字符串
func DBConnectString() string {
	arg := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=%v&loc=%s",
		Global.DB.Username, Global.DB.Password, Global.DB.Host, Global.DB.Port, Global.DB.Database,
		Global.DB.Charset, Global.DB.ParseTime, Global.DB.Loc)
	log.Println(arg)
	return arg
}


================================================
FILE: deprecated/config/config.toml
================================================
#关系型数据库配置
[mysql]
host = "127.0.0.1"
port = 3306
database = "douyin"
username = "root"
password = "123"
charset = "utf8mb4"
parse_time = true
loc = "Local"

#nosql配置,用于存储每个用户是否对某个视频点赞,以及关注了某个人(主要用于反馈前端情况,比如点了赞后会变成红心
[redis]
host = "127.0.0.1"
port = 49153
database = 0

#记录当前服务器的ip和启动端口号,当前服务器的ip用于生成对应的视频链接地址
[server]
ip = "127.0.0.1"
port = 8080

#用于保存资源的路径,以及用于截图工具的路径(截图工具放在lib目录
[path]
ffmpeg_path = "./lib/ffmpeg.exe"
static_source_path = "./static"

================================================
FILE: deprecated/go.mod
================================================
module github.com/ACking-you/byte_douyin_project

go 1.16

require (
	github.com/BurntSushi/toml v1.1.0
	github.com/dgrijalva/jwt-go v3.2.0+incompatible
	github.com/gin-gonic/gin v1.7.7
	github.com/go-redis/redis/v8 v8.11.5 // indirect
	github.com/golang/protobuf v1.5.2 // indirect
	gorm.io/driver/mysql v1.3.3
	gorm.io/gorm v1.23.5
)


================================================
FILE: deprecated/go.sum
================================================
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gorm.io/driver/mysql v1.3.3 h1:jXG9ANrwBc4+bMvBcSl8zCfPBaVoPyBEBshA8dA93X8=
gorm.io/driver/mysql v1.3.3/go.mod h1:ChK6AHbHgDCFZyJp0F+BmVGb06PSIoh9uVYKAlRbb2U=
gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.23.5 h1:TnlF26wScKSvknUC/Rn8t0NLLM22fypYBlvj1+aH6dM=
gorm.io/gorm v1.23.5/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=


================================================
FILE: deprecated/handlers/comment/post_comment_handler.go
================================================
package comment

import (
	"errors"
	"fmt"
	"github.com/ACking-you/byte_douyin_project/models"
	"github.com/ACking-you/byte_douyin_project/service/comment"
	"github.com/gin-gonic/gin"
	"net/http"
	"strconv"
)

type PostCommentResponse struct {
	models.CommonResponse
	*comment.Response
}

func PostCommentHandler(c *gin.Context) {
	NewProxyPostCommentHandler(c).Do()
}

type ProxyPostCommentHandler struct {
	*gin.Context

	videoId     int64
	userId      int64
	commentId   int64
	actionType  int64
	commentText string
}

func NewProxyPostCommentHandler(context *gin.Context) *ProxyPostCommentHandler {
	return &ProxyPostCommentHandler{Context: context}
}

func (p *ProxyPostCommentHandler) Do() {
	//解析参数
	if err := p.parseNum(); err != nil {
		p.SendError(err.Error())
		return
	}

	//正式调用Service层
	commentRes, err := comment.PostComment(p.userId, p.videoId, p.commentId, p.actionType, p.commentText)
	if err != nil {
		p.SendError(err.Error())
		return
	}

	//成功返回
	p.SendOk(commentRes)
}

func (p *ProxyPostCommentHandler) parseNum() error {
	rawUserId, _ := p.Get("user_id")
	userId, ok := rawUserId.(int64)
	if !ok {
		return errors.New("userId解析出错")
	}
	p.userId = userId

	rawVideoId := p.Query("video_id")
	videoId, err := strconv.ParseInt(rawVideoId, 10, 64)
	if err != nil {
		return err
	}
	p.videoId = videoId

	//根据actionType解析对应的可选参数
	rawActionType := p.Query("action_type")
	actionType, err := strconv.ParseInt(rawActionType, 10, 64)
	switch actionType {
	case comment.CREATE:
		p.commentText = p.Query("comment_text")
	case comment.DELETE:
		p.commentId, err = strconv.ParseInt(p.Query("comment_id"), 10, 64)
		if err != nil {
			return err
		}
	default:
		return fmt.Errorf("未定义的行为%d", actionType)
	}
	p.actionType = actionType
	return nil
}

func (p *ProxyPostCommentHandler) SendError(msg string) {
	p.JSON(http.StatusOK, PostCommentResponse{
		CommonResponse: models.CommonResponse{StatusCode: 1, StatusMsg: msg}, Response: &comment.Response{}})
}

func (p *ProxyPostCommentHandler) SendOk(comment *comment.Response) {
	p.JSON(http.StatusOK, PostCommentResponse{
		CommonResponse: models.CommonResponse{StatusCode: 0},
		Response:       comment,
	})
}


================================================
FILE: deprecated/handlers/comment/query_commentlist_handler.go
================================================
package comment

import (
	"errors"
	"github.com/ACking-you/byte_douyin_project/handlers/video"
	"github.com/ACking-you/byte_douyin_project/models"
	"github.com/ACking-you/byte_douyin_project/service/comment"
	"github.com/gin-gonic/gin"
	"net/http"
	"strconv"
)

type ListResponse struct {
	models.CommonResponse
	*comment.List
}

func QueryCommentListHandler(c *gin.Context) {
	NewProxyCommentListHandler(c).Do()
}

type ProxyCommentListHandler struct {
	*gin.Context

	videoId int64
	userId  int64
}

func NewProxyCommentListHandler(context *gin.Context) *ProxyCommentListHandler {
	return &ProxyCommentListHandler{Context: context}
}

func (p *ProxyCommentListHandler) Do() {
	//解析参数
	if err := p.parseNum(); err != nil {
		p.SendError(err.Error())
		return
	}

	//正式调用
	commentList, err := comment.QueryCommentList(p.userId, p.videoId)
	if err != nil {
		p.SendError(err.Error())
		return
	}

	//成功返回
	p.SendOk(commentList)
}

func (p *ProxyCommentListHandler) parseNum() error {
	rawUserId, _ := p.Get("user_id")
	userId, ok := rawUserId.(int64)
	if !ok {
		return errors.New("userId解析出错")
	}
	p.userId = userId

	rawVideoId := p.Query("video_id")
	videoId, err := strconv.ParseInt(rawVideoId, 10, 64)
	if err != nil {
		return err
	}
	p.videoId = videoId

	return nil
}

func (p *ProxyCommentListHandler) SendError(msg string) {
	p.JSON(http.StatusOK, video.FavorVideoListResponse{
		CommonResponse: models.CommonResponse{StatusCode: 1, StatusMsg: msg}})
}

func (p *ProxyCommentListHandler) SendOk(commentList *comment.List) {
	p.JSON(http.StatusOK, ListResponse{CommonResponse: models.CommonResponse{StatusCode: 0},
		List: commentList,
	})
}


================================================
FILE: deprecated/handlers/user_info/post_follow_action_handler.go
================================================
package user_info

import (
	"errors"
	"github.com/ACking-you/byte_douyin_project/models"
	"github.com/ACking-you/byte_douyin_project/service/user_info"
	"github.com/gin-gonic/gin"
	"net/http"
	"strconv"
)

func PostFollowActionHandler(c *gin.Context) {
	NewProxyPostFollowAction(c).Do()
}

type ProxyPostFollowAction struct {
	*gin.Context

	userId     int64
	followId   int64
	actionType int
}

func NewProxyPostFollowAction(context *gin.Context) *ProxyPostFollowAction {
	return &ProxyPostFollowAction{Context: context}
}

func (p *ProxyPostFollowAction) Do() {
	var err error
	if err = p.prepareNum(); err != nil {
		p.SendError(err.Error())
		return
	}
	if err = p.startAction(); err != nil {
		//当错误为model层发生的,那么就是重复键值的插入了
		if errors.Is(err, user_info.ErrIvdAct) || errors.Is(err, user_info.ErrIvdFolUsr) {
			p.SendError(err.Error())
		} else {
			p.SendError("请勿重复关注")
		}
		return
	}
	p.SendOk("操作成功")
}

func (p *ProxyPostFollowAction) prepareNum() error {
	rawUserId, _ := p.Get("user_id")
	userId, ok := rawUserId.(int64)
	if !ok {
		return errors.New("userId解析出错")
	}
	p.userId = userId

	//解析需要关注的id
	followId := p.Query("to_user_id")
	parseInt, err := strconv.ParseInt(followId, 10, 64)
	if err != nil {
		return err
	}
	p.followId = parseInt

	//解析action_type
	actionType := p.Query("action_type")
	parseInt, err = strconv.ParseInt(actionType, 10, 32)
	if err != nil {
		return err
	}
	p.actionType = int(parseInt)
	return nil
}

func (p *ProxyPostFollowAction) startAction() error {
	err := user_info.PostFollowAction(p.userId, p.followId, p.actionType)
	if err != nil {
		return err
	}
	return nil
}

func (p *ProxyPostFollowAction) SendError(msg string) {
	p.JSON(http.StatusOK, models.CommonResponse{StatusCode: 1, StatusMsg: msg})
}

func (p *ProxyPostFollowAction) SendOk(msg string) {
	p.JSON(http.StatusOK, models.CommonResponse{StatusCode: 1, StatusMsg: msg})
}


================================================
FILE: deprecated/handlers/user_info/query_follow_list_handler.go
================================================
package user_info

import (
	"errors"
	"github.com/ACking-you/byte_douyin_project/models"
	"github.com/ACking-you/byte_douyin_project/service/user_info"
	"github.com/gin-gonic/gin"
	"net/http"
)

type FollowListResponse struct {
	models.CommonResponse
	*user_info.FollowList
}

func QueryFollowListHandler(c *gin.Context) {
	NewProxyQueryFollowList(c).Do()
}

type ProxyQueryFollowList struct {
	*gin.Context

	userId int64

	*user_info.FollowList
}

func NewProxyQueryFollowList(context *gin.Context) *ProxyQueryFollowList {
	return &ProxyQueryFollowList{Context: context}
}

func (p *ProxyQueryFollowList) Do() {
	var err error
	if err = p.parseNum(); err != nil {
		p.SendError(err.Error())
		return
	}
	if err = p.prepareData(); err != nil {
		p.SendError(err.Error())
		return
	}
	p.SendOk("请求成功")
}

func (p *ProxyQueryFollowList) parseNum() error {
	rawUserId, _ := p.Get("user_id")
	userId, ok := rawUserId.(int64)
	if !ok {
		return errors.New("userId解析出错")
	}
	p.userId = userId
	return nil
}

func (p *ProxyQueryFollowList) prepareData() error {
	list, err := user_info.QueryFollowList(p.userId)
	if err != nil {
		return err
	}
	p.FollowList = list
	return nil
}

func (p *ProxyQueryFollowList) SendError(msg string) {
	p.JSON(http.StatusOK, FollowListResponse{
		CommonResponse: models.CommonResponse{StatusCode: 1, StatusMsg: msg},
	})
}

func (p *ProxyQueryFollowList) SendOk(msg string) {
	p.JSON(http.StatusOK, FollowListResponse{
		CommonResponse: models.CommonResponse{StatusCode: 0, StatusMsg: msg},
		FollowList:     p.FollowList,
	})
}


================================================
FILE: deprecated/handlers/user_info/query_follower_handler.go
================================================
package user_info

import (
	"errors"
	"github.com/ACking-you/byte_douyin_project/models"
	user_info2 "github.com/ACking-you/byte_douyin_project/service/user_info"
	"github.com/gin-gonic/gin"
	"net/http"
)

type FollowerListResponse struct {
	models.CommonResponse
	*user_info2.FollowerList
}

func QueryFollowerHandler(c *gin.Context) {
	NewProxyQueryFollowerHandler(c).Do()
}

type ProxyQueryFollowerHandler struct {
	*gin.Context

	userId int64

	*user_info2.FollowerList
}

func NewProxyQueryFollowerHandler(context *gin.Context) *ProxyQueryFollowerHandler {
	return &ProxyQueryFollowerHandler{Context: context}
}

func (p *ProxyQueryFollowerHandler) Do() {
	var err error
	if err = p.parseNum(); err != nil {
		p.SendError(err.Error())
		return
	}
	if err = p.prepareData(); err != nil {
		if errors.Is(err, user_info2.ErrUserNotExist) {
			p.SendError(err.Error())
		} else {
			p.SendError("准备数据出错")
		}
		return
	}
	p.SendOk("成功")
}

func (p *ProxyQueryFollowerHandler) parseNum() error {
	rawUserId, _ := p.Get("user_id")
	userId, ok := rawUserId.(int64)
	if !ok {
		return errors.New("userId解析出错")
	}
	p.userId = userId
	return nil
}

func (p *ProxyQueryFollowerHandler) prepareData() error {
	list, err := user_info2.QueryFollowerList(p.userId)
	if err != nil {
		return err
	}
	p.FollowerList = list
	return nil
}

func (p *ProxyQueryFollowerHandler) SendError(msg string) {
	p.JSON(http.StatusOK, FollowerListResponse{
		CommonResponse: models.CommonResponse{
			StatusCode: 1,
			StatusMsg:  msg,
		},
	})
}

func (p *ProxyQueryFollowerHandler) SendOk(msg string) {
	p.JSON(http.StatusOK, FollowerListResponse{
		CommonResponse: models.CommonResponse{
			StatusCode: 1,
			StatusMsg:  msg,
		},
		FollowerList: p.FollowerList,
	})
}


================================================
FILE: deprecated/handlers/user_info/userinfo_handler.go
================================================
package user_info

import (
	"errors"
	models2 "github.com/ACking-you/byte_douyin_project/models"
	"github.com/gin-gonic/gin"
	"net/http"
)

type UserResponse struct {
	models2.CommonResponse
	User *models2.UserInfo `json:"user"`
}

func UserInfoHandler(c *gin.Context) {
	p := NewProxyUserInfo(c)
	//得到上层中间件根据token解析的userId
	rawId, ok := c.Get("user_id")
	if !ok {
		p.UserInfoError("解析userId出错")
		return
	}
	err := p.DoQueryUserInfoByUserId(rawId)
	if err != nil {
		p.UserInfoError(err.Error())
	}
}

type ProxyUserInfo struct {
	c *gin.Context
}

func NewProxyUserInfo(c *gin.Context) *ProxyUserInfo {
	return &ProxyUserInfo{c: c}
}

func (p *ProxyUserInfo) DoQueryUserInfoByUserId(rawId interface{}) error {
	userId, ok := rawId.(int64)
	if !ok {
		return errors.New("解析userId失败")
	}
	//由于得到userinfo不需要组装model层的数据,所以直接调用model层的接口
	userinfoDAO := models2.NewUserInfoDAO()

	var userInfo models2.UserInfo
	err := userinfoDAO.QueryUserInfoById(userId, &userInfo)
	if err != nil {
		return err
	}
	p.UserInfoOk(&userInfo)
	return nil
}

func (p *ProxyUserInfo) UserInfoError(msg string) {
	p.c.JSON(http.StatusOK, UserResponse{
		CommonResponse: models2.CommonResponse{StatusCode: 1, StatusMsg: msg},
	})
}

func (p *ProxyUserInfo) UserInfoOk(user *models2.UserInfo) {
	p.c.JSON(http.StatusOK, UserResponse{
		CommonResponse: models2.CommonResponse{StatusCode: 0},
		User:           user,
	})
}


================================================
FILE: deprecated/handlers/user_login/user_login_handler.go
================================================
package user_login

import (
	"github.com/ACking-you/byte_douyin_project/models"
	"github.com/ACking-you/byte_douyin_project/service/user_login"
	"github.com/gin-gonic/gin"
	"net/http"
)

type UserLoginResponse struct {
	models.CommonResponse
	*user_login.LoginResponse
}

func UserLoginHandler(c *gin.Context) {
	username := c.Query("username")
	raw, _ := c.Get("password")
	password, ok := raw.(string)
	if !ok {
		c.JSON(http.StatusOK, UserLoginResponse{
			CommonResponse: models.CommonResponse{
				StatusCode: 1,
				StatusMsg:  "密码解析错误",
			},
		})
	}
	userLoginResponse, err := user_login.QueryUserLogin(username, password)

	//用户不存在返回对应的错误
	if err != nil {
		c.JSON(http.StatusOK, UserLoginResponse{
			CommonResponse: models.CommonResponse{StatusCode: 1, StatusMsg: err.Error()},
		})
		return
	}

	//用户存在,返回相应的id和token
	c.JSON(http.StatusOK, UserLoginResponse{
		CommonResponse: models.CommonResponse{StatusCode: 0},
		LoginResponse:  userLoginResponse,
	})
}


================================================
FILE: deprecated/handlers/user_login/user_register_handler.go
================================================
package user_login

import (
	"github.com/ACking-you/byte_douyin_project/models"
	user_login2 "github.com/ACking-you/byte_douyin_project/service/user_login"
	"github.com/gin-gonic/gin"
	"net/http"
)

type UserRegisterResponse struct {
	models.CommonResponse
	*user_login2.LoginResponse
}

func UserRegisterHandler(c *gin.Context) {
	username := c.Query("username")
	rawVal, _ := c.Get("password")
	password, ok := rawVal.(string)
	if !ok {
		c.JSON(http.StatusOK, UserRegisterResponse{
			CommonResponse: models.CommonResponse{
				StatusCode: 1,
				StatusMsg:  "密码解析出错",
			},
		})
		return
	}
	registerResponse, err := user_login2.PostUserLogin(username, password)

	if err != nil {
		c.JSON(http.StatusOK, UserRegisterResponse{
			CommonResponse: models.CommonResponse{
				StatusCode: 1,
				StatusMsg:  err.Error(),
			},
		})
		return
	}
	c.JSON(http.StatusOK, UserRegisterResponse{
		CommonResponse: models.CommonResponse{StatusCode: 0},
		LoginResponse:  registerResponse,
	})
}


================================================
FILE: deprecated/handlers/video/feed_videolist_handler.go
================================================
package video

import (
	"errors"
	"github.com/ACking-you/byte_douyin_project/middleware"
	"github.com/ACking-you/byte_douyin_project/models"
	"github.com/ACking-you/byte_douyin_project/service/video"
	"github.com/gin-gonic/gin"
	"net/http"
	"strconv"
	"time"
)

type FeedResponse struct {
	models.CommonResponse
	*video.FeedVideoList
}

func FeedVideoListHandler(c *gin.Context) {
	p := NewProxyFeedVideoList(c)
	token, ok := c.GetQuery("token")
	//无登录状态
	if !ok {
		err := p.DoNoToken()
		if err != nil {
			p.FeedVideoListError(err.Error())
		}
		return
	}

	//有登录状态
	err := p.DoHasToken(token)
	if err != nil {
		p.FeedVideoListError(err.Error())
	}
}

type ProxyFeedVideoList struct {
	*gin.Context
}

func NewProxyFeedVideoList(c *gin.Context) *ProxyFeedVideoList {
	return &ProxyFeedVideoList{Context: c}
}

// DoNoToken 未登录的视频流推送处理
func (p *ProxyFeedVideoList) DoNoToken() error {
	rawTimestamp := p.Query("latest_time")
	var latestTime time.Time
	intTime, err := strconv.ParseInt(rawTimestamp, 10, 64)
	if err == nil {
		latestTime = time.Unix(0, intTime*1e6) //注意:前端传来的时间戳是以ms为单位的
	}
	videoList, err := video.QueryFeedVideoList(0, latestTime)
	if err != nil {
		return err
	}
	p.FeedVideoListOk(videoList)
	return nil
}

// DoHasToken 如果是登录状态,则生成UserId字段
func (p *ProxyFeedVideoList) DoHasToken(token string) error {
	//解析成功
	if claim, ok := middleware.ParseToken(token); ok {
		//token超时
		if time.Now().Unix() > claim.ExpiresAt {
			return errors.New("token超时")
		}
		rawTimestamp := p.Query("latest_time")
		var latestTime time.Time
		intTime, err := strconv.ParseInt(rawTimestamp, 10, 64)
		if err != nil {
			latestTime = time.Unix(0, intTime*1e6) //注意:前端传来的时间戳是以ms为单位的
		}
		//调用service层接口
		videoList, err := video.QueryFeedVideoList(claim.UserId, latestTime)
		if err != nil {
			return err
		}
		p.FeedVideoListOk(videoList)
		return nil
	}
	//解析失败
	return errors.New("token不正确")
}

func (p *ProxyFeedVideoList) FeedVideoListError(msg string) {
	p.JSON(http.StatusOK, FeedResponse{CommonResponse: models.CommonResponse{
		StatusCode: 1,
		StatusMsg:  msg,
	}})
}

func (p *ProxyFeedVideoList) FeedVideoListOk(videoList *video.FeedVideoList) {
	p.JSON(http.StatusOK, FeedResponse{
		CommonResponse: models.CommonResponse{
			StatusCode: 0,
		},
		FeedVideoList: videoList,
	},
	)
}


================================================
FILE: deprecated/handlers/video/post_favor_handler.go
================================================
package video

import (
	"errors"
	"github.com/ACking-you/byte_douyin_project/models"
	"github.com/ACking-you/byte_douyin_project/service/video"
	"github.com/gin-gonic/gin"
	"net/http"
	"strconv"
)

func PostFavorHandler(c *gin.Context) {
	NewProxyPostFavorHandler(c).Do()
}

type ProxyPostFavorHandler struct {
	*gin.Context

	userId     int64
	videoId    int64
	actionType int64
}

func NewProxyPostFavorHandler(c *gin.Context) *ProxyPostFavorHandler {
	return &ProxyPostFavorHandler{Context: c}
}

func (p *ProxyPostFavorHandler) Do() {
	//解析参数
	if err := p.parseNum(); err != nil {
		p.SendError(err.Error())
		return
	}

	//正式调用
	err := video.PostFavorState(p.userId, p.videoId, p.actionType)
	if err != nil {
		p.SendError(err.Error())
		return
	}

	//成功返回
	p.SendOk()
}

func (p *ProxyPostFavorHandler) parseNum() error {
	//解析userId
	rawUserId, _ := p.Get("user_id")
	userId, ok := rawUserId.(int64)
	if !ok {
		return errors.New("userId解析出错")
	}

	rawVideoId := p.Query("video_id")
	videoId, err := strconv.ParseInt(rawVideoId, 10, 64)
	if err != nil {
		return err
	}
	rawActionType := p.Query("action_type")
	actionType, err := strconv.ParseInt(rawActionType, 10, 64)
	if err != nil {
		return err
	}
	p.videoId = videoId
	p.actionType = actionType
	p.userId = userId
	return nil
}

func (p *ProxyPostFavorHandler) SendError(msg string) {
	p.JSON(http.StatusOK, models.CommonResponse{StatusCode: 1, StatusMsg: msg})
}

func (p *ProxyPostFavorHandler) SendOk() {
	p.JSON(http.StatusOK, models.CommonResponse{StatusCode: 0})
}


================================================
FILE: deprecated/handlers/video/publish_video_handler.go
================================================
package video

import (
	"github.com/ACking-you/byte_douyin_project/config"
	"github.com/ACking-you/byte_douyin_project/models"
	"github.com/ACking-you/byte_douyin_project/service/video"
	util2 "github.com/ACking-you/byte_douyin_project/util"
	"github.com/gin-gonic/gin"
	"net/http"
	"path/filepath"
)

var (
	videoIndexMap = map[string]struct{}{
		".mp4":  {},
		".avi":  {},
		".wmv":  {},
		".flv":  {},
		".mpeg": {},
		".mov":  {},
	}
	pictureIndexMap = map[string]struct{}{
		".jpg": {},
		".bmp": {},
		".png": {},
		".svg": {},
	}
)

// PublishVideoHandler 发布视频,并截取一帧画面作为封面
func PublishVideoHandler(c *gin.Context) {
	//准备参数
	rawId, _ := c.Get("user_id")

	userId, ok := rawId.(int64)
	if !ok {
		PublishVideoError(c, "解析UserId出错")
		return
	}

	title := c.PostForm("title")

	form, err := c.MultipartForm()
	if err != nil {
		PublishVideoError(c, err.Error())
		return
	}

	//支持多文件上传
	files := form.File["data"]
	for _, file := range files {
		suffix := filepath.Ext(file.Filename)    //得到后缀
		if _, ok := videoIndexMap[suffix]; !ok { //判断是否为视频格式
			PublishVideoError(c, "不支持的视频格式")
			continue
		}
		name := util2.NewFileName(userId) //根据userId得到唯一的文件名
		filename := name + suffix
		savePath := filepath.Join(config.Global.StaticSourcePath, filename)
		err = c.SaveUploadedFile(file, savePath)
		if err != nil {
			PublishVideoError(c, err.Error())
			continue
		}
		//截取一帧画面作为封面
		err = util2.SaveImageFromVideo(name, false)
		if err != nil {
			PublishVideoError(c, err.Error())
			continue
		}
		//数据库持久化
		err := video.PostVideo(userId, filename, name+util2.GetDefaultImageSuffix(), title)
		if err != nil {
			PublishVideoError(c, err.Error())
			continue
		}
		PublishVideoOk(c, file.Filename+"上传成功")
	}
}

func PublishVideoError(c *gin.Context, msg string) {
	c.JSON(http.StatusOK, models.CommonResponse{StatusCode: 1,
		StatusMsg: msg})
}

func PublishVideoOk(c *gin.Context, msg string) {
	c.JSON(http.StatusOK, models.CommonResponse{StatusCode: 0, StatusMsg: msg})
}


================================================
FILE: deprecated/handlers/video/query_favor_videolist_handler.go
================================================
package video

import (
	"errors"
	"github.com/ACking-you/byte_douyin_project/models"
	"github.com/ACking-you/byte_douyin_project/service/video"
	"github.com/gin-gonic/gin"
	"net/http"
)

type FavorVideoListResponse struct {
	models.CommonResponse
	*video.FavorList
}

func QueryFavorVideoListHandler(c *gin.Context) {
	NewProxyFavorVideoListHandler(c).Do()
}

type ProxyFavorVideoListHandler struct {
	*gin.Context

	userId int64
}

func NewProxyFavorVideoListHandler(c *gin.Context) *ProxyFavorVideoListHandler {
	return &ProxyFavorVideoListHandler{Context: c}
}

func (p *ProxyFavorVideoListHandler) Do() {
	//解析参数
	if err := p.parseNum(); err != nil {
		p.SendError(err.Error())
		return
	}

	//正式调用
	favorVideoList, err := video.QueryFavorVideoList(p.userId)
	if err != nil {
		p.SendError(err.Error())
		return
	}

	//成功返回
	p.SendOk(favorVideoList)
}

func (p *ProxyFavorVideoListHandler) parseNum() error {
	rawUserId, _ := p.Get("user_id")
	userId, ok := rawUserId.(int64)
	if !ok {
		return errors.New("userId解析出错")
	}
	p.userId = userId
	return nil
}

func (p *ProxyFavorVideoListHandler) SendError(msg string) {
	p.JSON(http.StatusOK, FavorVideoListResponse{
		CommonResponse: models.CommonResponse{StatusCode: 1, StatusMsg: msg}})
}

func (p *ProxyFavorVideoListHandler) SendOk(favorList *video.FavorList) {
	p.JSON(http.StatusOK, FavorVideoListResponse{CommonResponse: models.CommonResponse{StatusCode: 0},
		FavorList: favorList,
	})
}


================================================
FILE: deprecated/handlers/video/query_videolist_handler.go
================================================
package video

import (
	"errors"
	"github.com/ACking-you/byte_douyin_project/models"
	"github.com/ACking-you/byte_douyin_project/service/video"
	"github.com/gin-gonic/gin"
	"net/http"
)

type ListResponse struct {
	models.CommonResponse
	*video.List
}

func QueryVideoListHandler(c *gin.Context) {
	p := NewProxyQueryVideoList(c)
	rawId, _ := c.Get("user_id")
	err := p.DoQueryVideoListByUserId(rawId)
	if err != nil {
		p.QueryVideoListError(err.Error())
	}
}

// ProxyQueryVideoList 代理类
type ProxyQueryVideoList struct {
	c *gin.Context
}

func NewProxyQueryVideoList(c *gin.Context) *ProxyQueryVideoList {
	return &ProxyQueryVideoList{c: c}
}

// DoQueryVideoListByUserId 根据userId字段进行查询
func (p *ProxyQueryVideoList) DoQueryVideoListByUserId(rawId interface{}) error {
	userId, ok := rawId.(int64)
	if !ok {
		return errors.New("userId解析出错")
	}

	videoList, err := video.QueryVideoListByUserId(userId)
	if err != nil {
		return err
	}

	p.QueryVideoListOk(videoList)
	return nil
}

func (p *ProxyQueryVideoList) QueryVideoListError(msg string) {
	p.c.JSON(http.StatusOK, ListResponse{CommonResponse: models.CommonResponse{
		StatusCode: 1,
		StatusMsg:  msg,
	}})
}

func (p *ProxyQueryVideoList) QueryVideoListOk(videoList *video.List) {
	p.c.JSON(http.StatusOK, ListResponse{
		CommonResponse: models.CommonResponse{
			StatusCode: 0,
		},
		List: videoList,
	})
}


================================================
FILE: deprecated/main.go
================================================
package main

import (
	"fmt"
	"github.com/ACking-you/byte_douyin_project/config"
	"github.com/ACking-you/byte_douyin_project/router"
)

func main() {
	r := router.Init()
	err := r.Run(fmt.Sprintf(":%d", config.Global.Port)) // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
	if err != nil {
		return
	}
}


================================================
FILE: deprecated/middleware/jwt.go
================================================
package middleware

import (
	models2 "github.com/ACking-you/byte_douyin_project/models"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"net/http"
	"time"
)

var jwtKey = []byte("acking-you.xyz")

type Claims struct {
	UserId int64
	jwt.StandardClaims
}

// ReleaseToken 颁发token
func ReleaseToken(user models2.UserLogin) (string, error) {
	expirationTime := time.Now().Add(7 * 24 * time.Hour)
	claims := &Claims{
		UserId: user.UserInfoId,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: expirationTime.Unix(),
			IssuedAt:  time.Now().Unix(),
			Issuer:    "douyin_pro_131",
			Subject:   "L_B__",
		}}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	tokenString, err := token.SignedString(jwtKey)
	if err != nil {
		return "", err
	}
	return tokenString, nil
}

// ParseToken 解析token
func ParseToken(tokenString string) (*Claims, bool) {
	token, _ := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
		return jwtKey, nil
	})
	if token != nil {
		if key, ok := token.Claims.(*Claims); ok {
			if token.Valid {
				return key, true
			} else {
				return key, false
			}
		}
	}
	return nil, false
}

// JWTMiddleWare 鉴权中间件,鉴权并设置user_id
func JWTMiddleWare() gin.HandlerFunc {
	return func(c *gin.Context) {
		tokenStr := c.Query("token")
		if tokenStr == "" {
			tokenStr = c.PostForm("token")
		}
		//用户不存在
		if tokenStr == "" {
			c.JSON(http.StatusOK, models2.CommonResponse{StatusCode: 401, StatusMsg: "用户不存在"})
			c.Abort() //阻止执行
			return
		}
		//验证token
		tokenStruck, ok := ParseToken(tokenStr)
		if !ok {
			c.JSON(http.StatusOK, models2.CommonResponse{
				StatusCode: 403,
				StatusMsg:  "token不正确",
			})
			c.Abort() //阻止执行
			return
		}
		//token超时
		if time.Now().Unix() > tokenStruck.ExpiresAt {
			c.JSON(http.StatusOK, models2.CommonResponse{
				StatusCode: 402,
				StatusMsg:  "token过期",
			})
			c.Abort() //阻止执行
			return
		}
		c.Set("user_id", tokenStruck.UserId)
		c.Next()
	}
}


================================================
FILE: deprecated/middleware/jwt_test.go
================================================
package middleware

import (
	"testing"
)

func TestJwt(t *testing.T) {

}


================================================
FILE: deprecated/middleware/normal.go
================================================
package middleware

import (
	"github.com/ACking-you/byte_douyin_project/models"
	"github.com/gin-gonic/gin"
	"net/http"
	"strconv"
)

func NoAuthToGetUserId() gin.HandlerFunc {
	return func(c *gin.Context) {
		rawId := c.Query("user_id")
		if rawId == "" {
			rawId = c.PostForm("user_id")
		}
		//用户不存在
		if rawId == "" {
			c.JSON(http.StatusOK, models.CommonResponse{StatusCode: 401, StatusMsg: "用户不存在"})
			c.Abort() //阻止执行
			return
		}
		userId, err := strconv.ParseInt(rawId, 10, 64)
		if err != nil {
			c.JSON(http.StatusOK, models.CommonResponse{StatusCode: 401, StatusMsg: "用户不存在"})
			c.Abort() //阻止执行
		}
		c.Set("user_id", userId)
		c.Next()
	}
}


================================================
FILE: deprecated/middleware/password.go
================================================
package middleware

import (
	"crypto/sha1"
	"encoding/hex"
	"github.com/gin-gonic/gin"
)

func SHA1(s string) string {

	o := sha1.New()

	o.Write([]byte(s))

	return hex.EncodeToString(o.Sum(nil))
}

func SHAMiddleWare() gin.HandlerFunc {
	return func(context *gin.Context) {
		password := context.Query("password")
		if password == "" {
			password = context.PostForm("password")
		}
		context.Set("password", SHA1(password))
		context.Next()
	}
}


================================================
FILE: deprecated/middleware/password_test.go
================================================
package middleware

import "testing"

//40bd001563085fc35165329ea1ff5c5ecbdbbeef
func TestSHA1(t *testing.T) {
	print(SHA1("123"))
}


================================================
FILE: deprecated/models/comment.go
================================================
package models

import (
	"errors"
	"gorm.io/gorm"
	"time"
)

type Comment struct {
	Id         int64     `json:"id"`
	UserInfoId int64     `json:"-"` //用于一对多关系的id
	VideoId    int64     `json:"-"` //一对多,视频对评论
	User       UserInfo  `json:"user" gorm:"-"`
	Content    string    `json:"content"`
	CreatedAt  time.Time `json:"-"`
	CreateDate string    `json:"create_date" gorm:"-"`
}

type CommentDAO struct {
}

var (
	commentDao CommentDAO
)

func NewCommentDAO() *CommentDAO {
	return &commentDao
}

func (c *CommentDAO) AddCommentAndUpdateCount(comment *Comment) error {
	if comment == nil {
		return errors.New("AddCommentAndUpdateCount comment空指针")
	}
	//执行事务
	return DB.Transaction(func(tx *gorm.DB) error {
		//添加评论数据
		if err := tx.Create(comment).Error; err != nil {
			// 返回任何错误都会回滚事务
			return err
		}
		//增加count
		if err := tx.Exec("UPDATE videos v SET v.comment_count = v.comment_count+1 WHERE v.id=?", comment.VideoId).Error; err != nil {
			return err
		}

		// 返回 nil 提交事务
		return nil
	})
}

func (c *CommentDAO) DeleteCommentAndUpdateCountById(commentId, videoId int64) error {
	//执行事务
	return DB.Transaction(func(tx *gorm.DB) error {
		//删除评论
		if err := tx.Exec("DELETE FROM comments WHERE id = ?", commentId).Error; err != nil {
			// 返回任何错误都会回滚事务
			return err
		}
		//减少count
		if err := tx.Exec("UPDATE videos v SET v.comment_count = v.comment_count-1 WHERE v.id=? AND v.comment_count>0", videoId).Error; err != nil {
			return err
		}
		// 返回 nil 提交事务
		return nil
	})
}

func (c *CommentDAO) QueryCommentById(id int64, comment *Comment) error {
	if comment == nil {
		return errors.New("QueryCommentById comment 空指针")
	}
	return DB.Where("id=?", id).First(comment).Error
}

func (c *CommentDAO) QueryCommentListByVideoId(videoId int64, comments *[]*Comment) error {
	if comments == nil {
		return errors.New("QueryCommentListByVideoId comments空指针")
	}
	if err := DB.Model(&Comment{}).Where("video_id=?", videoId).Find(comments).Error; err != nil {
		return err
	}
	return nil
}


================================================
FILE: deprecated/models/common.go
================================================
package models

type CommonResponse struct {
	StatusCode int32  `json:"status_code"`
	StatusMsg  string `json:"status_msg,omitempty"`
}


================================================
FILE: deprecated/models/init_db.go
================================================
package models

import (
	"github.com/ACking-you/byte_douyin_project/config"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

var DB *gorm.DB

func InitDB() {
	var err error
	DB, err = gorm.Open(mysql.Open(config.DBConnectString()), &gorm.Config{
		PrepareStmt:            true, //缓存预编译命令
		SkipDefaultTransaction: true, //禁用默认事务操作
		//Logger:                 logger.Default.LogMode(logger.Global), //打印sql语句
	})
	if err != nil {
		panic(err)
	}
	err = DB.AutoMigrate(&UserInfo{}, &Video{}, &Comment{}, &UserLogin{})
	if err != nil {
		panic(err)
	}
}


================================================
FILE: deprecated/models/user_info.go
================================================
package models

import (
	"errors"
	"gorm.io/gorm"
	"log"
	"sync"
)

var (
	ErrIvdPtr        = errors.New("空指针错误")
	ErrEmptyUserList = errors.New("用户列表为空")
)

type UserInfo struct {
	Id            int64       `json:"id" gorm:"id,omitempty"`
	Name          string      `json:"name" gorm:"name,omitempty"`
	FollowCount   int64       `json:"follow_count" gorm:"follow_count,omitempty"`
	FollowerCount int64       `json:"follower_count" gorm:"follower_count,omitempty"`
	IsFollow      bool        `json:"is_follow" gorm:"is_follow,omitempty"`
	User          *UserLogin  `json:"-"`                                     //用户与账号密码之间的一对一
	Videos        []*Video    `json:"-"`                                     //用户与投稿视频的一对多
	Follows       []*UserInfo `json:"-" gorm:"many2many:user_relations;"`    //用户之间的多对多
	FavorVideos   []*Video    `json:"-" gorm:"many2many:user_favor_videos;"` //用户与点赞视频之间的多对多
	Comments      []*Comment  `json:"-"`                                     //用户与评论的一对多
}

type UserInfoDAO struct {
}

var (
	userInfoDAO  *UserInfoDAO
	userInfoOnce sync.Once
)

func NewUserInfoDAO() *UserInfoDAO {
	userInfoOnce.Do(func() {
		userInfoDAO = new(UserInfoDAO)
	})
	return userInfoDAO
}

func (u *UserInfoDAO) QueryUserInfoById(userId int64, userinfo *UserInfo) error {
	if userinfo == nil {
		return ErrIvdPtr
	}
	//DB.Where("id=?",userId).First(userinfo)
	DB.Where("id=?", userId).Select([]string{"id", "name", "follow_count", "follower_count", "is_follow"}).First(userinfo)
	//id为零值,说明sql执行失败
	if userinfo.Id == 0 {
		return errors.New("该用户不存在")
	}
	return nil
}

func (u *UserInfoDAO) AddUserInfo(userinfo *UserInfo) error {
	if userinfo == nil {
		return ErrIvdPtr
	}
	return DB.Create(userinfo).Error
}

func (u *UserInfoDAO) IsUserExistById(id int64) bool {
	var userinfo UserInfo
	if err := DB.Where("id=?", id).Select("id").First(&userinfo).Error; err != nil {
		log.Println(err)
	}
	if userinfo.Id == 0 {
		return false
	}
	return true
}
func (u *UserInfoDAO) AddUserFollow(userId, userToId int64) error {
	return DB.Transaction(func(tx *gorm.DB) error {
		if err := tx.Exec("UPDATE user_infos SET follow_count=follow_count+1 WHERE id = ?", userId).Error; err != nil {
			return err
		}
		if err := tx.Exec("UPDATE user_infos SET follower_count=follower_count+1 WHERE id = ?", userToId).Error; err != nil {
			return err
		}
		if err := tx.Exec("INSERT INTO `user_relations` (`user_info_id`,`follow_id`) VALUES (?,?)", userId, userToId).Error; err != nil {
			return err
		}
		return nil
	})
}

func (u *UserInfoDAO) CancelUserFollow(userId, userToId int64) error {
	return DB.Transaction(func(tx *gorm.DB) error {
		if err := tx.Exec("UPDATE user_infos SET follow_count=follow_count-1 WHERE id = ? AND follow_count>0", userId).Error; err != nil {
			return err
		}
		if err := tx.Exec("UPDATE user_infos SET follower_count=follower_count-1 WHERE id = ? AND follower_count>0", userToId).Error; err != nil {
			return err
		}
		if err := tx.Exec("DELETE FROM `user_relations` WHERE user_info_id=? AND follow_id=?", userId, userToId).Error; err != nil {
			return err
		}
		return nil
	})
}

func (u *UserInfoDAO) GetFollowListByUserId(userId int64, userList *[]*UserInfo) error {
	if userList == nil {
		return ErrIvdPtr
	}
	var err error
	if err = DB.Raw("SELECT u.* FROM user_relations r, user_infos u WHERE r.user_info_id = ? AND r.follow_id = u.id", userId).Scan(userList).Error; err != nil {
		return err
	}
	if len(*userList) == 0 || (*userList)[0].Id == 0 {
		return ErrEmptyUserList
	}
	return nil
}

func (u *UserInfoDAO) GetFollowerListByUserId(userId int64, userList *[]*UserInfo) error {
	if userList == nil {
		return ErrIvdPtr
	}
	var err error
	if err = DB.Raw("SELECT u.* FROM user_relations r, user_infos u WHERE r.follow_id = ? AND r.user_info_id = u.id", userId).Scan(userList).Error; err != nil {
		return err
	}
	//if len(*userList) == 0 || (*userList)[0].Id == 0 {
	//	return ErrEmptyUserList
	//}
	return nil
}


================================================
FILE: deprecated/models/user_info_test.go
================================================
package models

import (
	"fmt"
	"os"
	"testing"
)

func TestMain(m *testing.M) {
	InitDB()
	code := m.Run()
	os.Exit(code)
}

func TestUserInfoDAO_GetFollowListByUserId(t *testing.T) {
	var userList []*UserInfo
	err := NewUserInfoDAO().GetFollowListByUserId(1, &userList)
	if err != nil {
		panic(err)
	}
	for _, user := range userList {
		fmt.Printf("%#v\n", *user)
	}
}

func TestUserInfoDAO_GetFollowerListByUserId(t *testing.T) {
	var userList []*UserInfo
	err := NewUserInfoDAO().GetFollowerListByUserId(2, &userList)
	if err != nil {
		panic(err)
	}
	for _, user := range userList {
		fmt.Printf("%#v\n", *user)
	}
}


================================================
FILE: deprecated/models/user_login.go
================================================
package models

import (
	"errors"
	"sync"
)

// UserLogin 用户登录表,和UserInfo属于一对一关系
type UserLogin struct {
	Id         int64 `gorm:"primary_key"`
	UserInfoId int64
	Username   string `gorm:"primary_key"`
	Password   string `gorm:"size:200;notnull"`
}

type UserLoginDAO struct {
}

var (
	userLoginDao  *UserLoginDAO
	userLoginOnce sync.Once
)

func NewUserLoginDao() *UserLoginDAO {
	userLoginOnce.Do(func() {
		userLoginDao = new(UserLoginDAO)
	})
	return userLoginDao
}

func (u *UserLoginDAO) QueryUserLogin(username, password string, login *UserLogin) error {
	if login == nil {
		return errors.New("结构体指针为空")
	}
	DB.Where("username=? and password=?", username, password).First(login)
	if login.Id == 0 {
		return errors.New("用户不存在,账号或密码出错")
	}
	return nil
}

func (u *UserLoginDAO) IsUserExistByUsername(username string) bool {
	var userLogin UserLogin
	DB.Where("username=?", username).First(&userLogin)
	if userLogin.Id == 0 {
		return false
	}
	return true
}


================================================
FILE: deprecated/models/video.go
================================================
package models

import (
	"errors"
	"gorm.io/gorm"
	"log"
	"sync"
	"time"
)

type Video struct {
	Id            int64       `json:"id,omitempty"`
	UserInfoId    int64       `json:"-"`
	Author        UserInfo    `json:"author,omitempty" gorm:"-"` //这里应该是作者对视频的一对多的关系,而不是视频对作者,故gorm不能存他,但json需要返回它
	PlayUrl       string      `json:"play_url,omitempty"`
	CoverUrl      string      `json:"cover_url,omitempty"`
	FavoriteCount int64       `json:"favorite_count,omitempty"`
	CommentCount  int64       `json:"comment_count,omitempty"`
	IsFavorite    bool        `json:"is_favorite,omitempty"`
	Title         string      `json:"title,omitempty"`
	Users         []*UserInfo `json:"-" gorm:"many2many:user_favor_videos;"`
	Comments      []*Comment  `json:"-"`
	CreatedAt     time.Time   `json:"-"`
	UpdatedAt     time.Time   `json:"-"`
}

type VideoDAO struct {
}

var (
	videoDAO  *VideoDAO
	videoOnce sync.Once
)

func NewVideoDAO() *VideoDAO {
	videoOnce.Do(func() {
		videoDAO = new(VideoDAO)
	})
	return videoDAO
}

// AddVideo 添加视频
// 注意:由于视频和userinfo有多对一的关系,所以传入的Video参数一定要进行id的映射处理!
func (v *VideoDAO) AddVideo(video *Video) error {
	if video == nil {
		return errors.New("AddVideo video 空指针")
	}
	return DB.Create(video).Error
}

func (v *VideoDAO) QueryVideoByVideoId(videoId int64, video *Video) error {
	if video == nil {
		return errors.New("QueryVideoByVideoId 空指针")
	}
	return DB.Where("id=?", videoId).
		Select([]string{"id", "user_info_id", "play_url", "cover_url", "favorite_count", "comment_count", "is_favorite", "title"}).
		First(video).Error
}

func (v *VideoDAO) QueryVideoCountByUserId(userId int64, count *int64) error {
	if count == nil {
		return errors.New("QueryVideoCountByUserId count 空指针")
	}
	return DB.Model(&Video{}).Where("user_info_id=?", userId).Count(count).Error
}

func (v *VideoDAO) QueryVideoListByUserId(userId int64, videoList *[]*Video) error {
	if videoList == nil {
		return errors.New("QueryVideoListByUserId videoList 空指针")
	}
	return DB.Where("user_info_id=?", userId).
		Select([]string{"id", "user_info_id", "play_url", "cover_url", "favorite_count", "comment_count", "is_favorite", "title"}).
		Find(videoList).Error
}

// QueryVideoListByLimitAndTime  返回按投稿时间倒序的视频列表,并限制为最多limit个
func (v *VideoDAO) QueryVideoListByLimitAndTime(limit int, latestTime time.Time, videoList *[]*Video) error {
	if videoList == nil {
		return errors.New("QueryVideoListByLimit videoList 空指针")
	}
	return DB.Model(&Video{}).Where("created_at<?", latestTime).
		Order("created_at ASC").Limit(limit).
		Select([]string{"id", "user_info_id", "play_url", "cover_url", "favorite_count", "comment_count", "is_favorite", "title", "created_at", "updated_at"}).
		Find(videoList).Error
}

// PlusOneFavorByUserIdAndVideoId 增加一个赞
func (v *VideoDAO) PlusOneFavorByUserIdAndVideoId(userId int64, videoId int64) error {
	return DB.Transaction(func(tx *gorm.DB) error {
		if err := tx.Exec("UPDATE videos SET favorite_count=favorite_count+1 WHERE id = ?", videoId).Error; err != nil {
			return err
		}
		if err := tx.Exec("INSERT INTO `user_favor_videos` (`user_info_id`,`video_id`) VALUES (?,?)", userId, videoId).Error; err != nil {
			return err
		}
		return nil
	})
}

// MinusOneFavorByUserIdAndVideoId 减少一个赞
func (v *VideoDAO) MinusOneFavorByUserIdAndVideoId(userId int64, videoId int64) error {
	return DB.Transaction(func(tx *gorm.DB) error {
		//执行-1之前需要先判断是否合法(不能被减少为负数
		if err := tx.Exec("UPDATE videos SET favorite_count=favorite_count-1 WHERE id = ? AND favorite_count>0", videoId).Error; err != nil {
			return err
		}
		if err := tx.Exec("DELETE FROM `user_favor_videos`  WHERE `user_info_id` = ? AND `video_id` = ?", userId, videoId).Error; err != nil {
			return err
		}
		return nil
	})
}

func (v *VideoDAO) QueryFavorVideoListByUserId(userId int64, videoList *[]*Video) error {
	if videoList == nil {
		return errors.New("QueryFavorVideoListByUserId videoList 空指针")
	}
	//多表查询,左连接得到结果,再映射到数据
	if err := DB.Raw("SELECT v.* FROM user_favor_videos u , videos v WHERE u.user_info_id = ? AND u.video_id = v.id", userId).Scan(videoList).Error; err != nil {
		return err
	}
	//如果id为0,则说明没有查到数据
	if len(*videoList) == 0 || (*videoList)[0].Id == 0 {
		return errors.New("点赞列表为空")
	}
	return nil
}

func (v *VideoDAO) IsVideoExistById(id int64) bool {
	var video Video
	if err := DB.Where("id=?", id).Select("id").First(&video).Error; err != nil {
		log.Println(err)
	}
	if video.Id == 0 {
		return false
	}
	return true
}


================================================
FILE: deprecated/models/video_test.go
================================================
package models

import (
	"fmt"
	"testing"
	"time"
)

func TestVideoDAO_QueryVideoListByUserId(t *testing.T) {
	InitDB()
	s := make([]*Video, 8)
	err := NewVideoDAO().QueryVideoListByUserId(1, &s)
	if err != nil {
		panic(err)
	}
	for _, v := range s {
		fmt.Printf("%#v\n", *v)
	}
}

func TestVideoDAO_QueryVideoListByLimit(t *testing.T) {
	InitDB()
	s := make([]*Video, 8)
	err := NewVideoDAO().QueryVideoListByLimitAndTime(2, time.Unix(1652895580, 0), &s)
	if err != nil {
		panic(err)
	}
	for _, v := range s {
		fmt.Printf("%#v\n", *v)
	}
}

func TestTime(t *testing.T) {
	println(time.Now().UnixNano())
}


================================================
FILE: deprecated/router/router_douyin.go
================================================
package router

import (
	"github.com/ACking-you/byte_douyin_project/config"
	comment2 "github.com/ACking-you/byte_douyin_project/handlers/comment"
	user_info2 "github.com/ACking-you/byte_douyin_project/handlers/user_info"
	user_login2 "github.com/ACking-you/byte_douyin_project/handlers/user_login"
	video2 "github.com/ACking-you/byte_douyin_project/handlers/video"
	middleware2 "github.com/ACking-you/byte_douyin_project/middleware"
	"github.com/ACking-you/byte_douyin_project/models"
	"github.com/gin-gonic/gin"
)

func Init() *gin.Engine {
	models.InitDB()
	r := gin.Default()

	r.Static("static", config.Global.StaticSourcePath)

	baseGroup := r.Group("/douyin")
	//根据灵活性考虑是否加入JWT中间件来进行鉴权,还是在之后再做鉴权
	// basic apis
	baseGroup.GET("/feed/", video2.FeedVideoListHandler)
	baseGroup.GET("/user/", middleware2.JWTMiddleWare(), user_info2.UserInfoHandler)
	baseGroup.POST("/user/login/", middleware2.SHAMiddleWare(), user_login2.UserLoginHandler)
	baseGroup.POST("/user/register/", middleware2.SHAMiddleWare(), user_login2.UserRegisterHandler)
	baseGroup.POST("/publish/action/", middleware2.JWTMiddleWare(), video2.PublishVideoHandler)
	baseGroup.GET("/publish/list/", middleware2.NoAuthToGetUserId(), video2.QueryVideoListHandler)

	//extend 1
	baseGroup.POST("/favorite/action/", middleware2.JWTMiddleWare(), video2.PostFavorHandler)
	baseGroup.GET("/favorite/list/", middleware2.NoAuthToGetUserId(), video2.QueryFavorVideoListHandler)
	baseGroup.POST("/comment/action/", middleware2.JWTMiddleWare(), comment2.PostCommentHandler)
	baseGroup.GET("/comment/list/", middleware2.JWTMiddleWare(), comment2.QueryCommentListHandler)

	//extend 2
	baseGroup.POST("/relation/action/", middleware2.JWTMiddleWare(), user_info2.PostFollowActionHandler)
	baseGroup.GET("/relation/follow/list/", middleware2.NoAuthToGetUserId(), user_info2.QueryFollowListHandler)
	baseGroup.GET("/relation/follower/list/", middleware2.NoAuthToGetUserId(), user_info2.QueryFollowerHandler)
	return r
}


================================================
FILE: deprecated/service/comment/post_comment.go
================================================
package comment

import (
	"errors"
	"fmt"
	models2 "github.com/ACking-you/byte_douyin_project/models"
	"github.com/ACking-you/byte_douyin_project/util"
)

const (
	CREATE = 1
	DELETE = 2
)

type Response struct {
	MyComment *models2.Comment `json:"comment"`
}

func PostComment(userId int64, videoId int64, commentId int64, actionType int64, commentText string) (*Response, error) {
	return NewPostCommentFlow(userId, videoId, commentId, actionType, commentText).Do()
}

type PostCommentFlow struct {
	userId      int64
	videoId     int64
	commentId   int64
	actionType  int64
	commentText string

	comment *models2.Comment

	*Response
}

func NewPostCommentFlow(userId int64, videoId int64, commentId int64, actionType int64, commentText string) *PostCommentFlow {
	return &PostCommentFlow{userId: userId, videoId: videoId, commentId: commentId, actionType: actionType, commentText: commentText}
}

func (p *PostCommentFlow) Do() (*Response, error) {
	var err error
	if err = p.checkNum(); err != nil {
		return nil, err
	}
	if err = p.prepareData(); err != nil {
		return nil, err
	}
	if err = p.packData(); err != nil {
		return nil, err
	}
	return p.Response, err
}

// CreateComment 增加评论
func (p *PostCommentFlow) CreateComment() (*models2.Comment, error) {
	comment := models2.Comment{UserInfoId: p.userId, VideoId: p.videoId, Content: p.commentText}
	err := models2.NewCommentDAO().AddCommentAndUpdateCount(&comment)
	if err != nil {
		return nil, err
	}

	return &comment, nil
}

// DeleteComment 删除评论
func (p *PostCommentFlow) DeleteComment() (*models2.Comment, error) {
	//获取comment
	var comment models2.Comment
	err := models2.NewCommentDAO().QueryCommentById(p.commentId, &comment)
	if err != nil {
		return nil, err
	}
	//删除comment
	err = models2.NewCommentDAO().DeleteCommentAndUpdateCountById(p.commentId, p.videoId)
	if err != nil {
		return nil, err
	}
	return &comment, nil
}

func (p *PostCommentFlow) checkNum() error {
	if !models2.NewUserInfoDAO().IsUserExistById(p.userId) {
		return fmt.Errorf("用户%d不存在", p.userId)
	}
	if !models2.NewVideoDAO().IsVideoExistById(p.videoId) {
		return fmt.Errorf("视频%d不存在", p.videoId)
	}
	if p.actionType != CREATE && p.actionType != DELETE {
		return errors.New("未定义的行为")
	}
	return nil
}

func (p *PostCommentFlow) prepareData() error {
	var err error
	switch p.actionType {
	case CREATE:
		p.comment, err = p.CreateComment()
	case DELETE:
		p.comment, err = p.DeleteComment()
	default:
		return errors.New("未定义的操作")
	}
	return err
}

func (p *PostCommentFlow) packData() error {
	//填充字段
	userInfo := models2.UserInfo{}
	_ = models2.NewUserInfoDAO().QueryUserInfoById(p.comment.UserInfoId, &userInfo)
	p.comment.User = userInfo
	_ = util.FillCommentFields(p.comment)

	p.Response = &Response{MyComment: p.comment}

	return nil
}


================================================
FILE: deprecated/service/comment/query_comment_list.go
================================================
package comment

import (
	"errors"
	"fmt"
	models2 "github.com/ACking-you/byte_douyin_project/models"
	"github.com/ACking-you/byte_douyin_project/util"
)

type List struct {
	Comments []*models2.Comment `json:"comment_list"`
}

func QueryCommentList(userId, videoId int64) (*List, error) {
	return NewQueryCommentListFlow(userId, videoId).Do()
}

type QueryCommentListFlow struct {
	userId  int64
	videoId int64

	comments []*models2.Comment

	commentList *List
}

func NewQueryCommentListFlow(userId, videoId int64) *QueryCommentListFlow {
	return &QueryCommentListFlow{userId: userId, videoId: videoId}
}

func (q *QueryCommentListFlow) Do() (*List, error) {
	if err := q.checkNum(); err != nil {
		return nil, err
	}
	if err := q.prepareData(); err != nil {
		return nil, err
	}
	if err := q.packData(); err != nil {
		return nil, err
	}
	return q.commentList, nil
}

func (q *QueryCommentListFlow) checkNum() error {
	if !models2.NewUserInfoDAO().IsUserExistById(q.userId) {
		return fmt.Errorf("用户%d处于登出状态", q.userId)
	}
	if !models2.NewVideoDAO().IsVideoExistById(q.videoId) {
		return fmt.Errorf("视频%d不存在或已经被删除", q.videoId)
	}
	return nil
}

func (q *QueryCommentListFlow) prepareData() error {
	err := models2.NewCommentDAO().QueryCommentListByVideoId(q.videoId, &q.comments)
	if err != nil {
		return err
	}
	//根据前端的要求填充正确的时间格式
	err = util.FillCommentListFields(&q.comments)
	if err != nil {
		return errors.New("暂时还没有人评论")
	}
	return nil
}

func (q *QueryCommentListFlow) packData() error {
	q.commentList = &List{Comments: q.comments}
	return nil
}


================================================
FILE: deprecated/service/user_info/post_follow_action.go
================================================
package user_info

import (
	"errors"
	"github.com/ACking-you/byte_douyin_project/cache"
	"github.com/ACking-you/byte_douyin_project/models"
)

const (
	FOLLOW = 1
	CANCEL = 2
)

var (
	ErrIvdAct    = errors.New("未定义操作")
	ErrIvdFolUsr = errors.New("关注用户不存在")
)

func PostFollowAction(userId, userToId int64, actionType int) error {
	return NewPostFollowActionFlow(userId, userToId, actionType).Do()
}

type PostFollowActionFlow struct {
	userId     int64
	userToId   int64
	actionType int
}

func NewPostFollowActionFlow(userId int64, userToId int64, actionType int) *PostFollowActionFlow {
	return &PostFollowActionFlow{userId: userId, userToId: userToId, actionType: actionType}
}

func (p *PostFollowActionFlow) Do() error {
	var err error
	if err = p.checkNum(); err != nil {
		return err
	}
	if err = p.publish(); err != nil {
		return err
	}
	return nil
}

func (p *PostFollowActionFlow) checkNum() error {
	//由于userId是经过乐token鉴权故不需要check,只需要检查userToId
	if !models.NewUserInfoDAO().IsUserExistById(p.userToId) {
		return ErrIvdFolUsr
	}
	if p.actionType != FOLLOW && p.actionType != CANCEL {
		return ErrIvdAct
	}
	//自己不能关注自己
	if p.userId == p.userToId {
		return ErrIvdAct
	}
	return nil
}

func (p *PostFollowActionFlow) publish() error {
	userDAO := models.NewUserInfoDAO()
	var err error
	switch p.actionType {
	case FOLLOW:
		err = userDAO.AddUserFollow(p.userId, p.userToId)
		//更新redis的关注信息
		cache.NewProxyIndexMap().UpdateUserRelation(p.userId, p.userToId, true)
	case CANCEL:
		err = userDAO.CancelUserFollow(p.userId, p.userToId)
		cache.NewProxyIndexMap().UpdateUserRelation(p.userId, p.userToId, false)
	default:
		return ErrIvdAct
	}
	return err
}


================================================
FILE: deprecated/service/user_info/query_follow_list.go
================================================
package user_info

import (
	"errors"
	"github.com/ACking-you/byte_douyin_project/models"
)

var (
	ErrUserNotExist = errors.New("用户不存在或已注销")
)

type FollowList struct {
	UserList []*models.UserInfo `json:"user_list"`
}

func QueryFollowList(userId int64) (*FollowList, error) {
	return NewQueryFollowListFlow(userId).Do()
}

type QueryFollowListFlow struct {
	userId int64

	userList []*models.UserInfo

	*FollowList
}

func NewQueryFollowListFlow(userId int64) *QueryFollowListFlow {
	return &QueryFollowListFlow{userId: userId}
}

func (q *QueryFollowListFlow) Do() (*FollowList, error) {
	var err error
	if err = q.checkNum(); err != nil {
		return nil, err
	}
	if err = q.prepareData(); err != nil {
		return nil, err
	}
	if err = q.packData(); err != nil {
		return nil, err
	}

	return q.FollowList, nil
}

func (q *QueryFollowListFlow) checkNum() error {
	if !models.NewUserInfoDAO().IsUserExistById(q.userId) {
		return ErrUserNotExist
	}
	return nil
}

func (q *QueryFollowListFlow) prepareData() error {
	var userList []*models.UserInfo
	err := models.NewUserInfoDAO().GetFollowListByUserId(q.userId, &userList)
	if err != nil {
		return err
	}
	for i, _ := range userList {
		userList[i].IsFollow = true //当前用户的关注列表,故isFollow定为true
	}
	q.userList = userList
	return nil
}

func (q *QueryFollowListFlow) packData() error {
	q.FollowList = &FollowList{UserList: q.userList}

	return nil
}


================================================
FILE: deprecated/service/user_info/query_follower_list.go
================================================
package user_info

import (
	"github.com/ACking-you/byte_douyin_project/cache"
	"github.com/ACking-you/byte_douyin_project/models"
)

type FollowerList struct {
	UserList []*models.UserInfo `json:"user_list"`
}

func QueryFollowerList(userId int64) (*FollowerList, error) {
	return NewQueryFollowerListFlow(userId).Do()
}

type QueryFollowerListFlow struct {
	userId int64

	userList []*models.UserInfo

	*FollowerList
}

func NewQueryFollowerListFlow(userId int64) *QueryFollowerListFlow {
	return &QueryFollowerListFlow{userId: userId}
}

func (q *QueryFollowerListFlow) Do() (*FollowerList, error) {
	var err error
	if err = q.checkNum(); err != nil {
		return nil, err
	}
	if err = q.prepareData(); err != nil {
		return nil, err
	}
	if err = q.packData(); err != nil {
		return nil, err
	}
	return q.FollowerList, nil
}

func (q *QueryFollowerListFlow) checkNum() error {
	if !models.NewUserInfoDAO().IsUserExistById(q.userId) {
		return ErrUserNotExist
	}
	return nil
}

func (q *QueryFollowerListFlow) prepareData() error {

	err := models.NewUserInfoDAO().GetFollowerListByUserId(q.userId, &q.userList)
	if err != nil {
		return err
	}
	//填充is_follow字段
	for _, v := range q.userList {
		v.IsFollow = cache.NewProxyIndexMap().GetUserRelation(q.userId, v.Id)
	}
	return nil
}

func (q *QueryFollowerListFlow) packData() error {
	q.FollowerList = &FollowerList{UserList: q.userList}

	return nil
}


================================================
FILE: deprecated/service/user_login/post_user_login.go
================================================
package user_login

import (
	"errors"
	"github.com/ACking-you/byte_douyin_project/middleware"
	models2 "github.com/ACking-you/byte_douyin_project/models"
)

// PostUserLogin 注册用户并得到token和id
func PostUserLogin(username, password string) (*LoginResponse, error) {
	return NewPostUserLoginFlow(username, password).Do()
}

func NewPostUserLoginFlow(username, password string) *PostUserLoginFlow {
	return &PostUserLoginFlow{username: username, password: password}
}

type PostUserLoginFlow struct {
	username string
	password string

	data   *LoginResponse
	userid int64
	token  string
}

func (q *PostUserLoginFlow) Do() (*LoginResponse, error) {
	//对参数进行合法性验证
	if err := q.checkNum(); err != nil {
		return nil, err
	}

	//更新数据到数据库
	if err := q.updateData(); err != nil {
		return nil, err
	}

	//打包response
	if err := q.packResponse(); err != nil {
		return nil, err
	}
	return q.data, nil
}

func (q *PostUserLoginFlow) checkNum() error {
	if q.username == "" {
		return errors.New("用户名为空")
	}
	if len(q.username) > MaxUsernameLength {
		return errors.New("用户名长度超出限制")
	}
	if q.password == "" {
		return errors.New("密码为空")
	}
	return nil
}

func (q *PostUserLoginFlow) updateData() error {

	//准备好userInfo,默认name为username
	userLogin := models2.UserLogin{Username: q.username, Password: q.password}
	userinfo := models2.UserInfo{User: &userLogin, Name: q.username}

	//判断用户名是否已经存在
	userLoginDAO := models2.NewUserLoginDao()
	if userLoginDAO.IsUserExistByUsername(q.username) {
		return errors.New("用户名已存在")
	}

	//更新操作,由于userLogin属于userInfo,故更新userInfo即可,且由于传入的是指针,所以插入的数据内容也是清楚的
	userInfoDAO := models2.NewUserInfoDAO()
	err := userInfoDAO.AddUserInfo(&userinfo)
	if err != nil {
		return err
	}

	//颁发token
	token, err := middleware.ReleaseToken(userLogin)
	if err != nil {
		return err
	}
	q.token = token
	q.userid = userinfo.Id
	return nil
}

func (q *PostUserLoginFlow) packResponse() error {
	q.data = &LoginResponse{
		UserId: q.userid,
		Token:  q.token,
	}
	return nil
}


================================================
FILE: deprecated/service/user_login/query_user_login.go
================================================
package user_login

import (
	"errors"
	"github.com/ACking-you/byte_douyin_project/middleware"
	"github.com/ACking-you/byte_douyin_project/models"
)

const (
	MaxUsernameLength = 100
	MaxPasswordLength = 20
	MinPasswordLength = 8
)

type LoginResponse struct {
	UserId int64  `json:"user_id"`
	Token  string `json:"token"`
}

// QueryUserLogin 查询用户是否存在,并返回token和id
func QueryUserLogin(username, password string) (*LoginResponse, error) {
	return NewQueryUserLoginFlow(username, password).Do()
}

func NewQueryUserLoginFlow(username, password string) *QueryUserLoginFlow {
	return &QueryUserLoginFlow{username: username, password: password}
}

type QueryUserLoginFlow struct {
	username string
	password string

	data   *LoginResponse
	userid int64
	token  string
}

func (q *QueryUserLoginFlow) Do() (*LoginResponse, error) {
	//对参数进行合法性验证
	if err := q.checkNum(); err != nil {
		return nil, err
	}
	//准备好数据
	if err := q.prepareData(); err != nil {
		return nil, err
	}
	//打包最终数据
	if err := q.packData(); err != nil {
		return nil, err
	}
	return q.data, nil
}

func (q *QueryUserLoginFlow) checkNum() error {
	if q.username == "" {
		return errors.New("用户名为空")
	}
	if len(q.username) > MaxUsernameLength {
		return errors.New("用户名长度超出限制")
	}
	if q.password == "" {
		return errors.New("密码为空")
	}
	return nil
}

func (q *QueryUserLoginFlow) prepareData() error {
	userLoginDAO := models.NewUserLoginDao()
	var login models.UserLogin
	//准备好userid
	err := userLoginDAO.QueryUserLogin(q.username, q.password, &login)
	if err != nil {
		return err
	}
	q.userid = login.UserInfoId

	//准备颁发token
	token, err := middleware.ReleaseToken(login)
	if err != nil {
		return err
	}
	q.token = token
	return nil
}

func (q *QueryUserLoginFlow) packData() error {
	q.data = &LoginResponse{
		UserId: q.userid,
		Token:  q.token,
	}
	return nil
}


================================================
FILE: deprecated/service/video/feed_videolist.go
================================================
package video

import (
	"github.com/ACking-you/byte_douyin_project/models"
	"github.com/ACking-you/byte_douyin_project/util"
	"time"
)

// MaxVideoNum 每次最多返回的视频流数量
const (
	MaxVideoNum = 30
)

type FeedVideoList struct {
	Videos   []*models.Video `json:"video_list,omitempty"`
	NextTime int64           `json:"next_time,omitempty"`
}

func QueryFeedVideoList(userId int64, latestTime time.Time) (*FeedVideoList, error) {
	return NewQueryFeedVideoListFlow(userId, latestTime).Do()
}

type QueryFeedVideoListFlow struct {
	userId     int64
	latestTime time.Time

	videos   []*models.Video
	nextTime int64

	feedVideo *FeedVideoList
}

func NewQueryFeedVideoListFlow(userId int64, latestTime time.Time) *QueryFeedVideoListFlow {
	return &QueryFeedVideoListFlow{userId: userId, latestTime: latestTime}
}

func (q *QueryFeedVideoListFlow) Do() (*FeedVideoList, error) {
	//所有传入的参数不填也应该给他正常处理
	q.checkNum()

	if err := q.prepareData(); err != nil {
		return nil, err
	}
	if err := q.packData(); err != nil {
		return nil, err
	}
	return q.feedVideo, nil
}

func (q *QueryFeedVideoListFlow) checkNum() {
	//上层通过把userId置零,表示userId不存在或不需要
	if q.userId > 0 {
		//这里说明userId是有效的,可以定制性的做一些登录用户的专属视频推荐
	}

	if q.latestTime.IsZero() {
		q.latestTime = time.Now()
	}
}

func (q *QueryFeedVideoListFlow) prepareData() error {
	err := models.NewVideoDAO().QueryVideoListByLimitAndTime(MaxVideoNum, q.latestTime, &q.videos)
	if err != nil {
		return err
	}
	//如果用户为登录状态,则更新该视频是否被该用户点赞的状态
	latestTime, _ := util.FillVideoListFields(q.userId, &q.videos) //不是致命错误,不返回

	//准备好时间戳
	if latestTime != nil {
		q.nextTime = (*latestTime).UnixNano() / 1e6
		return nil
	}
	q.nextTime = time.Now().Unix() / 1e6
	return nil
}

func (q *QueryFeedVideoListFlow) packData() error {
	q.feedVideo = &FeedVideoList{
		Videos:   q.videos,
		NextTime: q.nextTime,
	}
	return nil
}


================================================
FILE: deprecated/service/video/post_favor_state.go
================================================
package video

import (
	"errors"
	"github.com/ACking-you/byte_douyin_project/cache"
	models2 "github.com/ACking-you/byte_douyin_project/models"
)

const (
	PLUS  = 1
	MINUS = 2
)

func PostFavorState(userId, videoId, actionType int64) error {
	return NewPostFavorStateFlow(userId, videoId, actionType).Do()
}

type PostFavorStateFlow struct {
	userId     int64
	videoId    int64
	actionType int64
}

func NewPostFavorStateFlow(userId, videoId, action int64) *PostFavorStateFlow {
	return &PostFavorStateFlow{
		userId:     userId,
		videoId:    videoId,
		actionType: action,
	}
}

func (p *PostFavorStateFlow) Do() error {
	var err error
	if err = p.checkNum(); err != nil {
		return err
	}

	switch p.actionType {
	case PLUS:
		err = p.PlusOperation()
	case MINUS:
		err = p.MinusOperation()
	default:
		return errors.New("未定义的操作")
	}
	return err
}

// PlusOperation 点赞操作
func (p *PostFavorStateFlow) PlusOperation() error {
	//视频点赞数目+1
	err := models2.NewVideoDAO().PlusOneFavorByUserIdAndVideoId(p.userId, p.videoId)
	if err != nil {
		return errors.New("不要重复点赞")
	}
	//对应的用户是否点赞的映射状态更新
	cache.NewProxyIndexMap().UpdateVideoFavorState(p.userId, p.videoId, true)
	return nil
}

// MinusOperation 取消点赞
func (p *PostFavorStateFlow) MinusOperation() error {
	//视频点赞数目-1
	err := models2.NewVideoDAO().MinusOneFavorByUserIdAndVideoId(p.userId, p.videoId)
	if err != nil {
		return errors.New("点赞数目已经为0")
	}
	//对应的用户是否点赞的映射状态更新
	cache.NewProxyIndexMap().UpdateVideoFavorState(p.userId, p.videoId, false)
	return nil
}

func (p *PostFavorStateFlow) checkNum() error {
	if !models2.NewUserInfoDAO().IsUserExistById(p.userId) {
		return errors.New("用户不存在")
	}
	if p.actionType != PLUS && p.actionType != MINUS {
		return errors.New("未定义的行为")
	}
	return nil
}


================================================
FILE: deprecated/service/video/post_video.go
================================================
package video

import (
	"github.com/ACking-you/byte_douyin_project/models"
	"github.com/ACking-you/byte_douyin_project/util"
)

// PostVideo 投稿视频
func PostVideo(userId int64, videoName, coverName, title string) error {
	return NewPostVideoFlow(userId, videoName, coverName, title).Do()
}

func NewPostVideoFlow(userId int64, videoName, coverName, title string) *PostVideoFlow {
	return &PostVideoFlow{
		videoName: videoName,
		coverName: coverName,
		userId:    userId,
		title:     title,
	}
}

type PostVideoFlow struct {
	videoName string
	coverName string
	title     string
	userId    int64

	video *models.Video
}

func (f *PostVideoFlow) Do() error {
	f.prepareParam()

	if err := f.publish(); err != nil {
		return err
	}
	return nil
}

//准备好参数
func (f *PostVideoFlow) prepareParam() {
	f.videoName = util.GetFileUrl(f.videoName)
	f.coverName = util.GetFileUrl(f.coverName)
}

//组合并添加到数据库
func (f *PostVideoFlow) publish() error {
	video := &models.Video{
		UserInfoId: f.userId,
		PlayUrl:    f.videoName,
		CoverUrl:   f.coverName,
		Title:      f.title,
	}
	return models.NewVideoDAO().AddVideo(video)
}


================================================
FILE: deprecated/service/video/query_favor_videolist.go
================================================
package video

import (
	"errors"
	models2 "github.com/ACking-you/byte_douyin_project/models"
)

type FavorList struct {
	Videos []*models2.Video `json:"video_list"`
}

func QueryFavorVideoList(userId int64) (*FavorList, error) {
	return NewQueryFavorVideoListFlow(userId).Do()
}

type QueryFavorVideoListFlow struct {
	userId int64

	videos []*models2.Video

	videoList *FavorList
}

func NewQueryFavorVideoListFlow(userId int64) *QueryFavorVideoListFlow {
	return &QueryFavorVideoListFlow{userId: userId}
}

func (q *QueryFavorVideoListFlow) Do() (*FavorList, error) {
	if err := q.checkNum(); err != nil {
		return nil, err
	}
	if err := q.prepareData(); err != nil {
		return nil, err
	}
	if err := q.packData(); err != nil {
		return nil, err
	}
	return q.videoList, nil
}

func (q *QueryFavorVideoListFlow) checkNum() error {
	if !models2.NewUserInfoDAO().IsUserExistById(q.userId) {
		return errors.New("用户状态异常")
	}
	return nil
}

func (q *QueryFavorVideoListFlow) prepareData() error {
	err := models2.NewVideoDAO().QueryFavorVideoListByUserId(q.userId, &q.videos)
	if err != nil {
		return err
	}
	//填充信息(Author和IsFavorite字段,由于是点赞列表,故所有的都是点赞状态
	for i := range q.videos {
		//作者信息查询
		var userInfo models2.UserInfo
		err = models2.NewUserInfoDAO().QueryUserInfoById(q.videos[i].UserInfoId, &userInfo)
		if err == nil { //若查询未出错则更新,否则不更新作者信息
			q.videos[i].Author = userInfo
		}
		q.videos[i].IsFavorite = true
	}
	return nil
}

func (q *QueryFavorVideoListFlow) packData() error {
	q.videoList = &FavorList{Videos: q.videos}
	return nil
}


================================================
FILE: deprecated/service/video/query_videolist.go
================================================
package video

import (
	"errors"
	"github.com/ACking-you/byte_douyin_project/cache"
	models2 "github.com/ACking-you/byte_douyin_project/models"
)

type List struct {
	Videos []*models2.Video `json:"video_list,omitempty"`
}

func QueryVideoListByUserId(userId int64) (*List, error) {
	return NewQueryVideoListByUserIdFlow(userId).Do()
}

func NewQueryVideoListByUserIdFlow(userId int64) *QueryVideoListByUserIdFlow {
	return &QueryVideoListByUserIdFlow{userId: userId}
}

type QueryVideoListByUserIdFlow struct {
	userId int64
	videos []*models2.Video

	videoList *List
}

func (q *QueryVideoListByUserIdFlow) Do() (*List, error) {
	if err := q.checkNum(); err != nil {
		return nil, err
	}
	if err := q.packData(); err != nil {
		return nil, err
	}
	return q.videoList, nil
}

func (q *QueryVideoListByUserIdFlow) checkNum() error {
	//检查userId是否存在
	if !models2.NewUserInfoDAO().IsUserExistById(q.userId) {
		return errors.New("用户不存在")
	}

	return nil
}

//注意:Video由于在数据库中没有存储作者信息,所以需要手动填充
func (q *QueryVideoListByUserIdFlow) packData() error {
	err := models2.NewVideoDAO().QueryVideoListByUserId(q.userId, &q.videos)
	if err != nil {
		return err
	}
	//作者信息查询
	var userInfo models2.UserInfo
	err = models2.NewUserInfoDAO().QueryUserInfoById(q.userId, &userInfo)
	p := cache.NewProxyIndexMap()
	if err != nil {
		return err
	}
	//填充信息(Author和IsFavorite字段
	for i := range q.videos {
		q.videos[i].Author = userInfo
		q.videos[i].IsFavorite = p.GetVideoFavorState(q.userId, q.videos[i].Id)
	}

	q.videoList = &List{Videos: q.videos}

	return nil
}


================================================
FILE: deprecated/util/comment.go
================================================
package util

import (
	"errors"
	models2 "github.com/ACking-you/byte_douyin_project/models"
)

func FillCommentListFields(comments *[]*models2.Comment) error {
	size := len(*comments)
	if comments == nil || size == 0 {
		return errors.New("util.FillCommentListFields comments为空")
	}
	dao := models2.NewUserInfoDAO()
	for _, v := range *comments {
		_ = dao.QueryUserInfoById(v.UserInfoId, &v.User) //填充这条评论的作者信息
		v.CreateDate = v.CreatedAt.Format("1-2")         //转为前端要求的日期格式
	}
	return nil
}

func FillCommentFields(comment *models2.Comment) error {
	if comment == nil {
		return errors.New("FillCommentFields comments为空")
	}
	comment.CreateDate = comment.CreatedAt.Format("1-2") //转为前端要求的日期格式
	return nil
}


================================================
FILE: deprecated/util/ffmpeg.go
================================================
package util

import (
	"errors"
	"fmt"
	"os/exec"
	"path/filepath"
	"strconv"
	"sync"

	"github.com/ACking-you/byte_douyin_project/config"
	"log"
)

// 可以更改
var (
	globalMutex        sync.RWMutex
	defaultVideoSuffix = ".mp4"
	defaultImageSuffix = ".jpg"
)

type Video2Image struct {
	inputPath  string
	outputPath string
	startTime  string
	keepTime   string
	filter     string
	frameCount int
	debug      bool
}

func NewVideo2Image() *Video2Image {
	return &Video2Image{} // 每次返回新实例,避免共享状态
}

func GetDefaultVideoSuffix() string {
	globalMutex.RLock()
	defer globalMutex.RUnlock()
	return defaultVideoSuffix
}

func GetDefaultImageSuffix() string {
	globalMutex.RLock()
	defer globalMutex.RUnlock()
	return defaultImageSuffix
}

// ChangeVideoDefaultSuffix 全局配置修改方法(线程安全)
func ChangeVideoDefaultSuffix(suffix string) {
	globalMutex.Lock()
	defer globalMutex.Unlock()
	defaultVideoSuffix = normalizeExtension(suffix)
}

func ChangeImageDefaultSuffix(suffix string) {
	globalMutex.Lock()
	defer globalMutex.Unlock()
	defaultImageSuffix = normalizeExtension(suffix)
}

func normalizeExtension(ext string) string {
	if ext == "" {
		return ext
	}
	if ext[0] != '.' {
		return "." + ext
	}
	return ext
}

// SetInputPath 方法链式调用(非共享实例,无需加锁)
func (v *Video2Image) SetInputPath(path string) *Video2Image {
	v.inputPath = path
	return v
}

func (v *Video2Image) SetOutputPath(path string) *Video2Image {
	v.outputPath = path
	return v
}

func (v *Video2Image) SetTimeOptions(start, duration string) *Video2Image {
	v.startTime = start
	v.keepTime = duration
	return v
}

func (v *Video2Image) SetFilter(filter string) *Video2Image {
	v.filter = filter
	return v
}

func (v *Video2Image) SetFrameCount(count int) *Video2Image {
	v.frameCount = count
	return v
}

func (v *Video2Image) SetDebug(debug bool) *Video2Image {
	v.debug = debug
	return v
}

func (v *Video2Image) buildArgs() ([]string, error) {
	if v.inputPath == "" || v.outputPath == "" {
		return nil, errors.New("input and output path must be specified")
	}

	args := []string{
		"-i", filepath.ToSlash(v.inputPath),
		"-f", "image2",
	}

	if v.filter != "" {
		args = append(args, "-vf", v.filter)
	}
	if v.startTime != "" {
		args = append(args, "-ss", v.startTime)
	}
	if v.keepTime != "" {
		args = append(args, "-t", v.keepTime)
	}
	if v.frameCount > 0 {
		args = append(args, "-frames:v", strconv.Itoa(v.frameCount))
	}

	args = append(args, "-y", filepath.ToSlash(v.outputPath))
	return args, nil
}

func (v *Video2Image) Execute() error {
	args, err := v.buildArgs()
	if err != nil {
		return fmt.Errorf("参数构建失败: %w", err)
	}

	ffmpegPath := filepath.FromSlash(config.Global.FfmpegPath)

	cmd := exec.Command(ffmpegPath, args...)
	if v.debug {
		log.Printf("执行命令: %q", cmd.String())
		cmd.Stdout = log.Writer()
		cmd.Stderr = log.Writer()
	}

	if err := cmd.Run(); err != nil {
		return fmt.Errorf("ffmpeg执行失败: %w (命令: %q)", err, cmd.String())
	}
	return nil
}


================================================
FILE: deprecated/util/video.go
================================================
package util

import (
	"errors"
	"fmt"
	"github.com/ACking-you/byte_douyin_project/cache"
	"github.com/ACking-you/byte_douyin_project/config"
	models2 "github.com/ACking-you/byte_douyin_project/models"
	"log"
	"path/filepath"
	"time"
)

func GetFileUrl(fileName string) string {
	base := fmt.Sprintf("http://%s:%d/static/%s", config.Global.IP, config.Global.Port, fileName)
	return base
}

// NewFileName 根据userId+用户发布的视频数量连接成独一无二的文件名
func NewFileName(userId int64) string {
	var count int64

	err := models2.NewVideoDAO().QueryVideoCountByUserId(userId, &count)
	if err != nil {
		log.Println(err)
	}
	return fmt.Sprintf("%d-%d", userId, count)
}

// FillVideoListFields 填充每个视频的作者信息(因为作者与视频的一对多关系,数据库中存下的是作者的id
// 当userId>0时,我们判断当前为登录状态,其余情况为未登录状态,则不需要填充IsFavorite字段
func FillVideoListFields(userId int64, videos *[]*models2.Video) (*time.Time, error) {
	if videos == nil || (len(*videos) == 0) {
		return nil, errors.New("util.FillVideoListFields videos为空")
	}
	size := len(*videos)
	dao := models2.NewUserInfoDAO()
	p := cache.NewProxyIndexMap()

	latestTime := (*videos)[size-1].CreatedAt //获取最近的投稿时间
	//添加作者信息,以及is_follow状态
	for i := 0; i < size; i++ {
		var userInfo models2.UserInfo
		err := dao.QueryUserInfoById((*videos)[i].UserInfoId, &userInfo)
		if err != nil {
			continue
		}
		userInfo.IsFollow = p.GetUserRelation(userId, userInfo.Id) //根据cache更新是否被点赞
		(*videos)[i].Author = userInfo
		//填充有登录信息的点赞状态
		if userId > 0 {
			(*videos)[i].IsFavorite = p.GetVideoFavorState(userId, (*videos)[i].Id)
		}
	}
	return &latestTime, nil
}

// SaveImageFromVideo 将视频切一帧保存到本地
// isDebug用于控制是否打印出执行的ffmepg命令
func SaveImageFromVideo(name string, isDebug bool) error {
	return NewVideo2Image().
		SetInputPath(filepath.Join(config.Global.StaticSourcePath, name+GetDefaultVideoSuffix())).
		SetOutputPath(filepath.Join(config.Global.StaticSourcePath, name+GetDefaultImageSuffix())).
		SetFrameCount(1).
		SetDebug(isDebug).Execute()
}
Download .txt
gitextract_cvanrqe9/

├── .gitignore
├── LICENSE
├── README.md
└── deprecated/
    ├── cache/
    │   ├── index_map.go
    │   └── indexmap_test.go
    ├── config/
    │   ├── conf.go
    │   └── config.toml
    ├── go.mod
    ├── go.sum
    ├── handlers/
    │   ├── comment/
    │   │   ├── post_comment_handler.go
    │   │   └── query_commentlist_handler.go
    │   ├── user_info/
    │   │   ├── post_follow_action_handler.go
    │   │   ├── query_follow_list_handler.go
    │   │   ├── query_follower_handler.go
    │   │   └── userinfo_handler.go
    │   ├── user_login/
    │   │   ├── user_login_handler.go
    │   │   └── user_register_handler.go
    │   └── video/
    │       ├── feed_videolist_handler.go
    │       ├── post_favor_handler.go
    │       ├── publish_video_handler.go
    │       ├── query_favor_videolist_handler.go
    │       └── query_videolist_handler.go
    ├── main.go
    ├── middleware/
    │   ├── jwt.go
    │   ├── jwt_test.go
    │   ├── normal.go
    │   ├── password.go
    │   └── password_test.go
    ├── models/
    │   ├── comment.go
    │   ├── common.go
    │   ├── init_db.go
    │   ├── user_info.go
    │   ├── user_info_test.go
    │   ├── user_login.go
    │   ├── video.go
    │   └── video_test.go
    ├── router/
    │   └── router_douyin.go
    ├── service/
    │   ├── comment/
    │   │   ├── post_comment.go
    │   │   └── query_comment_list.go
    │   ├── user_info/
    │   │   ├── post_follow_action.go
    │   │   ├── query_follow_list.go
    │   │   └── query_follower_list.go
    │   ├── user_login/
    │   │   ├── post_user_login.go
    │   │   └── query_user_login.go
    │   └── video/
    │       ├── feed_videolist.go
    │       ├── post_favor_state.go
    │       ├── post_video.go
    │       ├── query_favor_videolist.go
    │       └── query_videolist.go
    └── util/
        ├── comment.go
        ├── ffmpeg.go
        └── video.go
Download .txt
SYMBOL INDEX (280 symbols across 46 files)

FILE: deprecated/cache/index_map.go
  constant favor (line 16) | favor    = "favor"
  constant relation (line 17) | relation = "relation"
  function init (line 20) | func init() {
  type ProxyIndexMap (line 33) | type ProxyIndexMap struct
    method UpdateVideoFavorState (line 41) | func (i *ProxyIndexMap) UpdateVideoFavorState(userId int64, videoId in...
    method GetVideoFavorState (line 51) | func (i *ProxyIndexMap) GetVideoFavorState(userId int64, videoId int64...
    method UpdateUserRelation (line 58) | func (i *ProxyIndexMap) UpdateUserRelation(userId int64, followId int6...
    method GetUserRelation (line 68) | func (i *ProxyIndexMap) GetUserRelation(userId int64, followId int64) ...
  function NewProxyIndexMap (line 36) | func NewProxyIndexMap() *ProxyIndexMap {

FILE: deprecated/cache/indexmap_test.go
  function TestProxyIndexMap_UpdateUserRelation (line 8) | func TestProxyIndexMap_UpdateUserRelation(t *testing.T) {
  function TestProxyIndexMap_GetVideoFavorState (line 13) | func TestProxyIndexMap_GetVideoFavorState(t *testing.T) {

FILE: deprecated/config/conf.go
  type Mysql (line 13) | type Mysql struct
  type Redis (line 24) | type Redis struct
  type Server (line 30) | type Server struct
  type Path (line 35) | type Path struct
  type Config (line 40) | type Config struct
  function ensurePathValid (line 49) | func ensurePathValid() {
  function init (line 76) | func init() {
  function DBConnectString (line 89) | func DBConnectString() string {

FILE: deprecated/handlers/comment/post_comment_handler.go
  type PostCommentResponse (line 13) | type PostCommentResponse struct
  function PostCommentHandler (line 18) | func PostCommentHandler(c *gin.Context) {
  type ProxyPostCommentHandler (line 22) | type ProxyPostCommentHandler struct
    method Do (line 36) | func (p *ProxyPostCommentHandler) Do() {
    method parseNum (line 54) | func (p *ProxyPostCommentHandler) parseNum() error {
    method SendError (line 87) | func (p *ProxyPostCommentHandler) SendError(msg string) {
    method SendOk (line 92) | func (p *ProxyPostCommentHandler) SendOk(comment *comment.Response) {
  function NewProxyPostCommentHandler (line 32) | func NewProxyPostCommentHandler(context *gin.Context) *ProxyPostCommentH...

FILE: deprecated/handlers/comment/query_commentlist_handler.go
  type ListResponse (line 13) | type ListResponse struct
  function QueryCommentListHandler (line 18) | func QueryCommentListHandler(c *gin.Context) {
  type ProxyCommentListHandler (line 22) | type ProxyCommentListHandler struct
    method Do (line 33) | func (p *ProxyCommentListHandler) Do() {
    method parseNum (line 51) | func (p *ProxyCommentListHandler) parseNum() error {
    method SendError (line 69) | func (p *ProxyCommentListHandler) SendError(msg string) {
    method SendOk (line 74) | func (p *ProxyCommentListHandler) SendOk(commentList *comment.List) {
  function NewProxyCommentListHandler (line 29) | func NewProxyCommentListHandler(context *gin.Context) *ProxyCommentListH...

FILE: deprecated/handlers/user_info/post_follow_action_handler.go
  function PostFollowActionHandler (line 12) | func PostFollowActionHandler(c *gin.Context) {
  type ProxyPostFollowAction (line 16) | type ProxyPostFollowAction struct
    method Do (line 28) | func (p *ProxyPostFollowAction) Do() {
    method prepareNum (line 46) | func (p *ProxyPostFollowAction) prepareNum() error {
    method startAction (line 72) | func (p *ProxyPostFollowAction) startAction() error {
    method SendError (line 80) | func (p *ProxyPostFollowAction) SendError(msg string) {
    method SendOk (line 84) | func (p *ProxyPostFollowAction) SendOk(msg string) {
  function NewProxyPostFollowAction (line 24) | func NewProxyPostFollowAction(context *gin.Context) *ProxyPostFollowActi...

FILE: deprecated/handlers/user_info/query_follow_list_handler.go
  type FollowListResponse (line 11) | type FollowListResponse struct
  function QueryFollowListHandler (line 16) | func QueryFollowListHandler(c *gin.Context) {
  type ProxyQueryFollowList (line 20) | type ProxyQueryFollowList struct
    method Do (line 32) | func (p *ProxyQueryFollowList) Do() {
    method parseNum (line 45) | func (p *ProxyQueryFollowList) parseNum() error {
    method prepareData (line 55) | func (p *ProxyQueryFollowList) prepareData() error {
    method SendError (line 64) | func (p *ProxyQueryFollowList) SendError(msg string) {
    method SendOk (line 70) | func (p *ProxyQueryFollowList) SendOk(msg string) {
  function NewProxyQueryFollowList (line 28) | func NewProxyQueryFollowList(context *gin.Context) *ProxyQueryFollowList {

FILE: deprecated/handlers/user_info/query_follower_handler.go
  type FollowerListResponse (line 11) | type FollowerListResponse struct
  function QueryFollowerHandler (line 16) | func QueryFollowerHandler(c *gin.Context) {
  type ProxyQueryFollowerHandler (line 20) | type ProxyQueryFollowerHandler struct
    method Do (line 32) | func (p *ProxyQueryFollowerHandler) Do() {
    method parseNum (line 49) | func (p *ProxyQueryFollowerHandler) parseNum() error {
    method prepareData (line 59) | func (p *ProxyQueryFollowerHandler) prepareData() error {
    method SendError (line 68) | func (p *ProxyQueryFollowerHandler) SendError(msg string) {
    method SendOk (line 77) | func (p *ProxyQueryFollowerHandler) SendOk(msg string) {
  function NewProxyQueryFollowerHandler (line 28) | func NewProxyQueryFollowerHandler(context *gin.Context) *ProxyQueryFollo...

FILE: deprecated/handlers/user_info/userinfo_handler.go
  type UserResponse (line 10) | type UserResponse struct
  function UserInfoHandler (line 15) | func UserInfoHandler(c *gin.Context) {
  type ProxyUserInfo (line 29) | type ProxyUserInfo struct
    method DoQueryUserInfoByUserId (line 37) | func (p *ProxyUserInfo) DoQueryUserInfoByUserId(rawId interface{}) err...
    method UserInfoError (line 54) | func (p *ProxyUserInfo) UserInfoError(msg string) {
    method UserInfoOk (line 60) | func (p *ProxyUserInfo) UserInfoOk(user *models2.UserInfo) {
  function NewProxyUserInfo (line 33) | func NewProxyUserInfo(c *gin.Context) *ProxyUserInfo {

FILE: deprecated/handlers/user_login/user_login_handler.go
  type UserLoginResponse (line 10) | type UserLoginResponse struct
  function UserLoginHandler (line 15) | func UserLoginHandler(c *gin.Context) {

FILE: deprecated/handlers/user_login/user_register_handler.go
  type UserRegisterResponse (line 10) | type UserRegisterResponse struct
  function UserRegisterHandler (line 15) | func UserRegisterHandler(c *gin.Context) {

FILE: deprecated/handlers/video/feed_videolist_handler.go
  type FeedResponse (line 14) | type FeedResponse struct
  function FeedVideoListHandler (line 19) | func FeedVideoListHandler(c *gin.Context) {
  type ProxyFeedVideoList (line 38) | type ProxyFeedVideoList struct
    method DoNoToken (line 47) | func (p *ProxyFeedVideoList) DoNoToken() error {
    method DoHasToken (line 63) | func (p *ProxyFeedVideoList) DoHasToken(token string) error {
    method FeedVideoListError (line 88) | func (p *ProxyFeedVideoList) FeedVideoListError(msg string) {
    method FeedVideoListOk (line 95) | func (p *ProxyFeedVideoList) FeedVideoListOk(videoList *video.FeedVide...
  function NewProxyFeedVideoList (line 42) | func NewProxyFeedVideoList(c *gin.Context) *ProxyFeedVideoList {

FILE: deprecated/handlers/video/post_favor_handler.go
  function PostFavorHandler (line 12) | func PostFavorHandler(c *gin.Context) {
  type ProxyPostFavorHandler (line 16) | type ProxyPostFavorHandler struct
    method Do (line 28) | func (p *ProxyPostFavorHandler) Do() {
    method parseNum (line 46) | func (p *ProxyPostFavorHandler) parseNum() error {
    method SendError (line 70) | func (p *ProxyPostFavorHandler) SendError(msg string) {
    method SendOk (line 74) | func (p *ProxyPostFavorHandler) SendOk() {
  function NewProxyPostFavorHandler (line 24) | func NewProxyPostFavorHandler(c *gin.Context) *ProxyPostFavorHandler {

FILE: deprecated/handlers/video/publish_video_handler.go
  function PublishVideoHandler (line 31) | func PublishVideoHandler(c *gin.Context) {
  function PublishVideoError (line 81) | func PublishVideoError(c *gin.Context, msg string) {
  function PublishVideoOk (line 86) | func PublishVideoOk(c *gin.Context, msg string) {

FILE: deprecated/handlers/video/query_favor_videolist_handler.go
  type FavorVideoListResponse (line 11) | type FavorVideoListResponse struct
  function QueryFavorVideoListHandler (line 16) | func QueryFavorVideoListHandler(c *gin.Context) {
  type ProxyFavorVideoListHandler (line 20) | type ProxyFavorVideoListHandler struct
    method Do (line 30) | func (p *ProxyFavorVideoListHandler) Do() {
    method parseNum (line 48) | func (p *ProxyFavorVideoListHandler) parseNum() error {
    method SendError (line 58) | func (p *ProxyFavorVideoListHandler) SendError(msg string) {
    method SendOk (line 63) | func (p *ProxyFavorVideoListHandler) SendOk(favorList *video.FavorList) {
  function NewProxyFavorVideoListHandler (line 26) | func NewProxyFavorVideoListHandler(c *gin.Context) *ProxyFavorVideoListH...

FILE: deprecated/handlers/video/query_videolist_handler.go
  type ListResponse (line 11) | type ListResponse struct
  function QueryVideoListHandler (line 16) | func QueryVideoListHandler(c *gin.Context) {
  type ProxyQueryVideoList (line 26) | type ProxyQueryVideoList struct
    method DoQueryVideoListByUserId (line 35) | func (p *ProxyQueryVideoList) DoQueryVideoListByUserId(rawId interface...
    method QueryVideoListError (line 50) | func (p *ProxyQueryVideoList) QueryVideoListError(msg string) {
    method QueryVideoListOk (line 57) | func (p *ProxyQueryVideoList) QueryVideoListOk(videoList *video.List) {
  function NewProxyQueryVideoList (line 30) | func NewProxyQueryVideoList(c *gin.Context) *ProxyQueryVideoList {

FILE: deprecated/main.go
  function main (line 9) | func main() {

FILE: deprecated/middleware/jwt.go
  type Claims (line 13) | type Claims struct
  function ReleaseToken (line 19) | func ReleaseToken(user models2.UserLogin) (string, error) {
  function ParseToken (line 39) | func ParseToken(tokenString string) (*Claims, bool) {
  function JWTMiddleWare (line 56) | func JWTMiddleWare() gin.HandlerFunc {

FILE: deprecated/middleware/jwt_test.go
  function TestJwt (line 7) | func TestJwt(t *testing.T) {

FILE: deprecated/middleware/normal.go
  function NoAuthToGetUserId (line 10) | func NoAuthToGetUserId() gin.HandlerFunc {

FILE: deprecated/middleware/password.go
  function SHA1 (line 9) | func SHA1(s string) string {
  function SHAMiddleWare (line 18) | func SHAMiddleWare() gin.HandlerFunc {

FILE: deprecated/middleware/password_test.go
  function TestSHA1 (line 6) | func TestSHA1(t *testing.T) {

FILE: deprecated/models/comment.go
  type Comment (line 9) | type Comment struct
  type CommentDAO (line 19) | type CommentDAO struct
    method AddCommentAndUpdateCount (line 30) | func (c *CommentDAO) AddCommentAndUpdateCount(comment *Comment) error {
    method DeleteCommentAndUpdateCountById (line 51) | func (c *CommentDAO) DeleteCommentAndUpdateCountById(commentId, videoI...
    method QueryCommentById (line 68) | func (c *CommentDAO) QueryCommentById(id int64, comment *Comment) error {
    method QueryCommentListByVideoId (line 75) | func (c *CommentDAO) QueryCommentListByVideoId(videoId int64, comments...
  function NewCommentDAO (line 26) | func NewCommentDAO() *CommentDAO {

FILE: deprecated/models/common.go
  type CommonResponse (line 3) | type CommonResponse struct

FILE: deprecated/models/init_db.go
  function InitDB (line 11) | func InitDB() {

FILE: deprecated/models/user_info.go
  type UserInfo (line 15) | type UserInfo struct
  type UserInfoDAO (line 28) | type UserInfoDAO struct
    method QueryUserInfoById (line 43) | func (u *UserInfoDAO) QueryUserInfoById(userId int64, userinfo *UserIn...
    method AddUserInfo (line 56) | func (u *UserInfoDAO) AddUserInfo(userinfo *UserInfo) error {
    method IsUserExistById (line 63) | func (u *UserInfoDAO) IsUserExistById(id int64) bool {
    method AddUserFollow (line 73) | func (u *UserInfoDAO) AddUserFollow(userId, userToId int64) error {
    method CancelUserFollow (line 88) | func (u *UserInfoDAO) CancelUserFollow(userId, userToId int64) error {
    method GetFollowListByUserId (line 103) | func (u *UserInfoDAO) GetFollowListByUserId(userId int64, userList *[]...
    method GetFollowerListByUserId (line 117) | func (u *UserInfoDAO) GetFollowerListByUserId(userId int64, userList *...
  function NewUserInfoDAO (line 36) | func NewUserInfoDAO() *UserInfoDAO {

FILE: deprecated/models/user_info_test.go
  function TestMain (line 9) | func TestMain(m *testing.M) {
  function TestUserInfoDAO_GetFollowListByUserId (line 15) | func TestUserInfoDAO_GetFollowListByUserId(t *testing.T) {
  function TestUserInfoDAO_GetFollowerListByUserId (line 26) | func TestUserInfoDAO_GetFollowerListByUserId(t *testing.T) {

FILE: deprecated/models/user_login.go
  type UserLogin (line 9) | type UserLogin struct
  type UserLoginDAO (line 16) | type UserLoginDAO struct
    method QueryUserLogin (line 31) | func (u *UserLoginDAO) QueryUserLogin(username, password string, login...
    method IsUserExistByUsername (line 42) | func (u *UserLoginDAO) IsUserExistByUsername(username string) bool {
  function NewUserLoginDao (line 24) | func NewUserLoginDao() *UserLoginDAO {

FILE: deprecated/models/video.go
  type Video (line 11) | type Video struct
  type VideoDAO (line 27) | type VideoDAO struct
    method AddVideo (line 44) | func (v *VideoDAO) AddVideo(video *Video) error {
    method QueryVideoByVideoId (line 51) | func (v *VideoDAO) QueryVideoByVideoId(videoId int64, video *Video) er...
    method QueryVideoCountByUserId (line 60) | func (v *VideoDAO) QueryVideoCountByUserId(userId int64, count *int64)...
    method QueryVideoListByUserId (line 67) | func (v *VideoDAO) QueryVideoListByUserId(userId int64, videoList *[]*...
    method QueryVideoListByLimitAndTime (line 77) | func (v *VideoDAO) QueryVideoListByLimitAndTime(limit int, latestTime ...
    method PlusOneFavorByUserIdAndVideoId (line 88) | func (v *VideoDAO) PlusOneFavorByUserIdAndVideoId(userId int64, videoI...
    method MinusOneFavorByUserIdAndVideoId (line 101) | func (v *VideoDAO) MinusOneFavorByUserIdAndVideoId(userId int64, video...
    method QueryFavorVideoListByUserId (line 114) | func (v *VideoDAO) QueryFavorVideoListByUserId(userId int64, videoList...
    method IsVideoExistById (line 129) | func (v *VideoDAO) IsVideoExistById(id int64) bool {
  function NewVideoDAO (line 35) | func NewVideoDAO() *VideoDAO {

FILE: deprecated/models/video_test.go
  function TestVideoDAO_QueryVideoListByUserId (line 9) | func TestVideoDAO_QueryVideoListByUserId(t *testing.T) {
  function TestVideoDAO_QueryVideoListByLimit (line 21) | func TestVideoDAO_QueryVideoListByLimit(t *testing.T) {
  function TestTime (line 33) | func TestTime(t *testing.T) {

FILE: deprecated/router/router_douyin.go
  function Init (line 14) | func Init() *gin.Engine {

FILE: deprecated/service/comment/post_comment.go
  constant CREATE (line 11) | CREATE = 1
  constant DELETE (line 12) | DELETE = 2
  type Response (line 15) | type Response struct
  function PostComment (line 19) | func PostComment(userId int64, videoId int64, commentId int64, actionTyp...
  type PostCommentFlow (line 23) | type PostCommentFlow struct
    method Do (line 39) | func (p *PostCommentFlow) Do() (*Response, error) {
    method CreateComment (line 54) | func (p *PostCommentFlow) CreateComment() (*models2.Comment, error) {
    method DeleteComment (line 65) | func (p *PostCommentFlow) DeleteComment() (*models2.Comment, error) {
    method checkNum (line 80) | func (p *PostCommentFlow) checkNum() error {
    method prepareData (line 93) | func (p *PostCommentFlow) prepareData() error {
    method packData (line 106) | func (p *PostCommentFlow) packData() error {
  function NewPostCommentFlow (line 35) | func NewPostCommentFlow(userId int64, videoId int64, commentId int64, ac...

FILE: deprecated/service/comment/query_comment_list.go
  type List (line 10) | type List struct
  function QueryCommentList (line 14) | func QueryCommentList(userId, videoId int64) (*List, error) {
  type QueryCommentListFlow (line 18) | type QueryCommentListFlow struct
    method Do (line 31) | func (q *QueryCommentListFlow) Do() (*List, error) {
    method checkNum (line 44) | func (q *QueryCommentListFlow) checkNum() error {
    method prepareData (line 54) | func (q *QueryCommentListFlow) prepareData() error {
    method packData (line 67) | func (q *QueryCommentListFlow) packData() error {
  function NewQueryCommentListFlow (line 27) | func NewQueryCommentListFlow(userId, videoId int64) *QueryCommentListFlow {

FILE: deprecated/service/user_info/post_follow_action.go
  constant FOLLOW (line 10) | FOLLOW = 1
  constant CANCEL (line 11) | CANCEL = 2
  function PostFollowAction (line 19) | func PostFollowAction(userId, userToId int64, actionType int) error {
  type PostFollowActionFlow (line 23) | type PostFollowActionFlow struct
    method Do (line 33) | func (p *PostFollowActionFlow) Do() error {
    method checkNum (line 44) | func (p *PostFollowActionFlow) checkNum() error {
    method publish (line 59) | func (p *PostFollowActionFlow) publish() error {
  function NewPostFollowActionFlow (line 29) | func NewPostFollowActionFlow(userId int64, userToId int64, actionType in...

FILE: deprecated/service/user_info/query_follow_list.go
  type FollowList (line 12) | type FollowList struct
  function QueryFollowList (line 16) | func QueryFollowList(userId int64) (*FollowList, error) {
  type QueryFollowListFlow (line 20) | type QueryFollowListFlow struct
    method Do (line 32) | func (q *QueryFollowListFlow) Do() (*FollowList, error) {
    method checkNum (line 47) | func (q *QueryFollowListFlow) checkNum() error {
    method prepareData (line 54) | func (q *QueryFollowListFlow) prepareData() error {
    method packData (line 67) | func (q *QueryFollowListFlow) packData() error {
  function NewQueryFollowListFlow (line 28) | func NewQueryFollowListFlow(userId int64) *QueryFollowListFlow {

FILE: deprecated/service/user_info/query_follower_list.go
  type FollowerList (line 8) | type FollowerList struct
  function QueryFollowerList (line 12) | func QueryFollowerList(userId int64) (*FollowerList, error) {
  type QueryFollowerListFlow (line 16) | type QueryFollowerListFlow struct
    method Do (line 28) | func (q *QueryFollowerListFlow) Do() (*FollowerList, error) {
    method checkNum (line 42) | func (q *QueryFollowerListFlow) checkNum() error {
    method prepareData (line 49) | func (q *QueryFollowerListFlow) prepareData() error {
    method packData (line 62) | func (q *QueryFollowerListFlow) packData() error {
  function NewQueryFollowerListFlow (line 24) | func NewQueryFollowerListFlow(userId int64) *QueryFollowerListFlow {

FILE: deprecated/service/user_login/post_user_login.go
  function PostUserLogin (line 10) | func PostUserLogin(username, password string) (*LoginResponse, error) {
  function NewPostUserLoginFlow (line 14) | func NewPostUserLoginFlow(username, password string) *PostUserLoginFlow {
  type PostUserLoginFlow (line 18) | type PostUserLoginFlow struct
    method Do (line 27) | func (q *PostUserLoginFlow) Do() (*LoginResponse, error) {
    method checkNum (line 45) | func (q *PostUserLoginFlow) checkNum() error {
    method updateData (line 58) | func (q *PostUserLoginFlow) updateData() error {
    method packResponse (line 87) | func (q *PostUserLoginFlow) packResponse() error {

FILE: deprecated/service/user_login/query_user_login.go
  constant MaxUsernameLength (line 10) | MaxUsernameLength = 100
  constant MaxPasswordLength (line 11) | MaxPasswordLength = 20
  constant MinPasswordLength (line 12) | MinPasswordLength = 8
  type LoginResponse (line 15) | type LoginResponse struct
  function QueryUserLogin (line 21) | func QueryUserLogin(username, password string) (*LoginResponse, error) {
  function NewQueryUserLoginFlow (line 25) | func NewQueryUserLoginFlow(username, password string) *QueryUserLoginFlow {
  type QueryUserLoginFlow (line 29) | type QueryUserLoginFlow struct
    method Do (line 38) | func (q *QueryUserLoginFlow) Do() (*LoginResponse, error) {
    method checkNum (line 54) | func (q *QueryUserLoginFlow) checkNum() error {
    method prepareData (line 67) | func (q *QueryUserLoginFlow) prepareData() error {
    method packData (line 86) | func (q *QueryUserLoginFlow) packData() error {

FILE: deprecated/service/video/feed_videolist.go
  constant MaxVideoNum (line 11) | MaxVideoNum = 30
  type FeedVideoList (line 14) | type FeedVideoList struct
  function QueryFeedVideoList (line 19) | func QueryFeedVideoList(userId int64, latestTime time.Time) (*FeedVideoL...
  type QueryFeedVideoListFlow (line 23) | type QueryFeedVideoListFlow struct
    method Do (line 37) | func (q *QueryFeedVideoListFlow) Do() (*FeedVideoList, error) {
    method checkNum (line 50) | func (q *QueryFeedVideoListFlow) checkNum() {
    method prepareData (line 61) | func (q *QueryFeedVideoListFlow) prepareData() error {
    method packData (line 78) | func (q *QueryFeedVideoListFlow) packData() error {
  function NewQueryFeedVideoListFlow (line 33) | func NewQueryFeedVideoListFlow(userId int64, latestTime time.Time) *Quer...

FILE: deprecated/service/video/post_favor_state.go
  constant PLUS (line 10) | PLUS  = 1
  constant MINUS (line 11) | MINUS = 2
  function PostFavorState (line 14) | func PostFavorState(userId, videoId, actionType int64) error {
  type PostFavorStateFlow (line 18) | type PostFavorStateFlow struct
    method Do (line 32) | func (p *PostFavorStateFlow) Do() error {
    method PlusOperation (line 50) | func (p *PostFavorStateFlow) PlusOperation() error {
    method MinusOperation (line 62) | func (p *PostFavorStateFlow) MinusOperation() error {
    method checkNum (line 73) | func (p *PostFavorStateFlow) checkNum() error {
  function NewPostFavorStateFlow (line 24) | func NewPostFavorStateFlow(userId, videoId, action int64) *PostFavorStat...

FILE: deprecated/service/video/post_video.go
  function PostVideo (line 9) | func PostVideo(userId int64, videoName, coverName, title string) error {
  function NewPostVideoFlow (line 13) | func NewPostVideoFlow(userId int64, videoName, coverName, title string) ...
  type PostVideoFlow (line 22) | type PostVideoFlow struct
    method Do (line 31) | func (f *PostVideoFlow) Do() error {
    method prepareParam (line 41) | func (f *PostVideoFlow) prepareParam() {
    method publish (line 47) | func (f *PostVideoFlow) publish() error {

FILE: deprecated/service/video/query_favor_videolist.go
  type FavorList (line 8) | type FavorList struct
  function QueryFavorVideoList (line 12) | func QueryFavorVideoList(userId int64) (*FavorList, error) {
  type QueryFavorVideoListFlow (line 16) | type QueryFavorVideoListFlow struct
    method Do (line 28) | func (q *QueryFavorVideoListFlow) Do() (*FavorList, error) {
    method checkNum (line 41) | func (q *QueryFavorVideoListFlow) checkNum() error {
    method prepareData (line 48) | func (q *QueryFavorVideoListFlow) prepareData() error {
    method packData (line 66) | func (q *QueryFavorVideoListFlow) packData() error {
  function NewQueryFavorVideoListFlow (line 24) | func NewQueryFavorVideoListFlow(userId int64) *QueryFavorVideoListFlow {

FILE: deprecated/service/video/query_videolist.go
  type List (line 9) | type List struct
  function QueryVideoListByUserId (line 13) | func QueryVideoListByUserId(userId int64) (*List, error) {
  function NewQueryVideoListByUserIdFlow (line 17) | func NewQueryVideoListByUserIdFlow(userId int64) *QueryVideoListByUserId...
  type QueryVideoListByUserIdFlow (line 21) | type QueryVideoListByUserIdFlow struct
    method Do (line 28) | func (q *QueryVideoListByUserIdFlow) Do() (*List, error) {
    method checkNum (line 38) | func (q *QueryVideoListByUserIdFlow) checkNum() error {
    method packData (line 48) | func (q *QueryVideoListByUserIdFlow) packData() error {

FILE: deprecated/util/comment.go
  function FillCommentListFields (line 8) | func FillCommentListFields(comments *[]*models2.Comment) error {
  function FillCommentFields (line 21) | func FillCommentFields(comment *models2.Comment) error {

FILE: deprecated/util/ffmpeg.go
  type Video2Image (line 22) | type Video2Image struct
    method SetInputPath (line 72) | func (v *Video2Image) SetInputPath(path string) *Video2Image {
    method SetOutputPath (line 77) | func (v *Video2Image) SetOutputPath(path string) *Video2Image {
    method SetTimeOptions (line 82) | func (v *Video2Image) SetTimeOptions(start, duration string) *Video2Im...
    method SetFilter (line 88) | func (v *Video2Image) SetFilter(filter string) *Video2Image {
    method SetFrameCount (line 93) | func (v *Video2Image) SetFrameCount(count int) *Video2Image {
    method SetDebug (line 98) | func (v *Video2Image) SetDebug(debug bool) *Video2Image {
    method buildArgs (line 103) | func (v *Video2Image) buildArgs() ([]string, error) {
    method Execute (line 130) | func (v *Video2Image) Execute() error {
  function NewVideo2Image (line 32) | func NewVideo2Image() *Video2Image {
  function GetDefaultVideoSuffix (line 36) | func GetDefaultVideoSuffix() string {
  function GetDefaultImageSuffix (line 42) | func GetDefaultImageSuffix() string {
  function ChangeVideoDefaultSuffix (line 49) | func ChangeVideoDefaultSuffix(suffix string) {
  function ChangeImageDefaultSuffix (line 55) | func ChangeImageDefaultSuffix(suffix string) {
  function normalizeExtension (line 61) | func normalizeExtension(ext string) string {

FILE: deprecated/util/video.go
  function GetFileUrl (line 14) | func GetFileUrl(fileName string) string {
  function NewFileName (line 20) | func NewFileName(userId int64) string {
  function FillVideoListFields (line 32) | func FillVideoListFields(userId int64, videos *[]*models2.Video) (*time....
  function SaveImageFromVideo (line 60) | func SaveImageFromVideo(name string, isDebug bool) error {
Condensed preview — 52 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (114K chars).
[
  {
    "path": ".gitignore",
    "chars": 79,
    "preview": ".idea\n!deprecated/static/1-1.jpg\n!deprecated/static/1-1.mp4\ndeprecated/static/*"
  },
  {
    "path": "LICENSE",
    "chars": 1062,
    "preview": "MIT License\n\nCopyright (c) 2022 L_B__\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof t"
  },
  {
    "path": "README.md",
    "chars": 7563,
    "preview": "[写给后续参加字节跳动青训营的同学](https://github.com/ACking-you/byte_douyin_project/issues/10)\n# 抖音极简版\n<!-- PROJECT SHIELDS -->\n\n[![Con"
  },
  {
    "path": "deprecated/cache/index_map.go",
    "chars": 1608,
    "preview": "package cache\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"github.com/ACking-you/byte_douyin_project/config\"\n\t\"github.com/go-redis/redi"
  },
  {
    "path": "deprecated/cache/indexmap_test.go",
    "chars": 442,
    "preview": "package cache\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\nfunc TestProxyIndexMap_UpdateUserRelation(t *testing.T) {\n\tNewProxyIndexMap"
  },
  {
    "path": "deprecated/config/conf.go",
    "chars": 2094,
    "preview": "package config\n\nimport (\n\t\"fmt\"\n\t\"github.com/BurntSushi/toml\"\n\t\"log\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\ntyp"
  },
  {
    "path": "deprecated/config/config.toml",
    "chars": 455,
    "preview": "#关系型数据库配置\n[mysql]\nhost = \"127.0.0.1\"\nport = 3306\ndatabase = \"douyin\"\nusername = \"root\"\npassword = \"123\"\ncharset = \"utf8m"
  },
  {
    "path": "deprecated/go.mod",
    "chars": 336,
    "preview": "module github.com/ACking-you/byte_douyin_project\n\ngo 1.16\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.1.0\n\tgithub.com/dgri"
  },
  {
    "path": "deprecated/go.sum",
    "chars": 15023,
    "preview": "github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=\ngithub.com/BurntSushi/toml v1.1.0/go.m"
  },
  {
    "path": "deprecated/handlers/comment/post_comment_handler.go",
    "chars": 2173,
    "preview": "package comment\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/ACking-you/b"
  },
  {
    "path": "deprecated/handlers/comment/query_commentlist_handler.go",
    "chars": 1651,
    "preview": "package comment\n\nimport (\n\t\"errors\"\n\t\"github.com/ACking-you/byte_douyin_project/handlers/video\"\n\t\"github.com/ACking-you/"
  },
  {
    "path": "deprecated/handlers/user_info/post_follow_action_handler.go",
    "chars": 1888,
    "preview": "package user_info\n\nimport (\n\t\"errors\"\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/ACking-you/byte_d"
  },
  {
    "path": "deprecated/handlers/user_info/query_follow_list_handler.go",
    "chars": 1558,
    "preview": "package user_info\n\nimport (\n\t\"errors\"\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/ACking-you/byte_d"
  },
  {
    "path": "deprecated/handlers/user_info/query_follower_handler.go",
    "chars": 1747,
    "preview": "package user_info\n\nimport (\n\t\"errors\"\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n\tuser_info2 \"github.com/ACking"
  },
  {
    "path": "deprecated/handlers/user_info/userinfo_handler.go",
    "chars": 1397,
    "preview": "package user_info\n\nimport (\n\t\"errors\"\n\tmodels2 \"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/gin-gonic"
  },
  {
    "path": "deprecated/handlers/user_login/user_login_handler.go",
    "chars": 971,
    "preview": "package user_login\n\nimport (\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/ACking-you/byte_douyin_pro"
  },
  {
    "path": "deprecated/handlers/user_login/user_register_handler.go",
    "chars": 988,
    "preview": "package user_login\n\nimport (\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n\tuser_login2 \"github.com/ACking-you/byt"
  },
  {
    "path": "deprecated/handlers/video/feed_videolist_handler.go",
    "chars": 2300,
    "preview": "package video\n\nimport (\n\t\"errors\"\n\t\"github.com/ACking-you/byte_douyin_project/middleware\"\n\t\"github.com/ACking-you/byte_d"
  },
  {
    "path": "deprecated/handlers/video/post_favor_handler.go",
    "chars": 1536,
    "preview": "package video\n\nimport (\n\t\"errors\"\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/ACking-you/byte_douyi"
  },
  {
    "path": "deprecated/handlers/video/publish_video_handler.go",
    "chars": 1987,
    "preview": "package video\n\nimport (\n\t\"github.com/ACking-you/byte_douyin_project/config\"\n\t\"github.com/ACking-you/byte_douyin_project/"
  },
  {
    "path": "deprecated/handlers/video/query_favor_videolist_handler.go",
    "chars": 1450,
    "preview": "package video\n\nimport (\n\t\"errors\"\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/ACking-you/byte_douyi"
  },
  {
    "path": "deprecated/handlers/video/query_videolist_handler.go",
    "chars": 1371,
    "preview": "package video\n\nimport (\n\t\"errors\"\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/ACking-you/byte_douyi"
  },
  {
    "path": "deprecated/main.go",
    "chars": 323,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/ACking-you/byte_douyin_project/config\"\n\t\"github.com/ACking-you/byte_douyin_pr"
  },
  {
    "path": "deprecated/middleware/jwt.go",
    "chars": 1977,
    "preview": "package middleware\n\nimport (\n\tmodels2 \"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/dgrijalva/jwt-go\"\n"
  },
  {
    "path": "deprecated/middleware/jwt_test.go",
    "chars": 75,
    "preview": "package middleware\n\nimport (\n\t\"testing\"\n)\n\nfunc TestJwt(t *testing.T) {\n\n}\n"
  },
  {
    "path": "deprecated/middleware/normal.go",
    "chars": 662,
    "preview": "package middleware\n\nimport (\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/gin-gonic/gin\"\n\t\"net/http\""
  },
  {
    "path": "deprecated/middleware/password.go",
    "chars": 451,
    "preview": "package middleware\n\nimport (\n\t\"crypto/sha1\"\n\t\"encoding/hex\"\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc SHA1(s string) string {\n"
  },
  {
    "path": "deprecated/middleware/password_test.go",
    "chars": 133,
    "preview": "package middleware\n\nimport \"testing\"\n\n//40bd001563085fc35165329ea1ff5c5ecbdbbeef\nfunc TestSHA1(t *testing.T) {\n\tprint(SH"
  },
  {
    "path": "deprecated/models/comment.go",
    "chars": 2002,
    "preview": "package models\n\nimport (\n\t\"errors\"\n\t\"gorm.io/gorm\"\n\t\"time\"\n)\n\ntype Comment struct {\n\tId         int64     `json:\"id\"`\n\tU"
  },
  {
    "path": "deprecated/models/common.go",
    "chars": 136,
    "preview": "package models\n\ntype CommonResponse struct {\n\tStatusCode int32  `json:\"status_code\"`\n\tStatusMsg  string `json:\"status_ms"
  },
  {
    "path": "deprecated/models/init_db.go",
    "chars": 546,
    "preview": "package models\n\nimport (\n\t\"github.com/ACking-you/byte_douyin_project/config\"\n\t\"gorm.io/driver/mysql\"\n\t\"gorm.io/gorm\"\n)\n\n"
  },
  {
    "path": "deprecated/models/user_info.go",
    "chars": 3948,
    "preview": "package models\n\nimport (\n\t\"errors\"\n\t\"gorm.io/gorm\"\n\t\"log\"\n\t\"sync\"\n)\n\nvar (\n\tErrIvdPtr        = errors.New(\"空指针错误\")\n\tErrE"
  },
  {
    "path": "deprecated/models/user_info_test.go",
    "chars": 624,
    "preview": "package models\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n)\n\nfunc TestMain(m *testing.M) {\n\tInitDB()\n\tcode := m.Run()\n\tos.Exit(co"
  },
  {
    "path": "deprecated/models/user_login.go",
    "chars": 967,
    "preview": "package models\n\nimport (\n\t\"errors\"\n\t\"sync\"\n)\n\n// UserLogin 用户登录表,和UserInfo属于一对一关系\ntype UserLogin struct {\n\tId         in"
  },
  {
    "path": "deprecated/models/video.go",
    "chars": 4446,
    "preview": "package models\n\nimport (\n\t\"errors\"\n\t\"gorm.io/gorm\"\n\t\"log\"\n\t\"sync\"\n\t\"time\"\n)\n\ntype Video struct {\n\tId            int64   "
  },
  {
    "path": "deprecated/models/video_test.go",
    "chars": 611,
    "preview": "package models\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestVideoDAO_QueryVideoListByUserId(t *testing.T) {\n\tInitDB()"
  },
  {
    "path": "deprecated/router/router_douyin.go",
    "chars": 1973,
    "preview": "package router\n\nimport (\n\t\"github.com/ACking-you/byte_douyin_project/config\"\n\tcomment2 \"github.com/ACking-you/byte_douyi"
  },
  {
    "path": "deprecated/service/comment/post_comment.go",
    "chars": 2788,
    "preview": "package comment\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\tmodels2 \"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/ACki"
  },
  {
    "path": "deprecated/service/comment/query_comment_list.go",
    "chars": 1561,
    "preview": "package comment\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\tmodels2 \"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/ACki"
  },
  {
    "path": "deprecated/service/user_info/post_follow_action.go",
    "chars": 1668,
    "preview": "package user_info\n\nimport (\n\t\"errors\"\n\t\"github.com/ACking-you/byte_douyin_project/cache\"\n\t\"github.com/ACking-you/byte_do"
  },
  {
    "path": "deprecated/service/user_info/query_follow_list.go",
    "chars": 1399,
    "preview": "package user_info\n\nimport (\n\t\"errors\"\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n)\n\nvar (\n\tErrUserNotExist = er"
  },
  {
    "path": "deprecated/service/user_info/query_follower_list.go",
    "chars": 1403,
    "preview": "package user_info\n\nimport (\n\t\"github.com/ACking-you/byte_douyin_project/cache\"\n\t\"github.com/ACking-you/byte_douyin_proje"
  },
  {
    "path": "deprecated/service/user_login/post_user_login.go",
    "chars": 1981,
    "preview": "package user_login\n\nimport (\n\t\"errors\"\n\t\"github.com/ACking-you/byte_douyin_project/middleware\"\n\tmodels2 \"github.com/ACki"
  },
  {
    "path": "deprecated/service/user_login/query_user_login.go",
    "chars": 1831,
    "preview": "package user_login\n\nimport (\n\t\"errors\"\n\t\"github.com/ACking-you/byte_douyin_project/middleware\"\n\t\"github.com/ACking-you/b"
  },
  {
    "path": "deprecated/service/video/feed_videolist.go",
    "chars": 1844,
    "preview": "package video\n\nimport (\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/ACking-you/byte_douyin_project/"
  },
  {
    "path": "deprecated/service/video/post_favor_state.go",
    "chars": 1754,
    "preview": "package video\n\nimport (\n\t\"errors\"\n\t\"github.com/ACking-you/byte_douyin_project/cache\"\n\tmodels2 \"github.com/ACking-you/byt"
  },
  {
    "path": "deprecated/service/video/post_video.go",
    "chars": 1116,
    "preview": "package video\n\nimport (\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/ACking-you/byte_douyin_project/"
  },
  {
    "path": "deprecated/service/video/query_favor_videolist.go",
    "chars": 1547,
    "preview": "package video\n\nimport (\n\t\"errors\"\n\tmodels2 \"github.com/ACking-you/byte_douyin_project/models\"\n)\n\ntype FavorList struct {"
  },
  {
    "path": "deprecated/service/video/query_videolist.go",
    "chars": 1549,
    "preview": "package video\n\nimport (\n\t\"errors\"\n\t\"github.com/ACking-you/byte_douyin_project/cache\"\n\tmodels2 \"github.com/ACking-you/byt"
  },
  {
    "path": "deprecated/util/comment.go",
    "chars": 711,
    "preview": "package util\n\nimport (\n\t\"errors\"\n\tmodels2 \"github.com/ACking-you/byte_douyin_project/models\"\n)\n\nfunc FillCommentListFiel"
  },
  {
    "path": "deprecated/util/ffmpeg.go",
    "chars": 2926,
    "preview": "package util\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"sync\"\n\n\t\"github.com/ACking-you/byte_dou"
  },
  {
    "path": "deprecated/util/video.go",
    "chars": 1937,
    "preview": "package util\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/ACking-you/byte_douyin_project/cache\"\n\t\"github.com/ACking-you/byte_"
  }
]

About this extraction

This page contains the full source code of the ACking-you/byte_douyin_project GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 52 files (92.4 KB), approximately 34.3k tokens, and a symbol index with 280 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!