[
  {
    "path": ".gitignore",
    "content": "# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\n\n# Test binary, built with `go test -c`\n*.test\n\n# Output of the go coverage tool, specifically when used with LiteIDE\n*.out\n\n# Dependency directories (remove the comment below to include it)\n# vendor/\n\n*.log\n.idea/\n"
  },
  {
    "path": "README.md",
    "content": "# Go Web开发进阶实战课程\n\n\n## 课程源代码地址\n\n[Github仓库地址](https://github.com/Q1mi/goweb_pro)\n\n## 视频课程地址\n\n- [网易云课堂课程连接](https://study.163.com/course/courseMain.htm?courseId=1210171207&share=2&shareId=480000002229610)\n- [51CTO课程链接](https://edu.51cto.com/sd/68c23)\n\n\n"
  },
  {
    "path": "bluebell/.air.conf",
    "content": "# [Air](https://github.com/cosmtrek/air) TOML 格式的配置文件\n\n# 工作目录\n# 使用 . 或绝对路径，请注意 `tmp_dir` 目录必须在 `root` 目录下\nroot = \".\"\ntmp_dir = \"tmp\"\n\n[build]\n# 只需要写你平常编译使用的shell命令。你也可以使用 `make`\ncmd = \"go build -o ./tmp/main\"\n# 由`cmd`命令得到的二进制文件名\nbin = \"tmp/main\"\n# 自定义的二进制，可以添加额外的编译标识例如添加 GIN_MODE=release\nfull_bin = \"./tmp/main ./conf/config.yaml\"\n# 监听以下文件扩展名的文件.\ninclude_ext = [\"go\", \"tpl\", \"tmpl\", \"html\", \"yaml\"]\n# 忽略这些文件扩展名或目录\nexclude_dir = [\"assets\", \"tmp\", \"vendor\", \"frontend/node_modules\"]\n# 监听以下指定目录的文件\ninclude_dir = []\n# 排除以下文件\nexclude_file = []\n# 如果文件更改过于频繁，则没有必要在每次更改时都触发构建。可以设置触发构建的延迟时间\ndelay = 1000 # ms\n# 发生构建错误时，停止运行旧的二进制文件。\nstop_on_error = true\n# air的日志文件名，该日志文件放置在你的`tmp_dir`中\nlog = \"air_errors.log\"\n\n[log]\n# 显示日志时间\ntime = true\n\n[color]\n# 自定义每个部分显示的颜色。如果找不到颜色，使用原始的应用程序日志。\nmain = \"magenta\"\nwatcher = \"cyan\"\nbuild = \"yellow\"\nrunner = \"green\"\n\n[misc]\n# 退出时删除tmp目录\nclean_on_exit = true"
  },
  {
    "path": "bluebell/Makefile",
    "content": ".PHONY: all build run gotool clean help\n\nBINARY=\"xx\"\n\nall: gotool build\n\nbuild:\n\tCGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ${BINARY}\n\nrun:\n\t@go run ./main.go conf/config.yaml\n\ngotool:\n\tgo fmt ./\n\tgo vet ./\n\nclean:\n\t@if [ -f ${BINARY} ] ; then rm ${BINARY} ; fi\n\nhelp:\n\t@echo \"make - 格式化 Go 代码, 并编译生成二进制文件\"\n\t@echo \"make build - 编译 Go 代码, 生成二进制文件\"\n\t@echo \"make run - 直接运行 Go 代码\"\n\t@echo \"make clean - 移除二进制文件和 vim swap files\"\n\t@echo \"make gotool - 运行 Go 工具 'fmt' and 'vet'\"\n"
  },
  {
    "path": "bluebell/conf/config.yaml",
    "content": "name: \"bluebell\"\nmode: \"dev\"\nport: 8081\nversion: \"v0.0.1\"\nstart_time: \"2020-07-01\"\nmachine_id: 1\n\nlog:\n  level: \"debug\"\n  filename: \"web_app.log\"\n  max_size: 200\n  max_age: 30\n  max_backups: 7\nmysql:\n  host: \"127.0.0.1\"\n  port: 13306\n  user: \"root\"\n  password: \"root1234\"\n  dbname: \"bluebell\"\n  max_open_conns: 200\n  max_idle_conns: 50\nredis:\n  host: \"127.0.0.1\"\n  port: 16379\n  password: \"\"\n  db: 0\n  pool_size: 100"
  },
  {
    "path": "bluebell/controller/code.go",
    "content": "package controller\n\ntype ResCode int64\n\nconst (\n\tCodeSuccess ResCode = 1000 + iota\n\tCodeInvalidParam\n\tCodeUserExist\n\tCodeUserNotExist\n\tCodeInvalidPassword\n\tCodeServerBusy\n\n\tCodeNeedLogin\n\tCodeInvalidToken\n)\n\nvar codeMsgMap = map[ResCode]string{\n\tCodeSuccess:         \"success\",\n\tCodeInvalidParam:    \"请求参数错误\",\n\tCodeUserExist:       \"用户名已存在\",\n\tCodeUserNotExist:    \"用户名不存在\",\n\tCodeInvalidPassword: \"用户名或密码错误\",\n\tCodeServerBusy:      \"服务繁忙\",\n\n\tCodeNeedLogin:    \"需要登录\",\n\tCodeInvalidToken: \"无效的token\",\n}\n\nfunc (c ResCode) Msg() string {\n\tmsg, ok := codeMsgMap[c]\n\tif !ok {\n\t\tmsg = codeMsgMap[CodeServerBusy]\n\t}\n\treturn msg\n}\n"
  },
  {
    "path": "bluebell/controller/request.go",
    "content": "package controller\n\nimport (\n\t\"errors\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\nconst CtxUserIDKey = \"userID\"\n\nvar ErrorUserNotLogin = errors.New(\"用户未登录\")\n\n// getCurrentUser 获取当前登录的用户ID\nfunc getCurrentUser(c *gin.Context) (userID int64, err error) {\n\tuid, ok := c.Get(CtxUserIDKey)\n\tif !ok {\n\t\terr = ErrorUserNotLogin\n\t\treturn\n\t}\n\tuserID, ok = uid.(int64)\n\tif !ok {\n\t\terr = ErrorUserNotLogin\n\t\treturn\n\t}\n\treturn\n}\n"
  },
  {
    "path": "bluebell/controller/response.go",
    "content": "package controller\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n/*\n{\n\t\"code\": 10000, // 程序中的错误码\n\t\"msg\": xx,     // 提示信息\n\t\"data\": {},    // 数据\n}\n\n*/\n\ntype ResponseData struct {\n\tCode ResCode     `json:\"code\"`\n\tMsg  interface{} `json:\"msg\"`\n\tData interface{} `json:\"data\"`\n}\n\nfunc ResponseError(c *gin.Context, code ResCode) {\n\tc.JSON(http.StatusOK, &ResponseData{\n\t\tCode: code,\n\t\tMsg:  code.Msg(),\n\t\tData: nil,\n\t})\n}\n\nfunc ResponseErrorWithMsg(c *gin.Context, code ResCode, msg interface{}) {\n\tc.JSON(http.StatusOK, &ResponseData{\n\t\tCode: code,\n\t\tMsg:  msg,\n\t\tData: nil,\n\t})\n}\n\nfunc ResponseSuccess(c *gin.Context, data interface{}) {\n\tc.JSON(http.StatusOK, &ResponseData{\n\t\tCode: CodeSuccess,\n\t\tMsg:  CodeSuccess.Msg(),\n\t\tData: data,\n\t})\n}\n"
  },
  {
    "path": "bluebell/controller/user.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/logic\"\n\t\"bluebell/models\"\n\t\"errors\"\n\n\t\"github.com/go-playground/validator/v10\"\n\n\t\"go.uber.org/zap\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// SignUpHandler 处理注册请求的函数\nfunc SignUpHandler(c *gin.Context) {\n\t// 1. 获取参数和参数校验\n\tp := new(models.ParamSignUp)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\t// 请求参数有误，直接返回响应\n\t\tzap.L().Error(\"SignUp with invalid param\", zap.Error(err))\n\t\t// 判断err是不是validator.ValidationErrors 类型\n\t\terrs, ok := err.(validator.ValidationErrors)\n\t\tif !ok {\n\t\t\tResponseError(c, CodeInvalidParam)\n\t\t\treturn\n\t\t}\n\t\tResponseErrorWithMsg(c, CodeInvalidParam, removeTopStruct(errs.Translate(trans)))\n\t\treturn\n\t}\n\t// 2. 业务处理\n\tif err := logic.SignUp(p); err != nil {\n\t\tzap.L().Error(\"logic.SignUp failed\", zap.Error(err))\n\t\tif errors.Is(err, mysql.ErrorUserExist) {\n\t\t\tResponseError(c, CodeUserExist)\n\t\t\treturn\n\t\t}\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\t// 3. 返回响应\n\tResponseSuccess(c, nil)\n}\n\nfunc LoginHandler(c *gin.Context) {\n\t// 1.获取请求参数及参数校验\n\tp := new(models.ParamLogin)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\t// 请求参数有误，直接返回响应\n\t\tzap.L().Error(\"Login with invalid param\", zap.Error(err))\n\t\t// 判断err是不是validator.ValidationErrors 类型\n\t\terrs, ok := err.(validator.ValidationErrors)\n\t\tif !ok {\n\t\t\tResponseError(c, CodeInvalidParam)\n\t\t\treturn\n\t\t}\n\t\tResponseErrorWithMsg(c, CodeInvalidParam, removeTopStruct(errs.Translate(trans)))\n\t\treturn\n\t}\n\t// 2.业务逻辑处理\n\ttoken, err := logic.Login(p)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.Login failed\", zap.String(\"username\", p.Username), zap.Error(err))\n\t\tif errors.Is(err, mysql.ErrorUserNotExist) {\n\t\t\tResponseError(c, CodeUserNotExist)\n\t\t\treturn\n\t\t}\n\t\tResponseError(c, CodeInvalidPassword)\n\t\treturn\n\t}\n\n\t// 3.返回响应\n\tResponseSuccess(c, token)\n}\n"
  },
  {
    "path": "bluebell/controller/validator.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/models\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin/binding\"\n\t\"github.com/go-playground/locales/en\"\n\t\"github.com/go-playground/locales/zh\"\n\tut \"github.com/go-playground/universal-translator\"\n\t\"github.com/go-playground/validator/v10\"\n\tenTranslations \"github.com/go-playground/validator/v10/translations/en\"\n\tzhTranslations \"github.com/go-playground/validator/v10/translations/zh\"\n)\n\n// 定义一个全局翻译器T\nvar trans ut.Translator\n\n// InitTrans 初始化翻译器\nfunc InitTrans(locale string) (err error) {\n\t// 修改gin框架中的Validator引擎属性，实现自定制\n\tif v, ok := binding.Validator.Engine().(*validator.Validate); ok {\n\n\t\t// 注册一个获取json tag的自定义方法\n\t\tv.RegisterTagNameFunc(func(fld reflect.StructField) string {\n\t\t\tname := strings.SplitN(fld.Tag.Get(\"json\"), \",\", 2)[0]\n\t\t\tif name == \"-\" {\n\t\t\t\treturn \"\"\n\t\t\t}\n\t\t\treturn name\n\t\t})\n\n\t\t// 为SignUpParam注册自定义校验方法\n\t\tv.RegisterStructValidation(SignUpParamStructLevelValidation, models.ParamSignUp{})\n\n\t\tzhT := zh.New() // 中文翻译器\n\t\tenT := en.New() // 英文翻译器\n\n\t\t// 第一个参数是备用（fallback）的语言环境\n\t\t// 后面的参数是应该支持的语言环境（支持多个）\n\t\t// uni := ut.New(zhT, zhT) 也是可以的\n\t\tuni := ut.New(enT, zhT, enT)\n\n\t\t// locale 通常取决于 http 请求头的 'Accept-Language'\n\t\tvar ok bool\n\t\t// 也可以使用 uni.FindTranslator(...) 传入多个locale进行查找\n\t\ttrans, ok = uni.GetTranslator(locale)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"uni.GetTranslator(%s) failed\", locale)\n\t\t}\n\n\t\t// 注册翻译器\n\t\tswitch locale {\n\t\tcase \"en\":\n\t\t\terr = enTranslations.RegisterDefaultTranslations(v, trans)\n\t\tcase \"zh\":\n\t\t\terr = zhTranslations.RegisterDefaultTranslations(v, trans)\n\t\tdefault:\n\t\t\terr = enTranslations.RegisterDefaultTranslations(v, trans)\n\t\t}\n\t\treturn\n\t}\n\treturn\n}\n\n// removeTopStruct 去除提示信息中的结构体名称\nfunc removeTopStruct(fields map[string]string) map[string]string {\n\tres := map[string]string{}\n\tfor field, err := range fields {\n\t\tres[field[strings.Index(field, \".\")+1:]] = err\n\t}\n\treturn res\n}\n\n// SignUpParamStructLevelValidation 自定义SignUpParam结构体校验函数\nfunc SignUpParamStructLevelValidation(sl validator.StructLevel) {\n\tsu := sl.Current().Interface().(models.ParamSignUp)\n\n\tif su.Password != su.RePassword {\n\t\t// 输出错误提示信息，最后一个参数就是传递的param\n\t\tsl.ReportError(su.RePassword, \"re_password\", \"RePassword\", \"eqfield\", \"password\")\n\t}\n}\n"
  },
  {
    "path": "bluebell/dao/mysql/mysql.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/setting\"\n\t\"fmt\"\n\n\t_ \"github.com/go-sql-driver/mysql\"\n\t\"github.com/jmoiron/sqlx\"\n)\n\nvar db *sqlx.DB\n\n// Init 初始化MySQL连接\nfunc Init(cfg *setting.MySQLConfig) (err error) {\n\t// \"user:password@tcp(host:port)/dbname\"\n\tdsn := fmt.Sprintf(\"%s:%s@tcp(%s:%d)/%s?parseTime=true&loc=Local\", cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.DB)\n\tdb, err = sqlx.Connect(\"mysql\", dsn)\n\tif err != nil {\n\t\treturn\n\t}\n\tdb.SetMaxOpenConns(cfg.MaxOpenConns)\n\tdb.SetMaxIdleConns(cfg.MaxIdleConns)\n\treturn\n}\n\n// Close 关闭MySQL连接\nfunc Close() {\n\t_ = db.Close()\n}\n"
  },
  {
    "path": "bluebell/dao/mysql/user.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/models\"\n\t\"crypto/md5\"\n\t\"database/sql\"\n\t\"encoding/hex\"\n\t\"errors\"\n)\n\n// 把每一步数据库操作封装成函数\n// 待logic层根据业务需求调用\n\nconst secret = \"liwenzhou.com\"\n\nvar (\n\tErrorUserExist       = errors.New(\"用户已存在\")\n\tErrorUserNotExist    = errors.New(\"用户不存在\")\n\tErrorInvalidPassword = errors.New(\"用户名或密码错误\")\n)\n\n// CheckUserExist 检查指定用户名的用户是否存在\nfunc CheckUserExist(username string) (err error) {\n\tsqlStr := `select count(user_id) from user where username = ?`\n\tvar count int64\n\tif err := db.Get(&count, sqlStr, username); err != nil {\n\t\treturn err\n\t}\n\tif count > 0 {\n\t\treturn ErrorUserExist\n\t}\n\treturn\n}\n\n// InsertUser 想数据库中插入一条新的用户记录\nfunc InsertUser(user *models.User) (err error) {\n\t// 对密码进行加密\n\tuser.Password = encryptPassword(user.Password)\n\t// 执行SQL语句入库\n\tsqlStr := `insert into user(user_id, username, password) values(?,?,?)`\n\t_, err = db.Exec(sqlStr, user.UserID, user.Username, user.Password)\n\treturn\n}\n\n// encryptPassword 密码加密\nfunc encryptPassword(oPassword string) string {\n\th := md5.New()\n\th.Write([]byte(secret))\n\treturn hex.EncodeToString(h.Sum([]byte(oPassword)))\n}\n\nfunc Login(user *models.User) (err error) {\n\toPassword := user.Password // 用户登录的密码\n\tsqlStr := `select user_id, username, password from user where username=?`\n\terr = db.Get(user, sqlStr, user.Username)\n\tif err == sql.ErrNoRows {\n\t\treturn ErrorUserNotExist\n\t}\n\tif err != nil {\n\t\t// 查询数据库失败\n\t\treturn err\n\t}\n\t// 判断密码是否正确\n\tpassword := encryptPassword(oPassword)\n\tif password != user.Password {\n\t\treturn ErrorInvalidPassword\n\t}\n\treturn\n}\n"
  },
  {
    "path": "bluebell/dao/redis/redis.go",
    "content": "package redis\n\nimport (\n\t\"bluebell/setting\"\n\t\"fmt\"\n\n\t\"github.com/go-redis/redis\"\n)\n\nvar (\n\tclient *redis.Client\n\tNil    = redis.Nil\n)\n\n// Init 初始化连接\nfunc Init(cfg *setting.RedisConfig) (err error) {\n\tclient = redis.NewClient(&redis.Options{\n\t\tAddr:         fmt.Sprintf(\"%s:%d\", cfg.Host, cfg.Port),\n\t\tPassword:     cfg.Password, // no password set\n\t\tDB:           cfg.DB,       // use default DB\n\t\tPoolSize:     cfg.PoolSize,\n\t\tMinIdleConns: cfg.MinIdleConns,\n\t})\n\n\t_, err = client.Ping().Result()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc Close() {\n\t_ = client.Close()\n}\n"
  },
  {
    "path": "bluebell/go.mod",
    "content": "module bluebell\n\ngo 1.14\n\nrequire (\n\tgithub.com/bwmarrin/snowflake v0.3.0\n\tgithub.com/dgrijalva/jwt-go v3.2.0+incompatible\n\tgithub.com/fsnotify/fsnotify v1.4.9\n\tgithub.com/gin-gonic/gin v1.6.3\n\tgithub.com/go-playground/locales v0.13.0\n\tgithub.com/go-playground/universal-translator v0.17.0\n\tgithub.com/go-playground/validator/v10 v10.2.0\n\tgithub.com/go-redis/redis v6.15.8+incompatible\n\tgithub.com/go-sql-driver/mysql v1.5.0\n\tgithub.com/jmoiron/sqlx v1.2.0\n\tgithub.com/natefinch/lumberjack v2.0.0+incompatible\n\tgithub.com/onsi/ginkgo v1.14.0 // indirect\n\tgithub.com/sony/sonyflake v1.0.0 // indirect\n\tgithub.com/spf13/viper v1.7.0\n\tgo.uber.org/zap v1.15.0\n\tgopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect\n)\n"
  },
  {
    "path": "bluebell/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ngithub.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=\ngithub.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=\ngithub.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=\ngithub.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=\ngithub.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=\ngithub.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=\ngithub.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=\ngithub.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0=\ngithub.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=\ngithub.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=\ngithub.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=\ngithub.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=\ngithub.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=\ngithub.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=\ngithub.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=\ngithub.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=\ngithub.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=\ngithub.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=\ngithub.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=\ngithub.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=\ngithub.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=\ngithub.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=\ngithub.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=\ngithub.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=\ngithub.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=\ngithub.com/go-redis/redis v6.15.8+incompatible h1:BKZuG6mCnRj5AOaWJXoCgf6rqTYnYJLe4en2hxT7r9o=\ngithub.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=\ngithub.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=\ngithub.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=\ngithub.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=\ngithub.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=\ngithub.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=\ngithub.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=\ngithub.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=\ngithub.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=\ngithub.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=\ngithub.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=\ngithub.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=\ngithub.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=\ngithub.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=\ngithub.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=\ngithub.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=\ngithub.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=\ngithub.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=\ngithub.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=\ngithub.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=\ngithub.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=\ngithub.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=\ngithub.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=\ngithub.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=\ngithub.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=\ngithub.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=\ngithub.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=\ngithub.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=\ngithub.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=\ngithub.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=\ngithub.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=\ngithub.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=\ngithub.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=\ngithub.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=\ngithub.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=\ngithub.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=\ngithub.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=\ngithub.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=\ngithub.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=\ngithub.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=\ngithub.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=\ngithub.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=\ngithub.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=\ngithub.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=\ngithub.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=\ngithub.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=\ngithub.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=\ngithub.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=\ngithub.com/sony/sonyflake v1.0.0 h1:MpU6Ro7tfXwgn2l5eluf9xQvQJDROTBImNCfRXn/YeM=\ngithub.com/sony/sonyflake v1.0.0/go.mod h1:Jv3cfhf/UFtolOTTRd3q4Nl6ENqM+KfyZ5PseKfZGF4=\ngithub.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=\ngithub.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=\ngithub.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=\ngithub.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=\ngithub.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=\ngithub.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=\ngithub.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=\ngithub.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=\ngithub.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=\ngithub.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=\ngithub.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=\ngithub.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=\ngithub.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=\ngithub.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=\ngithub.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=\ngo.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=\ngo.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=\ngo.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=\ngo.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=\ngo.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngo.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=\ngo.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc h1:NCy3Ohtk6Iny5V/reW2Ktypo4zIpWBdRJ1uFMjBxdg8=\ngolang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=\ngopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=\ngopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=\ngopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\n"
  },
  {
    "path": "bluebell/logger/logger.go",
    "content": "package logger\n\nimport (\n\t\"bluebell/setting\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"os\"\n\t\"runtime/debug\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/natefinch/lumberjack\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\nvar lg *zap.Logger\n\n// Init 初始化lg\nfunc Init(cfg *setting.LogConfig, mode string) (err error) {\n\twriteSyncer := getLogWriter(cfg.Filename, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge)\n\tencoder := getEncoder()\n\tvar l = new(zapcore.Level)\n\terr = l.UnmarshalText([]byte(cfg.Level))\n\tif err != nil {\n\t\treturn\n\t}\n\tvar core zapcore.Core\n\tif mode == \"dev\" {\n\t\t// 进入开发模式，日志输出到终端\n\t\tconsoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())\n\t\tcore = zapcore.NewTee(\n\t\t\tzapcore.NewCore(encoder, writeSyncer, l),\n\t\t\tzapcore.NewCore(consoleEncoder, zapcore.Lock(os.Stdout), zapcore.DebugLevel),\n\t\t)\n\t} else {\n\t\tcore = zapcore.NewCore(encoder, writeSyncer, l)\n\t}\n\n\tlg = zap.New(core, zap.AddCaller())\n\n\tzap.ReplaceGlobals(lg)\n\tzap.L().Info(\"init logger success\")\n\treturn\n}\n\nfunc getEncoder() zapcore.Encoder {\n\tencoderConfig := zap.NewProductionEncoderConfig()\n\tencoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder\n\tencoderConfig.TimeKey = \"time\"\n\tencoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder\n\tencoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder\n\tencoderConfig.EncodeCaller = zapcore.ShortCallerEncoder\n\treturn zapcore.NewJSONEncoder(encoderConfig)\n}\n\nfunc getLogWriter(filename string, maxSize, maxBackup, maxAge int) zapcore.WriteSyncer {\n\tlumberJackLogger := &lumberjack.Logger{\n\t\tFilename:   filename,\n\t\tMaxSize:    maxSize,\n\t\tMaxBackups: maxBackup,\n\t\tMaxAge:     maxAge,\n\t}\n\treturn zapcore.AddSync(lumberJackLogger)\n}\n\n// GinLogger 接收gin框架默认的日志\nfunc GinLogger() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tstart := time.Now()\n\t\tpath := c.Request.URL.Path\n\t\tquery := c.Request.URL.RawQuery\n\t\tc.Next()\n\n\t\tcost := time.Since(start)\n\t\tlg.Info(path,\n\t\t\tzap.Int(\"status\", c.Writer.Status()),\n\t\t\tzap.String(\"method\", c.Request.Method),\n\t\t\tzap.String(\"path\", path),\n\t\t\tzap.String(\"query\", query),\n\t\t\tzap.String(\"ip\", c.ClientIP()),\n\t\t\tzap.String(\"user-agent\", c.Request.UserAgent()),\n\t\t\tzap.String(\"errors\", c.Errors.ByType(gin.ErrorTypePrivate).String()),\n\t\t\tzap.Duration(\"cost\", cost),\n\t\t)\n\t}\n}\n\n// GinRecovery recover掉项目可能出现的panic，并使用zap记录相关日志\nfunc GinRecovery(stack bool) gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tdefer func() {\n\t\t\tif err := recover(); err != nil {\n\t\t\t\t// Check for a broken connection, as it is not really a\n\t\t\t\t// condition that warrants a panic stack trace.\n\t\t\t\tvar brokenPipe bool\n\t\t\t\tif ne, ok := err.(*net.OpError); ok {\n\t\t\t\t\tif se, ok := ne.Err.(*os.SyscallError); ok {\n\t\t\t\t\t\tif strings.Contains(strings.ToLower(se.Error()), \"broken pipe\") || strings.Contains(strings.ToLower(se.Error()), \"connection reset by peer\") {\n\t\t\t\t\t\t\tbrokenPipe = true\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\thttpRequest, _ := httputil.DumpRequest(c.Request, false)\n\t\t\t\tif brokenPipe {\n\t\t\t\t\tlg.Error(c.Request.URL.Path,\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t)\n\t\t\t\t\t// If the connection is dead, we can't write a status to it.\n\t\t\t\t\tc.Error(err.(error)) // nolint: errcheck\n\t\t\t\t\tc.Abort()\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif stack {\n\t\t\t\t\tlg.Error(\"[Recovery from panic]\",\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t\tzap.String(\"stack\", string(debug.Stack())),\n\t\t\t\t\t)\n\t\t\t\t} else {\n\t\t\t\t\tlg.Error(\"[Recovery from panic]\",\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tc.AbortWithStatus(http.StatusInternalServerError)\n\t\t\t}\n\t\t}()\n\t\tc.Next()\n\t}\n}\n"
  },
  {
    "path": "bluebell/logic/user.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/models\"\n\t\"bluebell/pkg/jwt\"\n\t\"bluebell/pkg/snowflake\"\n)\n\n// 存放业务逻辑的代码\n\nfunc SignUp(p *models.ParamSignUp) (err error) {\n\t// 1.判断用户存不存在\n\tif err := mysql.CheckUserExist(p.Username); err != nil {\n\t\treturn err\n\t}\n\t// 2.生成UID\n\tuserID := snowflake.GenID()\n\t// 构造一个User实例\n\tuser := &models.User{\n\t\tUserID:   userID,\n\t\tUsername: p.Username,\n\t\tPassword: p.Password,\n\t}\n\t// 3.保存进数据库\n\treturn mysql.InsertUser(user)\n}\n\nfunc Login(p *models.ParamLogin) (token string, err error) {\n\tuser := &models.User{\n\t\tUsername: p.Username,\n\t\tPassword: p.Password,\n\t}\n\t// 传递的是指针，就能拿到user.UserID\n\tif err := mysql.Login(user); err != nil {\n\t\treturn \"\", err\n\t}\n\t// 生成JWT\n\treturn jwt.GenToken(user.UserID, user.Username)\n}\n"
  },
  {
    "path": "bluebell/main.go",
    "content": "package main\n\nimport (\n\t\"bluebell/controller\"\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/dao/redis\"\n\t\"bluebell/logger\"\n\t\"bluebell/pkg/snowflake\"\n\t\"bluebell/router\"\n\t\"bluebell/setting\"\n\t\"fmt\"\n\t\"os\"\n)\n\nfunc main() {\n\tif len(os.Args) < 2 {\n\t\tfmt.Println(\"need config file.eg: bluebell config.yaml\")\n\t\treturn\n\t}\n\t// 加载配置\n\tif err := setting.Init(os.Args[1]); err != nil {\n\t\tfmt.Printf(\"load config failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tif err := logger.Init(setting.Conf.LogConfig, setting.Conf.Mode); err != nil {\n\t\tfmt.Printf(\"init logger failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tif err := mysql.Init(setting.Conf.MySQLConfig); err != nil {\n\t\tfmt.Printf(\"init mysql failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer mysql.Close() // 程序退出关闭数据库连接\n\tif err := redis.Init(setting.Conf.RedisConfig); err != nil {\n\t\tfmt.Printf(\"init redis failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer redis.Close()\n\n\tif err := snowflake.Init(setting.Conf.StartTime, setting.Conf.MachineID); err != nil {\n\t\tfmt.Printf(\"init snowflake failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\t// 初始化gin框架内置的校验器使用的翻译器\n\tif err := controller.InitTrans(\"zh\"); err != nil {\n\t\tfmt.Printf(\"init validator trans failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\t// 注册路由\n\tr := router.SetupRouter(setting.Conf.Mode)\n\terr := r.Run(fmt.Sprintf(\":%d\", setting.Conf.Port))\n\tif err != nil {\n\t\tfmt.Printf(\"run server failed, err:%v\\n\", err)\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "bluebell/middlewares/auth.go",
    "content": "package middlewares\n\nimport (\n\t\"bluebell/controller\"\n\t\"bluebell/pkg/jwt\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// JWTAuthMiddleware 基于JWT的认证中间件\nfunc JWTAuthMiddleware() func(c *gin.Context) {\n\treturn func(c *gin.Context) {\n\t\t// 客户端携带Token有三种方式 1.放在请求头 2.放在请求体 3.放在URI\n\t\t// 这里假设Token放在Header的Authorization中，并使用Bearer开头\n\t\t// Authorization: Bearer xxxxxxx.xxx.xxx  / X-TOKEN: xxx.xxx.xx\n\t\t// 这里的具体实现方式要依据你的实际业务情况决定\n\t\tauthHeader := c.Request.Header.Get(\"Authorization\")\n\t\tif authHeader == \"\" {\n\t\t\tcontroller.ResponseError(c, controller.CodeNeedLogin)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// 按空格分割\n\t\tparts := strings.SplitN(authHeader, \" \", 2)\n\t\tif !(len(parts) == 2 && parts[0] == \"Bearer\") {\n\t\t\tcontroller.ResponseError(c, controller.CodeInvalidToken)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// parts[1]是获取到的tokenString，我们使用之前定义好的解析JWT的函数来解析它\n\t\tmc, err := jwt.ParseToken(parts[1])\n\t\tif err != nil {\n\t\t\tcontroller.ResponseError(c, controller.CodeInvalidToken)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// 将当前请求的userID信息保存到请求的上下文c上\n\t\tc.Set(controller.CtxUserIDKey, mc.UserID)\n\n\t\tc.Next() // 后续的处理请求的函数中 可以用过c.Get(CtxUserIDKey) 来获取当前请求的用户信息\n\t}\n}\n"
  },
  {
    "path": "bluebell/models/create_table.sql",
    "content": "\nCREATE TABLE `user` (\n    `id` bigint(20) NOT NULL AUTO_INCREMENT,\n    `user_id` bigint(20) NOT NULL,\n    `username` varchar(64) COLLATE utf8mb4_general_ci NOT NULL,\n    `password` varchar(64) COLLATE utf8mb4_general_ci NOT NULL,\n    `email` varchar(64) COLLATE utf8mb4_general_ci,\n    `gender` tinyint(4) NOT NULL DEFAULT '0',\n    `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n    `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    PRIMARY KEY (`id`),\n    UNIQUE KEY `idx_username` (`username`) USING BTREE,\n    UNIQUE KEY `idx_user_id` (`user_id`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;"
  },
  {
    "path": "bluebell/models/params.go",
    "content": "package models\n\n// 定义请求的参数结构体\n\n// ParamSignUp 注册请求参数\ntype ParamSignUp struct {\n\tUsername   string `json:\"username\" binding:\"required\"`\n\tPassword   string `json:\"password\" binding:\"required\"`\n\tRePassword string `json:\"re_password\" binding:\"required,eqfield=Password\"`\n}\n\n// ParamLogin 登录请求参数\ntype ParamLogin struct {\n\tUsername string `json:\"username\" binding:\"required\"`\n\tPassword string `json:\"password\" binding:\"required\"`\n}\n"
  },
  {
    "path": "bluebell/models/user.go",
    "content": "package models\n\ntype User struct {\n\tUserID   int64  `db:\"user_id\"`\n\tUsername string `db:\"username\"`\n\tPassword string `db:\"password\"`\n}\n"
  },
  {
    "path": "bluebell/pkg/jwt/jwt.go",
    "content": "package jwt\n\nimport (\n\t\"errors\"\n\t\"time\"\n\n\t\"github.com/dgrijalva/jwt-go\"\n)\n\nconst TokenExpireDuration = time.Hour * 2\n\nvar mySecret = []byte(\"夏天夏天悄悄过去\")\n\n// MyClaims 自定义声明结构体并内嵌jwt.StandardClaims\n// jwt包自带的jwt.StandardClaims只包含了官方字段\n// 我们这里需要额外记录一个username字段，所以要自定义结构体\n// 如果想要保存更多信息，都可以添加到这个结构体中\ntype MyClaims struct {\n\tUserID   int64  `json:\"user_id\"`\n\tUsername string `json:\"username\"`\n\tjwt.StandardClaims\n}\n\n// GenToken 生成JWT\nfunc GenToken(userID int64, username string) (string, error) {\n\t// 创建一个我们自己的声明的数据\n\tc := MyClaims{\n\t\tuserID,\n\t\t\"username\", // 自定义字段\n\t\tjwt.StandardClaims{\n\t\t\tExpiresAt: time.Now().Add(TokenExpireDuration).Unix(), // 过期时间\n\t\t\tIssuer:    \"bluebell\",                                 // 签发人\n\t\t},\n\t}\n\t// 使用指定的签名方法创建签名对象\n\ttoken := jwt.NewWithClaims(jwt.SigningMethodHS256, c)\n\t// 使用指定的secret签名并获得完整的编码后的字符串token\n\treturn token.SignedString(mySecret)\n}\n\n// ParseToken 解析JWT\nfunc ParseToken(tokenString string) (*MyClaims, error) {\n\t// 解析token\n\tvar mc = new(MyClaims)\n\ttoken, err := jwt.ParseWithClaims(tokenString, mc, func(token *jwt.Token) (i interface{}, err error) {\n\t\treturn mySecret, nil\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif token.Valid { // 校验token\n\t\treturn mc, nil\n\t}\n\treturn nil, errors.New(\"invalid token\")\n}\n"
  },
  {
    "path": "bluebell/pkg/snowflake/snowflake.go",
    "content": "package snowflake\n\nimport (\n\t\"time\"\n\n\tsf \"github.com/bwmarrin/snowflake\"\n)\n\nvar node *sf.Node\n\nfunc Init(startTime string, machineID int64) (err error) {\n\tvar st time.Time\n\tst, err = time.Parse(\"2006-01-02\", startTime)\n\tif err != nil {\n\t\treturn\n\t}\n\tsf.Epoch = st.UnixNano() / 1000000\n\tnode, err = sf.NewNode(machineID)\n\treturn\n}\nfunc GenID() int64 {\n\treturn node.Generate().Int64()\n}\n"
  },
  {
    "path": "bluebell/router/route.go",
    "content": "package router\n\nimport (\n\t\"bluebell/controller\"\n\t\"bluebell/logger\"\n\t\"bluebell/middlewares\"\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc SetupRouter(mode string) *gin.Engine {\n\tif mode == gin.ReleaseMode {\n\t\tgin.SetMode(gin.ReleaseMode) // gin设置成发布模式\n\t}\n\tr := gin.New()\n\tr.Use(logger.GinLogger(), logger.GinRecovery(true))\n\n\t// 注册\n\tr.POST(\"/signup\", controller.SignUpHandler)\n\t// 登录\n\tr.POST(\"/login\", controller.LoginHandler)\n\n\tr.GET(\"/ping\", middlewares.JWTAuthMiddleware(), func(c *gin.Context) {\n\t\t// 如果是登录的用户,判断请求头中是否有 有效的JWT  ？\n\t\tc.JSON(http.StatusOK, gin.H{\n\t\t\t\"msg\": \"ok\",\n\t\t})\n\t})\n\n\tr.NoRoute(func(c *gin.Context) {\n\t\tc.JSON(http.StatusOK, gin.H{\n\t\t\t\"msg\": \"404\",\n\t\t})\n\t})\n\treturn r\n}\n"
  },
  {
    "path": "bluebell/setting/setting.go",
    "content": "package setting\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\t\"github.com/spf13/viper\"\n)\n\nvar Conf = new(AppConfig)\n\ntype AppConfig struct {\n\tName      string `mapstructure:\"name\"`\n\tMode      string `mapstructure:\"mode\"`\n\tVersion   string `mapstructure:\"version\"`\n\tStartTime string `mapstructure:\"start_time\"`\n\tMachineID int64  `mapstructure:\"machine_id\"`\n\tPort      int    `mapstructure:\"port\"`\n\n\t*LogConfig   `mapstructure:\"log\"`\n\t*MySQLConfig `mapstructure:\"mysql\"`\n\t*RedisConfig `mapstructure:\"redis\"`\n}\n\ntype MySQLConfig struct {\n\tHost         string `mapstructure:\"host\"`\n\tUser         string `mapstructure:\"user\"`\n\tPassword     string `mapstructure:\"password\"`\n\tDB           string `mapstructure:\"dbname\"`\n\tPort         int    `mapstructure:\"port\"`\n\tMaxOpenConns int    `mapstructure:\"max_open_conns\"`\n\tMaxIdleConns int    `mapstructure:\"max_idle_conns\"`\n}\n\ntype RedisConfig struct {\n\tHost         string `mapstructure:\"host\"`\n\tPassword     string `mapstructure:\"password\"`\n\tPort         int    `mapstructure:\"port\"`\n\tDB           int    `mapstructure:\"db\"`\n\tPoolSize     int    `mapstructure:\"pool_size\"`\n\tMinIdleConns int    `mapstructure:\"min_idle_conns\"`\n}\n\ntype LogConfig struct {\n\tLevel      string `mapstructure:\"level\"`\n\tFilename   string `mapstructure:\"filename\"`\n\tMaxSize    int    `mapstructure:\"max_size\"`\n\tMaxAge     int    `mapstructure:\"max_age\"`\n\tMaxBackups int    `mapstructure:\"max_backups\"`\n}\n\nfunc Init(filePath string) (err error) {\n\t// 方式1：直接指定配置文件路径（相对路径或者绝对路径）\n\t// 相对路径：相对执行的可执行文件的相对路径\n\t//viper.SetConfigFile(\"./conf/config.yaml\")\n\t// 绝对路径：系统中实际的文件路径\n\t//viper.SetConfigFile(\"/Users/liwenzhou/Desktop/bluebell/conf/config.yaml\")\n\n\t// 方式2：指定配置文件名和配置文件的位置，viper自行查找可用的配置文件\n\t// 配置文件名不需要带后缀\n\t// 配置文件位置可配置多个\n\t//viper.SetConfigName(\"config\") // 指定配置文件名（不带后缀）\n\t//viper.AddConfigPath(\".\") // 指定查找配置文件的路径（这里使用相对路径）\n\t//viper.AddConfigPath(\"./conf\")      // 指定查找配置文件的路径（这里使用相对路径）\n\n\t// 基本上是配合远程配置中心使用的，告诉viper当前的数据使用什么格式去解析\n\t//viper.SetConfigType(\"json\")\n\n\tviper.SetConfigFile(filePath)\n\n\terr = viper.ReadInConfig() // 读取配置信息\n\tif err != nil {\n\t\t// 读取配置信息失败\n\t\tfmt.Printf(\"viper.ReadInConfig failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\n\t// 把读取到的配置信息反序列化到 Conf 变量中\n\tif err := viper.Unmarshal(Conf); err != nil {\n\t\tfmt.Printf(\"viper.Unmarshal failed, err:%v\\n\", err)\n\t}\n\n\tviper.WatchConfig()\n\tviper.OnConfigChange(func(in fsnotify.Event) {\n\t\tfmt.Println(\"配置文件修改了...\")\n\t\tif err := viper.Unmarshal(Conf); err != nil {\n\t\t\tfmt.Printf(\"viper.Unmarshal failed, err:%v\\n\", err)\n\t\t}\n\t})\n\treturn\n}\n"
  },
  {
    "path": "docker_demo/Dockerfile",
    "content": "FROM golang:alpine AS builder\n\n# 为我们的镜像设置必要的环境变量\nENV GO111MODULE=on \\\n    CGO_ENABLED=0 \\\n    GOOS=linux \\\n    GOARCH=amd64\n\n# 移动到工作目录：/build\nWORKDIR /build\n\n# 将代码复制到容器中\nCOPY . .\n\n# 将我们的代码编译成二进制可执行文件 app\nRUN go build -o app .\n\n###################\n# 接下来创建一个小镜像\n###################\nFROM scratch\n\n# 从builder镜像中把/dist/app 拷贝到当前目录\nCOPY --from=builder /build/app /\n\n# 需要运行的命令\nENTRYPOINT [\"/app\"]"
  },
  {
    "path": "docker_demo/Dockerfile.back",
    "content": "FROM golang:alpine\n\n# 为我们的镜像设置必要的环境变量\nENV GO111MODULE=on \\\n    CGO_ENABLED=0 \\\n    GOOS=linux \\\n    GOARCH=amd64\n\n# 移动到工作目录：/build\nWORKDIR /build\n\n# 将代码复制到容器中\nCOPY . .\n\n# 将我们的代码编译成二进制可执行文件app\nRUN go build -o app .\n\n\n\n# 声明服务端口\nEXPOSE 8888\n\n# 启动容器时运行的命令\nCMD [\"/build/app\"]"
  },
  {
    "path": "docker_demo/go.mod",
    "content": "module \"docker_demo\""
  },
  {
    "path": "docker_demo/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n)\n\nfunc main() {\n\thttp.HandleFunc(\"/\", hello)\n\tserver := &http.Server{\n\t\tAddr: \":8888\",\n\t}\n\tfmt.Println(\"server startup...\")\n\tif err := server.ListenAndServe(); err != nil {\n\t\tfmt.Printf(\"server startup failed, err:%v\\n\", err)\n\t}\n}\n\nfunc hello(w http.ResponseWriter, _ *http.Request) {\n\tw.Write([]byte(\"hello liwenzhou.com!\"))\n}"
  },
  {
    "path": "flag_demo/args_demo.go",
    "content": "package main\n\n\nimport (\n\t\"fmt\"\n\t\"os\"\n)\n\n//os.Args demo\nfunc main() {\n\t// os.Args是一个[]string\n\t// os.Args[0] 是当前执行的程序\n\tfmt.Println(os.Args)\n\tif len(os.Args) > 0 {\n\t\tfor index, arg := range os.Args {\n\t\t\tfmt.Printf(\"args[%d]=%v\\n\", index, arg)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "flag_demo/flag_demo.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\t//定义命令行参数方式1\n\tvar name string\n\tvar age int\n\tvar married bool\n\tvar delay time.Duration\n\tflag.StringVar(&name, \"name\", \"张三\", \"姓名\")\n\tflag.IntVar(&age, \"age\", 18, \"年龄\")\n\tflag.BoolVar(&married, \"married\", false, \"婚否\")\n\tflag.DurationVar(&delay, \"d\", 0, \"延迟的时间间隔\")\n\n\t//解析命令行参数\n\tflag.Parse()\n\n\tfmt.Println(name, age, married, delay)\n\n\t//返回命令行参数后的其他参数\n\tfmt.Println(flag.Args())\n\t//返回命令行参数后的其他参数个数\n\tfmt.Println(flag.NArg())\n\t//返回使用的命令行参数个数\n\tfmt.Println(flag.NFlag())\n}"
  },
  {
    "path": "flag_demo/go.mod",
    "content": "module flag_demo\n\ngo 1.14\n"
  },
  {
    "path": "gin_demo/go.mod",
    "content": "module gin_demo\n\ngo 1.14\n\nrequire github.com/gin-gonic/gin v1.6.3\n"
  },
  {
    "path": "gin_demo/go.sum",
    "content": "github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=\ngithub.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=\ngithub.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=\ngithub.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=\ngithub.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=\ngithub.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=\ngithub.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=\ngithub.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=\ngithub.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=\ngithub.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=\ngithub.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=\ngithub.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=\ngithub.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=\ngithub.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=\ngithub.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=\ngithub.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=\ngithub.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=\ngithub.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\n"
  },
  {
    "path": "gin_demo/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/gin-gonic/gin\"\n\t\"net/http\"\n)\n\nfunc func1(c *gin.Context){\n\tfmt.Println(\"func1\")\n}\nfunc func2(c *gin.Context){\n\tfmt.Println(\"func2 before\")\n\tc.Next()\n\tfmt.Println(\"func2 after\")\n}\nfunc func3(c *gin.Context){\n\tfmt.Println(\"func3\")\n\t//c.Abort()\n}\nfunc func4(c *gin.Context){\n\tfmt.Println(\"func4\")\n\tc.Set(\"name\", \"q1mi\")\n}\nfunc func5(c *gin.Context){\n\tfmt.Println(\"func5\")\n\tv, ok := c.Get(\"name\")\n\tif ok{\n\t\tvStr := v.(string)  // 类型转换\n\t\tfmt.Println(vStr)\n\t}\n}\n\n\nfunc main() {\n\tr := gin.Default()\n\n\tr.GET(\"/hello\", func(c *gin.Context) {\n\t\tc.String(http.StatusOK, \"ok\")\n\t})\n\n\n\tshopGroup := r.Group(\"/shop\", func1, func2)\n\tshopGroup.Use(func3)\n\t{\n\t\tshopGroup.GET(\"/index\", func4, func5)\n\t}\n\n\tr.Run()\n}\n"
  },
  {
    "path": "json_demo/go.mod",
    "content": "module json_demo\n\ngo 1.14\n\nrequire github.com/gin-gonic/gin v1.6.3\n"
  },
  {
    "path": "json_demo/go.sum",
    "content": "github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=\ngithub.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=\ngithub.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=\ngithub.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=\ngithub.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=\ngithub.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=\ngithub.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=\ngithub.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=\ngithub.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=\ngithub.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=\ngithub.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=\ngithub.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=\ngithub.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=\ngithub.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=\ngithub.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=\ngithub.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=\ngithub.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=\ngithub.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\n"
  },
  {
    "path": "json_demo/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>json demo</title>\n</head>\n<body>\n    <h1>json demo</h1>\n    <script src=\"https://unpkg.com/axios/dist/axios.min.js\"></script>\n    <script>\n        axios.get('/data')\n            .then(function (response) {\n                console.log(response.data);\n            })\n            .catch(function (error) {\n                console.log(error);\n            });\n    </script>\n</body>\n</html>"
  },
  {
    "path": "json_demo/json_demo.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"math\"\n)\n\ntype MyData struct {\n\tID   int64  `json:\"id,string\"`\n\tName string `json:\"name\"`\n}\n\n//func (d *MyData) Unmarshal() {\n//\n//}\n//\n//func (d *MyData) Marshal() {\n//\n//}\n\n// 第一层：\n// 第二层：\n// 第五层：\n// 第九层：\n\nfunc main() {\n\t// 序列化： 后端的数据->JSON格式的数据\n\td1 := MyData{\n\t\tID:   math.MaxInt64,\n\t\tName: \"七米\",\n\t}\n\t// json序列化\n\tb, err := json.Marshal(d1)\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\tfmt.Println(string(b))\n\n\t// 反序列化：JSON格式的数据 -> Go语言中的数据\n\ts := `{\"id\":\"9223372036854775807\",\"name\":\"七米\"}`\n\tvar d2 MyData\n\tif err := json.Unmarshal([]byte(s), &d2); err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\tfmt.Printf(\"%#v type:%T\\n\", d2, d2.ID)\n}\n"
  },
  {
    "path": "json_demo/main.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// ???\n// 在后端使用的时候还是用int64\n// 进行json序列化与反序列化的时候就使用字符串\n\ntype Data struct {\n\tID int64 `json:\"id\"`\n}\n\nfunc main() {\n\tr := gin.Default()\n\tr.LoadHTMLFiles(\"index.html\")\n\n\tr.GET(\"/index\", func(c *gin.Context) {\n\t\tc.HTML(http.StatusOK, \"index.html\", nil)\n\t})\n\n\tr.GET(\"/data\", func(c *gin.Context) {\n\t\t// int64\n\t\t// math.MaxInt64  --> 1<<63 -1\n\t\t// math.MinInt64  --> -1 << 63\n\t\t//\t\t\t\t\t\t       1234567891234568000\n\t\tc.JSON(http.StatusOK, Data{1234567891234567889})\n\t})\n\n\tr.Run(\":8990\")\n}\n"
  },
  {
    "path": "lesson23/bluebell/.air.conf",
    "content": "# [Air](https://github.com/cosmtrek/air) TOML 格式的配置文件\n\n# 工作目录\n# 使用 . 或绝对路径，请注意 `tmp_dir` 目录必须在 `root` 目录下\nroot = \".\"\ntmp_dir = \"tmp\"\n\n[build]\n# 只需要写你平常编译使用的shell命令。你也可以使用 `make`\ncmd = \"go build -o ./tmp/main\"\n# 由`cmd`命令得到的二进制文件名\nbin = \"tmp/main\"\n# 自定义的二进制，可以添加额外的编译标识例如添加 GIN_MODE=release\nfull_bin = \"./tmp/main ./conf/config.yaml\"\n# 监听以下文件扩展名的文件.\ninclude_ext = [\"go\", \"tpl\", \"tmpl\", \"html\", \"yaml\"]\n# 忽略这些文件扩展名或目录\nexclude_dir = [\"assets\", \"tmp\", \"vendor\", \"frontend/node_modules\"]\n# 监听以下指定目录的文件\ninclude_dir = []\n# 排除以下文件\nexclude_file = []\n# 如果文件更改过于频繁，则没有必要在每次更改时都触发构建。可以设置触发构建的延迟时间\ndelay = 1000 # ms\n# 发生构建错误时，停止运行旧的二进制文件。\nstop_on_error = true\n# air的日志文件名，该日志文件放置在你的`tmp_dir`中\nlog = \"air_errors.log\"\n\n[log]\n# 显示日志时间\ntime = true\n\n[color]\n# 自定义每个部分显示的颜色。如果找不到颜色，使用原始的应用程序日志。\nmain = \"magenta\"\nwatcher = \"cyan\"\nbuild = \"yellow\"\nrunner = \"green\"\n\n[misc]\n# 退出时删除tmp目录\nclean_on_exit = true"
  },
  {
    "path": "lesson23/bluebell/Makefile",
    "content": ".PHONY: all build run gotool clean help\n\nBINARY=\"xx\"\n\nall: gotool build\n\nbuild:\n\tCGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ${BINARY}\n\nrun:\n\t@go run ./main.go conf/config.yaml\n\ngotool:\n\tgo fmt ./\n\tgo vet ./\n\nclean:\n\t@if [ -f ${BINARY} ] ; then rm ${BINARY} ; fi\n\nhelp:\n\t@echo \"make - 格式化 Go 代码, 并编译生成二进制文件\"\n\t@echo \"make build - 编译 Go 代码, 生成二进制文件\"\n\t@echo \"make run - 直接运行 Go 代码\"\n\t@echo \"make clean - 移除二进制文件和 vim swap files\"\n\t@echo \"make gotool - 运行 Go 工具 'fmt' and 'vet'\"\n"
  },
  {
    "path": "lesson23/bluebell/conf/config.yaml",
    "content": "name: \"bluebell\"\nmode: \"dev\"\nport: 8084\nversion: \"v0.0.1\"\nstart_time: \"2020-07-01\"\nmachine_id: 1\n\nauth:\n  jwt_expire: 8760\n\nlog:\n  level: \"debug\"\n  filename: \"web_app.log\"\n  max_size: 200\n  max_age: 30\n  max_backups: 7\nmysql:\n  host: \"127.0.0.1\"\n  port: 13306\n  user: \"root\"\n  password: \"root1234\"\n  dbname: \"bluebell\"\n  max_open_conns: 200\n  max_idle_conns: 50\nredis:\n  host: \"127.0.0.1\"\n  port: 16379\n  password: \"\"\n  db: 0\n  pool_size: 100"
  },
  {
    "path": "lesson23/bluebell/controller/code.go",
    "content": "package controller\n\ntype ResCode int64\n\nconst (\n\tCodeSuccess ResCode = 1000 + iota\n\tCodeInvalidParam\n\tCodeUserExist\n\tCodeUserNotExist\n\tCodeInvalidPassword\n\tCodeServerBusy\n\n\tCodeNeedLogin\n\tCodeInvalidToken\n)\n\nvar codeMsgMap = map[ResCode]string{\n\tCodeSuccess:         \"success\",\n\tCodeInvalidParam:    \"请求参数错误\",\n\tCodeUserExist:       \"用户名已存在\",\n\tCodeUserNotExist:    \"用户名不存在\",\n\tCodeInvalidPassword: \"用户名或密码错误\",\n\tCodeServerBusy:      \"服务繁忙\",\n\n\tCodeNeedLogin:    \"需要登录\",\n\tCodeInvalidToken: \"无效的token\",\n}\n\nfunc (c ResCode) Msg() string {\n\tmsg, ok := codeMsgMap[c]\n\tif !ok {\n\t\tmsg = codeMsgMap[CodeServerBusy]\n\t}\n\treturn msg\n}\n"
  },
  {
    "path": "lesson23/bluebell/controller/community.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/logic\"\n\t\"strconv\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"go.uber.org/zap\"\n)\n\n// ---- 跟社区相关的 ----\n\nfunc CommunityHandler(c *gin.Context) {\n\t// 查询到所有的社区（community_id, community_name) 以列表的形式返回\n\tdata, err := logic.GetCommunityList()\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetCommunityList() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy) // 不轻易把服务端报错暴露给外面\n\t\treturn\n\t}\n\tResponseSuccess(c, data)\n}\n\n// CommunityDetailHandler 社区分类详情\nfunc CommunityDetailHandler(c *gin.Context) {\n\t// 1. 获取社区id\n\tidStr := c.Param(\"id\") // 获取URL参数\n\tid, err := strconv.ParseInt(idStr, 10, 64)\n\tif err != nil {\n\t\tResponseError(c, CodeInvalidParam)\n\t\treturn\n\t}\n\n\t// 2. 根据id获取社区详情\n\tdata, err := logic.GetCommunityDetail(id)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetCommunityList() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy) // 不轻易把服务端报错暴露给外面\n\t\treturn\n\t}\n\tResponseSuccess(c, data)\n}\n"
  },
  {
    "path": "lesson23/bluebell/controller/post.go",
    "content": "package controller\n\nimport \"github.com/gin-gonic/gin\"\n\nfunc CreatePostHandler(c *gin.Context) {\n\t// 1. 获取参数及参数的校验\n\n\t//c.ShouldBindJSON()\n\n\t// 2. 创建帖子\n\n\t// 3. 返回响应\n}\n"
  },
  {
    "path": "lesson23/bluebell/controller/request.go",
    "content": "package controller\n\nimport (\n\t\"errors\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\nconst CtxUserIDKey = \"userID\"\n\nvar ErrorUserNotLogin = errors.New(\"用户未登录\")\n\n// getCurrentUser 获取当前登录的用户ID\nfunc getCurrentUser(c *gin.Context) (userID int64, err error) {\n\tuid, ok := c.Get(CtxUserIDKey)\n\tif !ok {\n\t\terr = ErrorUserNotLogin\n\t\treturn\n\t}\n\tuserID, ok = uid.(int64)\n\tif !ok {\n\t\terr = ErrorUserNotLogin\n\t\treturn\n\t}\n\treturn\n}\n"
  },
  {
    "path": "lesson23/bluebell/controller/response.go",
    "content": "package controller\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n/*\n{\n\t\"code\": 10000, // 程序中的错误码\n\t\"msg\": xx,     // 提示信息\n\t\"data\": {},    // 数据\n}\n\n*/\n\ntype ResponseData struct {\n\tCode ResCode     `json:\"code\"`\n\tMsg  interface{} `json:\"msg\"`\n\tData interface{} `json:\"data\"`\n}\n\nfunc ResponseError(c *gin.Context, code ResCode) {\n\tc.JSON(http.StatusOK, &ResponseData{\n\t\tCode: code,\n\t\tMsg:  code.Msg(),\n\t\tData: nil,\n\t})\n}\n\nfunc ResponseErrorWithMsg(c *gin.Context, code ResCode, msg interface{}) {\n\tc.JSON(http.StatusOK, &ResponseData{\n\t\tCode: code,\n\t\tMsg:  msg,\n\t\tData: nil,\n\t})\n}\n\nfunc ResponseSuccess(c *gin.Context, data interface{}) {\n\tc.JSON(http.StatusOK, &ResponseData{\n\t\tCode: CodeSuccess,\n\t\tMsg:  CodeSuccess.Msg(),\n\t\tData: data,\n\t})\n}\n"
  },
  {
    "path": "lesson23/bluebell/controller/user.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/logic\"\n\t\"bluebell/models\"\n\t\"errors\"\n\n\t\"github.com/go-playground/validator/v10\"\n\n\t\"go.uber.org/zap\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// SignUpHandler 处理注册请求的函数\nfunc SignUpHandler(c *gin.Context) {\n\t// 1. 获取参数和参数校验\n\tp := new(models.ParamSignUp)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\t// 请求参数有误，直接返回响应\n\t\tzap.L().Error(\"SignUp with invalid param\", zap.Error(err))\n\t\t// 判断err是不是validator.ValidationErrors 类型\n\t\terrs, ok := err.(validator.ValidationErrors)\n\t\tif !ok {\n\t\t\tResponseError(c, CodeInvalidParam)\n\t\t\treturn\n\t\t}\n\t\tResponseErrorWithMsg(c, CodeInvalidParam, removeTopStruct(errs.Translate(trans)))\n\t\treturn\n\t}\n\t// 2. 业务处理\n\tif err := logic.SignUp(p); err != nil {\n\t\tzap.L().Error(\"logic.SignUp failed\", zap.Error(err))\n\t\tif errors.Is(err, mysql.ErrorUserExist) {\n\t\t\tResponseError(c, CodeUserExist)\n\t\t\treturn\n\t\t}\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\t// 3. 返回响应\n\tResponseSuccess(c, nil)\n}\n\nfunc LoginHandler(c *gin.Context) {\n\t// 1.获取请求参数及参数校验\n\tp := new(models.ParamLogin)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\t// 请求参数有误，直接返回响应\n\t\tzap.L().Error(\"Login with invalid param\", zap.Error(err))\n\t\t// 判断err是不是validator.ValidationErrors 类型\n\t\terrs, ok := err.(validator.ValidationErrors)\n\t\tif !ok {\n\t\t\tResponseError(c, CodeInvalidParam)\n\t\t\treturn\n\t\t}\n\t\tResponseErrorWithMsg(c, CodeInvalidParam, removeTopStruct(errs.Translate(trans)))\n\t\treturn\n\t}\n\t// 2.业务逻辑处理\n\ttoken, err := logic.Login(p)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.Login failed\", zap.String(\"username\", p.Username), zap.Error(err))\n\t\tif errors.Is(err, mysql.ErrorUserNotExist) {\n\t\t\tResponseError(c, CodeUserNotExist)\n\t\t\treturn\n\t\t}\n\t\tResponseError(c, CodeInvalidPassword)\n\t\treturn\n\t}\n\n\t// 3.返回响应\n\tResponseSuccess(c, token)\n}\n"
  },
  {
    "path": "lesson23/bluebell/controller/validator.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/models\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin/binding\"\n\t\"github.com/go-playground/locales/en\"\n\t\"github.com/go-playground/locales/zh\"\n\tut \"github.com/go-playground/universal-translator\"\n\t\"github.com/go-playground/validator/v10\"\n\tenTranslations \"github.com/go-playground/validator/v10/translations/en\"\n\tzhTranslations \"github.com/go-playground/validator/v10/translations/zh\"\n)\n\n// 定义一个全局翻译器T\nvar trans ut.Translator\n\n// InitTrans 初始化翻译器\nfunc InitTrans(locale string) (err error) {\n\t// 修改gin框架中的Validator引擎属性，实现自定制\n\tif v, ok := binding.Validator.Engine().(*validator.Validate); ok {\n\n\t\t// 注册一个获取json tag的自定义方法\n\t\tv.RegisterTagNameFunc(func(fld reflect.StructField) string {\n\t\t\tname := strings.SplitN(fld.Tag.Get(\"json\"), \",\", 2)[0]\n\t\t\tif name == \"-\" {\n\t\t\t\treturn \"\"\n\t\t\t}\n\t\t\treturn name\n\t\t})\n\n\t\t// 为SignUpParam注册自定义校验方法\n\t\tv.RegisterStructValidation(SignUpParamStructLevelValidation, models.ParamSignUp{})\n\n\t\tzhT := zh.New() // 中文翻译器\n\t\tenT := en.New() // 英文翻译器\n\n\t\t// 第一个参数是备用（fallback）的语言环境\n\t\t// 后面的参数是应该支持的语言环境（支持多个）\n\t\t// uni := ut.New(zhT, zhT) 也是可以的\n\t\tuni := ut.New(enT, zhT, enT)\n\n\t\t// locale 通常取决于 http 请求头的 'Accept-Language'\n\t\tvar ok bool\n\t\t// 也可以使用 uni.FindTranslator(...) 传入多个locale进行查找\n\t\ttrans, ok = uni.GetTranslator(locale)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"uni.GetTranslator(%s) failed\", locale)\n\t\t}\n\n\t\t// 注册翻译器\n\t\tswitch locale {\n\t\tcase \"en\":\n\t\t\terr = enTranslations.RegisterDefaultTranslations(v, trans)\n\t\tcase \"zh\":\n\t\t\terr = zhTranslations.RegisterDefaultTranslations(v, trans)\n\t\tdefault:\n\t\t\terr = enTranslations.RegisterDefaultTranslations(v, trans)\n\t\t}\n\t\treturn\n\t}\n\treturn\n}\n\n// removeTopStruct 去除提示信息中的结构体名称\nfunc removeTopStruct(fields map[string]string) map[string]string {\n\tres := map[string]string{}\n\tfor field, err := range fields {\n\t\tres[field[strings.Index(field, \".\")+1:]] = err\n\t}\n\treturn res\n}\n\n// SignUpParamStructLevelValidation 自定义SignUpParam结构体校验函数\nfunc SignUpParamStructLevelValidation(sl validator.StructLevel) {\n\tsu := sl.Current().Interface().(models.ParamSignUp)\n\n\tif su.Password != su.RePassword {\n\t\t// 输出错误提示信息，最后一个参数就是传递的param\n\t\tsl.ReportError(su.RePassword, \"re_password\", \"RePassword\", \"eqfield\", \"password\")\n\t}\n}\n"
  },
  {
    "path": "lesson23/bluebell/dao/mysql/community.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/models\"\n\t\"database/sql\"\n\n\t\"go.uber.org/zap\"\n)\n\nfunc GetCommunityList() (communityList []*models.Community, err error) {\n\tsqlStr := \"select community_id, community_name from community\"\n\tif err := db.Select(&communityList, sqlStr); err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\tzap.L().Warn(\"there is no community in db\")\n\t\t\terr = nil\n\t\t}\n\t}\n\treturn\n}\n\n// GetCommunityDetailByID 根据ID查询社区详情\nfunc GetCommunityDetailByID(id int64) (community *models.CommunityDetail, err error) {\n\tcommunity = new(models.CommunityDetail)\n\tsqlStr := `select \n\t\t\tcommunity_id, community_name, introduction, create_time\n\t\t\tfrom community \n\t\t\twhere community_id = ?\n\t`\n\tif err := db.Get(community, sqlStr, id); err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\terr = ErrorInvalidID\n\t\t}\n\t}\n\treturn community, err\n}\n"
  },
  {
    "path": "lesson23/bluebell/dao/mysql/error_code.go",
    "content": "package mysql\n\nimport \"errors\"\n\nvar (\n\tErrorUserExist       = errors.New(\"用户已存在\")\n\tErrorUserNotExist    = errors.New(\"用户不存在\")\n\tErrorInvalidPassword = errors.New(\"用户名或密码错误\")\n\tErrorInvalidID       = errors.New(\"无效的ID\")\n)\n"
  },
  {
    "path": "lesson23/bluebell/dao/mysql/mysql.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/setting\"\n\t\"fmt\"\n\n\t_ \"github.com/go-sql-driver/mysql\"\n\t\"github.com/jmoiron/sqlx\"\n)\n\nvar db *sqlx.DB\n\n// Init 初始化MySQL连接\nfunc Init(cfg *setting.MySQLConfig) (err error) {\n\t// \"user:password@tcp(host:port)/dbname\"\n\tdsn := fmt.Sprintf(\"%s:%s@tcp(%s:%d)/%s?parseTime=true&loc=Local\", cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.DB)\n\tdb, err = sqlx.Connect(\"mysql\", dsn)\n\tif err != nil {\n\t\treturn\n\t}\n\tdb.SetMaxOpenConns(cfg.MaxOpenConns)\n\tdb.SetMaxIdleConns(cfg.MaxIdleConns)\n\treturn\n}\n\n// Close 关闭MySQL连接\nfunc Close() {\n\t_ = db.Close()\n}\n"
  },
  {
    "path": "lesson23/bluebell/dao/mysql/user.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/models\"\n\t\"crypto/md5\"\n\t\"database/sql\"\n\t\"encoding/hex\"\n)\n\n// 把每一步数据库操作封装成函数\n// 待logic层根据业务需求调用\n\nconst secret = \"liwenzhou.com\"\n\n// CheckUserExist 检查指定用户名的用户是否存在\nfunc CheckUserExist(username string) (err error) {\n\tsqlStr := `select count(user_id) from user where username = ?`\n\tvar count int64\n\tif err := db.Get(&count, sqlStr, username); err != nil {\n\t\treturn err\n\t}\n\tif count > 0 {\n\t\treturn ErrorUserExist\n\t}\n\treturn\n}\n\n// InsertUser 想数据库中插入一条新的用户记录\nfunc InsertUser(user *models.User) (err error) {\n\t// 对密码进行加密\n\tuser.Password = encryptPassword(user.Password)\n\t// 执行SQL语句入库\n\tsqlStr := `insert into user(user_id, username, password) values(?,?,?)`\n\t_, err = db.Exec(sqlStr, user.UserID, user.Username, user.Password)\n\treturn\n}\n\n// encryptPassword 密码加密\nfunc encryptPassword(oPassword string) string {\n\th := md5.New()\n\th.Write([]byte(secret))\n\treturn hex.EncodeToString(h.Sum([]byte(oPassword)))\n}\n\nfunc Login(user *models.User) (err error) {\n\toPassword := user.Password // 用户登录的密码\n\tsqlStr := `select user_id, username, password from user where username=?`\n\terr = db.Get(user, sqlStr, user.Username)\n\tif err == sql.ErrNoRows {\n\t\treturn ErrorUserNotExist\n\t}\n\tif err != nil {\n\t\t// 查询数据库失败\n\t\treturn err\n\t}\n\t// 判断密码是否正确\n\tpassword := encryptPassword(oPassword)\n\tif password != user.Password {\n\t\treturn ErrorInvalidPassword\n\t}\n\treturn\n}\n"
  },
  {
    "path": "lesson23/bluebell/dao/redis/redis.go",
    "content": "package redis\n\nimport (\n\t\"bluebell/setting\"\n\t\"fmt\"\n\n\t\"github.com/go-redis/redis\"\n)\n\nvar (\n\tclient *redis.Client\n\tNil    = redis.Nil\n)\n\n// Init 初始化连接\nfunc Init(cfg *setting.RedisConfig) (err error) {\n\tclient = redis.NewClient(&redis.Options{\n\t\tAddr:         fmt.Sprintf(\"%s:%d\", cfg.Host, cfg.Port),\n\t\tPassword:     cfg.Password, // no password set\n\t\tDB:           cfg.DB,       // use default DB\n\t\tPoolSize:     cfg.PoolSize,\n\t\tMinIdleConns: cfg.MinIdleConns,\n\t})\n\n\t_, err = client.Ping().Result()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc Close() {\n\t_ = client.Close()\n}\n"
  },
  {
    "path": "lesson23/bluebell/go.mod",
    "content": "module bluebell\n\ngo 1.14\n\nrequire (\n\tgithub.com/bwmarrin/snowflake v0.3.0\n\tgithub.com/dgrijalva/jwt-go v3.2.0+incompatible\n\tgithub.com/fsnotify/fsnotify v1.4.9\n\tgithub.com/gin-gonic/gin v1.6.3\n\tgithub.com/go-playground/locales v0.13.0\n\tgithub.com/go-playground/universal-translator v0.17.0\n\tgithub.com/go-playground/validator/v10 v10.2.0\n\tgithub.com/go-redis/redis v6.15.8+incompatible\n\tgithub.com/go-sql-driver/mysql v1.5.0\n\tgithub.com/jmoiron/sqlx v1.2.0\n\tgithub.com/natefinch/lumberjack v2.0.0+incompatible\n\tgithub.com/onsi/ginkgo v1.14.0 // indirect\n\tgithub.com/sony/sonyflake v1.0.0 // indirect\n\tgithub.com/spf13/viper v1.7.0\n\tgo.uber.org/zap v1.15.0\n\tgopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect\n)\n"
  },
  {
    "path": "lesson23/bluebell/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ngithub.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=\ngithub.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=\ngithub.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=\ngithub.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=\ngithub.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=\ngithub.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=\ngithub.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=\ngithub.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0=\ngithub.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=\ngithub.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=\ngithub.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=\ngithub.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=\ngithub.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=\ngithub.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=\ngithub.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=\ngithub.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=\ngithub.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=\ngithub.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=\ngithub.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=\ngithub.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=\ngithub.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=\ngithub.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=\ngithub.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=\ngithub.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=\ngithub.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=\ngithub.com/go-redis/redis v6.15.8+incompatible h1:BKZuG6mCnRj5AOaWJXoCgf6rqTYnYJLe4en2hxT7r9o=\ngithub.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=\ngithub.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=\ngithub.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=\ngithub.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=\ngithub.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=\ngithub.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=\ngithub.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=\ngithub.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=\ngithub.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=\ngithub.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=\ngithub.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=\ngithub.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=\ngithub.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=\ngithub.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=\ngithub.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=\ngithub.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=\ngithub.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=\ngithub.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=\ngithub.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=\ngithub.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=\ngithub.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=\ngithub.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=\ngithub.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=\ngithub.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=\ngithub.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=\ngithub.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=\ngithub.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=\ngithub.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=\ngithub.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=\ngithub.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=\ngithub.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=\ngithub.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=\ngithub.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=\ngithub.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=\ngithub.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=\ngithub.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=\ngithub.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=\ngithub.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=\ngithub.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=\ngithub.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=\ngithub.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=\ngithub.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=\ngithub.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=\ngithub.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=\ngithub.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=\ngithub.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=\ngithub.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=\ngithub.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=\ngithub.com/sony/sonyflake v1.0.0 h1:MpU6Ro7tfXwgn2l5eluf9xQvQJDROTBImNCfRXn/YeM=\ngithub.com/sony/sonyflake v1.0.0/go.mod h1:Jv3cfhf/UFtolOTTRd3q4Nl6ENqM+KfyZ5PseKfZGF4=\ngithub.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=\ngithub.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=\ngithub.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=\ngithub.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=\ngithub.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=\ngithub.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=\ngithub.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=\ngithub.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=\ngithub.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=\ngithub.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=\ngithub.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=\ngithub.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=\ngithub.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=\ngithub.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=\ngithub.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=\ngo.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=\ngo.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=\ngo.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=\ngo.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=\ngo.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngo.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=\ngo.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc h1:NCy3Ohtk6Iny5V/reW2Ktypo4zIpWBdRJ1uFMjBxdg8=\ngolang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=\ngopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=\ngopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=\ngopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\n"
  },
  {
    "path": "lesson23/bluebell/logger/logger.go",
    "content": "package logger\n\nimport (\n\t\"bluebell/setting\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"os\"\n\t\"runtime/debug\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/natefinch/lumberjack\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\nvar lg *zap.Logger\n\n// Init 初始化lg\nfunc Init(cfg *setting.LogConfig, mode string) (err error) {\n\twriteSyncer := getLogWriter(cfg.Filename, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge)\n\tencoder := getEncoder()\n\tvar l = new(zapcore.Level)\n\terr = l.UnmarshalText([]byte(cfg.Level))\n\tif err != nil {\n\t\treturn\n\t}\n\tvar core zapcore.Core\n\tif mode == \"dev\" {\n\t\t// 进入开发模式，日志输出到终端\n\t\tconsoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())\n\t\tcore = zapcore.NewTee(\n\t\t\tzapcore.NewCore(encoder, writeSyncer, l),\n\t\t\tzapcore.NewCore(consoleEncoder, zapcore.Lock(os.Stdout), zapcore.DebugLevel),\n\t\t)\n\t} else {\n\t\tcore = zapcore.NewCore(encoder, writeSyncer, l)\n\t}\n\n\tlg = zap.New(core, zap.AddCaller())\n\n\tzap.ReplaceGlobals(lg)\n\tzap.L().Info(\"init logger success\")\n\treturn\n}\n\nfunc getEncoder() zapcore.Encoder {\n\tencoderConfig := zap.NewProductionEncoderConfig()\n\tencoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder\n\tencoderConfig.TimeKey = \"time\"\n\tencoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder\n\tencoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder\n\tencoderConfig.EncodeCaller = zapcore.ShortCallerEncoder\n\treturn zapcore.NewJSONEncoder(encoderConfig)\n}\n\nfunc getLogWriter(filename string, maxSize, maxBackup, maxAge int) zapcore.WriteSyncer {\n\tlumberJackLogger := &lumberjack.Logger{\n\t\tFilename:   filename,\n\t\tMaxSize:    maxSize,\n\t\tMaxBackups: maxBackup,\n\t\tMaxAge:     maxAge,\n\t}\n\treturn zapcore.AddSync(lumberJackLogger)\n}\n\n// GinLogger 接收gin框架默认的日志\nfunc GinLogger() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tstart := time.Now()\n\t\tpath := c.Request.URL.Path\n\t\tquery := c.Request.URL.RawQuery\n\t\tc.Next()\n\n\t\tcost := time.Since(start)\n\t\tlg.Info(path,\n\t\t\tzap.Int(\"status\", c.Writer.Status()),\n\t\t\tzap.String(\"method\", c.Request.Method),\n\t\t\tzap.String(\"path\", path),\n\t\t\tzap.String(\"query\", query),\n\t\t\tzap.String(\"ip\", c.ClientIP()),\n\t\t\tzap.String(\"user-agent\", c.Request.UserAgent()),\n\t\t\tzap.String(\"errors\", c.Errors.ByType(gin.ErrorTypePrivate).String()),\n\t\t\tzap.Duration(\"cost\", cost),\n\t\t)\n\t}\n}\n\n// GinRecovery recover掉项目可能出现的panic，并使用zap记录相关日志\nfunc GinRecovery(stack bool) gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tdefer func() {\n\t\t\tif err := recover(); err != nil {\n\t\t\t\t// Check for a broken connection, as it is not really a\n\t\t\t\t// condition that warrants a panic stack trace.\n\t\t\t\tvar brokenPipe bool\n\t\t\t\tif ne, ok := err.(*net.OpError); ok {\n\t\t\t\t\tif se, ok := ne.Err.(*os.SyscallError); ok {\n\t\t\t\t\t\tif strings.Contains(strings.ToLower(se.Error()), \"broken pipe\") || strings.Contains(strings.ToLower(se.Error()), \"connection reset by peer\") {\n\t\t\t\t\t\t\tbrokenPipe = true\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\thttpRequest, _ := httputil.DumpRequest(c.Request, false)\n\t\t\t\tif brokenPipe {\n\t\t\t\t\tlg.Error(c.Request.URL.Path,\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t)\n\t\t\t\t\t// If the connection is dead, we can't write a status to it.\n\t\t\t\t\tc.Error(err.(error)) // nolint: errcheck\n\t\t\t\t\tc.Abort()\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif stack {\n\t\t\t\t\tlg.Error(\"[Recovery from panic]\",\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t\tzap.String(\"stack\", string(debug.Stack())),\n\t\t\t\t\t)\n\t\t\t\t} else {\n\t\t\t\t\tlg.Error(\"[Recovery from panic]\",\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tc.AbortWithStatus(http.StatusInternalServerError)\n\t\t\t}\n\t\t}()\n\t\tc.Next()\n\t}\n}\n"
  },
  {
    "path": "lesson23/bluebell/logic/community.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/models\"\n)\n\nfunc GetCommunityList() ([]*models.Community, error) {\n\t// 查数据库 查找到所有的community 并返回\n\treturn mysql.GetCommunityList()\n}\n\nfunc GetCommunityDetail(id int64) (*models.CommunityDetail, error) {\n\treturn mysql.GetCommunityDetailByID(id)\n}\n"
  },
  {
    "path": "lesson23/bluebell/logic/user.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/models\"\n\t\"bluebell/pkg/jwt\"\n\t\"bluebell/pkg/snowflake\"\n)\n\n// 存放业务逻辑的代码\n\nfunc SignUp(p *models.ParamSignUp) (err error) {\n\t// 1.判断用户存不存在\n\tif err := mysql.CheckUserExist(p.Username); err != nil {\n\t\treturn err\n\t}\n\t// 2.生成UID\n\tuserID := snowflake.GenID()\n\t// 构造一个User实例\n\tuser := &models.User{\n\t\tUserID:   userID,\n\t\tUsername: p.Username,\n\t\tPassword: p.Password,\n\t}\n\t// 3.保存进数据库\n\treturn mysql.InsertUser(user)\n}\n\nfunc Login(p *models.ParamLogin) (token string, err error) {\n\tuser := &models.User{\n\t\tUsername: p.Username,\n\t\tPassword: p.Password,\n\t}\n\t// 传递的是指针，就能拿到user.UserID\n\tif err := mysql.Login(user); err != nil {\n\t\treturn \"\", err\n\t}\n\t// 生成JWT\n\treturn jwt.GenToken(user.UserID, user.Username)\n}\n"
  },
  {
    "path": "lesson23/bluebell/main.go",
    "content": "package main\n\nimport (\n\t\"bluebell/controller\"\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/dao/redis\"\n\t\"bluebell/logger\"\n\t\"bluebell/pkg/snowflake\"\n\t\"bluebell/router\"\n\t\"bluebell/setting\"\n\t\"fmt\"\n\t\"os\"\n)\n\nfunc main() {\n\tif len(os.Args) < 2 {\n\t\tfmt.Println(\"need config file.eg: bluebell config.yaml\")\n\t\treturn\n\t}\n\t// 加载配置\n\tif err := setting.Init(os.Args[1]); err != nil {\n\t\tfmt.Printf(\"load config failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tif err := logger.Init(setting.Conf.LogConfig, setting.Conf.Mode); err != nil {\n\t\tfmt.Printf(\"init logger failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tif err := mysql.Init(setting.Conf.MySQLConfig); err != nil {\n\t\tfmt.Printf(\"init mysql failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer mysql.Close() // 程序退出关闭数据库连接\n\tif err := redis.Init(setting.Conf.RedisConfig); err != nil {\n\t\tfmt.Printf(\"init redis failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer redis.Close()\n\n\tif err := snowflake.Init(setting.Conf.StartTime, setting.Conf.MachineID); err != nil {\n\t\tfmt.Printf(\"init snowflake failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\t// 初始化gin框架内置的校验器使用的翻译器\n\tif err := controller.InitTrans(\"zh\"); err != nil {\n\t\tfmt.Printf(\"init validator trans failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\t// 注册路由\n\tr := router.SetupRouter(setting.Conf.Mode)\n\terr := r.Run(fmt.Sprintf(\":%d\", setting.Conf.Port))\n\tif err != nil {\n\t\tfmt.Printf(\"run server failed, err:%v\\n\", err)\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "lesson23/bluebell/middlewares/auth.go",
    "content": "package middlewares\n\nimport (\n\t\"bluebell/controller\"\n\t\"bluebell/pkg/jwt\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// JWTAuthMiddleware 基于JWT的认证中间件\nfunc JWTAuthMiddleware() func(c *gin.Context) {\n\treturn func(c *gin.Context) {\n\t\t// 客户端携带Token有三种方式 1.放在请求头 2.放在请求体 3.放在URI\n\t\t// 这里假设Token放在Header的Authorization中，并使用Bearer开头\n\t\t// Authorization: Bearer xxxxxxx.xxx.xxx  / X-TOKEN: xxx.xxx.xx\n\t\t// 这里的具体实现方式要依据你的实际业务情况决定\n\t\tauthHeader := c.Request.Header.Get(\"Authorization\")\n\t\tif authHeader == \"\" {\n\t\t\tcontroller.ResponseError(c, controller.CodeNeedLogin)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// 按空格分割\n\t\tparts := strings.SplitN(authHeader, \" \", 2)\n\t\tif !(len(parts) == 2 && parts[0] == \"Bearer\") {\n\t\t\tcontroller.ResponseError(c, controller.CodeInvalidToken)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// parts[1]是获取到的tokenString，我们使用之前定义好的解析JWT的函数来解析它\n\t\tmc, err := jwt.ParseToken(parts[1])\n\t\tif err != nil {\n\t\t\tcontroller.ResponseError(c, controller.CodeInvalidToken)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// 将当前请求的userID信息保存到请求的上下文c上\n\t\tc.Set(controller.CtxUserIDKey, mc.UserID)\n\n\t\tc.Next() // 后续的处理请求的函数中 可以用过c.Get(CtxUserIDKey) 来获取当前请求的用户信息\n\t}\n}\n"
  },
  {
    "path": "lesson23/bluebell/models/community.go",
    "content": "package models\n\nimport \"time\"\n\ntype Community struct {\n\tID   int64  `json:\"id\" db:\"community_id\"`\n\tName string `json:\"name\" db:\"community_name\"`\n}\n\ntype CommunityDetail struct {\n\tID           int64     `json:\"id\" db:\"community_id\"`\n\tName         string    `json:\"name\" db:\"community_name\"`\n\tIntroduction string    `json:\"introduction,omitempty\" db:\"introduction\"`\n\tCreateTime   time.Time `json:\"create_time\" db:\"create_time\"`\n}\n"
  },
  {
    "path": "lesson23/bluebell/models/create_table.sql",
    "content": "--\nCREATE TABLE `user` (\n    `id` bigint(20) NOT NULL AUTO_INCREMENT,\n    `user_id` bigint(20) NOT NULL,\n    `username` varchar(64) COLLATE utf8mb4_general_ci NOT NULL,\n    `password` varchar(64) COLLATE utf8mb4_general_ci NOT NULL,\n    `email` varchar(64) COLLATE utf8mb4_general_ci,\n    `gender` tinyint(4) NOT NULL DEFAULT '0',\n    `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n    `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    PRIMARY KEY (`id`),\n    UNIQUE KEY `idx_username` (`username`) USING BTREE,\n    UNIQUE KEY `idx_user_id` (`user_id`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\n\n\nDROP TABLE IF EXISTS `community`;\nCREATE TABLE `community` (\n     `id` int(11) NOT NULL AUTO_INCREMENT,\n     `community_id` int(10) unsigned NOT NULL,\n     `community_name` varchar(128) COLLATE utf8mb4_general_ci NOT NULL,\n     `introduction` varchar(256) COLLATE utf8mb4_general_ci NOT NULL,\n     `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n     `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n     PRIMARY KEY (`id`),\n     UNIQUE KEY `idx_community_id` (`community_id`),\n     UNIQUE KEY `idx_community_name` (`community_name`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\n\n\nINSERT INTO `community` VALUES ('1', '1', 'Go', 'Golang', '2016-11-01 08:10:10', '2016-11-01 08:10:10');\nINSERT INTO `community` VALUES ('2', '2', 'leetcode', '刷题刷题刷题', '2020-01-01 08:00:00', '2020-01-01 08:00:00');\nINSERT INTO `community` VALUES ('3', '3', 'CS:GO', 'Rush B。。。', '2018-08-07 08:30:00', '2018-08-07 08:30:00');\nINSERT INTO `community` VALUES ('4', '4', 'LOL', '欢迎来到英雄联盟!', '2016-01-01 08:00:00', '2016-01-01 08:00:00');\n\nDROP TABLE IF EXISTS `post`;\nCREATE TABLE `post` (\n    `id` bigint(20) NOT NULL AUTO_INCREMENT,\n    `post_id` bigint(20) NOT NULL COMMENT '帖子id',\n    `title` varchar(128) COLLATE utf8mb4_general_ci NOT NULL COMMENT '标题',\n    `content` varchar(8192) COLLATE utf8mb4_general_ci NOT NULL COMMENT '内容',\n    `author_id` bigint(20) NOT NULL COMMENT '作者的用户id',\n    `community_id` bigint(20) NOT NULL COMMENT '所属社区',\n    `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '帖子状态',\n    `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n    `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n    PRIMARY KEY (`id`),\n    UNIQUE KEY `idx_post_id` (`post_id`),\n    KEY `idx_author_id` (`author_id`),\n    KEY `idx_community_id` (`community_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;"
  },
  {
    "path": "lesson23/bluebell/models/params.go",
    "content": "package models\n\n// 定义请求的参数结构体\n\n// ParamSignUp 注册请求参数\ntype ParamSignUp struct {\n\tUsername   string `json:\"username\" binding:\"required\"`\n\tPassword   string `json:\"password\" binding:\"required\"`\n\tRePassword string `json:\"re_password\" binding:\"required,eqfield=Password\"`\n}\n\n// ParamLogin 登录请求参数\ntype ParamLogin struct {\n\tUsername string `json:\"username\" binding:\"required\"`\n\tPassword string `json:\"password\" binding:\"required\"`\n}\n"
  },
  {
    "path": "lesson23/bluebell/models/post.go",
    "content": "package models\n\nimport \"time\"\n\n// 内存对齐概念\n\ntype Post struct {\n\tID          int64     `json:\"id\" db:\"post_id\"`\n\tAuthorID    int64     `json:\"author_id\" db:\"author_id\"`\n\tCommunityID int64     `json:\"community_id\" db:\"community_id\"`\n\tStatus      int32     `json:\"status\" db:\"status\"`\n\tTitle       string    `json:\"title\" db:\"title\"`\n\tContent     string    `json:\"content\" db:\"content\"`\n\tCreateTime  time.Time `json:\"create_time\" db:\"create_time\"`\n}\n"
  },
  {
    "path": "lesson23/bluebell/models/struct_test.go",
    "content": "package models\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"unsafe\"\n)\n\n// Go 内存对齐\n\ntype s1 struct {\n\ta int8\n\tb string\n\tc int8\n}\n\ntype s2 struct {\n\ta int8\n\tc int8\n\tb string\n}\n\nfunc TestStruct(t *testing.T) {\n\tv1 := s1{\n\t\ta: 1,\n\t\tb: \"七米\",\n\t\tc: 2,\n\t}\n\n\tv2 := s2{\n\t\ta: 1,\n\t\tb: \"七米\",\n\t\tc: 2,\n\t}\n\n\tfmt.Println(unsafe.Sizeof(v1), unsafe.Sizeof(v2))\n}\n"
  },
  {
    "path": "lesson23/bluebell/models/user.go",
    "content": "package models\n\ntype User struct {\n\tUserID   int64  `db:\"user_id\"`\n\tUsername string `db:\"username\"`\n\tPassword string `db:\"password\"`\n}\n"
  },
  {
    "path": "lesson23/bluebell/pkg/jwt/jwt.go",
    "content": "package jwt\n\nimport (\n\t\"errors\"\n\t\"time\"\n\n\t\"github.com/spf13/viper\"\n\n\t\"github.com/dgrijalva/jwt-go\"\n)\n\nvar mySecret = []byte(\"夏天夏天悄悄过去\")\n\n// MyClaims 自定义声明结构体并内嵌jwt.StandardClaims\n// jwt包自带的jwt.StandardClaims只包含了官方字段\n// 我们这里需要额外记录一个username字段，所以要自定义结构体\n// 如果想要保存更多信息，都可以添加到这个结构体中\ntype MyClaims struct {\n\tUserID   int64  `json:\"user_id\"`\n\tUsername string `json:\"username\"`\n\tjwt.StandardClaims\n}\n\n// GenToken 生成JWT\nfunc GenToken(userID int64, username string) (string, error) {\n\t// 创建一个我们自己的声明的数据\n\tc := MyClaims{\n\t\tuserID,\n\t\t\"username\", // 自定义字段\n\t\tjwt.StandardClaims{\n\t\t\tExpiresAt: time.Now().Add(\n\t\t\t\ttime.Duration(viper.GetInt(\"auth.jwt_expire\")) * time.Hour).Unix(), // 过期时间\n\t\t\tIssuer: \"bluebell\", // 签发人\n\t\t},\n\t}\n\t// 使用指定的签名方法创建签名对象\n\ttoken := jwt.NewWithClaims(jwt.SigningMethodHS256, c)\n\t// 使用指定的secret签名并获得完整的编码后的字符串token\n\treturn token.SignedString(mySecret)\n}\n\n// ParseToken 解析JWT\nfunc ParseToken(tokenString string) (*MyClaims, error) {\n\t// 解析token\n\tvar mc = new(MyClaims)\n\ttoken, err := jwt.ParseWithClaims(tokenString, mc, func(token *jwt.Token) (i interface{}, err error) {\n\t\treturn mySecret, nil\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif token.Valid { // 校验token\n\t\treturn mc, nil\n\t}\n\treturn nil, errors.New(\"invalid token\")\n}\n"
  },
  {
    "path": "lesson23/bluebell/pkg/snowflake/snowflake.go",
    "content": "package snowflake\n\nimport (\n\t\"time\"\n\n\tsf \"github.com/bwmarrin/snowflake\"\n)\n\nvar node *sf.Node\n\nfunc Init(startTime string, machineID int64) (err error) {\n\tvar st time.Time\n\tst, err = time.Parse(\"2006-01-02\", startTime)\n\tif err != nil {\n\t\treturn\n\t}\n\tsf.Epoch = st.UnixNano() / 1000000\n\tnode, err = sf.NewNode(machineID)\n\treturn\n}\nfunc GenID() int64 {\n\treturn node.Generate().Int64()\n}\n"
  },
  {
    "path": "lesson23/bluebell/router/route.go",
    "content": "package router\n\nimport (\n\t\"bluebell/controller\"\n\t\"bluebell/logger\"\n\t\"bluebell/middlewares\"\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc SetupRouter(mode string) *gin.Engine {\n\tif mode == gin.ReleaseMode {\n\t\tgin.SetMode(gin.ReleaseMode) // gin设置成发布模式\n\t}\n\tr := gin.New()\n\tr.Use(logger.GinLogger(), logger.GinRecovery(true))\n\n\tv1 := r.Group(\"/api/v1\")\n\n\t// 注册\n\tv1.POST(\"/signup\", controller.SignUpHandler)\n\t// 登录\n\tv1.POST(\"/login\", controller.LoginHandler)\n\n\tv1.Use(middlewares.JWTAuthMiddleware()) // 应用JWT认证中间件\n\n\t{\n\t\tv1.GET(\"/community\", controller.CommunityHandler)\n\t\tv1.GET(\"/community/:id\", controller.CommunityDetailHandler)\n\n\t\tv1.POST(\"/post\", controller.CreatePostHandler)\n\t}\n\n\tr.NoRoute(func(c *gin.Context) {\n\t\tc.JSON(http.StatusOK, gin.H{\n\t\t\t\"msg\": \"404\",\n\t\t})\n\t})\n\treturn r\n}\n"
  },
  {
    "path": "lesson23/bluebell/setting/setting.go",
    "content": "package setting\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\t\"github.com/spf13/viper\"\n)\n\nvar Conf = new(AppConfig)\n\ntype AppConfig struct {\n\tName      string `mapstructure:\"name\"`\n\tMode      string `mapstructure:\"mode\"`\n\tVersion   string `mapstructure:\"version\"`\n\tStartTime string `mapstructure:\"start_time\"`\n\tMachineID int64  `mapstructure:\"machine_id\"`\n\tPort      int    `mapstructure:\"port\"`\n\n\t*LogConfig   `mapstructure:\"log\"`\n\t*MySQLConfig `mapstructure:\"mysql\"`\n\t*RedisConfig `mapstructure:\"redis\"`\n}\n\ntype MySQLConfig struct {\n\tHost         string `mapstructure:\"host\"`\n\tUser         string `mapstructure:\"user\"`\n\tPassword     string `mapstructure:\"password\"`\n\tDB           string `mapstructure:\"dbname\"`\n\tPort         int    `mapstructure:\"port\"`\n\tMaxOpenConns int    `mapstructure:\"max_open_conns\"`\n\tMaxIdleConns int    `mapstructure:\"max_idle_conns\"`\n}\n\ntype RedisConfig struct {\n\tHost         string `mapstructure:\"host\"`\n\tPassword     string `mapstructure:\"password\"`\n\tPort         int    `mapstructure:\"port\"`\n\tDB           int    `mapstructure:\"db\"`\n\tPoolSize     int    `mapstructure:\"pool_size\"`\n\tMinIdleConns int    `mapstructure:\"min_idle_conns\"`\n}\n\ntype LogConfig struct {\n\tLevel      string `mapstructure:\"level\"`\n\tFilename   string `mapstructure:\"filename\"`\n\tMaxSize    int    `mapstructure:\"max_size\"`\n\tMaxAge     int    `mapstructure:\"max_age\"`\n\tMaxBackups int    `mapstructure:\"max_backups\"`\n}\n\nfunc Init(filePath string) (err error) {\n\t// 方式1：直接指定配置文件路径（相对路径或者绝对路径）\n\t// 相对路径：相对执行的可执行文件的相对路径\n\t//viper.SetConfigFile(\"./conf/config.yaml\")\n\t// 绝对路径：系统中实际的文件路径\n\t//viper.SetConfigFile(\"/Users/liwenzhou/Desktop/bluebell/conf/config.yaml\")\n\n\t// 方式2：指定配置文件名和配置文件的位置，viper自行查找可用的配置文件\n\t// 配置文件名不需要带后缀\n\t// 配置文件位置可配置多个\n\t//viper.SetConfigName(\"config\") // 指定配置文件名（不带后缀）\n\t//viper.AddConfigPath(\".\") // 指定查找配置文件的路径（这里使用相对路径）\n\t//viper.AddConfigPath(\"./conf\")      // 指定查找配置文件的路径（这里使用相对路径）\n\n\t// 基本上是配合远程配置中心使用的，告诉viper当前的数据使用什么格式去解析\n\t//viper.SetConfigType(\"json\")\n\n\tviper.SetConfigFile(filePath)\n\n\terr = viper.ReadInConfig() // 读取配置信息\n\tif err != nil {\n\t\t// 读取配置信息失败\n\t\tfmt.Printf(\"viper.ReadInConfig failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\n\t// 把读取到的配置信息反序列化到 Conf 变量中\n\tif err := viper.Unmarshal(Conf); err != nil {\n\t\tfmt.Printf(\"viper.Unmarshal failed, err:%v\\n\", err)\n\t}\n\n\tviper.WatchConfig()\n\tviper.OnConfigChange(func(in fsnotify.Event) {\n\t\tfmt.Println(\"配置文件修改了...\")\n\t\tif err := viper.Unmarshal(Conf); err != nil {\n\t\t\tfmt.Printf(\"viper.Unmarshal failed, err:%v\\n\", err)\n\t\t}\n\t})\n\treturn\n}\n"
  },
  {
    "path": "lesson28/bluebell/.air.conf",
    "content": "# [Air](https://github.com/cosmtrek/air) TOML 格式的配置文件\n\n# 工作目录\n# 使用 . 或绝对路径，请注意 `tmp_dir` 目录必须在 `root` 目录下\nroot = \".\"\ntmp_dir = \"tmp\"\n\n[build]\n# 只需要写你平常编译使用的shell命令。你也可以使用 `make`\ncmd = \"go build -o ./tmp/main\"\n# 由`cmd`命令得到的二进制文件名\nbin = \"tmp/main\"\n# 自定义的二进制，可以添加额外的编译标识例如添加 GIN_MODE=release\nfull_bin = \"./tmp/main ./conf/config.yaml\"\n# 监听以下文件扩展名的文件.\ninclude_ext = [\"go\", \"tpl\", \"tmpl\", \"html\", \"yaml\"]\n# 忽略这些文件扩展名或目录\nexclude_dir = [\"assets\", \"tmp\", \"vendor\", \"frontend/node_modules\"]\n# 监听以下指定目录的文件\ninclude_dir = []\n# 排除以下文件\nexclude_file = []\n# 如果文件更改过于频繁，则没有必要在每次更改时都触发构建。可以设置触发构建的延迟时间\ndelay = 1000 # ms\n# 发生构建错误时，停止运行旧的二进制文件。\nstop_on_error = true\n# air的日志文件名，该日志文件放置在你的`tmp_dir`中\nlog = \"air_errors.log\"\n\n[log]\n# 显示日志时间\ntime = true\n\n[color]\n# 自定义每个部分显示的颜色。如果找不到颜色，使用原始的应用程序日志。\nmain = \"magenta\"\nwatcher = \"cyan\"\nbuild = \"yellow\"\nrunner = \"green\"\n\n[misc]\n# 退出时删除tmp目录\nclean_on_exit = true"
  },
  {
    "path": "lesson28/bluebell/Makefile",
    "content": ".PHONY: all build run gotool clean help\n\nBINARY=\"xx\"\n\nall: gotool build\n\nbuild:\n\tCGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ${BINARY}\n\nrun:\n\t@go run ./main.go conf/config.yaml\n\ngotool:\n\tgo fmt ./\n\tgo vet ./\n\nclean:\n\t@if [ -f ${BINARY} ] ; then rm ${BINARY} ; fi\n\nhelp:\n\t@echo \"make - 格式化 Go 代码, 并编译生成二进制文件\"\n\t@echo \"make build - 编译 Go 代码, 生成二进制文件\"\n\t@echo \"make run - 直接运行 Go 代码\"\n\t@echo \"make clean - 移除二进制文件和 vim swap files\"\n\t@echo \"make gotool - 运行 Go 工具 'fmt' and 'vet'\"\n"
  },
  {
    "path": "lesson28/bluebell/conf/config.yaml",
    "content": "name: \"bluebell\"\nmode: \"dev\"\nport: 8084\nversion: \"v0.0.1\"\nstart_time: \"2020-07-01\"\nmachine_id: 1\n\nauth:\n  jwt_expire: 8760\n\nlog:\n  level: \"debug\"\n  filename: \"web_app.log\"\n  max_size: 200\n  max_age: 30\n  max_backups: 7\nmysql:\n  host: \"127.0.0.1\"\n  port: 13306\n  user: \"root\"\n  password: \"root1234\"\n  dbname: \"bluebell\"\n  max_open_conns: 200\n  max_idle_conns: 50\nredis:\n  host: \"127.0.0.1\"\n  port: 16379\n  password: \"\"\n  db: 0\n  pool_size: 100"
  },
  {
    "path": "lesson28/bluebell/controller/code.go",
    "content": "package controller\n\ntype ResCode int64\n\nconst (\n\tCodeSuccess ResCode = 1000 + iota\n\tCodeInvalidParam\n\tCodeUserExist\n\tCodeUserNotExist\n\tCodeInvalidPassword\n\tCodeServerBusy\n\n\tCodeNeedLogin\n\tCodeInvalidToken\n)\n\nvar codeMsgMap = map[ResCode]string{\n\tCodeSuccess:         \"success\",\n\tCodeInvalidParam:    \"请求参数错误\",\n\tCodeUserExist:       \"用户名已存在\",\n\tCodeUserNotExist:    \"用户名不存在\",\n\tCodeInvalidPassword: \"用户名或密码错误\",\n\tCodeServerBusy:      \"服务繁忙\",\n\n\tCodeNeedLogin:    \"需要登录\",\n\tCodeInvalidToken: \"无效的token\",\n}\n\nfunc (c ResCode) Msg() string {\n\tmsg, ok := codeMsgMap[c]\n\tif !ok {\n\t\tmsg = codeMsgMap[CodeServerBusy]\n\t}\n\treturn msg\n}\n"
  },
  {
    "path": "lesson28/bluebell/controller/community.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/logic\"\n\t\"strconv\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"go.uber.org/zap\"\n)\n\n// ---- 跟社区相关的 ----\n\nfunc CommunityHandler(c *gin.Context) {\n\t// 查询到所有的社区（community_id, community_name) 以列表的形式返回\n\tdata, err := logic.GetCommunityList()\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetCommunityList() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy) // 不轻易把服务端报错暴露给外面\n\t\treturn\n\t}\n\tResponseSuccess(c, data)\n}\n\n// CommunityDetailHandler 社区分类详情\nfunc CommunityDetailHandler(c *gin.Context) {\n\t// 1. 获取社区id\n\tidStr := c.Param(\"id\") // 获取URL参数\n\tid, err := strconv.ParseInt(idStr, 10, 64)\n\tif err != nil {\n\t\tResponseError(c, CodeInvalidParam)\n\t\treturn\n\t}\n\n\t// 2. 根据id获取社区详情\n\tdata, err := logic.GetCommunityDetail(id)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetCommunityList() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy) // 不轻易把服务端报错暴露给外面\n\t\treturn\n\t}\n\tResponseSuccess(c, data)\n}\n"
  },
  {
    "path": "lesson28/bluebell/controller/post.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/logic\"\n\t\"bluebell/models\"\n\t\"strconv\"\n\n\t\"go.uber.org/zap\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// CreatePostHandler 创建帖子的处理函数\nfunc CreatePostHandler(c *gin.Context) {\n\t// 1. 获取参数及参数的校验\n\t//c.ShouldBindJSON()  // validator --> binding tag\n\tp := new(models.Post)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\tzap.L().Debug(\"c.ShouldBindJSON(p) error\", zap.Any(\"err\", err))\n\t\tzap.L().Error(\"create post with invalid param\")\n\t\tResponseError(c, CodeInvalidParam)\n\t\treturn\n\t}\n\t// 从 c 取到当前发请求的用户的ID\n\tuserID, err := getCurrentUserID(c)\n\tif err != nil {\n\t\tResponseError(c, CodeNeedLogin)\n\t\treturn\n\t}\n\tp.AuthorID = userID\n\t// 2. 创建帖子\n\tif err := logic.CreatePost(p); err != nil {\n\t\tzap.L().Error(\"logic.CreatePost(p) failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\n\t// 3. 返回响应\n\tResponseSuccess(c, nil)\n}\n\n// GetPostDetailHandler 获取帖子详情的处理函数\nfunc GetPostDetailHandler(c *gin.Context) {\n\t// 1. 获取参数（从URL中获取帖子的id）\n\tpidStr := c.Param(\"id\")\n\tpid, err := strconv.ParseInt(pidStr, 10, 64)\n\tif err != nil {\n\t\tzap.L().Error(\"get post detail with invalid param\", zap.Error(err))\n\t\tResponseError(c, CodeInvalidParam)\n\t\treturn\n\t}\n\n\t// 2. 根据id取出帖子数据（查数据库）\n\tdata, err := logic.GetPostById(pid)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetPostById(pid) failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\t// 3. 返回响应\n\tResponseSuccess(c, data)\n}\n\n// GetPostListHandler 获取帖子列表的处理函数\nfunc GetPostListHandler(c *gin.Context) {\n\t// 获取分页参数\n\tpage, size := getPageInfo(c)\n\t// 获取数据\n\tdata, err := logic.GetPostList(page, size)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetPostList() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\tResponseSuccess(c, data)\n\t// 返回响应\n}\n"
  },
  {
    "path": "lesson28/bluebell/controller/request.go",
    "content": "package controller\n\nimport (\n\t\"errors\"\n\t\"strconv\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\nconst CtxUserIDKey = \"userID\"\n\nvar ErrorUserNotLogin = errors.New(\"用户未登录\")\n\n// getCurrentUserID 获取当前登录的用户ID\nfunc getCurrentUserID(c *gin.Context) (userID int64, err error) {\n\tuid, ok := c.Get(CtxUserIDKey)\n\tif !ok {\n\t\terr = ErrorUserNotLogin\n\t\treturn\n\t}\n\tuserID, ok = uid.(int64)\n\tif !ok {\n\t\terr = ErrorUserNotLogin\n\t\treturn\n\t}\n\treturn\n}\n\nfunc getPageInfo(c *gin.Context) (int64, int64) {\n\tpageStr := c.Query(\"page\")\n\tsizeStr := c.Query(\"size\")\n\n\tvar (\n\t\tpage int64\n\t\tsize int64\n\t\terr  error\n\t)\n\n\tpage, err = strconv.ParseInt(pageStr, 10, 64)\n\tif err != nil {\n\t\tpage = 1\n\t}\n\tsize, err = strconv.ParseInt(sizeStr, 10, 64)\n\tif err != nil {\n\t\tsize = 10\n\t}\n\treturn page, size\n}\n"
  },
  {
    "path": "lesson28/bluebell/controller/response.go",
    "content": "package controller\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n/*\n{\n\t\"code\": 10000, // 程序中的错误码\n\t\"msg\": xx,     // 提示信息\n\t\"data\": {},    // 数据\n}\n\n*/\n\ntype ResponseData struct {\n\tCode ResCode     `json:\"code\"`\n\tMsg  interface{} `json:\"msg\"`\n\tData interface{} `json:\"data\"`\n}\n\nfunc ResponseError(c *gin.Context, code ResCode) {\n\tc.JSON(http.StatusOK, &ResponseData{\n\t\tCode: code,\n\t\tMsg:  code.Msg(),\n\t\tData: nil,\n\t})\n}\n\nfunc ResponseErrorWithMsg(c *gin.Context, code ResCode, msg interface{}) {\n\tc.JSON(http.StatusOK, &ResponseData{\n\t\tCode: code,\n\t\tMsg:  msg,\n\t\tData: nil,\n\t})\n}\n\nfunc ResponseSuccess(c *gin.Context, data interface{}) {\n\tc.JSON(http.StatusOK, &ResponseData{\n\t\tCode: CodeSuccess,\n\t\tMsg:  CodeSuccess.Msg(),\n\t\tData: data,\n\t})\n}\n"
  },
  {
    "path": "lesson28/bluebell/controller/user.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/logic\"\n\t\"bluebell/models\"\n\t\"errors\"\n\n\t\"github.com/go-playground/validator/v10\"\n\n\t\"go.uber.org/zap\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// SignUpHandler 处理注册请求的函数\nfunc SignUpHandler(c *gin.Context) {\n\t// 1. 获取参数和参数校验\n\tp := new(models.ParamSignUp)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\t// 请求参数有误，直接返回响应\n\t\tzap.L().Error(\"SignUp with invalid param\", zap.Error(err))\n\t\t// 判断err是不是validator.ValidationErrors 类型\n\t\terrs, ok := err.(validator.ValidationErrors)\n\t\tif !ok {\n\t\t\tResponseError(c, CodeInvalidParam)\n\t\t\treturn\n\t\t}\n\t\tResponseErrorWithMsg(c, CodeInvalidParam, removeTopStruct(errs.Translate(trans)))\n\t\treturn\n\t}\n\t// 2. 业务处理\n\tif err := logic.SignUp(p); err != nil {\n\t\tzap.L().Error(\"logic.SignUp failed\", zap.Error(err))\n\t\tif errors.Is(err, mysql.ErrorUserExist) {\n\t\t\tResponseError(c, CodeUserExist)\n\t\t\treturn\n\t\t}\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\t// 3. 返回响应\n\tResponseSuccess(c, nil)\n}\n\nfunc LoginHandler(c *gin.Context) {\n\t// 1.获取请求参数及参数校验\n\tp := new(models.ParamLogin)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\t// 请求参数有误，直接返回响应\n\t\tzap.L().Error(\"Login with invalid param\", zap.Error(err))\n\t\t// 判断err是不是validator.ValidationErrors 类型\n\t\terrs, ok := err.(validator.ValidationErrors)\n\t\tif !ok {\n\t\t\tResponseError(c, CodeInvalidParam)\n\t\t\treturn\n\t\t}\n\t\tResponseErrorWithMsg(c, CodeInvalidParam, removeTopStruct(errs.Translate(trans)))\n\t\treturn\n\t}\n\t// 2.业务逻辑处理\n\ttoken, err := logic.Login(p)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.Login failed\", zap.String(\"username\", p.Username), zap.Error(err))\n\t\tif errors.Is(err, mysql.ErrorUserNotExist) {\n\t\t\tResponseError(c, CodeUserNotExist)\n\t\t\treturn\n\t\t}\n\t\tResponseError(c, CodeInvalidPassword)\n\t\treturn\n\t}\n\n\t// 3.返回响应\n\tResponseSuccess(c, token)\n}\n"
  },
  {
    "path": "lesson28/bluebell/controller/validator.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/models\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin/binding\"\n\t\"github.com/go-playground/locales/en\"\n\t\"github.com/go-playground/locales/zh\"\n\tut \"github.com/go-playground/universal-translator\"\n\t\"github.com/go-playground/validator/v10\"\n\tenTranslations \"github.com/go-playground/validator/v10/translations/en\"\n\tzhTranslations \"github.com/go-playground/validator/v10/translations/zh\"\n)\n\n// 定义一个全局翻译器T\nvar trans ut.Translator\n\n// InitTrans 初始化翻译器\nfunc InitTrans(locale string) (err error) {\n\t// 修改gin框架中的Validator引擎属性，实现自定制\n\tif v, ok := binding.Validator.Engine().(*validator.Validate); ok {\n\n\t\t// 注册一个获取json tag的自定义方法\n\t\tv.RegisterTagNameFunc(func(fld reflect.StructField) string {\n\t\t\tname := strings.SplitN(fld.Tag.Get(\"json\"), \",\", 2)[0]\n\t\t\tif name == \"-\" {\n\t\t\t\treturn \"\"\n\t\t\t}\n\t\t\treturn name\n\t\t})\n\n\t\t// 为SignUpParam注册自定义校验方法\n\t\tv.RegisterStructValidation(SignUpParamStructLevelValidation, models.ParamSignUp{})\n\n\t\tzhT := zh.New() // 中文翻译器\n\t\tenT := en.New() // 英文翻译器\n\n\t\t// 第一个参数是备用（fallback）的语言环境\n\t\t// 后面的参数是应该支持的语言环境（支持多个）\n\t\t// uni := ut.New(zhT, zhT) 也是可以的\n\t\tuni := ut.New(enT, zhT, enT)\n\n\t\t// locale 通常取决于 http 请求头的 'Accept-Language'\n\t\tvar ok bool\n\t\t// 也可以使用 uni.FindTranslator(...) 传入多个locale进行查找\n\t\ttrans, ok = uni.GetTranslator(locale)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"uni.GetTranslator(%s) failed\", locale)\n\t\t}\n\n\t\t// 注册翻译器\n\t\tswitch locale {\n\t\tcase \"en\":\n\t\t\terr = enTranslations.RegisterDefaultTranslations(v, trans)\n\t\tcase \"zh\":\n\t\t\terr = zhTranslations.RegisterDefaultTranslations(v, trans)\n\t\tdefault:\n\t\t\terr = enTranslations.RegisterDefaultTranslations(v, trans)\n\t\t}\n\t\treturn\n\t}\n\treturn\n}\n\n// removeTopStruct 去除提示信息中的结构体名称\nfunc removeTopStruct(fields map[string]string) map[string]string {\n\tres := map[string]string{}\n\tfor field, err := range fields {\n\t\tres[field[strings.Index(field, \".\")+1:]] = err\n\t}\n\treturn res\n}\n\n// SignUpParamStructLevelValidation 自定义SignUpParam结构体校验函数\nfunc SignUpParamStructLevelValidation(sl validator.StructLevel) {\n\tsu := sl.Current().Interface().(models.ParamSignUp)\n\n\tif su.Password != su.RePassword {\n\t\t// 输出错误提示信息，最后一个参数就是传递的param\n\t\tsl.ReportError(su.RePassword, \"re_password\", \"RePassword\", \"eqfield\", \"password\")\n\t}\n}\n"
  },
  {
    "path": "lesson28/bluebell/dao/mysql/community.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/models\"\n\t\"database/sql\"\n\n\t\"go.uber.org/zap\"\n)\n\nfunc GetCommunityList() (communityList []*models.Community, err error) {\n\tsqlStr := \"select community_id, community_name from community\"\n\tif err := db.Select(&communityList, sqlStr); err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\tzap.L().Warn(\"there is no community in db\")\n\t\t\terr = nil\n\t\t}\n\t}\n\treturn\n}\n\n// GetCommunityDetailByID 根据ID查询社区详情\nfunc GetCommunityDetailByID(id int64) (community *models.CommunityDetail, err error) {\n\tcommunity = new(models.CommunityDetail)\n\tsqlStr := `select \n\t\t\tcommunity_id, community_name, introduction, create_time\n\t\t\tfrom community \n\t\t\twhere community_id = ?\n\t`\n\tif err := db.Get(community, sqlStr, id); err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\terr = ErrorInvalidID\n\t\t}\n\t}\n\treturn community, err\n}\n"
  },
  {
    "path": "lesson28/bluebell/dao/mysql/error_code.go",
    "content": "package mysql\n\nimport \"errors\"\n\nvar (\n\tErrorUserExist       = errors.New(\"用户已存在\")\n\tErrorUserNotExist    = errors.New(\"用户不存在\")\n\tErrorInvalidPassword = errors.New(\"用户名或密码错误\")\n\tErrorInvalidID       = errors.New(\"无效的ID\")\n)\n"
  },
  {
    "path": "lesson28/bluebell/dao/mysql/mysql.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/setting\"\n\t\"fmt\"\n\n\t_ \"github.com/go-sql-driver/mysql\"\n\t\"github.com/jmoiron/sqlx\"\n)\n\nvar db *sqlx.DB\n\n// Init 初始化MySQL连接\nfunc Init(cfg *setting.MySQLConfig) (err error) {\n\t// \"user:password@tcp(host:port)/dbname\"\n\tdsn := fmt.Sprintf(\"%s:%s@tcp(%s:%d)/%s?parseTime=true&loc=Local\", cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.DB)\n\tdb, err = sqlx.Connect(\"mysql\", dsn)\n\tif err != nil {\n\t\treturn\n\t}\n\tdb.SetMaxOpenConns(cfg.MaxOpenConns)\n\tdb.SetMaxIdleConns(cfg.MaxIdleConns)\n\treturn\n}\n\n// Close 关闭MySQL连接\nfunc Close() {\n\t_ = db.Close()\n}\n"
  },
  {
    "path": "lesson28/bluebell/dao/mysql/post.go",
    "content": "package mysql\n\nimport \"bluebell/models\"\n\nfunc CreatePost(p *models.Post) (err error) {\n\tsqlStr := `insert into post(\n\tpost_id, title, content, author_id, community_id)\n\tvalues (?, ?, ?, ?, ?)\n\t`\n\t_, err = db.Exec(sqlStr, p.ID, p.Title, p.Content, p.AuthorID, p.CommunityID)\n\treturn\n}\n\nfunc GetPostById(pid int64) (post *models.Post, err error) {\n\tpost = new(models.Post)\n\tsqlStr := `select\n\tpost_id, title, content, author_id, community_id, create_time\n\tfrom post\n\twhere post_id = ?\n\t`\n\terr = db.Get(post, sqlStr, pid)\n\treturn\n}\n\nfunc GetPostList(page, size int64) (posts []*models.Post, err error) {\n\tsqlStr := `select \n\tpost_id, title, content, author_id, community_id, create_time\n\tfrom post\n\tlimit ?,?\n\t`\n\tposts = make([]*models.Post, 0, 2) // 不要写成make([]*models.Post, 2)\n\terr = db.Select(&posts, sqlStr, (page-1)*size, size)\n\treturn\n}\n"
  },
  {
    "path": "lesson28/bluebell/dao/mysql/user.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/models\"\n\t\"crypto/md5\"\n\t\"database/sql\"\n\t\"encoding/hex\"\n)\n\n// 把每一步数据库操作封装成函数\n// 待logic层根据业务需求调用\n\nconst secret = \"liwenzhou.com\"\n\n// CheckUserExist 检查指定用户名的用户是否存在\nfunc CheckUserExist(username string) (err error) {\n\tsqlStr := `select count(user_id) from user where username = ?`\n\tvar count int64\n\tif err := db.Get(&count, sqlStr, username); err != nil {\n\t\treturn err\n\t}\n\tif count > 0 {\n\t\treturn ErrorUserExist\n\t}\n\treturn\n}\n\n// InsertUser 想数据库中插入一条新的用户记录\nfunc InsertUser(user *models.User) (err error) {\n\t// 对密码进行加密\n\tuser.Password = encryptPassword(user.Password)\n\t// 执行SQL语句入库\n\tsqlStr := `insert into user(user_id, username, password) values(?,?,?)`\n\t_, err = db.Exec(sqlStr, user.UserID, user.Username, user.Password)\n\treturn\n}\n\n// encryptPassword 密码加密\nfunc encryptPassword(oPassword string) string {\n\th := md5.New()\n\th.Write([]byte(secret))\n\treturn hex.EncodeToString(h.Sum([]byte(oPassword)))\n}\n\nfunc Login(user *models.User) (err error) {\n\toPassword := user.Password // 用户登录的密码\n\tsqlStr := `select user_id, username, password from user where username=?`\n\terr = db.Get(user, sqlStr, user.Username)\n\tif err == sql.ErrNoRows {\n\t\treturn ErrorUserNotExist\n\t}\n\tif err != nil {\n\t\t// 查询数据库失败\n\t\treturn err\n\t}\n\t// 判断密码是否正确\n\tpassword := encryptPassword(oPassword)\n\tif password != user.Password {\n\t\treturn ErrorInvalidPassword\n\t}\n\treturn\n}\n\n// GetUserById 根据id获取用户信息\nfunc GetUserById(uid int64) (user *models.User, err error) {\n\tuser = new(models.User)\n\tsqlStr := `select user_id, username from user where user_id = ?`\n\terr = db.Get(user, sqlStr, uid)\n\treturn\n}\n"
  },
  {
    "path": "lesson28/bluebell/dao/redis/redis.go",
    "content": "package redis\n\nimport (\n\t\"bluebell/setting\"\n\t\"fmt\"\n\n\t\"github.com/go-redis/redis\"\n)\n\nvar (\n\tclient *redis.Client\n\tNil    = redis.Nil\n)\n\n// Init 初始化连接\nfunc Init(cfg *setting.RedisConfig) (err error) {\n\tclient = redis.NewClient(&redis.Options{\n\t\tAddr:         fmt.Sprintf(\"%s:%d\", cfg.Host, cfg.Port),\n\t\tPassword:     cfg.Password, // no password set\n\t\tDB:           cfg.DB,       // use default DB\n\t\tPoolSize:     cfg.PoolSize,\n\t\tMinIdleConns: cfg.MinIdleConns,\n\t})\n\n\t_, err = client.Ping().Result()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc Close() {\n\t_ = client.Close()\n}\n"
  },
  {
    "path": "lesson28/bluebell/go.mod",
    "content": "module bluebell\n\ngo 1.14\n\nrequire (\n\tgithub.com/bwmarrin/snowflake v0.3.0\n\tgithub.com/dgrijalva/jwt-go v3.2.0+incompatible\n\tgithub.com/fsnotify/fsnotify v1.4.9\n\tgithub.com/gin-gonic/gin v1.6.3\n\tgithub.com/go-playground/locales v0.13.0\n\tgithub.com/go-playground/universal-translator v0.17.0\n\tgithub.com/go-playground/validator/v10 v10.2.0\n\tgithub.com/go-redis/redis v6.15.8+incompatible\n\tgithub.com/go-sql-driver/mysql v1.5.0\n\tgithub.com/jmoiron/sqlx v1.2.0\n\tgithub.com/natefinch/lumberjack v2.0.0+incompatible\n\tgithub.com/onsi/ginkgo v1.14.0 // indirect\n\tgithub.com/sony/sonyflake v1.0.0 // indirect\n\tgithub.com/spf13/viper v1.7.0\n\tgo.uber.org/zap v1.15.0\n\tgopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect\n)\n"
  },
  {
    "path": "lesson28/bluebell/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ngithub.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=\ngithub.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=\ngithub.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=\ngithub.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=\ngithub.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=\ngithub.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=\ngithub.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=\ngithub.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0=\ngithub.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=\ngithub.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=\ngithub.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=\ngithub.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=\ngithub.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=\ngithub.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=\ngithub.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=\ngithub.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=\ngithub.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=\ngithub.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=\ngithub.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=\ngithub.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=\ngithub.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=\ngithub.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=\ngithub.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=\ngithub.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=\ngithub.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=\ngithub.com/go-redis/redis v6.15.8+incompatible h1:BKZuG6mCnRj5AOaWJXoCgf6rqTYnYJLe4en2hxT7r9o=\ngithub.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=\ngithub.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=\ngithub.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=\ngithub.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=\ngithub.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=\ngithub.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=\ngithub.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=\ngithub.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=\ngithub.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=\ngithub.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=\ngithub.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=\ngithub.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=\ngithub.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=\ngithub.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=\ngithub.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=\ngithub.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=\ngithub.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=\ngithub.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=\ngithub.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=\ngithub.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=\ngithub.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=\ngithub.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=\ngithub.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=\ngithub.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=\ngithub.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=\ngithub.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=\ngithub.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=\ngithub.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=\ngithub.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=\ngithub.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=\ngithub.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=\ngithub.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=\ngithub.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=\ngithub.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=\ngithub.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=\ngithub.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=\ngithub.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=\ngithub.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=\ngithub.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=\ngithub.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=\ngithub.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=\ngithub.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=\ngithub.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=\ngithub.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=\ngithub.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=\ngithub.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=\ngithub.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=\ngithub.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=\ngithub.com/sony/sonyflake v1.0.0 h1:MpU6Ro7tfXwgn2l5eluf9xQvQJDROTBImNCfRXn/YeM=\ngithub.com/sony/sonyflake v1.0.0/go.mod h1:Jv3cfhf/UFtolOTTRd3q4Nl6ENqM+KfyZ5PseKfZGF4=\ngithub.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=\ngithub.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=\ngithub.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=\ngithub.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=\ngithub.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=\ngithub.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=\ngithub.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=\ngithub.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=\ngithub.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=\ngithub.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=\ngithub.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=\ngithub.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=\ngithub.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=\ngithub.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=\ngithub.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=\ngo.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=\ngo.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=\ngo.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=\ngo.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=\ngo.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngo.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=\ngo.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc h1:NCy3Ohtk6Iny5V/reW2Ktypo4zIpWBdRJ1uFMjBxdg8=\ngolang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=\ngopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=\ngopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=\ngopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\n"
  },
  {
    "path": "lesson28/bluebell/logger/logger.go",
    "content": "package logger\n\nimport (\n\t\"bluebell/setting\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"os\"\n\t\"runtime/debug\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/natefinch/lumberjack\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\nvar lg *zap.Logger\n\n// Init 初始化lg\nfunc Init(cfg *setting.LogConfig, mode string) (err error) {\n\twriteSyncer := getLogWriter(cfg.Filename, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge)\n\tencoder := getEncoder()\n\tvar l = new(zapcore.Level)\n\terr = l.UnmarshalText([]byte(cfg.Level))\n\tif err != nil {\n\t\treturn\n\t}\n\tvar core zapcore.Core\n\tif mode == \"dev\" {\n\t\t// 进入开发模式，日志输出到终端\n\t\tconsoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())\n\t\tcore = zapcore.NewTee(\n\t\t\tzapcore.NewCore(encoder, writeSyncer, l),\n\t\t\tzapcore.NewCore(consoleEncoder, zapcore.Lock(os.Stdout), zapcore.DebugLevel),\n\t\t)\n\t} else {\n\t\tcore = zapcore.NewCore(encoder, writeSyncer, l)\n\t}\n\n\tlg = zap.New(core, zap.AddCaller())\n\n\tzap.ReplaceGlobals(lg)\n\tzap.L().Info(\"init logger success\")\n\treturn\n}\n\nfunc getEncoder() zapcore.Encoder {\n\tencoderConfig := zap.NewProductionEncoderConfig()\n\tencoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder\n\tencoderConfig.TimeKey = \"time\"\n\tencoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder\n\tencoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder\n\tencoderConfig.EncodeCaller = zapcore.ShortCallerEncoder\n\treturn zapcore.NewJSONEncoder(encoderConfig)\n}\n\nfunc getLogWriter(filename string, maxSize, maxBackup, maxAge int) zapcore.WriteSyncer {\n\tlumberJackLogger := &lumberjack.Logger{\n\t\tFilename:   filename,\n\t\tMaxSize:    maxSize,\n\t\tMaxBackups: maxBackup,\n\t\tMaxAge:     maxAge,\n\t}\n\treturn zapcore.AddSync(lumberJackLogger)\n}\n\n// GinLogger 接收gin框架默认的日志\nfunc GinLogger() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tstart := time.Now()\n\t\tpath := c.Request.URL.Path\n\t\tquery := c.Request.URL.RawQuery\n\t\tc.Next()\n\n\t\tcost := time.Since(start)\n\t\tlg.Info(path,\n\t\t\tzap.Int(\"status\", c.Writer.Status()),\n\t\t\tzap.String(\"method\", c.Request.Method),\n\t\t\tzap.String(\"path\", path),\n\t\t\tzap.String(\"query\", query),\n\t\t\tzap.String(\"ip\", c.ClientIP()),\n\t\t\tzap.String(\"user-agent\", c.Request.UserAgent()),\n\t\t\tzap.String(\"errors\", c.Errors.ByType(gin.ErrorTypePrivate).String()),\n\t\t\tzap.Duration(\"cost\", cost),\n\t\t)\n\t}\n}\n\n// GinRecovery recover掉项目可能出现的panic，并使用zap记录相关日志\nfunc GinRecovery(stack bool) gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tdefer func() {\n\t\t\tif err := recover(); err != nil {\n\t\t\t\t// Check for a broken connection, as it is not really a\n\t\t\t\t// condition that warrants a panic stack trace.\n\t\t\t\tvar brokenPipe bool\n\t\t\t\tif ne, ok := err.(*net.OpError); ok {\n\t\t\t\t\tif se, ok := ne.Err.(*os.SyscallError); ok {\n\t\t\t\t\t\tif strings.Contains(strings.ToLower(se.Error()), \"broken pipe\") || strings.Contains(strings.ToLower(se.Error()), \"connection reset by peer\") {\n\t\t\t\t\t\t\tbrokenPipe = true\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\thttpRequest, _ := httputil.DumpRequest(c.Request, false)\n\t\t\t\tif brokenPipe {\n\t\t\t\t\tlg.Error(c.Request.URL.Path,\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t)\n\t\t\t\t\t// If the connection is dead, we can't write a status to it.\n\t\t\t\t\tc.Error(err.(error)) // nolint: errcheck\n\t\t\t\t\tc.Abort()\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif stack {\n\t\t\t\t\tlg.Error(\"[Recovery from panic]\",\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t\tzap.String(\"stack\", string(debug.Stack())),\n\t\t\t\t\t)\n\t\t\t\t} else {\n\t\t\t\t\tlg.Error(\"[Recovery from panic]\",\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tc.AbortWithStatus(http.StatusInternalServerError)\n\t\t\t}\n\t\t}()\n\t\tc.Next()\n\t}\n}\n"
  },
  {
    "path": "lesson28/bluebell/logic/community.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/models\"\n)\n\nfunc GetCommunityList() ([]*models.Community, error) {\n\t// 查数据库 查找到所有的community 并返回\n\treturn mysql.GetCommunityList()\n}\n\nfunc GetCommunityDetail(id int64) (*models.CommunityDetail, error) {\n\treturn mysql.GetCommunityDetailByID(id)\n}\n"
  },
  {
    "path": "lesson28/bluebell/logic/post.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/models\"\n\t\"bluebell/pkg/snowflake\"\n\n\t\"go.uber.org/zap\"\n)\n\nfunc CreatePost(p *models.Post) (err error) {\n\t// 1. 生成post id\n\tp.ID = snowflake.GenID()\n\t// 2. 保存到数据库\n\treturn mysql.CreatePost(p)\n\t// 3. 返回\n}\n\n// GetPostById 根据帖子id查询帖子详情数据\nfunc GetPostById(pid int64) (data *models.ApiPostDetail, err error) {\n\t// 查询并组合我们接口想用的数据\n\tpost, err := mysql.GetPostById(pid)\n\tif err != nil {\n\t\tzap.L().Error(\"mysql.GetPostById(pid) failed\",\n\t\t\tzap.Int64(\"pid\", pid),\n\t\t\tzap.Error(err))\n\t\treturn\n\t}\n\t// 根据作者id查询作者信息\n\tuser, err := mysql.GetUserById(post.AuthorID)\n\tif err != nil {\n\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\tzap.Int64(\"author_id\", post.AuthorID),\n\t\t\tzap.Error(err))\n\t\treturn\n\t}\n\t// 根据社区id查询社区详细信息\n\tcommunity, err := mysql.GetCommunityDetailByID(post.CommunityID)\n\tif err != nil {\n\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\tzap.Int64(\"community_id\", post.CommunityID),\n\t\t\tzap.Error(err))\n\t\treturn\n\t}\n\t// 接口数据拼接\n\tdata = &models.ApiPostDetail{\n\t\tAuthorName:      user.Username,\n\t\tPost:            post,\n\t\tCommunityDetail: community,\n\t}\n\treturn\n}\n\n// GetPostList 获取帖子列表\nfunc GetPostList(page, size int64) (data []*models.ApiPostDetail, err error) {\n\tposts, err := mysql.GetPostList(page, size)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdata = make([]*models.ApiPostDetail, 0, len(posts))\n\n\tfor _, post := range posts {\n\t\t// 根据作者id查询作者信息\n\t\tuser, err := mysql.GetUserById(post.AuthorID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"author_id\", post.AuthorID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\t// 根据社区id查询社区详细信息\n\t\tcommunity, err := mysql.GetCommunityDetailByID(post.CommunityID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"community_id\", post.CommunityID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\tpostDetail := &models.ApiPostDetail{\n\t\t\tAuthorName:      user.Username,\n\t\t\tPost:            post,\n\t\t\tCommunityDetail: community,\n\t\t}\n\t\tdata = append(data, postDetail)\n\t}\n\treturn\n}\n"
  },
  {
    "path": "lesson28/bluebell/logic/user.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/models\"\n\t\"bluebell/pkg/jwt\"\n\t\"bluebell/pkg/snowflake\"\n)\n\n// 存放业务逻辑的代码\n\nfunc SignUp(p *models.ParamSignUp) (err error) {\n\t// 1.判断用户存不存在\n\tif err := mysql.CheckUserExist(p.Username); err != nil {\n\t\treturn err\n\t}\n\t// 2.生成UID\n\tuserID := snowflake.GenID()\n\t// 构造一个User实例\n\tuser := &models.User{\n\t\tUserID:   userID,\n\t\tUsername: p.Username,\n\t\tPassword: p.Password,\n\t}\n\t// 3.保存进数据库\n\treturn mysql.InsertUser(user)\n}\n\nfunc Login(p *models.ParamLogin) (token string, err error) {\n\tuser := &models.User{\n\t\tUsername: p.Username,\n\t\tPassword: p.Password,\n\t}\n\t// 传递的是指针，就能拿到user.UserID\n\tif err := mysql.Login(user); err != nil {\n\t\treturn \"\", err\n\t}\n\t// 生成JWT\n\treturn jwt.GenToken(user.UserID, user.Username)\n}\n"
  },
  {
    "path": "lesson28/bluebell/main.go",
    "content": "package main\n\nimport (\n\t\"bluebell/controller\"\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/dao/redis\"\n\t\"bluebell/logger\"\n\t\"bluebell/pkg/snowflake\"\n\t\"bluebell/router\"\n\t\"bluebell/setting\"\n\t\"fmt\"\n\t\"os\"\n)\n\nfunc main() {\n\tif len(os.Args) < 2 {\n\t\tfmt.Println(\"need config file.eg: bluebell config.yaml\")\n\t\treturn\n\t}\n\t// 加载配置\n\tif err := setting.Init(os.Args[1]); err != nil {\n\t\tfmt.Printf(\"load config failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tif err := logger.Init(setting.Conf.LogConfig, setting.Conf.Mode); err != nil {\n\t\tfmt.Printf(\"init logger failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tif err := mysql.Init(setting.Conf.MySQLConfig); err != nil {\n\t\tfmt.Printf(\"init mysql failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer mysql.Close() // 程序退出关闭数据库连接\n\tif err := redis.Init(setting.Conf.RedisConfig); err != nil {\n\t\tfmt.Printf(\"init redis failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer redis.Close()\n\n\tif err := snowflake.Init(setting.Conf.StartTime, setting.Conf.MachineID); err != nil {\n\t\tfmt.Printf(\"init snowflake failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\t// 初始化gin框架内置的校验器使用的翻译器\n\tif err := controller.InitTrans(\"zh\"); err != nil {\n\t\tfmt.Printf(\"init validator trans failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\t// 注册路由\n\tr := router.SetupRouter(setting.Conf.Mode)\n\terr := r.Run(fmt.Sprintf(\":%d\", setting.Conf.Port))\n\tif err != nil {\n\t\tfmt.Printf(\"run server failed, err:%v\\n\", err)\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "lesson28/bluebell/middlewares/auth.go",
    "content": "package middlewares\n\nimport (\n\t\"bluebell/controller\"\n\t\"bluebell/pkg/jwt\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// JWTAuthMiddleware 基于JWT的认证中间件\nfunc JWTAuthMiddleware() func(c *gin.Context) {\n\treturn func(c *gin.Context) {\n\t\t// 客户端携带Token有三种方式 1.放在请求头 2.放在请求体 3.放在URI\n\t\t// 这里假设Token放在Header的Authorization中，并使用Bearer开头\n\t\t// Authorization: Bearer xxxxxxx.xxx.xxx  / X-TOKEN: xxx.xxx.xx\n\t\t// 这里的具体实现方式要依据你的实际业务情况决定\n\t\tauthHeader := c.Request.Header.Get(\"Authorization\")\n\t\tif authHeader == \"\" {\n\t\t\tcontroller.ResponseError(c, controller.CodeNeedLogin)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// 按空格分割\n\t\tparts := strings.SplitN(authHeader, \" \", 2)\n\t\tif !(len(parts) == 2 && parts[0] == \"Bearer\") {\n\t\t\tcontroller.ResponseError(c, controller.CodeInvalidToken)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// parts[1]是获取到的tokenString，我们使用之前定义好的解析JWT的函数来解析它\n\t\tmc, err := jwt.ParseToken(parts[1])\n\t\tif err != nil {\n\t\t\tcontroller.ResponseError(c, controller.CodeInvalidToken)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// 将当前请求的userID信息保存到请求的上下文c上\n\t\tc.Set(controller.CtxUserIDKey, mc.UserID)\n\n\t\tc.Next() // 后续的处理请求的函数中 可以用过c.Get(CtxUserIDKey) 来获取当前请求的用户信息\n\t}\n}\n"
  },
  {
    "path": "lesson28/bluebell/models/community.go",
    "content": "package models\n\nimport \"time\"\n\ntype Community struct {\n\tID   int64  `json:\"id\" db:\"community_id\"`\n\tName string `json:\"name\" db:\"community_name\"`\n}\n\ntype CommunityDetail struct {\n\tID           int64     `json:\"id\" db:\"community_id\"`\n\tName         string    `json:\"name\" db:\"community_name\"`\n\tIntroduction string    `json:\"introduction,omitempty\" db:\"introduction\"`\n\tCreateTime   time.Time `json:\"create_time\" db:\"create_time\"`\n}\n"
  },
  {
    "path": "lesson28/bluebell/models/create_table.sql",
    "content": "--\nCREATE TABLE `user` (\n    `id` bigint(20) NOT NULL AUTO_INCREMENT,\n    `user_id` bigint(20) NOT NULL,\n    `username` varchar(64) COLLATE utf8mb4_general_ci NOT NULL,\n    `password` varchar(64) COLLATE utf8mb4_general_ci NOT NULL,\n    `email` varchar(64) COLLATE utf8mb4_general_ci,\n    `gender` tinyint(4) NOT NULL DEFAULT '0',\n    `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n    `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    PRIMARY KEY (`id`),\n    UNIQUE KEY `idx_username` (`username`) USING BTREE,\n    UNIQUE KEY `idx_user_id` (`user_id`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\n\n\nDROP TABLE IF EXISTS `community`;\nCREATE TABLE `community` (\n     `id` int(11) NOT NULL AUTO_INCREMENT,\n     `community_id` int(10) unsigned NOT NULL,\n     `community_name` varchar(128) COLLATE utf8mb4_general_ci NOT NULL,\n     `introduction` varchar(256) COLLATE utf8mb4_general_ci NOT NULL,\n     `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n     `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n     PRIMARY KEY (`id`),\n     UNIQUE KEY `idx_community_id` (`community_id`),\n     UNIQUE KEY `idx_community_name` (`community_name`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\n\n\nINSERT INTO `community` VALUES ('1', '1', 'Go', 'Golang', '2016-11-01 08:10:10', '2016-11-01 08:10:10');\nINSERT INTO `community` VALUES ('2', '2', 'leetcode', '刷题刷题刷题', '2020-01-01 08:00:00', '2020-01-01 08:00:00');\nINSERT INTO `community` VALUES ('3', '3', 'CS:GO', 'Rush B。。。', '2018-08-07 08:30:00', '2018-08-07 08:30:00');\nINSERT INTO `community` VALUES ('4', '4', 'LOL', '欢迎来到英雄联盟!', '2016-01-01 08:00:00', '2016-01-01 08:00:00');\n\nDROP TABLE IF EXISTS `post`;\nCREATE TABLE `post` (\n    `id` bigint(20) NOT NULL AUTO_INCREMENT,\n    `post_id` bigint(20) NOT NULL COMMENT '帖子id',\n    `title` varchar(128) COLLATE utf8mb4_general_ci NOT NULL COMMENT '标题',\n    `content` varchar(8192) COLLATE utf8mb4_general_ci NOT NULL COMMENT '内容',\n    `author_id` bigint(20) NOT NULL COMMENT '作者的用户id',\n    `community_id` bigint(20) NOT NULL COMMENT '所属社区',\n    `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '帖子状态',\n    `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n    `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n    PRIMARY KEY (`id`),\n    UNIQUE KEY `idx_post_id` (`post_id`),\n    KEY `idx_author_id` (`author_id`),\n    KEY `idx_community_id` (`community_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;"
  },
  {
    "path": "lesson28/bluebell/models/params.go",
    "content": "package models\n\n// 定义请求的参数结构体\n\n// ParamSignUp 注册请求参数\ntype ParamSignUp struct {\n\tUsername   string `json:\"username\" binding:\"required\"`\n\tPassword   string `json:\"password\" binding:\"required\"`\n\tRePassword string `json:\"re_password\" binding:\"required,eqfield=Password\"`\n}\n\n// ParamLogin 登录请求参数\ntype ParamLogin struct {\n\tUsername string `json:\"username\" binding:\"required\"`\n\tPassword string `json:\"password\" binding:\"required\"`\n}\n"
  },
  {
    "path": "lesson28/bluebell/models/post.go",
    "content": "package models\n\nimport \"time\"\n\n// 内存对齐概念\n\ntype Post struct {\n\tID          int64     `json:\"id\" db:\"post_id\"`\n\tAuthorID    int64     `json:\"author_id\" db:\"author_id\"`\n\tCommunityID int64     `json:\"community_id\" db:\"community_id\" binding:\"required\"`\n\tStatus      int32     `json:\"status\" db:\"status\"`\n\tTitle       string    `json:\"title\" db:\"title\" binding:\"required\"`\n\tContent     string    `json:\"content\" db:\"content\" binding:\"required\"`\n\tCreateTime  time.Time `json:\"create_time\" db:\"create_time\"`\n}\n\n// ApiPostDetail 帖子详情接口的结构体\ntype ApiPostDetail struct {\n\tAuthorName       string             `json:\"author_name\"`\n\t*Post                               // 嵌入帖子结构体\n\t*CommunityDetail `json:\"community\"` // 嵌入社区信息\n}\n"
  },
  {
    "path": "lesson28/bluebell/models/struct_test.go",
    "content": "package models\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"unsafe\"\n)\n\n// Go 内存对齐\n\ntype s1 struct {\n\ta int8   // 1\n\tb string // 3\n\tc int8   // 1\n}\n\ntype s2 struct {\n\ta int8\n\tc int8   // 1\n\tb string // 2\n}\n\nfunc TestStruct(t *testing.T) {\n\tv1 := s1{\n\t\ta: 1,\n\t\tb: \"七米\",\n\t\tc: 2,\n\t}\n\n\tv2 := s2{\n\t\ta: 1,\n\t\tb: \"七米\",\n\t\tc: 2,\n\t}\n\n\tfmt.Println(unsafe.Sizeof(v1), unsafe.Sizeof(v2))\n}\n"
  },
  {
    "path": "lesson28/bluebell/models/user.go",
    "content": "package models\n\ntype User struct {\n\tUserID   int64  `db:\"user_id\"`\n\tUsername string `db:\"username\"`\n\tPassword string `db:\"password\"`\n}\n"
  },
  {
    "path": "lesson28/bluebell/pkg/jwt/jwt.go",
    "content": "package jwt\n\nimport (\n\t\"errors\"\n\t\"time\"\n\n\t\"github.com/spf13/viper\"\n\n\t\"github.com/dgrijalva/jwt-go\"\n)\n\nvar mySecret = []byte(\"夏天夏天悄悄过去\")\n\n// MyClaims 自定义声明结构体并内嵌jwt.StandardClaims\n// jwt包自带的jwt.StandardClaims只包含了官方字段\n// 我们这里需要额外记录一个username字段，所以要自定义结构体\n// 如果想要保存更多信息，都可以添加到这个结构体中\ntype MyClaims struct {\n\tUserID   int64  `json:\"user_id\"`\n\tUsername string `json:\"username\"`\n\tjwt.StandardClaims\n}\n\n// GenToken 生成JWT\nfunc GenToken(userID int64, username string) (string, error) {\n\t// 创建一个我们自己的声明的数据\n\tc := MyClaims{\n\t\tuserID,\n\t\t\"username\", // 自定义字段\n\t\tjwt.StandardClaims{\n\t\t\tExpiresAt: time.Now().Add(\n\t\t\t\ttime.Duration(viper.GetInt(\"auth.jwt_expire\")) * time.Hour).Unix(), // 过期时间\n\t\t\tIssuer: \"bluebell\", // 签发人\n\t\t},\n\t}\n\t// 使用指定的签名方法创建签名对象\n\ttoken := jwt.NewWithClaims(jwt.SigningMethodHS256, c)\n\t// 使用指定的secret签名并获得完整的编码后的字符串token\n\treturn token.SignedString(mySecret)\n}\n\n// ParseToken 解析JWT\nfunc ParseToken(tokenString string) (*MyClaims, error) {\n\t// 解析token\n\tvar mc = new(MyClaims)\n\ttoken, err := jwt.ParseWithClaims(tokenString, mc, func(token *jwt.Token) (i interface{}, err error) {\n\t\treturn mySecret, nil\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif token.Valid { // 校验token\n\t\treturn mc, nil\n\t}\n\treturn nil, errors.New(\"invalid token\")\n}\n"
  },
  {
    "path": "lesson28/bluebell/pkg/snowflake/snowflake.go",
    "content": "package snowflake\n\nimport (\n\t\"time\"\n\n\tsf \"github.com/bwmarrin/snowflake\"\n)\n\nvar node *sf.Node\n\nfunc Init(startTime string, machineID int64) (err error) {\n\tvar st time.Time\n\tst, err = time.Parse(\"2006-01-02\", startTime)\n\tif err != nil {\n\t\treturn\n\t}\n\tsf.Epoch = st.UnixNano() / 1000000\n\tnode, err = sf.NewNode(machineID)\n\treturn\n}\nfunc GenID() int64 {\n\treturn node.Generate().Int64()\n}\n"
  },
  {
    "path": "lesson28/bluebell/router/route.go",
    "content": "package router\n\nimport (\n\t\"bluebell/controller\"\n\t\"bluebell/logger\"\n\t\"bluebell/middlewares\"\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc SetupRouter(mode string) *gin.Engine {\n\tif mode == gin.ReleaseMode {\n\t\tgin.SetMode(gin.ReleaseMode) // gin设置成发布模式\n\t}\n\tr := gin.New()\n\tr.Use(logger.GinLogger(), logger.GinRecovery(true))\n\n\tv1 := r.Group(\"/api/v1\")\n\n\t// 注册\n\tv1.POST(\"/signup\", controller.SignUpHandler)\n\t// 登录\n\tv1.POST(\"/login\", controller.LoginHandler)\n\n\tv1.Use(middlewares.JWTAuthMiddleware()) // 应用JWT认证中间件\n\n\t{\n\t\tv1.GET(\"/community\", controller.CommunityHandler)\n\t\tv1.GET(\"/community/:id\", controller.CommunityDetailHandler)\n\n\t\tv1.POST(\"/post\", controller.CreatePostHandler)\n\t\tv1.GET(\"/post/:id\", controller.GetPostDetailHandler)\n\t\tv1.GET(\"/posts/\", controller.GetPostListHandler)\n\t}\n\n\tr.NoRoute(func(c *gin.Context) {\n\t\tc.JSON(http.StatusOK, gin.H{\n\t\t\t\"msg\": \"404\",\n\t\t})\n\t})\n\treturn r\n}\n"
  },
  {
    "path": "lesson28/bluebell/setting/setting.go",
    "content": "package setting\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\t\"github.com/spf13/viper\"\n)\n\nvar Conf = new(AppConfig)\n\ntype AppConfig struct {\n\tName      string `mapstructure:\"name\"`\n\tMode      string `mapstructure:\"mode\"`\n\tVersion   string `mapstructure:\"version\"`\n\tStartTime string `mapstructure:\"start_time\"`\n\tMachineID int64  `mapstructure:\"machine_id\"`\n\tPort      int    `mapstructure:\"port\"`\n\n\t*LogConfig   `mapstructure:\"log\"`\n\t*MySQLConfig `mapstructure:\"mysql\"`\n\t*RedisConfig `mapstructure:\"redis\"`\n}\n\ntype MySQLConfig struct {\n\tHost         string `mapstructure:\"host\"`\n\tUser         string `mapstructure:\"user\"`\n\tPassword     string `mapstructure:\"password\"`\n\tDB           string `mapstructure:\"dbname\"`\n\tPort         int    `mapstructure:\"port\"`\n\tMaxOpenConns int    `mapstructure:\"max_open_conns\"`\n\tMaxIdleConns int    `mapstructure:\"max_idle_conns\"`\n}\n\ntype RedisConfig struct {\n\tHost         string `mapstructure:\"host\"`\n\tPassword     string `mapstructure:\"password\"`\n\tPort         int    `mapstructure:\"port\"`\n\tDB           int    `mapstructure:\"db\"`\n\tPoolSize     int    `mapstructure:\"pool_size\"`\n\tMinIdleConns int    `mapstructure:\"min_idle_conns\"`\n}\n\ntype LogConfig struct {\n\tLevel      string `mapstructure:\"level\"`\n\tFilename   string `mapstructure:\"filename\"`\n\tMaxSize    int    `mapstructure:\"max_size\"`\n\tMaxAge     int    `mapstructure:\"max_age\"`\n\tMaxBackups int    `mapstructure:\"max_backups\"`\n}\n\nfunc Init(filePath string) (err error) {\n\t// 方式1：直接指定配置文件路径（相对路径或者绝对路径）\n\t// 相对路径：相对执行的可执行文件的相对路径\n\t//viper.SetConfigFile(\"./conf/config.yaml\")\n\t// 绝对路径：系统中实际的文件路径\n\t//viper.SetConfigFile(\"/Users/liwenzhou/Desktop/bluebell/conf/config.yaml\")\n\n\t// 方式2：指定配置文件名和配置文件的位置，viper自行查找可用的配置文件\n\t// 配置文件名不需要带后缀\n\t// 配置文件位置可配置多个\n\t//viper.SetConfigName(\"config\") // 指定配置文件名（不带后缀）\n\t//viper.AddConfigPath(\".\") // 指定查找配置文件的路径（这里使用相对路径）\n\t//viper.AddConfigPath(\"./conf\")      // 指定查找配置文件的路径（这里使用相对路径）\n\n\t// 基本上是配合远程配置中心使用的，告诉viper当前的数据使用什么格式去解析\n\t//viper.SetConfigType(\"json\")\n\n\tviper.SetConfigFile(filePath)\n\n\terr = viper.ReadInConfig() // 读取配置信息\n\tif err != nil {\n\t\t// 读取配置信息失败\n\t\tfmt.Printf(\"viper.ReadInConfig failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\n\t// 把读取到的配置信息反序列化到 Conf 变量中\n\tif err := viper.Unmarshal(Conf); err != nil {\n\t\tfmt.Printf(\"viper.Unmarshal failed, err:%v\\n\", err)\n\t}\n\n\tviper.WatchConfig()\n\tviper.OnConfigChange(func(in fsnotify.Event) {\n\t\tfmt.Println(\"配置文件修改了...\")\n\t\tif err := viper.Unmarshal(Conf); err != nil {\n\t\t\tfmt.Printf(\"viper.Unmarshal failed, err:%v\\n\", err)\n\t\t}\n\t})\n\treturn\n}\n"
  },
  {
    "path": "lesson35/bluebell/.air.conf",
    "content": "# [Air](https://github.com/cosmtrek/air) TOML 格式的配置文件\n\n# 工作目录\n# 使用 . 或绝对路径，请注意 `tmp_dir` 目录必须在 `root` 目录下\nroot = \".\"\ntmp_dir = \"tmp\"\n\n[build]\n# 只需要写你平常编译使用的shell命令。你也可以使用 `make`\ncmd = \"go build -o ./tmp/main\"\n# 由`cmd`命令得到的二进制文件名\nbin = \"tmp/main\"\n# 自定义的二进制，可以添加额外的编译标识例如添加 GIN_MODE=release\nfull_bin = \"./tmp/main ./conf/config.yaml\"\n# 监听以下文件扩展名的文件.\ninclude_ext = [\"go\", \"tpl\", \"tmpl\", \"html\", \"yaml\"]\n# 忽略这些文件扩展名或目录\nexclude_dir = [\"assets\", \"tmp\", \"vendor\", \"frontend/node_modules\"]\n# 监听以下指定目录的文件\ninclude_dir = []\n# 排除以下文件\nexclude_file = []\n# 如果文件更改过于频繁，则没有必要在每次更改时都触发构建。可以设置触发构建的延迟时间\ndelay = 1000 # ms\n# 发生构建错误时，停止运行旧的二进制文件。\nstop_on_error = true\n# air的日志文件名，该日志文件放置在你的`tmp_dir`中\nlog = \"air_errors.log\"\n\n[log]\n# 显示日志时间\ntime = true\n\n[color]\n# 自定义每个部分显示的颜色。如果找不到颜色，使用原始的应用程序日志。\nmain = \"magenta\"\nwatcher = \"cyan\"\nbuild = \"yellow\"\nrunner = \"green\"\n\n[misc]\n# 退出时删除tmp目录\nclean_on_exit = true"
  },
  {
    "path": "lesson35/bluebell/Makefile",
    "content": ".PHONY: all build run gotool clean help\n\nBINARY=\"xx\"\n\nall: gotool build\n\nbuild:\n\tCGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ${BINARY}\n\nrun:\n\t@go run ./main.go conf/config.yaml\n\ngotool:\n\tgo fmt ./\n\tgo vet ./\n\nclean:\n\t@if [ -f ${BINARY} ] ; then rm ${BINARY} ; fi\n\nhelp:\n\t@echo \"make - 格式化 Go 代码, 并编译生成二进制文件\"\n\t@echo \"make build - 编译 Go 代码, 生成二进制文件\"\n\t@echo \"make run - 直接运行 Go 代码\"\n\t@echo \"make clean - 移除二进制文件和 vim swap files\"\n\t@echo \"make gotool - 运行 Go 工具 'fmt' and 'vet'\"\n"
  },
  {
    "path": "lesson35/bluebell/conf/config.yaml",
    "content": "name: \"bluebell\"\nmode: \"dev\"\nport: 8084\nversion: \"v0.0.1\"\nstart_time: \"2020-07-01\"\nmachine_id: 1\n\nauth:\n  jwt_expire: 8760\n\nlog:\n  level: \"debug\"\n  filename: \"web_app.log\"\n  max_size: 200\n  max_age: 30\n  max_backups: 7\nmysql:\n  host: \"127.0.0.1\"\n  port: 13306\n  user: \"root\"\n  password: \"root1234\"\n  dbname: \"bluebell\"\n  max_open_conns: 200\n  max_idle_conns: 50\nredis:\n  host: \"127.0.0.1\"\n  port: 16379\n  password: \"\"\n  db: 0\n  pool_size: 100"
  },
  {
    "path": "lesson35/bluebell/controller/code.go",
    "content": "package controller\n\ntype ResCode int64\n\nconst (\n\tCodeSuccess ResCode = 1000 + iota\n\tCodeInvalidParam\n\tCodeUserExist\n\tCodeUserNotExist\n\tCodeInvalidPassword\n\tCodeServerBusy\n\n\tCodeNeedLogin\n\tCodeInvalidToken\n)\n\nvar codeMsgMap = map[ResCode]string{\n\tCodeSuccess:         \"success\",\n\tCodeInvalidParam:    \"请求参数错误\",\n\tCodeUserExist:       \"用户名已存在\",\n\tCodeUserNotExist:    \"用户名不存在\",\n\tCodeInvalidPassword: \"用户名或密码错误\",\n\tCodeServerBusy:      \"服务繁忙\",\n\n\tCodeNeedLogin:    \"需要登录\",\n\tCodeInvalidToken: \"无效的token\",\n}\n\nfunc (c ResCode) Msg() string {\n\tmsg, ok := codeMsgMap[c]\n\tif !ok {\n\t\tmsg = codeMsgMap[CodeServerBusy]\n\t}\n\treturn msg\n}\n"
  },
  {
    "path": "lesson35/bluebell/controller/community.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/logic\"\n\t\"strconv\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"go.uber.org/zap\"\n)\n\n// ---- 跟社区相关的 ----\n\nfunc CommunityHandler(c *gin.Context) {\n\t// 查询到所有的社区（community_id, community_name) 以列表的形式返回\n\tdata, err := logic.GetCommunityList()\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetCommunityList() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy) // 不轻易把服务端报错暴露给外面\n\t\treturn\n\t}\n\tResponseSuccess(c, data)\n}\n\n// CommunityDetailHandler 社区分类详情\nfunc CommunityDetailHandler(c *gin.Context) {\n\t// 1. 获取社区id\n\tidStr := c.Param(\"id\") // 获取URL参数\n\tid, err := strconv.ParseInt(idStr, 10, 64)\n\tif err != nil {\n\t\tResponseError(c, CodeInvalidParam)\n\t\treturn\n\t}\n\n\t// 2. 根据id获取社区详情\n\tdata, err := logic.GetCommunityDetail(id)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetCommunityList() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy) // 不轻易把服务端报错暴露给外面\n\t\treturn\n\t}\n\tResponseSuccess(c, data)\n}\n"
  },
  {
    "path": "lesson35/bluebell/controller/post.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/logic\"\n\t\"bluebell/models\"\n\t\"strconv\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"go.uber.org/zap\"\n)\n\n// CreatePostHandler 创建帖子的处理函数\nfunc CreatePostHandler(c *gin.Context) {\n\t// 1. 获取参数及参数的校验\n\t//c.ShouldBindJSON()  // validator --> binding tag\n\tp := new(models.Post)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\tzap.L().Debug(\"c.ShouldBindJSON(p) error\", zap.Any(\"err\", err))\n\t\tzap.L().Error(\"create post with invalid param\")\n\t\tResponseError(c, CodeInvalidParam)\n\t\treturn\n\t}\n\t// 从 c 取到当前发请求的用户的ID\n\tuserID, err := getCurrentUserID(c)\n\tif err != nil {\n\t\tResponseError(c, CodeNeedLogin)\n\t\treturn\n\t}\n\tp.AuthorID = userID\n\t// 2. 创建帖子\n\tif err := logic.CreatePost(p); err != nil {\n\t\tzap.L().Error(\"logic.CreatePost(p) failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\n\t// 3. 返回响应\n\tResponseSuccess(c, nil)\n}\n\n// GetPostDetailHandler 获取帖子详情的处理函数\nfunc GetPostDetailHandler(c *gin.Context) {\n\t// 1. 获取参数（从URL中获取帖子的id）\n\tpidStr := c.Param(\"id\")\n\tpid, err := strconv.ParseInt(pidStr, 10, 64)\n\tif err != nil {\n\t\tzap.L().Error(\"get post detail with invalid param\", zap.Error(err))\n\t\tResponseError(c, CodeInvalidParam)\n\t\treturn\n\t}\n\n\t// 2. 根据id取出帖子数据（查数据库）\n\tdata, err := logic.GetPostById(pid)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetPostById(pid) failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\t// 3. 返回响应\n\tResponseSuccess(c, data)\n}\n\n// GetPostListHandler 获取帖子列表的处理函数\nfunc GetPostListHandler(c *gin.Context) {\n\t// 获取分页参数\n\tpage, size := getPageInfo(c)\n\t// 获取数据\n\tdata, err := logic.GetPostList(page, size)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetPostList() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\tResponseSuccess(c, data)\n\t// 返回响应\n}\n"
  },
  {
    "path": "lesson35/bluebell/controller/request.go",
    "content": "package controller\n\nimport (\n\t\"errors\"\n\t\"strconv\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\nconst CtxUserIDKey = \"userID\"\n\nvar ErrorUserNotLogin = errors.New(\"用户未登录\")\n\n// getCurrentUserID 获取当前登录的用户ID\nfunc getCurrentUserID(c *gin.Context) (userID int64, err error) {\n\tuid, ok := c.Get(CtxUserIDKey)\n\tif !ok {\n\t\terr = ErrorUserNotLogin\n\t\treturn\n\t}\n\tuserID, ok = uid.(int64)\n\tif !ok {\n\t\terr = ErrorUserNotLogin\n\t\treturn\n\t}\n\treturn\n}\n\nfunc getPageInfo(c *gin.Context) (int64, int64) {\n\tpageStr := c.Query(\"page\")\n\tsizeStr := c.Query(\"size\")\n\n\tvar (\n\t\tpage int64\n\t\tsize int64\n\t\terr  error\n\t)\n\n\tpage, err = strconv.ParseInt(pageStr, 10, 64)\n\tif err != nil {\n\t\tpage = 1\n\t}\n\tsize, err = strconv.ParseInt(sizeStr, 10, 64)\n\tif err != nil {\n\t\tsize = 10\n\t}\n\treturn page, size\n}\n"
  },
  {
    "path": "lesson35/bluebell/controller/response.go",
    "content": "package controller\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n/*\n{\n\t\"code\": 10000, // 程序中的错误码\n\t\"msg\": xx,     // 提示信息\n\t\"data\": {},    // 数据\n}\n\n*/\n\ntype ResponseData struct {\n\tCode ResCode     `json:\"code\"`\n\tMsg  interface{} `json:\"msg\"`\n\tData interface{} `json:\"data,omitempty\"`\n}\n\nfunc ResponseError(c *gin.Context, code ResCode) {\n\tc.JSON(http.StatusOK, &ResponseData{\n\t\tCode: code,\n\t\tMsg:  code.Msg(),\n\t\tData: nil,\n\t})\n}\n\nfunc ResponseErrorWithMsg(c *gin.Context, code ResCode, msg interface{}) {\n\tc.JSON(http.StatusOK, &ResponseData{\n\t\tCode: code,\n\t\tMsg:  msg,\n\t\tData: nil,\n\t})\n}\n\nfunc ResponseSuccess(c *gin.Context, data interface{}) {\n\tc.JSON(http.StatusOK, &ResponseData{\n\t\tCode: CodeSuccess,\n\t\tMsg:  CodeSuccess.Msg(),\n\t\tData: data,\n\t})\n}\n"
  },
  {
    "path": "lesson35/bluebell/controller/user.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/logic\"\n\t\"bluebell/models\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/go-playground/validator/v10\"\n\t\"go.uber.org/zap\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// SignUpHandler 处理注册请求的函数\nfunc SignUpHandler(c *gin.Context) {\n\t// 1. 获取参数和参数校验\n\tp := new(models.ParamSignUp)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\t// 请求参数有误，直接返回响应\n\t\tzap.L().Error(\"SignUp with invalid param\", zap.Error(err))\n\t\t// 判断err是不是validator.ValidationErrors 类型\n\t\terrs, ok := err.(validator.ValidationErrors)\n\t\tif !ok {\n\t\t\tResponseError(c, CodeInvalidParam)\n\t\t\treturn\n\t\t}\n\t\tResponseErrorWithMsg(c, CodeInvalidParam, removeTopStruct(errs.Translate(trans)))\n\t\treturn\n\t}\n\t// 2. 业务处理\n\tif err := logic.SignUp(p); err != nil {\n\t\tzap.L().Error(\"logic.SignUp failed\", zap.Error(err))\n\t\tif errors.Is(err, mysql.ErrorUserExist) {\n\t\t\tResponseError(c, CodeUserExist)\n\t\t\treturn\n\t\t}\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\t// 3. 返回响应\n\tResponseSuccess(c, nil)\n}\n\nfunc LoginHandler(c *gin.Context) {\n\t// 1.获取请求参数及参数校验\n\tp := new(models.ParamLogin)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\t// 请求参数有误，直接返回响应\n\t\tzap.L().Error(\"Login with invalid param\", zap.Error(err))\n\t\t// 判断err是不是validator.ValidationErrors 类型\n\t\terrs, ok := err.(validator.ValidationErrors)\n\t\tif !ok {\n\t\t\tResponseError(c, CodeInvalidParam)\n\t\t\treturn\n\t\t}\n\t\tResponseErrorWithMsg(c, CodeInvalidParam, removeTopStruct(errs.Translate(trans)))\n\t\treturn\n\t}\n\t// 2.业务逻辑处理\n\tuser, err := logic.Login(p)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.Login failed\", zap.String(\"username\", p.Username), zap.Error(err))\n\t\tif errors.Is(err, mysql.ErrorUserNotExist) {\n\t\t\tResponseError(c, CodeUserNotExist)\n\t\t\treturn\n\t\t}\n\t\tResponseError(c, CodeInvalidPassword)\n\t\treturn\n\t}\n\n\t// 3.返回响应\n\tResponseSuccess(c, gin.H{\n\t\t\"user_id\":   fmt.Sprintf(\"%d\", user.UserID), // id值大于1<<53-1  int64类型的最大值是1<<63-1\n\t\t\"user_name\": user.Username,\n\t\t\"token\":     user.Token,\n\t})\n}\n"
  },
  {
    "path": "lesson35/bluebell/controller/validator.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/models\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin/binding\"\n\t\"github.com/go-playground/locales/en\"\n\t\"github.com/go-playground/locales/zh\"\n\tut \"github.com/go-playground/universal-translator\"\n\t\"github.com/go-playground/validator/v10\"\n\tenTranslations \"github.com/go-playground/validator/v10/translations/en\"\n\tzhTranslations \"github.com/go-playground/validator/v10/translations/zh\"\n)\n\n// 定义一个全局翻译器T\nvar trans ut.Translator\n\n// InitTrans 初始化翻译器\nfunc InitTrans(locale string) (err error) {\n\t// 修改gin框架中的Validator引擎属性，实现自定制\n\tif v, ok := binding.Validator.Engine().(*validator.Validate); ok {\n\n\t\t// 注册一个获取json tag的自定义方法\n\t\tv.RegisterTagNameFunc(func(fld reflect.StructField) string {\n\t\t\tname := strings.SplitN(fld.Tag.Get(\"json\"), \",\", 2)[0]\n\t\t\tif name == \"-\" {\n\t\t\t\treturn \"\"\n\t\t\t}\n\t\t\treturn name\n\t\t})\n\n\t\t// 为SignUpParam注册自定义校验方法\n\t\tv.RegisterStructValidation(SignUpParamStructLevelValidation, models.ParamSignUp{})\n\n\t\tzhT := zh.New() // 中文翻译器\n\t\tenT := en.New() // 英文翻译器\n\n\t\t// 第一个参数是备用（fallback）的语言环境\n\t\t// 后面的参数是应该支持的语言环境（支持多个）\n\t\t// uni := ut.New(zhT, zhT) 也是可以的\n\t\tuni := ut.New(enT, zhT, enT)\n\n\t\t// locale 通常取决于 http 请求头的 'Accept-Language'\n\t\tvar ok bool\n\t\t// 也可以使用 uni.FindTranslator(...) 传入多个locale进行查找\n\t\ttrans, ok = uni.GetTranslator(locale)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"uni.GetTranslator(%s) failed\", locale)\n\t\t}\n\n\t\t// 注册翻译器\n\t\tswitch locale {\n\t\tcase \"en\":\n\t\t\terr = enTranslations.RegisterDefaultTranslations(v, trans)\n\t\tcase \"zh\":\n\t\t\terr = zhTranslations.RegisterDefaultTranslations(v, trans)\n\t\tdefault:\n\t\t\terr = enTranslations.RegisterDefaultTranslations(v, trans)\n\t\t}\n\t\treturn\n\t}\n\treturn\n}\n\n// removeTopStruct 去除提示信息中的结构体名称\nfunc removeTopStruct(fields map[string]string) map[string]string {\n\tres := map[string]string{}\n\tfor field, err := range fields {\n\t\tres[field[strings.Index(field, \".\")+1:]] = err\n\t}\n\treturn res\n}\n\n// SignUpParamStructLevelValidation 自定义SignUpParam结构体校验函数\nfunc SignUpParamStructLevelValidation(sl validator.StructLevel) {\n\tsu := sl.Current().Interface().(models.ParamSignUp)\n\n\tif su.Password != su.RePassword {\n\t\t// 输出错误提示信息，最后一个参数就是传递的param\n\t\tsl.ReportError(su.RePassword, \"re_password\", \"RePassword\", \"eqfield\", \"password\")\n\t}\n}\n"
  },
  {
    "path": "lesson35/bluebell/controller/vote.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/logic\"\n\t\"bluebell/models\"\n\n\t\"go.uber.org/zap\"\n\n\t\"github.com/go-playground/validator/v10\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// 投票\n\n//type VoteData struct {\n//\t// UserID 从请求中获取当前的用户\n//\tPostID    int64 `json:\"post_id,string\"`   // 贴子id\n//\tDirection int   `json:\"direction,string\"` // 赞成票(1)还是反对票(-1)\n//}\n\nfunc PostVoteController(c *gin.Context) {\n\t// 参数校验\n\tp := new(models.ParamVoteData)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\terrs, ok := err.(validator.ValidationErrors) // 类型断言\n\t\tif !ok {\n\t\t\tResponseError(c, CodeInvalidParam)\n\t\t\treturn\n\t\t}\n\t\terrData := removeTopStruct(errs.Translate(trans)) // 翻译并去除掉错误提示中的结构体标识\n\t\tResponseErrorWithMsg(c, CodeInvalidParam, errData)\n\t\treturn\n\t}\n\t// 获取当前请求的用户的id\n\tuserID, err := getCurrentUserID(c)\n\tif err != nil {\n\t\tResponseError(c, CodeNeedLogin)\n\t\treturn\n\t}\n\t// 具体投票的业务逻辑\n\tif err := logic.VoteForPost(userID, p); err != nil {\n\t\tzap.L().Error(\"logic.VoteForPost() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\n\tResponseSuccess(c, nil)\n}\n"
  },
  {
    "path": "lesson35/bluebell/dao/mysql/community.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/models\"\n\t\"database/sql\"\n\n\t\"go.uber.org/zap\"\n)\n\nfunc GetCommunityList() (communityList []*models.Community, err error) {\n\tsqlStr := \"select community_id, community_name from community\"\n\tif err := db.Select(&communityList, sqlStr); err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\tzap.L().Warn(\"there is no community in db\")\n\t\t\terr = nil\n\t\t}\n\t}\n\treturn\n}\n\n// GetCommunityDetailByID 根据ID查询社区详情\nfunc GetCommunityDetailByID(id int64) (community *models.CommunityDetail, err error) {\n\tcommunity = new(models.CommunityDetail)\n\tsqlStr := `select \n\t\t\tcommunity_id, community_name, introduction, create_time\n\t\t\tfrom community \n\t\t\twhere community_id = ?\n\t`\n\tif err := db.Get(community, sqlStr, id); err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\terr = ErrorInvalidID\n\t\t}\n\t}\n\treturn community, err\n}\n"
  },
  {
    "path": "lesson35/bluebell/dao/mysql/error_code.go",
    "content": "package mysql\n\nimport \"errors\"\n\nvar (\n\tErrorUserExist       = errors.New(\"用户已存在\")\n\tErrorUserNotExist    = errors.New(\"用户不存在\")\n\tErrorInvalidPassword = errors.New(\"用户名或密码错误\")\n\tErrorInvalidID       = errors.New(\"无效的ID\")\n)\n"
  },
  {
    "path": "lesson35/bluebell/dao/mysql/mysql.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/setting\"\n\t\"fmt\"\n\n\t_ \"github.com/go-sql-driver/mysql\"\n\t\"github.com/jmoiron/sqlx\"\n)\n\nvar db *sqlx.DB\n\n// Init 初始化MySQL连接\nfunc Init(cfg *setting.MySQLConfig) (err error) {\n\t// \"user:password@tcp(host:port)/dbname\"\n\tdsn := fmt.Sprintf(\"%s:%s@tcp(%s:%d)/%s?parseTime=true&loc=Local\", cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.DB)\n\tdb, err = sqlx.Connect(\"mysql\", dsn)\n\tif err != nil {\n\t\treturn\n\t}\n\tdb.SetMaxOpenConns(cfg.MaxOpenConns)\n\tdb.SetMaxIdleConns(cfg.MaxIdleConns)\n\treturn\n}\n\n// Close 关闭MySQL连接\nfunc Close() {\n\t_ = db.Close()\n}\n"
  },
  {
    "path": "lesson35/bluebell/dao/mysql/post.go",
    "content": "package mysql\n\nimport \"bluebell/models\"\n\nfunc CreatePost(p *models.Post) (err error) {\n\tsqlStr := `insert into post(\n\tpost_id, title, content, author_id, community_id)\n\tvalues (?, ?, ?, ?, ?)\n\t`\n\t_, err = db.Exec(sqlStr, p.ID, p.Title, p.Content, p.AuthorID, p.CommunityID)\n\treturn\n}\n\nfunc GetPostById(pid int64) (post *models.Post, err error) {\n\tpost = new(models.Post)\n\tsqlStr := `select\n\tpost_id, title, content, author_id, community_id, create_time\n\tfrom post\n\twhere post_id = ?\n\t`\n\terr = db.Get(post, sqlStr, pid)\n\treturn\n}\n\nfunc GetPostList(page, size int64) (posts []*models.Post, err error) {\n\tsqlStr := `select \n\tpost_id, title, content, author_id, community_id, create_time\n\tfrom post\n\tlimit ?,?\n\t`\n\tposts = make([]*models.Post, 0, 2) // 不要写成make([]*models.Post, 2)\n\terr = db.Select(&posts, sqlStr, (page-1)*size, size)\n\treturn\n}\n"
  },
  {
    "path": "lesson35/bluebell/dao/mysql/user.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/models\"\n\t\"crypto/md5\"\n\t\"database/sql\"\n\t\"encoding/hex\"\n)\n\n// 把每一步数据库操作封装成函数\n// 待logic层根据业务需求调用\n\nconst secret = \"liwenzhou.com\"\n\n// CheckUserExist 检查指定用户名的用户是否存在\nfunc CheckUserExist(username string) (err error) {\n\tsqlStr := `select count(user_id) from user where username = ?`\n\tvar count int64\n\tif err := db.Get(&count, sqlStr, username); err != nil {\n\t\treturn err\n\t}\n\tif count > 0 {\n\t\treturn ErrorUserExist\n\t}\n\treturn\n}\n\n// InsertUser 想数据库中插入一条新的用户记录\nfunc InsertUser(user *models.User) (err error) {\n\t// 对密码进行加密\n\tuser.Password = encryptPassword(user.Password)\n\t// 执行SQL语句入库\n\tsqlStr := `insert into user(user_id, username, password) values(?,?,?)`\n\t_, err = db.Exec(sqlStr, user.UserID, user.Username, user.Password)\n\treturn\n}\n\n// encryptPassword 密码加密\nfunc encryptPassword(oPassword string) string {\n\th := md5.New()\n\th.Write([]byte(secret))\n\treturn hex.EncodeToString(h.Sum([]byte(oPassword)))\n}\n\nfunc Login(user *models.User) (err error) {\n\toPassword := user.Password // 用户登录的密码\n\tsqlStr := `select user_id, username, password from user where username=?`\n\terr = db.Get(user, sqlStr, user.Username)\n\tif err == sql.ErrNoRows {\n\t\treturn ErrorUserNotExist\n\t}\n\tif err != nil {\n\t\t// 查询数据库失败\n\t\treturn err\n\t}\n\t// 判断密码是否正确\n\tpassword := encryptPassword(oPassword)\n\tif password != user.Password {\n\t\treturn ErrorInvalidPassword\n\t}\n\treturn\n}\n\n// GetUserById 根据id获取用户信息\nfunc GetUserById(uid int64) (user *models.User, err error) {\n\tuser = new(models.User)\n\tsqlStr := `select user_id, username from user where user_id = ?`\n\terr = db.Get(user, sqlStr, uid)\n\treturn\n}\n"
  },
  {
    "path": "lesson35/bluebell/dao/redis/keys.go",
    "content": "package redis\n\n// redis key\n\n// redis key注意使用命名空间的方式,方便查询和拆分\n\nconst (\n\tPrefix             = \"bluebell:\"   // 项目key前缀\n\tKeyPostTimeZSet    = \"post:time\"   // zset;贴子及发帖时间\n\tKeyPostScoreZSet   = \"post:score\"  // zset;贴子及投票的分数\n\tKeyPostVotedZSetPF = \"post:voted:\" // zset;记录用户及投票类型;参数是post id\n)\n\n// 给redis key加上前缀\nfunc getRedisKey(key string) string {\n\treturn Prefix + key\n}\n"
  },
  {
    "path": "lesson35/bluebell/dao/redis/redis.go",
    "content": "package redis\n\nimport (\n\t\"bluebell/setting\"\n\t\"fmt\"\n\n\t\"github.com/go-redis/redis\"\n)\n\nvar (\n\tclient *redis.Client\n\tNil    = redis.Nil\n)\n\n// Init 初始化连接\nfunc Init(cfg *setting.RedisConfig) (err error) {\n\tclient = redis.NewClient(&redis.Options{\n\t\tAddr:         fmt.Sprintf(\"%s:%d\", cfg.Host, cfg.Port),\n\t\tPassword:     cfg.Password, // no password set\n\t\tDB:           cfg.DB,       // use default DB\n\t\tPoolSize:     cfg.PoolSize,\n\t\tMinIdleConns: cfg.MinIdleConns,\n\t})\n\n\t_, err = client.Ping().Result()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc Close() {\n\t_ = client.Close()\n}\n"
  },
  {
    "path": "lesson35/bluebell/dao/redis/vote.go",
    "content": "package redis\n\nimport (\n\t\"errors\"\n\t\"math\"\n\t\"time\"\n\n\t\"github.com/go-redis/redis\"\n)\n\n// 推荐阅读\n// 基于用户投票的相关算法：http://www.ruanyifeng.com/blog/algorithm/\n\n// 本项目使用简化版的投票分数\n// 投一票就加432分   86400/200  --> 200张赞成票可以给你的帖子续一天\n\n/* 投票的几种情况：\n   direction=1时，有两种情况：\n   \t1. 之前没有投过票，现在投赞成票    --> 更新分数和投票记录  差值的绝对值：1  +432\n   \t2. 之前投反对票，现在改投赞成票    --> 更新分数和投票记录  差值的绝对值：2  +432*2\n   direction=0时，有两种情况：\n   \t1. 之前投过反对票，现在要取消投票  --> 更新分数和投票记录  差值的绝对值：1  +432\n\t2. 之前投过赞成票，现在要取消投票  --> 更新分数和投票记录  差值的绝对值：1  -432\n   direction=-1时，有两种情况：\n   \t1. 之前没有投过票，现在投反对票    --> 更新分数和投票记录  差值的绝对值：1  -432\n   \t2. 之前投赞成票，现在改投反对票    --> 更新分数和投票记录  差值的绝对值：2  -432*2\n\n   投票的限制：\n   每个贴子自发表之日起一个星期之内允许用户投票，超过一个星期就不允许再投票了。\n   \t1. 到期之后将redis中保存的赞成票数及反对票数存储到mysql表中\n   \t2. 到期之后删除那个 KeyPostVotedZSetPF\n*/\n\nconst (\n\toneWeekInSeconds = 7 * 24 * 3600\n\tscorePerVote     = 432 // 每一票值多少分\n)\n\nvar (\n\tErrVoteTimeExpire = errors.New(\"投票时间已过\")\n)\n\nfunc CreatePost(postID int64) error {\n\tpipeline := client.TxPipeline()\n\t// 帖子时间\n\tpipeline.ZAdd(getRedisKey(KeyPostTimeZSet), redis.Z{\n\t\tScore:  float64(time.Now().Unix()),\n\t\tMember: postID,\n\t})\n\n\t// 帖子分数\n\tpipeline.ZAdd(getRedisKey(KeyPostScoreZSet), redis.Z{\n\t\tScore:  float64(time.Now().Unix()),\n\t\tMember: postID,\n\t})\n\t_, err := pipeline.Exec()\n\treturn err\n}\n\nfunc VoteForPost(userID, postID string, value float64) error {\n\t// 1. 判断投票限制\n\t// 去redis取帖子发布时间\n\tpostTime := client.ZScore(getRedisKey(KeyPostTimeZSet), postID).Val()\n\tif float64(time.Now().Unix())-postTime > oneWeekInSeconds {\n\t\treturn ErrVoteTimeExpire\n\t}\n\t// 2和3需要放到一个pipeline事务中操作\n\n\t// 2. 更新贴子的分数\n\t// 先查当前用户给当前帖子的投票记录\n\tov := client.ZScore(getRedisKey(KeyPostVotedZSetPF+postID), userID).Val()\n\tvar op float64\n\tif value > ov {\n\t\top = 1\n\t} else {\n\t\top = -1\n\t}\n\tdiff := math.Abs(ov - value) // 计算两次投票的差值\n\tpipeline := client.TxPipeline()\n\tpipeline.ZIncrBy(getRedisKey(KeyPostScoreZSet), op*diff*scorePerVote, postID)\n\n\t// 3. 记录用户为该贴子投票的数据\n\tif value == 0 {\n\t\tpipeline.ZRem(getRedisKey(KeyPostVotedZSetPF+postID), postID)\n\t} else {\n\t\tpipeline.ZAdd(getRedisKey(KeyPostVotedZSetPF+postID), redis.Z{\n\t\t\tScore:  value, // 赞成票还是反对票\n\t\t\tMember: userID,\n\t\t})\n\t}\n\t_, err := pipeline.Exec()\n\treturn err\n}\n"
  },
  {
    "path": "lesson35/bluebell/go.mod",
    "content": "module bluebell\n\ngo 1.14\n\nrequire (\n\tgithub.com/bwmarrin/snowflake v0.3.0\n\tgithub.com/dgrijalva/jwt-go v3.2.0+incompatible\n\tgithub.com/fsnotify/fsnotify v1.4.9\n\tgithub.com/gin-gonic/gin v1.6.3\n\tgithub.com/go-playground/locales v0.13.0\n\tgithub.com/go-playground/universal-translator v0.17.0\n\tgithub.com/go-playground/validator/v10 v10.2.0\n\tgithub.com/go-redis/redis v6.15.8+incompatible\n\tgithub.com/go-sql-driver/mysql v1.5.0\n\tgithub.com/jmoiron/sqlx v1.2.0\n\tgithub.com/natefinch/lumberjack v2.0.0+incompatible\n\tgithub.com/onsi/ginkgo v1.14.0 // indirect\n\tgithub.com/sony/sonyflake v1.0.0 // indirect\n\tgithub.com/spf13/viper v1.7.0\n\tgo.uber.org/zap v1.15.0\n\tgopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect\n)\n"
  },
  {
    "path": "lesson35/bluebell/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ngithub.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=\ngithub.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=\ngithub.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=\ngithub.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=\ngithub.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=\ngithub.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=\ngithub.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=\ngithub.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0=\ngithub.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=\ngithub.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=\ngithub.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=\ngithub.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=\ngithub.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=\ngithub.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=\ngithub.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=\ngithub.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=\ngithub.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=\ngithub.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=\ngithub.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=\ngithub.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=\ngithub.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=\ngithub.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=\ngithub.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=\ngithub.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=\ngithub.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=\ngithub.com/go-redis/redis v6.15.8+incompatible h1:BKZuG6mCnRj5AOaWJXoCgf6rqTYnYJLe4en2hxT7r9o=\ngithub.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=\ngithub.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=\ngithub.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=\ngithub.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=\ngithub.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=\ngithub.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=\ngithub.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=\ngithub.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=\ngithub.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=\ngithub.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=\ngithub.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=\ngithub.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=\ngithub.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=\ngithub.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=\ngithub.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=\ngithub.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=\ngithub.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=\ngithub.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=\ngithub.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=\ngithub.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=\ngithub.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=\ngithub.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=\ngithub.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=\ngithub.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=\ngithub.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=\ngithub.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=\ngithub.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=\ngithub.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=\ngithub.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=\ngithub.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=\ngithub.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=\ngithub.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=\ngithub.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=\ngithub.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=\ngithub.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=\ngithub.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=\ngithub.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=\ngithub.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=\ngithub.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=\ngithub.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=\ngithub.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=\ngithub.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=\ngithub.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=\ngithub.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=\ngithub.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=\ngithub.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=\ngithub.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=\ngithub.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=\ngithub.com/sony/sonyflake v1.0.0 h1:MpU6Ro7tfXwgn2l5eluf9xQvQJDROTBImNCfRXn/YeM=\ngithub.com/sony/sonyflake v1.0.0/go.mod h1:Jv3cfhf/UFtolOTTRd3q4Nl6ENqM+KfyZ5PseKfZGF4=\ngithub.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=\ngithub.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=\ngithub.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=\ngithub.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=\ngithub.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=\ngithub.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=\ngithub.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=\ngithub.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=\ngithub.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=\ngithub.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=\ngithub.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=\ngithub.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=\ngithub.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=\ngithub.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=\ngithub.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=\ngo.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=\ngo.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=\ngo.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=\ngo.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=\ngo.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngo.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=\ngo.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc h1:NCy3Ohtk6Iny5V/reW2Ktypo4zIpWBdRJ1uFMjBxdg8=\ngolang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=\ngopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=\ngopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=\ngopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\n"
  },
  {
    "path": "lesson35/bluebell/logger/logger.go",
    "content": "package logger\n\nimport (\n\t\"bluebell/setting\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"os\"\n\t\"runtime/debug\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/natefinch/lumberjack\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\nvar lg *zap.Logger\n\n// Init 初始化lg\nfunc Init(cfg *setting.LogConfig, mode string) (err error) {\n\twriteSyncer := getLogWriter(cfg.Filename, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge)\n\tencoder := getEncoder()\n\tvar l = new(zapcore.Level)\n\terr = l.UnmarshalText([]byte(cfg.Level))\n\tif err != nil {\n\t\treturn\n\t}\n\tvar core zapcore.Core\n\tif mode == \"dev\" {\n\t\t// 进入开发模式，日志输出到终端\n\t\tconsoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())\n\t\tcore = zapcore.NewTee(\n\t\t\tzapcore.NewCore(encoder, writeSyncer, l),\n\t\t\tzapcore.NewCore(consoleEncoder, zapcore.Lock(os.Stdout), zapcore.DebugLevel),\n\t\t)\n\t} else {\n\t\tcore = zapcore.NewCore(encoder, writeSyncer, l)\n\t}\n\n\tlg = zap.New(core, zap.AddCaller())\n\n\tzap.ReplaceGlobals(lg)\n\tzap.L().Info(\"init logger success\")\n\treturn\n}\n\nfunc getEncoder() zapcore.Encoder {\n\tencoderConfig := zap.NewProductionEncoderConfig()\n\tencoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder\n\tencoderConfig.TimeKey = \"time\"\n\tencoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder\n\tencoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder\n\tencoderConfig.EncodeCaller = zapcore.ShortCallerEncoder\n\treturn zapcore.NewJSONEncoder(encoderConfig)\n}\n\nfunc getLogWriter(filename string, maxSize, maxBackup, maxAge int) zapcore.WriteSyncer {\n\tlumberJackLogger := &lumberjack.Logger{\n\t\tFilename:   filename,\n\t\tMaxSize:    maxSize,\n\t\tMaxBackups: maxBackup,\n\t\tMaxAge:     maxAge,\n\t}\n\treturn zapcore.AddSync(lumberJackLogger)\n}\n\n// GinLogger 接收gin框架默认的日志\nfunc GinLogger() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tstart := time.Now()\n\t\tpath := c.Request.URL.Path\n\t\tquery := c.Request.URL.RawQuery\n\t\tc.Next()\n\n\t\tcost := time.Since(start)\n\t\tlg.Info(path,\n\t\t\tzap.Int(\"status\", c.Writer.Status()),\n\t\t\tzap.String(\"method\", c.Request.Method),\n\t\t\tzap.String(\"path\", path),\n\t\t\tzap.String(\"query\", query),\n\t\t\tzap.String(\"ip\", c.ClientIP()),\n\t\t\tzap.String(\"user-agent\", c.Request.UserAgent()),\n\t\t\tzap.String(\"errors\", c.Errors.ByType(gin.ErrorTypePrivate).String()),\n\t\t\tzap.Duration(\"cost\", cost),\n\t\t)\n\t}\n}\n\n// GinRecovery recover掉项目可能出现的panic，并使用zap记录相关日志\nfunc GinRecovery(stack bool) gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tdefer func() {\n\t\t\tif err := recover(); err != nil {\n\t\t\t\t// Check for a broken connection, as it is not really a\n\t\t\t\t// condition that warrants a panic stack trace.\n\t\t\t\tvar brokenPipe bool\n\t\t\t\tif ne, ok := err.(*net.OpError); ok {\n\t\t\t\t\tif se, ok := ne.Err.(*os.SyscallError); ok {\n\t\t\t\t\t\tif strings.Contains(strings.ToLower(se.Error()), \"broken pipe\") || strings.Contains(strings.ToLower(se.Error()), \"connection reset by peer\") {\n\t\t\t\t\t\t\tbrokenPipe = true\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\thttpRequest, _ := httputil.DumpRequest(c.Request, false)\n\t\t\t\tif brokenPipe {\n\t\t\t\t\tlg.Error(c.Request.URL.Path,\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t)\n\t\t\t\t\t// If the connection is dead, we can't write a status to it.\n\t\t\t\t\tc.Error(err.(error)) // nolint: errcheck\n\t\t\t\t\tc.Abort()\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif stack {\n\t\t\t\t\tlg.Error(\"[Recovery from panic]\",\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t\tzap.String(\"stack\", string(debug.Stack())),\n\t\t\t\t\t)\n\t\t\t\t} else {\n\t\t\t\t\tlg.Error(\"[Recovery from panic]\",\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tc.AbortWithStatus(http.StatusInternalServerError)\n\t\t\t}\n\t\t}()\n\t\tc.Next()\n\t}\n}\n"
  },
  {
    "path": "lesson35/bluebell/logic/community.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/models\"\n)\n\nfunc GetCommunityList() ([]*models.Community, error) {\n\t// 查数据库 查找到所有的community 并返回\n\treturn mysql.GetCommunityList()\n}\n\nfunc GetCommunityDetail(id int64) (*models.CommunityDetail, error) {\n\treturn mysql.GetCommunityDetailByID(id)\n}\n"
  },
  {
    "path": "lesson35/bluebell/logic/post.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/dao/redis\"\n\t\"bluebell/models\"\n\t\"bluebell/pkg/snowflake\"\n\n\t\"go.uber.org/zap\"\n)\n\nfunc CreatePost(p *models.Post) (err error) {\n\t// 1. 生成post id\n\tp.ID = snowflake.GenID()\n\t// 2. 保存到数据库\n\terr = mysql.CreatePost(p)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = redis.CreatePost(p.ID)\n\treturn\n\t// 3. 返回\n}\n\n// GetPostById 根据帖子id查询帖子详情数据\nfunc GetPostById(pid int64) (data *models.ApiPostDetail, err error) {\n\t// 查询并组合我们接口想用的数据\n\tpost, err := mysql.GetPostById(pid)\n\tif err != nil {\n\t\tzap.L().Error(\"mysql.GetPostById(pid) failed\",\n\t\t\tzap.Int64(\"pid\", pid),\n\t\t\tzap.Error(err))\n\t\treturn\n\t}\n\t// 根据作者id查询作者信息\n\tuser, err := mysql.GetUserById(post.AuthorID)\n\tif err != nil {\n\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\tzap.Int64(\"author_id\", post.AuthorID),\n\t\t\tzap.Error(err))\n\t\treturn\n\t}\n\t// 根据社区id查询社区详细信息\n\tcommunity, err := mysql.GetCommunityDetailByID(post.CommunityID)\n\tif err != nil {\n\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\tzap.Int64(\"community_id\", post.CommunityID),\n\t\t\tzap.Error(err))\n\t\treturn\n\t}\n\t// 接口数据拼接\n\tdata = &models.ApiPostDetail{\n\t\tAuthorName:      user.Username,\n\t\tPost:            post,\n\t\tCommunityDetail: community,\n\t}\n\treturn\n}\n\n// GetPostList 获取帖子列表\nfunc GetPostList(page, size int64) (data []*models.ApiPostDetail, err error) {\n\tposts, err := mysql.GetPostList(page, size)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdata = make([]*models.ApiPostDetail, 0, len(posts))\n\n\tfor _, post := range posts {\n\t\t// 根据作者id查询作者信息\n\t\tuser, err := mysql.GetUserById(post.AuthorID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"author_id\", post.AuthorID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\t// 根据社区id查询社区详细信息\n\t\tcommunity, err := mysql.GetCommunityDetailByID(post.CommunityID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"community_id\", post.CommunityID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\tpostDetail := &models.ApiPostDetail{\n\t\t\tAuthorName:      user.Username,\n\t\t\tPost:            post,\n\t\t\tCommunityDetail: community,\n\t\t}\n\t\tdata = append(data, postDetail)\n\t}\n\treturn\n}\n"
  },
  {
    "path": "lesson35/bluebell/logic/user.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/models\"\n\t\"bluebell/pkg/jwt\"\n\t\"bluebell/pkg/snowflake\"\n)\n\n// 存放业务逻辑的代码\n\nfunc SignUp(p *models.ParamSignUp) (err error) {\n\t// 1.判断用户存不存在\n\tif err := mysql.CheckUserExist(p.Username); err != nil {\n\t\treturn err\n\t}\n\t// 2.生成UID\n\tuserID := snowflake.GenID()\n\t// 构造一个User实例\n\tuser := &models.User{\n\t\tUserID:   userID,\n\t\tUsername: p.Username,\n\t\tPassword: p.Password,\n\t}\n\t// 3.保存进数据库\n\treturn mysql.InsertUser(user)\n}\n\nfunc Login(p *models.ParamLogin) (user *models.User, err error) {\n\tuser = &models.User{\n\t\tUsername: p.Username,\n\t\tPassword: p.Password,\n\t}\n\t// 传递的是指针，就能拿到user.UserID\n\tif err := mysql.Login(user); err != nil {\n\t\treturn nil, err\n\t}\n\t// 生成JWT\n\ttoken, err := jwt.GenToken(user.UserID, user.Username)\n\tif err != nil {\n\t\treturn\n\t}\n\tuser.Token = token\n\treturn\n}\n"
  },
  {
    "path": "lesson35/bluebell/logic/vote.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/redis\"\n\t\"bluebell/models\"\n\t\"strconv\"\n\n\t\"go.uber.org/zap\"\n)\n\n// 推荐阅读\n// 基于用户投票的相关算法：http://www.ruanyifeng.com/blog/algorithm/\n\n// 本项目使用简化版的投票分数\n// 投一票就加432分   86400/200  --> 200张赞成票可以给你的帖子续一天\n\n/* 投票的几种情况：\ndirection=1时，有两种情况：\n\t1. 之前没有投过票，现在投赞成票    --> 更新分数和投票记录\n\t2. 之前投反对票，现在改投赞成票    --> 更新分数和投票记录\ndirection=0时，有两种情况：\n\t1. 之前投过赞成票，现在要取消投票  --> 更新分数和投票记录\n\t2. 之前投过反对票，现在要取消投票  --> 更新分数和投票记录\ndirection=-1时，有两种情况：\n\t1. 之前没有投过票，现在投反对票    --> 更新分数和投票记录\n\t2. 之前投赞成票，现在改投反对票    --> 更新分数和投票记录\n\n投票的限制：\n每个贴子自发表之日起一个星期之内允许用户投票，超过一个星期就不允许再投票了。\n\t1. 到期之后将redis中保存的赞成票数及反对票数存储到mysql表中\n\t2. 到期之后删除那个 KeyPostVotedZSetPF\n*/\n\n// VoteForPost 为帖子投票的函数\nfunc VoteForPost(userID int64, p *models.ParamVoteData) error {\n\tzap.L().Debug(\"VoteForPost\",\n\t\tzap.Int64(\"userID\", userID),\n\t\tzap.String(\"postID\", p.PostID),\n\t\tzap.Int8(\"direction\", p.Direction))\n\treturn redis.VoteForPost(strconv.Itoa(int(userID)), p.PostID, float64(p.Direction))\n}\n"
  },
  {
    "path": "lesson35/bluebell/main.go",
    "content": "package main\n\nimport (\n\t\"bluebell/controller\"\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/dao/redis\"\n\t\"bluebell/logger\"\n\t\"bluebell/pkg/snowflake\"\n\t\"bluebell/router\"\n\t\"bluebell/setting\"\n\t\"fmt\"\n\t\"os\"\n)\n\nfunc main() {\n\tif len(os.Args) < 2 {\n\t\tfmt.Println(\"need config file.eg: bluebell config.yaml\")\n\t\treturn\n\t}\n\t// 加载配置\n\tif err := setting.Init(os.Args[1]); err != nil {\n\t\tfmt.Printf(\"load config failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tif err := logger.Init(setting.Conf.LogConfig, setting.Conf.Mode); err != nil {\n\t\tfmt.Printf(\"init logger failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tif err := mysql.Init(setting.Conf.MySQLConfig); err != nil {\n\t\tfmt.Printf(\"init mysql failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer mysql.Close() // 程序退出关闭数据库连接\n\tif err := redis.Init(setting.Conf.RedisConfig); err != nil {\n\t\tfmt.Printf(\"init redis failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer redis.Close()\n\n\tif err := snowflake.Init(setting.Conf.StartTime, setting.Conf.MachineID); err != nil {\n\t\tfmt.Printf(\"init snowflake failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\t// 初始化gin框架内置的校验器使用的翻译器\n\tif err := controller.InitTrans(\"zh\"); err != nil {\n\t\tfmt.Printf(\"init validator trans failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\t// 注册路由\n\tr := router.SetupRouter(setting.Conf.Mode)\n\terr := r.Run(fmt.Sprintf(\":%d\", setting.Conf.Port))\n\tif err != nil {\n\t\tfmt.Printf(\"run server failed, err:%v\\n\", err)\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "lesson35/bluebell/middlewares/auth.go",
    "content": "package middlewares\n\nimport (\n\t\"bluebell/controller\"\n\t\"bluebell/pkg/jwt\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// JWTAuthMiddleware 基于JWT的认证中间件\nfunc JWTAuthMiddleware() func(c *gin.Context) {\n\treturn func(c *gin.Context) {\n\t\t// 客户端携带Token有三种方式 1.放在请求头 2.放在请求体 3.放在URI\n\t\t// 这里假设Token放在Header的Authorization中，并使用Bearer开头\n\t\t// Authorization: Bearer xxxxxxx.xxx.xxx  / X-TOKEN: xxx.xxx.xx\n\t\t// 这里的具体实现方式要依据你的实际业务情况决定\n\t\tauthHeader := c.Request.Header.Get(\"Authorization\")\n\t\tif authHeader == \"\" {\n\t\t\tcontroller.ResponseError(c, controller.CodeNeedLogin)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// 按空格分割\n\t\tparts := strings.SplitN(authHeader, \" \", 2)\n\t\tif !(len(parts) == 2 && parts[0] == \"Bearer\") {\n\t\t\tcontroller.ResponseError(c, controller.CodeInvalidToken)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// parts[1]是获取到的tokenString，我们使用之前定义好的解析JWT的函数来解析它\n\t\tmc, err := jwt.ParseToken(parts[1])\n\t\tif err != nil {\n\t\t\tcontroller.ResponseError(c, controller.CodeInvalidToken)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// 将当前请求的userID信息保存到请求的上下文c上\n\t\tc.Set(controller.CtxUserIDKey, mc.UserID)\n\n\t\tc.Next() // 后续的处理请求的函数中 可以用过c.Get(CtxUserIDKey) 来获取当前请求的用户信息\n\t}\n}\n"
  },
  {
    "path": "lesson35/bluebell/models/community.go",
    "content": "package models\n\nimport \"time\"\n\ntype Community struct {\n\tID   int64  `json:\"id\" db:\"community_id\"`\n\tName string `json:\"name\" db:\"community_name\"`\n}\n\ntype CommunityDetail struct {\n\tID           int64     `json:\"id\" db:\"community_id\"`\n\tName         string    `json:\"name\" db:\"community_name\"`\n\tIntroduction string    `json:\"introduction,omitempty\" db:\"introduction\"`\n\tCreateTime   time.Time `json:\"create_time\" db:\"create_time\"`\n}\n"
  },
  {
    "path": "lesson35/bluebell/models/create_table.sql",
    "content": "--\nCREATE TABLE `user` (\n    `id` bigint(20) NOT NULL AUTO_INCREMENT,\n    `user_id` bigint(20) NOT NULL,\n    `username` varchar(64) COLLATE utf8mb4_general_ci NOT NULL,\n    `password` varchar(64) COLLATE utf8mb4_general_ci NOT NULL,\n    `email` varchar(64) COLLATE utf8mb4_general_ci,\n    `gender` tinyint(4) NOT NULL DEFAULT '0',\n    `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n    `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    PRIMARY KEY (`id`),\n    UNIQUE KEY `idx_username` (`username`) USING BTREE,\n    UNIQUE KEY `idx_user_id` (`user_id`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\n\n\nDROP TABLE IF EXISTS `community`;\nCREATE TABLE `community` (\n     `id` int(11) NOT NULL AUTO_INCREMENT,\n     `community_id` int(10) unsigned NOT NULL,\n     `community_name` varchar(128) COLLATE utf8mb4_general_ci NOT NULL,\n     `introduction` varchar(256) COLLATE utf8mb4_general_ci NOT NULL,\n     `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n     `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n     PRIMARY KEY (`id`),\n     UNIQUE KEY `idx_community_id` (`community_id`),\n     UNIQUE KEY `idx_community_name` (`community_name`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\n\n\nINSERT INTO `community` VALUES ('1', '1', 'Go', 'Golang', '2016-11-01 08:10:10', '2016-11-01 08:10:10');\nINSERT INTO `community` VALUES ('2', '2', 'leetcode', '刷题刷题刷题', '2020-01-01 08:00:00', '2020-01-01 08:00:00');\nINSERT INTO `community` VALUES ('3', '3', 'CS:GO', 'Rush B。。。', '2018-08-07 08:30:00', '2018-08-07 08:30:00');\nINSERT INTO `community` VALUES ('4', '4', 'LOL', '欢迎来到英雄联盟!', '2016-01-01 08:00:00', '2016-01-01 08:00:00');\n\nDROP TABLE IF EXISTS `post`;\nCREATE TABLE `post` (\n    `id` bigint(20) NOT NULL AUTO_INCREMENT,\n    `post_id` bigint(20) NOT NULL COMMENT '帖子id',\n    `title` varchar(128) COLLATE utf8mb4_general_ci NOT NULL COMMENT '标题',\n    `content` varchar(8192) COLLATE utf8mb4_general_ci NOT NULL COMMENT '内容',\n    `author_id` bigint(20) NOT NULL COMMENT '作者的用户id',\n    `community_id` bigint(20) NOT NULL COMMENT '所属社区',\n    `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '帖子状态',\n    `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n    `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n    PRIMARY KEY (`id`),\n    UNIQUE KEY `idx_post_id` (`post_id`),\n    KEY `idx_author_id` (`author_id`),\n    KEY `idx_community_id` (`community_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;"
  },
  {
    "path": "lesson35/bluebell/models/params.go",
    "content": "package models\n\n// 定义请求的参数结构体\n\n// ParamSignUp 注册请求参数\ntype ParamSignUp struct {\n\tUsername   string `json:\"username\" binding:\"required\"`\n\tPassword   string `json:\"password\" binding:\"required\"`\n\tRePassword string `json:\"re_password\" binding:\"required,eqfield=Password\"`\n}\n\n// ParamLogin 登录请求参数\ntype ParamLogin struct {\n\tUsername string `json:\"username\" binding:\"required\"`\n\tPassword string `json:\"password\" binding:\"required\"`\n}\n\n// ParamVoteData 投票数据\ntype ParamVoteData struct {\n\t// UserID 从请求中获取当前的用户\n\tPostID    string `json:\"post_id\" binding:\"required\"`               // 贴子id\n\tDirection int8   `json:\"direction,string\" binding:\"oneof=1 0 -1\" ` // 赞成票(1)还是反对票(-1)取消投票(0)\n}\n"
  },
  {
    "path": "lesson35/bluebell/models/post.go",
    "content": "package models\n\nimport \"time\"\n\n// 内存对齐概念\n\ntype Post struct {\n\tID          int64     `json:\"id,string\" db:\"post_id\"`\n\tAuthorID    int64     `json:\"author_id\" db:\"author_id\"`\n\tCommunityID int64     `json:\"community_id\" db:\"community_id\" binding:\"required\"`\n\tStatus      int32     `json:\"status\" db:\"status\"`\n\tTitle       string    `json:\"title\" db:\"title\" binding:\"required\"`\n\tContent     string    `json:\"content\" db:\"content\" binding:\"required\"`\n\tCreateTime  time.Time `json:\"create_time\" db:\"create_time\"`\n}\n\n// ApiPostDetail 帖子详情接口的结构体\ntype ApiPostDetail struct {\n\tAuthorName       string             `json:\"author_name\"`\n\t*Post                               // 嵌入帖子结构体\n\t*CommunityDetail `json:\"community\"` // 嵌入社区信息\n}\n"
  },
  {
    "path": "lesson35/bluebell/models/struct_test.go",
    "content": "package models\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"unsafe\"\n)\n\n// Go 内存对齐\n\ntype s1 struct {\n\ta int8   // 1\n\tb string // 3\n\tc int8   // 1\n}\n\ntype s2 struct {\n\ta int8\n\tc int8   // 1\n\tb string // 2\n}\n\nfunc TestStruct(t *testing.T) {\n\tv1 := s1{\n\t\ta: 1,\n\t\tb: \"七米\",\n\t\tc: 2,\n\t}\n\n\tv2 := s2{\n\t\ta: 1,\n\t\tb: \"七米\",\n\t\tc: 2,\n\t}\n\n\tfmt.Println(unsafe.Sizeof(v1), unsafe.Sizeof(v2))\n}\n"
  },
  {
    "path": "lesson35/bluebell/models/user.go",
    "content": "package models\n\ntype User struct {\n\tUserID   int64  `db:\"user_id\"`\n\tUsername string `db:\"username\"`\n\tPassword string `db:\"password\"`\n\tToken    string\n}\n"
  },
  {
    "path": "lesson35/bluebell/pkg/jwt/jwt.go",
    "content": "package jwt\n\nimport (\n\t\"errors\"\n\t\"time\"\n\n\t\"github.com/spf13/viper\"\n\n\t\"github.com/dgrijalva/jwt-go\"\n)\n\nvar mySecret = []byte(\"夏天夏天悄悄过去\")\n\n// MyClaims 自定义声明结构体并内嵌jwt.StandardClaims\n// jwt包自带的jwt.StandardClaims只包含了官方字段\n// 我们这里需要额外记录一个username字段，所以要自定义结构体\n// 如果想要保存更多信息，都可以添加到这个结构体中\ntype MyClaims struct {\n\tUserID   int64  `json:\"user_id\"`\n\tUsername string `json:\"username\"`\n\tjwt.StandardClaims\n}\n\n// GenToken 生成JWT\nfunc GenToken(userID int64, username string) (string, error) {\n\t// 创建一个我们自己的声明的数据\n\tc := MyClaims{\n\t\tuserID,\n\t\t\"username\", // 自定义字段\n\t\tjwt.StandardClaims{\n\t\t\tExpiresAt: time.Now().Add(\n\t\t\t\ttime.Duration(viper.GetInt(\"auth.jwt_expire\")) * time.Hour).Unix(), // 过期时间\n\t\t\tIssuer: \"bluebell\", // 签发人\n\t\t},\n\t}\n\t// 使用指定的签名方法创建签名对象\n\ttoken := jwt.NewWithClaims(jwt.SigningMethodHS256, c)\n\t// 使用指定的secret签名并获得完整的编码后的字符串token\n\treturn token.SignedString(mySecret)\n}\n\n// ParseToken 解析JWT\nfunc ParseToken(tokenString string) (*MyClaims, error) {\n\t// 解析token\n\tvar mc = new(MyClaims)\n\ttoken, err := jwt.ParseWithClaims(tokenString, mc, func(token *jwt.Token) (i interface{}, err error) {\n\t\treturn mySecret, nil\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif token.Valid { // 校验token\n\t\treturn mc, nil\n\t}\n\treturn nil, errors.New(\"invalid token\")\n}\n"
  },
  {
    "path": "lesson35/bluebell/pkg/snowflake/snowflake.go",
    "content": "package snowflake\n\nimport (\n\t\"time\"\n\n\tsf \"github.com/bwmarrin/snowflake\"\n)\n\nvar node *sf.Node\n\nfunc Init(startTime string, machineID int64) (err error) {\n\tvar st time.Time\n\tst, err = time.Parse(\"2006-01-02\", startTime)\n\tif err != nil {\n\t\treturn\n\t}\n\tsf.Epoch = st.UnixNano() / 1000000\n\tnode, err = sf.NewNode(machineID)\n\treturn\n}\nfunc GenID() int64 {\n\treturn node.Generate().Int64()\n}\n"
  },
  {
    "path": "lesson35/bluebell/router/route.go",
    "content": "package router\n\nimport (\n\t\"bluebell/controller\"\n\t\"bluebell/logger\"\n\t\"bluebell/middlewares\"\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc SetupRouter(mode string) *gin.Engine {\n\tif mode == gin.ReleaseMode {\n\t\tgin.SetMode(gin.ReleaseMode) // gin设置成发布模式\n\t}\n\tr := gin.New()\n\tr.Use(logger.GinLogger(), logger.GinRecovery(true))\n\n\tv1 := r.Group(\"/api/v1\")\n\n\t// 注册\n\tv1.POST(\"/signup\", controller.SignUpHandler)\n\t// 登录\n\tv1.POST(\"/login\", controller.LoginHandler)\n\n\tv1.Use(middlewares.JWTAuthMiddleware()) // 应用JWT认证中间件\n\n\t{\n\t\tv1.GET(\"/community\", controller.CommunityHandler)\n\t\tv1.GET(\"/community/:id\", controller.CommunityDetailHandler)\n\n\t\tv1.POST(\"/post\", controller.CreatePostHandler)\n\t\tv1.GET(\"/post/:id\", controller.GetPostDetailHandler)\n\t\tv1.GET(\"/posts/\", controller.GetPostListHandler)\n\n\t\t// 投票\n\t\tv1.POST(\"/vote\", controller.PostVoteController)\n\n\t}\n\n\tr.NoRoute(func(c *gin.Context) {\n\t\tc.JSON(http.StatusOK, gin.H{\n\t\t\t\"msg\": \"404\",\n\t\t})\n\t})\n\treturn r\n}\n"
  },
  {
    "path": "lesson35/bluebell/setting/setting.go",
    "content": "package setting\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\t\"github.com/spf13/viper\"\n)\n\nvar Conf = new(AppConfig)\n\ntype AppConfig struct {\n\tName      string `mapstructure:\"name\"`\n\tMode      string `mapstructure:\"mode\"`\n\tVersion   string `mapstructure:\"version\"`\n\tStartTime string `mapstructure:\"start_time\"`\n\tMachineID int64  `mapstructure:\"machine_id\"`\n\tPort      int    `mapstructure:\"port\"`\n\n\t*LogConfig   `mapstructure:\"log\"`\n\t*MySQLConfig `mapstructure:\"mysql\"`\n\t*RedisConfig `mapstructure:\"redis\"`\n}\n\ntype MySQLConfig struct {\n\tHost         string `mapstructure:\"host\"`\n\tUser         string `mapstructure:\"user\"`\n\tPassword     string `mapstructure:\"password\"`\n\tDB           string `mapstructure:\"dbname\"`\n\tPort         int    `mapstructure:\"port\"`\n\tMaxOpenConns int    `mapstructure:\"max_open_conns\"`\n\tMaxIdleConns int    `mapstructure:\"max_idle_conns\"`\n}\n\ntype RedisConfig struct {\n\tHost         string `mapstructure:\"host\"`\n\tPassword     string `mapstructure:\"password\"`\n\tPort         int    `mapstructure:\"port\"`\n\tDB           int    `mapstructure:\"db\"`\n\tPoolSize     int    `mapstructure:\"pool_size\"`\n\tMinIdleConns int    `mapstructure:\"min_idle_conns\"`\n}\n\ntype LogConfig struct {\n\tLevel      string `mapstructure:\"level\"`\n\tFilename   string `mapstructure:\"filename\"`\n\tMaxSize    int    `mapstructure:\"max_size\"`\n\tMaxAge     int    `mapstructure:\"max_age\"`\n\tMaxBackups int    `mapstructure:\"max_backups\"`\n}\n\nfunc Init(filePath string) (err error) {\n\t// 方式1：直接指定配置文件路径（相对路径或者绝对路径）\n\t// 相对路径：相对执行的可执行文件的相对路径\n\t//viper.SetConfigFile(\"./conf/config.yaml\")\n\t// 绝对路径：系统中实际的文件路径\n\t//viper.SetConfigFile(\"/Users/liwenzhou/Desktop/bluebell/conf/config.yaml\")\n\n\t// 方式2：指定配置文件名和配置文件的位置，viper自行查找可用的配置文件\n\t// 配置文件名不需要带后缀\n\t// 配置文件位置可配置多个\n\t//viper.SetConfigName(\"config\") // 指定配置文件名（不带后缀）\n\t//viper.AddConfigPath(\".\") // 指定查找配置文件的路径（这里使用相对路径）\n\t//viper.AddConfigPath(\"./conf\")      // 指定查找配置文件的路径（这里使用相对路径）\n\n\t// 基本上是配合远程配置中心使用的，告诉viper当前的数据使用什么格式去解析\n\t//viper.SetConfigType(\"json\")\n\n\tviper.SetConfigFile(filePath)\n\n\terr = viper.ReadInConfig() // 读取配置信息\n\tif err != nil {\n\t\t// 读取配置信息失败\n\t\tfmt.Printf(\"viper.ReadInConfig failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\n\t// 把读取到的配置信息反序列化到 Conf 变量中\n\tif err := viper.Unmarshal(Conf); err != nil {\n\t\tfmt.Printf(\"viper.Unmarshal failed, err:%v\\n\", err)\n\t}\n\n\tviper.WatchConfig()\n\tviper.OnConfigChange(func(in fsnotify.Event) {\n\t\tfmt.Println(\"配置文件修改了...\")\n\t\tif err := viper.Unmarshal(Conf); err != nil {\n\t\t\tfmt.Printf(\"viper.Unmarshal failed, err:%v\\n\", err)\n\t\t}\n\t})\n\treturn\n}\n"
  },
  {
    "path": "lesson68/bluebell/.air.conf",
    "content": "# [Air](https://github.com/cosmtrek/air) TOML 格式的配置文件\n\n# 工作目录\n# 使用 . 或绝对路径，请注意 `tmp_dir` 目录必须在 `root` 目录下\nroot = \".\"\ntmp_dir = \"tmp\"\n\n[build]\n# 只需要写你平常编译使用的shell命令。你也可以使用 `make`\ncmd = \"go build -o ./tmp/main\"\n# 由`cmd`命令得到的二进制文件名\nbin = \"tmp/main\"\n# 自定义的二进制，可以添加额外的编译标识例如添加 GIN_MODE=release\nfull_bin = \"./tmp/main ./conf/config.yaml\"\n# 监听以下文件扩展名的文件.\ninclude_ext = [\"go\", \"tpl\", \"tmpl\", \"html\", \"yaml\"]\n# 忽略这些文件扩展名或目录\nexclude_dir = [\"assets\", \"tmp\", \"vendor\", \"frontend/node_modules\"]\n# 监听以下指定目录的文件\ninclude_dir = []\n# 排除以下文件\nexclude_file = []\n# 如果文件更改过于频繁，则没有必要在每次更改时都触发构建。可以设置触发构建的延迟时间\ndelay = 1000 # ms\n# 发生构建错误时，停止运行旧的二进制文件。\nstop_on_error = true\n# air的日志文件名，该日志文件放置在你的`tmp_dir`中\nlog = \"air_errors.log\"\n\n[log]\n# 显示日志时间\ntime = true\n\n[color]\n# 自定义每个部分显示的颜色。如果找不到颜色，使用原始的应用程序日志。\nmain = \"magenta\"\nwatcher = \"cyan\"\nbuild = \"yellow\"\nrunner = \"green\"\n\n[misc]\n# 退出时删除tmp目录\nclean_on_exit = true"
  },
  {
    "path": "lesson68/bluebell/Makefile",
    "content": ".PHONY: all build run gotool clean help\n\nBINARY=\"xx\"\n\nall: gotool build\n\nbuild:\n\tCGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ${BINARY}\n\nrun:\n\t@go run ./main.go conf/config.yaml\n\ngotool:\n\tgo fmt ./\n\tgo vet ./\n\nclean:\n\t@if [ -f ${BINARY} ] ; then rm ${BINARY} ; fi\n\nhelp:\n\t@echo \"make - 格式化 Go 代码, 并编译生成二进制文件\"\n\t@echo \"make build - 编译 Go 代码, 生成二进制文件\"\n\t@echo \"make run - 直接运行 Go 代码\"\n\t@echo \"make clean - 移除二进制文件和 vim swap files\"\n\t@echo \"make gotool - 运行 Go 工具 'fmt' and 'vet'\"\n"
  },
  {
    "path": "lesson68/bluebell/conf/config.yaml",
    "content": "name: \"bluebell\"\nmode: \"dev\"\nport: 8084\nversion: \"v0.0.1\"\nstart_time: \"2020-07-01\"\nmachine_id: 1\n\nauth:\n  jwt_expire: 8760\n\nlog:\n  level: \"debug\"\n  filename: \"web_app.log\"\n  max_size: 200\n  max_age: 30\n  max_backups: 7\nmysql:\n  host: \"127.0.0.1\"\n  port: 13306\n  user: \"root\"\n  password: \"root1234\"\n  dbname: \"bluebell\"\n  max_open_conns: 200\n  max_idle_conns: 50\nredis:\n  host: \"127.0.0.1\"\n  port: 16379\n  password: \"\"\n  db: 0\n  pool_size: 100"
  },
  {
    "path": "lesson68/bluebell/controller/code.go",
    "content": "package controller\n\ntype ResCode int64\n\nconst (\n\tCodeSuccess ResCode = 1000 + iota\n\tCodeInvalidParam\n\tCodeUserExist\n\tCodeUserNotExist\n\tCodeInvalidPassword\n\tCodeServerBusy\n\n\tCodeNeedLogin\n\tCodeInvalidToken\n)\n\nvar codeMsgMap = map[ResCode]string{\n\tCodeSuccess:         \"success\",\n\tCodeInvalidParam:    \"请求参数错误\",\n\tCodeUserExist:       \"用户名已存在\",\n\tCodeUserNotExist:    \"用户名不存在\",\n\tCodeInvalidPassword: \"用户名或密码错误\",\n\tCodeServerBusy:      \"服务繁忙\",\n\n\tCodeNeedLogin:    \"需要登录\",\n\tCodeInvalidToken: \"无效的token\",\n}\n\nfunc (c ResCode) Msg() string {\n\tmsg, ok := codeMsgMap[c]\n\tif !ok {\n\t\tmsg = codeMsgMap[CodeServerBusy]\n\t}\n\treturn msg\n}\n"
  },
  {
    "path": "lesson68/bluebell/controller/community.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/logic\"\n\t\"strconv\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"go.uber.org/zap\"\n)\n\n// ---- 跟社区相关的 ----\n\nfunc CommunityHandler(c *gin.Context) {\n\t// 查询到所有的社区（community_id, community_name) 以列表的形式返回\n\tdata, err := logic.GetCommunityList()\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetCommunityList() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy) // 不轻易把服务端报错暴露给外面\n\t\treturn\n\t}\n\tResponseSuccess(c, data)\n}\n\n// CommunityDetailHandler 社区分类详情\nfunc CommunityDetailHandler(c *gin.Context) {\n\t// 1. 获取社区id\n\tidStr := c.Param(\"id\") // 获取URL参数\n\tid, err := strconv.ParseInt(idStr, 10, 64)\n\tif err != nil {\n\t\tResponseError(c, CodeInvalidParam)\n\t\treturn\n\t}\n\n\t// 2. 根据id获取社区详情\n\tdata, err := logic.GetCommunityDetail(id)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetCommunityList() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy) // 不轻易把服务端报错暴露给外面\n\t\treturn\n\t}\n\tResponseSuccess(c, data)\n}\n"
  },
  {
    "path": "lesson68/bluebell/controller/post.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/logic\"\n\t\"bluebell/models\"\n\t\"strconv\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"go.uber.org/zap\"\n)\n\n// CreatePostHandler 创建帖子的处理函数\nfunc CreatePostHandler(c *gin.Context) {\n\t// 1. 获取参数及参数的校验\n\t//c.ShouldBindJSON()  // validator --> binding tag\n\tp := new(models.Post)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\tzap.L().Debug(\"c.ShouldBindJSON(p) error\", zap.Any(\"err\", err))\n\t\tzap.L().Error(\"create post with invalid param\")\n\t\tResponseError(c, CodeInvalidParam)\n\t\treturn\n\t}\n\t// 从 c 取到当前发请求的用户的ID\n\tuserID, err := getCurrentUserID(c)\n\tif err != nil {\n\t\tResponseError(c, CodeNeedLogin)\n\t\treturn\n\t}\n\tp.AuthorID = userID\n\t// 2. 创建帖子\n\tif err := logic.CreatePost(p); err != nil {\n\t\tzap.L().Error(\"logic.CreatePost(p) failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\n\t// 3. 返回响应\n\tResponseSuccess(c, nil)\n}\n\n// GetPostDetailHandler 获取帖子详情的处理函数\nfunc GetPostDetailHandler(c *gin.Context) {\n\t// 1. 获取参数（从URL中获取帖子的id）\n\tpidStr := c.Param(\"id\")\n\tpid, err := strconv.ParseInt(pidStr, 10, 64)\n\tif err != nil {\n\t\tzap.L().Error(\"get post detail with invalid param\", zap.Error(err))\n\t\tResponseError(c, CodeInvalidParam)\n\t\treturn\n\t}\n\n\t// 2. 根据id取出帖子数据（查数据库）\n\tdata, err := logic.GetPostById(pid)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetPostById(pid) failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\t// 3. 返回响应\n\tResponseSuccess(c, data)\n}\n\n// GetPostListHandler 获取帖子列表的处理函数\nfunc GetPostListHandler(c *gin.Context) {\n\t// 获取分页参数\n\tpage, size := getPageInfo(c)\n\t// 获取数据\n\tdata, err := logic.GetPostList(page, size)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetPostList() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\tResponseSuccess(c, data)\n\t// 返回响应\n}\n\n// GetPostListHandler2 升级版帖子列表接口\n// 根据前端传来的参数动态的获取帖子列表\n// 按创建时间排序 或者 按照 分数排序\n// 1. 获取请求的query string参数\n// 2. 去redis查询id列表\n// 3. 根据id去数据库查询帖子详细信息\nfunc GetPostListHandler2(c *gin.Context) {\n\t// GET请求参数(query string)：/api/v1/posts2?page=1&size=10&order=time\n\t// 初始化结构体时指定初始参数\n\tp := &models.ParamPostList{\n\t\tPage:  1,\n\t\tSize:  10,\n\t\tOrder: models.OrderTime, // magic string\n\t}\n\t//c.ShouldBind()  根据请求的数据类型选择相应的方法去获取数据\n\t//c.ShouldBindJSON() 如果请求中携带的是json格式的数据，才能用这个方法获取到数据\n\tif err := c.ShouldBindQuery(p); err != nil {\n\t\tzap.L().Error(\"GetPostListHandler2 with invalid params\", zap.Error(err))\n\t\tResponseError(c, CodeInvalidParam)\n\t\treturn\n\t}\n\tdata, err := logic.GetPostListNew(p) // 更新：合二为一\n\t// 获取数据\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetPostList() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\tResponseSuccess(c, data)\n\t// 返回响应\n}\n\n// 根据社区去查询帖子列表\n//func GetCommunityPostListHandler(c *gin.Context) {\n//\t// 初始化结构体时指定初始参数\n//\tp := &models.ParamCommunityPostList{\n//\t\tParamPostList: &models.ParamPostList{\n//\t\t\tPage:  1,\n//\t\t\tSize:  10,\n//\t\t\tOrder: models.OrderTime,\n//\t\t},\n//\t}\n//\t//c.ShouldBind()  根据请求的数据类型选择相应的方法去获取数据\n//\t//c.ShouldBindJSON() 如果请求中携带的是json格式的数据，才能用这个方法获取到数据\n//\tif err := c.ShouldBindQuery(p); err != nil {\n//\t\tzap.L().Error(\"GetCommunityPostListHandler with invalid params\", zap.Error(err))\n//\t\tResponseError(c, CodeInvalidParam)\n//\t\treturn\n//\t}\n//\n//\t// 获取数据\n//\tdata, err := logic.GetCommunityPostList(p)\n//\tif err != nil {\n//\t\tzap.L().Error(\"logic.GetPostList() failed\", zap.Error(err))\n//\t\tResponseError(c, CodeServerBusy)\n//\t\treturn\n//\t}\n//\tResponseSuccess(c, data)\n//}\n"
  },
  {
    "path": "lesson68/bluebell/controller/request.go",
    "content": "package controller\n\nimport (\n\t\"errors\"\n\t\"strconv\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\nconst CtxUserIDKey = \"userID\"\n\nvar ErrorUserNotLogin = errors.New(\"用户未登录\")\n\n// getCurrentUserID 获取当前登录的用户ID\nfunc getCurrentUserID(c *gin.Context) (userID int64, err error) {\n\tuid, ok := c.Get(CtxUserIDKey)\n\tif !ok {\n\t\terr = ErrorUserNotLogin\n\t\treturn\n\t}\n\tuserID, ok = uid.(int64)\n\tif !ok {\n\t\terr = ErrorUserNotLogin\n\t\treturn\n\t}\n\treturn\n}\n\nfunc getPageInfo(c *gin.Context) (int64, int64) {\n\tpageStr := c.Query(\"page\")\n\tsizeStr := c.Query(\"size\")\n\n\tvar (\n\t\tpage int64\n\t\tsize int64\n\t\terr  error\n\t)\n\n\tpage, err = strconv.ParseInt(pageStr, 10, 64)\n\tif err != nil {\n\t\tpage = 1\n\t}\n\tsize, err = strconv.ParseInt(sizeStr, 10, 64)\n\tif err != nil {\n\t\tsize = 10\n\t}\n\treturn page, size\n}\n"
  },
  {
    "path": "lesson68/bluebell/controller/response.go",
    "content": "package controller\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n/*\n{\n\t\"code\": 10000, // 程序中的错误码\n\t\"msg\": xx,     // 提示信息\n\t\"data\": {},    // 数据\n}\n\n*/\n\ntype ResponseData struct {\n\tCode ResCode     `json:\"code\"`\n\tMsg  interface{} `json:\"msg\"`\n\tData interface{} `json:\"data,omitempty\"`\n}\n\nfunc ResponseError(c *gin.Context, code ResCode) {\n\tc.JSON(http.StatusOK, &ResponseData{\n\t\tCode: code,\n\t\tMsg:  code.Msg(),\n\t\tData: nil,\n\t})\n}\n\nfunc ResponseErrorWithMsg(c *gin.Context, code ResCode, msg interface{}) {\n\tc.JSON(http.StatusOK, &ResponseData{\n\t\tCode: code,\n\t\tMsg:  msg,\n\t\tData: nil,\n\t})\n}\n\nfunc ResponseSuccess(c *gin.Context, data interface{}) {\n\tc.JSON(http.StatusOK, &ResponseData{\n\t\tCode: CodeSuccess,\n\t\tMsg:  CodeSuccess.Msg(),\n\t\tData: data,\n\t})\n}\n"
  },
  {
    "path": "lesson68/bluebell/controller/user.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/logic\"\n\t\"bluebell/models\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/go-playground/validator/v10\"\n\t\"go.uber.org/zap\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// SignUpHandler 处理注册请求的函数\nfunc SignUpHandler(c *gin.Context) {\n\t// 1. 获取参数和参数校验\n\tp := new(models.ParamSignUp)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\t// 请求参数有误，直接返回响应\n\t\tzap.L().Error(\"SignUp with invalid param\", zap.Error(err))\n\t\t// 判断err是不是validator.ValidationErrors 类型\n\t\terrs, ok := err.(validator.ValidationErrors)\n\t\tif !ok {\n\t\t\tResponseError(c, CodeInvalidParam)\n\t\t\treturn\n\t\t}\n\t\tResponseErrorWithMsg(c, CodeInvalidParam, removeTopStruct(errs.Translate(trans)))\n\t\treturn\n\t}\n\t// 2. 业务处理\n\tif err := logic.SignUp(p); err != nil {\n\t\tzap.L().Error(\"logic.SignUp failed\", zap.Error(err))\n\t\tif errors.Is(err, mysql.ErrorUserExist) {\n\t\t\tResponseError(c, CodeUserExist)\n\t\t\treturn\n\t\t}\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\t// 3. 返回响应\n\tResponseSuccess(c, nil)\n}\n\nfunc LoginHandler(c *gin.Context) {\n\t// 1.获取请求参数及参数校验\n\tp := new(models.ParamLogin)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\t// 请求参数有误，直接返回响应\n\t\tzap.L().Error(\"Login with invalid param\", zap.Error(err))\n\t\t// 判断err是不是validator.ValidationErrors 类型\n\t\terrs, ok := err.(validator.ValidationErrors)\n\t\tif !ok {\n\t\t\tResponseError(c, CodeInvalidParam)\n\t\t\treturn\n\t\t}\n\t\tResponseErrorWithMsg(c, CodeInvalidParam, removeTopStruct(errs.Translate(trans)))\n\t\treturn\n\t}\n\t// 2.业务逻辑处理\n\tuser, err := logic.Login(p)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.Login failed\", zap.String(\"username\", p.Username), zap.Error(err))\n\t\tif errors.Is(err, mysql.ErrorUserNotExist) {\n\t\t\tResponseError(c, CodeUserNotExist)\n\t\t\treturn\n\t\t}\n\t\tResponseError(c, CodeInvalidPassword)\n\t\treturn\n\t}\n\n\t// 3.返回响应\n\tResponseSuccess(c, gin.H{\n\t\t\"user_id\":   fmt.Sprintf(\"%d\", user.UserID), // id值大于1<<53-1  int64类型的最大值是1<<63-1\n\t\t\"user_name\": user.Username,\n\t\t\"token\":     user.Token,\n\t})\n}\n"
  },
  {
    "path": "lesson68/bluebell/controller/validator.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/models\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin/binding\"\n\t\"github.com/go-playground/locales/en\"\n\t\"github.com/go-playground/locales/zh\"\n\tut \"github.com/go-playground/universal-translator\"\n\t\"github.com/go-playground/validator/v10\"\n\tenTranslations \"github.com/go-playground/validator/v10/translations/en\"\n\tzhTranslations \"github.com/go-playground/validator/v10/translations/zh\"\n)\n\n// 定义一个全局翻译器T\nvar trans ut.Translator\n\n// InitTrans 初始化翻译器\nfunc InitTrans(locale string) (err error) {\n\t// 修改gin框架中的Validator引擎属性，实现自定制\n\tif v, ok := binding.Validator.Engine().(*validator.Validate); ok {\n\n\t\t// 注册一个获取json tag的自定义方法\n\t\tv.RegisterTagNameFunc(func(fld reflect.StructField) string {\n\t\t\tname := strings.SplitN(fld.Tag.Get(\"json\"), \",\", 2)[0]\n\t\t\tif name == \"-\" {\n\t\t\t\treturn \"\"\n\t\t\t}\n\t\t\treturn name\n\t\t})\n\n\t\t// 为SignUpParam注册自定义校验方法\n\t\tv.RegisterStructValidation(SignUpParamStructLevelValidation, models.ParamSignUp{})\n\n\t\tzhT := zh.New() // 中文翻译器\n\t\tenT := en.New() // 英文翻译器\n\n\t\t// 第一个参数是备用（fallback）的语言环境\n\t\t// 后面的参数是应该支持的语言环境（支持多个）\n\t\t// uni := ut.New(zhT, zhT) 也是可以的\n\t\tuni := ut.New(enT, zhT, enT)\n\n\t\t// locale 通常取决于 http 请求头的 'Accept-Language'\n\t\tvar ok bool\n\t\t// 也可以使用 uni.FindTranslator(...) 传入多个locale进行查找\n\t\ttrans, ok = uni.GetTranslator(locale)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"uni.GetTranslator(%s) failed\", locale)\n\t\t}\n\n\t\t// 注册翻译器\n\t\tswitch locale {\n\t\tcase \"en\":\n\t\t\terr = enTranslations.RegisterDefaultTranslations(v, trans)\n\t\tcase \"zh\":\n\t\t\terr = zhTranslations.RegisterDefaultTranslations(v, trans)\n\t\tdefault:\n\t\t\terr = enTranslations.RegisterDefaultTranslations(v, trans)\n\t\t}\n\t\treturn\n\t}\n\treturn\n}\n\n// removeTopStruct 去除提示信息中的结构体名称\nfunc removeTopStruct(fields map[string]string) map[string]string {\n\tres := map[string]string{}\n\tfor field, err := range fields {\n\t\tres[field[strings.Index(field, \".\")+1:]] = err\n\t}\n\treturn res\n}\n\n// SignUpParamStructLevelValidation 自定义SignUpParam结构体校验函数\nfunc SignUpParamStructLevelValidation(sl validator.StructLevel) {\n\tsu := sl.Current().Interface().(models.ParamSignUp)\n\n\tif su.Password != su.RePassword {\n\t\t// 输出错误提示信息，最后一个参数就是传递的param\n\t\tsl.ReportError(su.RePassword, \"re_password\", \"RePassword\", \"eqfield\", \"password\")\n\t}\n}\n"
  },
  {
    "path": "lesson68/bluebell/controller/vote.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/logic\"\n\t\"bluebell/models\"\n\n\t\"go.uber.org/zap\"\n\n\t\"github.com/go-playground/validator/v10\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// 投票\n\n//type VoteData struct {\n//\t// UserID 从请求中获取当前的用户\n//\tPostID    int64 `json:\"post_id,string\"`   // 贴子id\n//\tDirection int   `json:\"direction,string\"` // 赞成票(1)还是反对票(-1)\n//}\n\nfunc PostVoteController(c *gin.Context) {\n\t// 参数校验\n\tp := new(models.ParamVoteData)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\terrs, ok := err.(validator.ValidationErrors) // 类型断言\n\t\tif !ok {\n\t\t\tResponseError(c, CodeInvalidParam)\n\t\t\treturn\n\t\t}\n\t\terrData := removeTopStruct(errs.Translate(trans)) // 翻译并去除掉错误提示中的结构体标识\n\t\tResponseErrorWithMsg(c, CodeInvalidParam, errData)\n\t\treturn\n\t}\n\t// 获取当前请求的用户的id\n\tuserID, err := getCurrentUserID(c)\n\tif err != nil {\n\t\tResponseError(c, CodeNeedLogin)\n\t\treturn\n\t}\n\t// 具体投票的业务逻辑\n\tif err := logic.VoteForPost(userID, p); err != nil {\n\t\tzap.L().Error(\"logic.VoteForPost() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\n\tResponseSuccess(c, nil)\n}\n"
  },
  {
    "path": "lesson68/bluebell/dao/mysql/community.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/models\"\n\t\"database/sql\"\n\n\t\"go.uber.org/zap\"\n)\n\nfunc GetCommunityList() (communityList []*models.Community, err error) {\n\tsqlStr := \"select community_id, community_name from community\"\n\tif err := db.Select(&communityList, sqlStr); err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\tzap.L().Warn(\"there is no community in db\")\n\t\t\terr = nil\n\t\t}\n\t}\n\treturn\n}\n\n// GetCommunityDetailByID 根据ID查询社区详情\nfunc GetCommunityDetailByID(id int64) (community *models.CommunityDetail, err error) {\n\tcommunity = new(models.CommunityDetail)\n\tsqlStr := `select \n\t\t\tcommunity_id, community_name, introduction, create_time\n\t\t\tfrom community \n\t\t\twhere community_id = ?\n\t`\n\tif err := db.Get(community, sqlStr, id); err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\terr = ErrorInvalidID\n\t\t}\n\t}\n\treturn community, err\n}\n"
  },
  {
    "path": "lesson68/bluebell/dao/mysql/error_code.go",
    "content": "package mysql\n\nimport \"errors\"\n\nvar (\n\tErrorUserExist       = errors.New(\"用户已存在\")\n\tErrorUserNotExist    = errors.New(\"用户不存在\")\n\tErrorInvalidPassword = errors.New(\"用户名或密码错误\")\n\tErrorInvalidID       = errors.New(\"无效的ID\")\n)\n"
  },
  {
    "path": "lesson68/bluebell/dao/mysql/mysql.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/setting\"\n\t\"fmt\"\n\n\t_ \"github.com/go-sql-driver/mysql\"\n\t\"github.com/jmoiron/sqlx\"\n)\n\nvar db *sqlx.DB\n\n// Init 初始化MySQL连接\nfunc Init(cfg *setting.MySQLConfig) (err error) {\n\t// \"user:password@tcp(host:port)/dbname\"\n\tdsn := fmt.Sprintf(\"%s:%s@tcp(%s:%d)/%s?parseTime=true&loc=Local\", cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.DB)\n\tdb, err = sqlx.Connect(\"mysql\", dsn)\n\tif err != nil {\n\t\treturn\n\t}\n\tdb.SetMaxOpenConns(cfg.MaxOpenConns)\n\tdb.SetMaxIdleConns(cfg.MaxIdleConns)\n\treturn\n}\n\n// Close 关闭MySQL连接\nfunc Close() {\n\t_ = db.Close()\n}\n"
  },
  {
    "path": "lesson68/bluebell/dao/mysql/post.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/models\"\n\t\"strings\"\n\n\t\"github.com/jmoiron/sqlx\"\n)\n\n// CreatePost 创建帖子\nfunc CreatePost(p *models.Post) (err error) {\n\tsqlStr := `insert into post(\n\tpost_id, title, content, author_id, community_id)\n\tvalues (?, ?, ?, ?, ?)\n\t`\n\t_, err = db.Exec(sqlStr, p.ID, p.Title, p.Content, p.AuthorID, p.CommunityID)\n\treturn\n}\n\n// GetPostById 根据id查询单个贴子数据\nfunc GetPostById(pid int64) (post *models.Post, err error) {\n\tpost = new(models.Post)\n\tsqlStr := `select\n\tpost_id, title, content, author_id, community_id, create_time\n\tfrom post\n\twhere post_id = ?\n\t`\n\terr = db.Get(post, sqlStr, pid)\n\treturn\n}\n\n// GetPostList 查询帖子列表函数\nfunc GetPostList(page, size int64) (posts []*models.Post, err error) {\n\tsqlStr := `select \n\tpost_id, title, content, author_id, community_id, create_time\n\tfrom post\n\tORDER BY create_time\n\tDESC\n\tlimit ?,?\n\t`\n\tposts = make([]*models.Post, 0, 2) // 不要写成make([]*models.Post, 2)\n\terr = db.Select(&posts, sqlStr, (page-1)*size, size)\n\treturn\n}\n\n// GetPostListByIDs 根据给定的id列表查询帖子数据\nfunc GetPostListByIDs(ids []string) (postList []*models.Post, err error) {\n\tsqlStr := `select post_id, title, content, author_id, community_id, create_time\n\tfrom post\n\twhere post_id in (?)\n\torder by FIND_IN_SET(post_id, ?)\n\t`\n\t// https: //www.liwenzhou.com/posts/Go/sqlx/\n\tquery, args, err := sqlx.In(sqlStr, ids, strings.Join(ids, \",\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tquery = db.Rebind(query)\n\terr = db.Select(&postList, query, args...) // !!!!!!\n\treturn\n}\n"
  },
  {
    "path": "lesson68/bluebell/dao/mysql/user.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/models\"\n\t\"crypto/md5\"\n\t\"database/sql\"\n\t\"encoding/hex\"\n)\n\n// 把每一步数据库操作封装成函数\n// 待logic层根据业务需求调用\n\nconst secret = \"liwenzhou.com\"\n\n// CheckUserExist 检查指定用户名的用户是否存在\nfunc CheckUserExist(username string) (err error) {\n\tsqlStr := `select count(user_id) from user where username = ?`\n\tvar count int64\n\tif err := db.Get(&count, sqlStr, username); err != nil {\n\t\treturn err\n\t}\n\tif count > 0 {\n\t\treturn ErrorUserExist\n\t}\n\treturn\n}\n\n// InsertUser 想数据库中插入一条新的用户记录\nfunc InsertUser(user *models.User) (err error) {\n\t// 对密码进行加密\n\tuser.Password = encryptPassword(user.Password)\n\t// 执行SQL语句入库\n\tsqlStr := `insert into user(user_id, username, password) values(?,?,?)`\n\t_, err = db.Exec(sqlStr, user.UserID, user.Username, user.Password)\n\treturn\n}\n\n// encryptPassword 密码加密\nfunc encryptPassword(oPassword string) string {\n\th := md5.New()\n\th.Write([]byte(secret))\n\treturn hex.EncodeToString(h.Sum([]byte(oPassword)))\n}\n\nfunc Login(user *models.User) (err error) {\n\toPassword := user.Password // 用户登录的密码\n\tsqlStr := `select user_id, username, password from user where username=?`\n\terr = db.Get(user, sqlStr, user.Username)\n\tif err == sql.ErrNoRows {\n\t\treturn ErrorUserNotExist\n\t}\n\tif err != nil {\n\t\t// 查询数据库失败\n\t\treturn err\n\t}\n\t// 判断密码是否正确\n\tpassword := encryptPassword(oPassword)\n\tif password != user.Password {\n\t\treturn ErrorInvalidPassword\n\t}\n\treturn\n}\n\n// GetUserById 根据id获取用户信息\nfunc GetUserById(uid int64) (user *models.User, err error) {\n\tuser = new(models.User)\n\tsqlStr := `select user_id, username from user where user_id = ?`\n\terr = db.Get(user, sqlStr, uid)\n\treturn\n}\n"
  },
  {
    "path": "lesson68/bluebell/dao/redis/keys.go",
    "content": "package redis\n\n// redis key\n\n// redis key注意使用命名空间的方式,方便查询和拆分\n\nconst (\n\tPrefix             = \"bluebell:\"   // 项目key前缀\n\tKeyPostTimeZSet    = \"post:time\"   // zset;贴子及发帖时间\n\tKeyPostScoreZSet   = \"post:score\"  // zset;贴子及投票的分数\n\tKeyPostVotedZSetPF = \"post:voted:\" // zset;记录用户及投票类型;参数是post id\n\n\tKeyCommunitySetPF = \"community:\" // set;保存每个分区下帖子的id\n)\n\n// 给redis key加上前缀\nfunc getRedisKey(key string) string {\n\treturn Prefix + key\n}\n"
  },
  {
    "path": "lesson68/bluebell/dao/redis/post.go",
    "content": "package redis\n\nimport (\n\t\"bluebell/models\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/go-redis/redis\"\n)\n\nfunc getIDsFormKey(key string, page, size int64) ([]string, error) {\n\tstart := (page - 1) * size\n\tend := start + size - 1\n\t// 3. ZREVRANGE 按分数从大到小的顺序查询指定数量的元素\n\treturn client.ZRevRange(key, start, end).Result()\n}\n\nfunc GetPostIDsInOrder(p *models.ParamPostList) ([]string, error) {\n\t// 从redis获取id\n\t// 1. 根据用户请求中携带的order参数确定要查询的redis key\n\tkey := getRedisKey(KeyPostTimeZSet)\n\tif p.Order == models.OrderScore {\n\t\tkey = getRedisKey(KeyPostScoreZSet)\n\t}\n\t// 2. 确定查询的索引起始点\n\treturn getIDsFormKey(key, p.Page, p.Size)\n}\n\n// GetPostVoteData 根据ids查询每篇帖子的投赞成票的数据\nfunc GetPostVoteData(ids []string) (data []int64, err error) {\n\t//data = make([]int64, 0, len(ids))\n\t//for _, id := range ids {\n\t//\tkey := getRedisKey(KeyPostVotedZSetPF + id)\n\t//\t// 查找key中分数是1的元素的数量->统计每篇帖子的赞成票的数量\n\t//\tv := client.ZCount(key, \"1\", \"1\").Val()\n\t//\tdata = append(data, v)\n\t//}\n\t// 使用pipeline一次发送多条命令,减少RTT\n\tpipeline := client.Pipeline()\n\tfor _, id := range ids {\n\t\tkey := getRedisKey(KeyPostVotedZSetPF + id)\n\t\tpipeline.ZCount(key, \"1\", \"1\")\n\t}\n\tcmders, err := pipeline.Exec()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdata = make([]int64, 0, len(cmders))\n\tfor _, cmder := range cmders {\n\t\tv := cmder.(*redis.IntCmd).Val()\n\t\tdata = append(data, v)\n\t}\n\treturn\n}\n\n// GetCommunityPostIDsInOrder 按社区查询ids\nfunc GetCommunityPostIDsInOrder(p *models.ParamCommunityPostList) ([]string, error) {\n\n\torderKey := getRedisKey(KeyPostTimeZSet)\n\tif p.Order == models.OrderScore {\n\t\torderKey = getRedisKey(KeyPostScoreZSet)\n\t}\n\n\t// 使用 zinterstore 把分区的帖子set与帖子分数的 zset 生成一个新的zset\n\t// 针对新的zset 按之前的逻辑取数据\n\n\t// 社区的key\n\tcKey := getRedisKey(KeyCommunitySetPF + strconv.Itoa(int(p.CommunityID)))\n\n\t// 利用缓存key减少zinterstore执行的次数\n\tkey := orderKey + strconv.Itoa(int(p.CommunityID))\n\tif client.Exists(key).Val() < 1 {\n\t\t// 不存在，需要计算\n\t\tpipeline := client.Pipeline()\n\t\tpipeline.ZInterStore(key, redis.ZStore{\n\t\t\tAggregate: \"MAX\",\n\t\t}, cKey, orderKey) // zinterstore 计算\n\t\tpipeline.Expire(key, 60*time.Second) // 设置超时时间\n\t\t_, err := pipeline.Exec()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\t// 存在的话就直接根据key查询ids\n\treturn getIDsFormKey(key, p.Page, p.Size)\n}\n"
  },
  {
    "path": "lesson68/bluebell/dao/redis/redis.go",
    "content": "package redis\n\nimport (\n\t\"bluebell/setting\"\n\t\"fmt\"\n\n\t\"github.com/go-redis/redis\"\n)\n\nvar (\n\tclient *redis.Client\n\tNil    = redis.Nil\n)\n\n// Init 初始化连接\nfunc Init(cfg *setting.RedisConfig) (err error) {\n\tclient = redis.NewClient(&redis.Options{\n\t\tAddr:         fmt.Sprintf(\"%s:%d\", cfg.Host, cfg.Port),\n\t\tPassword:     cfg.Password, // no password set\n\t\tDB:           cfg.DB,       // use default DB\n\t\tPoolSize:     cfg.PoolSize,\n\t\tMinIdleConns: cfg.MinIdleConns,\n\t})\n\n\t_, err = client.Ping().Result()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc Close() {\n\t_ = client.Close()\n}\n"
  },
  {
    "path": "lesson68/bluebell/dao/redis/vote.go",
    "content": "package redis\n\nimport (\n\t\"errors\"\n\t\"math\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/go-redis/redis\"\n)\n\n// 推荐阅读\n// 基于用户投票的相关算法：http://www.ruanyifeng.com/blog/algorithm/\n\n// 本项目使用简化版的投票分数\n// 投一票就加432分   86400/200  --> 200张赞成票可以给你的帖子续一天\n\n/* 投票的几种情况：\n   direction=1时，有两种情况：\n   \t1. 之前没有投过票，现在投赞成票    --> 更新分数和投票记录  差值的绝对值：1  +432\n   \t2. 之前投反对票，现在改投赞成票    --> 更新分数和投票记录  差值的绝对值：2  +432*2\n   direction=0时，有两种情况：\n   \t1. 之前投过反对票，现在要取消投票  --> 更新分数和投票记录  差值的绝对值：1  +432\n\t2. 之前投过赞成票，现在要取消投票  --> 更新分数和投票记录  差值的绝对值：1  -432\n   direction=-1时，有两种情况：\n   \t1. 之前没有投过票，现在投反对票    --> 更新分数和投票记录  差值的绝对值：1  -432\n   \t2. 之前投赞成票，现在改投反对票    --> 更新分数和投票记录  差值的绝对值：2  -432*2\n\n   投票的限制：\n   每个贴子自发表之日起一个星期之内允许用户投票，超过一个星期就不允许再投票了。\n   \t1. 到期之后将redis中保存的赞成票数及反对票数存储到mysql表中\n   \t2. 到期之后删除那个 KeyPostVotedZSetPF\n*/\n\nconst (\n\toneWeekInSeconds = 7 * 24 * 3600\n\tscorePerVote     = 432 // 每一票值多少分\n)\n\nvar (\n\tErrVoteTimeExpire = errors.New(\"投票时间已过\")\n\tErrVoteRepeated   = errors.New(\"不允许重复投票\")\n)\n\nfunc CreatePost(postID, communityID int64) error {\n\tpipeline := client.TxPipeline()\n\t// 帖子时间\n\tpipeline.ZAdd(getRedisKey(KeyPostTimeZSet), redis.Z{\n\t\tScore:  float64(time.Now().Unix()),\n\t\tMember: postID,\n\t})\n\n\t// 帖子分数\n\tpipeline.ZAdd(getRedisKey(KeyPostScoreZSet), redis.Z{\n\t\tScore:  float64(time.Now().Unix()),\n\t\tMember: postID,\n\t})\n\t// 更新：把帖子id加到社区的set\n\tcKey := getRedisKey(KeyCommunitySetPF + strconv.Itoa(int(communityID)))\n\tpipeline.SAdd(cKey, postID)\n\t_, err := pipeline.Exec()\n\treturn err\n}\n\nfunc VoteForPost(userID, postID string, value float64) error {\n\t// 1. 判断投票限制\n\t// 去redis取帖子发布时间\n\tpostTime := client.ZScore(getRedisKey(KeyPostTimeZSet), postID).Val()\n\tif float64(time.Now().Unix())-postTime > oneWeekInSeconds {\n\t\treturn ErrVoteTimeExpire\n\t}\n\t// 2和3需要放到一个pipeline事务中操作\n\n\t// 2. 更新贴子的分数\n\t// 先查当前用户给当前帖子的投票记录\n\tov := client.ZScore(getRedisKey(KeyPostVotedZSetPF+postID), userID).Val()\n\n\t// 更新：如果这一次投票的值和之前保存的值一致，就提示不允许重复投票\n\tif value == ov {\n\t\treturn ErrVoteRepeated\n\t}\n\tvar op float64\n\tif value > ov {\n\t\top = 1\n\t} else {\n\t\top = -1\n\t}\n\tdiff := math.Abs(ov - value) // 计算两次投票的差值\n\tpipeline := client.TxPipeline()\n\tpipeline.ZIncrBy(getRedisKey(KeyPostScoreZSet), op*diff*scorePerVote, postID)\n\n\t// 3. 记录用户为该贴子投票的数据\n\tif value == 0 {\n\t\tpipeline.ZRem(getRedisKey(KeyPostVotedZSetPF+postID), userID)\n\t} else {\n\t\tpipeline.ZAdd(getRedisKey(KeyPostVotedZSetPF+postID), redis.Z{\n\t\t\tScore:  value, // 赞成票还是反对票\n\t\t\tMember: userID,\n\t\t})\n\t}\n\t_, err := pipeline.Exec()\n\treturn err\n}\n"
  },
  {
    "path": "lesson68/bluebell/go.mod",
    "content": "module bluebell\n\ngo 1.14\n\nrequire (\n\tgithub.com/bwmarrin/snowflake v0.3.0\n\tgithub.com/dgrijalva/jwt-go v3.2.0+incompatible\n\tgithub.com/fsnotify/fsnotify v1.4.9\n\tgithub.com/gin-gonic/gin v1.6.3\n\tgithub.com/go-playground/locales v0.13.0\n\tgithub.com/go-playground/universal-translator v0.17.0\n\tgithub.com/go-playground/validator/v10 v10.2.0\n\tgithub.com/go-redis/redis v6.15.8+incompatible\n\tgithub.com/go-sql-driver/mysql v1.5.0\n\tgithub.com/jmoiron/sqlx v1.2.0\n\tgithub.com/natefinch/lumberjack v2.0.0+incompatible\n\tgithub.com/onsi/ginkgo v1.14.0 // indirect\n\tgithub.com/sony/sonyflake v1.0.0 // indirect\n\tgithub.com/spf13/viper v1.7.0\n\tgo.uber.org/zap v1.15.0\n\tgopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect\n)\n"
  },
  {
    "path": "lesson68/bluebell/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ngithub.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=\ngithub.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=\ngithub.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=\ngithub.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=\ngithub.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=\ngithub.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=\ngithub.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=\ngithub.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0=\ngithub.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=\ngithub.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=\ngithub.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=\ngithub.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=\ngithub.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=\ngithub.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=\ngithub.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=\ngithub.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=\ngithub.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=\ngithub.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=\ngithub.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=\ngithub.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=\ngithub.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=\ngithub.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=\ngithub.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=\ngithub.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=\ngithub.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=\ngithub.com/go-redis/redis v6.15.8+incompatible h1:BKZuG6mCnRj5AOaWJXoCgf6rqTYnYJLe4en2hxT7r9o=\ngithub.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=\ngithub.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=\ngithub.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=\ngithub.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=\ngithub.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=\ngithub.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=\ngithub.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=\ngithub.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=\ngithub.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=\ngithub.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=\ngithub.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=\ngithub.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=\ngithub.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=\ngithub.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=\ngithub.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=\ngithub.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=\ngithub.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=\ngithub.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=\ngithub.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=\ngithub.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=\ngithub.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=\ngithub.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=\ngithub.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=\ngithub.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=\ngithub.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=\ngithub.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=\ngithub.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=\ngithub.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=\ngithub.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=\ngithub.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=\ngithub.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=\ngithub.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=\ngithub.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=\ngithub.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=\ngithub.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=\ngithub.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=\ngithub.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=\ngithub.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=\ngithub.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=\ngithub.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=\ngithub.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=\ngithub.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=\ngithub.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=\ngithub.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=\ngithub.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=\ngithub.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=\ngithub.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=\ngithub.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=\ngithub.com/sony/sonyflake v1.0.0 h1:MpU6Ro7tfXwgn2l5eluf9xQvQJDROTBImNCfRXn/YeM=\ngithub.com/sony/sonyflake v1.0.0/go.mod h1:Jv3cfhf/UFtolOTTRd3q4Nl6ENqM+KfyZ5PseKfZGF4=\ngithub.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=\ngithub.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=\ngithub.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=\ngithub.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=\ngithub.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=\ngithub.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=\ngithub.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=\ngithub.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=\ngithub.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=\ngithub.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=\ngithub.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=\ngithub.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=\ngithub.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=\ngithub.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=\ngithub.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=\ngo.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=\ngo.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=\ngo.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=\ngo.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=\ngo.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngo.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=\ngo.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc h1:NCy3Ohtk6Iny5V/reW2Ktypo4zIpWBdRJ1uFMjBxdg8=\ngolang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=\ngopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=\ngopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=\ngopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\n"
  },
  {
    "path": "lesson68/bluebell/logger/logger.go",
    "content": "package logger\n\nimport (\n\t\"bluebell/setting\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"os\"\n\t\"runtime/debug\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/natefinch/lumberjack\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\nvar lg *zap.Logger\n\n// Init 初始化lg\nfunc Init(cfg *setting.LogConfig, mode string) (err error) {\n\twriteSyncer := getLogWriter(cfg.Filename, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge)\n\tencoder := getEncoder()\n\tvar l = new(zapcore.Level)\n\terr = l.UnmarshalText([]byte(cfg.Level))\n\tif err != nil {\n\t\treturn\n\t}\n\tvar core zapcore.Core\n\tif mode == \"dev\" {\n\t\t// 进入开发模式，日志输出到终端\n\t\tconsoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())\n\t\tcore = zapcore.NewTee(\n\t\t\tzapcore.NewCore(encoder, writeSyncer, l),\n\t\t\tzapcore.NewCore(consoleEncoder, zapcore.Lock(os.Stdout), zapcore.DebugLevel),\n\t\t)\n\t} else {\n\t\tcore = zapcore.NewCore(encoder, writeSyncer, l)\n\t}\n\n\tlg = zap.New(core, zap.AddCaller())\n\n\tzap.ReplaceGlobals(lg)\n\tzap.L().Info(\"init logger success\")\n\treturn\n}\n\nfunc getEncoder() zapcore.Encoder {\n\tencoderConfig := zap.NewProductionEncoderConfig()\n\tencoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder\n\tencoderConfig.TimeKey = \"time\"\n\tencoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder\n\tencoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder\n\tencoderConfig.EncodeCaller = zapcore.ShortCallerEncoder\n\treturn zapcore.NewJSONEncoder(encoderConfig)\n}\n\nfunc getLogWriter(filename string, maxSize, maxBackup, maxAge int) zapcore.WriteSyncer {\n\tlumberJackLogger := &lumberjack.Logger{\n\t\tFilename:   filename,\n\t\tMaxSize:    maxSize,\n\t\tMaxBackups: maxBackup,\n\t\tMaxAge:     maxAge,\n\t}\n\treturn zapcore.AddSync(lumberJackLogger)\n}\n\n// GinLogger 接收gin框架默认的日志\nfunc GinLogger() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tstart := time.Now()\n\t\tpath := c.Request.URL.Path\n\t\tquery := c.Request.URL.RawQuery\n\t\tc.Next()\n\n\t\tcost := time.Since(start)\n\t\tlg.Info(path,\n\t\t\tzap.Int(\"status\", c.Writer.Status()),\n\t\t\tzap.String(\"method\", c.Request.Method),\n\t\t\tzap.String(\"path\", path),\n\t\t\tzap.String(\"query\", query),\n\t\t\tzap.String(\"ip\", c.ClientIP()),\n\t\t\tzap.String(\"user-agent\", c.Request.UserAgent()),\n\t\t\tzap.String(\"errors\", c.Errors.ByType(gin.ErrorTypePrivate).String()),\n\t\t\tzap.Duration(\"cost\", cost),\n\t\t)\n\t}\n}\n\n// GinRecovery recover掉项目可能出现的panic，并使用zap记录相关日志\nfunc GinRecovery(stack bool) gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tdefer func() {\n\t\t\tif err := recover(); err != nil {\n\t\t\t\t// Check for a broken connection, as it is not really a\n\t\t\t\t// condition that warrants a panic stack trace.\n\t\t\t\tvar brokenPipe bool\n\t\t\t\tif ne, ok := err.(*net.OpError); ok {\n\t\t\t\t\tif se, ok := ne.Err.(*os.SyscallError); ok {\n\t\t\t\t\t\tif strings.Contains(strings.ToLower(se.Error()), \"broken pipe\") || strings.Contains(strings.ToLower(se.Error()), \"connection reset by peer\") {\n\t\t\t\t\t\t\tbrokenPipe = true\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\thttpRequest, _ := httputil.DumpRequest(c.Request, false)\n\t\t\t\tif brokenPipe {\n\t\t\t\t\tlg.Error(c.Request.URL.Path,\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t)\n\t\t\t\t\t// If the connection is dead, we can't write a status to it.\n\t\t\t\t\tc.Error(err.(error)) // nolint: errcheck\n\t\t\t\t\tc.Abort()\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif stack {\n\t\t\t\t\tlg.Error(\"[Recovery from panic]\",\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t\tzap.String(\"stack\", string(debug.Stack())),\n\t\t\t\t\t)\n\t\t\t\t} else {\n\t\t\t\t\tlg.Error(\"[Recovery from panic]\",\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tc.AbortWithStatus(http.StatusInternalServerError)\n\t\t\t}\n\t\t}()\n\t\tc.Next()\n\t}\n}\n"
  },
  {
    "path": "lesson68/bluebell/logic/community.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/models\"\n)\n\nfunc GetCommunityList() ([]*models.Community, error) {\n\t// 查数据库 查找到所有的community 并返回\n\treturn mysql.GetCommunityList()\n}\n\nfunc GetCommunityDetail(id int64) (*models.CommunityDetail, error) {\n\treturn mysql.GetCommunityDetailByID(id)\n}\n"
  },
  {
    "path": "lesson68/bluebell/logic/post.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/dao/redis\"\n\t\"bluebell/models\"\n\t\"bluebell/pkg/snowflake\"\n\n\t\"go.uber.org/zap\"\n)\n\nfunc CreatePost(p *models.Post) (err error) {\n\t// 1. 生成post id\n\tp.ID = snowflake.GenID()\n\t// 2. 保存到数据库\n\terr = mysql.CreatePost(p)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = redis.CreatePost(p.ID, p.CommunityID)\n\treturn\n\t// 3. 返回\n}\n\n// GetPostById 根据帖子id查询帖子详情数据\nfunc GetPostById(pid int64) (data *models.ApiPostDetail, err error) {\n\t// 查询并组合我们接口想用的数据\n\tpost, err := mysql.GetPostById(pid)\n\tif err != nil {\n\t\tzap.L().Error(\"mysql.GetPostById(pid) failed\",\n\t\t\tzap.Int64(\"pid\", pid),\n\t\t\tzap.Error(err))\n\t\treturn\n\t}\n\t// 根据作者id查询作者信息\n\tuser, err := mysql.GetUserById(post.AuthorID)\n\tif err != nil {\n\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\tzap.Int64(\"author_id\", post.AuthorID),\n\t\t\tzap.Error(err))\n\t\treturn\n\t}\n\t// 根据社区id查询社区详细信息\n\tcommunity, err := mysql.GetCommunityDetailByID(post.CommunityID)\n\tif err != nil {\n\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\tzap.Int64(\"community_id\", post.CommunityID),\n\t\t\tzap.Error(err))\n\t\treturn\n\t}\n\t// 接口数据拼接\n\tdata = &models.ApiPostDetail{\n\t\tAuthorName:      user.Username,\n\t\tPost:            post,\n\t\tCommunityDetail: community,\n\t}\n\treturn\n}\n\n// GetPostList 获取帖子列表\nfunc GetPostList(page, size int64) (data []*models.ApiPostDetail, err error) {\n\tposts, err := mysql.GetPostList(page, size)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdata = make([]*models.ApiPostDetail, 0, len(posts))\n\n\tfor _, post := range posts {\n\t\t// 根据作者id查询作者信息\n\t\tuser, err := mysql.GetUserById(post.AuthorID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"author_id\", post.AuthorID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\t// 根据社区id查询社区详细信息\n\t\tcommunity, err := mysql.GetCommunityDetailByID(post.CommunityID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"community_id\", post.CommunityID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\tpostDetail := &models.ApiPostDetail{\n\t\t\tAuthorName:      user.Username,\n\t\t\tPost:            post,\n\t\t\tCommunityDetail: community,\n\t\t}\n\t\tdata = append(data, postDetail)\n\t}\n\treturn\n}\n\nfunc GetPostList2(p *models.ParamPostList) (data []*models.ApiPostDetail, err error) {\n\t// 2. 去redis查询id列表\n\tids, err := redis.GetPostIDsInOrder(p)\n\tif err != nil {\n\t\treturn\n\t}\n\tif len(ids) == 0 {\n\t\tzap.L().Warn(\"redis.GetPostIDsInOrder(p) return 0 data\")\n\t\treturn\n\t}\n\tzap.L().Debug(\"GetPostList2\", zap.Any(\"ids\", ids))\n\t// 3. 根据id去MySQL数据库查询帖子详细信息\n\t// 返回的数据还要按照我给定的id的顺序返回\n\tposts, err := mysql.GetPostListByIDs(ids)\n\tif err != nil {\n\t\treturn\n\t}\n\tzap.L().Debug(\"GetPostList2\", zap.Any(\"posts\", posts))\n\t// 提前查询好每篇帖子的投票数\n\tvoteData, err := redis.GetPostVoteData(ids)\n\tif err != nil {\n\t\treturn\n\t}\n\n\t// 将帖子的作者及分区信息查询出来填充到帖子中\n\tfor idx, post := range posts {\n\t\t// 根据作者id查询作者信息\n\t\tuser, err := mysql.GetUserById(post.AuthorID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"author_id\", post.AuthorID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\t// 根据社区id查询社区详细信息\n\t\tcommunity, err := mysql.GetCommunityDetailByID(post.CommunityID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"community_id\", post.CommunityID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\tpostDetail := &models.ApiPostDetail{\n\t\t\tAuthorName:      user.Username,\n\t\t\tVoteNum:         voteData[idx],\n\t\t\tPost:            post,\n\t\t\tCommunityDetail: community,\n\t\t}\n\t\tdata = append(data, postDetail)\n\t}\n\treturn\n\n}\n\nfunc GetCommunityPostList(p *models.ParamPostList) (data []*models.ApiPostDetail, err error) {\n\t// 2. 去redis查询id列表\n\tids, err := redis.GetCommunityPostIDsInOrder(p)\n\tif err != nil {\n\t\treturn\n\t}\n\tif len(ids) == 0 {\n\t\tzap.L().Warn(\"redis.GetPostIDsInOrder(p) return 0 data\")\n\t\treturn\n\t}\n\tzap.L().Debug(\"GetCommunityPostIDsInOrder\", zap.Any(\"ids\", ids))\n\t// 3. 根据id去MySQL数据库查询帖子详细信息\n\t// 返回的数据还要按照我给定的id的顺序返回\n\tposts, err := mysql.GetPostListByIDs(ids)\n\tif err != nil {\n\t\treturn\n\t}\n\tzap.L().Debug(\"GetPostList2\", zap.Any(\"posts\", posts))\n\t// 提前查询好每篇帖子的投票数\n\tvoteData, err := redis.GetPostVoteData(ids)\n\tif err != nil {\n\t\treturn\n\t}\n\n\t// 将帖子的作者及分区信息查询出来填充到帖子中\n\tfor idx, post := range posts {\n\t\t// 根据作者id查询作者信息\n\t\tuser, err := mysql.GetUserById(post.AuthorID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"author_id\", post.AuthorID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\t// 根据社区id查询社区详细信息\n\t\tcommunity, err := mysql.GetCommunityDetailByID(post.CommunityID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"community_id\", post.CommunityID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\tpostDetail := &models.ApiPostDetail{\n\t\t\tAuthorName:      user.Username,\n\t\t\tVoteNum:         voteData[idx],\n\t\t\tPost:            post,\n\t\t\tCommunityDetail: community,\n\t\t}\n\t\tdata = append(data, postDetail)\n\t}\n\treturn\n}\n\n// GetPostListNew  将两个查询帖子列表逻辑合二为一的函数\nfunc GetPostListNew(p *models.ParamPostList) (data []*models.ApiPostDetail, err error) {\n\t// 根据请求参数的不同，执行不同的逻辑。\n\tif p.CommunityID == 0 {\n\t\t// 查所有\n\t\tdata, err = GetPostList2(p)\n\t} else {\n\t\t// 根据社区id查询\n\t\tdata, err = GetCommunityPostList(p)\n\t}\n\tif err != nil {\n\t\tzap.L().Error(\"GetPostListNew failed\", zap.Error(err))\n\t\treturn nil, err\n\t}\n\treturn\n}\n"
  },
  {
    "path": "lesson68/bluebell/logic/user.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/models\"\n\t\"bluebell/pkg/jwt\"\n\t\"bluebell/pkg/snowflake\"\n)\n\n// 存放业务逻辑的代码\n\nfunc SignUp(p *models.ParamSignUp) (err error) {\n\t// 1.判断用户存不存在\n\tif err := mysql.CheckUserExist(p.Username); err != nil {\n\t\treturn err\n\t}\n\t// 2.生成UID\n\tuserID := snowflake.GenID()\n\t// 构造一个User实例\n\tuser := &models.User{\n\t\tUserID:   userID,\n\t\tUsername: p.Username,\n\t\tPassword: p.Password,\n\t}\n\t// 3.保存进数据库\n\treturn mysql.InsertUser(user)\n}\n\nfunc Login(p *models.ParamLogin) (user *models.User, err error) {\n\tuser = &models.User{\n\t\tUsername: p.Username,\n\t\tPassword: p.Password,\n\t}\n\t// 传递的是指针，就能拿到user.UserID\n\tif err := mysql.Login(user); err != nil {\n\t\treturn nil, err\n\t}\n\t// 生成JWT\n\ttoken, err := jwt.GenToken(user.UserID, user.Username)\n\tif err != nil {\n\t\treturn\n\t}\n\tuser.Token = token\n\treturn\n}\n"
  },
  {
    "path": "lesson68/bluebell/logic/vote.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/redis\"\n\t\"bluebell/models\"\n\t\"strconv\"\n\n\t\"go.uber.org/zap\"\n)\n\n// 推荐阅读\n// 基于用户投票的相关算法：http://www.ruanyifeng.com/blog/algorithm/\n\n// 本项目使用简化版的投票分数\n// 投一票就加432分   86400/200  --> 200张赞成票可以给你的帖子续一天\n\n/* 投票的几种情况：\ndirection=1时，有两种情况：\n\t1. 之前没有投过票，现在投赞成票    --> 更新分数和投票记录\n\t2. 之前投反对票，现在改投赞成票    --> 更新分数和投票记录\ndirection=0时，有两种情况：\n\t1. 之前投过赞成票，现在要取消投票  --> 更新分数和投票记录\n\t2. 之前投过反对票，现在要取消投票  --> 更新分数和投票记录\ndirection=-1时，有两种情况：\n\t1. 之前没有投过票，现在投反对票    --> 更新分数和投票记录\n\t2. 之前投赞成票，现在改投反对票    --> 更新分数和投票记录\n\n投票的限制：\n每个贴子自发表之日起一个星期之内允许用户投票，超过一个星期就不允许再投票了。\n\t1. 到期之后将redis中保存的赞成票数及反对票数存储到mysql表中\n\t2. 到期之后删除那个 KeyPostVotedZSetPF\n*/\n\n// VoteForPost 为帖子投票的函数\nfunc VoteForPost(userID int64, p *models.ParamVoteData) error {\n\tzap.L().Debug(\"VoteForPost\",\n\t\tzap.Int64(\"userID\", userID),\n\t\tzap.String(\"postID\", p.PostID),\n\t\tzap.Int8(\"direction\", p.Direction))\n\treturn redis.VoteForPost(strconv.Itoa(int(userID)), p.PostID, float64(p.Direction))\n}\n"
  },
  {
    "path": "lesson68/bluebell/main.go",
    "content": "package main\n\nimport (\n\t\"bluebell/controller\"\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/dao/redis\"\n\t\"bluebell/logger\"\n\t\"bluebell/pkg/snowflake\"\n\t\"bluebell/router\"\n\t\"bluebell/setting\"\n\t\"fmt\"\n\t\"os\"\n)\n\nfunc main() {\n\tif len(os.Args) < 2 {\n\t\tfmt.Println(\"need config file.eg: bluebell config.yaml\")\n\t\treturn\n\t}\n\t// 加载配置\n\tif err := setting.Init(os.Args[1]); err != nil {\n\t\tfmt.Printf(\"load config failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tif err := logger.Init(setting.Conf.LogConfig, setting.Conf.Mode); err != nil {\n\t\tfmt.Printf(\"init logger failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tif err := mysql.Init(setting.Conf.MySQLConfig); err != nil {\n\t\tfmt.Printf(\"init mysql failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer mysql.Close() // 程序退出关闭数据库连接\n\tif err := redis.Init(setting.Conf.RedisConfig); err != nil {\n\t\tfmt.Printf(\"init redis failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer redis.Close()\n\n\tif err := snowflake.Init(setting.Conf.StartTime, setting.Conf.MachineID); err != nil {\n\t\tfmt.Printf(\"init snowflake failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\t// 初始化gin框架内置的校验器使用的翻译器\n\tif err := controller.InitTrans(\"zh\"); err != nil {\n\t\tfmt.Printf(\"init validator trans failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\t// 注册路由\n\tr := router.SetupRouter(setting.Conf.Mode)\n\terr := r.Run(fmt.Sprintf(\":%d\", setting.Conf.Port))\n\tif err != nil {\n\t\tfmt.Printf(\"run server failed, err:%v\\n\", err)\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "lesson68/bluebell/middlewares/auth.go",
    "content": "package middlewares\n\nimport (\n\t\"bluebell/controller\"\n\t\"bluebell/pkg/jwt\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// JWTAuthMiddleware 基于JWT的认证中间件\nfunc JWTAuthMiddleware() func(c *gin.Context) {\n\treturn func(c *gin.Context) {\n\t\t// 客户端携带Token有三种方式 1.放在请求头 2.放在请求体 3.放在URI\n\t\t// 这里假设Token放在Header的Authorization中，并使用Bearer开头\n\t\t// Authorization: Bearer xxxxxxx.xxx.xxx  / X-TOKEN: xxx.xxx.xx\n\t\t// 这里的具体实现方式要依据你的实际业务情况决定\n\t\tauthHeader := c.Request.Header.Get(\"Authorization\")\n\t\tif authHeader == \"\" {\n\t\t\tcontroller.ResponseError(c, controller.CodeNeedLogin)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// 按空格分割\n\t\tparts := strings.SplitN(authHeader, \" \", 2)\n\t\tif !(len(parts) == 2 && parts[0] == \"Bearer\") {\n\t\t\tcontroller.ResponseError(c, controller.CodeInvalidToken)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// parts[1]是获取到的tokenString，我们使用之前定义好的解析JWT的函数来解析它\n\t\tmc, err := jwt.ParseToken(parts[1])\n\t\tif err != nil {\n\t\t\tcontroller.ResponseError(c, controller.CodeInvalidToken)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// 将当前请求的userID信息保存到请求的上下文c上\n\t\tc.Set(controller.CtxUserIDKey, mc.UserID)\n\n\t\tc.Next() // 后续的处理请求的函数中 可以用过c.Get(CtxUserIDKey) 来获取当前请求的用户信息\n\t}\n}\n"
  },
  {
    "path": "lesson68/bluebell/models/community.go",
    "content": "package models\n\nimport \"time\"\n\ntype Community struct {\n\tID   int64  `json:\"id\" db:\"community_id\"`\n\tName string `json:\"name\" db:\"community_name\"`\n}\n\ntype CommunityDetail struct {\n\tID           int64     `json:\"id\" db:\"community_id\"`\n\tName         string    `json:\"name\" db:\"community_name\"`\n\tIntroduction string    `json:\"introduction,omitempty\" db:\"introduction\"`\n\tCreateTime   time.Time `json:\"create_time\" db:\"create_time\"`\n}\n"
  },
  {
    "path": "lesson68/bluebell/models/create_table.sql",
    "content": "--\nCREATE TABLE `user` (\n    `id` bigint(20) NOT NULL AUTO_INCREMENT,\n    `user_id` bigint(20) NOT NULL,\n    `username` varchar(64) COLLATE utf8mb4_general_ci NOT NULL,\n    `password` varchar(64) COLLATE utf8mb4_general_ci NOT NULL,\n    `email` varchar(64) COLLATE utf8mb4_general_ci,\n    `gender` tinyint(4) NOT NULL DEFAULT '0',\n    `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n    `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    PRIMARY KEY (`id`),\n    UNIQUE KEY `idx_username` (`username`) USING BTREE,\n    UNIQUE KEY `idx_user_id` (`user_id`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\n\n\nDROP TABLE IF EXISTS `community`;\nCREATE TABLE `community` (\n     `id` int(11) NOT NULL AUTO_INCREMENT,\n     `community_id` int(10) unsigned NOT NULL,\n     `community_name` varchar(128) COLLATE utf8mb4_general_ci NOT NULL,\n     `introduction` varchar(256) COLLATE utf8mb4_general_ci NOT NULL,\n     `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n     `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n     PRIMARY KEY (`id`),\n     UNIQUE KEY `idx_community_id` (`community_id`),\n     UNIQUE KEY `idx_community_name` (`community_name`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\n\n\nINSERT INTO `community` VALUES ('1', '1', 'Go', 'Golang', '2016-11-01 08:10:10', '2016-11-01 08:10:10');\nINSERT INTO `community` VALUES ('2', '2', 'leetcode', '刷题刷题刷题', '2020-01-01 08:00:00', '2020-01-01 08:00:00');\nINSERT INTO `community` VALUES ('3', '3', 'CS:GO', 'Rush B。。。', '2018-08-07 08:30:00', '2018-08-07 08:30:00');\nINSERT INTO `community` VALUES ('4', '4', 'LOL', '欢迎来到英雄联盟!', '2016-01-01 08:00:00', '2016-01-01 08:00:00');\n\nDROP TABLE IF EXISTS `post`;\nCREATE TABLE `post` (\n    `id` bigint(20) NOT NULL AUTO_INCREMENT,\n    `post_id` bigint(20) NOT NULL COMMENT '帖子id',\n    `title` varchar(128) COLLATE utf8mb4_general_ci NOT NULL COMMENT '标题',\n    `content` varchar(8192) COLLATE utf8mb4_general_ci NOT NULL COMMENT '内容',\n    `author_id` bigint(20) NOT NULL COMMENT '作者的用户id',\n    `community_id` bigint(20) NOT NULL COMMENT '所属社区',\n    `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '帖子状态',\n    `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n    `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n    PRIMARY KEY (`id`),\n    UNIQUE KEY `idx_post_id` (`post_id`),\n    KEY `idx_author_id` (`author_id`),\n    KEY `idx_community_id` (`community_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;"
  },
  {
    "path": "lesson68/bluebell/models/params.go",
    "content": "package models\n\n// 定义请求的参数结构体\n\nconst (\n\tOrderTime  = \"time\"\n\tOrderScore = \"score\"\n)\n\n// ParamSignUp 注册请求参数\ntype ParamSignUp struct {\n\tUsername   string `json:\"username\" binding:\"required\"`\n\tPassword   string `json:\"password\" binding:\"required\"`\n\tRePassword string `json:\"re_password\" binding:\"required,eqfield=Password\"`\n}\n\n// ParamLogin 登录请求参数\ntype ParamLogin struct {\n\tUsername string `json:\"username\" binding:\"required\"`\n\tPassword string `json:\"password\" binding:\"required\"`\n}\n\n// ParamVoteData 投票数据\ntype ParamVoteData struct {\n\t// UserID 从请求中获取当前的用户\n\tPostID    string `json:\"post_id\" binding:\"required\"`               // 贴子id\n\tDirection int8   `json:\"direction,string\" binding:\"oneof=1 0 -1\" ` // 赞成票(1)还是反对票(-1)取消投票(0)\n}\n\n// ParamPostList 获取帖子列表query string参数\ntype ParamPostList struct {\n\tCommunityID int64  `json:\"community_id\" form:\"community_id\"` // 可以为空\n\tPage        int64  `json:\"page\" form:\"page\"`\n\tSize        int64  `json:\"size\" form:\"size\"`\n\tOrder       string `json:\"order\" form:\"order\"`\n}\n"
  },
  {
    "path": "lesson68/bluebell/models/post.go",
    "content": "package models\n\nimport \"time\"\n\n// 内存对齐概念\n\ntype Post struct {\n\tID          int64     `json:\"id,string\" db:\"post_id\"`\n\tAuthorID    int64     `json:\"author_id\" db:\"author_id\"`\n\tCommunityID int64     `json:\"community_id\" db:\"community_id\" binding:\"required\"`\n\tStatus      int32     `json:\"status\" db:\"status\"`\n\tTitle       string    `json:\"title\" db:\"title\" binding:\"required\"`\n\tContent     string    `json:\"content\" db:\"content\" binding:\"required\"`\n\tCreateTime  time.Time `json:\"create_time\" db:\"create_time\"`\n}\n\n// ApiPostDetail 帖子详情接口的结构体\ntype ApiPostDetail struct {\n\tAuthorName       string             `json:\"author_name\"`\n\tVoteNum          int64              `json:\"vote_num\"`\n\t*Post                               // 嵌入帖子结构体\n\t*CommunityDetail `json:\"community\"` // 嵌入社区信息\n}\n"
  },
  {
    "path": "lesson68/bluebell/models/struct_test.go",
    "content": "package models\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"unsafe\"\n)\n\n// Go 内存对齐\n\ntype s1 struct {\n\ta int8   // 1\n\tb string // 3\n\tc int8   // 1\n}\n\ntype s2 struct {\n\ta int8\n\tc int8   // 1\n\tb string // 2\n}\n\nfunc TestStruct(t *testing.T) {\n\tv1 := s1{\n\t\ta: 1,\n\t\tb: \"七米\",\n\t\tc: 2,\n\t}\n\n\tv2 := s2{\n\t\ta: 1,\n\t\tb: \"七米\",\n\t\tc: 2,\n\t}\n\n\tfmt.Println(unsafe.Sizeof(v1), unsafe.Sizeof(v2))\n}\n"
  },
  {
    "path": "lesson68/bluebell/models/user.go",
    "content": "package models\n\ntype User struct {\n\tUserID   int64  `db:\"user_id\"`\n\tUsername string `db:\"username\"`\n\tPassword string `db:\"password\"`\n\tToken    string\n}\n"
  },
  {
    "path": "lesson68/bluebell/pkg/jwt/jwt.go",
    "content": "package jwt\n\nimport (\n\t\"errors\"\n\t\"time\"\n\n\t\"github.com/spf13/viper\"\n\n\t\"github.com/dgrijalva/jwt-go\"\n)\n\nvar mySecret = []byte(\"夏天夏天悄悄过去\")\n\n// MyClaims 自定义声明结构体并内嵌jwt.StandardClaims\n// jwt包自带的jwt.StandardClaims只包含了官方字段\n// 我们这里需要额外记录一个username字段，所以要自定义结构体\n// 如果想要保存更多信息，都可以添加到这个结构体中\ntype MyClaims struct {\n\tUserID   int64  `json:\"user_id\"`\n\tUsername string `json:\"username\"`\n\tjwt.StandardClaims\n}\n\n// GenToken 生成JWT\nfunc GenToken(userID int64, username string) (string, error) {\n\t// 创建一个我们自己的声明的数据\n\tc := MyClaims{\n\t\tuserID,\n\t\t\"username\", // 自定义字段\n\t\tjwt.StandardClaims{\n\t\t\tExpiresAt: time.Now().Add(\n\t\t\t\ttime.Duration(viper.GetInt(\"auth.jwt_expire\")) * time.Hour).Unix(), // 过期时间\n\t\t\tIssuer: \"bluebell\", // 签发人\n\t\t},\n\t}\n\t// 使用指定的签名方法创建签名对象\n\ttoken := jwt.NewWithClaims(jwt.SigningMethodHS256, c)\n\t// 使用指定的secret签名并获得完整的编码后的字符串token\n\treturn token.SignedString(mySecret)\n}\n\n// ParseToken 解析JWT\nfunc ParseToken(tokenString string) (*MyClaims, error) {\n\t// 解析token\n\tvar mc = new(MyClaims)\n\ttoken, err := jwt.ParseWithClaims(tokenString, mc, func(token *jwt.Token) (i interface{}, err error) {\n\t\treturn mySecret, nil\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif token.Valid { // 校验token\n\t\treturn mc, nil\n\t}\n\treturn nil, errors.New(\"invalid token\")\n}\n"
  },
  {
    "path": "lesson68/bluebell/pkg/snowflake/snowflake.go",
    "content": "package snowflake\n\nimport (\n\t\"time\"\n\n\tsf \"github.com/bwmarrin/snowflake\"\n)\n\nvar node *sf.Node\n\nfunc Init(startTime string, machineID int64) (err error) {\n\tvar st time.Time\n\tst, err = time.Parse(\"2006-01-02\", startTime)\n\tif err != nil {\n\t\treturn\n\t}\n\tsf.Epoch = st.UnixNano() / 1000000\n\tnode, err = sf.NewNode(machineID)\n\treturn\n}\nfunc GenID() int64 {\n\treturn node.Generate().Int64()\n}\n"
  },
  {
    "path": "lesson68/bluebell/router/route.go",
    "content": "package router\n\nimport (\n\t\"bluebell/controller\"\n\t\"bluebell/logger\"\n\t\"bluebell/middlewares\"\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc SetupRouter(mode string) *gin.Engine {\n\tif mode == gin.ReleaseMode {\n\t\tgin.SetMode(gin.ReleaseMode) // gin设置成发布模式\n\t}\n\tr := gin.New()\n\tr.Use(logger.GinLogger(), logger.GinRecovery(true))\n\n\tv1 := r.Group(\"/api/v1\")\n\n\t// 注册\n\tv1.POST(\"/signup\", controller.SignUpHandler)\n\t// 登录\n\tv1.POST(\"/login\", controller.LoginHandler)\n\n\tv1.Use(middlewares.JWTAuthMiddleware()) // 应用JWT认证中间件\n\n\t{\n\t\tv1.GET(\"/community\", controller.CommunityHandler)\n\t\tv1.GET(\"/community/:id\", controller.CommunityDetailHandler)\n\n\t\tv1.POST(\"/post\", controller.CreatePostHandler)\n\t\tv1.GET(\"/post/:id\", controller.GetPostDetailHandler)\n\t\tv1.GET(\"/posts\", controller.GetPostListHandler)\n\t\t// 根据时间或分数获取帖子列表\n\t\tv1.GET(\"/posts2\", controller.GetPostListHandler2)\n\n\t\t// 投票\n\t\tv1.POST(\"/vote\", controller.PostVoteController)\n\n\t}\n\n\tr.NoRoute(func(c *gin.Context) {\n\t\tc.JSON(http.StatusOK, gin.H{\n\t\t\t\"msg\": \"404\",\n\t\t})\n\t})\n\treturn r\n}\n"
  },
  {
    "path": "lesson68/bluebell/setting/setting.go",
    "content": "package setting\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\t\"github.com/spf13/viper\"\n)\n\nvar Conf = new(AppConfig)\n\ntype AppConfig struct {\n\tName      string `mapstructure:\"name\"`\n\tMode      string `mapstructure:\"mode\"`\n\tVersion   string `mapstructure:\"version\"`\n\tStartTime string `mapstructure:\"start_time\"`\n\tMachineID int64  `mapstructure:\"machine_id\"`\n\tPort      int    `mapstructure:\"port\"`\n\n\t*LogConfig   `mapstructure:\"log\"`\n\t*MySQLConfig `mapstructure:\"mysql\"`\n\t*RedisConfig `mapstructure:\"redis\"`\n}\n\ntype MySQLConfig struct {\n\tHost         string `mapstructure:\"host\"`\n\tUser         string `mapstructure:\"user\"`\n\tPassword     string `mapstructure:\"password\"`\n\tDB           string `mapstructure:\"dbname\"`\n\tPort         int    `mapstructure:\"port\"`\n\tMaxOpenConns int    `mapstructure:\"max_open_conns\"`\n\tMaxIdleConns int    `mapstructure:\"max_idle_conns\"`\n}\n\ntype RedisConfig struct {\n\tHost         string `mapstructure:\"host\"`\n\tPassword     string `mapstructure:\"password\"`\n\tPort         int    `mapstructure:\"port\"`\n\tDB           int    `mapstructure:\"db\"`\n\tPoolSize     int    `mapstructure:\"pool_size\"`\n\tMinIdleConns int    `mapstructure:\"min_idle_conns\"`\n}\n\ntype LogConfig struct {\n\tLevel      string `mapstructure:\"level\"`\n\tFilename   string `mapstructure:\"filename\"`\n\tMaxSize    int    `mapstructure:\"max_size\"`\n\tMaxAge     int    `mapstructure:\"max_age\"`\n\tMaxBackups int    `mapstructure:\"max_backups\"`\n}\n\nfunc Init(filePath string) (err error) {\n\t// 方式1：直接指定配置文件路径（相对路径或者绝对路径）\n\t// 相对路径：相对执行的可执行文件的相对路径\n\t//viper.SetConfigFile(\"./conf/config.yaml\")\n\t// 绝对路径：系统中实际的文件路径\n\t//viper.SetConfigFile(\"/Users/liwenzhou/Desktop/bluebell/conf/config.yaml\")\n\n\t// 方式2：指定配置文件名和配置文件的位置，viper自行查找可用的配置文件\n\t// 配置文件名不需要带后缀\n\t// 配置文件位置可配置多个\n\t//viper.SetConfigName(\"config\") // 指定配置文件名（不带后缀）\n\t//viper.AddConfigPath(\".\") // 指定查找配置文件的路径（这里使用相对路径）\n\t//viper.AddConfigPath(\"./conf\")      // 指定查找配置文件的路径（这里使用相对路径）\n\n\t// 基本上是配合远程配置中心使用的，告诉viper当前的数据使用什么格式去解析\n\t//viper.SetConfigType(\"json\")\n\n\tviper.SetConfigFile(filePath)\n\n\terr = viper.ReadInConfig() // 读取配置信息\n\tif err != nil {\n\t\t// 读取配置信息失败\n\t\tfmt.Printf(\"viper.ReadInConfig failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\n\t// 把读取到的配置信息反序列化到 Conf 变量中\n\tif err := viper.Unmarshal(Conf); err != nil {\n\t\tfmt.Printf(\"viper.Unmarshal failed, err:%v\\n\", err)\n\t}\n\n\tviper.WatchConfig()\n\tviper.OnConfigChange(func(in fsnotify.Event) {\n\t\tfmt.Println(\"配置文件修改了...\")\n\t\tif err := viper.Unmarshal(Conf); err != nil {\n\t\t\tfmt.Printf(\"viper.Unmarshal failed, err:%v\\n\", err)\n\t\t}\n\t})\n\treturn\n}\n"
  },
  {
    "path": "lesson69/bluebell/.air.conf",
    "content": "# [Air](https://github.com/cosmtrek/air) TOML 格式的配置文件\n\n# 工作目录\n# 使用 . 或绝对路径，请注意 `tmp_dir` 目录必须在 `root` 目录下\nroot = \".\"\ntmp_dir = \"tmp\"\n\n[build]\n# 只需要写你平常编译使用的shell命令。你也可以使用 `make`\ncmd = \"swag init && go build -o ./tmp/main\"\n# 由`cmd`命令得到的二进制文件名\nbin = \"tmp/main\"\n# 自定义的二进制，可以添加额外的编译标识例如添加 GIN_MODE=release\nfull_bin = \"./tmp/main ./conf/config.yaml\"\n# 监听以下文件扩展名的文件.\ninclude_ext = [\"go\", \"tpl\", \"tmpl\", \"html\", \"yaml\"]\n# 忽略这些文件扩展名或目录\nexclude_dir = [\"assets\", \"tmp\", \"vendor\", \"frontend/node_modules\"]\n# 监听以下指定目录的文件\ninclude_dir = []\n# 排除以下文件\nexclude_file = []\n# 如果文件更改过于频繁，则没有必要在每次更改时都触发构建。可以设置触发构建的延迟时间\ndelay = 1000 # ms\n# 发生构建错误时，停止运行旧的二进制文件。\nstop_on_error = true\n# air的日志文件名，该日志文件放置在你的`tmp_dir`中\nlog = \"air_errors.log\"\n\n[log]\n# 显示日志时间\ntime = true\n\n[color]\n# 自定义每个部分显示的颜色。如果找不到颜色，使用原始的应用程序日志。\nmain = \"magenta\"\nwatcher = \"cyan\"\nbuild = \"yellow\"\nrunner = \"green\"\n\n[misc]\n# 退出时删除tmp目录\nclean_on_exit = true"
  },
  {
    "path": "lesson69/bluebell/Makefile",
    "content": ".PHONY: all build run gotool clean help\n\nBINARY=\"xx\"\n\nall: gotool build\n\nbuild:\n\tCGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ${BINARY}\n\nrun:\n\t@go run ./main.go conf/config.yaml\n\ngotool:\n\tgo fmt ./\n\tgo vet ./\n\nclean:\n\t@if [ -f ${BINARY} ] ; then rm ${BINARY} ; fi\n\nhelp:\n\t@echo \"make - 格式化 Go 代码, 并编译生成二进制文件\"\n\t@echo \"make build - 编译 Go 代码, 生成二进制文件\"\n\t@echo \"make run - 直接运行 Go 代码\"\n\t@echo \"make clean - 移除二进制文件和 vim swap files\"\n\t@echo \"make gotool - 运行 Go 工具 'fmt' and 'vet'\"\n"
  },
  {
    "path": "lesson69/bluebell/conf/config.yaml",
    "content": "name: \"bluebell\"\nmode: \"dev\"\nport: 8084\nversion: \"v0.0.1\"\nstart_time: \"2020-07-01\"\nmachine_id: 1\n\nauth:\n  jwt_expire: 8760\n\nlog:\n  level: \"debug\"\n  filename: \"web_app.log\"\n  max_size: 200\n  max_age: 30\n  max_backups: 7\nmysql:\n  host: \"127.0.0.1\"\n  port: 13306\n  user: \"root\"\n  password: \"root1234\"\n  dbname: \"bluebell\"\n  max_open_conns: 200\n  max_idle_conns: 50\nredis:\n  host: \"127.0.0.1\"\n  port: 16379\n  password: \"\"\n  db: 0\n  pool_size: 100"
  },
  {
    "path": "lesson69/bluebell/controller/code.go",
    "content": "package controller\n\ntype ResCode int64\n\nconst (\n\tCodeSuccess ResCode = 1000 + iota\n\tCodeInvalidParam\n\tCodeUserExist\n\tCodeUserNotExist\n\tCodeInvalidPassword\n\tCodeServerBusy\n\n\tCodeNeedLogin\n\tCodeInvalidToken\n)\n\nvar codeMsgMap = map[ResCode]string{\n\tCodeSuccess:         \"success\",\n\tCodeInvalidParam:    \"请求参数错误\",\n\tCodeUserExist:       \"用户名已存在\",\n\tCodeUserNotExist:    \"用户名不存在\",\n\tCodeInvalidPassword: \"用户名或密码错误\",\n\tCodeServerBusy:      \"服务繁忙\",\n\n\tCodeNeedLogin:    \"需要登录\",\n\tCodeInvalidToken: \"无效的token\",\n}\n\nfunc (c ResCode) Msg() string {\n\tmsg, ok := codeMsgMap[c]\n\tif !ok {\n\t\tmsg = codeMsgMap[CodeServerBusy]\n\t}\n\treturn msg\n}\n"
  },
  {
    "path": "lesson69/bluebell/controller/community.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/logic\"\n\t\"strconv\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"go.uber.org/zap\"\n)\n\n// ---- 跟社区相关的 ----\n\nfunc CommunityHandler(c *gin.Context) {\n\t// 查询到所有的社区（community_id, community_name) 以列表的形式返回\n\tdata, err := logic.GetCommunityList()\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetCommunityList() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy) // 不轻易把服务端报错暴露给外面\n\t\treturn\n\t}\n\tResponseSuccess(c, data)\n}\n\n// CommunityDetailHandler 社区分类详情\nfunc CommunityDetailHandler(c *gin.Context) {\n\t// 1. 获取社区id\n\tidStr := c.Param(\"id\") // 获取URL参数\n\tid, err := strconv.ParseInt(idStr, 10, 64)\n\tif err != nil {\n\t\tResponseError(c, CodeInvalidParam)\n\t\treturn\n\t}\n\n\t// 2. 根据id获取社区详情\n\tdata, err := logic.GetCommunityDetail(id)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetCommunityList() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy) // 不轻易把服务端报错暴露给外面\n\t\treturn\n\t}\n\tResponseSuccess(c, data)\n}\n"
  },
  {
    "path": "lesson69/bluebell/controller/doc_response_models.go",
    "content": "package controller\n\nimport \"bluebell/models\"\n\n// 专门用来放接口文档用到的model\n// 因为我们的接口文档返回的数据格式是一致的，但是具体的data类型不一致\n\ntype _ResponsePostList struct {\n\tCode    ResCode                 `json:\"code\"`    // 业务响应状态码\n\tMessage string                  `json:\"message\"` // 提示信息\n\tData    []*models.ApiPostDetail `json:\"data\"`    // 数据\n}\n"
  },
  {
    "path": "lesson69/bluebell/controller/post.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/logic\"\n\t\"bluebell/models\"\n\t\"strconv\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"go.uber.org/zap\"\n\t// swagger 嵌入文件\n)\n\n// CreatePostHandler 创建帖子的处理函数\nfunc CreatePostHandler(c *gin.Context) {\n\t// 1. 获取参数及参数的校验\n\t//c.ShouldBindJSON()  // validator --> binding tag\n\tp := new(models.Post)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\tzap.L().Debug(\"c.ShouldBindJSON(p) error\", zap.Any(\"err\", err))\n\t\tzap.L().Error(\"create post with invalid param\")\n\t\tResponseError(c, CodeInvalidParam)\n\t\treturn\n\t}\n\t// 从 c 取到当前发请求的用户的ID\n\tuserID, err := getCurrentUserID(c)\n\tif err != nil {\n\t\tResponseError(c, CodeNeedLogin)\n\t\treturn\n\t}\n\tp.AuthorID = userID\n\t// 2. 创建帖子\n\tif err := logic.CreatePost(p); err != nil {\n\t\tzap.L().Error(\"logic.CreatePost(p) failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\n\t// 3. 返回响应\n\tResponseSuccess(c, nil)\n}\n\n// GetPostDetailHandler 获取帖子详情的处理函数\nfunc GetPostDetailHandler(c *gin.Context) {\n\t// 1. 获取参数（从URL中获取帖子的id）\n\tpidStr := c.Param(\"id\")\n\tpid, err := strconv.ParseInt(pidStr, 10, 64)\n\tif err != nil {\n\t\tzap.L().Error(\"get post detail with invalid param\", zap.Error(err))\n\t\tResponseError(c, CodeInvalidParam)\n\t\treturn\n\t}\n\n\t// 2. 根据id取出帖子数据（查数据库）\n\tdata, err := logic.GetPostById(pid)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetPostById(pid) failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\t// 3. 返回响应\n\tResponseSuccess(c, data)\n}\n\n// GetPostListHandler 获取帖子列表的处理函数\nfunc GetPostListHandler(c *gin.Context) {\n\t// 获取分页参数\n\tpage, size := getPageInfo(c)\n\t// 获取数据\n\tdata, err := logic.GetPostList(page, size)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetPostList() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\tResponseSuccess(c, data)\n\t// 返回响应\n}\n\n// GetPostListHandler2 升级版帖子列表接口\n// @Summary 升级版帖子列表接口\n// @Description 可按社区按时间或分数排序查询帖子列表接口\n// @Tags 帖子相关接口(api分组展示使用的)\n// @Accept application/json\n// @Produce application/json\n// @Param Authorization header string true \"Bearer JWT\"\n// @Param object query models.ParamPostList false \"查询参数\"\n// @Security ApiKeyAuth\n// @Success 200 {object} _ResponsePostList\n// @Router /posts2 [get]\nfunc GetPostListHandler2(c *gin.Context) {\n\t// GET请求参数(query string)：/api/v1/posts2?page=1&size=10&order=time\n\t// 初始化结构体时指定初始参数\n\tp := &models.ParamPostList{\n\t\tPage:  1,\n\t\tSize:  10,\n\t\tOrder: models.OrderTime, // magic string\n\t}\n\t//c.ShouldBind()  根据请求的数据类型选择相应的方法去获取数据\n\t//c.ShouldBindJSON() 如果请求中携带的是json格式的数据，才能用这个方法获取到数据\n\tif err := c.ShouldBindQuery(p); err != nil {\n\t\tzap.L().Error(\"GetPostListHandler2 with invalid params\", zap.Error(err))\n\t\tResponseError(c, CodeInvalidParam)\n\t\treturn\n\t}\n\tdata, err := logic.GetPostListNew(p) // 更新：合二为一\n\t// 获取数据\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetPostList() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\tResponseSuccess(c, data)\n\t// 返回响应\n}\n\n// 根据社区去查询帖子列表\n//func GetCommunityPostListHandler(c *gin.Context) {\n//\t// 初始化结构体时指定初始参数\n//\tp := &models.ParamCommunityPostList{\n//\t\tParamPostList: &models.ParamPostList{\n//\t\t\tPage:  1,\n//\t\t\tSize:  10,\n//\t\t\tOrder: models.OrderTime,\n//\t\t},\n//\t}\n//\t//c.ShouldBind()  根据请求的数据类型选择相应的方法去获取数据\n//\t//c.ShouldBindJSON() 如果请求中携带的是json格式的数据，才能用这个方法获取到数据\n//\tif err := c.ShouldBindQuery(p); err != nil {\n//\t\tzap.L().Error(\"GetCommunityPostListHandler with invalid params\", zap.Error(err))\n//\t\tResponseError(c, CodeInvalidParam)\n//\t\treturn\n//\t}\n//\n//\t// 获取数据\n//\tdata, err := logic.GetCommunityPostList(p)\n//\tif err != nil {\n//\t\tzap.L().Error(\"logic.GetPostList() failed\", zap.Error(err))\n//\t\tResponseError(c, CodeServerBusy)\n//\t\treturn\n//\t}\n//\tResponseSuccess(c, data)\n//}\n"
  },
  {
    "path": "lesson69/bluebell/controller/request.go",
    "content": "package controller\n\nimport (\n\t\"errors\"\n\t\"strconv\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\nconst CtxUserIDKey = \"userID\"\n\nvar ErrorUserNotLogin = errors.New(\"用户未登录\")\n\n// getCurrentUserID 获取当前登录的用户ID\nfunc getCurrentUserID(c *gin.Context) (userID int64, err error) {\n\tuid, ok := c.Get(CtxUserIDKey)\n\tif !ok {\n\t\terr = ErrorUserNotLogin\n\t\treturn\n\t}\n\tuserID, ok = uid.(int64)\n\tif !ok {\n\t\terr = ErrorUserNotLogin\n\t\treturn\n\t}\n\treturn\n}\n\nfunc getPageInfo(c *gin.Context) (int64, int64) {\n\tpageStr := c.Query(\"page\")\n\tsizeStr := c.Query(\"size\")\n\n\tvar (\n\t\tpage int64\n\t\tsize int64\n\t\terr  error\n\t)\n\n\tpage, err = strconv.ParseInt(pageStr, 10, 64)\n\tif err != nil {\n\t\tpage = 1\n\t}\n\tsize, err = strconv.ParseInt(sizeStr, 10, 64)\n\tif err != nil {\n\t\tsize = 10\n\t}\n\treturn page, size\n}\n"
  },
  {
    "path": "lesson69/bluebell/controller/response.go",
    "content": "package controller\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n/*\n{\n\t\"code\": 10000, // 程序中的错误码\n\t\"msg\": xx,     // 提示信息\n\t\"data\": {},    // 数据\n}\n\n*/\n\ntype ResponseData struct {\n\tCode ResCode     `json:\"code\"`\n\tMsg  interface{} `json:\"msg\"`\n\tData interface{} `json:\"data,omitempty\"`\n}\n\nfunc ResponseError(c *gin.Context, code ResCode) {\n\tc.JSON(http.StatusOK, &ResponseData{\n\t\tCode: code,\n\t\tMsg:  code.Msg(),\n\t\tData: nil,\n\t})\n}\n\nfunc ResponseErrorWithMsg(c *gin.Context, code ResCode, msg interface{}) {\n\tc.JSON(http.StatusOK, &ResponseData{\n\t\tCode: code,\n\t\tMsg:  msg,\n\t\tData: nil,\n\t})\n}\n\nfunc ResponseSuccess(c *gin.Context, data interface{}) {\n\tc.JSON(http.StatusOK, &ResponseData{\n\t\tCode: CodeSuccess,\n\t\tMsg:  CodeSuccess.Msg(),\n\t\tData: data,\n\t})\n}\n"
  },
  {
    "path": "lesson69/bluebell/controller/user.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/logic\"\n\t\"bluebell/models\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/go-playground/validator/v10\"\n\t\"go.uber.org/zap\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// SignUpHandler 处理注册请求的函数\nfunc SignUpHandler(c *gin.Context) {\n\t// 1. 获取参数和参数校验\n\tp := new(models.ParamSignUp)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\t// 请求参数有误，直接返回响应\n\t\tzap.L().Error(\"SignUp with invalid param\", zap.Error(err))\n\t\t// 判断err是不是validator.ValidationErrors 类型\n\t\terrs, ok := err.(validator.ValidationErrors)\n\t\tif !ok {\n\t\t\tResponseError(c, CodeInvalidParam)\n\t\t\treturn\n\t\t}\n\t\tResponseErrorWithMsg(c, CodeInvalidParam, removeTopStruct(errs.Translate(trans)))\n\t\treturn\n\t}\n\t// 2. 业务处理\n\tif err := logic.SignUp(p); err != nil {\n\t\tzap.L().Error(\"logic.SignUp failed\", zap.Error(err))\n\t\tif errors.Is(err, mysql.ErrorUserExist) {\n\t\t\tResponseError(c, CodeUserExist)\n\t\t\treturn\n\t\t}\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\t// 3. 返回响应\n\tResponseSuccess(c, nil)\n}\n\nfunc LoginHandler(c *gin.Context) {\n\t// 1.获取请求参数及参数校验\n\tp := new(models.ParamLogin)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\t// 请求参数有误，直接返回响应\n\t\tzap.L().Error(\"Login with invalid param\", zap.Error(err))\n\t\t// 判断err是不是validator.ValidationErrors 类型\n\t\terrs, ok := err.(validator.ValidationErrors)\n\t\tif !ok {\n\t\t\tResponseError(c, CodeInvalidParam)\n\t\t\treturn\n\t\t}\n\t\tResponseErrorWithMsg(c, CodeInvalidParam, removeTopStruct(errs.Translate(trans)))\n\t\treturn\n\t}\n\t// 2.业务逻辑处理\n\tuser, err := logic.Login(p)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.Login failed\", zap.String(\"username\", p.Username), zap.Error(err))\n\t\tif errors.Is(err, mysql.ErrorUserNotExist) {\n\t\t\tResponseError(c, CodeUserNotExist)\n\t\t\treturn\n\t\t}\n\t\tResponseError(c, CodeInvalidPassword)\n\t\treturn\n\t}\n\n\t// 3.返回响应\n\tResponseSuccess(c, gin.H{\n\t\t\"user_id\":   fmt.Sprintf(\"%d\", user.UserID), // id值大于1<<53-1  int64类型的最大值是1<<63-1\n\t\t\"user_name\": user.Username,\n\t\t\"token\":     user.Token,\n\t})\n}\n"
  },
  {
    "path": "lesson69/bluebell/controller/validator.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/models\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin/binding\"\n\t\"github.com/go-playground/locales/en\"\n\t\"github.com/go-playground/locales/zh\"\n\tut \"github.com/go-playground/universal-translator\"\n\t\"github.com/go-playground/validator/v10\"\n\tenTranslations \"github.com/go-playground/validator/v10/translations/en\"\n\tzhTranslations \"github.com/go-playground/validator/v10/translations/zh\"\n)\n\n// 定义一个全局翻译器T\nvar trans ut.Translator\n\n// InitTrans 初始化翻译器\nfunc InitTrans(locale string) (err error) {\n\t// 修改gin框架中的Validator引擎属性，实现自定制\n\tif v, ok := binding.Validator.Engine().(*validator.Validate); ok {\n\n\t\t// 注册一个获取json tag的自定义方法\n\t\tv.RegisterTagNameFunc(func(fld reflect.StructField) string {\n\t\t\tname := strings.SplitN(fld.Tag.Get(\"json\"), \",\", 2)[0]\n\t\t\tif name == \"-\" {\n\t\t\t\treturn \"\"\n\t\t\t}\n\t\t\treturn name\n\t\t})\n\n\t\t// 为SignUpParam注册自定义校验方法\n\t\tv.RegisterStructValidation(SignUpParamStructLevelValidation, models.ParamSignUp{})\n\n\t\tzhT := zh.New() // 中文翻译器\n\t\tenT := en.New() // 英文翻译器\n\n\t\t// 第一个参数是备用（fallback）的语言环境\n\t\t// 后面的参数是应该支持的语言环境（支持多个）\n\t\t// uni := ut.New(zhT, zhT) 也是可以的\n\t\tuni := ut.New(enT, zhT, enT)\n\n\t\t// locale 通常取决于 http 请求头的 'Accept-Language'\n\t\tvar ok bool\n\t\t// 也可以使用 uni.FindTranslator(...) 传入多个locale进行查找\n\t\ttrans, ok = uni.GetTranslator(locale)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"uni.GetTranslator(%s) failed\", locale)\n\t\t}\n\n\t\t// 注册翻译器\n\t\tswitch locale {\n\t\tcase \"en\":\n\t\t\terr = enTranslations.RegisterDefaultTranslations(v, trans)\n\t\tcase \"zh\":\n\t\t\terr = zhTranslations.RegisterDefaultTranslations(v, trans)\n\t\tdefault:\n\t\t\terr = enTranslations.RegisterDefaultTranslations(v, trans)\n\t\t}\n\t\treturn\n\t}\n\treturn\n}\n\n// removeTopStruct 去除提示信息中的结构体名称\nfunc removeTopStruct(fields map[string]string) map[string]string {\n\tres := map[string]string{}\n\tfor field, err := range fields {\n\t\tres[field[strings.Index(field, \".\")+1:]] = err\n\t}\n\treturn res\n}\n\n// SignUpParamStructLevelValidation 自定义SignUpParam结构体校验函数\nfunc SignUpParamStructLevelValidation(sl validator.StructLevel) {\n\tsu := sl.Current().Interface().(models.ParamSignUp)\n\n\tif su.Password != su.RePassword {\n\t\t// 输出错误提示信息，最后一个参数就是传递的param\n\t\tsl.ReportError(su.RePassword, \"re_password\", \"RePassword\", \"eqfield\", \"password\")\n\t}\n}\n"
  },
  {
    "path": "lesson69/bluebell/controller/vote.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/logic\"\n\t\"bluebell/models\"\n\n\t\"go.uber.org/zap\"\n\n\t\"github.com/go-playground/validator/v10\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// 投票\n\n//type VoteData struct {\n//\t// UserID 从请求中获取当前的用户\n//\tPostID    int64 `json:\"post_id,string\"`   // 贴子id\n//\tDirection int   `json:\"direction,string\"` // 赞成票(1)还是反对票(-1)\n//}\n\nfunc PostVoteController(c *gin.Context) {\n\t// 参数校验\n\tp := new(models.ParamVoteData)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\terrs, ok := err.(validator.ValidationErrors) // 类型断言\n\t\tif !ok {\n\t\t\tResponseError(c, CodeInvalidParam)\n\t\t\treturn\n\t\t}\n\t\terrData := removeTopStruct(errs.Translate(trans)) // 翻译并去除掉错误提示中的结构体标识\n\t\tResponseErrorWithMsg(c, CodeInvalidParam, errData)\n\t\treturn\n\t}\n\t// 获取当前请求的用户的id\n\tuserID, err := getCurrentUserID(c)\n\tif err != nil {\n\t\tResponseError(c, CodeNeedLogin)\n\t\treturn\n\t}\n\t// 具体投票的业务逻辑\n\tif err := logic.VoteForPost(userID, p); err != nil {\n\t\tzap.L().Error(\"logic.VoteForPost() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\n\tResponseSuccess(c, nil)\n}\n"
  },
  {
    "path": "lesson69/bluebell/dao/mysql/community.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/models\"\n\t\"database/sql\"\n\n\t\"go.uber.org/zap\"\n)\n\nfunc GetCommunityList() (communityList []*models.Community, err error) {\n\tsqlStr := \"select community_id, community_name from community\"\n\tif err := db.Select(&communityList, sqlStr); err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\tzap.L().Warn(\"there is no community in db\")\n\t\t\terr = nil\n\t\t}\n\t}\n\treturn\n}\n\n// GetCommunityDetailByID 根据ID查询社区详情\nfunc GetCommunityDetailByID(id int64) (community *models.CommunityDetail, err error) {\n\tcommunity = new(models.CommunityDetail)\n\tsqlStr := `select \n\t\t\tcommunity_id, community_name, introduction, create_time\n\t\t\tfrom community \n\t\t\twhere community_id = ?\n\t`\n\tif err := db.Get(community, sqlStr, id); err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\terr = ErrorInvalidID\n\t\t}\n\t}\n\treturn community, err\n}\n"
  },
  {
    "path": "lesson69/bluebell/dao/mysql/error_code.go",
    "content": "package mysql\n\nimport \"errors\"\n\nvar (\n\tErrorUserExist       = errors.New(\"用户已存在\")\n\tErrorUserNotExist    = errors.New(\"用户不存在\")\n\tErrorInvalidPassword = errors.New(\"用户名或密码错误\")\n\tErrorInvalidID       = errors.New(\"无效的ID\")\n)\n"
  },
  {
    "path": "lesson69/bluebell/dao/mysql/mysql.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/setting\"\n\t\"fmt\"\n\n\t_ \"github.com/go-sql-driver/mysql\"\n\t\"github.com/jmoiron/sqlx\"\n)\n\nvar db *sqlx.DB\n\n// Init 初始化MySQL连接\nfunc Init(cfg *setting.MySQLConfig) (err error) {\n\t// \"user:password@tcp(host:port)/dbname\"\n\tdsn := fmt.Sprintf(\"%s:%s@tcp(%s:%d)/%s?parseTime=true&loc=Local\", cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.DB)\n\tdb, err = sqlx.Connect(\"mysql\", dsn)\n\tif err != nil {\n\t\treturn\n\t}\n\tdb.SetMaxOpenConns(cfg.MaxOpenConns)\n\tdb.SetMaxIdleConns(cfg.MaxIdleConns)\n\treturn\n}\n\n// Close 关闭MySQL连接\nfunc Close() {\n\t_ = db.Close()\n}\n"
  },
  {
    "path": "lesson69/bluebell/dao/mysql/post.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/models\"\n\t\"strings\"\n\n\t\"github.com/jmoiron/sqlx\"\n)\n\n// CreatePost 创建帖子\nfunc CreatePost(p *models.Post) (err error) {\n\tsqlStr := `insert into post(\n\tpost_id, title, content, author_id, community_id)\n\tvalues (?, ?, ?, ?, ?)\n\t`\n\t_, err = db.Exec(sqlStr, p.ID, p.Title, p.Content, p.AuthorID, p.CommunityID)\n\treturn\n}\n\n// GetPostById 根据id查询单个贴子数据\nfunc GetPostById(pid int64) (post *models.Post, err error) {\n\tpost = new(models.Post)\n\tsqlStr := `select\n\tpost_id, title, content, author_id, community_id, create_time\n\tfrom post\n\twhere post_id = ?\n\t`\n\terr = db.Get(post, sqlStr, pid)\n\treturn\n}\n\n// GetPostList 查询帖子列表函数\nfunc GetPostList(page, size int64) (posts []*models.Post, err error) {\n\tsqlStr := `select \n\tpost_id, title, content, author_id, community_id, create_time\n\tfrom post\n\tORDER BY create_time\n\tDESC\n\tlimit ?,?\n\t`\n\tposts = make([]*models.Post, 0, 2) // 不要写成make([]*models.Post, 2)\n\terr = db.Select(&posts, sqlStr, (page-1)*size, size)\n\treturn\n}\n\n// GetPostListByIDs 根据给定的id列表查询帖子数据\nfunc GetPostListByIDs(ids []string) (postList []*models.Post, err error) {\n\tsqlStr := `select post_id, title, content, author_id, community_id, create_time\n\tfrom post\n\twhere post_id in (?)\n\torder by FIND_IN_SET(post_id, ?)\n\t`\n\t// https: //www.liwenzhou.com/posts/Go/sqlx/\n\tquery, args, err := sqlx.In(sqlStr, ids, strings.Join(ids, \",\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tquery = db.Rebind(query)\n\terr = db.Select(&postList, query, args...) // !!!!!!\n\treturn\n}\n"
  },
  {
    "path": "lesson69/bluebell/dao/mysql/user.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/models\"\n\t\"crypto/md5\"\n\t\"database/sql\"\n\t\"encoding/hex\"\n)\n\n// 把每一步数据库操作封装成函数\n// 待logic层根据业务需求调用\n\nconst secret = \"liwenzhou.com\"\n\n// CheckUserExist 检查指定用户名的用户是否存在\nfunc CheckUserExist(username string) (err error) {\n\tsqlStr := `select count(user_id) from user where username = ?`\n\tvar count int64\n\tif err := db.Get(&count, sqlStr, username); err != nil {\n\t\treturn err\n\t}\n\tif count > 0 {\n\t\treturn ErrorUserExist\n\t}\n\treturn\n}\n\n// InsertUser 想数据库中插入一条新的用户记录\nfunc InsertUser(user *models.User) (err error) {\n\t// 对密码进行加密\n\tuser.Password = encryptPassword(user.Password)\n\t// 执行SQL语句入库\n\tsqlStr := `insert into user(user_id, username, password) values(?,?,?)`\n\t_, err = db.Exec(sqlStr, user.UserID, user.Username, user.Password)\n\treturn\n}\n\n// encryptPassword 密码加密\nfunc encryptPassword(oPassword string) string {\n\th := md5.New()\n\th.Write([]byte(secret))\n\treturn hex.EncodeToString(h.Sum([]byte(oPassword)))\n}\n\nfunc Login(user *models.User) (err error) {\n\toPassword := user.Password // 用户登录的密码\n\tsqlStr := `select user_id, username, password from user where username=?`\n\terr = db.Get(user, sqlStr, user.Username)\n\tif err == sql.ErrNoRows {\n\t\treturn ErrorUserNotExist\n\t}\n\tif err != nil {\n\t\t// 查询数据库失败\n\t\treturn err\n\t}\n\t// 判断密码是否正确\n\tpassword := encryptPassword(oPassword)\n\tif password != user.Password {\n\t\treturn ErrorInvalidPassword\n\t}\n\treturn\n}\n\n// GetUserById 根据id获取用户信息\nfunc GetUserById(uid int64) (user *models.User, err error) {\n\tuser = new(models.User)\n\tsqlStr := `select user_id, username from user where user_id = ?`\n\terr = db.Get(user, sqlStr, uid)\n\treturn\n}\n"
  },
  {
    "path": "lesson69/bluebell/dao/redis/keys.go",
    "content": "package redis\n\n// redis key\n\n// redis key注意使用命名空间的方式,方便查询和拆分\n\nconst (\n\tPrefix             = \"bluebell:\"   // 项目key前缀\n\tKeyPostTimeZSet    = \"post:time\"   // zset;贴子及发帖时间\n\tKeyPostScoreZSet   = \"post:score\"  // zset;贴子及投票的分数\n\tKeyPostVotedZSetPF = \"post:voted:\" // zset;记录用户及投票类型;参数是post id\n\n\tKeyCommunitySetPF = \"community:\" // set;保存每个分区下帖子的id\n)\n\n// 给redis key加上前缀\nfunc getRedisKey(key string) string {\n\treturn Prefix + key\n}\n"
  },
  {
    "path": "lesson69/bluebell/dao/redis/post.go",
    "content": "package redis\n\nimport (\n\t\"bluebell/models\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/go-redis/redis\"\n)\n\nfunc getIDsFormKey(key string, page, size int64) ([]string, error) {\n\tstart := (page - 1) * size\n\tend := start + size - 1\n\t// 3. ZREVRANGE 按分数从大到小的顺序查询指定数量的元素\n\treturn client.ZRevRange(key, start, end).Result()\n}\n\nfunc GetPostIDsInOrder(p *models.ParamPostList) ([]string, error) {\n\t// 从redis获取id\n\t// 1. 根据用户请求中携带的order参数确定要查询的redis key\n\tkey := getRedisKey(KeyPostTimeZSet)\n\tif p.Order == models.OrderScore {\n\t\tkey = getRedisKey(KeyPostScoreZSet)\n\t}\n\t// 2. 确定查询的索引起始点\n\treturn getIDsFormKey(key, p.Page, p.Size)\n}\n\n// GetPostVoteData 根据ids查询每篇帖子的投赞成票的数据\nfunc GetPostVoteData(ids []string) (data []int64, err error) {\n\t//data = make([]int64, 0, len(ids))\n\t//for _, id := range ids {\n\t//\tkey := getRedisKey(KeyPostVotedZSetPF + id)\n\t//\t// 查找key中分数是1的元素的数量->统计每篇帖子的赞成票的数量\n\t//\tv := client.ZCount(key, \"1\", \"1\").Val()\n\t//\tdata = append(data, v)\n\t//}\n\t// 使用pipeline一次发送多条命令,减少RTT\n\tpipeline := client.Pipeline()\n\tfor _, id := range ids {\n\t\tkey := getRedisKey(KeyPostVotedZSetPF + id)\n\t\tpipeline.ZCount(key, \"1\", \"1\")\n\t}\n\tcmders, err := pipeline.Exec()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdata = make([]int64, 0, len(cmders))\n\tfor _, cmder := range cmders {\n\t\tv := cmder.(*redis.IntCmd).Val()\n\t\tdata = append(data, v)\n\t}\n\treturn\n}\n\n// GetCommunityPostIDsInOrder 按社区查询ids\nfunc GetCommunityPostIDsInOrder(p *models.ParamPostList) ([]string, error) {\n\n\torderKey := getRedisKey(KeyPostTimeZSet)\n\tif p.Order == models.OrderScore {\n\t\torderKey = getRedisKey(KeyPostScoreZSet)\n\t}\n\n\t// 使用 zinterstore 把分区的帖子set与帖子分数的 zset 生成一个新的zset\n\t// 针对新的zset 按之前的逻辑取数据\n\n\t// 社区的key\n\tcKey := getRedisKey(KeyCommunitySetPF + strconv.Itoa(int(p.CommunityID)))\n\n\t// 利用缓存key减少zinterstore执行的次数\n\tkey := orderKey + strconv.Itoa(int(p.CommunityID))\n\tif client.Exists(key).Val() < 1 {\n\t\t// 不存在，需要计算\n\t\tpipeline := client.Pipeline()\n\t\tpipeline.ZInterStore(key, redis.ZStore{\n\t\t\tAggregate: \"MAX\",\n\t\t}, cKey, orderKey) // zinterstore 计算\n\t\tpipeline.Expire(key, 60*time.Second) // 设置超时时间\n\t\t_, err := pipeline.Exec()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\t// 存在的话就直接根据key查询ids\n\treturn getIDsFormKey(key, p.Page, p.Size)\n}\n"
  },
  {
    "path": "lesson69/bluebell/dao/redis/redis.go",
    "content": "package redis\n\nimport (\n\t\"bluebell/setting\"\n\t\"fmt\"\n\n\t\"github.com/go-redis/redis\"\n)\n\nvar (\n\tclient *redis.Client\n\tNil    = redis.Nil\n)\n\n// Init 初始化连接\nfunc Init(cfg *setting.RedisConfig) (err error) {\n\tclient = redis.NewClient(&redis.Options{\n\t\tAddr:         fmt.Sprintf(\"%s:%d\", cfg.Host, cfg.Port),\n\t\tPassword:     cfg.Password, // no password set\n\t\tDB:           cfg.DB,       // use default DB\n\t\tPoolSize:     cfg.PoolSize,\n\t\tMinIdleConns: cfg.MinIdleConns,\n\t})\n\n\t_, err = client.Ping().Result()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc Close() {\n\t_ = client.Close()\n}\n"
  },
  {
    "path": "lesson69/bluebell/dao/redis/vote.go",
    "content": "package redis\n\nimport (\n\t\"errors\"\n\t\"math\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/go-redis/redis\"\n)\n\n// 推荐阅读\n// 基于用户投票的相关算法：http://www.ruanyifeng.com/blog/algorithm/\n\n// 本项目使用简化版的投票分数\n// 投一票就加432分   86400/200  --> 200张赞成票可以给你的帖子续一天\n\n/* 投票的几种情况：\n   direction=1时，有两种情况：\n   \t1. 之前没有投过票，现在投赞成票    --> 更新分数和投票记录  差值的绝对值：1  +432\n   \t2. 之前投反对票，现在改投赞成票    --> 更新分数和投票记录  差值的绝对值：2  +432*2\n   direction=0时，有两种情况：\n   \t1. 之前投过反对票，现在要取消投票  --> 更新分数和投票记录  差值的绝对值：1  +432\n\t2. 之前投过赞成票，现在要取消投票  --> 更新分数和投票记录  差值的绝对值：1  -432\n   direction=-1时，有两种情况：\n   \t1. 之前没有投过票，现在投反对票    --> 更新分数和投票记录  差值的绝对值：1  -432\n   \t2. 之前投赞成票，现在改投反对票    --> 更新分数和投票记录  差值的绝对值：2  -432*2\n\n   投票的限制：\n   每个贴子自发表之日起一个星期之内允许用户投票，超过一个星期就不允许再投票了。\n   \t1. 到期之后将redis中保存的赞成票数及反对票数存储到mysql表中\n   \t2. 到期之后删除那个 KeyPostVotedZSetPF\n*/\n\nconst (\n\toneWeekInSeconds = 7 * 24 * 3600\n\tscorePerVote     = 432 // 每一票值多少分\n)\n\nvar (\n\tErrVoteTimeExpire = errors.New(\"投票时间已过\")\n\tErrVoteRepeated   = errors.New(\"不允许重复投票\")\n)\n\nfunc CreatePost(postID, communityID int64) error {\n\tpipeline := client.TxPipeline()\n\t// 帖子时间\n\tpipeline.ZAdd(getRedisKey(KeyPostTimeZSet), redis.Z{\n\t\tScore:  float64(time.Now().Unix()),\n\t\tMember: postID,\n\t})\n\n\t// 帖子分数\n\tpipeline.ZAdd(getRedisKey(KeyPostScoreZSet), redis.Z{\n\t\tScore:  float64(time.Now().Unix()),\n\t\tMember: postID,\n\t})\n\t// 更新：把帖子id加到社区的set\n\tcKey := getRedisKey(KeyCommunitySetPF + strconv.Itoa(int(communityID)))\n\tpipeline.SAdd(cKey, postID)\n\t_, err := pipeline.Exec()\n\treturn err\n}\n\nfunc VoteForPost(userID, postID string, value float64) error {\n\t// 1. 判断投票限制\n\t// 去redis取帖子发布时间\n\tpostTime := client.ZScore(getRedisKey(KeyPostTimeZSet), postID).Val()\n\tif float64(time.Now().Unix())-postTime > oneWeekInSeconds {\n\t\treturn ErrVoteTimeExpire\n\t}\n\t// 2和3需要放到一个pipeline事务中操作\n\n\t// 2. 更新贴子的分数\n\t// 先查当前用户给当前帖子的投票记录\n\tov := client.ZScore(getRedisKey(KeyPostVotedZSetPF+postID), userID).Val()\n\n\t// 更新：如果这一次投票的值和之前保存的值一致，就提示不允许重复投票\n\tif value == ov {\n\t\treturn ErrVoteRepeated\n\t}\n\tvar op float64\n\tif value > ov {\n\t\top = 1\n\t} else {\n\t\top = -1\n\t}\n\tdiff := math.Abs(ov - value) // 计算两次投票的差值\n\tpipeline := client.TxPipeline()\n\tpipeline.ZIncrBy(getRedisKey(KeyPostScoreZSet), op*diff*scorePerVote, postID)\n\n\t// 3. 记录用户为该贴子投票的数据\n\tif value == 0 {\n\t\tpipeline.ZRem(getRedisKey(KeyPostVotedZSetPF+postID), userID)\n\t} else {\n\t\tpipeline.ZAdd(getRedisKey(KeyPostVotedZSetPF+postID), redis.Z{\n\t\t\tScore:  value, // 赞成票还是反对票\n\t\t\tMember: userID,\n\t\t})\n\t}\n\t_, err := pipeline.Exec()\n\treturn err\n}\n"
  },
  {
    "path": "lesson69/bluebell/docs/docs.go",
    "content": "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n// This file was generated by swaggo/swag\n\npackage docs\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"strings\"\n\n\t\"github.com/alecthomas/template\"\n\t\"github.com/swaggo/swag\"\n)\n\nvar doc = `{\n    \"schemes\": {{ marshal .Schemes }},\n    \"swagger\": \"2.0\",\n    \"info\": {\n        \"description\": \"{{.Description}}\",\n        \"title\": \"{{.Title}}\",\n        \"contact\": {\n            \"name\": \"liwenzhou\",\n            \"url\": \"http://www.liwenzhou.com\"\n        },\n        \"license\": {},\n        \"version\": \"{{.Version}}\"\n    },\n    \"host\": \"{{.Host}}\",\n    \"basePath\": \"{{.BasePath}}\",\n    \"paths\": {\n        \"/posts2\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"ApiKeyAuth\": []\n                    }\n                ],\n                \"description\": \"可按社区按时间或分数排序查询帖子列表接口\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"帖子相关接口(api分组展示使用的)\"\n                ],\n                \"summary\": \"升级版帖子列表接口\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Bearer JWT\",\n                        \"name\": \"Authorization\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"可以为空\",\n                        \"name\": \"community_id\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"score\",\n                        \"description\": \"排序依据\",\n                        \"name\": \"order\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"页码\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"每页数据量\",\n                        \"name\": \"size\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"OK\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/controller._ResponsePostList\"\n                        }\n                    }\n                }\n            }\n        }\n    },\n    \"definitions\": {\n        \"controller._ResponsePostList\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"code\": {\n                    \"description\": \"业务响应状态码\",\n                    \"type\": \"integer\"\n                },\n                \"data\": {\n                    \"description\": \"数据\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/definitions/models.ApiPostDetail\"\n                    }\n                },\n                \"message\": {\n                    \"description\": \"提示信息\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"models.ApiPostDetail\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"community_id\",\n                \"content\",\n                \"title\"\n            ],\n            \"properties\": {\n                \"author_id\": {\n                    \"description\": \"作者id\",\n                    \"type\": \"integer\"\n                },\n                \"author_name\": {\n                    \"description\": \"作者\",\n                    \"type\": \"string\"\n                },\n                \"community_id\": {\n                    \"description\": \"社区id\",\n                    \"type\": \"integer\"\n                },\n                \"content\": {\n                    \"description\": \"帖子内容\",\n                    \"type\": \"string\"\n                },\n                \"create_time\": {\n                    \"type\": \"string\"\n                },\n                \"id\": {\n                    \"type\": \"integer\"\n                },\n                \"introduction\": {\n                    \"type\": \"string\"\n                },\n                \"name\": {\n                    \"type\": \"string\"\n                },\n                \"status\": {\n                    \"description\": \"帖子状态\",\n                    \"type\": \"integer\"\n                },\n                \"title\": {\n                    \"description\": \"帖子标题\",\n                    \"type\": \"string\"\n                },\n                \"vote_num\": {\n                    \"description\": \"投票数\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"models.ParamPostList\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"community_id\": {\n                    \"description\": \"可以为空\",\n                    \"type\": \"integer\"\n                },\n                \"order\": {\n                    \"description\": \"排序依据\",\n                    \"type\": \"string\",\n                    \"example\": \"score\"\n                },\n                \"page\": {\n                    \"description\": \"页码\",\n                    \"type\": \"integer\"\n                },\n                \"size\": {\n                    \"description\": \"每页数据量\",\n                    \"type\": \"integer\"\n                }\n            }\n        }\n    }\n}`\n\ntype swaggerInfo struct {\n\tVersion     string\n\tHost        string\n\tBasePath    string\n\tSchemes     []string\n\tTitle       string\n\tDescription string\n}\n\n// SwaggerInfo holds exported Swagger Info so clients can modify it\nvar SwaggerInfo = swaggerInfo{\n\tVersion:     \"1.0\",\n\tHost:        \"127.0.0.1:8084\",\n\tBasePath:    \"/api/v1\",\n\tSchemes:     []string{},\n\tTitle:       \"bluebell项目接口文档\",\n\tDescription: \"Go web开发进阶项目实战课程bluebell\",\n}\n\ntype s struct{}\n\nfunc (s *s) ReadDoc() string {\n\tsInfo := SwaggerInfo\n\tsInfo.Description = strings.Replace(sInfo.Description, \"\\n\", \"\\\\n\", -1)\n\n\tt, err := template.New(\"swagger_info\").Funcs(template.FuncMap{\n\t\t\"marshal\": func(v interface{}) string {\n\t\t\ta, _ := json.Marshal(v)\n\t\t\treturn string(a)\n\t\t},\n\t}).Parse(doc)\n\tif err != nil {\n\t\treturn doc\n\t}\n\n\tvar tpl bytes.Buffer\n\tif err := t.Execute(&tpl, sInfo); err != nil {\n\t\treturn doc\n\t}\n\n\treturn tpl.String()\n}\n\nfunc init() {\n\tswag.Register(swag.Name, &s{})\n}\n"
  },
  {
    "path": "lesson69/bluebell/docs/swagger.json",
    "content": "{\n    \"swagger\": \"2.0\",\n    \"info\": {\n        \"description\": \"Go web开发进阶项目实战课程bluebell\",\n        \"title\": \"bluebell项目接口文档\",\n        \"contact\": {\n            \"name\": \"liwenzhou\",\n            \"url\": \"http://www.liwenzhou.com\"\n        },\n        \"license\": {},\n        \"version\": \"1.0\"\n    },\n    \"host\": \"127.0.0.1:8084\",\n    \"basePath\": \"/api/v1\",\n    \"paths\": {\n        \"/posts2\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"ApiKeyAuth\": []\n                    }\n                ],\n                \"description\": \"可按社区按时间或分数排序查询帖子列表接口\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"帖子相关接口(api分组展示使用的)\"\n                ],\n                \"summary\": \"升级版帖子列表接口\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Bearer JWT\",\n                        \"name\": \"Authorization\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"可以为空\",\n                        \"name\": \"community_id\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"score\",\n                        \"description\": \"排序依据\",\n                        \"name\": \"order\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"页码\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"每页数据量\",\n                        \"name\": \"size\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"OK\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/controller._ResponsePostList\"\n                        }\n                    }\n                }\n            }\n        }\n    },\n    \"definitions\": {\n        \"controller._ResponsePostList\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"code\": {\n                    \"description\": \"业务响应状态码\",\n                    \"type\": \"integer\"\n                },\n                \"data\": {\n                    \"description\": \"数据\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/definitions/models.ApiPostDetail\"\n                    }\n                },\n                \"message\": {\n                    \"description\": \"提示信息\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"models.ApiPostDetail\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"community_id\",\n                \"content\",\n                \"title\"\n            ],\n            \"properties\": {\n                \"author_id\": {\n                    \"description\": \"作者id\",\n                    \"type\": \"integer\"\n                },\n                \"author_name\": {\n                    \"description\": \"作者\",\n                    \"type\": \"string\"\n                },\n                \"community_id\": {\n                    \"description\": \"社区id\",\n                    \"type\": \"integer\"\n                },\n                \"content\": {\n                    \"description\": \"帖子内容\",\n                    \"type\": \"string\"\n                },\n                \"create_time\": {\n                    \"type\": \"string\"\n                },\n                \"id\": {\n                    \"type\": \"integer\"\n                },\n                \"introduction\": {\n                    \"type\": \"string\"\n                },\n                \"name\": {\n                    \"type\": \"string\"\n                },\n                \"status\": {\n                    \"description\": \"帖子状态\",\n                    \"type\": \"integer\"\n                },\n                \"title\": {\n                    \"description\": \"帖子标题\",\n                    \"type\": \"string\"\n                },\n                \"vote_num\": {\n                    \"description\": \"投票数\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"models.ParamPostList\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"community_id\": {\n                    \"description\": \"可以为空\",\n                    \"type\": \"integer\"\n                },\n                \"order\": {\n                    \"description\": \"排序依据\",\n                    \"type\": \"string\",\n                    \"example\": \"score\"\n                },\n                \"page\": {\n                    \"description\": \"页码\",\n                    \"type\": \"integer\"\n                },\n                \"size\": {\n                    \"description\": \"每页数据量\",\n                    \"type\": \"integer\"\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "lesson69/bluebell/docs/swagger.yaml",
    "content": "basePath: /api/v1\ndefinitions:\n  controller._ResponsePostList:\n    properties:\n      code:\n        description: 业务响应状态码\n        type: integer\n      data:\n        description: 数据\n        items:\n          $ref: '#/definitions/models.ApiPostDetail'\n        type: array\n      message:\n        description: 提示信息\n        type: string\n    type: object\n  models.ApiPostDetail:\n    properties:\n      author_id:\n        description: 作者id\n        type: integer\n      author_name:\n        description: 作者\n        type: string\n      community_id:\n        description: 社区id\n        type: integer\n      content:\n        description: 帖子内容\n        type: string\n      create_time:\n        type: string\n      id:\n        type: integer\n      introduction:\n        type: string\n      name:\n        type: string\n      status:\n        description: 帖子状态\n        type: integer\n      title:\n        description: 帖子标题\n        type: string\n      vote_num:\n        description: 投票数\n        type: integer\n    required:\n    - community_id\n    - content\n    - title\n    type: object\n  models.ParamPostList:\n    properties:\n      community_id:\n        description: 可以为空\n        type: integer\n      order:\n        description: 排序依据\n        example: score\n        type: string\n      page:\n        description: 页码\n        type: integer\n      size:\n        description: 每页数据量\n        type: integer\n    type: object\nhost: 127.0.0.1:8084\ninfo:\n  contact:\n    name: liwenzhou\n    url: http://www.liwenzhou.com\n  description: Go web开发进阶项目实战课程bluebell\n  license: {}\n  title: bluebell项目接口文档\n  version: \"1.0\"\npaths:\n  /posts2:\n    get:\n      consumes:\n      - application/json\n      description: 可按社区按时间或分数排序查询帖子列表接口\n      parameters:\n      - description: Bearer JWT\n        in: header\n        name: Authorization\n        required: true\n        type: string\n      - description: 可以为空\n        in: query\n        name: community_id\n        type: integer\n      - description: 排序依据\n        example: score\n        in: query\n        name: order\n        type: string\n      - description: 页码\n        in: query\n        name: page\n        type: integer\n      - description: 每页数据量\n        in: query\n        name: size\n        type: integer\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: OK\n          schema:\n            $ref: '#/definitions/controller._ResponsePostList'\n      security:\n      - ApiKeyAuth: []\n      summary: 升级版帖子列表接口\n      tags:\n      - 帖子相关接口(api分组展示使用的)\nswagger: \"2.0\"\n"
  },
  {
    "path": "lesson69/bluebell/go.mod",
    "content": "module bluebell\n\ngo 1.14\n\nrequire (\n\tgithub.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751\n\tgithub.com/bwmarrin/snowflake v0.3.0\n\tgithub.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect\n\tgithub.com/dgrijalva/jwt-go v3.2.0+incompatible\n\tgithub.com/fsnotify/fsnotify v1.4.9\n\tgithub.com/gin-gonic/gin v1.6.3\n\tgithub.com/go-openapi/spec v0.19.9 // indirect\n\tgithub.com/go-openapi/swag v0.19.9 // indirect\n\tgithub.com/go-playground/locales v0.13.0\n\tgithub.com/go-playground/universal-translator v0.17.0\n\tgithub.com/go-playground/validator/v10 v10.3.0\n\tgithub.com/go-redis/redis v6.15.8+incompatible\n\tgithub.com/go-sql-driver/mysql v1.5.0\n\tgithub.com/jmoiron/sqlx v1.2.0\n\tgithub.com/json-iterator/go v1.1.10 // indirect\n\tgithub.com/mailru/easyjson v0.7.6 // indirect\n\tgithub.com/natefinch/lumberjack v2.0.0+incompatible\n\tgithub.com/onsi/ginkgo v1.14.0 // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/sony/sonyflake v1.0.0 // indirect\n\tgithub.com/spf13/viper v1.7.0\n\tgithub.com/swaggo/gin-swagger v1.2.0\n\tgithub.com/swaggo/swag v1.6.7\n\tgithub.com/urfave/cli/v2 v2.2.0 // indirect\n\tgo.uber.org/zap v1.15.0\n\tgolang.org/x/net v0.0.0-20200904194848-62affa334b73 // indirect\n\tgolang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f // indirect\n\tgolang.org/x/tools v0.0.0-20200904185747-39188db58858 // indirect\n\tgoogle.golang.org/protobuf v1.25.0 // indirect\n\tgopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect\n)\n"
  },
  {
    "path": "lesson69/bluebell/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ngithub.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=\ngithub.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=\ngithub.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=\ngithub.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4=\ngithub.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=\ngithub.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=\ngithub.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=\ngithub.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=\ngithub.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=\ngithub.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=\ngithub.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=\ngithub.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=\ngithub.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=\ngithub.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=\ngithub.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=\ngithub.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=\ngithub.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0=\ngithub.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=\ngithub.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=\ngithub.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=\ngithub.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=\ngithub.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w=\ngithub.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=\ngithub.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=\ngithub.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=\ngithub.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=\ngithub.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=\ngithub.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=\ngithub.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=\ngithub.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=\ngithub.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=\ngithub.com/go-openapi/jsonpointer v0.17.0 h1:nH6xp8XdXHx8dqveo0ZuJBluCO2qGrPbDNZ0dwoRHP0=\ngithub.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=\ngithub.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=\ngithub.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=\ngithub.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=\ngithub.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=\ngithub.com/go-openapi/jsonreference v0.19.0 h1:BqWKpV1dFd+AuiKlgtddwVIFQsuMpxfBDBHGfM2yNpk=\ngithub.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=\ngithub.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=\ngithub.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=\ngithub.com/go-openapi/jsonreference v0.19.4 h1:3Vw+rh13uq2JFNxgnMTGE1rnoieU9FmyE1gvnyylsYg=\ngithub.com/go-openapi/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=\ngithub.com/go-openapi/spec v0.19.0 h1:A4SZ6IWh3lnjH0rG0Z5lkxazMGBECtrZcbyYQi+64k4=\ngithub.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=\ngithub.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=\ngithub.com/go-openapi/spec v0.19.9 h1:9z9cbFuZJ7AcvOHKIY+f6Aevb4vObNDkTEyoMfO7rAc=\ngithub.com/go-openapi/spec v0.19.9/go.mod h1:vqK/dIdLGCosfvYsQV3WfC7N3TiZSnGY2RZKoFK7X28=\ngithub.com/go-openapi/swag v0.17.0 h1:iqrgMg7Q7SvtbWLlltPrkMs0UBJI6oTSs79JFRUi880=\ngithub.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=\ngithub.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=\ngithub.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=\ngithub.com/go-openapi/swag v0.19.9 h1:1IxuqvBUU3S2Bi4YC7tlP9SJF1gVpCvqN0T2Qof4azE=\ngithub.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY=\ngithub.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=\ngithub.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=\ngithub.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=\ngithub.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=\ngithub.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=\ngithub.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=\ngithub.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=\ngithub.com/go-playground/validator/v10 v10.3.0 h1:nZU+7q+yJoFmwvNgv/LnPUkwPal62+b2xXj0AU1Es7o=\ngithub.com/go-playground/validator/v10 v10.3.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=\ngithub.com/go-redis/redis v6.15.8+incompatible h1:BKZuG6mCnRj5AOaWJXoCgf6rqTYnYJLe4en2hxT7r9o=\ngithub.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=\ngithub.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=\ngithub.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=\ngithub.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=\ngithub.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=\ngithub.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=\ngithub.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=\ngithub.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=\ngithub.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=\ngithub.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=\ngithub.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=\ngithub.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=\ngithub.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=\ngithub.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=\ngithub.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=\ngithub.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=\ngithub.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=\ngithub.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=\ngithub.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=\ngithub.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=\ngithub.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=\ngithub.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=\ngithub.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=\ngithub.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=\ngithub.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=\ngithub.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=\ngithub.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=\ngithub.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=\ngithub.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=\ngithub.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=\ngithub.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=\ngithub.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=\ngithub.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=\ngithub.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=\ngithub.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=\ngithub.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=\ngithub.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=\ngithub.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=\ngithub.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=\ngithub.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=\ngithub.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=\ngithub.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=\ngithub.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=\ngithub.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=\ngithub.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=\ngithub.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=\ngithub.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=\ngithub.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=\ngithub.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=\ngithub.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=\ngithub.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=\ngithub.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=\ngithub.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=\ngithub.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=\ngithub.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=\ngithub.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=\ngithub.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=\ngithub.com/sony/sonyflake v1.0.0 h1:MpU6Ro7tfXwgn2l5eluf9xQvQJDROTBImNCfRXn/YeM=\ngithub.com/sony/sonyflake v1.0.0/go.mod h1:Jv3cfhf/UFtolOTTRd3q4Nl6ENqM+KfyZ5PseKfZGF4=\ngithub.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=\ngithub.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=\ngithub.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=\ngithub.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=\ngithub.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=\ngithub.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=\ngithub.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=\ngithub.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=\ngithub.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=\ngithub.com/swaggo/files v0.0.0-20190704085106-630677cd5c14 h1:PyYN9JH5jY9j6av01SpfRMb+1DWg/i3MbGOKPxJ2wjM=\ngithub.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E=\ngithub.com/swaggo/gin-swagger v1.2.0 h1:YskZXEiv51fjOMTsXrOetAjrMDfFaXD79PEoQBOe2W0=\ngithub.com/swaggo/gin-swagger v1.2.0/go.mod h1:qlH2+W7zXGZkczuL+r2nEBR2JTT+/lX05Nn6vPhc7OI=\ngithub.com/swaggo/swag v1.5.1 h1:2Agm8I4K5qb00620mHq0VJ05/KT4FtmALPIcQR9lEZM=\ngithub.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y=\ngithub.com/swaggo/swag v1.6.7 h1:e8GC2xDllJZr3omJkm9YfmK0Y56+rMO3cg0JBKNz09s=\ngithub.com/swaggo/swag v1.6.7/go.mod h1:xDhTyuFIujYiN3DKWC/H/83xcfHp+UE/IzWWampG7Zc=\ngithub.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=\ngithub.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=\ngithub.com/ugorji/go v1.1.5-pre/go.mod h1:FwP/aQVg39TXzItUBMwnWp9T9gPQnXw4Poh4/oBQZ/0=\ngithub.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=\ngithub.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=\ngithub.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=\ngithub.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI=\ngithub.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=\ngithub.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=\ngithub.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=\ngithub.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=\ngithub.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k=\ngithub.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=\ngithub.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4=\ngithub.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=\ngithub.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngo.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=\ngo.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=\ngo.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=\ngo.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=\ngo.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngo.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=\ngo.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA=\ngolang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f h1:Fqb3ao1hUmOR3GkUOg/Y+BadLwykBIzs5q8Ez2SbHyc=\ngolang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc h1:NCy3Ohtk6Iny5V/reW2Ktypo4zIpWBdRJ1uFMjBxdg8=\ngolang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20200904185747-39188db58858 h1:xLt+iB5ksWcZVxqc+g9K41ZHy+6MKWfXCDsjSThnsPA=\ngolang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=\ngopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=\ngopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=\ngopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=\ngopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=\ngopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\n"
  },
  {
    "path": "lesson69/bluebell/logger/logger.go",
    "content": "package logger\n\nimport (\n\t\"bluebell/setting\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"os\"\n\t\"runtime/debug\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/natefinch/lumberjack\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\nvar lg *zap.Logger\n\n// Init 初始化lg\nfunc Init(cfg *setting.LogConfig, mode string) (err error) {\n\twriteSyncer := getLogWriter(cfg.Filename, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge)\n\tencoder := getEncoder()\n\tvar l = new(zapcore.Level)\n\terr = l.UnmarshalText([]byte(cfg.Level))\n\tif err != nil {\n\t\treturn\n\t}\n\tvar core zapcore.Core\n\tif mode == \"dev\" {\n\t\t// 进入开发模式，日志输出到终端\n\t\tconsoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())\n\t\tcore = zapcore.NewTee(\n\t\t\tzapcore.NewCore(encoder, writeSyncer, l),\n\t\t\tzapcore.NewCore(consoleEncoder, zapcore.Lock(os.Stdout), zapcore.DebugLevel),\n\t\t)\n\t} else {\n\t\tcore = zapcore.NewCore(encoder, writeSyncer, l)\n\t}\n\n\tlg = zap.New(core, zap.AddCaller())\n\n\tzap.ReplaceGlobals(lg)\n\tzap.L().Info(\"init logger success\")\n\treturn\n}\n\nfunc getEncoder() zapcore.Encoder {\n\tencoderConfig := zap.NewProductionEncoderConfig()\n\tencoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder\n\tencoderConfig.TimeKey = \"time\"\n\tencoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder\n\tencoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder\n\tencoderConfig.EncodeCaller = zapcore.ShortCallerEncoder\n\treturn zapcore.NewJSONEncoder(encoderConfig)\n}\n\nfunc getLogWriter(filename string, maxSize, maxBackup, maxAge int) zapcore.WriteSyncer {\n\tlumberJackLogger := &lumberjack.Logger{\n\t\tFilename:   filename,\n\t\tMaxSize:    maxSize,\n\t\tMaxBackups: maxBackup,\n\t\tMaxAge:     maxAge,\n\t}\n\treturn zapcore.AddSync(lumberJackLogger)\n}\n\n// GinLogger 接收gin框架默认的日志\nfunc GinLogger() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tstart := time.Now()\n\t\tpath := c.Request.URL.Path\n\t\tquery := c.Request.URL.RawQuery\n\t\tc.Next()\n\n\t\tcost := time.Since(start)\n\t\tlg.Info(path,\n\t\t\tzap.Int(\"status\", c.Writer.Status()),\n\t\t\tzap.String(\"method\", c.Request.Method),\n\t\t\tzap.String(\"path\", path),\n\t\t\tzap.String(\"query\", query),\n\t\t\tzap.String(\"ip\", c.ClientIP()),\n\t\t\tzap.String(\"user-agent\", c.Request.UserAgent()),\n\t\t\tzap.String(\"errors\", c.Errors.ByType(gin.ErrorTypePrivate).String()),\n\t\t\tzap.Duration(\"cost\", cost),\n\t\t)\n\t}\n}\n\n// GinRecovery recover掉项目可能出现的panic，并使用zap记录相关日志\nfunc GinRecovery(stack bool) gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tdefer func() {\n\t\t\tif err := recover(); err != nil {\n\t\t\t\t// Check for a broken connection, as it is not really a\n\t\t\t\t// condition that warrants a panic stack trace.\n\t\t\t\tvar brokenPipe bool\n\t\t\t\tif ne, ok := err.(*net.OpError); ok {\n\t\t\t\t\tif se, ok := ne.Err.(*os.SyscallError); ok {\n\t\t\t\t\t\tif strings.Contains(strings.ToLower(se.Error()), \"broken pipe\") || strings.Contains(strings.ToLower(se.Error()), \"connection reset by peer\") {\n\t\t\t\t\t\t\tbrokenPipe = true\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\thttpRequest, _ := httputil.DumpRequest(c.Request, false)\n\t\t\t\tif brokenPipe {\n\t\t\t\t\tlg.Error(c.Request.URL.Path,\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t)\n\t\t\t\t\t// If the connection is dead, we can't write a status to it.\n\t\t\t\t\tc.Error(err.(error)) // nolint: errcheck\n\t\t\t\t\tc.Abort()\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif stack {\n\t\t\t\t\tlg.Error(\"[Recovery from panic]\",\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t\tzap.String(\"stack\", string(debug.Stack())),\n\t\t\t\t\t)\n\t\t\t\t} else {\n\t\t\t\t\tlg.Error(\"[Recovery from panic]\",\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tc.AbortWithStatus(http.StatusInternalServerError)\n\t\t\t}\n\t\t}()\n\t\tc.Next()\n\t}\n}\n"
  },
  {
    "path": "lesson69/bluebell/logic/community.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/models\"\n)\n\nfunc GetCommunityList() ([]*models.Community, error) {\n\t// 查数据库 查找到所有的community 并返回\n\treturn mysql.GetCommunityList()\n}\n\nfunc GetCommunityDetail(id int64) (*models.CommunityDetail, error) {\n\treturn mysql.GetCommunityDetailByID(id)\n}\n"
  },
  {
    "path": "lesson69/bluebell/logic/post.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/dao/redis\"\n\t\"bluebell/models\"\n\t\"bluebell/pkg/snowflake\"\n\n\t\"go.uber.org/zap\"\n)\n\nfunc CreatePost(p *models.Post) (err error) {\n\t// 1. 生成post id\n\tp.ID = snowflake.GenID()\n\t// 2. 保存到数据库\n\terr = mysql.CreatePost(p)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = redis.CreatePost(p.ID, p.CommunityID)\n\treturn\n\t// 3. 返回\n}\n\n// GetPostById 根据帖子id查询帖子详情数据\nfunc GetPostById(pid int64) (data *models.ApiPostDetail, err error) {\n\t// 查询并组合我们接口想用的数据\n\tpost, err := mysql.GetPostById(pid)\n\tif err != nil {\n\t\tzap.L().Error(\"mysql.GetPostById(pid) failed\",\n\t\t\tzap.Int64(\"pid\", pid),\n\t\t\tzap.Error(err))\n\t\treturn\n\t}\n\t// 根据作者id查询作者信息\n\tuser, err := mysql.GetUserById(post.AuthorID)\n\tif err != nil {\n\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\tzap.Int64(\"author_id\", post.AuthorID),\n\t\t\tzap.Error(err))\n\t\treturn\n\t}\n\t// 根据社区id查询社区详细信息\n\tcommunity, err := mysql.GetCommunityDetailByID(post.CommunityID)\n\tif err != nil {\n\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\tzap.Int64(\"community_id\", post.CommunityID),\n\t\t\tzap.Error(err))\n\t\treturn\n\t}\n\t// 接口数据拼接\n\tdata = &models.ApiPostDetail{\n\t\tAuthorName:      user.Username,\n\t\tPost:            post,\n\t\tCommunityDetail: community,\n\t}\n\treturn\n}\n\n// GetPostList 获取帖子列表\nfunc GetPostList(page, size int64) (data []*models.ApiPostDetail, err error) {\n\tposts, err := mysql.GetPostList(page, size)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdata = make([]*models.ApiPostDetail, 0, len(posts))\n\n\tfor _, post := range posts {\n\t\t// 根据作者id查询作者信息\n\t\tuser, err := mysql.GetUserById(post.AuthorID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"author_id\", post.AuthorID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\t// 根据社区id查询社区详细信息\n\t\tcommunity, err := mysql.GetCommunityDetailByID(post.CommunityID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"community_id\", post.CommunityID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\tpostDetail := &models.ApiPostDetail{\n\t\t\tAuthorName:      user.Username,\n\t\t\tPost:            post,\n\t\t\tCommunityDetail: community,\n\t\t}\n\t\tdata = append(data, postDetail)\n\t}\n\treturn\n}\n\nfunc GetPostList2(p *models.ParamPostList) (data []*models.ApiPostDetail, err error) {\n\t// 2. 去redis查询id列表\n\tids, err := redis.GetPostIDsInOrder(p)\n\tif err != nil {\n\t\treturn\n\t}\n\tif len(ids) == 0 {\n\t\tzap.L().Warn(\"redis.GetPostIDsInOrder(p) return 0 data\")\n\t\treturn\n\t}\n\tzap.L().Debug(\"GetPostList2\", zap.Any(\"ids\", ids))\n\t// 3. 根据id去MySQL数据库查询帖子详细信息\n\t// 返回的数据还要按照我给定的id的顺序返回\n\tposts, err := mysql.GetPostListByIDs(ids)\n\tif err != nil {\n\t\treturn\n\t}\n\tzap.L().Debug(\"GetPostList2\", zap.Any(\"posts\", posts))\n\t// 提前查询好每篇帖子的投票数\n\tvoteData, err := redis.GetPostVoteData(ids)\n\tif err != nil {\n\t\treturn\n\t}\n\n\t// 将帖子的作者及分区信息查询出来填充到帖子中\n\tfor idx, post := range posts {\n\t\t// 根据作者id查询作者信息\n\t\tuser, err := mysql.GetUserById(post.AuthorID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"author_id\", post.AuthorID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\t// 根据社区id查询社区详细信息\n\t\tcommunity, err := mysql.GetCommunityDetailByID(post.CommunityID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"community_id\", post.CommunityID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\tpostDetail := &models.ApiPostDetail{\n\t\t\tAuthorName:      user.Username,\n\t\t\tVoteNum:         voteData[idx],\n\t\t\tPost:            post,\n\t\t\tCommunityDetail: community,\n\t\t}\n\t\tdata = append(data, postDetail)\n\t}\n\treturn\n\n}\n\nfunc GetCommunityPostList(p *models.ParamPostList) (data []*models.ApiPostDetail, err error) {\n\t// 2. 去redis查询id列表\n\tids, err := redis.GetCommunityPostIDsInOrder(p)\n\tif err != nil {\n\t\treturn\n\t}\n\tif len(ids) == 0 {\n\t\tzap.L().Warn(\"redis.GetPostIDsInOrder(p) return 0 data\")\n\t\treturn\n\t}\n\tzap.L().Debug(\"GetCommunityPostIDsInOrder\", zap.Any(\"ids\", ids))\n\t// 3. 根据id去MySQL数据库查询帖子详细信息\n\t// 返回的数据还要按照我给定的id的顺序返回\n\tposts, err := mysql.GetPostListByIDs(ids)\n\tif err != nil {\n\t\treturn\n\t}\n\tzap.L().Debug(\"GetPostList2\", zap.Any(\"posts\", posts))\n\t// 提前查询好每篇帖子的投票数\n\tvoteData, err := redis.GetPostVoteData(ids)\n\tif err != nil {\n\t\treturn\n\t}\n\n\t// 将帖子的作者及分区信息查询出来填充到帖子中\n\tfor idx, post := range posts {\n\t\t// 根据作者id查询作者信息\n\t\tuser, err := mysql.GetUserById(post.AuthorID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"author_id\", post.AuthorID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\t// 根据社区id查询社区详细信息\n\t\tcommunity, err := mysql.GetCommunityDetailByID(post.CommunityID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"community_id\", post.CommunityID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\tpostDetail := &models.ApiPostDetail{\n\t\t\tAuthorName:      user.Username,\n\t\t\tVoteNum:         voteData[idx],\n\t\t\tPost:            post,\n\t\t\tCommunityDetail: community,\n\t\t}\n\t\tdata = append(data, postDetail)\n\t}\n\treturn\n}\n\n// GetPostListNew  将两个查询帖子列表逻辑合二为一的函数\nfunc GetPostListNew(p *models.ParamPostList) (data []*models.ApiPostDetail, err error) {\n\t// 根据请求参数的不同，执行不同的逻辑。\n\tif p.CommunityID == 0 {\n\t\t// 查所有\n\t\tdata, err = GetPostList2(p)\n\t} else {\n\t\t// 根据社区id查询\n\t\tdata, err = GetCommunityPostList(p)\n\t}\n\tif err != nil {\n\t\tzap.L().Error(\"GetPostListNew failed\", zap.Error(err))\n\t\treturn nil, err\n\t}\n\treturn\n}\n"
  },
  {
    "path": "lesson69/bluebell/logic/user.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/models\"\n\t\"bluebell/pkg/jwt\"\n\t\"bluebell/pkg/snowflake\"\n)\n\n// 存放业务逻辑的代码\n\nfunc SignUp(p *models.ParamSignUp) (err error) {\n\t// 1.判断用户存不存在\n\tif err := mysql.CheckUserExist(p.Username); err != nil {\n\t\treturn err\n\t}\n\t// 2.生成UID\n\tuserID := snowflake.GenID()\n\t// 构造一个User实例\n\tuser := &models.User{\n\t\tUserID:   userID,\n\t\tUsername: p.Username,\n\t\tPassword: p.Password,\n\t}\n\t// 3.保存进数据库\n\treturn mysql.InsertUser(user)\n}\n\nfunc Login(p *models.ParamLogin) (user *models.User, err error) {\n\tuser = &models.User{\n\t\tUsername: p.Username,\n\t\tPassword: p.Password,\n\t}\n\t// 传递的是指针，就能拿到user.UserID\n\tif err := mysql.Login(user); err != nil {\n\t\treturn nil, err\n\t}\n\t// 生成JWT\n\ttoken, err := jwt.GenToken(user.UserID, user.Username)\n\tif err != nil {\n\t\treturn\n\t}\n\tuser.Token = token\n\treturn\n}\n"
  },
  {
    "path": "lesson69/bluebell/logic/vote.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/redis\"\n\t\"bluebell/models\"\n\t\"strconv\"\n\n\t\"go.uber.org/zap\"\n)\n\n// 推荐阅读\n// 基于用户投票的相关算法：http://www.ruanyifeng.com/blog/algorithm/\n\n// 本项目使用简化版的投票分数\n// 投一票就加432分   86400/200  --> 200张赞成票可以给你的帖子续一天\n\n/* 投票的几种情况：\ndirection=1时，有两种情况：\n\t1. 之前没有投过票，现在投赞成票    --> 更新分数和投票记录\n\t2. 之前投反对票，现在改投赞成票    --> 更新分数和投票记录\ndirection=0时，有两种情况：\n\t1. 之前投过赞成票，现在要取消投票  --> 更新分数和投票记录\n\t2. 之前投过反对票，现在要取消投票  --> 更新分数和投票记录\ndirection=-1时，有两种情况：\n\t1. 之前没有投过票，现在投反对票    --> 更新分数和投票记录\n\t2. 之前投赞成票，现在改投反对票    --> 更新分数和投票记录\n\n投票的限制：\n每个贴子自发表之日起一个星期之内允许用户投票，超过一个星期就不允许再投票了。\n\t1. 到期之后将redis中保存的赞成票数及反对票数存储到mysql表中\n\t2. 到期之后删除那个 KeyPostVotedZSetPF\n*/\n\n// VoteForPost 为帖子投票的函数\nfunc VoteForPost(userID int64, p *models.ParamVoteData) error {\n\tzap.L().Debug(\"VoteForPost\",\n\t\tzap.Int64(\"userID\", userID),\n\t\tzap.String(\"postID\", p.PostID),\n\t\tzap.Int8(\"direction\", p.Direction))\n\treturn redis.VoteForPost(strconv.Itoa(int(userID)), p.PostID, float64(p.Direction))\n}\n"
  },
  {
    "path": "lesson69/bluebell/main.go",
    "content": "package main\n\nimport (\n\t\"bluebell/controller\"\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/dao/redis\"\n\t\"bluebell/logger\"\n\t\"bluebell/pkg/snowflake\"\n\t\"bluebell/router\"\n\t\"bluebell/setting\"\n\t\"fmt\"\n\t\"os\"\n)\n\n// @title bluebell项目接口文档\n// @version 1.0\n// @description Go web开发进阶项目实战课程bluebell\n\n// @contact.name liwenzhou\n// @contact.url http://www.liwenzhou.com\n\n// @host 127.0.0.1:8084\n// @BasePath /api/v1\nfunc main() {\n\tif len(os.Args) < 2 {\n\t\tfmt.Println(\"need config file.eg: bluebell config.yaml\")\n\t\treturn\n\t}\n\t// 加载配置\n\tif err := setting.Init(os.Args[1]); err != nil {\n\t\tfmt.Printf(\"load config failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tif err := logger.Init(setting.Conf.LogConfig, setting.Conf.Mode); err != nil {\n\t\tfmt.Printf(\"init logger failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tif err := mysql.Init(setting.Conf.MySQLConfig); err != nil {\n\t\tfmt.Printf(\"init mysql failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer mysql.Close() // 程序退出关闭数据库连接\n\tif err := redis.Init(setting.Conf.RedisConfig); err != nil {\n\t\tfmt.Printf(\"init redis failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer redis.Close()\n\n\tif err := snowflake.Init(setting.Conf.StartTime, setting.Conf.MachineID); err != nil {\n\t\tfmt.Printf(\"init snowflake failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\t// 初始化gin框架内置的校验器使用的翻译器\n\tif err := controller.InitTrans(\"zh\"); err != nil {\n\t\tfmt.Printf(\"init validator trans failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\t// 注册路由\n\tr := router.SetupRouter(setting.Conf.Mode)\n\terr := r.Run(fmt.Sprintf(\":%d\", setting.Conf.Port))\n\tif err != nil {\n\t\tfmt.Printf(\"run server failed, err:%v\\n\", err)\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "lesson69/bluebell/middlewares/auth.go",
    "content": "package middlewares\n\nimport (\n\t\"bluebell/controller\"\n\t\"bluebell/pkg/jwt\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// JWTAuthMiddleware 基于JWT的认证中间件\nfunc JWTAuthMiddleware() func(c *gin.Context) {\n\treturn func(c *gin.Context) {\n\t\t// 客户端携带Token有三种方式 1.放在请求头 2.放在请求体 3.放在URI\n\t\t// 这里假设Token放在Header的Authorization中，并使用Bearer开头\n\t\t// Authorization: Bearer xxxxxxx.xxx.xxx  / X-TOKEN: xxx.xxx.xx\n\t\t// 这里的具体实现方式要依据你的实际业务情况决定\n\t\tauthHeader := c.Request.Header.Get(\"Authorization\")\n\t\tif authHeader == \"\" {\n\t\t\tcontroller.ResponseError(c, controller.CodeNeedLogin)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// 按空格分割\n\t\tparts := strings.SplitN(authHeader, \" \", 2)\n\t\tif !(len(parts) == 2 && parts[0] == \"Bearer\") {\n\t\t\tcontroller.ResponseError(c, controller.CodeInvalidToken)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// parts[1]是获取到的tokenString，我们使用之前定义好的解析JWT的函数来解析它\n\t\tmc, err := jwt.ParseToken(parts[1])\n\t\tif err != nil {\n\t\t\tcontroller.ResponseError(c, controller.CodeInvalidToken)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// 将当前请求的userID信息保存到请求的上下文c上\n\t\tc.Set(controller.CtxUserIDKey, mc.UserID)\n\n\t\tc.Next() // 后续的处理请求的函数中 可以用过c.Get(CtxUserIDKey) 来获取当前请求的用户信息\n\t}\n}\n"
  },
  {
    "path": "lesson69/bluebell/models/community.go",
    "content": "package models\n\nimport \"time\"\n\ntype Community struct {\n\tID   int64  `json:\"id\" db:\"community_id\"`\n\tName string `json:\"name\" db:\"community_name\"`\n}\n\ntype CommunityDetail struct {\n\tID           int64     `json:\"id\" db:\"community_id\"`\n\tName         string    `json:\"name\" db:\"community_name\"`\n\tIntroduction string    `json:\"introduction,omitempty\" db:\"introduction\"`\n\tCreateTime   time.Time `json:\"create_time\" db:\"create_time\"`\n}\n"
  },
  {
    "path": "lesson69/bluebell/models/create_table.sql",
    "content": "--\nCREATE TABLE `user` (\n    `id` bigint(20) NOT NULL AUTO_INCREMENT,\n    `user_id` bigint(20) NOT NULL,\n    `username` varchar(64) COLLATE utf8mb4_general_ci NOT NULL,\n    `password` varchar(64) COLLATE utf8mb4_general_ci NOT NULL,\n    `email` varchar(64) COLLATE utf8mb4_general_ci,\n    `gender` tinyint(4) NOT NULL DEFAULT '0',\n    `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n    `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    PRIMARY KEY (`id`),\n    UNIQUE KEY `idx_username` (`username`) USING BTREE,\n    UNIQUE KEY `idx_user_id` (`user_id`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\n\n\nDROP TABLE IF EXISTS `community`;\nCREATE TABLE `community` (\n     `id` int(11) NOT NULL AUTO_INCREMENT,\n     `community_id` int(10) unsigned NOT NULL,\n     `community_name` varchar(128) COLLATE utf8mb4_general_ci NOT NULL,\n     `introduction` varchar(256) COLLATE utf8mb4_general_ci NOT NULL,\n     `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n     `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n     PRIMARY KEY (`id`),\n     UNIQUE KEY `idx_community_id` (`community_id`),\n     UNIQUE KEY `idx_community_name` (`community_name`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\n\n\nINSERT INTO `community` VALUES ('1', '1', 'Go', 'Golang', '2016-11-01 08:10:10', '2016-11-01 08:10:10');\nINSERT INTO `community` VALUES ('2', '2', 'leetcode', '刷题刷题刷题', '2020-01-01 08:00:00', '2020-01-01 08:00:00');\nINSERT INTO `community` VALUES ('3', '3', 'CS:GO', 'Rush B。。。', '2018-08-07 08:30:00', '2018-08-07 08:30:00');\nINSERT INTO `community` VALUES ('4', '4', 'LOL', '欢迎来到英雄联盟!', '2016-01-01 08:00:00', '2016-01-01 08:00:00');\n\nDROP TABLE IF EXISTS `post`;\nCREATE TABLE `post` (\n    `id` bigint(20) NOT NULL AUTO_INCREMENT,\n    `post_id` bigint(20) NOT NULL COMMENT '帖子id',\n    `title` varchar(128) COLLATE utf8mb4_general_ci NOT NULL COMMENT '标题',\n    `content` varchar(8192) COLLATE utf8mb4_general_ci NOT NULL COMMENT '内容',\n    `author_id` bigint(20) NOT NULL COMMENT '作者的用户id',\n    `community_id` bigint(20) NOT NULL COMMENT '所属社区',\n    `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '帖子状态',\n    `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n    `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n    PRIMARY KEY (`id`),\n    UNIQUE KEY `idx_post_id` (`post_id`),\n    KEY `idx_author_id` (`author_id`),\n    KEY `idx_community_id` (`community_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;"
  },
  {
    "path": "lesson69/bluebell/models/params.go",
    "content": "package models\n\n// 定义请求的参数结构体\n\nconst (\n\tOrderTime  = \"time\"\n\tOrderScore = \"score\"\n)\n\n// ParamSignUp 注册请求参数\ntype ParamSignUp struct {\n\tUsername   string `json:\"username\" binding:\"required\"`\n\tPassword   string `json:\"password\" binding:\"required\"`\n\tRePassword string `json:\"re_password\" binding:\"required,eqfield=Password\"`\n}\n\n// ParamLogin 登录请求参数\ntype ParamLogin struct {\n\tUsername string `json:\"username\" binding:\"required\"`\n\tPassword string `json:\"password\" binding:\"required\"`\n}\n\n// ParamVoteData 投票数据\ntype ParamVoteData struct {\n\t// UserID 从请求中获取当前的用户\n\tPostID    string `json:\"post_id\" binding:\"required\"`               // 贴子id\n\tDirection int8   `json:\"direction,string\" binding:\"oneof=1 0 -1\" ` // 赞成票(1)还是反对票(-1)取消投票(0)\n}\n\n// ParamPostList 获取帖子列表query string参数\ntype ParamPostList struct {\n\tCommunityID int64  `json:\"community_id\" form:\"community_id\"`   // 可以为空\n\tPage        int64  `json:\"page\" form:\"page\"`                   // 页码\n\tSize        int64  `json:\"size\" form:\"size\"`                   // 每页数据量\n\tOrder       string `json:\"order\" form:\"order\" example:\"score\"` // 排序依据\n}\n"
  },
  {
    "path": "lesson69/bluebell/models/post.go",
    "content": "package models\n\nimport \"time\"\n\n// 内存对齐概念\n\ntype Post struct {\n\tID          int64     `json:\"id,string\" db:\"post_id\"`                            // 帖子id\n\tAuthorID    int64     `json:\"author_id\" db:\"author_id\"`                          // 作者id\n\tCommunityID int64     `json:\"community_id\" db:\"community_id\" binding:\"required\"` // 社区id\n\tStatus      int32     `json:\"status\" db:\"status\"`                                // 帖子状态\n\tTitle       string    `json:\"title\" db:\"title\" binding:\"required\"`               // 帖子标题\n\tContent     string    `json:\"content\" db:\"content\" binding:\"required\"`           // 帖子内容\n\tCreateTime  time.Time `json:\"create_time\" db:\"create_time\"`                      // 帖子创建时间\n}\n\n// ApiPostDetail 帖子详情接口的结构体\ntype ApiPostDetail struct {\n\tAuthorName       string             `json:\"author_name\"` // 作者\n\tVoteNum          int64              `json:\"vote_num\"`    // 投票数\n\t*Post                               // 嵌入帖子结构体\n\t*CommunityDetail `json:\"community\"` // 嵌入社区信息\n}\n"
  },
  {
    "path": "lesson69/bluebell/models/struct_test.go",
    "content": "package models\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"unsafe\"\n)\n\n// Go 内存对齐\n\ntype s1 struct {\n\ta int8   // 1\n\tb string // 3\n\tc int8   // 1\n}\n\ntype s2 struct {\n\ta int8\n\tc int8   // 1\n\tb string // 2\n}\n\nfunc TestStruct(t *testing.T) {\n\tv1 := s1{\n\t\ta: 1,\n\t\tb: \"七米\",\n\t\tc: 2,\n\t}\n\n\tv2 := s2{\n\t\ta: 1,\n\t\tb: \"七米\",\n\t\tc: 2,\n\t}\n\n\tfmt.Println(unsafe.Sizeof(v1), unsafe.Sizeof(v2))\n}\n"
  },
  {
    "path": "lesson69/bluebell/models/user.go",
    "content": "package models\n\ntype User struct {\n\tUserID   int64  `db:\"user_id\"`\n\tUsername string `db:\"username\"`\n\tPassword string `db:\"password\"`\n\tToken    string\n}\n"
  },
  {
    "path": "lesson69/bluebell/pkg/jwt/jwt.go",
    "content": "package jwt\n\nimport (\n\t\"errors\"\n\t\"time\"\n\n\t\"github.com/spf13/viper\"\n\n\t\"github.com/dgrijalva/jwt-go\"\n)\n\nvar mySecret = []byte(\"夏天夏天悄悄过去\")\n\n// MyClaims 自定义声明结构体并内嵌jwt.StandardClaims\n// jwt包自带的jwt.StandardClaims只包含了官方字段\n// 我们这里需要额外记录一个username字段，所以要自定义结构体\n// 如果想要保存更多信息，都可以添加到这个结构体中\ntype MyClaims struct {\n\tUserID   int64  `json:\"user_id\"`\n\tUsername string `json:\"username\"`\n\tjwt.StandardClaims\n}\n\n// GenToken 生成JWT\nfunc GenToken(userID int64, username string) (string, error) {\n\t// 创建一个我们自己的声明的数据\n\tc := MyClaims{\n\t\tuserID,\n\t\t\"username\", // 自定义字段\n\t\tjwt.StandardClaims{\n\t\t\tExpiresAt: time.Now().Add(\n\t\t\t\ttime.Duration(viper.GetInt(\"auth.jwt_expire\")) * time.Hour).Unix(), // 过期时间\n\t\t\tIssuer: \"bluebell\", // 签发人\n\t\t},\n\t}\n\t// 使用指定的签名方法创建签名对象\n\ttoken := jwt.NewWithClaims(jwt.SigningMethodHS256, c)\n\t// 使用指定的secret签名并获得完整的编码后的字符串token\n\treturn token.SignedString(mySecret)\n}\n\n// ParseToken 解析JWT\nfunc ParseToken(tokenString string) (*MyClaims, error) {\n\t// 解析token\n\tvar mc = new(MyClaims)\n\ttoken, err := jwt.ParseWithClaims(tokenString, mc, func(token *jwt.Token) (i interface{}, err error) {\n\t\treturn mySecret, nil\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif token.Valid { // 校验token\n\t\treturn mc, nil\n\t}\n\treturn nil, errors.New(\"invalid token\")\n}\n"
  },
  {
    "path": "lesson69/bluebell/pkg/snowflake/snowflake.go",
    "content": "package snowflake\n\nimport (\n\t\"time\"\n\n\tsf \"github.com/bwmarrin/snowflake\"\n)\n\nvar node *sf.Node\n\nfunc Init(startTime string, machineID int64) (err error) {\n\tvar st time.Time\n\tst, err = time.Parse(\"2006-01-02\", startTime)\n\tif err != nil {\n\t\treturn\n\t}\n\tsf.Epoch = st.UnixNano() / 1000000\n\tnode, err = sf.NewNode(machineID)\n\treturn\n}\nfunc GenID() int64 {\n\treturn node.Generate().Int64()\n}\n"
  },
  {
    "path": "lesson69/bluebell/router/route.go",
    "content": "package router\n\nimport (\n\t\"bluebell/controller\"\n\t\"bluebell/logger\"\n\t\"bluebell/middlewares\"\n\t\"net/http\"\n\n\tginSwagger \"github.com/swaggo/gin-swagger\"\n\t\"github.com/swaggo/gin-swagger/swaggerFiles\"\n\n\t_ \"bluebell/docs\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc SetupRouter(mode string) *gin.Engine {\n\tif mode == gin.ReleaseMode {\n\t\tgin.SetMode(gin.ReleaseMode) // gin设置成发布模式\n\t}\n\tr := gin.New()\n\tr.Use(logger.GinLogger(), logger.GinRecovery(true))\n\n\tr.GET(\"/swagger/*any\", ginSwagger.WrapHandler(swaggerFiles.Handler))\n\n\tv1 := r.Group(\"/api/v1\")\n\n\t// 注册\n\tv1.POST(\"/signup\", controller.SignUpHandler)\n\t// 登录\n\tv1.POST(\"/login\", controller.LoginHandler)\n\n\tv1.Use(middlewares.JWTAuthMiddleware()) // 应用JWT认证中间件\n\n\t{\n\t\tv1.GET(\"/community\", controller.CommunityHandler)\n\t\tv1.GET(\"/community/:id\", controller.CommunityDetailHandler)\n\n\t\tv1.POST(\"/post\", controller.CreatePostHandler)\n\t\tv1.GET(\"/post/:id\", controller.GetPostDetailHandler)\n\t\tv1.GET(\"/posts\", controller.GetPostListHandler)\n\t\t// 根据时间或分数获取帖子列表\n\t\tv1.GET(\"/posts2\", controller.GetPostListHandler2)\n\n\t\t// 投票\n\t\tv1.POST(\"/vote\", controller.PostVoteController)\n\n\t}\n\n\tr.NoRoute(func(c *gin.Context) {\n\t\tc.JSON(http.StatusOK, gin.H{\n\t\t\t\"msg\": \"404\",\n\t\t})\n\t})\n\treturn r\n}\n"
  },
  {
    "path": "lesson69/bluebell/setting/setting.go",
    "content": "package setting\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\t\"github.com/spf13/viper\"\n)\n\nvar Conf = new(AppConfig)\n\ntype AppConfig struct {\n\tName      string `mapstructure:\"name\"`\n\tMode      string `mapstructure:\"mode\"`\n\tVersion   string `mapstructure:\"version\"`\n\tStartTime string `mapstructure:\"start_time\"`\n\tMachineID int64  `mapstructure:\"machine_id\"`\n\tPort      int    `mapstructure:\"port\"`\n\n\t*LogConfig   `mapstructure:\"log\"`\n\t*MySQLConfig `mapstructure:\"mysql\"`\n\t*RedisConfig `mapstructure:\"redis\"`\n}\n\ntype MySQLConfig struct {\n\tHost         string `mapstructure:\"host\"`\n\tUser         string `mapstructure:\"user\"`\n\tPassword     string `mapstructure:\"password\"`\n\tDB           string `mapstructure:\"dbname\"`\n\tPort         int    `mapstructure:\"port\"`\n\tMaxOpenConns int    `mapstructure:\"max_open_conns\"`\n\tMaxIdleConns int    `mapstructure:\"max_idle_conns\"`\n}\n\ntype RedisConfig struct {\n\tHost         string `mapstructure:\"host\"`\n\tPassword     string `mapstructure:\"password\"`\n\tPort         int    `mapstructure:\"port\"`\n\tDB           int    `mapstructure:\"db\"`\n\tPoolSize     int    `mapstructure:\"pool_size\"`\n\tMinIdleConns int    `mapstructure:\"min_idle_conns\"`\n}\n\ntype LogConfig struct {\n\tLevel      string `mapstructure:\"level\"`\n\tFilename   string `mapstructure:\"filename\"`\n\tMaxSize    int    `mapstructure:\"max_size\"`\n\tMaxAge     int    `mapstructure:\"max_age\"`\n\tMaxBackups int    `mapstructure:\"max_backups\"`\n}\n\nfunc Init(filePath string) (err error) {\n\t// 方式1：直接指定配置文件路径（相对路径或者绝对路径）\n\t// 相对路径：相对执行的可执行文件的相对路径\n\t//viper.SetConfigFile(\"./conf/config.yaml\")\n\t// 绝对路径：系统中实际的文件路径\n\t//viper.SetConfigFile(\"/Users/liwenzhou/Desktop/bluebell/conf/config.yaml\")\n\n\t// 方式2：指定配置文件名和配置文件的位置，viper自行查找可用的配置文件\n\t// 配置文件名不需要带后缀\n\t// 配置文件位置可配置多个\n\t//viper.SetConfigName(\"config\") // 指定配置文件名（不带后缀）\n\t//viper.AddConfigPath(\".\") // 指定查找配置文件的路径（这里使用相对路径）\n\t//viper.AddConfigPath(\"./conf\")      // 指定查找配置文件的路径（这里使用相对路径）\n\n\t// 基本上是配合远程配置中心使用的，告诉viper当前的数据使用什么格式去解析\n\t//viper.SetConfigType(\"json\")\n\n\tviper.SetConfigFile(filePath)\n\n\terr = viper.ReadInConfig() // 读取配置信息\n\tif err != nil {\n\t\t// 读取配置信息失败\n\t\tfmt.Printf(\"viper.ReadInConfig failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\n\t// 把读取到的配置信息反序列化到 Conf 变量中\n\tif err := viper.Unmarshal(Conf); err != nil {\n\t\tfmt.Printf(\"viper.Unmarshal failed, err:%v\\n\", err)\n\t}\n\n\tviper.WatchConfig()\n\tviper.OnConfigChange(func(in fsnotify.Event) {\n\t\tfmt.Println(\"配置文件修改了...\")\n\t\tif err := viper.Unmarshal(Conf); err != nil {\n\t\t\tfmt.Printf(\"viper.Unmarshal failed, err:%v\\n\", err)\n\t\t}\n\t})\n\treturn\n}\n"
  },
  {
    "path": "lesson70/bluebell/.air.conf",
    "content": "# [Air](https://github.com/cosmtrek/air) TOML 格式的配置文件\n\n# 工作目录\n# 使用 . 或绝对路径，请注意 `tmp_dir` 目录必须在 `root` 目录下\nroot = \".\"\ntmp_dir = \"tmp\"\n\n[build]\n# 只需要写你平常编译使用的shell命令。你也可以使用 `make`\ncmd = \"swag init && go build -o ./tmp/main\"\n# 由`cmd`命令得到的二进制文件名\nbin = \"tmp/main\"\n# 自定义的二进制，可以添加额外的编译标识例如添加 GIN_MODE=release\nfull_bin = \"./tmp/main ./conf/config.yaml\"\n# 监听以下文件扩展名的文件.\ninclude_ext = [\"go\", \"tpl\", \"tmpl\", \"html\", \"yaml\"]\n# 忽略这些文件扩展名或目录\nexclude_dir = [\"assets\", \"tmp\", \"vendor\", \"frontend/node_modules\"]\n# 监听以下指定目录的文件\ninclude_dir = []\n# 排除以下文件\nexclude_file = []\n# 如果文件更改过于频繁，则没有必要在每次更改时都触发构建。可以设置触发构建的延迟时间\ndelay = 1000 # ms\n# 发生构建错误时，停止运行旧的二进制文件。\nstop_on_error = true\n# air的日志文件名，该日志文件放置在你的`tmp_dir`中\nlog = \"air_errors.log\"\n\n[log]\n# 显示日志时间\ntime = true\n\n[color]\n# 自定义每个部分显示的颜色。如果找不到颜色，使用原始的应用程序日志。\nmain = \"magenta\"\nwatcher = \"cyan\"\nbuild = \"yellow\"\nrunner = \"green\"\n\n[misc]\n# 退出时删除tmp目录\nclean_on_exit = true"
  },
  {
    "path": "lesson70/bluebell/Makefile",
    "content": ".PHONY: all build run gotool clean help\n\nBINARY=\"xx\"\n\nall: gotool build\n\nbuild:\n\tCGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ${BINARY}\n\nrun:\n\t@go run ./main.go conf/config.yaml\n\ngotool:\n\tgo fmt ./\n\tgo vet ./\n\nclean:\n\t@if [ -f ${BINARY} ] ; then rm ${BINARY} ; fi\n\nhelp:\n\t@echo \"make - 格式化 Go 代码, 并编译生成二进制文件\"\n\t@echo \"make build - 编译 Go 代码, 生成二进制文件\"\n\t@echo \"make run - 直接运行 Go 代码\"\n\t@echo \"make clean - 移除二进制文件和 vim swap files\"\n\t@echo \"make gotool - 运行 Go 工具 'fmt' and 'vet'\"\n"
  },
  {
    "path": "lesson70/bluebell/conf/config.yaml",
    "content": "name: \"bluebell\"\nmode: \"dev\"\nport: 8084\nversion: \"v0.0.1\"\nstart_time: \"2020-07-01\"\nmachine_id: 1\n\nauth:\n  jwt_expire: 8760\n\nlog:\n  level: \"debug\"\n  filename: \"web_app.log\"\n  max_size: 200\n  max_age: 30\n  max_backups: 7\nmysql:\n  host: \"127.0.0.1\"\n  port: 13306\n  user: \"root\"\n  password: \"root1234\"\n  dbname: \"bluebell\"\n  max_open_conns: 200\n  max_idle_conns: 50\nredis:\n  host: \"127.0.0.1\"\n  port: 16379\n  password: \"\"\n  db: 0\n  pool_size: 100"
  },
  {
    "path": "lesson70/bluebell/controller/code.go",
    "content": "package controller\n\ntype ResCode int64\n\nconst (\n\tCodeSuccess ResCode = 1000 + iota\n\tCodeInvalidParam\n\tCodeUserExist\n\tCodeUserNotExist\n\tCodeInvalidPassword\n\tCodeServerBusy\n\n\tCodeNeedLogin\n\tCodeInvalidToken\n)\n\nvar codeMsgMap = map[ResCode]string{\n\tCodeSuccess:         \"success\",\n\tCodeInvalidParam:    \"请求参数错误\",\n\tCodeUserExist:       \"用户名已存在\",\n\tCodeUserNotExist:    \"用户名不存在\",\n\tCodeInvalidPassword: \"用户名或密码错误\",\n\tCodeServerBusy:      \"服务繁忙\",\n\n\tCodeNeedLogin:    \"需要登录\",\n\tCodeInvalidToken: \"无效的token\",\n}\n\nfunc (c ResCode) Msg() string {\n\tmsg, ok := codeMsgMap[c]\n\tif !ok {\n\t\tmsg = codeMsgMap[CodeServerBusy]\n\t}\n\treturn msg\n}\n"
  },
  {
    "path": "lesson70/bluebell/controller/community.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/logic\"\n\t\"strconv\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"go.uber.org/zap\"\n)\n\n// ---- 跟社区相关的 ----\n\nfunc CommunityHandler(c *gin.Context) {\n\t// 查询到所有的社区（community_id, community_name) 以列表的形式返回\n\tdata, err := logic.GetCommunityList()\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetCommunityList() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy) // 不轻易把服务端报错暴露给外面\n\t\treturn\n\t}\n\tResponseSuccess(c, data)\n}\n\n// CommunityDetailHandler 社区分类详情\nfunc CommunityDetailHandler(c *gin.Context) {\n\t// 1. 获取社区id\n\tidStr := c.Param(\"id\") // 获取URL参数\n\tid, err := strconv.ParseInt(idStr, 10, 64)\n\tif err != nil {\n\t\tResponseError(c, CodeInvalidParam)\n\t\treturn\n\t}\n\n\t// 2. 根据id获取社区详情\n\tdata, err := logic.GetCommunityDetail(id)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetCommunityList() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy) // 不轻易把服务端报错暴露给外面\n\t\treturn\n\t}\n\tResponseSuccess(c, data)\n}\n"
  },
  {
    "path": "lesson70/bluebell/controller/doc_response_models.go",
    "content": "package controller\n\nimport \"bluebell/models\"\n\n// 专门用来放接口文档用到的model\n// 因为我们的接口文档返回的数据格式是一致的，但是具体的data类型不一致\n\n// _ResponsePostList 帖子列表接口响应数据\ntype _ResponsePostList struct {\n\tCode    ResCode                 `json:\"code\"`    // 业务响应状态码\n\tMessage string                  `json:\"message\"` // 提示信息\n\tData    []*models.ApiPostDetail `json:\"data\"`    // 数据\n}\n"
  },
  {
    "path": "lesson70/bluebell/controller/post.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/logic\"\n\t\"bluebell/models\"\n\t\"strconv\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"go.uber.org/zap\"\n\t// swagger 嵌入文件\n)\n\n// CreatePostHandler 创建帖子的处理函数\nfunc CreatePostHandler(c *gin.Context) {\n\t// 1. 获取参数及参数的校验\n\t//c.ShouldBindJSON()  // validator --> binding tag\n\tp := new(models.Post)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\tzap.L().Debug(\"c.ShouldBindJSON(p) error\", zap.Any(\"err\", err))\n\t\tzap.L().Error(\"create post with invalid param\")\n\t\tResponseError(c, CodeInvalidParam)\n\t\treturn\n\t}\n\t// 从 c 取到当前发请求的用户的ID\n\tuserID, err := getCurrentUserID(c)\n\tif err != nil {\n\t\tResponseError(c, CodeNeedLogin)\n\t\treturn\n\t}\n\tp.AuthorID = userID\n\t// 2. 创建帖子\n\tif err := logic.CreatePost(p); err != nil {\n\t\tzap.L().Error(\"logic.CreatePost(p) failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\n\t// 3. 返回响应\n\tResponseSuccess(c, nil)\n}\n\n// GetPostDetailHandler 获取帖子详情的处理函数\nfunc GetPostDetailHandler(c *gin.Context) {\n\t// 1. 获取参数（从URL中获取帖子的id）\n\tpidStr := c.Param(\"id\")\n\tpid, err := strconv.ParseInt(pidStr, 10, 64)\n\tif err != nil {\n\t\tzap.L().Error(\"get post detail with invalid param\", zap.Error(err))\n\t\tResponseError(c, CodeInvalidParam)\n\t\treturn\n\t}\n\n\t// 2. 根据id取出帖子数据（查数据库）\n\tdata, err := logic.GetPostById(pid)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetPostById(pid) failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\t// 3. 返回响应\n\tResponseSuccess(c, data)\n}\n\n// GetPostListHandler 获取帖子列表的处理函数\nfunc GetPostListHandler(c *gin.Context) {\n\t// 获取分页参数\n\tpage, size := getPageInfo(c)\n\t// 获取数据\n\tdata, err := logic.GetPostList(page, size)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetPostList() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\tResponseSuccess(c, data)\n\t// 返回响应\n}\n\n// GetPostListHandler2 升级版帖子列表接口\n// @Summary 升级版帖子列表接口\n// @Description 可按社区按时间或分数排序查询帖子列表接口\n// @Tags 帖子相关接口(api分组展示使用的)\n// @Accept application/json\n// @Produce application/json\n// @Param Authorization header string true \"Bearer JWT\"\n// @Param object query models.ParamPostList false \"查询参数\"\n// @Security ApiKeyAuth\n// @Success 200 {object} _ResponsePostList\n// @Router /posts2 [get]\nfunc GetPostListHandler2(c *gin.Context) {\n\t// GET请求参数(query string)：/api/v1/posts2?page=1&size=10&order=time\n\t// 初始化结构体时指定初始参数\n\tp := &models.ParamPostList{\n\t\tPage:  1,\n\t\tSize:  10,\n\t\tOrder: models.OrderTime, // magic string\n\t}\n\t//c.ShouldBind()  根据请求的数据类型选择相应的方法去获取数据\n\t//c.ShouldBindJSON() 如果请求中携带的是json格式的数据，才能用这个方法获取到数据\n\tif err := c.ShouldBindQuery(p); err != nil {\n\t\tzap.L().Error(\"GetPostListHandler2 with invalid params\", zap.Error(err))\n\t\tResponseError(c, CodeInvalidParam)\n\t\treturn\n\t}\n\tdata, err := logic.GetPostListNew(p) // 更新：合二为一\n\t// 获取数据\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetPostList() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\tResponseSuccess(c, data)\n\t// 返回响应\n}\n\n// 根据社区去查询帖子列表\n//func GetCommunityPostListHandler(c *gin.Context) {\n//\t// 初始化结构体时指定初始参数\n//\tp := &models.ParamCommunityPostList{\n//\t\tParamPostList: &models.ParamPostList{\n//\t\t\tPage:  1,\n//\t\t\tSize:  10,\n//\t\t\tOrder: models.OrderTime,\n//\t\t},\n//\t}\n//\t//c.ShouldBind()  根据请求的数据类型选择相应的方法去获取数据\n//\t//c.ShouldBindJSON() 如果请求中携带的是json格式的数据，才能用这个方法获取到数据\n//\tif err := c.ShouldBindQuery(p); err != nil {\n//\t\tzap.L().Error(\"GetCommunityPostListHandler with invalid params\", zap.Error(err))\n//\t\tResponseError(c, CodeInvalidParam)\n//\t\treturn\n//\t}\n//\n//\t// 获取数据\n//\tdata, err := logic.GetCommunityPostList(p)\n//\tif err != nil {\n//\t\tzap.L().Error(\"logic.GetPostList() failed\", zap.Error(err))\n//\t\tResponseError(c, CodeServerBusy)\n//\t\treturn\n//\t}\n//\tResponseSuccess(c, data)\n//}\n"
  },
  {
    "path": "lesson70/bluebell/controller/post_test.go",
    "content": "package controller\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestCreatePostHandler(t *testing.T) {\n\n\tgin.SetMode(gin.TestMode)\n\tr := gin.Default()\n\turl := \"/api/v1/post\"\n\tr.POST(url, CreatePostHandler)\n\n\tbody := `{\n\t\t\"community_id\": 1,\n\t\t\"title\": \"test\",\n\t\t\"content\": \"just a test\"\n\t}`\n\n\treq, _ := http.NewRequest(http.MethodPost, url, bytes.NewReader([]byte(body)))\n\n\tw := httptest.NewRecorder()\n\tr.ServeHTTP(w, req)\n\n\tassert.Equal(t, 200, w.Code)\n\n\t// 判断响应的内容是不是按预期返回了需要登录的错误\n\n\t// 方法1：判断响应内容中是不是包含指定的字符串\n\t//assert.Contains(t, w.Body.String(), \"需要登录\")\n\n\t// 方法2：将响应的内容反序列化到ResponseData 然后判断字段与预期是否一致\n\tres := new(ResponseData)\n\tif err := json.Unmarshal(w.Body.Bytes(), res); err != nil {\n\t\tt.Fatalf(\"json.Unmarshal w.Body failed, err:%v\\n\", err)\n\t}\n\tassert.Equal(t, res.Code, CodeNeedLogin)\n}\n"
  },
  {
    "path": "lesson70/bluebell/controller/request.go",
    "content": "package controller\n\nimport (\n\t\"errors\"\n\t\"strconv\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\nconst CtxUserIDKey = \"userID\"\n\nvar ErrorUserNotLogin = errors.New(\"用户未登录\")\n\n// getCurrentUserID 获取当前登录的用户ID\nfunc getCurrentUserID(c *gin.Context) (userID int64, err error) {\n\tuid, ok := c.Get(CtxUserIDKey)\n\tif !ok {\n\t\terr = ErrorUserNotLogin\n\t\treturn\n\t}\n\tuserID, ok = uid.(int64)\n\tif !ok {\n\t\terr = ErrorUserNotLogin\n\t\treturn\n\t}\n\treturn\n}\n\nfunc getPageInfo(c *gin.Context) (int64, int64) {\n\tpageStr := c.Query(\"page\")\n\tsizeStr := c.Query(\"size\")\n\n\tvar (\n\t\tpage int64\n\t\tsize int64\n\t\terr  error\n\t)\n\n\tpage, err = strconv.ParseInt(pageStr, 10, 64)\n\tif err != nil {\n\t\tpage = 1\n\t}\n\tsize, err = strconv.ParseInt(sizeStr, 10, 64)\n\tif err != nil {\n\t\tsize = 10\n\t}\n\treturn page, size\n}\n"
  },
  {
    "path": "lesson70/bluebell/controller/response.go",
    "content": "package controller\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n/*\n{\n\t\"code\": 10000, // 程序中的错误码\n\t\"msg\": xx,     // 提示信息\n\t\"data\": {},    // 数据\n}\n\n*/\n\ntype ResponseData struct {\n\tCode ResCode     `json:\"code\"`\n\tMsg  interface{} `json:\"msg\"`\n\tData interface{} `json:\"data,omitempty\"`\n}\n\nfunc ResponseError(c *gin.Context, code ResCode) {\n\tc.JSON(http.StatusOK, &ResponseData{\n\t\tCode: code,\n\t\tMsg:  code.Msg(),\n\t\tData: nil,\n\t})\n}\n\nfunc ResponseErrorWithMsg(c *gin.Context, code ResCode, msg interface{}) {\n\tc.JSON(http.StatusOK, &ResponseData{\n\t\tCode: code,\n\t\tMsg:  msg,\n\t\tData: nil,\n\t})\n}\n\nfunc ResponseSuccess(c *gin.Context, data interface{}) {\n\tc.JSON(http.StatusOK, &ResponseData{\n\t\tCode: CodeSuccess,\n\t\tMsg:  CodeSuccess.Msg(),\n\t\tData: data,\n\t})\n}\n"
  },
  {
    "path": "lesson70/bluebell/controller/user.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/logic\"\n\t\"bluebell/models\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/go-playground/validator/v10\"\n\t\"go.uber.org/zap\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// SignUpHandler 处理注册请求的函数\nfunc SignUpHandler(c *gin.Context) {\n\t// 1. 获取参数和参数校验\n\tp := new(models.ParamSignUp)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\t// 请求参数有误，直接返回响应\n\t\tzap.L().Error(\"SignUp with invalid param\", zap.Error(err))\n\t\t// 判断err是不是validator.ValidationErrors 类型\n\t\terrs, ok := err.(validator.ValidationErrors)\n\t\tif !ok {\n\t\t\tResponseError(c, CodeInvalidParam)\n\t\t\treturn\n\t\t}\n\t\tResponseErrorWithMsg(c, CodeInvalidParam, removeTopStruct(errs.Translate(trans)))\n\t\treturn\n\t}\n\t// 2. 业务处理\n\tif err := logic.SignUp(p); err != nil {\n\t\tzap.L().Error(\"logic.SignUp failed\", zap.Error(err))\n\t\tif errors.Is(err, mysql.ErrorUserExist) {\n\t\t\tResponseError(c, CodeUserExist)\n\t\t\treturn\n\t\t}\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\t// 3. 返回响应\n\tResponseSuccess(c, nil)\n}\n\nfunc LoginHandler(c *gin.Context) {\n\t// 1.获取请求参数及参数校验\n\tp := new(models.ParamLogin)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\t// 请求参数有误，直接返回响应\n\t\tzap.L().Error(\"Login with invalid param\", zap.Error(err))\n\t\t// 判断err是不是validator.ValidationErrors 类型\n\t\terrs, ok := err.(validator.ValidationErrors)\n\t\tif !ok {\n\t\t\tResponseError(c, CodeInvalidParam)\n\t\t\treturn\n\t\t}\n\t\tResponseErrorWithMsg(c, CodeInvalidParam, removeTopStruct(errs.Translate(trans)))\n\t\treturn\n\t}\n\t// 2.业务逻辑处理\n\tuser, err := logic.Login(p)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.Login failed\", zap.String(\"username\", p.Username), zap.Error(err))\n\t\tif errors.Is(err, mysql.ErrorUserNotExist) {\n\t\t\tResponseError(c, CodeUserNotExist)\n\t\t\treturn\n\t\t}\n\t\tResponseError(c, CodeInvalidPassword)\n\t\treturn\n\t}\n\n\t// 3.返回响应\n\tResponseSuccess(c, gin.H{\n\t\t\"user_id\":   fmt.Sprintf(\"%d\", user.UserID), // id值大于1<<53-1  int64类型的最大值是1<<63-1\n\t\t\"user_name\": user.Username,\n\t\t\"token\":     user.Token,\n\t})\n}\n"
  },
  {
    "path": "lesson70/bluebell/controller/validator.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/models\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin/binding\"\n\t\"github.com/go-playground/locales/en\"\n\t\"github.com/go-playground/locales/zh\"\n\tut \"github.com/go-playground/universal-translator\"\n\t\"github.com/go-playground/validator/v10\"\n\tenTranslations \"github.com/go-playground/validator/v10/translations/en\"\n\tzhTranslations \"github.com/go-playground/validator/v10/translations/zh\"\n)\n\n// 定义一个全局翻译器T\nvar trans ut.Translator\n\n// InitTrans 初始化翻译器\nfunc InitTrans(locale string) (err error) {\n\t// 修改gin框架中的Validator引擎属性，实现自定制\n\tif v, ok := binding.Validator.Engine().(*validator.Validate); ok {\n\n\t\t// 注册一个获取json tag的自定义方法\n\t\tv.RegisterTagNameFunc(func(fld reflect.StructField) string {\n\t\t\tname := strings.SplitN(fld.Tag.Get(\"json\"), \",\", 2)[0]\n\t\t\tif name == \"-\" {\n\t\t\t\treturn \"\"\n\t\t\t}\n\t\t\treturn name\n\t\t})\n\n\t\t// 为SignUpParam注册自定义校验方法\n\t\tv.RegisterStructValidation(SignUpParamStructLevelValidation, models.ParamSignUp{})\n\n\t\tzhT := zh.New() // 中文翻译器\n\t\tenT := en.New() // 英文翻译器\n\n\t\t// 第一个参数是备用（fallback）的语言环境\n\t\t// 后面的参数是应该支持的语言环境（支持多个）\n\t\t// uni := ut.New(zhT, zhT) 也是可以的\n\t\tuni := ut.New(enT, zhT, enT)\n\n\t\t// locale 通常取决于 http 请求头的 'Accept-Language'\n\t\tvar ok bool\n\t\t// 也可以使用 uni.FindTranslator(...) 传入多个locale进行查找\n\t\ttrans, ok = uni.GetTranslator(locale)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"uni.GetTranslator(%s) failed\", locale)\n\t\t}\n\n\t\t// 注册翻译器\n\t\tswitch locale {\n\t\tcase \"en\":\n\t\t\terr = enTranslations.RegisterDefaultTranslations(v, trans)\n\t\tcase \"zh\":\n\t\t\terr = zhTranslations.RegisterDefaultTranslations(v, trans)\n\t\tdefault:\n\t\t\terr = enTranslations.RegisterDefaultTranslations(v, trans)\n\t\t}\n\t\treturn\n\t}\n\treturn\n}\n\n// removeTopStruct 去除提示信息中的结构体名称\nfunc removeTopStruct(fields map[string]string) map[string]string {\n\tres := map[string]string{}\n\tfor field, err := range fields {\n\t\tres[field[strings.Index(field, \".\")+1:]] = err\n\t}\n\treturn res\n}\n\n// SignUpParamStructLevelValidation 自定义SignUpParam结构体校验函数\nfunc SignUpParamStructLevelValidation(sl validator.StructLevel) {\n\tsu := sl.Current().Interface().(models.ParamSignUp)\n\n\tif su.Password != su.RePassword {\n\t\t// 输出错误提示信息，最后一个参数就是传递的param\n\t\tsl.ReportError(su.RePassword, \"re_password\", \"RePassword\", \"eqfield\", \"password\")\n\t}\n}\n"
  },
  {
    "path": "lesson70/bluebell/controller/vote.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/logic\"\n\t\"bluebell/models\"\n\n\t\"go.uber.org/zap\"\n\n\t\"github.com/go-playground/validator/v10\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// 投票\n\n//type VoteData struct {\n//\t// UserID 从请求中获取当前的用户\n//\tPostID    int64 `json:\"post_id,string\"`   // 贴子id\n//\tDirection int   `json:\"direction,string\"` // 赞成票(1)还是反对票(-1)\n//}\n\nfunc PostVoteController(c *gin.Context) {\n\t// 参数校验\n\tp := new(models.ParamVoteData)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\terrs, ok := err.(validator.ValidationErrors) // 类型断言\n\t\tif !ok {\n\t\t\tResponseError(c, CodeInvalidParam)\n\t\t\treturn\n\t\t}\n\t\terrData := removeTopStruct(errs.Translate(trans)) // 翻译并去除掉错误提示中的结构体标识\n\t\tResponseErrorWithMsg(c, CodeInvalidParam, errData)\n\t\treturn\n\t}\n\t// 获取当前请求的用户的id\n\tuserID, err := getCurrentUserID(c)\n\tif err != nil {\n\t\tResponseError(c, CodeNeedLogin)\n\t\treturn\n\t}\n\t// 具体投票的业务逻辑\n\tif err := logic.VoteForPost(userID, p); err != nil {\n\t\tzap.L().Error(\"logic.VoteForPost() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\n\tResponseSuccess(c, nil)\n}\n"
  },
  {
    "path": "lesson70/bluebell/dao/mysql/community.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/models\"\n\t\"database/sql\"\n\n\t\"go.uber.org/zap\"\n)\n\nfunc GetCommunityList() (communityList []*models.Community, err error) {\n\tsqlStr := \"select community_id, community_name from community\"\n\tif err := db.Select(&communityList, sqlStr); err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\tzap.L().Warn(\"there is no community in db\")\n\t\t\terr = nil\n\t\t}\n\t}\n\treturn\n}\n\n// GetCommunityDetailByID 根据ID查询社区详情\nfunc GetCommunityDetailByID(id int64) (community *models.CommunityDetail, err error) {\n\tcommunity = new(models.CommunityDetail)\n\tsqlStr := `select \n\t\t\tcommunity_id, community_name, introduction, create_time\n\t\t\tfrom community \n\t\t\twhere community_id = ?\n\t`\n\tif err := db.Get(community, sqlStr, id); err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\terr = ErrorInvalidID\n\t\t}\n\t}\n\treturn community, err\n}\n"
  },
  {
    "path": "lesson70/bluebell/dao/mysql/error_code.go",
    "content": "package mysql\n\nimport \"errors\"\n\nvar (\n\tErrorUserExist       = errors.New(\"用户已存在\")\n\tErrorUserNotExist    = errors.New(\"用户不存在\")\n\tErrorInvalidPassword = errors.New(\"用户名或密码错误\")\n\tErrorInvalidID       = errors.New(\"无效的ID\")\n)\n"
  },
  {
    "path": "lesson70/bluebell/dao/mysql/mysql.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/setting\"\n\t\"fmt\"\n\n\t_ \"github.com/go-sql-driver/mysql\"\n\t\"github.com/jmoiron/sqlx\"\n)\n\nvar db *sqlx.DB\n\n// Init 初始化MySQL连接\nfunc Init(cfg *setting.MySQLConfig) (err error) {\n\t// \"user:password@tcp(host:port)/dbname\"\n\tdsn := fmt.Sprintf(\"%s:%s@tcp(%s:%d)/%s?parseTime=true&loc=Local\", cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.DB)\n\tdb, err = sqlx.Connect(\"mysql\", dsn)\n\tif err != nil {\n\t\treturn\n\t}\n\tdb.SetMaxOpenConns(cfg.MaxOpenConns)\n\tdb.SetMaxIdleConns(cfg.MaxIdleConns)\n\treturn\n}\n\n// Close 关闭MySQL连接\nfunc Close() {\n\t_ = db.Close()\n}\n"
  },
  {
    "path": "lesson70/bluebell/dao/mysql/post.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/models\"\n\t\"strings\"\n\n\t\"github.com/jmoiron/sqlx\"\n)\n\n// CreatePost 创建帖子\nfunc CreatePost(p *models.Post) (err error) {\n\tsqlStr := `insert into post(\n\tpost_id, title, content, author_id, community_id)\n\tvalues (?, ?, ?, ?, ?)\n\t`\n\t_, err = db.Exec(sqlStr, p.ID, p.Title, p.Content, p.AuthorID, p.CommunityID)\n\treturn\n}\n\n// GetPostById 根据id查询单个贴子数据\nfunc GetPostById(pid int64) (post *models.Post, err error) {\n\tpost = new(models.Post)\n\tsqlStr := `select\n\tpost_id, title, content, author_id, community_id, create_time\n\tfrom post\n\twhere post_id = ?\n\t`\n\terr = db.Get(post, sqlStr, pid)\n\treturn\n}\n\n// GetPostList 查询帖子列表函数\nfunc GetPostList(page, size int64) (posts []*models.Post, err error) {\n\tsqlStr := `select \n\tpost_id, title, content, author_id, community_id, create_time\n\tfrom post\n\tORDER BY create_time\n\tDESC\n\tlimit ?,?\n\t`\n\tposts = make([]*models.Post, 0, 2) // 不要写成make([]*models.Post, 2)\n\terr = db.Select(&posts, sqlStr, (page-1)*size, size)\n\treturn\n}\n\n// GetPostListByIDs 根据给定的id列表查询帖子数据\nfunc GetPostListByIDs(ids []string) (postList []*models.Post, err error) {\n\tsqlStr := `select post_id, title, content, author_id, community_id, create_time\n\tfrom post\n\twhere post_id in (?)\n\torder by FIND_IN_SET(post_id, ?)\n\t`\n\t// https: //www.liwenzhou.com/posts/Go/sqlx/\n\tquery, args, err := sqlx.In(sqlStr, ids, strings.Join(ids, \",\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tquery = db.Rebind(query)\n\terr = db.Select(&postList, query, args...) // !!!!!!\n\treturn\n}\n"
  },
  {
    "path": "lesson70/bluebell/dao/mysql/post_test.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/models\"\n\t\"bluebell/setting\"\n\t\"testing\"\n)\n\nfunc init() {\n\tdbCfg := setting.MySQLConfig{\n\t\tHost:         \"127.0.0.1\",\n\t\tUser:         \"root\",\n\t\tPassword:     \"root1234\",\n\t\tDB:           \"bluebell\",\n\t\tPort:         13306,\n\t\tMaxOpenConns: 10,\n\t\tMaxIdleConns: 10,\n\t}\n\terr := Init(&dbCfg)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc TestCreatePost(t *testing.T) {\n\tpost := models.Post{\n\t\tID:          10,\n\t\tAuthorID:    123,\n\t\tCommunityID: 1,\n\t\tTitle:       \"test\",\n\t\tContent:     \"just a test\",\n\t}\n\terr := CreatePost(&post)\n\tif err != nil {\n\t\tt.Fatalf(\"CreatePost insert record into mysql failed, err:%v\\n\", err)\n\t}\n\tt.Logf(\"CreatePost insert record into mysql success\")\n}\n"
  },
  {
    "path": "lesson70/bluebell/dao/mysql/user.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/models\"\n\t\"crypto/md5\"\n\t\"database/sql\"\n\t\"encoding/hex\"\n)\n\n// 把每一步数据库操作封装成函数\n// 待logic层根据业务需求调用\n\nconst secret = \"liwenzhou.com\"\n\n// CheckUserExist 检查指定用户名的用户是否存在\nfunc CheckUserExist(username string) (err error) {\n\tsqlStr := `select count(user_id) from user where username = ?`\n\tvar count int64\n\tif err := db.Get(&count, sqlStr, username); err != nil {\n\t\treturn err\n\t}\n\tif count > 0 {\n\t\treturn ErrorUserExist\n\t}\n\treturn\n}\n\n// InsertUser 想数据库中插入一条新的用户记录\nfunc InsertUser(user *models.User) (err error) {\n\t// 对密码进行加密\n\tuser.Password = encryptPassword(user.Password)\n\t// 执行SQL语句入库\n\tsqlStr := `insert into user(user_id, username, password) values(?,?,?)`\n\t_, err = db.Exec(sqlStr, user.UserID, user.Username, user.Password)\n\treturn\n}\n\n// encryptPassword 密码加密\nfunc encryptPassword(oPassword string) string {\n\th := md5.New()\n\th.Write([]byte(secret))\n\treturn hex.EncodeToString(h.Sum([]byte(oPassword)))\n}\n\nfunc Login(user *models.User) (err error) {\n\toPassword := user.Password // 用户登录的密码\n\tsqlStr := `select user_id, username, password from user where username=?`\n\terr = db.Get(user, sqlStr, user.Username)\n\tif err == sql.ErrNoRows {\n\t\treturn ErrorUserNotExist\n\t}\n\tif err != nil {\n\t\t// 查询数据库失败\n\t\treturn err\n\t}\n\t// 判断密码是否正确\n\tpassword := encryptPassword(oPassword)\n\tif password != user.Password {\n\t\treturn ErrorInvalidPassword\n\t}\n\treturn\n}\n\n// GetUserById 根据id获取用户信息\nfunc GetUserById(uid int64) (user *models.User, err error) {\n\tuser = new(models.User)\n\tsqlStr := `select user_id, username from user where user_id = ?`\n\terr = db.Get(user, sqlStr, uid)\n\treturn\n}\n"
  },
  {
    "path": "lesson70/bluebell/dao/redis/keys.go",
    "content": "package redis\n\n// redis key\n\n// redis key注意使用命名空间的方式,方便查询和拆分\n\nconst (\n\tPrefix             = \"bluebell:\"   // 项目key前缀\n\tKeyPostTimeZSet    = \"post:time\"   // zset;贴子及发帖时间\n\tKeyPostScoreZSet   = \"post:score\"  // zset;贴子及投票的分数\n\tKeyPostVotedZSetPF = \"post:voted:\" // zset;记录用户及投票类型;参数是post id\n\n\tKeyCommunitySetPF = \"community:\" // set;保存每个分区下帖子的id\n)\n\n// 给redis key加上前缀\nfunc getRedisKey(key string) string {\n\treturn Prefix + key\n}\n"
  },
  {
    "path": "lesson70/bluebell/dao/redis/post.go",
    "content": "package redis\n\nimport (\n\t\"bluebell/models\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/go-redis/redis\"\n)\n\nfunc getIDsFormKey(key string, page, size int64) ([]string, error) {\n\tstart := (page - 1) * size\n\tend := start + size - 1\n\t// 3. ZREVRANGE 按分数从大到小的顺序查询指定数量的元素\n\treturn client.ZRevRange(key, start, end).Result()\n}\n\nfunc GetPostIDsInOrder(p *models.ParamPostList) ([]string, error) {\n\t// 从redis获取id\n\t// 1. 根据用户请求中携带的order参数确定要查询的redis key\n\tkey := getRedisKey(KeyPostTimeZSet)\n\tif p.Order == models.OrderScore {\n\t\tkey = getRedisKey(KeyPostScoreZSet)\n\t}\n\t// 2. 确定查询的索引起始点\n\treturn getIDsFormKey(key, p.Page, p.Size)\n}\n\n// GetPostVoteData 根据ids查询每篇帖子的投赞成票的数据\nfunc GetPostVoteData(ids []string) (data []int64, err error) {\n\t//data = make([]int64, 0, len(ids))\n\t//for _, id := range ids {\n\t//\tkey := getRedisKey(KeyPostVotedZSetPF + id)\n\t//\t// 查找key中分数是1的元素的数量->统计每篇帖子的赞成票的数量\n\t//\tv := client.ZCount(key, \"1\", \"1\").Val()\n\t//\tdata = append(data, v)\n\t//}\n\t// 使用pipeline一次发送多条命令,减少RTT\n\tpipeline := client.Pipeline()\n\tfor _, id := range ids {\n\t\tkey := getRedisKey(KeyPostVotedZSetPF + id)\n\t\tpipeline.ZCount(key, \"1\", \"1\")\n\t}\n\tcmders, err := pipeline.Exec()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdata = make([]int64, 0, len(cmders))\n\tfor _, cmder := range cmders {\n\t\tv := cmder.(*redis.IntCmd).Val()\n\t\tdata = append(data, v)\n\t}\n\treturn\n}\n\n// GetCommunityPostIDsInOrder 按社区查询ids\nfunc GetCommunityPostIDsInOrder(p *models.ParamPostList) ([]string, error) {\n\n\torderKey := getRedisKey(KeyPostTimeZSet)\n\tif p.Order == models.OrderScore {\n\t\torderKey = getRedisKey(KeyPostScoreZSet)\n\t}\n\n\t// 使用 zinterstore 把分区的帖子set与帖子分数的 zset 生成一个新的zset\n\t// 针对新的zset 按之前的逻辑取数据\n\n\t// 社区的key\n\tcKey := getRedisKey(KeyCommunitySetPF + strconv.Itoa(int(p.CommunityID)))\n\n\t// 利用缓存key减少zinterstore执行的次数\n\tkey := orderKey + strconv.Itoa(int(p.CommunityID))\n\tif client.Exists(key).Val() < 1 {\n\t\t// 不存在，需要计算\n\t\tpipeline := client.Pipeline()\n\t\tpipeline.ZInterStore(key, redis.ZStore{\n\t\t\tAggregate: \"MAX\",\n\t\t}, cKey, orderKey) // zinterstore 计算\n\t\tpipeline.Expire(key, 60*time.Second) // 设置超时时间\n\t\t_, err := pipeline.Exec()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\t// 存在的话就直接根据key查询ids\n\treturn getIDsFormKey(key, p.Page, p.Size)\n}\n"
  },
  {
    "path": "lesson70/bluebell/dao/redis/redis.go",
    "content": "package redis\n\nimport (\n\t\"bluebell/setting\"\n\t\"fmt\"\n\n\t\"github.com/go-redis/redis\"\n)\n\nvar (\n\tclient *redis.Client\n\tNil    = redis.Nil\n)\n\n// Init 初始化连接\nfunc Init(cfg *setting.RedisConfig) (err error) {\n\tclient = redis.NewClient(&redis.Options{\n\t\tAddr:         fmt.Sprintf(\"%s:%d\", cfg.Host, cfg.Port),\n\t\tPassword:     cfg.Password, // no password set\n\t\tDB:           cfg.DB,       // use default DB\n\t\tPoolSize:     cfg.PoolSize,\n\t\tMinIdleConns: cfg.MinIdleConns,\n\t})\n\n\t_, err = client.Ping().Result()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc Close() {\n\t_ = client.Close()\n}\n"
  },
  {
    "path": "lesson70/bluebell/dao/redis/vote.go",
    "content": "package redis\n\nimport (\n\t\"errors\"\n\t\"math\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/go-redis/redis\"\n)\n\n// 推荐阅读\n// 基于用户投票的相关算法：http://www.ruanyifeng.com/blog/algorithm/\n\n// 本项目使用简化版的投票分数\n// 投一票就加432分   86400/200  --> 200张赞成票可以给你的帖子续一天\n\n/* 投票的几种情况：\n   direction=1时，有两种情况：\n   \t1. 之前没有投过票，现在投赞成票    --> 更新分数和投票记录  差值的绝对值：1  +432\n   \t2. 之前投反对票，现在改投赞成票    --> 更新分数和投票记录  差值的绝对值：2  +432*2\n   direction=0时，有两种情况：\n   \t1. 之前投过反对票，现在要取消投票  --> 更新分数和投票记录  差值的绝对值：1  +432\n\t2. 之前投过赞成票，现在要取消投票  --> 更新分数和投票记录  差值的绝对值：1  -432\n   direction=-1时，有两种情况：\n   \t1. 之前没有投过票，现在投反对票    --> 更新分数和投票记录  差值的绝对值：1  -432\n   \t2. 之前投赞成票，现在改投反对票    --> 更新分数和投票记录  差值的绝对值：2  -432*2\n\n   投票的限制：\n   每个贴子自发表之日起一个星期之内允许用户投票，超过一个星期就不允许再投票了。\n   \t1. 到期之后将redis中保存的赞成票数及反对票数存储到mysql表中\n   \t2. 到期之后删除那个 KeyPostVotedZSetPF\n*/\n\nconst (\n\toneWeekInSeconds = 7 * 24 * 3600\n\tscorePerVote     = 432 // 每一票值多少分\n)\n\nvar (\n\tErrVoteTimeExpire = errors.New(\"投票时间已过\")\n\tErrVoteRepeated   = errors.New(\"不允许重复投票\")\n)\n\nfunc CreatePost(postID, communityID int64) error {\n\tpipeline := client.TxPipeline()\n\t// 帖子时间\n\tpipeline.ZAdd(getRedisKey(KeyPostTimeZSet), redis.Z{\n\t\tScore:  float64(time.Now().Unix()),\n\t\tMember: postID,\n\t})\n\n\t// 帖子分数\n\tpipeline.ZAdd(getRedisKey(KeyPostScoreZSet), redis.Z{\n\t\tScore:  float64(time.Now().Unix()),\n\t\tMember: postID,\n\t})\n\t// 更新：把帖子id加到社区的set\n\tcKey := getRedisKey(KeyCommunitySetPF + strconv.Itoa(int(communityID)))\n\tpipeline.SAdd(cKey, postID)\n\t_, err := pipeline.Exec()\n\treturn err\n}\n\nfunc VoteForPost(userID, postID string, value float64) error {\n\t// 1. 判断投票限制\n\t// 去redis取帖子发布时间\n\tpostTime := client.ZScore(getRedisKey(KeyPostTimeZSet), postID).Val()\n\tif float64(time.Now().Unix())-postTime > oneWeekInSeconds {\n\t\treturn ErrVoteTimeExpire\n\t}\n\t// 2和3需要放到一个pipeline事务中操作\n\n\t// 2. 更新贴子的分数\n\t// 先查当前用户给当前帖子的投票记录\n\tov := client.ZScore(getRedisKey(KeyPostVotedZSetPF+postID), userID).Val()\n\n\t// 更新：如果这一次投票的值和之前保存的值一致，就提示不允许重复投票\n\tif value == ov {\n\t\treturn ErrVoteRepeated\n\t}\n\tvar op float64\n\tif value > ov {\n\t\top = 1\n\t} else {\n\t\top = -1\n\t}\n\tdiff := math.Abs(ov - value) // 计算两次投票的差值\n\tpipeline := client.TxPipeline()\n\tpipeline.ZIncrBy(getRedisKey(KeyPostScoreZSet), op*diff*scorePerVote, postID)\n\n\t// 3. 记录用户为该贴子投票的数据\n\tif value == 0 {\n\t\tpipeline.ZRem(getRedisKey(KeyPostVotedZSetPF+postID), userID)\n\t} else {\n\t\tpipeline.ZAdd(getRedisKey(KeyPostVotedZSetPF+postID), redis.Z{\n\t\t\tScore:  value, // 赞成票还是反对票\n\t\t\tMember: userID,\n\t\t})\n\t}\n\t_, err := pipeline.Exec()\n\treturn err\n}\n"
  },
  {
    "path": "lesson70/bluebell/docs/docs.go",
    "content": "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n// This file was generated by swaggo/swag\n\npackage docs\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"strings\"\n\n\t\"github.com/alecthomas/template\"\n\t\"github.com/swaggo/swag\"\n)\n\nvar doc = `{\n    \"schemes\": {{ marshal .Schemes }},\n    \"swagger\": \"2.0\",\n    \"info\": {\n        \"description\": \"{{.Description}}\",\n        \"title\": \"{{.Title}}\",\n        \"contact\": {\n            \"name\": \"liwenzhou\",\n            \"url\": \"http://www.liwenzhou.com\"\n        },\n        \"license\": {},\n        \"version\": \"{{.Version}}\"\n    },\n    \"host\": \"{{.Host}}\",\n    \"basePath\": \"{{.BasePath}}\",\n    \"paths\": {\n        \"/posts2\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"ApiKeyAuth\": []\n                    }\n                ],\n                \"description\": \"可按社区按时间或分数排序查询帖子列表接口\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"帖子相关接口(api分组展示使用的)\"\n                ],\n                \"summary\": \"升级版帖子列表接口\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Bearer JWT\",\n                        \"name\": \"Authorization\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"可以为空\",\n                        \"name\": \"community_id\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"score\",\n                        \"description\": \"排序依据\",\n                        \"name\": \"order\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"页码\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"每页数据量\",\n                        \"name\": \"size\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"OK\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/controller._ResponsePostList\"\n                        }\n                    }\n                }\n            }\n        }\n    },\n    \"definitions\": {\n        \"controller._ResponsePostList\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"code\": {\n                    \"description\": \"业务响应状态码\",\n                    \"type\": \"integer\"\n                },\n                \"data\": {\n                    \"description\": \"数据\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/definitions/models.ApiPostDetail\"\n                    }\n                },\n                \"message\": {\n                    \"description\": \"提示信息\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"models.ApiPostDetail\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"community_id\",\n                \"content\",\n                \"title\"\n            ],\n            \"properties\": {\n                \"author_id\": {\n                    \"description\": \"作者id\",\n                    \"type\": \"integer\"\n                },\n                \"author_name\": {\n                    \"description\": \"作者\",\n                    \"type\": \"string\"\n                },\n                \"community_id\": {\n                    \"description\": \"社区id\",\n                    \"type\": \"integer\"\n                },\n                \"content\": {\n                    \"description\": \"帖子内容\",\n                    \"type\": \"string\"\n                },\n                \"create_time\": {\n                    \"type\": \"string\"\n                },\n                \"id\": {\n                    \"type\": \"integer\"\n                },\n                \"introduction\": {\n                    \"type\": \"string\"\n                },\n                \"name\": {\n                    \"type\": \"string\"\n                },\n                \"status\": {\n                    \"description\": \"帖子状态\",\n                    \"type\": \"integer\"\n                },\n                \"title\": {\n                    \"description\": \"帖子标题\",\n                    \"type\": \"string\"\n                },\n                \"vote_num\": {\n                    \"description\": \"投票数\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"models.ParamPostList\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"community_id\": {\n                    \"description\": \"可以为空\",\n                    \"type\": \"integer\"\n                },\n                \"order\": {\n                    \"description\": \"排序依据\",\n                    \"type\": \"string\",\n                    \"example\": \"score\"\n                },\n                \"page\": {\n                    \"description\": \"页码\",\n                    \"type\": \"integer\"\n                },\n                \"size\": {\n                    \"description\": \"每页数据量\",\n                    \"type\": \"integer\"\n                }\n            }\n        }\n    }\n}`\n\ntype swaggerInfo struct {\n\tVersion     string\n\tHost        string\n\tBasePath    string\n\tSchemes     []string\n\tTitle       string\n\tDescription string\n}\n\n// SwaggerInfo holds exported Swagger Info so clients can modify it\nvar SwaggerInfo = swaggerInfo{\n\tVersion:     \"1.0\",\n\tHost:        \"127.0.0.1:8084\",\n\tBasePath:    \"/api/v1\",\n\tSchemes:     []string{},\n\tTitle:       \"bluebell项目接口文档\",\n\tDescription: \"Go web开发进阶项目实战课程bluebell\",\n}\n\ntype s struct{}\n\nfunc (s *s) ReadDoc() string {\n\tsInfo := SwaggerInfo\n\tsInfo.Description = strings.Replace(sInfo.Description, \"\\n\", \"\\\\n\", -1)\n\n\tt, err := template.New(\"swagger_info\").Funcs(template.FuncMap{\n\t\t\"marshal\": func(v interface{}) string {\n\t\t\ta, _ := json.Marshal(v)\n\t\t\treturn string(a)\n\t\t},\n\t}).Parse(doc)\n\tif err != nil {\n\t\treturn doc\n\t}\n\n\tvar tpl bytes.Buffer\n\tif err := t.Execute(&tpl, sInfo); err != nil {\n\t\treturn doc\n\t}\n\n\treturn tpl.String()\n}\n\nfunc init() {\n\tswag.Register(swag.Name, &s{})\n}\n"
  },
  {
    "path": "lesson70/bluebell/docs/swagger.json",
    "content": "{\n    \"swagger\": \"2.0\",\n    \"info\": {\n        \"description\": \"Go web开发进阶项目实战课程bluebell\",\n        \"title\": \"bluebell项目接口文档\",\n        \"contact\": {\n            \"name\": \"liwenzhou\",\n            \"url\": \"http://www.liwenzhou.com\"\n        },\n        \"license\": {},\n        \"version\": \"1.0\"\n    },\n    \"host\": \"127.0.0.1:8084\",\n    \"basePath\": \"/api/v1\",\n    \"paths\": {\n        \"/posts2\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"ApiKeyAuth\": []\n                    }\n                ],\n                \"description\": \"可按社区按时间或分数排序查询帖子列表接口\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"帖子相关接口(api分组展示使用的)\"\n                ],\n                \"summary\": \"升级版帖子列表接口\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Bearer JWT\",\n                        \"name\": \"Authorization\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"可以为空\",\n                        \"name\": \"community_id\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"score\",\n                        \"description\": \"排序依据\",\n                        \"name\": \"order\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"页码\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"每页数据量\",\n                        \"name\": \"size\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"OK\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/controller._ResponsePostList\"\n                        }\n                    }\n                }\n            }\n        }\n    },\n    \"definitions\": {\n        \"controller._ResponsePostList\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"code\": {\n                    \"description\": \"业务响应状态码\",\n                    \"type\": \"integer\"\n                },\n                \"data\": {\n                    \"description\": \"数据\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/definitions/models.ApiPostDetail\"\n                    }\n                },\n                \"message\": {\n                    \"description\": \"提示信息\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"models.ApiPostDetail\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"community_id\",\n                \"content\",\n                \"title\"\n            ],\n            \"properties\": {\n                \"author_id\": {\n                    \"description\": \"作者id\",\n                    \"type\": \"integer\"\n                },\n                \"author_name\": {\n                    \"description\": \"作者\",\n                    \"type\": \"string\"\n                },\n                \"community_id\": {\n                    \"description\": \"社区id\",\n                    \"type\": \"integer\"\n                },\n                \"content\": {\n                    \"description\": \"帖子内容\",\n                    \"type\": \"string\"\n                },\n                \"create_time\": {\n                    \"type\": \"string\"\n                },\n                \"id\": {\n                    \"type\": \"integer\"\n                },\n                \"introduction\": {\n                    \"type\": \"string\"\n                },\n                \"name\": {\n                    \"type\": \"string\"\n                },\n                \"status\": {\n                    \"description\": \"帖子状态\",\n                    \"type\": \"integer\"\n                },\n                \"title\": {\n                    \"description\": \"帖子标题\",\n                    \"type\": \"string\"\n                },\n                \"vote_num\": {\n                    \"description\": \"投票数\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"models.ParamPostList\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"community_id\": {\n                    \"description\": \"可以为空\",\n                    \"type\": \"integer\"\n                },\n                \"order\": {\n                    \"description\": \"排序依据\",\n                    \"type\": \"string\",\n                    \"example\": \"score\"\n                },\n                \"page\": {\n                    \"description\": \"页码\",\n                    \"type\": \"integer\"\n                },\n                \"size\": {\n                    \"description\": \"每页数据量\",\n                    \"type\": \"integer\"\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "lesson70/bluebell/docs/swagger.yaml",
    "content": "basePath: /api/v1\ndefinitions:\n  controller._ResponsePostList:\n    properties:\n      code:\n        description: 业务响应状态码\n        type: integer\n      data:\n        description: 数据\n        items:\n          $ref: '#/definitions/models.ApiPostDetail'\n        type: array\n      message:\n        description: 提示信息\n        type: string\n    type: object\n  models.ApiPostDetail:\n    properties:\n      author_id:\n        description: 作者id\n        type: integer\n      author_name:\n        description: 作者\n        type: string\n      community_id:\n        description: 社区id\n        type: integer\n      content:\n        description: 帖子内容\n        type: string\n      create_time:\n        type: string\n      id:\n        type: integer\n      introduction:\n        type: string\n      name:\n        type: string\n      status:\n        description: 帖子状态\n        type: integer\n      title:\n        description: 帖子标题\n        type: string\n      vote_num:\n        description: 投票数\n        type: integer\n    required:\n    - community_id\n    - content\n    - title\n    type: object\n  models.ParamPostList:\n    properties:\n      community_id:\n        description: 可以为空\n        type: integer\n      order:\n        description: 排序依据\n        example: score\n        type: string\n      page:\n        description: 页码\n        type: integer\n      size:\n        description: 每页数据量\n        type: integer\n    type: object\nhost: 127.0.0.1:8084\ninfo:\n  contact:\n    name: liwenzhou\n    url: http://www.liwenzhou.com\n  description: Go web开发进阶项目实战课程bluebell\n  license: {}\n  title: bluebell项目接口文档\n  version: \"1.0\"\npaths:\n  /posts2:\n    get:\n      consumes:\n      - application/json\n      description: 可按社区按时间或分数排序查询帖子列表接口\n      parameters:\n      - description: Bearer JWT\n        in: header\n        name: Authorization\n        required: true\n        type: string\n      - description: 可以为空\n        in: query\n        name: community_id\n        type: integer\n      - description: 排序依据\n        example: score\n        in: query\n        name: order\n        type: string\n      - description: 页码\n        in: query\n        name: page\n        type: integer\n      - description: 每页数据量\n        in: query\n        name: size\n        type: integer\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: OK\n          schema:\n            $ref: '#/definitions/controller._ResponsePostList'\n      security:\n      - ApiKeyAuth: []\n      summary: 升级版帖子列表接口\n      tags:\n      - 帖子相关接口(api分组展示使用的)\nswagger: \"2.0\"\n"
  },
  {
    "path": "lesson70/bluebell/go.mod",
    "content": "module bluebell\n\ngo 1.14\n\nrequire (\n\tgithub.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751\n\tgithub.com/bwmarrin/snowflake v0.3.0\n\tgithub.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect\n\tgithub.com/dgrijalva/jwt-go v3.2.0+incompatible\n\tgithub.com/fsnotify/fsnotify v1.4.9\n\tgithub.com/gin-gonic/gin v1.6.3\n\tgithub.com/go-openapi/spec v0.19.9 // indirect\n\tgithub.com/go-openapi/swag v0.19.9 // indirect\n\tgithub.com/go-playground/locales v0.13.0\n\tgithub.com/go-playground/universal-translator v0.17.0\n\tgithub.com/go-playground/validator/v10 v10.3.0\n\tgithub.com/go-redis/redis v6.15.8+incompatible\n\tgithub.com/go-sql-driver/mysql v1.5.0\n\tgithub.com/jmoiron/sqlx v1.2.0\n\tgithub.com/json-iterator/go v1.1.10 // indirect\n\tgithub.com/mailru/easyjson v0.7.6 // indirect\n\tgithub.com/natefinch/lumberjack v2.0.0+incompatible\n\tgithub.com/onsi/ginkgo v1.14.0 // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/sony/sonyflake v1.0.0 // indirect\n\tgithub.com/spf13/viper v1.7.0\n\tgithub.com/stretchr/testify v1.4.0\n\tgithub.com/swaggo/gin-swagger v1.2.0\n\tgithub.com/swaggo/swag v1.6.7\n\tgithub.com/urfave/cli/v2 v2.2.0 // indirect\n\tgo.uber.org/zap v1.15.0\n\tgolang.org/x/net v0.0.0-20200904194848-62affa334b73 // indirect\n\tgolang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f // indirect\n\tgolang.org/x/tools v0.0.0-20200904185747-39188db58858 // indirect\n\tgoogle.golang.org/protobuf v1.25.0 // indirect\n\tgopkg.in/go-playground/assert.v1 v1.2.1\n\tgopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect\n)\n"
  },
  {
    "path": "lesson70/bluebell/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ngithub.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=\ngithub.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=\ngithub.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=\ngithub.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4=\ngithub.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=\ngithub.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=\ngithub.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=\ngithub.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=\ngithub.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=\ngithub.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=\ngithub.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=\ngithub.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=\ngithub.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=\ngithub.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=\ngithub.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=\ngithub.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=\ngithub.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0=\ngithub.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=\ngithub.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=\ngithub.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=\ngithub.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=\ngithub.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w=\ngithub.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=\ngithub.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=\ngithub.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=\ngithub.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=\ngithub.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=\ngithub.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=\ngithub.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=\ngithub.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=\ngithub.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=\ngithub.com/go-openapi/jsonpointer v0.17.0 h1:nH6xp8XdXHx8dqveo0ZuJBluCO2qGrPbDNZ0dwoRHP0=\ngithub.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=\ngithub.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=\ngithub.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=\ngithub.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=\ngithub.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=\ngithub.com/go-openapi/jsonreference v0.19.0 h1:BqWKpV1dFd+AuiKlgtddwVIFQsuMpxfBDBHGfM2yNpk=\ngithub.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=\ngithub.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=\ngithub.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=\ngithub.com/go-openapi/jsonreference v0.19.4 h1:3Vw+rh13uq2JFNxgnMTGE1rnoieU9FmyE1gvnyylsYg=\ngithub.com/go-openapi/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=\ngithub.com/go-openapi/spec v0.19.0 h1:A4SZ6IWh3lnjH0rG0Z5lkxazMGBECtrZcbyYQi+64k4=\ngithub.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=\ngithub.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=\ngithub.com/go-openapi/spec v0.19.9 h1:9z9cbFuZJ7AcvOHKIY+f6Aevb4vObNDkTEyoMfO7rAc=\ngithub.com/go-openapi/spec v0.19.9/go.mod h1:vqK/dIdLGCosfvYsQV3WfC7N3TiZSnGY2RZKoFK7X28=\ngithub.com/go-openapi/swag v0.17.0 h1:iqrgMg7Q7SvtbWLlltPrkMs0UBJI6oTSs79JFRUi880=\ngithub.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=\ngithub.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=\ngithub.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=\ngithub.com/go-openapi/swag v0.19.9 h1:1IxuqvBUU3S2Bi4YC7tlP9SJF1gVpCvqN0T2Qof4azE=\ngithub.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY=\ngithub.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=\ngithub.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=\ngithub.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=\ngithub.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=\ngithub.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=\ngithub.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=\ngithub.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=\ngithub.com/go-playground/validator/v10 v10.3.0 h1:nZU+7q+yJoFmwvNgv/LnPUkwPal62+b2xXj0AU1Es7o=\ngithub.com/go-playground/validator/v10 v10.3.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=\ngithub.com/go-redis/redis v6.15.8+incompatible h1:BKZuG6mCnRj5AOaWJXoCgf6rqTYnYJLe4en2hxT7r9o=\ngithub.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=\ngithub.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=\ngithub.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=\ngithub.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=\ngithub.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=\ngithub.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=\ngithub.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=\ngithub.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=\ngithub.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=\ngithub.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=\ngithub.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=\ngithub.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=\ngithub.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=\ngithub.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=\ngithub.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=\ngithub.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=\ngithub.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=\ngithub.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=\ngithub.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=\ngithub.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=\ngithub.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=\ngithub.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=\ngithub.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=\ngithub.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=\ngithub.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=\ngithub.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=\ngithub.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=\ngithub.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=\ngithub.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=\ngithub.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=\ngithub.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=\ngithub.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=\ngithub.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=\ngithub.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=\ngithub.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=\ngithub.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=\ngithub.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=\ngithub.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=\ngithub.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=\ngithub.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=\ngithub.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=\ngithub.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=\ngithub.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=\ngithub.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=\ngithub.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=\ngithub.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=\ngithub.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=\ngithub.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=\ngithub.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=\ngithub.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=\ngithub.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=\ngithub.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=\ngithub.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=\ngithub.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=\ngithub.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=\ngithub.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=\ngithub.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=\ngithub.com/sony/sonyflake v1.0.0 h1:MpU6Ro7tfXwgn2l5eluf9xQvQJDROTBImNCfRXn/YeM=\ngithub.com/sony/sonyflake v1.0.0/go.mod h1:Jv3cfhf/UFtolOTTRd3q4Nl6ENqM+KfyZ5PseKfZGF4=\ngithub.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=\ngithub.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=\ngithub.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=\ngithub.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=\ngithub.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=\ngithub.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=\ngithub.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=\ngithub.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=\ngithub.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=\ngithub.com/swaggo/files v0.0.0-20190704085106-630677cd5c14 h1:PyYN9JH5jY9j6av01SpfRMb+1DWg/i3MbGOKPxJ2wjM=\ngithub.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E=\ngithub.com/swaggo/gin-swagger v1.2.0 h1:YskZXEiv51fjOMTsXrOetAjrMDfFaXD79PEoQBOe2W0=\ngithub.com/swaggo/gin-swagger v1.2.0/go.mod h1:qlH2+W7zXGZkczuL+r2nEBR2JTT+/lX05Nn6vPhc7OI=\ngithub.com/swaggo/swag v1.5.1 h1:2Agm8I4K5qb00620mHq0VJ05/KT4FtmALPIcQR9lEZM=\ngithub.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y=\ngithub.com/swaggo/swag v1.6.7 h1:e8GC2xDllJZr3omJkm9YfmK0Y56+rMO3cg0JBKNz09s=\ngithub.com/swaggo/swag v1.6.7/go.mod h1:xDhTyuFIujYiN3DKWC/H/83xcfHp+UE/IzWWampG7Zc=\ngithub.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=\ngithub.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=\ngithub.com/ugorji/go v1.1.5-pre/go.mod h1:FwP/aQVg39TXzItUBMwnWp9T9gPQnXw4Poh4/oBQZ/0=\ngithub.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=\ngithub.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=\ngithub.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=\ngithub.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI=\ngithub.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=\ngithub.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=\ngithub.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=\ngithub.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=\ngithub.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k=\ngithub.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=\ngithub.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4=\ngithub.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=\ngithub.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngo.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=\ngo.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=\ngo.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=\ngo.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=\ngo.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngo.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=\ngo.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA=\ngolang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f h1:Fqb3ao1hUmOR3GkUOg/Y+BadLwykBIzs5q8Ez2SbHyc=\ngolang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc h1:NCy3Ohtk6Iny5V/reW2Ktypo4zIpWBdRJ1uFMjBxdg8=\ngolang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20200904185747-39188db58858 h1:xLt+iB5ksWcZVxqc+g9K41ZHy+6MKWfXCDsjSThnsPA=\ngolang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=\ngopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=\ngopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=\ngopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=\ngopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=\ngopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=\ngopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\n"
  },
  {
    "path": "lesson70/bluebell/logger/logger.go",
    "content": "package logger\n\nimport (\n\t\"bluebell/setting\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"os\"\n\t\"runtime/debug\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/natefinch/lumberjack\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\nvar lg *zap.Logger\n\n// Init 初始化lg\nfunc Init(cfg *setting.LogConfig, mode string) (err error) {\n\twriteSyncer := getLogWriter(cfg.Filename, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge)\n\tencoder := getEncoder()\n\tvar l = new(zapcore.Level)\n\terr = l.UnmarshalText([]byte(cfg.Level))\n\tif err != nil {\n\t\treturn\n\t}\n\tvar core zapcore.Core\n\tif mode == \"dev\" {\n\t\t// 进入开发模式，日志输出到终端\n\t\tconsoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())\n\t\tcore = zapcore.NewTee(\n\t\t\tzapcore.NewCore(encoder, writeSyncer, l),\n\t\t\tzapcore.NewCore(consoleEncoder, zapcore.Lock(os.Stdout), zapcore.DebugLevel),\n\t\t)\n\t} else {\n\t\tcore = zapcore.NewCore(encoder, writeSyncer, l)\n\t}\n\n\tlg = zap.New(core, zap.AddCaller())\n\n\tzap.ReplaceGlobals(lg)\n\tzap.L().Info(\"init logger success\")\n\treturn\n}\n\nfunc getEncoder() zapcore.Encoder {\n\tencoderConfig := zap.NewProductionEncoderConfig()\n\tencoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder\n\tencoderConfig.TimeKey = \"time\"\n\tencoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder\n\tencoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder\n\tencoderConfig.EncodeCaller = zapcore.ShortCallerEncoder\n\treturn zapcore.NewJSONEncoder(encoderConfig)\n}\n\nfunc getLogWriter(filename string, maxSize, maxBackup, maxAge int) zapcore.WriteSyncer {\n\tlumberJackLogger := &lumberjack.Logger{\n\t\tFilename:   filename,\n\t\tMaxSize:    maxSize,\n\t\tMaxBackups: maxBackup,\n\t\tMaxAge:     maxAge,\n\t}\n\treturn zapcore.AddSync(lumberJackLogger)\n}\n\n// GinLogger 接收gin框架默认的日志\nfunc GinLogger() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tstart := time.Now()\n\t\tpath := c.Request.URL.Path\n\t\tquery := c.Request.URL.RawQuery\n\t\tc.Next()\n\n\t\tcost := time.Since(start)\n\t\tlg.Info(path,\n\t\t\tzap.Int(\"status\", c.Writer.Status()),\n\t\t\tzap.String(\"method\", c.Request.Method),\n\t\t\tzap.String(\"path\", path),\n\t\t\tzap.String(\"query\", query),\n\t\t\tzap.String(\"ip\", c.ClientIP()),\n\t\t\tzap.String(\"user-agent\", c.Request.UserAgent()),\n\t\t\tzap.String(\"errors\", c.Errors.ByType(gin.ErrorTypePrivate).String()),\n\t\t\tzap.Duration(\"cost\", cost),\n\t\t)\n\t}\n}\n\n// GinRecovery recover掉项目可能出现的panic，并使用zap记录相关日志\nfunc GinRecovery(stack bool) gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tdefer func() {\n\t\t\tif err := recover(); err != nil {\n\t\t\t\t// Check for a broken connection, as it is not really a\n\t\t\t\t// condition that warrants a panic stack trace.\n\t\t\t\tvar brokenPipe bool\n\t\t\t\tif ne, ok := err.(*net.OpError); ok {\n\t\t\t\t\tif se, ok := ne.Err.(*os.SyscallError); ok {\n\t\t\t\t\t\tif strings.Contains(strings.ToLower(se.Error()), \"broken pipe\") || strings.Contains(strings.ToLower(se.Error()), \"connection reset by peer\") {\n\t\t\t\t\t\t\tbrokenPipe = true\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\thttpRequest, _ := httputil.DumpRequest(c.Request, false)\n\t\t\t\tif brokenPipe {\n\t\t\t\t\tlg.Error(c.Request.URL.Path,\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t)\n\t\t\t\t\t// If the connection is dead, we can't write a status to it.\n\t\t\t\t\tc.Error(err.(error)) // nolint: errcheck\n\t\t\t\t\tc.Abort()\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif stack {\n\t\t\t\t\tlg.Error(\"[Recovery from panic]\",\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t\tzap.String(\"stack\", string(debug.Stack())),\n\t\t\t\t\t)\n\t\t\t\t} else {\n\t\t\t\t\tlg.Error(\"[Recovery from panic]\",\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tc.AbortWithStatus(http.StatusInternalServerError)\n\t\t\t}\n\t\t}()\n\t\tc.Next()\n\t}\n}\n"
  },
  {
    "path": "lesson70/bluebell/logic/community.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/models\"\n)\n\nfunc GetCommunityList() ([]*models.Community, error) {\n\t// 查数据库 查找到所有的community 并返回\n\treturn mysql.GetCommunityList()\n}\n\nfunc GetCommunityDetail(id int64) (*models.CommunityDetail, error) {\n\treturn mysql.GetCommunityDetailByID(id)\n}\n"
  },
  {
    "path": "lesson70/bluebell/logic/post.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/dao/redis\"\n\t\"bluebell/models\"\n\t\"bluebell/pkg/snowflake\"\n\n\t\"go.uber.org/zap\"\n)\n\nfunc CreatePost(p *models.Post) (err error) {\n\t// 1. 生成post id\n\tp.ID = snowflake.GenID()\n\t// 2. 保存到数据库\n\terr = mysql.CreatePost(p)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = redis.CreatePost(p.ID, p.CommunityID)\n\treturn\n\t// 3. 返回\n}\n\n// GetPostById 根据帖子id查询帖子详情数据\nfunc GetPostById(pid int64) (data *models.ApiPostDetail, err error) {\n\t// 查询并组合我们接口想用的数据\n\tpost, err := mysql.GetPostById(pid)\n\tif err != nil {\n\t\tzap.L().Error(\"mysql.GetPostById(pid) failed\",\n\t\t\tzap.Int64(\"pid\", pid),\n\t\t\tzap.Error(err))\n\t\treturn\n\t}\n\t// 根据作者id查询作者信息\n\tuser, err := mysql.GetUserById(post.AuthorID)\n\tif err != nil {\n\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\tzap.Int64(\"author_id\", post.AuthorID),\n\t\t\tzap.Error(err))\n\t\treturn\n\t}\n\t// 根据社区id查询社区详细信息\n\tcommunity, err := mysql.GetCommunityDetailByID(post.CommunityID)\n\tif err != nil {\n\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\tzap.Int64(\"community_id\", post.CommunityID),\n\t\t\tzap.Error(err))\n\t\treturn\n\t}\n\t// 接口数据拼接\n\tdata = &models.ApiPostDetail{\n\t\tAuthorName:      user.Username,\n\t\tPost:            post,\n\t\tCommunityDetail: community,\n\t}\n\treturn\n}\n\n// GetPostList 获取帖子列表\nfunc GetPostList(page, size int64) (data []*models.ApiPostDetail, err error) {\n\tposts, err := mysql.GetPostList(page, size)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdata = make([]*models.ApiPostDetail, 0, len(posts))\n\n\tfor _, post := range posts {\n\t\t// 根据作者id查询作者信息\n\t\tuser, err := mysql.GetUserById(post.AuthorID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"author_id\", post.AuthorID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\t// 根据社区id查询社区详细信息\n\t\tcommunity, err := mysql.GetCommunityDetailByID(post.CommunityID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"community_id\", post.CommunityID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\tpostDetail := &models.ApiPostDetail{\n\t\t\tAuthorName:      user.Username,\n\t\t\tPost:            post,\n\t\t\tCommunityDetail: community,\n\t\t}\n\t\tdata = append(data, postDetail)\n\t}\n\treturn\n}\n\nfunc GetPostList2(p *models.ParamPostList) (data []*models.ApiPostDetail, err error) {\n\t// 2. 去redis查询id列表\n\tids, err := redis.GetPostIDsInOrder(p)\n\tif err != nil {\n\t\treturn\n\t}\n\tif len(ids) == 0 {\n\t\tzap.L().Warn(\"redis.GetPostIDsInOrder(p) return 0 data\")\n\t\treturn\n\t}\n\tzap.L().Debug(\"GetPostList2\", zap.Any(\"ids\", ids))\n\t// 3. 根据id去MySQL数据库查询帖子详细信息\n\t// 返回的数据还要按照我给定的id的顺序返回\n\tposts, err := mysql.GetPostListByIDs(ids)\n\tif err != nil {\n\t\treturn\n\t}\n\tzap.L().Debug(\"GetPostList2\", zap.Any(\"posts\", posts))\n\t// 提前查询好每篇帖子的投票数\n\tvoteData, err := redis.GetPostVoteData(ids)\n\tif err != nil {\n\t\treturn\n\t}\n\n\t// 将帖子的作者及分区信息查询出来填充到帖子中\n\tfor idx, post := range posts {\n\t\t// 根据作者id查询作者信息\n\t\tuser, err := mysql.GetUserById(post.AuthorID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"author_id\", post.AuthorID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\t// 根据社区id查询社区详细信息\n\t\tcommunity, err := mysql.GetCommunityDetailByID(post.CommunityID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"community_id\", post.CommunityID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\tpostDetail := &models.ApiPostDetail{\n\t\t\tAuthorName:      user.Username,\n\t\t\tVoteNum:         voteData[idx],\n\t\t\tPost:            post,\n\t\t\tCommunityDetail: community,\n\t\t}\n\t\tdata = append(data, postDetail)\n\t}\n\treturn\n\n}\n\nfunc GetCommunityPostList(p *models.ParamPostList) (data []*models.ApiPostDetail, err error) {\n\t// 2. 去redis查询id列表\n\tids, err := redis.GetCommunityPostIDsInOrder(p)\n\tif err != nil {\n\t\treturn\n\t}\n\tif len(ids) == 0 {\n\t\tzap.L().Warn(\"redis.GetPostIDsInOrder(p) return 0 data\")\n\t\treturn\n\t}\n\tzap.L().Debug(\"GetCommunityPostIDsInOrder\", zap.Any(\"ids\", ids))\n\t// 3. 根据id去MySQL数据库查询帖子详细信息\n\t// 返回的数据还要按照我给定的id的顺序返回\n\tposts, err := mysql.GetPostListByIDs(ids)\n\tif err != nil {\n\t\treturn\n\t}\n\tzap.L().Debug(\"GetPostList2\", zap.Any(\"posts\", posts))\n\t// 提前查询好每篇帖子的投票数\n\tvoteData, err := redis.GetPostVoteData(ids)\n\tif err != nil {\n\t\treturn\n\t}\n\n\t// 将帖子的作者及分区信息查询出来填充到帖子中\n\tfor idx, post := range posts {\n\t\t// 根据作者id查询作者信息\n\t\tuser, err := mysql.GetUserById(post.AuthorID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"author_id\", post.AuthorID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\t// 根据社区id查询社区详细信息\n\t\tcommunity, err := mysql.GetCommunityDetailByID(post.CommunityID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"community_id\", post.CommunityID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\tpostDetail := &models.ApiPostDetail{\n\t\t\tAuthorName:      user.Username,\n\t\t\tVoteNum:         voteData[idx],\n\t\t\tPost:            post,\n\t\t\tCommunityDetail: community,\n\t\t}\n\t\tdata = append(data, postDetail)\n\t}\n\treturn\n}\n\n// GetPostListNew  将两个查询帖子列表逻辑合二为一的函数\nfunc GetPostListNew(p *models.ParamPostList) (data []*models.ApiPostDetail, err error) {\n\t// 根据请求参数的不同，执行不同的逻辑。\n\tif p.CommunityID == 0 {\n\t\t// 查所有\n\t\tdata, err = GetPostList2(p)\n\t} else {\n\t\t// 根据社区id查询\n\t\tdata, err = GetCommunityPostList(p)\n\t}\n\tif err != nil {\n\t\tzap.L().Error(\"GetPostListNew failed\", zap.Error(err))\n\t\treturn nil, err\n\t}\n\treturn\n}\n"
  },
  {
    "path": "lesson70/bluebell/logic/user.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/models\"\n\t\"bluebell/pkg/jwt\"\n\t\"bluebell/pkg/snowflake\"\n)\n\n// 存放业务逻辑的代码\n\nfunc SignUp(p *models.ParamSignUp) (err error) {\n\t// 1.判断用户存不存在\n\tif err := mysql.CheckUserExist(p.Username); err != nil {\n\t\treturn err\n\t}\n\t// 2.生成UID\n\tuserID := snowflake.GenID()\n\t// 构造一个User实例\n\tuser := &models.User{\n\t\tUserID:   userID,\n\t\tUsername: p.Username,\n\t\tPassword: p.Password,\n\t}\n\t// 3.保存进数据库\n\treturn mysql.InsertUser(user)\n}\n\nfunc Login(p *models.ParamLogin) (user *models.User, err error) {\n\tuser = &models.User{\n\t\tUsername: p.Username,\n\t\tPassword: p.Password,\n\t}\n\t// 传递的是指针，就能拿到user.UserID\n\tif err := mysql.Login(user); err != nil {\n\t\treturn nil, err\n\t}\n\t// 生成JWT\n\ttoken, err := jwt.GenToken(user.UserID, user.Username)\n\tif err != nil {\n\t\treturn\n\t}\n\tuser.Token = token\n\treturn\n}\n"
  },
  {
    "path": "lesson70/bluebell/logic/vote.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/redis\"\n\t\"bluebell/models\"\n\t\"strconv\"\n\n\t\"go.uber.org/zap\"\n)\n\n// 推荐阅读\n// 基于用户投票的相关算法：http://www.ruanyifeng.com/blog/algorithm/\n\n// 本项目使用简化版的投票分数\n// 投一票就加432分   86400/200  --> 200张赞成票可以给你的帖子续一天\n\n/* 投票的几种情况：\ndirection=1时，有两种情况：\n\t1. 之前没有投过票，现在投赞成票    --> 更新分数和投票记录\n\t2. 之前投反对票，现在改投赞成票    --> 更新分数和投票记录\ndirection=0时，有两种情况：\n\t1. 之前投过赞成票，现在要取消投票  --> 更新分数和投票记录\n\t2. 之前投过反对票，现在要取消投票  --> 更新分数和投票记录\ndirection=-1时，有两种情况：\n\t1. 之前没有投过票，现在投反对票    --> 更新分数和投票记录\n\t2. 之前投赞成票，现在改投反对票    --> 更新分数和投票记录\n\n投票的限制：\n每个贴子自发表之日起一个星期之内允许用户投票，超过一个星期就不允许再投票了。\n\t1. 到期之后将redis中保存的赞成票数及反对票数存储到mysql表中\n\t2. 到期之后删除那个 KeyPostVotedZSetPF\n*/\n\n// VoteForPost 为帖子投票的函数\nfunc VoteForPost(userID int64, p *models.ParamVoteData) error {\n\tzap.L().Debug(\"VoteForPost\",\n\t\tzap.Int64(\"userID\", userID),\n\t\tzap.String(\"postID\", p.PostID),\n\t\tzap.Int8(\"direction\", p.Direction))\n\treturn redis.VoteForPost(strconv.Itoa(int(userID)), p.PostID, float64(p.Direction))\n}\n"
  },
  {
    "path": "lesson70/bluebell/main.go",
    "content": "package main\n\nimport (\n\t\"bluebell/controller\"\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/dao/redis\"\n\t\"bluebell/logger\"\n\t\"bluebell/pkg/snowflake\"\n\t\"bluebell/router\"\n\t\"bluebell/setting\"\n\t\"fmt\"\n\t\"os\"\n)\n\n// @title bluebell项目接口文档\n// @version 1.0\n// @description Go web开发进阶项目实战课程bluebell\n\n// @contact.name liwenzhou\n// @contact.url http://www.liwenzhou.com\n\n// @host 127.0.0.1:8084\n// @BasePath /api/v1\nfunc main() {\n\tif len(os.Args) < 2 {\n\t\tfmt.Println(\"need config file.eg: bluebell config.yaml\")\n\t\treturn\n\t}\n\t// 加载配置\n\tif err := setting.Init(os.Args[1]); err != nil {\n\t\tfmt.Printf(\"load config failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tif err := logger.Init(setting.Conf.LogConfig, setting.Conf.Mode); err != nil {\n\t\tfmt.Printf(\"init logger failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tif err := mysql.Init(setting.Conf.MySQLConfig); err != nil {\n\t\tfmt.Printf(\"init mysql failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer mysql.Close() // 程序退出关闭数据库连接\n\tif err := redis.Init(setting.Conf.RedisConfig); err != nil {\n\t\tfmt.Printf(\"init redis failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer redis.Close()\n\n\tif err := snowflake.Init(setting.Conf.StartTime, setting.Conf.MachineID); err != nil {\n\t\tfmt.Printf(\"init snowflake failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\t// 初始化gin框架内置的校验器使用的翻译器\n\tif err := controller.InitTrans(\"zh\"); err != nil {\n\t\tfmt.Printf(\"init validator trans failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\t// 注册路由\n\tr := router.SetupRouter(setting.Conf.Mode)\n\terr := r.Run(fmt.Sprintf(\":%d\", setting.Conf.Port))\n\tif err != nil {\n\t\tfmt.Printf(\"run server failed, err:%v\\n\", err)\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "lesson70/bluebell/middlewares/auth.go",
    "content": "package middlewares\n\nimport (\n\t\"bluebell/controller\"\n\t\"bluebell/pkg/jwt\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// JWTAuthMiddleware 基于JWT的认证中间件\nfunc JWTAuthMiddleware() func(c *gin.Context) {\n\treturn func(c *gin.Context) {\n\t\t// 客户端携带Token有三种方式 1.放在请求头 2.放在请求体 3.放在URI\n\t\t// 这里假设Token放在Header的Authorization中，并使用Bearer开头\n\t\t// Authorization: Bearer xxxxxxx.xxx.xxx  / X-TOKEN: xxx.xxx.xx\n\t\t// 这里的具体实现方式要依据你的实际业务情况决定\n\t\tauthHeader := c.Request.Header.Get(\"Authorization\")\n\t\tif authHeader == \"\" {\n\t\t\tcontroller.ResponseError(c, controller.CodeNeedLogin)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// 按空格分割\n\t\tparts := strings.SplitN(authHeader, \" \", 2)\n\t\tif !(len(parts) == 2 && parts[0] == \"Bearer\") {\n\t\t\tcontroller.ResponseError(c, controller.CodeInvalidToken)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// parts[1]是获取到的tokenString，我们使用之前定义好的解析JWT的函数来解析它\n\t\tmc, err := jwt.ParseToken(parts[1])\n\t\tif err != nil {\n\t\t\tcontroller.ResponseError(c, controller.CodeInvalidToken)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// 将当前请求的userID信息保存到请求的上下文c上\n\t\tc.Set(controller.CtxUserIDKey, mc.UserID)\n\n\t\tc.Next() // 后续的处理请求的函数中 可以用过c.Get(CtxUserIDKey) 来获取当前请求的用户信息\n\t}\n}\n"
  },
  {
    "path": "lesson70/bluebell/models/community.go",
    "content": "package models\n\nimport \"time\"\n\ntype Community struct {\n\tID   int64  `json:\"id\" db:\"community_id\"`\n\tName string `json:\"name\" db:\"community_name\"`\n}\n\ntype CommunityDetail struct {\n\tID           int64     `json:\"id\" db:\"community_id\"`\n\tName         string    `json:\"name\" db:\"community_name\"`\n\tIntroduction string    `json:\"introduction,omitempty\" db:\"introduction\"`\n\tCreateTime   time.Time `json:\"create_time\" db:\"create_time\"`\n}\n"
  },
  {
    "path": "lesson70/bluebell/models/create_table.sql",
    "content": "--\nCREATE TABLE `user` (\n    `id` bigint(20) NOT NULL AUTO_INCREMENT,\n    `user_id` bigint(20) NOT NULL,\n    `username` varchar(64) COLLATE utf8mb4_general_ci NOT NULL,\n    `password` varchar(64) COLLATE utf8mb4_general_ci NOT NULL,\n    `email` varchar(64) COLLATE utf8mb4_general_ci,\n    `gender` tinyint(4) NOT NULL DEFAULT '0',\n    `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n    `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    PRIMARY KEY (`id`),\n    UNIQUE KEY `idx_username` (`username`) USING BTREE,\n    UNIQUE KEY `idx_user_id` (`user_id`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\n\n\nDROP TABLE IF EXISTS `community`;\nCREATE TABLE `community` (\n     `id` int(11) NOT NULL AUTO_INCREMENT,\n     `community_id` int(10) unsigned NOT NULL,\n     `community_name` varchar(128) COLLATE utf8mb4_general_ci NOT NULL,\n     `introduction` varchar(256) COLLATE utf8mb4_general_ci NOT NULL,\n     `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n     `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n     PRIMARY KEY (`id`),\n     UNIQUE KEY `idx_community_id` (`community_id`),\n     UNIQUE KEY `idx_community_name` (`community_name`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\n\n\nINSERT INTO `community` VALUES ('1', '1', 'Go', 'Golang', '2016-11-01 08:10:10', '2016-11-01 08:10:10');\nINSERT INTO `community` VALUES ('2', '2', 'leetcode', '刷题刷题刷题', '2020-01-01 08:00:00', '2020-01-01 08:00:00');\nINSERT INTO `community` VALUES ('3', '3', 'CS:GO', 'Rush B。。。', '2018-08-07 08:30:00', '2018-08-07 08:30:00');\nINSERT INTO `community` VALUES ('4', '4', 'LOL', '欢迎来到英雄联盟!', '2016-01-01 08:00:00', '2016-01-01 08:00:00');\n\nDROP TABLE IF EXISTS `post`;\nCREATE TABLE `post` (\n    `id` bigint(20) NOT NULL AUTO_INCREMENT,\n    `post_id` bigint(20) NOT NULL COMMENT '帖子id',\n    `title` varchar(128) COLLATE utf8mb4_general_ci NOT NULL COMMENT '标题',\n    `content` varchar(8192) COLLATE utf8mb4_general_ci NOT NULL COMMENT '内容',\n    `author_id` bigint(20) NOT NULL COMMENT '作者的用户id',\n    `community_id` bigint(20) NOT NULL COMMENT '所属社区',\n    `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '帖子状态',\n    `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n    `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n    PRIMARY KEY (`id`),\n    UNIQUE KEY `idx_post_id` (`post_id`),\n    KEY `idx_author_id` (`author_id`),\n    KEY `idx_community_id` (`community_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;"
  },
  {
    "path": "lesson70/bluebell/models/params.go",
    "content": "package models\n\n// 定义请求的参数结构体\n\nconst (\n\tOrderTime  = \"time\"\n\tOrderScore = \"score\"\n)\n\n// ParamSignUp 注册请求参数\ntype ParamSignUp struct {\n\tUsername   string `json:\"username\" binding:\"required\"`\n\tPassword   string `json:\"password\" binding:\"required\"`\n\tRePassword string `json:\"re_password\" binding:\"required,eqfield=Password\"`\n}\n\n// ParamLogin 登录请求参数\ntype ParamLogin struct {\n\tUsername string `json:\"username\" binding:\"required\"`\n\tPassword string `json:\"password\" binding:\"required\"`\n}\n\n// ParamVoteData 投票数据\ntype ParamVoteData struct {\n\t// UserID 从请求中获取当前的用户\n\tPostID    string `json:\"post_id\" binding:\"required\"`               // 贴子id\n\tDirection int8   `json:\"direction,string\" binding:\"oneof=1 0 -1\" ` // 赞成票(1)还是反对票(-1)取消投票(0)\n}\n\n// ParamPostList 获取帖子列表query string参数\ntype ParamPostList struct {\n\tCommunityID int64  `json:\"community_id\" form:\"community_id\"`   // 可以为空\n\tPage        int64  `json:\"page\" form:\"page\" example:\"1\"`       // 页码\n\tSize        int64  `json:\"size\" form:\"size\" example:\"10\"`      // 每页数据量\n\tOrder       string `json:\"order\" form:\"order\" example:\"score\"` // 排序依据\n}\n"
  },
  {
    "path": "lesson70/bluebell/models/post.go",
    "content": "package models\n\nimport \"time\"\n\n// 内存对齐概念\n\ntype Post struct {\n\tID          int64     `json:\"id,string\" db:\"post_id\"`                            // 帖子id\n\tAuthorID    int64     `json:\"author_id\" db:\"author_id\"`                          // 作者id\n\tCommunityID int64     `json:\"community_id\" db:\"community_id\" binding:\"required\"` // 社区id\n\tStatus      int32     `json:\"status\" db:\"status\"`                                // 帖子状态\n\tTitle       string    `json:\"title\" db:\"title\" binding:\"required\"`               // 帖子标题\n\tContent     string    `json:\"content\" db:\"content\" binding:\"required\"`           // 帖子内容\n\tCreateTime  time.Time `json:\"create_time\" db:\"create_time\"`                      // 帖子创建时间\n}\n\n// ApiPostDetail 帖子详情接口的结构体\ntype ApiPostDetail struct {\n\tAuthorName       string             `json:\"author_name\"` // 作者\n\tVoteNum          int64              `json:\"vote_num\"`    // 投票数\n\t*Post                               // 嵌入帖子结构体\n\t*CommunityDetail `json:\"community\"` // 嵌入社区信息\n}\n"
  },
  {
    "path": "lesson70/bluebell/models/struct_test.go",
    "content": "package models\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"unsafe\"\n)\n\n// Go 内存对齐\n\ntype s1 struct {\n\ta int8   // 1\n\tb string // 3\n\tc int8   // 1\n}\n\ntype s2 struct {\n\ta int8\n\tc int8   // 1\n\tb string // 2\n}\n\nfunc TestStruct(t *testing.T) {\n\tv1 := s1{\n\t\ta: 1,\n\t\tb: \"七米\",\n\t\tc: 2,\n\t}\n\n\tv2 := s2{\n\t\ta: 1,\n\t\tb: \"七米\",\n\t\tc: 2,\n\t}\n\n\tfmt.Println(unsafe.Sizeof(v1), unsafe.Sizeof(v2))\n}\n"
  },
  {
    "path": "lesson70/bluebell/models/user.go",
    "content": "package models\n\ntype User struct {\n\tUserID   int64  `db:\"user_id\"`\n\tUsername string `db:\"username\"`\n\tPassword string `db:\"password\"`\n\tToken    string\n}\n"
  },
  {
    "path": "lesson70/bluebell/pkg/jwt/jwt.go",
    "content": "package jwt\n\nimport (\n\t\"errors\"\n\t\"time\"\n\n\t\"github.com/spf13/viper\"\n\n\t\"github.com/dgrijalva/jwt-go\"\n)\n\nvar mySecret = []byte(\"夏天夏天悄悄过去\")\n\n// MyClaims 自定义声明结构体并内嵌jwt.StandardClaims\n// jwt包自带的jwt.StandardClaims只包含了官方字段\n// 我们这里需要额外记录一个username字段，所以要自定义结构体\n// 如果想要保存更多信息，都可以添加到这个结构体中\ntype MyClaims struct {\n\tUserID   int64  `json:\"user_id\"`\n\tUsername string `json:\"username\"`\n\tjwt.StandardClaims\n}\n\n// GenToken 生成JWT\nfunc GenToken(userID int64, username string) (string, error) {\n\t// 创建一个我们自己的声明的数据\n\tc := MyClaims{\n\t\tuserID,\n\t\t\"username\", // 自定义字段\n\t\tjwt.StandardClaims{\n\t\t\tExpiresAt: time.Now().Add(\n\t\t\t\ttime.Duration(viper.GetInt(\"auth.jwt_expire\")) * time.Hour).Unix(), // 过期时间\n\t\t\tIssuer: \"bluebell\", // 签发人\n\t\t},\n\t}\n\t// 使用指定的签名方法创建签名对象\n\ttoken := jwt.NewWithClaims(jwt.SigningMethodHS256, c)\n\t// 使用指定的secret签名并获得完整的编码后的字符串token\n\treturn token.SignedString(mySecret)\n}\n\n// ParseToken 解析JWT\nfunc ParseToken(tokenString string) (*MyClaims, error) {\n\t// 解析token\n\tvar mc = new(MyClaims)\n\ttoken, err := jwt.ParseWithClaims(tokenString, mc, func(token *jwt.Token) (i interface{}, err error) {\n\t\treturn mySecret, nil\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif token.Valid { // 校验token\n\t\treturn mc, nil\n\t}\n\treturn nil, errors.New(\"invalid token\")\n}\n"
  },
  {
    "path": "lesson70/bluebell/pkg/snowflake/snowflake.go",
    "content": "package snowflake\n\nimport (\n\t\"time\"\n\n\tsf \"github.com/bwmarrin/snowflake\"\n)\n\nvar node *sf.Node\n\nfunc Init(startTime string, machineID int64) (err error) {\n\tvar st time.Time\n\tst, err = time.Parse(\"2006-01-02\", startTime)\n\tif err != nil {\n\t\treturn\n\t}\n\tsf.Epoch = st.UnixNano() / 1000000\n\tnode, err = sf.NewNode(machineID)\n\treturn\n}\nfunc GenID() int64 {\n\treturn node.Generate().Int64()\n}\n"
  },
  {
    "path": "lesson70/bluebell/router/route.go",
    "content": "package router\n\nimport (\n\t\"bluebell/controller\"\n\t\"bluebell/logger\"\n\t\"bluebell/middlewares\"\n\t\"net/http\"\n\n\tginSwagger \"github.com/swaggo/gin-swagger\"\n\t\"github.com/swaggo/gin-swagger/swaggerFiles\"\n\n\t_ \"bluebell/docs\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc SetupRouter(mode string) *gin.Engine {\n\tif mode == gin.ReleaseMode {\n\t\tgin.SetMode(gin.ReleaseMode) // gin设置成发布模式\n\t}\n\tr := gin.New()\n\tr.Use(logger.GinLogger(), logger.GinRecovery(true))\n\n\tr.GET(\"/ping\", func(c *gin.Context) {\n\t\tc.String(http.StatusOK, \"pong\")\n\t})\n\n\tr.GET(\"/swagger/*any\", ginSwagger.WrapHandler(swaggerFiles.Handler))\n\n\tv1 := r.Group(\"/api/v1\")\n\n\t// 注册\n\tv1.POST(\"/signup\", controller.SignUpHandler)\n\t// 登录\n\tv1.POST(\"/login\", controller.LoginHandler)\n\n\tv1.Use(middlewares.JWTAuthMiddleware()) // 应用JWT认证中间件\n\n\t{\n\t\tv1.GET(\"/community\", controller.CommunityHandler)\n\t\tv1.GET(\"/community/:id\", controller.CommunityDetailHandler)\n\n\t\tv1.POST(\"/post\", controller.CreatePostHandler)\n\t\tv1.GET(\"/post/:id\", controller.GetPostDetailHandler)\n\t\tv1.GET(\"/posts\", controller.GetPostListHandler)\n\t\t// 根据时间或分数获取帖子列表\n\t\tv1.GET(\"/posts2\", controller.GetPostListHandler2)\n\n\t\t// 投票\n\t\tv1.POST(\"/vote\", controller.PostVoteController)\n\n\t}\n\n\tr.NoRoute(func(c *gin.Context) {\n\t\tc.JSON(http.StatusOK, gin.H{\n\t\t\t\"msg\": \"404\",\n\t\t})\n\t})\n\treturn r\n}\n"
  },
  {
    "path": "lesson70/bluebell/setting/setting.go",
    "content": "package setting\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\t\"github.com/spf13/viper\"\n)\n\nvar Conf = new(AppConfig)\n\ntype AppConfig struct {\n\tName      string `mapstructure:\"name\"`\n\tMode      string `mapstructure:\"mode\"`\n\tVersion   string `mapstructure:\"version\"`\n\tStartTime string `mapstructure:\"start_time\"`\n\tMachineID int64  `mapstructure:\"machine_id\"`\n\tPort      int    `mapstructure:\"port\"`\n\n\t*LogConfig   `mapstructure:\"log\"`\n\t*MySQLConfig `mapstructure:\"mysql\"`\n\t*RedisConfig `mapstructure:\"redis\"`\n}\n\ntype MySQLConfig struct {\n\tHost         string `mapstructure:\"host\"`\n\tUser         string `mapstructure:\"user\"`\n\tPassword     string `mapstructure:\"password\"`\n\tDB           string `mapstructure:\"dbname\"`\n\tPort         int    `mapstructure:\"port\"`\n\tMaxOpenConns int    `mapstructure:\"max_open_conns\"`\n\tMaxIdleConns int    `mapstructure:\"max_idle_conns\"`\n}\n\ntype RedisConfig struct {\n\tHost         string `mapstructure:\"host\"`\n\tPassword     string `mapstructure:\"password\"`\n\tPort         int    `mapstructure:\"port\"`\n\tDB           int    `mapstructure:\"db\"`\n\tPoolSize     int    `mapstructure:\"pool_size\"`\n\tMinIdleConns int    `mapstructure:\"min_idle_conns\"`\n}\n\ntype LogConfig struct {\n\tLevel      string `mapstructure:\"level\"`\n\tFilename   string `mapstructure:\"filename\"`\n\tMaxSize    int    `mapstructure:\"max_size\"`\n\tMaxAge     int    `mapstructure:\"max_age\"`\n\tMaxBackups int    `mapstructure:\"max_backups\"`\n}\n\nfunc Init(filePath string) (err error) {\n\t// 方式1：直接指定配置文件路径（相对路径或者绝对路径）\n\t// 相对路径：相对执行的可执行文件的相对路径\n\t//viper.SetConfigFile(\"./conf/config.yaml\")\n\t// 绝对路径：系统中实际的文件路径\n\t//viper.SetConfigFile(\"/Users/liwenzhou/Desktop/bluebell/conf/config.yaml\")\n\n\t// 方式2：指定配置文件名和配置文件的位置，viper自行查找可用的配置文件\n\t// 配置文件名不需要带后缀\n\t// 配置文件位置可配置多个\n\t//viper.SetConfigName(\"config\") // 指定配置文件名（不带后缀）\n\t//viper.AddConfigPath(\".\") // 指定查找配置文件的路径（这里使用相对路径）\n\t//viper.AddConfigPath(\"./conf\")      // 指定查找配置文件的路径（这里使用相对路径）\n\n\t// 基本上是配合远程配置中心使用的，告诉viper当前的数据使用什么格式去解析\n\t//viper.SetConfigType(\"json\")\n\n\tviper.SetConfigFile(filePath)\n\n\terr = viper.ReadInConfig() // 读取配置信息\n\tif err != nil {\n\t\t// 读取配置信息失败\n\t\tfmt.Printf(\"viper.ReadInConfig failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\n\t// 把读取到的配置信息反序列化到 Conf 变量中\n\tif err := viper.Unmarshal(Conf); err != nil {\n\t\tfmt.Printf(\"viper.Unmarshal failed, err:%v\\n\", err)\n\t}\n\n\tviper.WatchConfig()\n\tviper.OnConfigChange(func(in fsnotify.Event) {\n\t\tfmt.Println(\"配置文件修改了...\")\n\t\tif err := viper.Unmarshal(Conf); err != nil {\n\t\t\tfmt.Printf(\"viper.Unmarshal failed, err:%v\\n\", err)\n\t\t}\n\t})\n\treturn\n}\n"
  },
  {
    "path": "lesson77/bluebell/.air.conf",
    "content": "# [Air](https://github.com/cosmtrek/air) TOML 格式的配置文件\n\n# 工作目录\n# 使用 . 或绝对路径，请注意 `tmp_dir` 目录必须在 `root` 目录下\nroot = \".\"\ntmp_dir = \"tmp\"\n\n[build]\n# 只需要写你平常编译使用的shell命令。你也可以使用 `make`\ncmd = \"swag init && go build -o ./tmp/main\"\n# 由`cmd`命令得到的二进制文件名\nbin = \"tmp/main\"\n# 自定义的二进制，可以添加额外的编译标识例如添加 GIN_MODE=release\nfull_bin = \"./tmp/main ./conf/config.yaml\"\n# 监听以下文件扩展名的文件.\ninclude_ext = [\"go\", \"tpl\", \"tmpl\", \"html\", \"yaml\"]\n# 忽略这些文件扩展名或目录\nexclude_dir = [\"assets\", \"tmp\", \"vendor\", \"frontend/node_modules\"]\n# 监听以下指定目录的文件\ninclude_dir = []\n# 排除以下文件\nexclude_file = []\n# 如果文件更改过于频繁，则没有必要在每次更改时都触发构建。可以设置触发构建的延迟时间\ndelay = 1000 # ms\n# 发生构建错误时，停止运行旧的二进制文件。\nstop_on_error = true\n# air的日志文件名，该日志文件放置在你的`tmp_dir`中\nlog = \"air_errors.log\"\n\n[log]\n# 显示日志时间\ntime = true\n\n[color]\n# 自定义每个部分显示的颜色。如果找不到颜色，使用原始的应用程序日志。\nmain = \"magenta\"\nwatcher = \"cyan\"\nbuild = \"yellow\"\nrunner = \"green\"\n\n[misc]\n# 退出时删除tmp目录\nclean_on_exit = true"
  },
  {
    "path": "lesson77/bluebell/Dockerfile",
    "content": "FROM golang:alpine AS builder\n\n# 为我们的镜像设置必要的环境变量\nENV GO111MODULE=on \\\n    CGO_ENABLED=0 \\\n    GOOS=linux \\\n    GOARCH=amd64\n\n# 移动到工作目录：/build\nWORKDIR /build\n\n# 复制项目中的 go.mod 和 go.sum文件并下载依赖信息\nCOPY go.mod .\nCOPY go.sum .\nRUN go mod download\n\n# 将代码复制到容器中\nCOPY . .\n\n# 将我们的代码编译成二进制可执行文件 bluebell_app\nRUN go build -o bluebell_app .\n\n###################\n# 接下来创建一个小镜像\n###################\nFROM debian:stretch-slim\n\nCOPY ./wait-for.sh /\nCOPY ./templates /templates\nCOPY ./static /static\nCOPY ./conf /conf\n\n# 从builder镜像中把/dist/app 拷贝到当前目录\nCOPY --from=builder /build/bluebell_app /\n\nRUN set -eux; \\\n\tapt-get update; \\\n\tapt-get install -y \\\n\t\t--no-install-recommends \\\n\t\tnetcat; \\\n        chmod 755 wait-for.sh\n\n# 声明服务端口\nEXPOSE 8084\n\n# 需要运行的命令\n#ENTRYPOINT [\"/bluebell_app\", \"conf/config.yaml\"]"
  },
  {
    "path": "lesson77/bluebell/Dockerfile.back",
    "content": "FROM golang:alpine AS builder\n\n# 为我们的镜像设置必要的环境变量\nENV GO111MODULE=on \\\n    CGO_ENABLED=0 \\\n    GOOS=linux \\\n    GOARCH=amd64\n\n# 移动到工作目录：/build\nWORKDIR /build\n\n# 复制项目中的 go.mod 和 go.sum文件并下载依赖信息\nCOPY go.mod .\nCOPY go.sum .\nRUN go mod download\n\n# 将代码复制到容器中\nCOPY . .\n\n# 将我们的代码编译成二进制可执行文件 bluebell_app\nRUN go build -o bluebell_app .\n\n###################\n# 接下来创建一个小镜像\n###################\nFROM scratch\n\nCOPY ./templates /templates\nCOPY ./static /static\nCOPY ./conf /conf\n\n# 从builder镜像中把/dist/app 拷贝到当前目录\nCOPY --from=builder /build/bluebell_app /\n\n# 声明服务端口\nEXPOSE 8084\n\n# 需要运行的命令\nENTRYPOINT [\"/bluebell_app\", \"conf/config.yaml\"]"
  },
  {
    "path": "lesson77/bluebell/Makefile",
    "content": ".PHONY: all build run gotool clean help\n\nBINARY=\"xx\"\n\nall: gotool build\n\nbuild:\n\tCGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ${BINARY}\n\nrun:\n\t@go run ./main.go conf/config.yaml\n\ngotool:\n\tgo fmt ./\n\tgo vet ./\n\nclean:\n\t@if [ -f ${BINARY} ] ; then rm ${BINARY} ; fi\n\nhelp:\n\t@echo \"make - 格式化 Go 代码, 并编译生成二进制文件\"\n\t@echo \"make build - 编译 Go 代码, 生成二进制文件\"\n\t@echo \"make run - 直接运行 Go 代码\"\n\t@echo \"make clean - 移除二进制文件和 vim swap files\"\n\t@echo \"make gotool - 运行 Go 工具 'fmt' and 'vet'\"\n"
  },
  {
    "path": "lesson77/bluebell/conf/config.yaml",
    "content": "name: \"bluebell\"\nmode: \"dev\"\nport: 8084\nversion: \"v0.0.1\"\nstart_time: \"2020-07-01\"\nmachine_id: 1\n\nauth:\n  jwt_expire: 8760\n\nlog:\n  level: \"debug\"\n  filename: \"web_app.log\"\n  max_size: 200\n  max_age: 30\n  max_backups: 7\nmysql:\n  host: mysql8019\n  port: 3306\n  user: \"root\"\n  password: \"root1234\"\n  dbname: \"bluebell\"\n  max_open_conns: 200\n  max_idle_conns: 50\nredis:\n  host: redis507\n  port: 6379\n  password: \"\"\n  db: 0\n  pool_size: 100"
  },
  {
    "path": "lesson77/bluebell/controller/code.go",
    "content": "package controller\n\ntype ResCode int64\n\nconst (\n\tCodeSuccess ResCode = 1000 + iota\n\tCodeInvalidParam\n\tCodeUserExist\n\tCodeUserNotExist\n\tCodeInvalidPassword\n\tCodeServerBusy\n\n\tCodeNeedLogin\n\tCodeInvalidToken\n)\n\nvar codeMsgMap = map[ResCode]string{\n\tCodeSuccess:         \"success\",\n\tCodeInvalidParam:    \"请求参数错误\",\n\tCodeUserExist:       \"用户名已存在\",\n\tCodeUserNotExist:    \"用户名不存在\",\n\tCodeInvalidPassword: \"用户名或密码错误\",\n\tCodeServerBusy:      \"服务繁忙\",\n\n\tCodeNeedLogin:    \"需要登录\",\n\tCodeInvalidToken: \"无效的token\",\n}\n\nfunc (c ResCode) Msg() string {\n\tmsg, ok := codeMsgMap[c]\n\tif !ok {\n\t\tmsg = codeMsgMap[CodeServerBusy]\n\t}\n\treturn msg\n}\n"
  },
  {
    "path": "lesson77/bluebell/controller/community.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/logic\"\n\t\"strconv\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"go.uber.org/zap\"\n)\n\n// ---- 跟社区相关的 ----\n\nfunc CommunityHandler(c *gin.Context) {\n\t// 查询到所有的社区（community_id, community_name) 以列表的形式返回\n\tdata, err := logic.GetCommunityList()\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetCommunityList() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy) // 不轻易把服务端报错暴露给外面\n\t\treturn\n\t}\n\tResponseSuccess(c, data)\n}\n\n// CommunityDetailHandler 社区分类详情\nfunc CommunityDetailHandler(c *gin.Context) {\n\t// 1. 获取社区id\n\tidStr := c.Param(\"id\") // 获取URL参数\n\tid, err := strconv.ParseInt(idStr, 10, 64)\n\tif err != nil {\n\t\tResponseError(c, CodeInvalidParam)\n\t\treturn\n\t}\n\n\t// 2. 根据id获取社区详情\n\tdata, err := logic.GetCommunityDetail(id)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetCommunityList() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy) // 不轻易把服务端报错暴露给外面\n\t\treturn\n\t}\n\tResponseSuccess(c, data)\n}\n"
  },
  {
    "path": "lesson77/bluebell/controller/doc_response_models.go",
    "content": "package controller\n\nimport \"bluebell/models\"\n\n// 专门用来放接口文档用到的model\n// 因为我们的接口文档返回的数据格式是一致的，但是具体的data类型不一致\n\n// _ResponsePostList 帖子列表接口响应数据\ntype _ResponsePostList struct {\n\tCode    ResCode                 `json:\"code\"`    // 业务响应状态码\n\tMessage string                  `json:\"message\"` // 提示信息\n\tData    []*models.ApiPostDetail `json:\"data\"`    // 数据\n}\n"
  },
  {
    "path": "lesson77/bluebell/controller/post.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/logic\"\n\t\"bluebell/models\"\n\t\"strconv\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"go.uber.org/zap\"\n\t// swagger 嵌入文件\n)\n\n// CreatePostHandler 创建帖子的处理函数\nfunc CreatePostHandler(c *gin.Context) {\n\t// 1. 获取参数及参数的校验\n\t//c.ShouldBindJSON()  // validator --> binding tag\n\tp := new(models.Post)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\tzap.L().Debug(\"c.ShouldBindJSON(p) error\", zap.Any(\"err\", err))\n\t\tzap.L().Error(\"create post with invalid param\")\n\t\tResponseError(c, CodeInvalidParam)\n\t\treturn\n\t}\n\t// 从 c 取到当前发请求的用户的ID\n\tuserID, err := getCurrentUserID(c)\n\tif err != nil {\n\t\tResponseError(c, CodeNeedLogin)\n\t\treturn\n\t}\n\tp.AuthorID = userID\n\t// 2. 创建帖子\n\tif err := logic.CreatePost(p); err != nil {\n\t\tzap.L().Error(\"logic.CreatePost(p) failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\n\t// 3. 返回响应\n\tResponseSuccess(c, nil)\n}\n\n// GetPostDetailHandler 获取帖子详情的处理函数\nfunc GetPostDetailHandler(c *gin.Context) {\n\t// 1. 获取参数（从URL中获取帖子的id）\n\tpidStr := c.Param(\"id\")\n\tpid, err := strconv.ParseInt(pidStr, 10, 64)\n\tif err != nil {\n\t\tzap.L().Error(\"get post detail with invalid param\", zap.Error(err))\n\t\tResponseError(c, CodeInvalidParam)\n\t\treturn\n\t}\n\n\t// 2. 根据id取出帖子数据（查数据库）\n\tdata, err := logic.GetPostById(pid)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetPostById(pid) failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\t// 3. 返回响应\n\tResponseSuccess(c, data)\n}\n\n// GetPostListHandler 获取帖子列表的处理函数\nfunc GetPostListHandler(c *gin.Context) {\n\t// 获取分页参数\n\tpage, size := getPageInfo(c)\n\t// 获取数据\n\tdata, err := logic.GetPostList(page, size)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetPostList() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\tResponseSuccess(c, data)\n\t// 返回响应\n}\n\n// GetPostListHandler2 升级版帖子列表接口\n// @Summary 升级版帖子列表接口\n// @Description 可按社区按时间或分数排序查询帖子列表接口\n// @Tags 帖子相关接口(api分组展示使用的)\n// @Accept application/json\n// @Produce application/json\n// @Param Authorization header string true \"Bearer JWT\"\n// @Param object query models.ParamPostList false \"查询参数\"\n// @Security ApiKeyAuth\n// @Success 200 {object} _ResponsePostList\n// @Router /posts2 [get]\nfunc GetPostListHandler2(c *gin.Context) {\n\t// GET请求参数(query string)：/api/v1/posts2?page=1&size=10&order=time\n\t// 初始化结构体时指定初始参数\n\tp := &models.ParamPostList{\n\t\tPage:  1,\n\t\tSize:  10,\n\t\tOrder: models.OrderTime, // magic string\n\t}\n\t//c.ShouldBind()  根据请求的数据类型选择相应的方法去获取数据\n\t//c.ShouldBindJSON() 如果请求中携带的是json格式的数据，才能用这个方法获取到数据\n\tif err := c.ShouldBindQuery(p); err != nil {\n\t\tzap.L().Error(\"GetPostListHandler2 with invalid params\", zap.Error(err))\n\t\tResponseError(c, CodeInvalidParam)\n\t\treturn\n\t}\n\tdata, err := logic.GetPostListNew(p) // 更新：合二为一\n\t// 获取数据\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetPostList() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\tResponseSuccess(c, data)\n\t// 返回响应\n}\n\n// 根据社区去查询帖子列表\n//func GetCommunityPostListHandler(c *gin.Context) {\n//\t// 初始化结构体时指定初始参数\n//\tp := &models.ParamCommunityPostList{\n//\t\tParamPostList: &models.ParamPostList{\n//\t\t\tPage:  1,\n//\t\t\tSize:  10,\n//\t\t\tOrder: models.OrderTime,\n//\t\t},\n//\t}\n//\t//c.ShouldBind()  根据请求的数据类型选择相应的方法去获取数据\n//\t//c.ShouldBindJSON() 如果请求中携带的是json格式的数据，才能用这个方法获取到数据\n//\tif err := c.ShouldBindQuery(p); err != nil {\n//\t\tzap.L().Error(\"GetCommunityPostListHandler with invalid params\", zap.Error(err))\n//\t\tResponseError(c, CodeInvalidParam)\n//\t\treturn\n//\t}\n//\n//\t// 获取数据\n//\tdata, err := logic.GetCommunityPostList(p)\n//\tif err != nil {\n//\t\tzap.L().Error(\"logic.GetPostList() failed\", zap.Error(err))\n//\t\tResponseError(c, CodeServerBusy)\n//\t\treturn\n//\t}\n//\tResponseSuccess(c, data)\n//}\n"
  },
  {
    "path": "lesson77/bluebell/controller/post_test.go",
    "content": "package controller\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestCreatePostHandler(t *testing.T) {\n\n\tgin.SetMode(gin.TestMode)\n\tr := gin.Default()\n\turl := \"/api/v1/post\"\n\tr.POST(url, CreatePostHandler)\n\n\tbody := `{\n\t\t\"community_id\": 1,\n\t\t\"title\": \"test\",\n\t\t\"content\": \"just a test\"\n\t}`\n\n\treq, _ := http.NewRequest(http.MethodPost, url, bytes.NewReader([]byte(body)))\n\n\tw := httptest.NewRecorder()\n\tr.ServeHTTP(w, req)\n\n\tassert.Equal(t, 200, w.Code)\n\n\t// 判断响应的内容是不是按预期返回了需要登录的错误\n\n\t// 方法1：判断响应内容中是不是包含指定的字符串\n\t//assert.Contains(t, w.Body.String(), \"需要登录\")\n\n\t// 方法2：将响应的内容反序列化到ResponseData 然后判断字段与预期是否一致\n\tres := new(ResponseData)\n\tif err := json.Unmarshal(w.Body.Bytes(), res); err != nil {\n\t\tt.Fatalf(\"json.Unmarshal w.Body failed, err:%v\\n\", err)\n\t}\n\tassert.Equal(t, res.Code, CodeNeedLogin)\n}\n"
  },
  {
    "path": "lesson77/bluebell/controller/request.go",
    "content": "package controller\n\nimport (\n\t\"errors\"\n\t\"strconv\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\nconst CtxUserIDKey = \"userID\"\n\nvar ErrorUserNotLogin = errors.New(\"用户未登录\")\n\n// getCurrentUserID 获取当前登录的用户ID\nfunc getCurrentUserID(c *gin.Context) (userID int64, err error) {\n\tuid, ok := c.Get(CtxUserIDKey)\n\tif !ok {\n\t\terr = ErrorUserNotLogin\n\t\treturn\n\t}\n\tuserID, ok = uid.(int64)\n\tif !ok {\n\t\terr = ErrorUserNotLogin\n\t\treturn\n\t}\n\treturn\n}\n\nfunc getPageInfo(c *gin.Context) (int64, int64) {\n\tpageStr := c.Query(\"page\")\n\tsizeStr := c.Query(\"size\")\n\n\tvar (\n\t\tpage int64\n\t\tsize int64\n\t\terr  error\n\t)\n\n\tpage, err = strconv.ParseInt(pageStr, 10, 64)\n\tif err != nil {\n\t\tpage = 1\n\t}\n\tsize, err = strconv.ParseInt(sizeStr, 10, 64)\n\tif err != nil {\n\t\tsize = 10\n\t}\n\treturn page, size\n}\n"
  },
  {
    "path": "lesson77/bluebell/controller/response.go",
    "content": "package controller\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n/*\n{\n\t\"code\": 10000, // 程序中的错误码\n\t\"msg\": xx,     // 提示信息\n\t\"data\": {},    // 数据\n}\n\n*/\n\ntype ResponseData struct {\n\tCode ResCode     `json:\"code\"`\n\tMsg  interface{} `json:\"msg\"`\n\tData interface{} `json:\"data,omitempty\"`\n}\n\nfunc ResponseError(c *gin.Context, code ResCode) {\n\tc.JSON(http.StatusOK, &ResponseData{\n\t\tCode: code,\n\t\tMsg:  code.Msg(),\n\t\tData: nil,\n\t})\n}\n\nfunc ResponseErrorWithMsg(c *gin.Context, code ResCode, msg interface{}) {\n\tc.JSON(http.StatusOK, &ResponseData{\n\t\tCode: code,\n\t\tMsg:  msg,\n\t\tData: nil,\n\t})\n}\n\nfunc ResponseSuccess(c *gin.Context, data interface{}) {\n\tc.JSON(http.StatusOK, &ResponseData{\n\t\tCode: CodeSuccess,\n\t\tMsg:  CodeSuccess.Msg(),\n\t\tData: data,\n\t})\n}\n"
  },
  {
    "path": "lesson77/bluebell/controller/user.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/logic\"\n\t\"bluebell/models\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/go-playground/validator/v10\"\n\t\"go.uber.org/zap\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// SignUpHandler 处理注册请求的函数\nfunc SignUpHandler(c *gin.Context) {\n\t// 1. 获取参数和参数校验\n\tp := new(models.ParamSignUp)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\t// 请求参数有误，直接返回响应\n\t\tzap.L().Error(\"SignUp with invalid param\", zap.Error(err))\n\t\t// 判断err是不是validator.ValidationErrors 类型\n\t\terrs, ok := err.(validator.ValidationErrors)\n\t\tif !ok {\n\t\t\tResponseError(c, CodeInvalidParam)\n\t\t\treturn\n\t\t}\n\t\tResponseErrorWithMsg(c, CodeInvalidParam, removeTopStruct(errs.Translate(trans)))\n\t\treturn\n\t}\n\t// 2. 业务处理\n\tif err := logic.SignUp(p); err != nil {\n\t\tzap.L().Error(\"logic.SignUp failed\", zap.Error(err))\n\t\tif errors.Is(err, mysql.ErrorUserExist) {\n\t\t\tResponseError(c, CodeUserExist)\n\t\t\treturn\n\t\t}\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\t// 3. 返回响应\n\tResponseSuccess(c, nil)\n}\n\nfunc LoginHandler(c *gin.Context) {\n\t// 1.获取请求参数及参数校验\n\tp := new(models.ParamLogin)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\t// 请求参数有误，直接返回响应\n\t\tzap.L().Error(\"Login with invalid param\", zap.Error(err))\n\t\t// 判断err是不是validator.ValidationErrors 类型\n\t\terrs, ok := err.(validator.ValidationErrors)\n\t\tif !ok {\n\t\t\tResponseError(c, CodeInvalidParam)\n\t\t\treturn\n\t\t}\n\t\tResponseErrorWithMsg(c, CodeInvalidParam, removeTopStruct(errs.Translate(trans)))\n\t\treturn\n\t}\n\t// 2.业务逻辑处理\n\tuser, err := logic.Login(p)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.Login failed\", zap.String(\"username\", p.Username), zap.Error(err))\n\t\tif errors.Is(err, mysql.ErrorUserNotExist) {\n\t\t\tResponseError(c, CodeUserNotExist)\n\t\t\treturn\n\t\t}\n\t\tResponseError(c, CodeInvalidPassword)\n\t\treturn\n\t}\n\n\t// 3.返回响应\n\tResponseSuccess(c, gin.H{\n\t\t\"user_id\":   fmt.Sprintf(\"%d\", user.UserID), // id值大于1<<53-1  int64类型的最大值是1<<63-1\n\t\t\"user_name\": user.Username,\n\t\t\"token\":     user.Token,\n\t})\n}\n"
  },
  {
    "path": "lesson77/bluebell/controller/validator.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/models\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin/binding\"\n\t\"github.com/go-playground/locales/en\"\n\t\"github.com/go-playground/locales/zh\"\n\tut \"github.com/go-playground/universal-translator\"\n\t\"github.com/go-playground/validator/v10\"\n\tenTranslations \"github.com/go-playground/validator/v10/translations/en\"\n\tzhTranslations \"github.com/go-playground/validator/v10/translations/zh\"\n)\n\n// 定义一个全局翻译器T\nvar trans ut.Translator\n\n// InitTrans 初始化翻译器\nfunc InitTrans(locale string) (err error) {\n\t// 修改gin框架中的Validator引擎属性，实现自定制\n\tif v, ok := binding.Validator.Engine().(*validator.Validate); ok {\n\n\t\t// 注册一个获取json tag的自定义方法\n\t\tv.RegisterTagNameFunc(func(fld reflect.StructField) string {\n\t\t\tname := strings.SplitN(fld.Tag.Get(\"json\"), \",\", 2)[0]\n\t\t\tif name == \"-\" {\n\t\t\t\treturn \"\"\n\t\t\t}\n\t\t\treturn name\n\t\t})\n\n\t\t// 为SignUpParam注册自定义校验方法\n\t\tv.RegisterStructValidation(SignUpParamStructLevelValidation, models.ParamSignUp{})\n\n\t\tzhT := zh.New() // 中文翻译器\n\t\tenT := en.New() // 英文翻译器\n\n\t\t// 第一个参数是备用（fallback）的语言环境\n\t\t// 后面的参数是应该支持的语言环境（支持多个）\n\t\t// uni := ut.New(zhT, zhT) 也是可以的\n\t\tuni := ut.New(enT, zhT, enT)\n\n\t\t// locale 通常取决于 http 请求头的 'Accept-Language'\n\t\tvar ok bool\n\t\t// 也可以使用 uni.FindTranslator(...) 传入多个locale进行查找\n\t\ttrans, ok = uni.GetTranslator(locale)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"uni.GetTranslator(%s) failed\", locale)\n\t\t}\n\n\t\t// 注册翻译器\n\t\tswitch locale {\n\t\tcase \"en\":\n\t\t\terr = enTranslations.RegisterDefaultTranslations(v, trans)\n\t\tcase \"zh\":\n\t\t\terr = zhTranslations.RegisterDefaultTranslations(v, trans)\n\t\tdefault:\n\t\t\terr = enTranslations.RegisterDefaultTranslations(v, trans)\n\t\t}\n\t\treturn\n\t}\n\treturn\n}\n\n// removeTopStruct 去除提示信息中的结构体名称\nfunc removeTopStruct(fields map[string]string) map[string]string {\n\tres := map[string]string{}\n\tfor field, err := range fields {\n\t\tres[field[strings.Index(field, \".\")+1:]] = err\n\t}\n\treturn res\n}\n\n// SignUpParamStructLevelValidation 自定义SignUpParam结构体校验函数\nfunc SignUpParamStructLevelValidation(sl validator.StructLevel) {\n\tsu := sl.Current().Interface().(models.ParamSignUp)\n\n\tif su.Password != su.RePassword {\n\t\t// 输出错误提示信息，最后一个参数就是传递的param\n\t\tsl.ReportError(su.RePassword, \"re_password\", \"RePassword\", \"eqfield\", \"password\")\n\t}\n}\n"
  },
  {
    "path": "lesson77/bluebell/controller/vote.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/logic\"\n\t\"bluebell/models\"\n\n\t\"go.uber.org/zap\"\n\n\t\"github.com/go-playground/validator/v10\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// 投票\n\n//type VoteData struct {\n//\t// UserID 从请求中获取当前的用户\n//\tPostID    int64 `json:\"post_id,string\"`   // 贴子id\n//\tDirection int   `json:\"direction,string\"` // 赞成票(1)还是反对票(-1)\n//}\n\nfunc PostVoteController(c *gin.Context) {\n\t// 参数校验\n\tp := new(models.ParamVoteData)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\terrs, ok := err.(validator.ValidationErrors) // 类型断言\n\t\tif !ok {\n\t\t\tResponseError(c, CodeInvalidParam)\n\t\t\treturn\n\t\t}\n\t\terrData := removeTopStruct(errs.Translate(trans)) // 翻译并去除掉错误提示中的结构体标识\n\t\tResponseErrorWithMsg(c, CodeInvalidParam, errData)\n\t\treturn\n\t}\n\t// 获取当前请求的用户的id\n\tuserID, err := getCurrentUserID(c)\n\tif err != nil {\n\t\tResponseError(c, CodeNeedLogin)\n\t\treturn\n\t}\n\t// 具体投票的业务逻辑\n\tif err := logic.VoteForPost(userID, p); err != nil {\n\t\tzap.L().Error(\"logic.VoteForPost() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\n\tResponseSuccess(c, nil)\n}\n"
  },
  {
    "path": "lesson77/bluebell/dao/mysql/community.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/models\"\n\t\"database/sql\"\n\n\t\"go.uber.org/zap\"\n)\n\nfunc GetCommunityList() (communityList []*models.Community, err error) {\n\tsqlStr := \"select community_id, community_name from community\"\n\tif err := db.Select(&communityList, sqlStr); err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\tzap.L().Warn(\"there is no community in db\")\n\t\t\terr = nil\n\t\t}\n\t}\n\treturn\n}\n\n// GetCommunityDetailByID 根据ID查询社区详情\nfunc GetCommunityDetailByID(id int64) (community *models.CommunityDetail, err error) {\n\tcommunity = new(models.CommunityDetail)\n\tsqlStr := `select \n\t\t\tcommunity_id, community_name, introduction, create_time\n\t\t\tfrom community \n\t\t\twhere community_id = ?\n\t`\n\tif err := db.Get(community, sqlStr, id); err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\terr = ErrorInvalidID\n\t\t}\n\t}\n\treturn community, err\n}\n"
  },
  {
    "path": "lesson77/bluebell/dao/mysql/error_code.go",
    "content": "package mysql\n\nimport \"errors\"\n\nvar (\n\tErrorUserExist       = errors.New(\"用户已存在\")\n\tErrorUserNotExist    = errors.New(\"用户不存在\")\n\tErrorInvalidPassword = errors.New(\"用户名或密码错误\")\n\tErrorInvalidID       = errors.New(\"无效的ID\")\n)\n"
  },
  {
    "path": "lesson77/bluebell/dao/mysql/mysql.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/setting\"\n\t\"fmt\"\n\n\t_ \"github.com/go-sql-driver/mysql\"\n\t\"github.com/jmoiron/sqlx\"\n)\n\nvar db *sqlx.DB\n\n// Init 初始化MySQL连接\nfunc Init(cfg *setting.MySQLConfig) (err error) {\n\t// \"user:password@tcp(host:port)/dbname\"\n\tdsn := fmt.Sprintf(\"%s:%s@tcp(%s:%d)/%s?parseTime=true&loc=Local\", cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.DB)\n\tdb, err = sqlx.Connect(\"mysql\", dsn)\n\tif err != nil {\n\t\treturn\n\t}\n\tdb.SetMaxOpenConns(cfg.MaxOpenConns)\n\tdb.SetMaxIdleConns(cfg.MaxIdleConns)\n\treturn\n}\n\n// Close 关闭MySQL连接\nfunc Close() {\n\t_ = db.Close()\n}\n"
  },
  {
    "path": "lesson77/bluebell/dao/mysql/post.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/models\"\n\t\"strings\"\n\n\t\"github.com/jmoiron/sqlx\"\n)\n\n// CreatePost 创建帖子\nfunc CreatePost(p *models.Post) (err error) {\n\tsqlStr := `insert into post(\n\tpost_id, title, content, author_id, community_id)\n\tvalues (?, ?, ?, ?, ?)\n\t`\n\t_, err = db.Exec(sqlStr, p.ID, p.Title, p.Content, p.AuthorID, p.CommunityID)\n\treturn\n}\n\n// GetPostById 根据id查询单个贴子数据\nfunc GetPostById(pid int64) (post *models.Post, err error) {\n\tpost = new(models.Post)\n\tsqlStr := `select\n\tpost_id, title, content, author_id, community_id, create_time\n\tfrom post\n\twhere post_id = ?\n\t`\n\terr = db.Get(post, sqlStr, pid)\n\treturn\n}\n\n// GetPostList 查询帖子列表函数\nfunc GetPostList(page, size int64) (posts []*models.Post, err error) {\n\tsqlStr := `select \n\tpost_id, title, content, author_id, community_id, create_time\n\tfrom post\n\tORDER BY create_time\n\tDESC\n\tlimit ?,?\n\t`\n\tposts = make([]*models.Post, 0, 2) // 不要写成make([]*models.Post, 2)\n\terr = db.Select(&posts, sqlStr, (page-1)*size, size)\n\treturn\n}\n\n// GetPostListByIDs 根据给定的id列表查询帖子数据\nfunc GetPostListByIDs(ids []string) (postList []*models.Post, err error) {\n\tsqlStr := `select post_id, title, content, author_id, community_id, create_time\n\tfrom post\n\twhere post_id in (?)\n\torder by FIND_IN_SET(post_id, ?)\n\t`\n\t// https: //www.liwenzhou.com/posts/Go/sqlx/\n\tquery, args, err := sqlx.In(sqlStr, ids, strings.Join(ids, \",\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tquery = db.Rebind(query)\n\terr = db.Select(&postList, query, args...) // !!!!!!\n\treturn\n}\n"
  },
  {
    "path": "lesson77/bluebell/dao/mysql/post_test.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/models\"\n\t\"bluebell/setting\"\n\t\"testing\"\n)\n\nfunc init() {\n\tdbCfg := setting.MySQLConfig{\n\t\tHost:         \"127.0.0.1\",\n\t\tUser:         \"root\",\n\t\tPassword:     \"root1234\",\n\t\tDB:           \"bluebell\",\n\t\tPort:         13306,\n\t\tMaxOpenConns: 10,\n\t\tMaxIdleConns: 10,\n\t}\n\terr := Init(&dbCfg)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc TestCreatePost(t *testing.T) {\n\tpost := models.Post{\n\t\tID:          10,\n\t\tAuthorID:    123,\n\t\tCommunityID: 1,\n\t\tTitle:       \"test\",\n\t\tContent:     \"just a test\",\n\t}\n\terr := CreatePost(&post)\n\tif err != nil {\n\t\tt.Fatalf(\"CreatePost insert record into mysql failed, err:%v\\n\", err)\n\t}\n\tt.Logf(\"CreatePost insert record into mysql success\")\n}\n"
  },
  {
    "path": "lesson77/bluebell/dao/mysql/user.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/models\"\n\t\"crypto/md5\"\n\t\"database/sql\"\n\t\"encoding/hex\"\n)\n\n// 把每一步数据库操作封装成函数\n// 待logic层根据业务需求调用\n\nconst secret = \"liwenzhou.com\"\n\n// CheckUserExist 检查指定用户名的用户是否存在\nfunc CheckUserExist(username string) (err error) {\n\tsqlStr := `select count(user_id) from user where username = ?`\n\tvar count int64\n\tif err := db.Get(&count, sqlStr, username); err != nil {\n\t\treturn err\n\t}\n\tif count > 0 {\n\t\treturn ErrorUserExist\n\t}\n\treturn\n}\n\n// InsertUser 想数据库中插入一条新的用户记录\nfunc InsertUser(user *models.User) (err error) {\n\t// 对密码进行加密\n\tuser.Password = encryptPassword(user.Password)\n\t// 执行SQL语句入库\n\tsqlStr := `insert into user(user_id, username, password) values(?,?,?)`\n\t_, err = db.Exec(sqlStr, user.UserID, user.Username, user.Password)\n\treturn\n}\n\n// encryptPassword 密码加密\nfunc encryptPassword(oPassword string) string {\n\th := md5.New()\n\th.Write([]byte(secret))\n\treturn hex.EncodeToString(h.Sum([]byte(oPassword)))\n}\n\nfunc Login(user *models.User) (err error) {\n\toPassword := user.Password // 用户登录的密码\n\tsqlStr := `select user_id, username, password from user where username=?`\n\terr = db.Get(user, sqlStr, user.Username)\n\tif err == sql.ErrNoRows {\n\t\treturn ErrorUserNotExist\n\t}\n\tif err != nil {\n\t\t// 查询数据库失败\n\t\treturn err\n\t}\n\t// 判断密码是否正确\n\tpassword := encryptPassword(oPassword)\n\tif password != user.Password {\n\t\treturn ErrorInvalidPassword\n\t}\n\treturn\n}\n\n// GetUserById 根据id获取用户信息\nfunc GetUserById(uid int64) (user *models.User, err error) {\n\tuser = new(models.User)\n\tsqlStr := `select user_id, username from user where user_id = ?`\n\terr = db.Get(user, sqlStr, uid)\n\treturn\n}\n"
  },
  {
    "path": "lesson77/bluebell/dao/redis/keys.go",
    "content": "package redis\n\n// redis key\n\n// redis key注意使用命名空间的方式,方便查询和拆分\n\nconst (\n\tPrefix             = \"bluebell:\"   // 项目key前缀\n\tKeyPostTimeZSet    = \"post:time\"   // zset;贴子及发帖时间\n\tKeyPostScoreZSet   = \"post:score\"  // zset;贴子及投票的分数\n\tKeyPostVotedZSetPF = \"post:voted:\" // zset;记录用户及投票类型;参数是post id\n\n\tKeyCommunitySetPF = \"community:\" // set;保存每个分区下帖子的id\n)\n\n// 给redis key加上前缀\nfunc getRedisKey(key string) string {\n\treturn Prefix + key\n}\n"
  },
  {
    "path": "lesson77/bluebell/dao/redis/post.go",
    "content": "package redis\n\nimport (\n\t\"bluebell/models\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/go-redis/redis\"\n)\n\nfunc getIDsFormKey(key string, page, size int64) ([]string, error) {\n\tstart := (page - 1) * size\n\tend := start + size - 1\n\t// 3. ZREVRANGE 按分数从大到小的顺序查询指定数量的元素\n\treturn client.ZRevRange(key, start, end).Result()\n}\n\nfunc GetPostIDsInOrder(p *models.ParamPostList) ([]string, error) {\n\t// 从redis获取id\n\t// 1. 根据用户请求中携带的order参数确定要查询的redis key\n\tkey := getRedisKey(KeyPostTimeZSet)\n\tif p.Order == models.OrderScore {\n\t\tkey = getRedisKey(KeyPostScoreZSet)\n\t}\n\t// 2. 确定查询的索引起始点\n\treturn getIDsFormKey(key, p.Page, p.Size)\n}\n\n// GetPostVoteData 根据ids查询每篇帖子的投赞成票的数据\nfunc GetPostVoteData(ids []string) (data []int64, err error) {\n\t//data = make([]int64, 0, len(ids))\n\t//for _, id := range ids {\n\t//\tkey := getRedisKey(KeyPostVotedZSetPF + id)\n\t//\t// 查找key中分数是1的元素的数量->统计每篇帖子的赞成票的数量\n\t//\tv := client.ZCount(key, \"1\", \"1\").Val()\n\t//\tdata = append(data, v)\n\t//}\n\t// 使用pipeline一次发送多条命令,减少RTT\n\tpipeline := client.Pipeline()\n\tfor _, id := range ids {\n\t\tkey := getRedisKey(KeyPostVotedZSetPF + id)\n\t\tpipeline.ZCount(key, \"1\", \"1\")\n\t}\n\tcmders, err := pipeline.Exec()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdata = make([]int64, 0, len(cmders))\n\tfor _, cmder := range cmders {\n\t\tv := cmder.(*redis.IntCmd).Val()\n\t\tdata = append(data, v)\n\t}\n\treturn\n}\n\n// GetCommunityPostIDsInOrder 按社区查询ids\nfunc GetCommunityPostIDsInOrder(p *models.ParamPostList) ([]string, error) {\n\n\torderKey := getRedisKey(KeyPostTimeZSet)\n\tif p.Order == models.OrderScore {\n\t\torderKey = getRedisKey(KeyPostScoreZSet)\n\t}\n\n\t// 使用 zinterstore 把分区的帖子set与帖子分数的 zset 生成一个新的zset\n\t// 针对新的zset 按之前的逻辑取数据\n\n\t// 社区的key\n\tcKey := getRedisKey(KeyCommunitySetPF + strconv.Itoa(int(p.CommunityID)))\n\n\t// 利用缓存key减少zinterstore执行的次数\n\tkey := orderKey + strconv.Itoa(int(p.CommunityID))\n\tif client.Exists(key).Val() < 1 {\n\t\t// 不存在，需要计算\n\t\tpipeline := client.Pipeline()\n\t\tpipeline.ZInterStore(key, redis.ZStore{\n\t\t\tAggregate: \"MAX\",\n\t\t}, cKey, orderKey) // zinterstore 计算\n\t\tpipeline.Expire(key, 60*time.Second) // 设置超时时间\n\t\t_, err := pipeline.Exec()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\t// 存在的话就直接根据key查询ids\n\treturn getIDsFormKey(key, p.Page, p.Size)\n}\n"
  },
  {
    "path": "lesson77/bluebell/dao/redis/redis.go",
    "content": "package redis\n\nimport (\n\t\"bluebell/setting\"\n\t\"fmt\"\n\n\t\"github.com/go-redis/redis\"\n)\n\nvar (\n\tclient *redis.Client\n\tNil    = redis.Nil\n)\n\n// Init 初始化连接\nfunc Init(cfg *setting.RedisConfig) (err error) {\n\tclient = redis.NewClient(&redis.Options{\n\t\tAddr:         fmt.Sprintf(\"%s:%d\", cfg.Host, cfg.Port),\n\t\tPassword:     cfg.Password, // no password set\n\t\tDB:           cfg.DB,       // use default DB\n\t\tPoolSize:     cfg.PoolSize,\n\t\tMinIdleConns: cfg.MinIdleConns,\n\t})\n\n\t_, err = client.Ping().Result()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc Close() {\n\t_ = client.Close()\n}\n"
  },
  {
    "path": "lesson77/bluebell/dao/redis/vote.go",
    "content": "package redis\n\nimport (\n\t\"errors\"\n\t\"math\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/go-redis/redis\"\n)\n\n// 推荐阅读\n// 基于用户投票的相关算法：http://www.ruanyifeng.com/blog/algorithm/\n\n// 本项目使用简化版的投票分数\n// 投一票就加432分   86400/200  --> 200张赞成票可以给你的帖子续一天\n\n/* 投票的几种情况：\n   direction=1时，有两种情况：\n   \t1. 之前没有投过票，现在投赞成票    --> 更新分数和投票记录  差值的绝对值：1  +432\n   \t2. 之前投反对票，现在改投赞成票    --> 更新分数和投票记录  差值的绝对值：2  +432*2\n   direction=0时，有两种情况：\n   \t1. 之前投过反对票，现在要取消投票  --> 更新分数和投票记录  差值的绝对值：1  +432\n\t2. 之前投过赞成票，现在要取消投票  --> 更新分数和投票记录  差值的绝对值：1  -432\n   direction=-1时，有两种情况：\n   \t1. 之前没有投过票，现在投反对票    --> 更新分数和投票记录  差值的绝对值：1  -432\n   \t2. 之前投赞成票，现在改投反对票    --> 更新分数和投票记录  差值的绝对值：2  -432*2\n\n   投票的限制：\n   每个贴子自发表之日起一个星期之内允许用户投票，超过一个星期就不允许再投票了。\n   \t1. 到期之后将redis中保存的赞成票数及反对票数存储到mysql表中\n   \t2. 到期之后删除那个 KeyPostVotedZSetPF\n*/\n\nconst (\n\toneWeekInSeconds = 7 * 24 * 3600\n\tscorePerVote     = 432 // 每一票值多少分\n)\n\nvar (\n\tErrVoteTimeExpire = errors.New(\"投票时间已过\")\n\tErrVoteRepeated   = errors.New(\"不允许重复投票\")\n)\n\nfunc CreatePost(postID, communityID int64) error {\n\tpipeline := client.TxPipeline()\n\t// 帖子时间\n\tpipeline.ZAdd(getRedisKey(KeyPostTimeZSet), redis.Z{\n\t\tScore:  float64(time.Now().Unix()),\n\t\tMember: postID,\n\t})\n\n\t// 帖子分数\n\tpipeline.ZAdd(getRedisKey(KeyPostScoreZSet), redis.Z{\n\t\tScore:  float64(time.Now().Unix()),\n\t\tMember: postID,\n\t})\n\t// 更新：把帖子id加到社区的set\n\tcKey := getRedisKey(KeyCommunitySetPF + strconv.Itoa(int(communityID)))\n\tpipeline.SAdd(cKey, postID)\n\t_, err := pipeline.Exec()\n\treturn err\n}\n\nfunc VoteForPost(userID, postID string, value float64) error {\n\t// 1. 判断投票限制\n\t// 去redis取帖子发布时间\n\tpostTime := client.ZScore(getRedisKey(KeyPostTimeZSet), postID).Val()\n\tif float64(time.Now().Unix())-postTime > oneWeekInSeconds {\n\t\treturn ErrVoteTimeExpire\n\t}\n\t// 2和3需要放到一个pipeline事务中操作\n\n\t// 2. 更新贴子的分数\n\t// 先查当前用户给当前帖子的投票记录\n\tov := client.ZScore(getRedisKey(KeyPostVotedZSetPF+postID), userID).Val()\n\n\t// 更新：如果这一次投票的值和之前保存的值一致，就提示不允许重复投票\n\tif value == ov {\n\t\treturn ErrVoteRepeated\n\t}\n\tvar op float64\n\tif value > ov {\n\t\top = 1\n\t} else {\n\t\top = -1\n\t}\n\tdiff := math.Abs(ov - value) // 计算两次投票的差值\n\tpipeline := client.TxPipeline()\n\tpipeline.ZIncrBy(getRedisKey(KeyPostScoreZSet), op*diff*scorePerVote, postID)\n\n\t// 3. 记录用户为该贴子投票的数据\n\tif value == 0 {\n\t\tpipeline.ZRem(getRedisKey(KeyPostVotedZSetPF+postID), userID)\n\t} else {\n\t\tpipeline.ZAdd(getRedisKey(KeyPostVotedZSetPF+postID), redis.Z{\n\t\t\tScore:  value, // 赞成票还是反对票\n\t\t\tMember: userID,\n\t\t})\n\t}\n\t_, err := pipeline.Exec()\n\treturn err\n}\n"
  },
  {
    "path": "lesson77/bluebell/docker-compose.yml",
    "content": "# yaml 配置\nversion: \"3.7\"\nservices:\n  mysql8019:\n    image: \"mysql:8.0.19\"\n    ports:\n      - \"33061:3306\"\n    command: \"--default-authentication-plugin=mysql_native_password --init-file /data/application/init.sql\"\n    environment:\n      MYSQL_ROOT_PASSWORD: \"root1234\"\n      MYSQL_DATABASE: \"bluebell\"\n      MYSQL_PASSWORD: \"root1234\"\n    volumes:\n      - ./init.sql:/data/application/init.sql\n  redis507:\n    image: \"redis:5.0.7\"\n    ports:\n      - \"26379:6379\"\n  bluebell_app:\n    build: .\n    command: sh -c \"./wait-for.sh mysql8019:3306 redis507:6379 -- ./bluebell_app ./conf/config.yaml\"\n    depends_on:\n      - mysql8019\n      - redis507\n    ports:\n      - \"8888:8084\""
  },
  {
    "path": "lesson77/bluebell/docs/docs.go",
    "content": "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n// This file was generated by swaggo/swag\n\npackage docs\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"strings\"\n\n\t\"github.com/alecthomas/template\"\n\t\"github.com/swaggo/swag\"\n)\n\nvar doc = `{\n    \"schemes\": {{ marshal .Schemes }},\n    \"swagger\": \"2.0\",\n    \"info\": {\n        \"description\": \"{{.Description}}\",\n        \"title\": \"{{.Title}}\",\n        \"contact\": {\n            \"name\": \"liwenzhou\",\n            \"url\": \"http://www.liwenzhou.com\"\n        },\n        \"license\": {},\n        \"version\": \"{{.Version}}\"\n    },\n    \"host\": \"{{.Host}}\",\n    \"basePath\": \"{{.BasePath}}\",\n    \"paths\": {\n        \"/posts2\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"ApiKeyAuth\": []\n                    }\n                ],\n                \"description\": \"可按社区按时间或分数排序查询帖子列表接口\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"帖子相关接口(api分组展示使用的)\"\n                ],\n                \"summary\": \"升级版帖子列表接口\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Bearer JWT\",\n                        \"name\": \"Authorization\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"可以为空\",\n                        \"name\": \"community_id\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"score\",\n                        \"description\": \"排序依据\",\n                        \"name\": \"order\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"example\": 1,\n                        \"description\": \"页码\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"example\": 10,\n                        \"description\": \"每页数据量\",\n                        \"name\": \"size\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"OK\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/controller._ResponsePostList\"\n                        }\n                    }\n                }\n            }\n        }\n    },\n    \"definitions\": {\n        \"controller._ResponsePostList\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"code\": {\n                    \"description\": \"业务响应状态码\",\n                    \"type\": \"integer\"\n                },\n                \"data\": {\n                    \"description\": \"数据\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/definitions/models.ApiPostDetail\"\n                    }\n                },\n                \"message\": {\n                    \"description\": \"提示信息\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"models.ApiPostDetail\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"community_id\",\n                \"content\",\n                \"title\"\n            ],\n            \"properties\": {\n                \"author_id\": {\n                    \"description\": \"作者id\",\n                    \"type\": \"integer\"\n                },\n                \"author_name\": {\n                    \"description\": \"作者\",\n                    \"type\": \"string\"\n                },\n                \"community_id\": {\n                    \"description\": \"社区id\",\n                    \"type\": \"integer\"\n                },\n                \"content\": {\n                    \"description\": \"帖子内容\",\n                    \"type\": \"string\"\n                },\n                \"create_time\": {\n                    \"type\": \"string\"\n                },\n                \"id\": {\n                    \"type\": \"integer\"\n                },\n                \"introduction\": {\n                    \"type\": \"string\"\n                },\n                \"name\": {\n                    \"type\": \"string\"\n                },\n                \"status\": {\n                    \"description\": \"帖子状态\",\n                    \"type\": \"integer\"\n                },\n                \"title\": {\n                    \"description\": \"帖子标题\",\n                    \"type\": \"string\"\n                },\n                \"vote_num\": {\n                    \"description\": \"投票数\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"models.ParamPostList\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"community_id\": {\n                    \"description\": \"可以为空\",\n                    \"type\": \"integer\"\n                },\n                \"order\": {\n                    \"description\": \"排序依据\",\n                    \"type\": \"string\",\n                    \"example\": \"score\"\n                },\n                \"page\": {\n                    \"description\": \"页码\",\n                    \"type\": \"integer\",\n                    \"example\": 1\n                },\n                \"size\": {\n                    \"description\": \"每页数据量\",\n                    \"type\": \"integer\",\n                    \"example\": 10\n                }\n            }\n        }\n    }\n}`\n\ntype swaggerInfo struct {\n\tVersion     string\n\tHost        string\n\tBasePath    string\n\tSchemes     []string\n\tTitle       string\n\tDescription string\n}\n\n// SwaggerInfo holds exported Swagger Info so clients can modify it\nvar SwaggerInfo = swaggerInfo{\n\tVersion:     \"1.0\",\n\tHost:        \"127.0.0.1:8084\",\n\tBasePath:    \"/api/v1\",\n\tSchemes:     []string{},\n\tTitle:       \"bluebell项目接口文档\",\n\tDescription: \"Go web开发进阶项目实战课程bluebell\",\n}\n\ntype s struct{}\n\nfunc (s *s) ReadDoc() string {\n\tsInfo := SwaggerInfo\n\tsInfo.Description = strings.Replace(sInfo.Description, \"\\n\", \"\\\\n\", -1)\n\n\tt, err := template.New(\"swagger_info\").Funcs(template.FuncMap{\n\t\t\"marshal\": func(v interface{}) string {\n\t\t\ta, _ := json.Marshal(v)\n\t\t\treturn string(a)\n\t\t},\n\t}).Parse(doc)\n\tif err != nil {\n\t\treturn doc\n\t}\n\n\tvar tpl bytes.Buffer\n\tif err := t.Execute(&tpl, sInfo); err != nil {\n\t\treturn doc\n\t}\n\n\treturn tpl.String()\n}\n\nfunc init() {\n\tswag.Register(swag.Name, &s{})\n}\n"
  },
  {
    "path": "lesson77/bluebell/docs/swagger.json",
    "content": "{\n    \"swagger\": \"2.0\",\n    \"info\": {\n        \"description\": \"Go web开发进阶项目实战课程bluebell\",\n        \"title\": \"bluebell项目接口文档\",\n        \"contact\": {\n            \"name\": \"liwenzhou\",\n            \"url\": \"http://www.liwenzhou.com\"\n        },\n        \"license\": {},\n        \"version\": \"1.0\"\n    },\n    \"host\": \"127.0.0.1:8084\",\n    \"basePath\": \"/api/v1\",\n    \"paths\": {\n        \"/posts2\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"ApiKeyAuth\": []\n                    }\n                ],\n                \"description\": \"可按社区按时间或分数排序查询帖子列表接口\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"帖子相关接口(api分组展示使用的)\"\n                ],\n                \"summary\": \"升级版帖子列表接口\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Bearer JWT\",\n                        \"name\": \"Authorization\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"可以为空\",\n                        \"name\": \"community_id\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"score\",\n                        \"description\": \"排序依据\",\n                        \"name\": \"order\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"example\": 1,\n                        \"description\": \"页码\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"example\": 10,\n                        \"description\": \"每页数据量\",\n                        \"name\": \"size\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"OK\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/controller._ResponsePostList\"\n                        }\n                    }\n                }\n            }\n        }\n    },\n    \"definitions\": {\n        \"controller._ResponsePostList\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"code\": {\n                    \"description\": \"业务响应状态码\",\n                    \"type\": \"integer\"\n                },\n                \"data\": {\n                    \"description\": \"数据\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/definitions/models.ApiPostDetail\"\n                    }\n                },\n                \"message\": {\n                    \"description\": \"提示信息\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"models.ApiPostDetail\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"community_id\",\n                \"content\",\n                \"title\"\n            ],\n            \"properties\": {\n                \"author_id\": {\n                    \"description\": \"作者id\",\n                    \"type\": \"integer\"\n                },\n                \"author_name\": {\n                    \"description\": \"作者\",\n                    \"type\": \"string\"\n                },\n                \"community_id\": {\n                    \"description\": \"社区id\",\n                    \"type\": \"integer\"\n                },\n                \"content\": {\n                    \"description\": \"帖子内容\",\n                    \"type\": \"string\"\n                },\n                \"create_time\": {\n                    \"type\": \"string\"\n                },\n                \"id\": {\n                    \"type\": \"integer\"\n                },\n                \"introduction\": {\n                    \"type\": \"string\"\n                },\n                \"name\": {\n                    \"type\": \"string\"\n                },\n                \"status\": {\n                    \"description\": \"帖子状态\",\n                    \"type\": \"integer\"\n                },\n                \"title\": {\n                    \"description\": \"帖子标题\",\n                    \"type\": \"string\"\n                },\n                \"vote_num\": {\n                    \"description\": \"投票数\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"models.ParamPostList\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"community_id\": {\n                    \"description\": \"可以为空\",\n                    \"type\": \"integer\"\n                },\n                \"order\": {\n                    \"description\": \"排序依据\",\n                    \"type\": \"string\",\n                    \"example\": \"score\"\n                },\n                \"page\": {\n                    \"description\": \"页码\",\n                    \"type\": \"integer\",\n                    \"example\": 1\n                },\n                \"size\": {\n                    \"description\": \"每页数据量\",\n                    \"type\": \"integer\",\n                    \"example\": 10\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "lesson77/bluebell/docs/swagger.yaml",
    "content": "basePath: /api/v1\ndefinitions:\n  controller._ResponsePostList:\n    properties:\n      code:\n        description: 业务响应状态码\n        type: integer\n      data:\n        description: 数据\n        items:\n          $ref: '#/definitions/models.ApiPostDetail'\n        type: array\n      message:\n        description: 提示信息\n        type: string\n    type: object\n  models.ApiPostDetail:\n    properties:\n      author_id:\n        description: 作者id\n        type: integer\n      author_name:\n        description: 作者\n        type: string\n      community_id:\n        description: 社区id\n        type: integer\n      content:\n        description: 帖子内容\n        type: string\n      create_time:\n        type: string\n      id:\n        type: integer\n      introduction:\n        type: string\n      name:\n        type: string\n      status:\n        description: 帖子状态\n        type: integer\n      title:\n        description: 帖子标题\n        type: string\n      vote_num:\n        description: 投票数\n        type: integer\n    required:\n    - community_id\n    - content\n    - title\n    type: object\n  models.ParamPostList:\n    properties:\n      community_id:\n        description: 可以为空\n        type: integer\n      order:\n        description: 排序依据\n        example: score\n        type: string\n      page:\n        description: 页码\n        example: 1\n        type: integer\n      size:\n        description: 每页数据量\n        example: 10\n        type: integer\n    type: object\nhost: 127.0.0.1:8084\ninfo:\n  contact:\n    name: liwenzhou\n    url: http://www.liwenzhou.com\n  description: Go web开发进阶项目实战课程bluebell\n  license: {}\n  title: bluebell项目接口文档\n  version: \"1.0\"\npaths:\n  /posts2:\n    get:\n      consumes:\n      - application/json\n      description: 可按社区按时间或分数排序查询帖子列表接口\n      parameters:\n      - description: Bearer JWT\n        in: header\n        name: Authorization\n        required: true\n        type: string\n      - description: 可以为空\n        in: query\n        name: community_id\n        type: integer\n      - description: 排序依据\n        example: score\n        in: query\n        name: order\n        type: string\n      - description: 页码\n        example: 1\n        in: query\n        name: page\n        type: integer\n      - description: 每页数据量\n        example: 10\n        in: query\n        name: size\n        type: integer\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: OK\n          schema:\n            $ref: '#/definitions/controller._ResponsePostList'\n      security:\n      - ApiKeyAuth: []\n      summary: 升级版帖子列表接口\n      tags:\n      - 帖子相关接口(api分组展示使用的)\nswagger: \"2.0\"\n"
  },
  {
    "path": "lesson77/bluebell/go.mod",
    "content": "module bluebell\n\ngo 1.14\n\nrequire (\n\tgithub.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751\n\tgithub.com/bwmarrin/snowflake v0.3.0\n\tgithub.com/dgrijalva/jwt-go v3.2.0+incompatible\n\tgithub.com/fsnotify/fsnotify v1.4.9\n\tgithub.com/gin-contrib/pprof v1.3.0\n\tgithub.com/gin-gonic/gin v1.6.3\n\tgithub.com/go-openapi/spec v0.19.9 // indirect\n\tgithub.com/go-openapi/swag v0.19.9 // indirect\n\tgithub.com/go-playground/locales v0.13.0\n\tgithub.com/go-playground/universal-translator v0.17.0\n\tgithub.com/go-playground/validator/v10 v10.3.0\n\tgithub.com/go-redis/redis v6.15.8+incompatible\n\tgithub.com/go-sql-driver/mysql v1.5.0\n\tgithub.com/jmoiron/sqlx v1.2.0\n\tgithub.com/json-iterator/go v1.1.10 // indirect\n\tgithub.com/juju/ratelimit v1.0.1\n\tgithub.com/mailru/easyjson v0.7.6 // indirect\n\tgithub.com/natefinch/lumberjack v2.0.0+incompatible\n\tgithub.com/onsi/ginkgo v1.14.0 // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/spf13/viper v1.7.0\n\tgithub.com/stretchr/testify v1.4.0\n\tgithub.com/swaggo/gin-swagger v1.2.0\n\tgithub.com/swaggo/swag v1.6.7\n\tgo.uber.org/zap v1.15.0\n\tgolang.org/x/net v0.0.0-20200904194848-62affa334b73 // indirect\n\tgolang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f // indirect\n\tgolang.org/x/tools v0.0.0-20200904185747-39188db58858 // indirect\n\tgoogle.golang.org/protobuf v1.25.0 // indirect\n\tgopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect\n)\n"
  },
  {
    "path": "lesson77/bluebell/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ngithub.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=\ngithub.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=\ngithub.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=\ngithub.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4=\ngithub.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=\ngithub.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=\ngithub.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=\ngithub.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=\ngithub.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=\ngithub.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=\ngithub.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=\ngithub.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=\ngithub.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=\ngithub.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=\ngithub.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=\ngithub.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=\ngithub.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0=\ngithub.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=\ngithub.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=\ngithub.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=\ngithub.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=\ngithub.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/gin-contrib/gzip v0.0.1 h1:ezvKOL6jH+jlzdHNE4h9h8q8uMpDQjyl0NN0Jd7jozc=\ngithub.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w=\ngithub.com/gin-contrib/pprof v1.3.0 h1:G9eK6HnbkSqDZBYbzG4wrjCsA4e+cvYAHUZw6W+W9K0=\ngithub.com/gin-contrib/pprof v1.3.0/go.mod h1:waMjT1H9b179t3CxuG1cV3DHpga6ybizwfBaM5OXaB0=\ngithub.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=\ngithub.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=\ngithub.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=\ngithub.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=\ngithub.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=\ngithub.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=\ngithub.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=\ngithub.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=\ngithub.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=\ngithub.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=\ngithub.com/go-openapi/jsonpointer v0.17.0 h1:nH6xp8XdXHx8dqveo0ZuJBluCO2qGrPbDNZ0dwoRHP0=\ngithub.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=\ngithub.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=\ngithub.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=\ngithub.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=\ngithub.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=\ngithub.com/go-openapi/jsonreference v0.19.0 h1:BqWKpV1dFd+AuiKlgtddwVIFQsuMpxfBDBHGfM2yNpk=\ngithub.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=\ngithub.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=\ngithub.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=\ngithub.com/go-openapi/jsonreference v0.19.4 h1:3Vw+rh13uq2JFNxgnMTGE1rnoieU9FmyE1gvnyylsYg=\ngithub.com/go-openapi/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=\ngithub.com/go-openapi/spec v0.19.0 h1:A4SZ6IWh3lnjH0rG0Z5lkxazMGBECtrZcbyYQi+64k4=\ngithub.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=\ngithub.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=\ngithub.com/go-openapi/spec v0.19.9 h1:9z9cbFuZJ7AcvOHKIY+f6Aevb4vObNDkTEyoMfO7rAc=\ngithub.com/go-openapi/spec v0.19.9/go.mod h1:vqK/dIdLGCosfvYsQV3WfC7N3TiZSnGY2RZKoFK7X28=\ngithub.com/go-openapi/swag v0.17.0 h1:iqrgMg7Q7SvtbWLlltPrkMs0UBJI6oTSs79JFRUi880=\ngithub.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=\ngithub.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=\ngithub.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=\ngithub.com/go-openapi/swag v0.19.9 h1:1IxuqvBUU3S2Bi4YC7tlP9SJF1gVpCvqN0T2Qof4azE=\ngithub.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY=\ngithub.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=\ngithub.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=\ngithub.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=\ngithub.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=\ngithub.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=\ngithub.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=\ngithub.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=\ngithub.com/go-playground/validator/v10 v10.3.0 h1:nZU+7q+yJoFmwvNgv/LnPUkwPal62+b2xXj0AU1Es7o=\ngithub.com/go-playground/validator/v10 v10.3.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=\ngithub.com/go-redis/redis v6.15.8+incompatible h1:BKZuG6mCnRj5AOaWJXoCgf6rqTYnYJLe4en2hxT7r9o=\ngithub.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=\ngithub.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=\ngithub.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=\ngithub.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=\ngithub.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=\ngithub.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=\ngithub.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=\ngithub.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=\ngithub.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=\ngithub.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=\ngithub.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=\ngithub.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=\ngithub.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=\ngithub.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=\ngithub.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=\ngithub.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=\ngithub.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=\ngithub.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=\ngithub.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/juju/ratelimit v1.0.1 h1:+7AIFJVQ0EQgq/K9+0Krm7m530Du7tIz0METWzN0RgY=\ngithub.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=\ngithub.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=\ngithub.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=\ngithub.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=\ngithub.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=\ngithub.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=\ngithub.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=\ngithub.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=\ngithub.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=\ngithub.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=\ngithub.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=\ngithub.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=\ngithub.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=\ngithub.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=\ngithub.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=\ngithub.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=\ngithub.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=\ngithub.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=\ngithub.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=\ngithub.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=\ngithub.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=\ngithub.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=\ngithub.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=\ngithub.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=\ngithub.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=\ngithub.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=\ngithub.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=\ngithub.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=\ngithub.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=\ngithub.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=\ngithub.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=\ngithub.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=\ngithub.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=\ngithub.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=\ngithub.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=\ngithub.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=\ngithub.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=\ngithub.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=\ngithub.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=\ngithub.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=\ngithub.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=\ngithub.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=\ngithub.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=\ngithub.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=\ngithub.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=\ngithub.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=\ngithub.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=\ngithub.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=\ngithub.com/swaggo/files v0.0.0-20190704085106-630677cd5c14 h1:PyYN9JH5jY9j6av01SpfRMb+1DWg/i3MbGOKPxJ2wjM=\ngithub.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E=\ngithub.com/swaggo/gin-swagger v1.2.0 h1:YskZXEiv51fjOMTsXrOetAjrMDfFaXD79PEoQBOe2W0=\ngithub.com/swaggo/gin-swagger v1.2.0/go.mod h1:qlH2+W7zXGZkczuL+r2nEBR2JTT+/lX05Nn6vPhc7OI=\ngithub.com/swaggo/swag v1.5.1 h1:2Agm8I4K5qb00620mHq0VJ05/KT4FtmALPIcQR9lEZM=\ngithub.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y=\ngithub.com/swaggo/swag v1.6.7 h1:e8GC2xDllJZr3omJkm9YfmK0Y56+rMO3cg0JBKNz09s=\ngithub.com/swaggo/swag v1.6.7/go.mod h1:xDhTyuFIujYiN3DKWC/H/83xcfHp+UE/IzWWampG7Zc=\ngithub.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=\ngithub.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=\ngithub.com/ugorji/go v1.1.5-pre/go.mod h1:FwP/aQVg39TXzItUBMwnWp9T9gPQnXw4Poh4/oBQZ/0=\ngithub.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=\ngithub.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=\ngithub.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=\ngithub.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI=\ngithub.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=\ngithub.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=\ngithub.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=\ngithub.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=\ngithub.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k=\ngithub.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=\ngithub.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngo.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=\ngo.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=\ngo.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=\ngo.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=\ngo.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngo.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=\ngo.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA=\ngolang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f h1:Fqb3ao1hUmOR3GkUOg/Y+BadLwykBIzs5q8Ez2SbHyc=\ngolang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc h1:NCy3Ohtk6Iny5V/reW2Ktypo4zIpWBdRJ1uFMjBxdg8=\ngolang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20200904185747-39188db58858 h1:xLt+iB5ksWcZVxqc+g9K41ZHy+6MKWfXCDsjSThnsPA=\ngolang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=\ngopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=\ngopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=\ngopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=\ngopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=\ngopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=\ngopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\n"
  },
  {
    "path": "lesson77/bluebell/init.sql",
    "content": "-- create the databases\nCREATE DATABASE IF NOT EXISTS bluebell;"
  },
  {
    "path": "lesson77/bluebell/logger/logger.go",
    "content": "package logger\n\nimport (\n\t\"bluebell/setting\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"os\"\n\t\"runtime/debug\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/natefinch/lumberjack\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\nvar lg *zap.Logger\n\n// Init 初始化lg\nfunc Init(cfg *setting.LogConfig, mode string) (err error) {\n\twriteSyncer := getLogWriter(cfg.Filename, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge)\n\tencoder := getEncoder()\n\tvar l = new(zapcore.Level)\n\terr = l.UnmarshalText([]byte(cfg.Level))\n\tif err != nil {\n\t\treturn\n\t}\n\tvar core zapcore.Core\n\tif mode == \"dev\" {\n\t\t// 进入开发模式，日志输出到终端\n\t\tconsoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())\n\t\tcore = zapcore.NewTee(\n\t\t\tzapcore.NewCore(encoder, writeSyncer, l),\n\t\t\tzapcore.NewCore(consoleEncoder, zapcore.Lock(os.Stdout), zapcore.DebugLevel),\n\t\t)\n\t} else {\n\t\tcore = zapcore.NewCore(encoder, writeSyncer, l)\n\t}\n\n\tlg = zap.New(core, zap.AddCaller())\n\n\tzap.ReplaceGlobals(lg)\n\tzap.L().Info(\"init logger success\")\n\treturn\n}\n\nfunc getEncoder() zapcore.Encoder {\n\tencoderConfig := zap.NewProductionEncoderConfig()\n\tencoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder\n\tencoderConfig.TimeKey = \"time\"\n\tencoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder\n\tencoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder\n\tencoderConfig.EncodeCaller = zapcore.ShortCallerEncoder\n\treturn zapcore.NewJSONEncoder(encoderConfig)\n}\n\nfunc getLogWriter(filename string, maxSize, maxBackup, maxAge int) zapcore.WriteSyncer {\n\tlumberJackLogger := &lumberjack.Logger{\n\t\tFilename:   filename,\n\t\tMaxSize:    maxSize,\n\t\tMaxBackups: maxBackup,\n\t\tMaxAge:     maxAge,\n\t}\n\treturn zapcore.AddSync(lumberJackLogger)\n}\n\n// GinLogger 接收gin框架默认的日志\nfunc GinLogger() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tstart := time.Now()\n\t\tpath := c.Request.URL.Path\n\t\tquery := c.Request.URL.RawQuery\n\t\tc.Next()\n\n\t\tcost := time.Since(start)\n\t\tlg.Info(path,\n\t\t\tzap.Int(\"status\", c.Writer.Status()),\n\t\t\tzap.String(\"method\", c.Request.Method),\n\t\t\tzap.String(\"path\", path),\n\t\t\tzap.String(\"query\", query),\n\t\t\tzap.String(\"ip\", c.ClientIP()),\n\t\t\tzap.String(\"user-agent\", c.Request.UserAgent()),\n\t\t\tzap.String(\"errors\", c.Errors.ByType(gin.ErrorTypePrivate).String()),\n\t\t\tzap.Duration(\"cost\", cost),\n\t\t)\n\t}\n}\n\n// GinRecovery recover掉项目可能出现的panic，并使用zap记录相关日志\nfunc GinRecovery(stack bool) gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tdefer func() {\n\t\t\tif err := recover(); err != nil {\n\t\t\t\t// Check for a broken connection, as it is not really a\n\t\t\t\t// condition that warrants a panic stack trace.\n\t\t\t\tvar brokenPipe bool\n\t\t\t\tif ne, ok := err.(*net.OpError); ok {\n\t\t\t\t\tif se, ok := ne.Err.(*os.SyscallError); ok {\n\t\t\t\t\t\tif strings.Contains(strings.ToLower(se.Error()), \"broken pipe\") || strings.Contains(strings.ToLower(se.Error()), \"connection reset by peer\") {\n\t\t\t\t\t\t\tbrokenPipe = true\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\thttpRequest, _ := httputil.DumpRequest(c.Request, false)\n\t\t\t\tif brokenPipe {\n\t\t\t\t\tlg.Error(c.Request.URL.Path,\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t)\n\t\t\t\t\t// If the connection is dead, we can't write a status to it.\n\t\t\t\t\tc.Error(err.(error)) // nolint: errcheck\n\t\t\t\t\tc.Abort()\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif stack {\n\t\t\t\t\tlg.Error(\"[Recovery from panic]\",\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t\tzap.String(\"stack\", string(debug.Stack())),\n\t\t\t\t\t)\n\t\t\t\t} else {\n\t\t\t\t\tlg.Error(\"[Recovery from panic]\",\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tc.AbortWithStatus(http.StatusInternalServerError)\n\t\t\t}\n\t\t}()\n\t\tc.Next()\n\t}\n}\n"
  },
  {
    "path": "lesson77/bluebell/logic/community.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/models\"\n)\n\nfunc GetCommunityList() ([]*models.Community, error) {\n\t// 查数据库 查找到所有的community 并返回\n\treturn mysql.GetCommunityList()\n}\n\nfunc GetCommunityDetail(id int64) (*models.CommunityDetail, error) {\n\treturn mysql.GetCommunityDetailByID(id)\n}\n"
  },
  {
    "path": "lesson77/bluebell/logic/post.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/dao/redis\"\n\t\"bluebell/models\"\n\t\"bluebell/pkg/snowflake\"\n\n\t\"go.uber.org/zap\"\n)\n\nfunc CreatePost(p *models.Post) (err error) {\n\t// 1. 生成post id\n\tp.ID = snowflake.GenID()\n\t// 2. 保存到数据库\n\terr = mysql.CreatePost(p)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = redis.CreatePost(p.ID, p.CommunityID)\n\treturn\n\t// 3. 返回\n}\n\n// GetPostById 根据帖子id查询帖子详情数据\nfunc GetPostById(pid int64) (data *models.ApiPostDetail, err error) {\n\t// 查询并组合我们接口想用的数据\n\tpost, err := mysql.GetPostById(pid)\n\tif err != nil {\n\t\tzap.L().Error(\"mysql.GetPostById(pid) failed\",\n\t\t\tzap.Int64(\"pid\", pid),\n\t\t\tzap.Error(err))\n\t\treturn\n\t}\n\t// 根据作者id查询作者信息\n\tuser, err := mysql.GetUserById(post.AuthorID)\n\tif err != nil {\n\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\tzap.Int64(\"author_id\", post.AuthorID),\n\t\t\tzap.Error(err))\n\t\treturn\n\t}\n\t// 根据社区id查询社区详细信息\n\tcommunity, err := mysql.GetCommunityDetailByID(post.CommunityID)\n\tif err != nil {\n\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\tzap.Int64(\"community_id\", post.CommunityID),\n\t\t\tzap.Error(err))\n\t\treturn\n\t}\n\t// 接口数据拼接\n\tdata = &models.ApiPostDetail{\n\t\tAuthorName:      user.Username,\n\t\tPost:            post,\n\t\tCommunityDetail: community,\n\t}\n\treturn\n}\n\n// GetPostList 获取帖子列表\nfunc GetPostList(page, size int64) (data []*models.ApiPostDetail, err error) {\n\tposts, err := mysql.GetPostList(page, size)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdata = make([]*models.ApiPostDetail, 0, len(posts))\n\n\tfor _, post := range posts {\n\t\t// 根据作者id查询作者信息\n\t\tuser, err := mysql.GetUserById(post.AuthorID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"author_id\", post.AuthorID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\t// 根据社区id查询社区详细信息\n\t\tcommunity, err := mysql.GetCommunityDetailByID(post.CommunityID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"community_id\", post.CommunityID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\tpostDetail := &models.ApiPostDetail{\n\t\t\tAuthorName:      user.Username,\n\t\t\tPost:            post,\n\t\t\tCommunityDetail: community,\n\t\t}\n\t\tdata = append(data, postDetail)\n\t}\n\treturn\n}\n\nfunc GetPostList2(p *models.ParamPostList) (data []*models.ApiPostDetail, err error) {\n\t// 2. 去redis查询id列表\n\tids, err := redis.GetPostIDsInOrder(p)\n\tif err != nil {\n\t\treturn\n\t}\n\tif len(ids) == 0 {\n\t\tzap.L().Warn(\"redis.GetPostIDsInOrder(p) return 0 data\")\n\t\treturn\n\t}\n\tzap.L().Debug(\"GetPostList2\", zap.Any(\"ids\", ids))\n\t// 3. 根据id去MySQL数据库查询帖子详细信息\n\t// 返回的数据还要按照我给定的id的顺序返回\n\tposts, err := mysql.GetPostListByIDs(ids)\n\tif err != nil {\n\t\treturn\n\t}\n\tzap.L().Debug(\"GetPostList2\", zap.Any(\"posts\", posts))\n\t// 提前查询好每篇帖子的投票数\n\tvoteData, err := redis.GetPostVoteData(ids)\n\tif err != nil {\n\t\treturn\n\t}\n\n\t// 将帖子的作者及分区信息查询出来填充到帖子中\n\tfor idx, post := range posts {\n\t\t// 根据作者id查询作者信息\n\t\tuser, err := mysql.GetUserById(post.AuthorID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"author_id\", post.AuthorID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\t// 根据社区id查询社区详细信息\n\t\tcommunity, err := mysql.GetCommunityDetailByID(post.CommunityID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"community_id\", post.CommunityID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\tpostDetail := &models.ApiPostDetail{\n\t\t\tAuthorName:      user.Username,\n\t\t\tVoteNum:         voteData[idx],\n\t\t\tPost:            post,\n\t\t\tCommunityDetail: community,\n\t\t}\n\t\tdata = append(data, postDetail)\n\t}\n\treturn\n\n}\n\nfunc GetCommunityPostList(p *models.ParamPostList) (data []*models.ApiPostDetail, err error) {\n\t// 2. 去redis查询id列表\n\tids, err := redis.GetCommunityPostIDsInOrder(p)\n\tif err != nil {\n\t\treturn\n\t}\n\tif len(ids) == 0 {\n\t\tzap.L().Warn(\"redis.GetPostIDsInOrder(p) return 0 data\")\n\t\treturn\n\t}\n\tzap.L().Debug(\"GetCommunityPostIDsInOrder\", zap.Any(\"ids\", ids))\n\t// 3. 根据id去MySQL数据库查询帖子详细信息\n\t// 返回的数据还要按照我给定的id的顺序返回\n\tposts, err := mysql.GetPostListByIDs(ids)\n\tif err != nil {\n\t\treturn\n\t}\n\tzap.L().Debug(\"GetPostList2\", zap.Any(\"posts\", posts))\n\t// 提前查询好每篇帖子的投票数\n\tvoteData, err := redis.GetPostVoteData(ids)\n\tif err != nil {\n\t\treturn\n\t}\n\n\t// 将帖子的作者及分区信息查询出来填充到帖子中\n\tfor idx, post := range posts {\n\t\t// 根据作者id查询作者信息\n\t\tuser, err := mysql.GetUserById(post.AuthorID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"author_id\", post.AuthorID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\t// 根据社区id查询社区详细信息\n\t\tcommunity, err := mysql.GetCommunityDetailByID(post.CommunityID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"community_id\", post.CommunityID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\tpostDetail := &models.ApiPostDetail{\n\t\t\tAuthorName:      user.Username,\n\t\t\tVoteNum:         voteData[idx],\n\t\t\tPost:            post,\n\t\t\tCommunityDetail: community,\n\t\t}\n\t\tdata = append(data, postDetail)\n\t}\n\treturn\n}\n\n// GetPostListNew  将两个查询帖子列表逻辑合二为一的函数\nfunc GetPostListNew(p *models.ParamPostList) (data []*models.ApiPostDetail, err error) {\n\t// 根据请求参数的不同，执行不同的逻辑。\n\tif p.CommunityID == 0 {\n\t\t// 查所有\n\t\tdata, err = GetPostList2(p)\n\t} else {\n\t\t// 根据社区id查询\n\t\tdata, err = GetCommunityPostList(p)\n\t}\n\tif err != nil {\n\t\tzap.L().Error(\"GetPostListNew failed\", zap.Error(err))\n\t\treturn nil, err\n\t}\n\treturn\n}\n"
  },
  {
    "path": "lesson77/bluebell/logic/user.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/models\"\n\t\"bluebell/pkg/jwt\"\n\t\"bluebell/pkg/snowflake\"\n)\n\n// 存放业务逻辑的代码\n\nfunc SignUp(p *models.ParamSignUp) (err error) {\n\t// 1.判断用户存不存在\n\tif err := mysql.CheckUserExist(p.Username); err != nil {\n\t\treturn err\n\t}\n\t// 2.生成UID\n\tuserID := snowflake.GenID()\n\t// 构造一个User实例\n\tuser := &models.User{\n\t\tUserID:   userID,\n\t\tUsername: p.Username,\n\t\tPassword: p.Password,\n\t}\n\t// 3.保存进数据库\n\treturn mysql.InsertUser(user)\n}\n\nfunc Login(p *models.ParamLogin) (user *models.User, err error) {\n\tuser = &models.User{\n\t\tUsername: p.Username,\n\t\tPassword: p.Password,\n\t}\n\t// 传递的是指针，就能拿到user.UserID\n\tif err := mysql.Login(user); err != nil {\n\t\treturn nil, err\n\t}\n\t// 生成JWT\n\ttoken, err := jwt.GenToken(user.UserID, user.Username)\n\tif err != nil {\n\t\treturn\n\t}\n\tuser.Token = token\n\treturn\n}\n"
  },
  {
    "path": "lesson77/bluebell/logic/vote.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/redis\"\n\t\"bluebell/models\"\n\t\"strconv\"\n\n\t\"go.uber.org/zap\"\n)\n\n// 推荐阅读\n// 基于用户投票的相关算法：http://www.ruanyifeng.com/blog/algorithm/\n\n// 本项目使用简化版的投票分数\n// 投一票就加432分   86400/200  --> 200张赞成票可以给你的帖子续一天\n\n/* 投票的几种情况：\ndirection=1时，有两种情况：\n\t1. 之前没有投过票，现在投赞成票    --> 更新分数和投票记录\n\t2. 之前投反对票，现在改投赞成票    --> 更新分数和投票记录\ndirection=0时，有两种情况：\n\t1. 之前投过赞成票，现在要取消投票  --> 更新分数和投票记录\n\t2. 之前投过反对票，现在要取消投票  --> 更新分数和投票记录\ndirection=-1时，有两种情况：\n\t1. 之前没有投过票，现在投反对票    --> 更新分数和投票记录\n\t2. 之前投赞成票，现在改投反对票    --> 更新分数和投票记录\n\n投票的限制：\n每个贴子自发表之日起一个星期之内允许用户投票，超过一个星期就不允许再投票了。\n\t1. 到期之后将redis中保存的赞成票数及反对票数存储到mysql表中\n\t2. 到期之后删除那个 KeyPostVotedZSetPF\n*/\n\n// VoteForPost 为帖子投票的函数\nfunc VoteForPost(userID int64, p *models.ParamVoteData) error {\n\tzap.L().Debug(\"VoteForPost\",\n\t\tzap.Int64(\"userID\", userID),\n\t\tzap.String(\"postID\", p.PostID),\n\t\tzap.Int8(\"direction\", p.Direction))\n\treturn redis.VoteForPost(strconv.Itoa(int(userID)), p.PostID, float64(p.Direction))\n}\n"
  },
  {
    "path": "lesson77/bluebell/main.go",
    "content": "package main\n\nimport (\n\t\"bluebell/controller\"\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/dao/redis\"\n\t\"bluebell/logger\"\n\t\"bluebell/pkg/snowflake\"\n\t\"bluebell/router\"\n\t\"bluebell/setting\"\n\t\"fmt\"\n\t\"os\"\n)\n\n// @title bluebell项目接口文档\n// @version 1.0\n// @description Go web开发进阶项目实战课程bluebell\n\n// @contact.name liwenzhou\n// @contact.url http://www.liwenzhou.com\n\n// @host 127.0.0.1:8084\n// @BasePath /api/v1\nfunc main() {\n\tif len(os.Args) < 2 {\n\t\tfmt.Println(\"need config file.eg: bluebell config.yaml\")\n\t\treturn\n\t}\n\t// 加载配置\n\tif err := setting.Init(os.Args[1]); err != nil {\n\t\tfmt.Printf(\"load config failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tif err := logger.Init(setting.Conf.LogConfig, setting.Conf.Mode); err != nil {\n\t\tfmt.Printf(\"init logger failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tif err := mysql.Init(setting.Conf.MySQLConfig); err != nil {\n\t\tfmt.Printf(\"init mysql failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer mysql.Close() // 程序退出关闭数据库连接\n\tif err := redis.Init(setting.Conf.RedisConfig); err != nil {\n\t\tfmt.Printf(\"init redis failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer redis.Close()\n\n\tif err := snowflake.Init(setting.Conf.StartTime, setting.Conf.MachineID); err != nil {\n\t\tfmt.Printf(\"init snowflake failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\t// 初始化gin框架内置的校验器使用的翻译器\n\tif err := controller.InitTrans(\"zh\"); err != nil {\n\t\tfmt.Printf(\"init validator trans failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\t// 注册路由\n\tr := router.SetupRouter(setting.Conf.Mode)\n\terr := r.Run(fmt.Sprintf(\":%d\", setting.Conf.Port))\n\tif err != nil {\n\t\tfmt.Printf(\"run server failed, err:%v\\n\", err)\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "lesson77/bluebell/middlewares/auth.go",
    "content": "package middlewares\n\nimport (\n\t\"bluebell/controller\"\n\t\"bluebell/pkg/jwt\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// JWTAuthMiddleware 基于JWT的认证中间件\nfunc JWTAuthMiddleware() func(c *gin.Context) {\n\treturn func(c *gin.Context) {\n\t\t// 客户端携带Token有三种方式 1.放在请求头 2.放在请求体 3.放在URI\n\t\t// 这里假设Token放在Header的Authorization中，并使用Bearer开头\n\t\t// Authorization: Bearer xxxxxxx.xxx.xxx  / X-TOKEN: xxx.xxx.xx\n\t\t// 这里的具体实现方式要依据你的实际业务情况决定\n\t\tauthHeader := c.Request.Header.Get(\"Authorization\")\n\t\tif authHeader == \"\" {\n\t\t\tcontroller.ResponseError(c, controller.CodeNeedLogin)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// 按空格分割\n\t\tparts := strings.SplitN(authHeader, \" \", 2)\n\t\tif !(len(parts) == 2 && parts[0] == \"Bearer\") {\n\t\t\tcontroller.ResponseError(c, controller.CodeInvalidToken)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// parts[1]是获取到的tokenString，我们使用之前定义好的解析JWT的函数来解析它\n\t\tmc, err := jwt.ParseToken(parts[1])\n\t\tif err != nil {\n\t\t\tcontroller.ResponseError(c, controller.CodeInvalidToken)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// 将当前请求的userID信息保存到请求的上下文c上\n\t\tc.Set(controller.CtxUserIDKey, mc.UserID)\n\n\t\tc.Next() // 后续的处理请求的函数中 可以用过c.Get(CtxUserIDKey) 来获取当前请求的用户信息\n\t}\n}\n"
  },
  {
    "path": "lesson77/bluebell/middlewares/ratelimit.go",
    "content": "package middlewares\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/juju/ratelimit\"\n)\n\nfunc RateLimitMiddleware(fillInterval time.Duration, cap int64) func(c *gin.Context) {\n\tbucket := ratelimit.NewBucket(fillInterval, cap)\n\treturn func(c *gin.Context) {\n\t\t// 如果取不到令牌就返回响应\n\t\t//if bucket.Take(1) > 0 {\n\t\tif bucket.TakeAvailable(1) != 1 {\n\t\t\tc.String(http.StatusOK, \"rate limit...\")\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// 取到令牌就放行\n\t\tc.Next()\n\t}\n}\n"
  },
  {
    "path": "lesson77/bluebell/models/community.go",
    "content": "package models\n\nimport \"time\"\n\ntype Community struct {\n\tID   int64  `json:\"id\" db:\"community_id\"`\n\tName string `json:\"name\" db:\"community_name\"`\n}\n\ntype CommunityDetail struct {\n\tID           int64     `json:\"id\" db:\"community_id\"`\n\tName         string    `json:\"name\" db:\"community_name\"`\n\tIntroduction string    `json:\"introduction,omitempty\" db:\"introduction\"`\n\tCreateTime   time.Time `json:\"create_time\" db:\"create_time\"`\n}\n"
  },
  {
    "path": "lesson77/bluebell/models/create_table.sql",
    "content": "--\nCREATE TABLE `user` (\n    `id` bigint(20) NOT NULL AUTO_INCREMENT,\n    `user_id` bigint(20) NOT NULL,\n    `username` varchar(64) COLLATE utf8mb4_general_ci NOT NULL,\n    `password` varchar(64) COLLATE utf8mb4_general_ci NOT NULL,\n    `email` varchar(64) COLLATE utf8mb4_general_ci,\n    `gender` tinyint(4) NOT NULL DEFAULT '0',\n    `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n    `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    PRIMARY KEY (`id`),\n    UNIQUE KEY `idx_username` (`username`) USING BTREE,\n    UNIQUE KEY `idx_user_id` (`user_id`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\n\n\nDROP TABLE IF EXISTS `community`;\nCREATE TABLE `community` (\n     `id` int(11) NOT NULL AUTO_INCREMENT,\n     `community_id` int(10) unsigned NOT NULL,\n     `community_name` varchar(128) COLLATE utf8mb4_general_ci NOT NULL,\n     `introduction` varchar(256) COLLATE utf8mb4_general_ci NOT NULL,\n     `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n     `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n     PRIMARY KEY (`id`),\n     UNIQUE KEY `idx_community_id` (`community_id`),\n     UNIQUE KEY `idx_community_name` (`community_name`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\n\n\nINSERT INTO `community` VALUES ('1', '1', 'Go', 'Golang', '2016-11-01 08:10:10', '2016-11-01 08:10:10');\nINSERT INTO `community` VALUES ('2', '2', 'leetcode', '刷题刷题刷题', '2020-01-01 08:00:00', '2020-01-01 08:00:00');\nINSERT INTO `community` VALUES ('3', '3', 'CS:GO', 'Rush B。。。', '2018-08-07 08:30:00', '2018-08-07 08:30:00');\nINSERT INTO `community` VALUES ('4', '4', 'LOL', '欢迎来到英雄联盟!', '2016-01-01 08:00:00', '2016-01-01 08:00:00');\n\nDROP TABLE IF EXISTS `post`;\nCREATE TABLE `post` (\n    `id` bigint(20) NOT NULL AUTO_INCREMENT,\n    `post_id` bigint(20) NOT NULL COMMENT '帖子id',\n    `title` varchar(128) COLLATE utf8mb4_general_ci NOT NULL COMMENT '标题',\n    `content` varchar(8192) COLLATE utf8mb4_general_ci NOT NULL COMMENT '内容',\n    `author_id` bigint(20) NOT NULL COMMENT '作者的用户id',\n    `community_id` bigint(20) NOT NULL COMMENT '所属社区',\n    `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '帖子状态',\n    `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n    `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n    PRIMARY KEY (`id`),\n    UNIQUE KEY `idx_post_id` (`post_id`),\n    KEY `idx_author_id` (`author_id`),\n    KEY `idx_community_id` (`community_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;"
  },
  {
    "path": "lesson77/bluebell/models/params.go",
    "content": "package models\n\n// 定义请求的参数结构体\n\nconst (\n\tOrderTime  = \"time\"\n\tOrderScore = \"score\"\n)\n\n// ParamSignUp 注册请求参数\ntype ParamSignUp struct {\n\tUsername   string `json:\"username\" binding:\"required\"`\n\tPassword   string `json:\"password\" binding:\"required\"`\n\tRePassword string `json:\"re_password\" binding:\"required,eqfield=Password\"`\n}\n\n// ParamLogin 登录请求参数\ntype ParamLogin struct {\n\tUsername string `json:\"username\" binding:\"required\"`\n\tPassword string `json:\"password\" binding:\"required\"`\n}\n\n// ParamVoteData 投票数据\ntype ParamVoteData struct {\n\t// UserID 从请求中获取当前的用户\n\tPostID    string `json:\"post_id\" binding:\"required\"`               // 贴子id\n\tDirection int8   `json:\"direction,string\" binding:\"oneof=1 0 -1\" ` // 赞成票(1)还是反对票(-1)取消投票(0)\n}\n\n// ParamPostList 获取帖子列表query string参数\ntype ParamPostList struct {\n\tCommunityID int64  `json:\"community_id\" form:\"community_id\"`   // 可以为空\n\tPage        int64  `json:\"page\" form:\"page\" example:\"1\"`       // 页码\n\tSize        int64  `json:\"size\" form:\"size\" example:\"10\"`      // 每页数据量\n\tOrder       string `json:\"order\" form:\"order\" example:\"score\"` // 排序依据\n}\n"
  },
  {
    "path": "lesson77/bluebell/models/post.go",
    "content": "package models\n\nimport \"time\"\n\n// 内存对齐概念\n\ntype Post struct {\n\tID          int64     `json:\"id,string\" db:\"post_id\"`                            // 帖子id\n\tAuthorID    int64     `json:\"author_id\" db:\"author_id\"`                          // 作者id\n\tCommunityID int64     `json:\"community_id\" db:\"community_id\" binding:\"required\"` // 社区id\n\tStatus      int32     `json:\"status\" db:\"status\"`                                // 帖子状态\n\tTitle       string    `json:\"title\" db:\"title\" binding:\"required\"`               // 帖子标题\n\tContent     string    `json:\"content\" db:\"content\" binding:\"required\"`           // 帖子内容\n\tCreateTime  time.Time `json:\"create_time\" db:\"create_time\"`                      // 帖子创建时间\n}\n\n// ApiPostDetail 帖子详情接口的结构体\ntype ApiPostDetail struct {\n\tAuthorName       string             `json:\"author_name\"` // 作者\n\tVoteNum          int64              `json:\"vote_num\"`    // 投票数\n\t*Post                               // 嵌入帖子结构体\n\t*CommunityDetail `json:\"community\"` // 嵌入社区信息\n}\n"
  },
  {
    "path": "lesson77/bluebell/models/struct_test.go",
    "content": "package models\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"unsafe\"\n)\n\n// Go 内存对齐\n\ntype s1 struct {\n\ta int8   // 1\n\tb string // 3\n\tc int8   // 1\n}\n\ntype s2 struct {\n\ta int8\n\tc int8   // 1\n\tb string // 2\n}\n\nfunc TestStruct(t *testing.T) {\n\tv1 := s1{\n\t\ta: 1,\n\t\tb: \"七米\",\n\t\tc: 2,\n\t}\n\n\tv2 := s2{\n\t\ta: 1,\n\t\tb: \"七米\",\n\t\tc: 2,\n\t}\n\n\tfmt.Println(unsafe.Sizeof(v1), unsafe.Sizeof(v2))\n}\n"
  },
  {
    "path": "lesson77/bluebell/models/user.go",
    "content": "package models\n\ntype User struct {\n\tUserID   int64  `db:\"user_id\"`\n\tUsername string `db:\"username\"`\n\tPassword string `db:\"password\"`\n\tToken    string\n}\n"
  },
  {
    "path": "lesson77/bluebell/pkg/jwt/jwt.go",
    "content": "package jwt\n\nimport (\n\t\"errors\"\n\t\"time\"\n\n\t\"github.com/spf13/viper\"\n\n\t\"github.com/dgrijalva/jwt-go\"\n)\n\nvar mySecret = []byte(\"夏天夏天悄悄过去\")\n\n// MyClaims 自定义声明结构体并内嵌jwt.StandardClaims\n// jwt包自带的jwt.StandardClaims只包含了官方字段\n// 我们这里需要额外记录一个username字段，所以要自定义结构体\n// 如果想要保存更多信息，都可以添加到这个结构体中\ntype MyClaims struct {\n\tUserID   int64  `json:\"user_id\"`\n\tUsername string `json:\"username\"`\n\tjwt.StandardClaims\n}\n\n// GenToken 生成JWT\nfunc GenToken(userID int64, username string) (string, error) {\n\t// 创建一个我们自己的声明的数据\n\tc := MyClaims{\n\t\tuserID,\n\t\t\"username\", // 自定义字段\n\t\tjwt.StandardClaims{\n\t\t\tExpiresAt: time.Now().Add(\n\t\t\t\ttime.Duration(viper.GetInt(\"auth.jwt_expire\")) * time.Hour).Unix(), // 过期时间\n\t\t\tIssuer: \"bluebell\", // 签发人\n\t\t},\n\t}\n\t// 使用指定的签名方法创建签名对象\n\ttoken := jwt.NewWithClaims(jwt.SigningMethodHS256, c)\n\t// 使用指定的secret签名并获得完整的编码后的字符串token\n\treturn token.SignedString(mySecret)\n}\n\n// ParseToken 解析JWT\nfunc ParseToken(tokenString string) (*MyClaims, error) {\n\t// 解析token\n\tvar mc = new(MyClaims)\n\ttoken, err := jwt.ParseWithClaims(tokenString, mc, func(token *jwt.Token) (i interface{}, err error) {\n\t\treturn mySecret, nil\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif token.Valid { // 校验token\n\t\treturn mc, nil\n\t}\n\treturn nil, errors.New(\"invalid token\")\n}\n"
  },
  {
    "path": "lesson77/bluebell/pkg/snowflake/snowflake.go",
    "content": "package snowflake\n\nimport (\n\t\"time\"\n\n\tsf \"github.com/bwmarrin/snowflake\"\n)\n\nvar node *sf.Node\n\nfunc Init(startTime string, machineID int64) (err error) {\n\tvar st time.Time\n\tst, err = time.Parse(\"2006-01-02\", startTime)\n\tif err != nil {\n\t\treturn\n\t}\n\tsf.Epoch = st.UnixNano() / 1000000\n\tnode, err = sf.NewNode(machineID)\n\treturn\n}\nfunc GenID() int64 {\n\treturn node.Generate().Int64()\n}\n"
  },
  {
    "path": "lesson77/bluebell/router/route.go",
    "content": "package router\n\nimport (\n\t\"bluebell/controller\"\n\t\"bluebell/logger\"\n\t\"bluebell/middlewares\"\n\t\"net/http\"\n\n\tginSwagger \"github.com/swaggo/gin-swagger\"\n\t\"github.com/swaggo/gin-swagger/swaggerFiles\"\n\n\t_ \"bluebell/docs\"\n\n\t\"github.com/gin-contrib/pprof\"\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc SetupRouter(mode string) *gin.Engine {\n\tif mode == gin.ReleaseMode {\n\t\tgin.SetMode(gin.ReleaseMode) // gin设置成发布模式\n\t}\n\tr := gin.New()\n\t//r.Use(logger.GinLogger(), logger.GinRecovery(true), middlewares.RateLimitMiddleware(2*time.Second, 1))\n\tr.Use(logger.GinLogger(), logger.GinRecovery(true))\n\n\tr.LoadHTMLFiles(\"./templates/index.html\")\n\tr.Static(\"/static\", \"./static\")\n\tr.GET(\"/\", func(c *gin.Context) {\n\t\tc.HTML(http.StatusOK, \"index.html\", nil)\n\t})\n\n\tr.GET(\"/ping\", func(c *gin.Context) {\n\t\tc.String(http.StatusOK, \"pong\")\n\t})\n\n\tr.GET(\"/swagger/*any\", ginSwagger.WrapHandler(swaggerFiles.Handler))\n\n\tv1 := r.Group(\"/api/v1\")\n\n\t// 注册\n\tv1.POST(\"/signup\", controller.SignUpHandler)\n\t// 登录\n\tv1.POST(\"/login\", controller.LoginHandler)\n\n\tv1.Use(middlewares.JWTAuthMiddleware()) // 应用JWT认证中间件\n\n\t// 根据时间或分数获取帖子列表\n\tv1.GET(\"/posts2\", controller.GetPostListHandler2)\n\tv1.GET(\"/posts\", controller.GetPostListHandler)\n\t{\n\t\tv1.GET(\"/community\", controller.CommunityHandler)\n\t\tv1.GET(\"/community/:id\", controller.CommunityDetailHandler)\n\n\t\tv1.POST(\"/post\", controller.CreatePostHandler)\n\t\tv1.GET(\"/post/:id\", controller.GetPostDetailHandler)\n\n\t\t// 投票\n\t\tv1.POST(\"/vote\", controller.PostVoteController)\n\t}\n\n\tpprof.Register(r) // 注册pprof相关路由\n\n\tr.NoRoute(func(c *gin.Context) {\n\t\tc.JSON(http.StatusOK, gin.H{\n\t\t\t\"msg\": \"404\",\n\t\t})\n\t})\n\treturn r\n}\n"
  },
  {
    "path": "lesson77/bluebell/setting/setting.go",
    "content": "package setting\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\t\"github.com/spf13/viper\"\n)\n\nvar Conf = new(AppConfig)\n\ntype AppConfig struct {\n\tName      string `mapstructure:\"name\"`\n\tMode      string `mapstructure:\"mode\"`\n\tVersion   string `mapstructure:\"version\"`\n\tStartTime string `mapstructure:\"start_time\"`\n\tMachineID int64  `mapstructure:\"machine_id\"`\n\tPort      int    `mapstructure:\"port\"`\n\n\t*LogConfig   `mapstructure:\"log\"`\n\t*MySQLConfig `mapstructure:\"mysql\"`\n\t*RedisConfig `mapstructure:\"redis\"`\n}\n\ntype MySQLConfig struct {\n\tHost         string `mapstructure:\"host\"`\n\tUser         string `mapstructure:\"user\"`\n\tPassword     string `mapstructure:\"password\"`\n\tDB           string `mapstructure:\"dbname\"`\n\tPort         int    `mapstructure:\"port\"`\n\tMaxOpenConns int    `mapstructure:\"max_open_conns\"`\n\tMaxIdleConns int    `mapstructure:\"max_idle_conns\"`\n}\n\ntype RedisConfig struct {\n\tHost         string `mapstructure:\"host\"`\n\tPassword     string `mapstructure:\"password\"`\n\tPort         int    `mapstructure:\"port\"`\n\tDB           int    `mapstructure:\"db\"`\n\tPoolSize     int    `mapstructure:\"pool_size\"`\n\tMinIdleConns int    `mapstructure:\"min_idle_conns\"`\n}\n\ntype LogConfig struct {\n\tLevel      string `mapstructure:\"level\"`\n\tFilename   string `mapstructure:\"filename\"`\n\tMaxSize    int    `mapstructure:\"max_size\"`\n\tMaxAge     int    `mapstructure:\"max_age\"`\n\tMaxBackups int    `mapstructure:\"max_backups\"`\n}\n\nfunc Init(filePath string) (err error) {\n\t// 方式1：直接指定配置文件路径（相对路径或者绝对路径）\n\t// 相对路径：相对执行的可执行文件的相对路径\n\t//viper.SetConfigFile(\"./conf/config.yaml\")\n\t// 绝对路径：系统中实际的文件路径\n\t//viper.SetConfigFile(\"/Users/liwenzhou/Desktop/bluebell/conf/config.yaml\")\n\n\t// 方式2：指定配置文件名和配置文件的位置，viper自行查找可用的配置文件\n\t// 配置文件名不需要带后缀\n\t// 配置文件位置可配置多个\n\t//viper.SetConfigName(\"config\") // 指定配置文件名（不带后缀）\n\t//viper.AddConfigPath(\".\") // 指定查找配置文件的路径（这里使用相对路径）\n\t//viper.AddConfigPath(\"./conf\")      // 指定查找配置文件的路径（这里使用相对路径）\n\n\t// 基本上是配合远程配置中心使用的，告诉viper当前的数据使用什么格式去解析\n\t//viper.SetConfigType(\"json\")\n\n\tviper.SetConfigFile(filePath)\n\n\terr = viper.ReadInConfig() // 读取配置信息\n\tif err != nil {\n\t\t// 读取配置信息失败\n\t\tfmt.Printf(\"viper.ReadInConfig failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\n\t// 把读取到的配置信息反序列化到 Conf 变量中\n\tif err := viper.Unmarshal(Conf); err != nil {\n\t\tfmt.Printf(\"viper.Unmarshal failed, err:%v\\n\", err)\n\t}\n\n\tviper.WatchConfig()\n\tviper.OnConfigChange(func(in fsnotify.Event) {\n\t\tfmt.Println(\"配置文件修改了...\")\n\t\tif err := viper.Unmarshal(Conf); err != nil {\n\t\t\tfmt.Printf(\"viper.Unmarshal failed, err:%v\\n\", err)\n\t\t}\n\t})\n\treturn\n}\n"
  },
  {
    "path": "lesson77/bluebell/static/css/app.6310b918.css",
    "content": ".header[data-v-75d7d08c]{width:100%;height:48px;position:fixed;background:#fff;display:flex;display:-webkit-flex;align-items:center;top:0;z-index:1}.header .logo[data-v-75d7d08c]{margin-left:10px;height:32px;background:url(../../static/img/logo.da56125f.png) no-repeat;background-size:32px 32px;background-position:0;padding-left:35px;line-height:32px;flex-grow:0;margin-right:16px;cursor:pointer}.header .search[data-v-75d7d08c]{flex-grow:1;margin:0 auto;max-width:690px;position:relative;display:flex;display:-webkit-flex}.header .search .s-logo[data-v-75d7d08c]{width:18px;height:18px;background:url(../../static/img/search.8e85063d.png) no-repeat;background-size:cover;display:inline-block;position:absolute;top:50%;margin-top:-9px;left:15px}.header .search .s-input[data-v-75d7d08c]{flex-grow:1;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#f6f7f8;border-radius:4px;border:1px solid #edeff1;box-shadow:none;color:#c1c1c1;display:block;height:36px;outline:none;padding:0 16px 0 40px;width:100%}.header .btns[data-v-75d7d08c]{flex-grow:0;margin-left:16px;margin-right:10px;display:flex;display:-webkit-flex;align-items:center}.header .btns .login-btn[data-v-75d7d08c]{border:1px solid transparent;border-radius:4px;box-sizing:border-box;text-align:center;letter-spacing:1px;text-decoration:none;font-size:12px;font-weight:700;letter-spacing:.5px;line-height:24px;text-transform:uppercase;padding:3px 16px;border-color:#0079d3;color:#0079d3;fill:#0079d3;display:inline-block;cursor:pointer}.header .btns .login-btn[data-v-75d7d08c]:first-child{margin-right:5px}.header .btns .login-btn[data-v-75d7d08c]:nth-child(2){margin-right:10px}.header .btns .user[data-v-75d7d08c]{width:auto;height:24px;background:url(../../static/img/avatar.7b0a9835.png) no-repeat;background-size:24px 24px;background-position:0;padding-left:28px;display:flex;display:-webkit-flex;align-items:center;cursor:pointer;padding:12px 12px 12px 28px}.header .btns .user[data-v-75d7d08c]:after{content:\"\";width:0;height:0;border-top:5px solid #878a8c;border-right:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid transparent;margin-top:5px;margin-left:10px}.header .btns .dropdown-content[data-v-75d7d08c]{display:none;position:absolute;background-color:#f9f9f9;min-width:160px;box-shadow:0 8px 16px 0 rgba(0,0,0,.2)}.header .btns .dropdown-content a[data-v-75d7d08c]{color:#000;padding:12px 16px;text-decoration:none;display:block;cursor:pointer}.header .btns .dropdown-content a[data-v-75d7d08c]:hover{background-color:#f1f1f1}.header .btns .user-box:hover .dropdown-content[data-v-75d7d08c]{display:block}@font-face{font-family:iconfont;src:url(data:application/vnd.ms-fontobject;base64,FAwAAGwLAAABAAIAAAAAAAIABQMAAAAAAAABAJABAAAAAExQAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAA/zftSwAAAAAAAAAAAAAAAAAAAAAAABAAaQBjAG8AbgBmAG8AbgB0AAAADgBSAGUAZwB1AGwAYQByAAAAFgBWAGUAcgBzAGkAbwBuACAAMQAuADAAAAAQAGkAYwBvAG4AZgBvAG4AdAAAAAAAAAEAAAALAIAAAwAwR1NVQrD+s+0AAAE4AAAAQk9TLzI8fEi5AAABfAAAAFZjbWFwlyE39gAAAfgAAAIKZ2x5ZhJIvHEAAAQYAAAEfGhlYWQZkfxxAAAA4AAAADZoaGVhCN8EiQAAALwAAAAkaG10eCYfAAAAAAHUAAAAJGxvY2EEZgVyAAAEBAAAABRtYXhwARgAXQAAARgAAAAgbmFtZT5U/n0AAAiUAAACbXBvc3Rl44HVAAALBAAAAGUAAQAAA4D/gABcBQAAAP//BQAAAQAAAAAAAAAAAAAAAAAAAAkAAQAAAAEAAEvtN/9fDzz1AAsEAAAAAADauVw6AAAAANq5XDoAAP+ABQADgAAAAAgAAgAAAAAAAAABAAAACQBRAAUAAAAAAAIAAAAKAAoAAAD/AAAAAAAAAAEAAAAKAB4ALAABREZMVAAIAAQAAAAAAAAAAQAAAAFsaWdhAAgAAAABAAAAAQAEAAQAAAABAAgAAQAGAAAAAQAAAAAAAQQ8AZAABQAIAokCzAAAAI8CiQLMAAAB6wAyAQgAAAIABQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUGZFZABA5gbmiwOA/4AAXAOAAIAAAAABAAAAAAAABAAAAASqAAAEAAAABAAAAAR1AAAEAAAABAAAAAQAAAAFAAAAAAAABQAAAAMAAAAsAAAABAAAAZoAAQAAAAAAlAADAAEAAAAsAAMACgAAAZoABABoAAAAEgAQAAMAAuYG5gjmIuYu5kXmSeaA5ov//wAA5gbmCOYi5i7mReZJ5oDmi///AAAAAAAAAAAAAAAAAAAAAAABABIAEgASABIAEgASABIAEgAAAAEAAgAIAAQABgAHAAUAAwAAAQYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAcAAAAAAAAAAIAADmBgAA5gYAAAABAADmCAAA5ggAAAACAADmIgAA5iIAAAAIAADmLgAA5i4AAAAEAADmRQAA5kUAAAAGAADmSQAA5kkAAAAHAADmgAAA5oAAAAAFAADmiwAA5osAAAADAAAAAAAAADIAVgDYATABhgGuAdYCPgABAAD/gASrA4AAIQAAJSMRNCYrASIGFREjESMiBhURIyIGHQEhNS4BKwERLgErAQMvbRgSgBMYbasSGIASGQSrARgSewEYEqpVAwERGRgS/P8BKxgT/wAZEaurEhgB1hIYAAAAAQAA/4AD7gOAABMAAAUzPwEnNy8BIycHIw8BFwcfATMXAni5OYMyMoM5uXh4uTmDMjKDObl4KbBfmppfsFdXsF+aml+wVwAABQAA/4cD+QOAABkAJAAoACsAUAAAJSMiLgI/ATUBPgEzMhYfAR4BFRQGBwE0BgE3NCYjJyYiDwEXARcBJwE3JyUyFhURDgEHIS4BJxE+ATchHgEUBiMhDgEVERQWFyEyNjURNDYBMwcGCwsGBTMB6gsfEBYjESsRDQsL/g5gAhcIAwUsBg8BM0n+HlABZlD+WF9CAlgQFAE/LfzxLT8CAj8tAfkQFBQQ/g4QFBQQAwgQFBRbAgsUC74IAfgLCxEMMxAjFhEeDP4ABhYCUgcGCTMGBixQ/qhQAW1Y/fkWUOoUEP4kMkIBAUIyAw8yQgEFFiITARoR/PgRGgEWFgHcEBQAAAAEAAD/gARyA4AAFwAgACwANQAAASEOAQcRHgEXMxceATI2PwEzPgE3ES4BATIWFAYiJjQ2BTQ+AR4CDgEjIiYFIiY0NjIWFAYDlv1KX34DA35fw2AKHCAbCmDEX34DA3v+RiEsLEIsLP7fGiwuIgkTJhghLAI2ISwsQiwtA4ADfl/+Vl9+An0MDg4MfQJ+XwGqXn7+qixCLCxCLE0YJhMJIi0tGSwsLEIsLEEtAAAAAAEAAP+fA6ADYwAzAAABJicmJyYnLgIGFRYCBwYWFxY2Jy4BNz4BNz4BFxYXFgYHBhY3PgE3LgEnJgYHDgEHBiYCvgExKEIlKgkhIAkD2S87hJcTBQ83KRUZUhgEFw5tQRcYZQ4cFZLaBAFALwsTAgMTIgwPAVFvZGRWKSMHGBkEEbr+71Gf1RkDCwwmekA7XGQQBwxlhjh4SQsQAhPLdCuQRQ4EES9OJw4GAAABAAD/pgOLA4AAFQAABQYiJwEmNjczET4BMyEyFhcRMx4BBwI2Fz4X/sIWESSqARgSAQASGAGqJBEWPxsbAX4bJQEB1RIZGRL+KwElGwAAAAEAAP+AA4sDWgAVAAABJiIHAQYWFzMRHgEzITI2NxEzPgEnAjYXPhf+whYRJKoBGBIBABIYAaokERYDPxsb/oIbJQH+KxIZGRIB1QElGwAABQAA/4AFAAOAABAAGwAoADQAPQAAEyEeARcRDgEHIS4BJxE+ATcRMSERJQEOAS8BBzU3PgEfAQE+AR8BESEFHgEXDgEHLgEnPgEXMjY0JiIGFBZnBDIsOgEBOiz7ziw6AQE6LAQy/vf+yQ0lEeXKqQ0gD94BPRAsEOb7zgEPPlECAlE+PVECAlE9EhcXIxgYA4ABNin8wCk2AQE2KQNAKTYB/GABG/3+mQ4ECpBYeEEJAQiMAW4PAQ7cAZxiAUw5OksCAks6OUysFiEWFiEWAAAAEgDeAAEAAAAAAAAAFQAAAAEAAAAAAAEACAAVAAEAAAAAAAIABwAdAAEAAAAAAAMACAAkAAEAAAAAAAQACAAsAAEAAAAAAAUACwA0AAEAAAAAAAYACAA/AAEAAAAAAAoAKwBHAAEAAAAAAAsAEwByAAMAAQQJAAAAKgCFAAMAAQQJAAEAEACvAAMAAQQJAAIADgC/AAMAAQQJAAMAEADNAAMAAQQJAAQAEADdAAMAAQQJAAUAFgDtAAMAAQQJAAYAEAEDAAMAAQQJAAoAVgETAAMAAQQJAAsAJgFpCkNyZWF0ZWQgYnkgaWNvbmZvbnQKaWNvbmZvbnRSZWd1bGFyaWNvbmZvbnRpY29uZm9udFZlcnNpb24gMS4waWNvbmZvbnRHZW5lcmF0ZWQgYnkgc3ZnMnR0ZiBmcm9tIEZvbnRlbGxvIHByb2plY3QuaHR0cDovL2ZvbnRlbGxvLmNvbQAKAEMAcgBlAGEAdABlAGQAIABiAHkAIABpAGMAbwBuAGYAbwBuAHQACgBpAGMAbwBuAGYAbwBuAHQAUgBlAGcAdQBsAGEAcgBpAGMAbwBuAGYAbwBuAHQAaQBjAG8AbgBmAG8AbgB0AFYAZQByAHMAaQBvAG4AIAAxAC4AMABpAGMAbwBuAGYAbwBuAHQARwBlAG4AZQByAGEAdABlAGQAIABiAHkAIABzAHYAZwAyAHQAdABmACAAZgByAG8AbQAgAEYAbwBuAHQAZQBsAGwAbwAgAHAAcgBvAGoAZQBjAHQALgBoAHQAdABwADoALwAvAGYAbwBuAHQAZQBsAGwAbwAuAGMAbwBtAAAAAAIAAAAAAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQECAQMBBAEFAQYBBwEIAQkBCgADdG9wCnBvbHlnb25yZWQEZWRpdAdjb21tZW50A2hvdARkb3duAnVwBWltYWdlAAAAAAA=);src:url(data:application/vnd.ms-fontobject;base64,FAwAAGwLAAABAAIAAAAAAAIABQMAAAAAAAABAJABAAAAAExQAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAA/zftSwAAAAAAAAAAAAAAAAAAAAAAABAAaQBjAG8AbgBmAG8AbgB0AAAADgBSAGUAZwB1AGwAYQByAAAAFgBWAGUAcgBzAGkAbwBuACAAMQAuADAAAAAQAGkAYwBvAG4AZgBvAG4AdAAAAAAAAAEAAAALAIAAAwAwR1NVQrD+s+0AAAE4AAAAQk9TLzI8fEi5AAABfAAAAFZjbWFwlyE39gAAAfgAAAIKZ2x5ZhJIvHEAAAQYAAAEfGhlYWQZkfxxAAAA4AAAADZoaGVhCN8EiQAAALwAAAAkaG10eCYfAAAAAAHUAAAAJGxvY2EEZgVyAAAEBAAAABRtYXhwARgAXQAAARgAAAAgbmFtZT5U/n0AAAiUAAACbXBvc3Rl44HVAAALBAAAAGUAAQAAA4D/gABcBQAAAP//BQAAAQAAAAAAAAAAAAAAAAAAAAkAAQAAAAEAAEvtN/9fDzz1AAsEAAAAAADauVw6AAAAANq5XDoAAP+ABQADgAAAAAgAAgAAAAAAAAABAAAACQBRAAUAAAAAAAIAAAAKAAoAAAD/AAAAAAAAAAEAAAAKAB4ALAABREZMVAAIAAQAAAAAAAAAAQAAAAFsaWdhAAgAAAABAAAAAQAEAAQAAAABAAgAAQAGAAAAAQAAAAAAAQQ8AZAABQAIAokCzAAAAI8CiQLMAAAB6wAyAQgAAAIABQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUGZFZABA5gbmiwOA/4AAXAOAAIAAAAABAAAAAAAABAAAAASqAAAEAAAABAAAAAR1AAAEAAAABAAAAAQAAAAFAAAAAAAABQAAAAMAAAAsAAAABAAAAZoAAQAAAAAAlAADAAEAAAAsAAMACgAAAZoABABoAAAAEgAQAAMAAuYG5gjmIuYu5kXmSeaA5ov//wAA5gbmCOYi5i7mReZJ5oDmi///AAAAAAAAAAAAAAAAAAAAAAABABIAEgASABIAEgASABIAEgAAAAEAAgAIAAQABgAHAAUAAwAAAQYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAcAAAAAAAAAAIAADmBgAA5gYAAAABAADmCAAA5ggAAAACAADmIgAA5iIAAAAIAADmLgAA5i4AAAAEAADmRQAA5kUAAAAGAADmSQAA5kkAAAAHAADmgAAA5oAAAAAFAADmiwAA5osAAAADAAAAAAAAADIAVgDYATABhgGuAdYCPgABAAD/gASrA4AAIQAAJSMRNCYrASIGFREjESMiBhURIyIGHQEhNS4BKwERLgErAQMvbRgSgBMYbasSGIASGQSrARgSewEYEqpVAwERGRgS/P8BKxgT/wAZEaurEhgB1hIYAAAAAQAA/4AD7gOAABMAAAUzPwEnNy8BIycHIw8BFwcfATMXAni5OYMyMoM5uXh4uTmDMjKDObl4KbBfmppfsFdXsF+aml+wVwAABQAA/4cD+QOAABkAJAAoACsAUAAAJSMiLgI/ATUBPgEzMhYfAR4BFRQGBwE0BgE3NCYjJyYiDwEXARcBJwE3JyUyFhURDgEHIS4BJxE+ATchHgEUBiMhDgEVERQWFyEyNjURNDYBMwcGCwsGBTMB6gsfEBYjESsRDQsL/g5gAhcIAwUsBg8BM0n+HlABZlD+WF9CAlgQFAE/LfzxLT8CAj8tAfkQFBQQ/g4QFBQQAwgQFBRbAgsUC74IAfgLCxEMMxAjFhEeDP4ABhYCUgcGCTMGBixQ/qhQAW1Y/fkWUOoUEP4kMkIBAUIyAw8yQgEFFiITARoR/PgRGgEWFgHcEBQAAAAEAAD/gARyA4AAFwAgACwANQAAASEOAQcRHgEXMxceATI2PwEzPgE3ES4BATIWFAYiJjQ2BTQ+AR4CDgEjIiYFIiY0NjIWFAYDlv1KX34DA35fw2AKHCAbCmDEX34DA3v+RiEsLEIsLP7fGiwuIgkTJhghLAI2ISwsQiwtA4ADfl/+Vl9+An0MDg4MfQJ+XwGqXn7+qixCLCxCLE0YJhMJIi0tGSwsLEIsLEEtAAAAAAEAAP+fA6ADYwAzAAABJicmJyYnLgIGFRYCBwYWFxY2Jy4BNz4BNz4BFxYXFgYHBhY3PgE3LgEnJgYHDgEHBiYCvgExKEIlKgkhIAkD2S87hJcTBQ83KRUZUhgEFw5tQRcYZQ4cFZLaBAFALwsTAgMTIgwPAVFvZGRWKSMHGBkEEbr+71Gf1RkDCwwmekA7XGQQBwxlhjh4SQsQAhPLdCuQRQ4EES9OJw4GAAABAAD/pgOLA4AAFQAABQYiJwEmNjczET4BMyEyFhcRMx4BBwI2Fz4X/sIWESSqARgSAQASGAGqJBEWPxsbAX4bJQEB1RIZGRL+KwElGwAAAAEAAP+AA4sDWgAVAAABJiIHAQYWFzMRHgEzITI2NxEzPgEnAjYXPhf+whYRJKoBGBIBABIYAaokERYDPxsb/oIbJQH+KxIZGRIB1QElGwAABQAA/4AFAAOAABAAGwAoADQAPQAAEyEeARcRDgEHIS4BJxE+ATcRMSERJQEOAS8BBzU3PgEfAQE+AR8BESEFHgEXDgEHLgEnPgEXMjY0JiIGFBZnBDIsOgEBOiz7ziw6AQE6LAQy/vf+yQ0lEeXKqQ0gD94BPRAsEOb7zgEPPlECAlE+PVECAlE9EhcXIxgYA4ABNin8wCk2AQE2KQNAKTYB/GABG/3+mQ4ECpBYeEEJAQiMAW4PAQ7cAZxiAUw5OksCAks6OUysFiEWFiEWAAAAEgDeAAEAAAAAAAAAFQAAAAEAAAAAAAEACAAVAAEAAAAAAAIABwAdAAEAAAAAAAMACAAkAAEAAAAAAAQACAAsAAEAAAAAAAUACwA0AAEAAAAAAAYACAA/AAEAAAAAAAoAKwBHAAEAAAAAAAsAEwByAAMAAQQJAAAAKgCFAAMAAQQJAAEAEACvAAMAAQQJAAIADgC/AAMAAQQJAAMAEADNAAMAAQQJAAQAEADdAAMAAQQJAAUAFgDtAAMAAQQJAAYAEAEDAAMAAQQJAAoAVgETAAMAAQQJAAsAJgFpCkNyZWF0ZWQgYnkgaWNvbmZvbnQKaWNvbmZvbnRSZWd1bGFyaWNvbmZvbnRpY29uZm9udFZlcnNpb24gMS4waWNvbmZvbnRHZW5lcmF0ZWQgYnkgc3ZnMnR0ZiBmcm9tIEZvbnRlbGxvIHByb2plY3QuaHR0cDovL2ZvbnRlbGxvLmNvbQAKAEMAcgBlAGEAdABlAGQAIABiAHkAIABpAGMAbwBuAGYAbwBuAHQACgBpAGMAbwBuAGYAbwBuAHQAUgBlAGcAdQBsAGEAcgBpAGMAbwBuAGYAbwBuAHQAaQBjAG8AbgBmAG8AbgB0AFYAZQByAHMAaQBvAG4AIAAxAC4AMABpAGMAbwBuAGYAbwBuAHQARwBlAG4AZQByAGEAdABlAGQAIABiAHkAIABzAHYAZwAyAHQAdABmACAAZgByAG8AbQAgAEYAbwBuAHQAZQBsAGwAbwAgAHAAcgBvAGoAZQBjAHQALgBoAHQAdABwADoALwAvAGYAbwBuAHQAZQBsAGwAbwAuAGMAbwBtAAAAAAIAAAAAAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQECAQMBBAEFAQYBBwEIAQkBCgADdG9wCnBvbHlnb25yZWQEZWRpdAdjb21tZW50A2hvdARkb3duAnVwBWltYWdlAAAAAAA=#iefix) format(\"embedded-opentype\"),url(\"data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAXwAAsAAAAAC2wAAAWiAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCECgqIfIcWATYCJAMkCxQABCAFhG0HZRunCSMRdntwMpD9RYJ5wtTNaA/lYPNf05ILYQVbigOMr5PFFM7087iZ7+dDCEklBL/zmipy4tWZVixsDpsjVTs4E4G2E1cCgOf/vd23/TOmiQ10WCYBRpBJJ6zNH54B7zvXH7BR+ShHcvaTRztMu2L6shz9sZ/LsyYWir4+6cXs/Zlp+2m3tLyEXecISUSjaIRQIVIpsaEibk8x2y7uwPUAAaAtEwbNzzINUATYbggA2qZpM1AON0EED6FsZMCxlZbXQIhqLxLXAOBV7efRCzGhAAjCDuxGVY359ZD1OcmEeTcP6zAPfHFBADifBIABhAEQAHQFhs3YKIfB6OfGAs0AwGFhRFG/S/ERIV8x/nOT2w1eMjYP4XgZACGG4l8eSUQIAzSArB4K8bCjQoHPSckJ6IkUnEAAfaCNxgg4QQDMhwWyYDGcIALycIIQaALCsqGJj+EASAFaC0APMGaYIIIOVoGmNvbvVh/ZIkaA/VViD3GcQiFhPLF5By0y2GnaOAskjY8WQk6CFPUHWDAKDzB6vY7L6GidhtZotF9Fy2jraKPV2q/UoybbDGM67J89NQR8r6ze4GxRGqx0jtUoK9PWa5t0zXrdQ6atQIw649gDcz6iiflET8tWfpb2MFd2AX6rXGJyIGxwdHg5nRrC5Sr23pYp04xSz8zP6/HcXL3pOH/MeMJwcpNh59H9x4e2HaIdlgduGt/AY3M6fv4TGdYjdjO4FjROl3bKprgxF7t3Xm+bkR+adjs15qnAQh53eE5nnVQNOtPM1gdmjSGN9p2JDB5wMJ6mrWqC2b89qMNuDKqf3R7PEH1bA57EsRgzB3eGdEj3315KDt6K4w7cWSYauZtIIineUm/eF6DToPwT2ibbQfnBMbf5vGvlqDPvwpzPxLzW5DDYjbP81pgftQFJWjle2sKhB0IdXIsUeO4FiYMVUQvXMYf17NASdDhp51Mbovu4SlEZgcMtOiL1fCBUa0TkmL22JOY49dUf9q9ZxQpm3PuMQrLd8ZObbOSK6ifJIOEerbcwtKjyI31lZcP9lfoZcoM8W13vKzaWfRdlZJ/yPiUHJd4S4/5//RrvE8P+/GOyhJIk/0ix8Yp32VhIruMPbCC6uogNdlF1WGcDOruxc+D4iu0DQuTcBhQ0tr6967B25dEcftyKMpVlz4LkpbBVqpVH40PvnzH7GhyvghhVF1iXrS164znRn09LiMMLbERVyvDTTqvKp+SbhqC2SmP1WmVdZuM799dnPdukbKkvevtk9uvcW1fwY3TmqxF5233ZCK5ccfwziM+4F39NetcaQZTdhVo54FqRyx4lWANPdZmf3XaP8TG7LFQWZdmlPeQoxCym/k1ovJ4r/vTVy/rW2axLP37xnintgf93/ffZO4MxXptvVZWq1FUlt09LDrXKumiag2tY2eD7xMon4BIBALh5/D0eCZ7Hf4lG8CiuC09jk9PMg1jzL9gOUj/1N5b2U8wvY8Hbh9fVlHLzQkieBQqxQRG+86sJAcDtFl4MzCWuR/gG0CgmT0u3M+gyRMSqbdpizTSfAfimxznvEyFP3A2AUOQWEESWAkbxowgYYSDEiAMSJRNooQpPZkg9ChgqMQCEeFYAhLUTCF5OAsa6ThEw3gMhhe+AxAYDTYek52Qc9hpzemPsBEfo72AGikr0yUnv/UZ99h3bDZrwH3k2OSizwl37wohcxAHzRVciChRTgE/hZug9QWKyOEg2iaQ6z1XQM2UDhcXJG2MnOIL+DsxAUSnz5eT5/m/UZ99xBOlT+R95NmsHpUyRgPJljYlImzI8X3RFqKiAOndTAJ8URM/LBFLwVhYHyUwZiqmW0zSV1JpVT4R5vQIAGnsQcQiEkQAJEYlEiEJiRMP6oHRK5O+aIuO4EYNG9n166YBR1hPJZqRrXJ3T1oRO42IBAAA=\") format(\"woff2\"),url(data:font/woff;base64,d09GRgABAAAAAAfwAAsAAAAAC2wAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADMAAABCsP6z7U9TLzIAAAE8AAAARAAAAFY8fEi5Y21hcAAAAYAAAACQAAACCpchN/ZnbHlmAAACEAAAA6UAAAR8Eki8cWhlYWQAAAW4AAAALwAAADYZkfxxaGhlYQAABegAAAAeAAAAJAjfBIlobXR4AAAGCAAAABkAAAAkJh8AAGxvY2EAAAYkAAAAFAAAABQEZgVybWF4cAAABjgAAAAfAAAAIAEYAF1uYW1lAAAGWAAAAUUAAAJtPlT+fXBvc3QAAAegAAAATQAAAGVl44HVeJxjYGRgYOBikGPQYWB0cfMJYeBgYGGAAJAMY05meiJQDMoDyrGAaQ4gZoOIAgCKIwNPAHicY2BksWGcwMDKwMHUyXSGgYGhH0IzvmYwYuRgYGBiYGVmwAoC0lxTGByesT3rZm7438AQw9zA0AAUZgTJAQD7aAyweJzlkTsOwjAQRJ/jYCxEgThCapRbpMhNKDlC+rRUHHCuEXa9NHxuwFjP0o681moH2AHZuBg9pAcJ193c1PzMofk9V6vPnMzpVFQ1aNSkWYvWbYNf3oeS9b8f9zqq/V7Y2zQ2Sypfff+nY7tvr6r6dgNPRTWwzaEhaG/GwJPUFOB9c2AbRkvgyWsNyE++qCzQeJx1UkFsG1UQ/fO//TfOOuv9Xnt3HRcnuxvvNnhtq7vOmqSK3VgCqbQgpxJtBAhHkMIpICEOEVUtiyI4lEPFAUSFVE4IEg69tocKARdA6i1IoApOcOgBxIlGTTbMuhVwQf/vzM5o5s2fmUeAkMNRaoeNiEVI1REttwY2LwpHOGNl80fAWmhADUQiWHPTzI80c3Mnb47ypdQOmPkL+G0/w0CUzPz+IdRM7ZCUxA5GwA95kxAyrsF+xxoaIemwC167CY4nOTkwpBkIDbp1Y/FSEFxavLH179/89cHVq4Pr58491JiKOO+yPcQpkTlylNRIP3mz3aBdWIAOhIE+AxUoFrgELQ7tlut4ro1F8HjQ9qqBXhQKSFYDPNGBtlWBAncsBYqioBtWEC2IVgShxGWZp0O4K8+ouiNqYkqWY2WdGhMs7fMchCfjSh9e7sdrgx5dUwvQre//We9S2q3DnlooqLGSSDaB8jkqF+RbE3BPlkU2VB1dVLIx4To9I/FMyLnfjz/rw+bawZ7ev4uZc0EPoBewHOq0bmswLfbviWnQdbijFnCWqfG+XscZGGSW+GQBx4sdSKICRmhUIIi6EGJvuC0I9AK33VaUbnWgQhVwbDed2ImffXDw5GDI2HDw9frkkdny5Po3iXkhfsLy/Z7vx79M+w07o7mm5dNo7KuzEYbHZwdDejGrKNmLdDiA7ReG8XaS0POfMl0tY9frJX+MsFInD3d/jX3CXiIhGq6XnAblRZ1KXDf0yGtAu5NcAy2OvsTC/bhcwq64S2/BsaO96qMZazbDfmwef/tDLZ1rzxdLZ8yUoWyuGOZ55Ujx/Z9ScKIpa5RpdjYHq69tbJyddySzlBI34z9Wr+2WmJx13zxx/PkNVcqef+exrZOySrXv36hdeVxJiebTnsIfvPVTdhlnW0SycdsDN2qHyJTQCnRDhBWQaGR0jPgrXcxtI+uBIMe354TeLZdhWK4C7OZLpXxcg2r5H95fZs8meODaEmDLIW4K8aK2wDV5/4PHEDB+CwHjWgIIu2PAhP+jNMH3qaSM/G+RZUI0ZLHxX1aLY5aoggJNkBZwmDMAiRBWGuMwDKNw2EHUcm1e0F9JBf4SwJJ///YDnQriv+Jvp6ri1+8+n5rN/QzLqq/+dv825DqrlK52lhO5nDcMxzTZCKL5/S/nI0DNTqDeX4fyQfyRkpq8sra1koGJ9+DVHCh34OMX4fTi0ilKTy0tnv5Ct3S8fwMKbd/oAAAAeJxjYGRgYABi77eS5+P5bb4ycLMwgMCtnTFWCPp/AysDcwOQy8HABBIFACtoCjQAeJxjYGRgYG7438AQw8rAwPD/P5AEiqAATgByEgRyAAB4nGNhYGBgWQXEDFBcisQGYlYgBgAgPQFFAAAAAAAAAAAyAFYA2AEwAYYBrgHWAj54nGNgZGBg4GQIZGBlAAEmIOYCQgaG/2A+AwARmQF2AHicZY9NTsMwEIVf+gekEqqoYIfkBWIBKP0Rq25YVGr3XXTfpk6bKokjx63UA3AejsAJOALcgDvwSCebNpbH37x5Y08A3OAHHo7fLfeRPVwyO3INF7gXrlN/EG6QX4SbaONVuEX9TdjHM6bCbXRheYPXuGL2hHdhDx18CNdwjU/hOvUv4Qb5W7iJO/wKt9Dx6sI+5l5XuI1HL/bHVi+cXqnlQcWhySKTOb+CmV7vkoWt0uqca1vEJlODoF9JU51pW91T7NdD5yIVWZOqCas6SYzKrdnq0AUb5/JRrxeJHoQm5Vhj/rbGAo5xBYUlDowxQhhkiMro6DtVZvSvsUPCXntWPc3ndFsU1P9zhQEC9M9cU7qy0nk6T4E9XxtSdXQrbsuelDSRXs1JErJCXta2VELqATZlV44RelzRiT8oZ0j/AAlabsgAAAB4nG3BSw6AIAwFwD4+ItzSSAMkQompMd7ehVtnyNAn0b8IAwsHjwUBKyISWZWZphxPkXFydpybhl1656G2iros9zDX9K1vhYletHgQlQAAAA==) format(\"woff\"),url(data:font/ttf;base64,AAEAAAALAIAAAwAwR1NVQrD+s+0AAAE4AAAAQk9TLzI8fEi5AAABfAAAAFZjbWFwlyE39gAAAfgAAAIKZ2x5ZhJIvHEAAAQYAAAEfGhlYWQZkfxxAAAA4AAAADZoaGVhCN8EiQAAALwAAAAkaG10eCYfAAAAAAHUAAAAJGxvY2EEZgVyAAAEBAAAABRtYXhwARgAXQAAARgAAAAgbmFtZT5U/n0AAAiUAAACbXBvc3Rl44HVAAALBAAAAGUAAQAAA4D/gABcBQAAAP//BQAAAQAAAAAAAAAAAAAAAAAAAAkAAQAAAAEAAEvtGc9fDzz1AAsEAAAAAADauVw6AAAAANq5XDoAAP+ABQADgAAAAAgAAgAAAAAAAAABAAAACQBRAAUAAAAAAAIAAAAKAAoAAAD/AAAAAAAAAAEAAAAKAB4ALAABREZMVAAIAAQAAAAAAAAAAQAAAAFsaWdhAAgAAAABAAAAAQAEAAQAAAABAAgAAQAGAAAAAQAAAAAAAQQ8AZAABQAIAokCzAAAAI8CiQLMAAAB6wAyAQgAAAIABQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUGZFZABA5gbmiwOA/4AAXAOAAIAAAAABAAAAAAAABAAAAASqAAAEAAAABAAAAAR1AAAEAAAABAAAAAQAAAAFAAAAAAAABQAAAAMAAAAsAAAABAAAAZoAAQAAAAAAlAADAAEAAAAsAAMACgAAAZoABABoAAAAEgAQAAMAAuYG5gjmIuYu5kXmSeaA5ov//wAA5gbmCOYi5i7mReZJ5oDmi///AAAAAAAAAAAAAAAAAAAAAAABABIAEgASABIAEgASABIAEgAAAAEAAgAIAAQABgAHAAUAAwAAAQYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAcAAAAAAAAAAIAADmBgAA5gYAAAABAADmCAAA5ggAAAACAADmIgAA5iIAAAAIAADmLgAA5i4AAAAEAADmRQAA5kUAAAAGAADmSQAA5kkAAAAHAADmgAAA5oAAAAAFAADmiwAA5osAAAADAAAAAAAAADIAVgDYATABhgGuAdYCPgABAAD/gASrA4AAIQAAJSMRNCYrASIGFREjESMiBhURIyIGHQEhNS4BKwERLgErAQMvbRgSgBMYbasSGIASGQSrARgSewEYEqpVAwERGRgS/P8BKxgT/wAZEaurEhgB1hIYAAAAAQAA/4AD7gOAABMAAAUzPwEnNy8BIycHIw8BFwcfATMXAni5OYMyMoM5uXh4uTmDMjKDObl4KbBfmppfsFdXsF+aml+wVwAABQAA/4cD+QOAABkAJAAoACsAUAAAJSMiLgI/ATUBPgEzMhYfAR4BFRQGBwE0BgE3NCYjJyYiDwEXARcBJwE3JyUyFhURDgEHIS4BJxE+ATchHgEUBiMhDgEVERQWFyEyNjURNDYBMwcGCwsGBTMB6gsfEBYjESsRDQsL/g5gAhcIAwUsBg8BM0n+HlABZlD+WF9CAlgQFAE/LfzxLT8CAj8tAfkQFBQQ/g4QFBQQAwgQFBRbAgsUC74IAfgLCxEMMxAjFhEeDP4ABhYCUgcGCTMGBixQ/qhQAW1Y/fkWUOoUEP4kMkIBAUIyAw8yQgEFFiITARoR/PgRGgEWFgHcEBQAAAAEAAD/gARyA4AAFwAgACwANQAAASEOAQcRHgEXMxceATI2PwEzPgE3ES4BATIWFAYiJjQ2BTQ+AR4CDgEjIiYFIiY0NjIWFAYDlv1KX34DA35fw2AKHCAbCmDEX34DA3v+RiEsLEIsLP7fGiwuIgkTJhghLAI2ISwsQiwtA4ADfl/+Vl9+An0MDg4MfQJ+XwGqXn7+qixCLCxCLE0YJhMJIi0tGSwsLEIsLEEtAAAAAAEAAP+fA6ADYwAzAAABJicmJyYnLgIGFRYCBwYWFxY2Jy4BNz4BNz4BFxYXFgYHBhY3PgE3LgEnJgYHDgEHBiYCvgExKEIlKgkhIAkD2S87hJcTBQ83KRUZUhgEFw5tQRcYZQ4cFZLaBAFALwsTAgMTIgwPAVFvZGRWKSMHGBkEEbr+71Gf1RkDCwwmekA7XGQQBwxlhjh4SQsQAhPLdCuQRQ4EES9OJw4GAAABAAD/pgOLA4AAFQAABQYiJwEmNjczET4BMyEyFhcRMx4BBwI2Fz4X/sIWESSqARgSAQASGAGqJBEWPxsbAX4bJQEB1RIZGRL+KwElGwAAAAEAAP+AA4sDWgAVAAABJiIHAQYWFzMRHgEzITI2NxEzPgEnAjYXPhf+whYRJKoBGBIBABIYAaokERYDPxsb/oIbJQH+KxIZGRIB1QElGwAABQAA/4AFAAOAABAAGwAoADQAPQAAEyEeARcRDgEHIS4BJxE+ATcRMSERJQEOAS8BBzU3PgEfAQE+AR8BESEFHgEXDgEHLgEnPgEXMjY0JiIGFBZnBDIsOgEBOiz7ziw6AQE6LAQy/vf+yQ0lEeXKqQ0gD94BPRAsEOb7zgEPPlECAlE+PVECAlE9EhcXIxgYA4ABNin8wCk2AQE2KQNAKTYB/GABG/3+mQ4ECpBYeEEJAQiMAW4PAQ7cAZxiAUw5OksCAks6OUysFiEWFiEWAAAAEgDeAAEAAAAAAAAAFQAAAAEAAAAAAAEACAAVAAEAAAAAAAIABwAdAAEAAAAAAAMACAAkAAEAAAAAAAQACAAsAAEAAAAAAAUACwA0AAEAAAAAAAYACAA/AAEAAAAAAAoAKwBHAAEAAAAAAAsAEwByAAMAAQQJAAAAKgCFAAMAAQQJAAEAEACvAAMAAQQJAAIADgC/AAMAAQQJAAMAEADNAAMAAQQJAAQAEADdAAMAAQQJAAUAFgDtAAMAAQQJAAYAEAEDAAMAAQQJAAoAVgETAAMAAQQJAAsAJgFpCkNyZWF0ZWQgYnkgaWNvbmZvbnQKaWNvbmZvbnRSZWd1bGFyaWNvbmZvbnRpY29uZm9udFZlcnNpb24gMS4waWNvbmZvbnRHZW5lcmF0ZWQgYnkgc3ZnMnR0ZiBmcm9tIEZvbnRlbGxvIHByb2plY3QuaHR0cDovL2ZvbnRlbGxvLmNvbQAKAEMAcgBlAGEAdABlAGQAIABiAHkAIABpAGMAbwBuAGYAbwBuAHQACgBpAGMAbwBuAGYAbwBuAHQAUgBlAGcAdQBsAGEAcgBpAGMAbwBuAGYAbwBuAHQAaQBjAG8AbgBmAG8AbgB0AFYAZQByAHMAaQBvAG4AIAAxAC4AMABpAGMAbwBuAGYAbwBuAHQARwBlAG4AZQByAGEAdABlAGQAIABiAHkAIABzAHYAZwAyAHQAdABmACAAZgByAG8AbQAgAEYAbwBuAHQAZQBsAGwAbwAgAHAAcgBvAGoAZQBjAHQALgBoAHQAdABwADoALwAvAGYAbwBuAHQAZQBsAGwAbwAuAGMAbwBtAAAAAAIAAAAAAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQECAQMBBAEFAQYBBwEIAQkBCgADdG9wCnBvbHlnb25yZWQEZWRpdAdjb21tZW50A2hvdARkb3duAnVwBWltYWdlAAAAAAA=) format(\"truetype\"),url(../../static/img/iconfont.cdbe38a0.svg#iconfont) format(\"svg\")}.iconfont{font-family:iconfont!important;font-size:16px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.icon-top:before{content:\"\\e606\"}.icon-polygonred:before{content:\"\\e608\"}.icon-edit:before{content:\"\\e68b\"}.icon-comment:before{content:\"\\e62e\"}.icon-hot:before{content:\"\\e680\"}.icon-down:before{content:\"\\e645\"}.icon-up:before{content:\"\\e649\"}.icon-image:before{content:\"\\e622\"}body,html{width:100%;height:100%;font-family:IBMPlexSans,Arial,sans-serif;background:#eee}a,abbr,acronym,address,applet,article,aside,audio,b,big,blockquote,body,button,canvas,caption,center,cite,code,dd,del,details,dfn,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,html,i,iframe,img,input,ins,kbd,label,legend,li,mark,menu,nav,object,ol,output,p,pre,q,ruby,s,samp,section,small,span,strike,strong,sub,summary,sup,table,tbody,td,tfoot,th,thead,time,tr,tt,u,ul,var,video{margin:0;padding:0;border:0;outline:0}a .page,abbr .page,acronym .page,address .page,applet .page,article .page,aside .page,audio .page,b .page,big .page,blockquote .page,body .page,button .page,canvas .page,caption .page,center .page,cite .page,code .page,dd .page,del .page,details .page,dfn .page,div .page,dl .page,dt .page,em .page,embed .page,fieldset .page,figcaption .page,figure .page,footer .page,form .page,h1 .page,h2 .page,h3 .page,h4 .page,h5 .page,h6 .page,header .page,hgroup .page,html .page,i .page,iframe .page,img .page,input .page,ins .page,kbd .page,label .page,legend .page,li .page,mark .page,menu .page,nav .page,object .page,ol .page,output .page,p .page,pre .page,q .page,ruby .page,s .page,samp .page,section .page,small .page,span .page,strike .page,strong .page,sub .page,summary .page,sup .page,table .page,tbody .page,td .page,tfoot .page,th .page,thead .page,time .page,tr .page,tt .page,u .page,ul .page,var .page,video .page{width:100%;height:auto}.content[data-v-2baf73fc]{max-width:100%;box-sizing:border-box;display:flex;flex-direction:row;justify-content:center;margin:48px auto 0;padding:20px 24px}.content .left[data-v-2baf73fc]{width:640px;padding-bottom:10px}.content .left .c-l-title[data-v-2baf73fc]{font-size:14px;font-weight:500;line-height:18px;color:#1a1a1b;text-transform:unset;padding-bottom:10px}.content .left .c-l-header[data-v-2baf73fc]{align-items:center;background-color:#fff;border:1px solid #ccc;border-radius:4px;box-sizing:border-box;display:flex;flex-flow:row nowrap;height:56px;justify-content:flex-start;margin-bottom:16px;padding:0 12px}.content .left .c-l-header .iconfont[data-v-2baf73fc]{margin-right:4px}.content .left .c-l-header .btn-iconfont[data-v-2baf73fc]{display:flex;display:-webkit-flex}.content .left .c-l-header .active[data-v-2baf73fc]{background:#f6f7f8;color:#0079d3;fill:#0079d3;border-radius:20px;height:32px;line-height:32px;margin-right:8px;padding:0 10px}.content .left .c-l-header .new[data-v-2baf73fc]{font-size:14px;margin-right:18px}.content .left .c-l-header .top[data-v-2baf73fc]{font-size:14px}.content .left .c-l-header .btn-publish[data-v-2baf73fc]{width:64px;height:32px;line-height:32px;background-color:#54b351;color:#fff;border:1px solid transparent;border-radius:4px;box-sizing:border-box;text-align:center;margin-left:auto;cursor:pointer}.content .left .c-l-header .sort[data-v-2baf73fc]{margin-left:300px;display:flex;color:#0079d3;display:-webkit-flex;align-items:center}.content .left .c-l-header .sort .sort-triangle[data-v-2baf73fc]{width:0;height:0;border-top:5px solid #0079d3;border-right:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid transparent;margin-top:5px;margin-left:10px}.content .left .c-l-list .c-l-item[data-v-2baf73fc]{list-style:none;border-radius:4px;padding-left:40px;cursor:pointer;border:1px solid #ccc;margin-bottom:10px;background-color:hsla(0,0%,100%,.8);color:#878a8c;position:relative}.content .left .c-l-list .c-l-item .post[data-v-2baf73fc]{align-items:center;box-sizing:border-box;display:flex;flex-direction:column;height:100%;left:0;padding:8px 4px 8px 0;position:absolute;top:0;width:40px;border-left:4px solid transparent;background:#f8f9fa}.content .left .c-l-list .c-l-item .post .iconfont[data-v-2baf73fc]{margin-right:0}.content .left .c-l-list .c-l-item .post .down[data-v-2baf73fc]{transform:scaleY(-1)}.content .left .c-l-list .c-l-item .post .text[data-v-2baf73fc]{color:#1a1a1b;font-size:12px;font-weight:700;line-height:16px;pointer-events:none;word-break:normal}.content .left .c-l-list .c-l-item .l-container[data-v-2baf73fc]{padding:15px}.content .left .c-l-list .c-l-item .l-container .con-title[data-v-2baf73fc]{color:#000;font-size:18px;font-weight:500;line-height:22px;text-decoration:none;word-break:break-word}.content .left .c-l-list .c-l-item .l-container .con-memo[data-v-2baf73fc]{margin-top:10px;margin-bottom:10px}.content .left .c-l-list .c-l-item .l-container .con-cover[data-v-2baf73fc]{height:512px;width:100%;background:url(https://timgsa.baidu.com/timg?di=7e9061211c23e3ed9f0c4375bb3822dc&image=&imgtype=0&quality=80&sec=1585999647247&size=b9999_10000&src=http%3A%2F%2Fi1.hdslb.com%2Fbfs%2Farchive%2F04d8cda08e170f4a58c18c45a93c539375c22162.jpg) no-repeat;background-size:cover;margin-top:10px;margin-bottom:10px}.content .left .c-l-list .c-l-item .l-container .user-btn[data-v-2baf73fc]{font-size:14px;display:flex;display:-webkit-flex}.content .left .c-l-list .c-l-item .l-container .user-btn .btn-item[data-v-2baf73fc]{display:flex;display:-webkit-flex;margin-right:10px}.content .left .c-l-list .c-l-item .l-container .user-btn .btn-item .iconfont[data-v-2baf73fc]{margin-right:4px}.content .right[data-v-2baf73fc]{width:312px;margin-left:24px;margin-top:28px}.content .right .communities[data-v-2baf73fc]{background-color:#fff;color:#1a1a1b;border:1px solid #ccc;border-radius:4px;overflow:visible;word-wrap:break-word;margin-bottom:20px}.content .right .communities .r-c-title[data-v-2baf73fc]{background-image:linear-gradient(0deg,rgba(0,0,0,.3),transparent);background-color:#0079d3;height:80px;width:100%;color:#fff;font-size:20px;line-height:120px;padding-left:10px;box-sizing:border-box;text-align:center}.content .right .communities .r-c-content .r-c-item[data-v-2baf73fc]{align-items:center;display:flex;display:-webkit-flex;height:48px;padding:0 12px;border-bottom:thin solid #edeff1;font-size:14px}.content .right .communities .r-c-content .r-c-item .index[data-v-2baf73fc]{width:20px;color:#1c1c1c;font-size:14px;font-weight:500;line-height:18px}.content .right .communities .r-c-content .r-c-item .icon[data-v-2baf73fc]{width:32px;height:32px;background-image:url(../../static/img/avatar.7b0a9835.png);background-repeat:no-repeat;background-size:cover;margin-right:20px}.content .right .communities .r-c-content .r-c-item[data-v-2baf73fc]:last-child{border-bottom:none}.content .right .communities .view-all[data-v-2baf73fc]{background-color:#0079d3;border:1px solid transparent;border-radius:4px;box-sizing:border-box;text-align:center;letter-spacing:1px;text-decoration:none;font-size:12px;font-weight:700;letter-spacing:.5px;line-height:24px;text-transform:uppercase;padding:3px 0;width:280px;color:#fff;margin:20px 0 20px 16px}.content .right .r-trending[data-v-2baf73fc]{padding-top:16px;width:312px;background-color:#fff;color:#1a1a1b;fill:#1a1a1b;border:1px solid #ccc;border-radius:4px;overflow:visible;word-wrap:break-word}.content .right .r-trending .r-t-title[data-v-2baf73fc]{font-size:10px;font-weight:700;letter-spacing:.5px;line-height:12px;text-transform:uppercase;background-color:#fff;border-radius:3px 3px 0 0;color:#1a1a1b;display:flex;fill:#1a1a1b;padding:0 12px 12px}.content .right .r-trending .rank[data-v-2baf73fc]{padding:12px}.content .right .r-trending .rank .r-t-cell[data-v-2baf73fc]{display:flex;display:-webkit-flex;align-items:center;justify-content:space-between;margin-bottom:16px}.content .right .r-trending .rank .r-t-cell .r-t-cell-info[data-v-2baf73fc]{display:flex}.content .right .r-trending .rank .r-t-cell .avatar[data-v-2baf73fc]{width:32px;height:32px;background:url(../../static/img/avatar.7b0a9835.png) no-repeat;background-size:cover;margin-right:10px}.content .right .r-trending .rank .r-t-cell .info[data-v-2baf73fc]{margin-right:10px}.content .right .r-trending .rank .r-t-cell .info .info-title[data-v-2baf73fc]{font-size:12px;font-weight:500;line-height:16px;text-overflow:ellipsis;width:144px}.content .right .r-trending .rank .r-t-cell .info .info-num[data-v-2baf73fc]{font-size:12px;font-weight:400;line-height:16px;padding-bottom:4px}.content .right .r-trending .rank .r-t-cell .join-btn[data-v-2baf73fc]{width:106px;height:32px;line-height:32px;background-color:#0079d3;color:#fff;border:1px solid transparent;border-radius:4px;box-sizing:border-box;text-align:center}.content[data-v-4466eba1]{max-width:100%;box-sizing:border-box;display:flex;flex-direction:row;justify-content:center;margin:0 auto;padding:20px 24px;margin-top:48px}.content .left[data-v-4466eba1]{flex-grow:1;max-width:740px;border-radius:4px;word-break:break-word;background:#fff;border:#edeff1;flex:1;margin:32px;margin-right:12px;padding-bottom:30px;position:relative}.content .left .container[data-v-4466eba1]{width:100%;height:auto;position:relative}.content .left .container .post[data-v-4466eba1]{align-items:center;box-sizing:border-box;display:flex;flex-direction:column;height:100%;left:0;padding:8px 4px 8px 0;position:absolute;top:0;width:40px;border-left:4px solid transparent}.content .left .container .post .text[data-v-4466eba1]{color:#1a1a1b;font-size:12px;font-weight:700;line-height:16px;pointer-events:none;word-break:normal}.content .left .container .l-container[data-v-4466eba1]{padding:15px;margin-left:40px}.content .left .container .l-container .con-title[data-v-4466eba1]{color:#000;font-size:18px;font-weight:500;line-height:22px;text-decoration:none;word-break:break-word}.content .left .container .l-container .con-info[data-v-4466eba1]{margin:25px 0;padding:15px 0;border-bottom:1px solid grey}.content .left .container .l-container .con-cover[data-v-4466eba1]{height:512px;width:100%;background:url(https://timgsa.baidu.com/timg?di=7e9061211c23e3ed9f0c4375bb3822dc&image=&imgtype=0&quality=80&sec=1585999647247&size=b9999_10000&src=http%3A%2F%2Fi1.hdslb.com%2Fbfs%2Farchive%2F04d8cda08e170f4a58c18c45a93c539375c22162.jpg) no-repeat;background-size:cover;margin-top:10px;margin-bottom:10px}.content .left .container .l-container .user-btn[data-v-4466eba1]{font-size:12px;display:flex;display:-webkit-flex}.content .left .container .l-container .user-btn .btn-item[data-v-4466eba1]{display:flex;display:-webkit-flex;align-items:center;margin-right:10px}.content .left .container .l-container .user-btn .btn-item .iconfont[data-v-4466eba1]{margin-right:4px}.content .left .comment[data-v-4466eba1]{width:100%;height:auto;position:relative}.content .left .comment .c-left .line[data-v-4466eba1]{border-right:2px solid #edeff1;height:100%;position:absolute;left:20px}.content .left .comment .c-left .c-arrow[data-v-4466eba1]{display:flex;display:-webkit-flex;position:absolute;z-index:2;flex-direction:column;left:12px;background:#fff;padding-bottom:5px}.content .left .comment .c-right[data-v-4466eba1]{margin-left:40px;padding-right:10px}.content .left .comment .c-right .c-user-info[data-v-4466eba1]{margin-bottom:10px}.content .left .comment .c-right .c-user-info .name[data-v-4466eba1]{color:#1c1c1c;font-size:12px;font-weight:400;line-height:16px}.content .left .comment .c-right .c-user-info .num[data-v-4466eba1]{padding-left:4px;font-size:12px;font-weight:400;line-height:16px;color:#7c7c7c}.content .left .comment .c-right .c-content[data-v-4466eba1]{font-family:Noto Sans,Arial,sans-serif;font-size:14px;font-weight:400;line-height:21px;word-break:break-word;color:#1a1a1b}.content .right[data-v-4466eba1]{flex-grow:0;width:312px;margin-top:32px}.content .right .topic-info[data-v-4466eba1]{width:100%;cursor:pointer;background-color:#fff;color:#1a1a1b;border:1px solid #ccc;border-radius:4px;overflow:visible;word-wrap:break-word;padding-bottom:30px}.content .right .topic-info .t-header[data-v-4466eba1]{width:100%;height:34px;background:#0079d3}.content .right .topic-info .t-info[data-v-4466eba1]{padding:0 12px;display:flex;display:-webkit-flex;width:100%;height:54px;align-items:center}.content .right .topic-info .t-info .avatar[data-v-4466eba1]{width:54px;height:54px;background:url(../../static/img/avatar.7b0a9835.png) no-repeat;background-size:cover;margin-right:10px}.content .right .topic-info .t-info .topic-name[data-v-4466eba1]{height:100%;line-height:54px;font-size:16px;font-weight:500}.content .right .topic-info .t-desc[data-v-4466eba1]{font-family:Noto Sans,Arial,sans-serif;font-size:14px;line-height:21px;font-weight:400;word-wrap:break-word;margin-bottom:8px;padding:0 12px}.content .right .topic-info .t-num[data-v-4466eba1]{padding:0 12px 20px 12px;display:flex;display:-webkit-flex;align-items:center;border-bottom:1px solid #edeff1}.content .right .topic-info .t-num .t-num-item[data-v-4466eba1]{list-style:none;display:flex;display:-webkit-flex;flex-direction:column;width:50%}.content .right .topic-info .t-num .t-num-item .number[data-v-4466eba1]{font-size:16px;font-weight:500;line-height:20px}.content .right .topic-info .t-num .t-num-item .unit[data-v-4466eba1]{font-size:12px;font-weight:500;line-height:16px;word-break:break-word}.content .right .topic-info .date[data-v-4466eba1]{font-family:Noto Sans,Arial,sans-serif;font-size:14px;font-weight:400;line-height:18px;margin-top:20px;padding:0 12px}.content .right .topic-info .topic-btn[data-v-4466eba1]{width:286px;height:34px;line-height:34px;color:#fff;margin:12px auto 0 auto;background:#003f6d;border-radius:4px;box-sizing:border-box;margin-left:13px}.content[data-v-7a22bae9]{max-width:100%;box-sizing:border-box;display:flex;flex-direction:row;justify-content:center;margin:0 auto;padding:20px 24px;margin-top:48px}.content .left[data-v-7a22bae9]{flex-grow:1;max-width:740px;word-break:break-word;flex:1;margin:32px;margin-right:12px;padding-bottom:30px;position:relative}.content .left .post-name[data-v-7a22bae9]{padding:4px;margin:16px 0;border-bottom:1px solid #edeff1;display:flex;justify-content:space-between}.content .left .post-name .p-btn[data-v-7a22bae9]{font-size:12px;font-weight:700;letter-spacing:.5px;line-height:24px;text-transform:uppercase;border:none;padding:0;margin-left:10px;color:#0079d3}.content .left .post-name .p-num[data-v-7a22bae9]{font-size:12px;font-weight:400;line-height:16px;background:#878a8c;border-radius:2px;color:#fff;margin-left:4px;padding:1px 3px}.content .left .post-type[data-v-7a22bae9]{box-sizing:border-box;width:300px;height:40px;border-radius:4px;transition:box-shadow .2s ease;box-shadow:0 0 0 0 #fff;border:1px solid #edeff1;background-color:#fff;padding-left:10px;position:relative}.content .left .post-type .post-type-value[data-v-7a22bae9]{font-size:14px;font-weight:500;line-height:40px;width:100%;vertical-align:middle;color:#1c1c1c;background-color:transparent;cursor:pointer}.content .left .post-type .post-type-options[data-v-7a22bae9]{position:absolute;width:100%;background-color:#fff;left:0;z-index:1;border-radius:4px}.content .left .post-type .post-type-options .post-type-cell[data-v-7a22bae9]{margin:14px 8px 5px;font-size:14px;list-style:none;border-bottom:1px solid #edeff1;padding-bottom:8px;color:#1c1c1c;cursor:pointer}.content .left .post-type .p-icon[data-v-7a22bae9]{width:0;height:0;border-top:5px solid #878a8c;border-right:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid transparent;margin-left:10px;position:absolute;top:50%;right:10px;cursor:pointer}.content .left .post-content[data-v-7a22bae9]{background-color:#fff;margin:10px 0;padding-bottom:15px;border-radius:5px}.content .left .post-content .cat[data-v-7a22bae9]{display:flex;display:-webkit-flex;justify-content:space-between;align-items:center;width:100%;height:53px}.content .left .post-content .cat .cat-item[data-v-7a22bae9]{padding:10px 0;width:50%;height:40px;line-height:40px;text-align:center;list-style:none;border-bottom:1px solid #edeff1;border-right:1px solid #edeff1;color:#878a8c}.content .left .post-content .cat .cat-item .iconfont[data-v-7a22bae9]{margin-right:4px}.content .left .post-content .cat .active[data-v-7a22bae9]{color:#0079d3;font-weight:bolder;background:none}.content .left .post-content .post-sub-container[data-v-7a22bae9]{padding:16px}.content .left .post-content .post-sub-container .post-sub-header[data-v-7a22bae9]{position:relative}.content .left .post-content .post-sub-container .post-sub-header .post-title[data-v-7a22bae9]{resize:none;box-sizing:border-box;overflow:hidden;display:block;width:100%;height:40px;padding:0 0 0 10px;outline:none;border:1px solid #edeff1;border-radius:4px;color:#1c1c1c;font-size:14px;font-weight:400;line-height:40px}.content .left .post-content .post-sub-container .post-sub-header .textarea-num[data-v-7a22bae9]{font-size:10px;font-weight:700;letter-spacing:.5px;line-height:12px;text-transform:uppercase;bottom:12px;color:#878a8c;pointer-events:none;position:absolute;right:12px}.content .left .post-content .post-sub-container .post-text-con[data-v-7a22bae9]{width:100%;height:200px;border:1px solid #edeff1;margin-top:20px}.content .left .post-content .post-sub-container .post-text-con .post-content-t[data-v-7a22bae9]{resize:none;box-sizing:border-box;overflow:hidden;display:block;width:100%;height:200px;padding:12px 8px;outline:none;border:1px solid #edeff1;border-radius:4px;color:#1c1c1c;font-size:14px;font-weight:400;line-height:21px}.content .left .post-content .post-footer[data-v-7a22bae9]{display:flex;display:-webkit-flex;margin:0 16px;justify-content:flex-end}.content .left .post-content .post-footer .sign[data-v-7a22bae9]{display:flex;display:-webkit-flex}.content .left .post-content .post-footer .sign .sign-item[data-v-7a22bae9]{list-style:none;padding:5px 8px;border:1px solid #edeff1;margin-right:10px;color:#878a8c;font-size:12px;font-weight:700}.content .left .post-content .post-footer .btns .btn[data-v-7a22bae9]{border:1px solid transparent;border-radius:4px;box-sizing:border-box;text-align:center;text-decoration:none;font-size:12px;font-weight:700;letter-spacing:.5px;line-height:24px;text-transform:uppercase;padding:3px 16px;background:#0079d3;color:#fff;margin-left:8px;cursor:pointer}.content .left .post-content .alias[data-v-7a22bae9]{background-color:#f6f7f8;border-radius:0 0 6px 6px;border-top:1px solid #edeff1;display:flex;flex-flow:column;padding:8px 16px 21px;position:relative}.content .left .post-content .alias .send-post[data-v-7a22bae9]{font-size:14px;font-weight:500;line-height:18px;color:#1c1c1c;margin-right:4px}.content .left .post-content .alias .connect[data-v-7a22bae9]{font-size:14px;font-weight:500;line-height:18px;color:#0079d3;display:block;margin-right:4px;margin-top:10px}.content .right[data-v-7a22bae9]{flex-grow:0;width:312px;margin-top:62px}.content .right .post-rank[data-v-7a22bae9]{background-color:#fff;border-radius:4px;margin-top:15px;padding:12px}.content .right .post-rank .p-r-title[data-v-7a22bae9]{display:flex;display:-webkit-flex;font-size:16px;font-weight:500;line-height:20px;align-items:center;border-bottom:1px solid #edeff1;color:#1c1c1c;padding-bottom:10px}.content .right .post-rank .p-r-title .p-r-icon[data-v-7a22bae9]{width:40px;height:40px;background:url(../../static/img/avatar.7b0a9835.png) no-repeat;background-size:cover;margin-right:10px}.content .right .post-rank .p-r-content[data-v-7a22bae9]{display:flex;display:-webkit-flex;flex-direction:column}.content .right .post-rank .p-r-content .p-r-item[data-v-7a22bae9]{list-style:none;border-bottom:1px solid #edeff1;color:#1c1c1c;padding:10px 5px}.main[data-v-5d78ac92]{background:#f8f8f8;padding:150px 0}.main .container[data-v-5d78ac92]{width:600px;background:#fff;margin:0 auto;max-width:1200px;padding:20px}.main .container .form-title[data-v-5d78ac92]{margin-bottom:33px;text-align:center}.main .container .form-group[data-v-5d78ac92]{margin:15px}.main .container .form-group label[data-v-5d78ac92]{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}.main .container .form-group .form-control[data-v-5d78ac92]{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px}.main .container .form-btn[data-v-5d78ac92]{display:flex;justify-content:center}.main .container .form-btn .btn[data-v-5d78ac92]{padding:6px 20px;font-size:18px;line-height:1.3333333;border-radius:6px;display:inline-block;margin-bottom:0;font-weight:400;text-align:center;white-space:nowrap;vertical-align:middle;touch-action:manipulation;cursor:pointer;border:1px solid transparent}.main .container .form-btn .btn-info[data-v-5d78ac92]{color:#fff;background-color:#5bc0de;border-color:#46b8da}.main[data-v-61f1d9ff]{background:#f8f8f8;padding:150px 0}.main .container[data-v-61f1d9ff]{width:600px;background:#fff;margin:0 auto;max-width:1200px;padding:20px}.main .container .form-title[data-v-61f1d9ff]{margin-bottom:33px;text-align:center}.main .container .form-group[data-v-61f1d9ff]{margin:15px}.main .container .form-group label[data-v-61f1d9ff]{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}.main .container .form-group .form-control[data-v-61f1d9ff]{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px}.main .container .form-btn[data-v-61f1d9ff]{display:flex;justify-content:center}.main .container .form-btn .btn[data-v-61f1d9ff]{padding:6px 20px;font-size:18px;line-height:1.3333333;border-radius:6px;display:inline-block;margin-bottom:0;font-weight:400;text-align:center;white-space:nowrap;vertical-align:middle;touch-action:manipulation;cursor:pointer;border:1px solid transparent}.main .container .form-btn .btn-info[data-v-61f1d9ff]{color:#fff;background-color:#5bc0de;border-color:#46b8da}"
  },
  {
    "path": "lesson77/bluebell/static/js/app.ea0d453e.js",
    "content": "(function(t){function s(s){for(var n,o,c=s[0],r=s[1],l=s[2],m=0,p=[];m<c.length;m++)o=c[m],Object.prototype.hasOwnProperty.call(i,o)&&i[o]&&p.push(i[o][0]),i[o]=0;for(n in r)Object.prototype.hasOwnProperty.call(r,n)&&(t[n]=r[n]);u&&u(s);while(p.length)p.shift()();return a.push.apply(a,l||[]),e()}function e(){for(var t,s=0;s<a.length;s++){for(var e=a[s],n=!0,c=1;c<e.length;c++){var r=e[c];0!==i[r]&&(n=!1)}n&&(a.splice(s--,1),t=o(o.s=e[0]))}return t}var n={},i={app:0},a=[];function o(s){if(n[s])return n[s].exports;var e=n[s]={i:s,l:!1,exports:{}};return t[s].call(e.exports,e,e.exports,o),e.l=!0,e.exports}o.m=t,o.c=n,o.d=function(t,s,e){o.o(t,s)||Object.defineProperty(t,s,{enumerable:!0,get:e})},o.r=function(t){\"undefined\"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:\"Module\"}),Object.defineProperty(t,\"__esModule\",{value:!0})},o.t=function(t,s){if(1&s&&(t=o(t)),8&s)return t;if(4&s&&\"object\"===typeof t&&t&&t.__esModule)return t;var e=Object.create(null);if(o.r(e),Object.defineProperty(e,\"default\",{enumerable:!0,value:t}),2&s&&\"string\"!=typeof t)for(var n in t)o.d(e,n,function(s){return t[s]}.bind(null,n));return e},o.n=function(t){var s=t&&t.__esModule?function(){return t[\"default\"]}:function(){return t};return o.d(s,\"a\",s),s},o.o=function(t,s){return Object.prototype.hasOwnProperty.call(t,s)},o.p=\"/\";var c=window[\"webpackJsonp\"]=window[\"webpackJsonp\"]||[],r=c.push.bind(c);c.push=s,c=c.slice();for(var l=0;l<c.length;l++)s(c[l]);var u=r;a.push([0,\"chunk-vendors\"]),e()})({0:function(t,s,e){t.exports=e(\"56d7\")},2395:function(t,s,e){},3315:function(t,s,e){},5159:function(t,s,e){\"use strict\";var n=e(\"5ffa\"),i=e.n(n);i.a},\"56d7\":function(t,s,e){\"use strict\";e.r(s);e(\"e260\"),e(\"e6cf\"),e(\"cca6\"),e(\"a79d\");var n=e(\"2b0e\"),i=function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"div\",{attrs:{id:\"app\"}},[e(\"div\",{staticClass:\"page\"},[e(\"HeadBar\"),e(\"router-view\")],1)])},a=[],o=function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"header\",{staticClass:\"header\"},[e(\"span\",{staticClass:\"logo\",on:{click:t.goIndex}},[t._v(\"bluebell\")]),t._m(0),e(\"div\",{staticClass:\"btns\"},[e(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:!t.isLogin,expression:\"!isLogin\"}]},[e(\"a\",{staticClass:\"login-btn\",on:{click:t.goLogin}},[t._v(\"登录\")]),e(\"a\",{staticClass:\"login-btn\",on:{click:t.goSignUp}},[t._v(\"注册\")])]),e(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:t.isLogin,expression:\"isLogin\"}],staticClass:\"user-box\"},[e(\"span\",{staticClass:\"user\"},[t._v(t._s(t.currUsername))]),e(\"div\",{staticClass:\"dropdown-content\"},[e(\"a\",{on:{click:t.goLogout}},[t._v(\"登出\")])])])])])},c=[function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"div\",{staticClass:\"search\"},[e(\"label\",{staticClass:\"s-logo\"}),e(\"input\",{staticClass:\"s-input\",attrs:{type:\"text\",placeholder:\"搜索\"}})])}],r={name:\"HeadBar\",created:function(){this.$store.commit(\"init\")},computed:{isLogin:function(){return this.$store.getters.isLogin},currUsername:function(){return console.log(this.$store.getters.username),this.$store.getters.username}},methods:{goIndex:function(){this.$router.push({name:\"Home\"})},goLogin:function(){this.$router.push({name:\"Login\"})},goSignUp:function(){this.$router.push({name:\"SignUp\"})},goLogout:function(){this.$store.commit(\"logout\")}}},l=r,u=(e(\"aee9\"),e(\"2877\")),m=Object(u[\"a\"])(l,o,c,!1,null,\"75d7d08c\",null),p=m.exports,d={components:{HeadBar:p}},v=d,f=(e(\"7c55\"),Object(u[\"a\"])(v,i,a,!1,null,null,null)),h=f.exports,C=e(\"8c4f\"),g=function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"div\",{staticClass:\"content\"},[e(\"div\",{staticClass:\"left\"},[e(\"div\",{staticClass:\"c-l-header\"},[e(\"div\",{staticClass:\"new btn-iconfont\",class:{active:t.timeOrder},on:{click:function(s){return t.selectOrder(\"time\")}}},[e(\"i\",{staticClass:\"iconfont icon-polygonred\"}),t._v(\"New \")]),e(\"div\",{staticClass:\"top btn-iconfont\",class:{active:t.scoreOrder},on:{click:function(s){return t.selectOrder(\"score\")}}},[e(\"i\",{staticClass:\"iconfont icon-top\"}),t._v(\"Score \")]),e(\"button\",{staticClass:\"btn-publish\",on:{click:t.goPublish}},[t._v(\"发表\")])]),e(\"ul\",{staticClass:\"c-l-list\"},t._l(t.postList,(function(s){return e(\"li\",{key:s.id,staticClass:\"c-l-item\"},[e(\"div\",{staticClass:\"post\"},[e(\"a\",{staticClass:\"vote\"},[e(\"span\",{staticClass:\"iconfont icon-up\",on:{click:function(e){return t.vote(s.id,\"1\")}}})]),e(\"span\",{staticClass:\"text\"},[t._v(t._s(s.vote_num))]),e(\"a\",{staticClass:\"vote\"},[e(\"span\",{staticClass:\"iconfont icon-down\",on:{click:function(e){return t.vote(s.id,\"-1\")}}})])]),e(\"div\",{staticClass:\"l-container\",on:{click:function(e){return t.goDetail(s.id)}}},[e(\"h4\",{staticClass:\"con-title\"},[t._v(t._s(s.title))]),e(\"div\",{staticClass:\"con-memo\"},[e(\"p\",[t._v(t._s(s.content))])])])])})),0)]),t._m(0)])},_=[function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"div\",{staticClass:\"right\"},[e(\"div\",{staticClass:\"communities\"},[e(\"h2\",{staticClass:\"r-c-title\"},[t._v(\"今日火热频道排行榜\")]),e(\"ul\",{staticClass:\"r-c-content\"},[e(\"li\",{staticClass:\"r-c-item\"},[e(\"span\",{staticClass:\"index\"},[t._v(\"1\")]),e(\"i\",{staticClass:\"icon\"}),t._v(\" b/coding \")]),e(\"li\",{staticClass:\"r-c-item\"},[e(\"span\",{staticClass:\"index\"},[t._v(\"2\")]),e(\"i\",{staticClass:\"icon\"}),t._v(\" b/tree_hole \")]),e(\"li\",{staticClass:\"r-c-item\"},[e(\"span\",{staticClass:\"index\"},[t._v(\"3\")]),e(\"i\",{staticClass:\"icon\"}),t._v(\" b/job \")])]),e(\"button\",{staticClass:\"view-all\"},[t._v(\"查看所有\")])]),e(\"div\",{staticClass:\"r-trending\"},[e(\"h2\",{staticClass:\"r-t-title\"},[t._v(\"持续热门频道\")]),e(\"ul\",{staticClass:\"rank\"},[e(\"li\",{staticClass:\"r-t-cell\"},[e(\"div\",{staticClass:\"r-t-cell-info\"},[e(\"div\",{staticClass:\"avatar\"}),e(\"div\",{staticClass:\"info\"},[e(\"span\",{staticClass:\"info-title\"},[t._v(\"b/Book\")]),e(\"p\",{staticClass:\"info-num\"},[t._v(\"7.1k members\")])])]),e(\"button\",{staticClass:\"join-btn\"},[t._v(\"JOIN\")])]),e(\"li\",{staticClass:\"r-t-cell\"},[e(\"div\",{staticClass:\"r-t-cell-info\"},[e(\"div\",{staticClass:\"avatar\"}),e(\"div\",{staticClass:\"info\"},[e(\"span\",{staticClass:\"info-title\"},[t._v(\"b/coding\")]),e(\"p\",{staticClass:\"info-num\"},[t._v(\"3.2k members\")])])]),e(\"button\",{staticClass:\"join-btn\"},[t._v(\"JOIN\")])]),e(\"li\",{staticClass:\"r-t-cell\"},[e(\"div\",{staticClass:\"r-t-cell-info\"},[e(\"div\",{staticClass:\"avatar\"}),e(\"div\",{staticClass:\"info\"},[e(\"span\",{staticClass:\"info-title\"},[t._v(\"b/job\")]),e(\"p\",{staticClass:\"info-num\"},[t._v(\"2.5k members\")])])]),e(\"button\",{staticClass:\"join-btn\"},[t._v(\"JOIN\")])])])])])}],b={name:\"Home\",components:{},data:function(){return{order:\"time\",page:1,postList:[]}},methods:{selectOrder:function(t){this.order=t,this.getPostList()},goPublish:function(){this.$router.push({name:\"Publish\"})},goDetail:function(t){this.$router.push({name:\"Content\",params:{id:t}})},getPostList:function(){var t=this;this.$axios({method:\"get\",url:\"/posts2\",params:{page:this.page,order:this.order}}).then((function(s){console.log(s.data,222),1e3==s.code?t.postList=s.data:console.log(s.msg)})).catch((function(t){console.log(t)}))},vote:function(t,s){this.$axios({method:\"post\",url:\"/vote\",data:JSON.stringify({post_id:t,direction:s})}).then((function(t){1e3==t.code?console.log(\"vote success\"):console.log(t.msg)})).catch((function(t){console.log(t)}))}},mounted:function(){this.getPostList()},computed:{timeOrder:function(){return\"time\"==this.order},scoreOrder:function(){return\"score\"==this.order}}},y=b,w=(e(\"97e5\"),Object(u[\"a\"])(y,g,_,!1,null,\"2baf73fc\",null)),x=w.exports,$=function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"div\",{staticClass:\"content\"},[e(\"div\",{staticClass:\"left\"},[e(\"div\",{staticClass:\"container\"},[t._m(0),e(\"div\",{staticClass:\"l-container\"},[e(\"h4\",{staticClass:\"con-title\"},[t._v(t._s(t.post.title))]),e(\"div\",{staticClass:\"con-info\"},[t._v(t._s(t.post.content))]),t._m(1)])])]),e(\"div\",{staticClass:\"right\"},[e(\"div\",{staticClass:\"topic-info\"},[e(\"h5\",{staticClass:\"t-header\"}),e(\"div\",{staticClass:\"t-info\"},[e(\"a\",{staticClass:\"avatar\"}),e(\"span\",{staticClass:\"topic-name\"},[t._v(\"b/\"+t._s(t.post.community_name))])]),e(\"p\",{staticClass:\"t-desc\"},[t._v(\"树洞 树洞 无限树洞的树洞\")]),t._m(2),e(\"div\",{staticClass:\"date\"},[t._v(\"Created Apr 10, 2008\")]),e(\"button\",{staticClass:\"topic-btn\"},[t._v(\"JOIN\")])])])])},O=[function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"div\",{staticClass:\"post\"},[e(\"a\",{staticClass:\"vote\"},[e(\"span\",{staticClass:\"iconfont icon-up\"})]),e(\"span\",{staticClass:\"text\"},[t._v(\"50.2k\")]),e(\"a\",{staticClass:\"vote\"},[e(\"span\",{staticClass:\"iconfont icon-down\"})])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"div\",{staticClass:\"user-btn\"},[e(\"span\",{staticClass:\"btn-item\"},[e(\"i\",{staticClass:\"iconfont icon-comment\"}),t._v(\"comment \")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"ul\",{staticClass:\"t-num\"},[e(\"li\",{staticClass:\"t-num-item\"},[e(\"p\",{staticClass:\"number\"},[t._v(\"5.2m\")]),e(\"span\",{staticClass:\"unit\"},[t._v(\"Members\")])]),e(\"li\",{staticClass:\"t-num-item\"},[e(\"p\",{staticClass:\"number\"},[t._v(\"5.2m\")]),e(\"span\",{staticClass:\"unit\"},[t._v(\"Members\")])])])}],L={name:\"Content\",data:function(){return{post:{}}},methods:{getPostDetail:function(){var t=this;this.$axios({method:\"get\",url:\"/post/\"+this.$route.params.id}).then((function(s){console.log(1,s.data),1e3==s.code?t.post=s.data:console.log(s.msg)})).catch((function(t){console.log(t)}))}},mounted:function(){this.getPostDetail()}},k=L,P=(e(\"f6e4\"),Object(u[\"a\"])(k,$,O,!1,null,\"4466eba1\",null)),S=P.exports,j=function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"div\",{staticClass:\"content\"},[e(\"div\",{staticClass:\"left\"},[e(\"div\",{staticClass:\"post-name\"},[t._v(\"我好想写点什么\")]),e(\"div\",{staticClass:\"post-type\"},[e(\"input\",{directives:[{name:\"model\",rawName:\"v-model\",value:t.selectCommunity.community_name,expression:\"selectCommunity.community_name\"}],staticClass:\"post-type-value\",attrs:{type:\"text\",placeholder:\"选择一个频道\"},domProps:{value:t.selectCommunity.community_name},on:{click:function(s){return t.showCommunity()},input:function(s){s.target.composing||t.$set(t.selectCommunity,\"community_name\",s.target.value)}}}),e(\"ul\",{directives:[{name:\"show\",rawName:\"v-show\",value:t.showCommunityList,expression:\"showCommunityList\"}],staticClass:\"post-type-options\"},t._l(t.communityList,(function(s,n){return e(\"li\",{key:s.community_id,staticClass:\"post-type-cell\",on:{click:function(s){return t.selected(n)}}},[t._v(\" \"+t._s(s.community_name)+\" \")])})),0),e(\"i\",{staticClass:\"p-icon\"})]),e(\"div\",{staticClass:\"post-content\"},[t._m(0),e(\"div\",{staticClass:\"post-sub-container\"},[e(\"div\",{staticClass:\"post-sub-header\"},[e(\"textarea\",{directives:[{name:\"model\",rawName:\"v-model\",value:t.title,expression:\"title\"}],staticClass:\"post-title\",attrs:{id:\"\",cols:\"30\",rows:\"10\",placeholder:\"标题\"},domProps:{value:t.title},on:{input:function(s){s.target.composing||(t.title=s.target.value)}}}),e(\"span\",{staticClass:\"textarea-num\"},[t._v(\"0/300\")])]),e(\"div\",{staticClass:\"post-text-con\"},[e(\"textarea\",{directives:[{name:\"model\",rawName:\"v-model\",value:t.content,expression:\"content\"}],staticClass:\"post-content-t\",attrs:{id:\"\",cols:\"30\",rows:\"10\",placeholder:\"内容\"},domProps:{value:t.content},on:{input:function(s){s.target.composing||(t.content=s.target.value)}}})])]),e(\"div\",{staticClass:\"post-footer\"},[e(\"div\",{staticClass:\"btns\"},[e(\"button\",{staticClass:\"btn\"},[t._v(\"取消\")]),e(\"button\",{staticClass:\"btn\",on:{click:function(s){return t.submit()}}},[t._v(\"发表\")])])])])]),t._m(1)])},N=[function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"ul\",{staticClass:\"cat\"},[e(\"li\",{staticClass:\"cat-item active\"},[e(\"i\",{staticClass:\"iconfont icon-edit\"}),t._v(\"post \")]),e(\"li\",{staticClass:\"cat-item\"},[e(\"i\",{staticClass:\"iconfont icon-image\"}),t._v(\"image/video \")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"div\",{staticClass:\"right\"},[e(\"div\",{staticClass:\"post-rank\"},[e(\"h5\",{staticClass:\"p-r-title\"},[e(\"i\",{staticClass:\"p-r-icon\"}),t._v(\"发帖规范 \")]),e(\"ul\",{staticClass:\"p-r-content\"},[e(\"li\",{staticClass:\"p-r-item\"},[t._v(\"1.网络不是法外之地\")]),e(\"li\",{staticClass:\"p-r-item\"},[t._v(\"2.网络不是法外之地\")]),e(\"li\",{staticClass:\"p-r-item\"},[t._v(\"3.网络不是法外之地\")])])])])}],E={name:\"Publish\",data:function(){return{title:\"\",content:\"\",showCommunityList:!1,selectCommunity:{},communityList:[]}},methods:{submit:function(){var t=this;this.$axios({method:\"post\",url:\"/post\",data:JSON.stringify({title:this.title,content:this.content,community_id:this.selectCommunity.community_id})}).then((function(s){console.log(s.data),1e3==s.code?t.$router.push({path:t.redirect||\"/\"}):console.log(s.msg)})).catch((function(t){console.log(t)}))},getCommunityList:function(){var t=this;this.$axios({method:\"get\",url:\"/community\"}).then((function(s){console.log(s.data),1e3==s.code?t.communityList=s.data:console.log(s.msg)})).catch((function(t){console.log(t)}))},showCommunity:function(){this.showCommunityList=!this.showCommunityList},selected:function(t){this.selectCommunity=this.communityList[t],this.showCommunityList=!1}},mounted:function(){this.getCommunityList()}},R=E,J=(e(\"5159\"),Object(u[\"a\"])(R,j,N,!1,null,\"7a22bae9\",null)),I=J.exports,U=function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"div\",{staticClass:\"main\"},[e(\"div\",{staticClass:\"container\"},[e(\"h2\",{staticClass:\"form-title\"},[t._v(\"登录\")]),e(\"div\",{staticClass:\"form-group\"},[e(\"label\",{attrs:{for:\"name\"}},[t._v(\"用户名\")]),e(\"input\",{directives:[{name:\"model\",rawName:\"v-model\",value:t.username,expression:\"username\"}],staticClass:\"form-control\",attrs:{type:\"text\",name:\"name\",id:\"name\",placeholder:\"用户名\"},domProps:{value:t.username},on:{input:function(s){s.target.composing||(t.username=s.target.value)}}})]),e(\"div\",{staticClass:\"form-group\"},[e(\"label\",{attrs:{for:\"pass\"}},[t._v(\"密码\")]),e(\"input\",{directives:[{name:\"model\",rawName:\"v-model\",value:t.password,expression:\"password\"}],staticClass:\"form-control\",attrs:{type:\"password\",name:\"pass\",id:\"pass\",placeholder:\"密码\"},domProps:{value:t.password},on:{input:function(s){s.target.composing||(t.password=s.target.value)}}})]),e(\"div\",{staticClass:\"form-btn\"},[e(\"button\",{staticClass:\"btn btn-info\",attrs:{type:\"button\"},on:{click:t.submit}},[t._v(\"提交\")])])])])},H=[],M={name:\"Login\",data:function(){return{username:\"\",password:\"\",submitted:!1}},computed:{},created:function(){},methods:{submit:function(){var t=this;this.$axios({method:\"post\",url:\"/login\",data:JSON.stringify({username:this.username,password:this.password})}).then((function(s){console.log(s.data),1e3==s.code?(localStorage.setItem(\"loginResult\",JSON.stringify(s.data)),t.$store.commit(\"login\",s.data),t.$router.push({path:t.redirect||\"/\"})):console.log(s.msg)})).catch((function(t){console.log(t)}))}}},B=M,D=(e(\"a87d\"),Object(u[\"a\"])(B,U,H,!1,null,\"5d78ac92\",null)),T=D.exports,A=function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"div\",{staticClass:\"main\"},[e(\"div\",{staticClass:\"container\"},[e(\"h2\",{staticClass:\"form-title\"},[t._v(\"注册\")]),e(\"div\",{staticClass:\"form-group\"},[e(\"label\",{attrs:{for:\"name\"}},[t._v(\"用户名\")]),e(\"input\",{directives:[{name:\"model\",rawName:\"v-model\",value:t.username,expression:\"username\"}],staticClass:\"form-control\",attrs:{type:\"text\",name:\"name\",id:\"name\",placeholder:\"用户名\"},domProps:{value:t.username},on:{input:function(s){s.target.composing||(t.username=s.target.value)}}})]),e(\"div\",{staticClass:\"form-group\"},[e(\"label\",{attrs:{for:\"pass\"}},[t._v(\"密码\")]),e(\"input\",{directives:[{name:\"model\",rawName:\"v-model\",value:t.password,expression:\"password\"}],staticClass:\"form-control\",attrs:{type:\"password\",name:\"pass\",id:\"pass\",placeholder:\"密码\"},domProps:{value:t.password},on:{input:function(s){s.target.composing||(t.password=s.target.value)}}})]),e(\"div\",{staticClass:\"form-group\"},[e(\"label\",{attrs:{for:\"re_pass\"}},[t._v(\"确认密码\")]),e(\"input\",{directives:[{name:\"model\",rawName:\"v-model\",value:t.confirm_password,expression:\"confirm_password\"}],staticClass:\"form-control\",attrs:{type:\"password\",name:\"re_pass\",id:\"re_pass\",placeholder:\"确认密码\"},domProps:{value:t.confirm_password},on:{input:function(s){s.target.composing||(t.confirm_password=s.target.value)}}})]),e(\"div\",{staticClass:\"form-btn\"},[e(\"button\",{staticClass:\"btn btn-info\",attrs:{type:\"button\"},on:{click:t.submit}},[t._v(\"提交\")])])])])},q=[],z={name:\"SignUp\",data:function(){return{username:\"\",password:\"\",confirm_password:\"\",submitted:!1}},computed:{},created:function(){},methods:{submit:function(){var t=this;this.$axios({method:\"post\",url:\"/signup\",data:JSON.stringify({username:this.username,password:this.password,confirm_password:this.confirm_password})}).then((function(s){console.log(s.data),1e3==s.code?(console.log(\"signup success\"),t.$router.push({name:\"Login\"})):console.log(s.msg)})).catch((function(t){console.log(t)}))}}},F=z,G=(e(\"fe14\"),Object(u[\"a\"])(F,A,q,!1,null,\"61f1d9ff\",null)),K=G.exports,Q=C[\"a\"].prototype.push;C[\"a\"].prototype.push=function(t){return Q.call(this,t).catch((function(t){return t}))},n[\"a\"].use(C[\"a\"]);var V=[{path:\"/\",name:\"Home\",component:x},{path:\"/post/:id\",name:\"Content\",component:S},{path:\"/publish\",name:\"Publish\",component:I},{path:\"/login\",name:\"Login\",component:T},{path:\"/signup\",name:\"SignUp\",component:K}],W=new C[\"a\"]({mode:\"history\",base:\"/\",routes:V}),X=W,Y=e(\"2f62\");n[\"a\"].use(Y[\"a\"]);var Z={token:null,user_id:null,user_name:null},tt=new Y[\"a\"].Store({state:{isLogin:!1,loginResult:Z},mutations:{init:function(t){var s=JSON.parse(localStorage.getItem(\"loginResult\"));console.log(localStorage.getItem(\"loginResult\")),null!=s&&(t.loginResult=s)},login:function(t,s){t.loginResult=s},logout:function(t){localStorage.removeItem(\"loginResult\"),t.loginResult=Z}},actions:{},getters:{isLogin:function(t){return null!==t.loginResult.user_id},userID:function(t){return t.loginResult.user_id},username:function(t){return t.loginResult.user_name},accessToken:function(t){return t.loginResult.token}}}),st=(e(\"d3b7\"),e(\"bc3a\")),et=e.n(st);et.a.defaults.baseURL=\"/api/v1/\",et.a.interceptors.request.use((function(t){var s=JSON.parse(localStorage.getItem(\"loginResult\"));if(s){var e=s.token;t.headers.Authorization=\"Bearer \".concat(e)}return t}),(function(t){return Promise.reject(t)})),et.a.interceptors.response.use((function(t){return 200===t.status?Promise.resolve(t.data):Promise.reject(t.data)}),(function(t){console.log(\"error\",t)}));var nt=et.a;n[\"a\"].prototype.$axios=nt,n[\"a\"].config.productionTip=!1,new n[\"a\"]({router:X,store:tt,render:function(t){return t(h)}}).$mount(\"#app\")},\"5ffa\":function(t,s,e){},\"7c55\":function(t,s,e){\"use strict\";var n=e(\"2395\"),i=e.n(n);i.a},\"97e5\":function(t,s,e){\"use strict\";var n=e(\"3315\"),i=e.n(n);i.a},\"9c02\":function(t,s,e){},a87d:function(t,s,e){\"use strict\";var n=e(\"d746\"),i=e.n(n);i.a},aee9:function(t,s,e){\"use strict\";var n=e(\"c255\"),i=e.n(n);i.a},b040:function(t,s,e){},c255:function(t,s,e){},d746:function(t,s,e){},f6e4:function(t,s,e){\"use strict\";var n=e(\"9c02\"),i=e.n(n);i.a},fe14:function(t,s,e){\"use strict\";var n=e(\"b040\"),i=e.n(n);i.a}});\n//# sourceMappingURL=app.ea0d453e.js.map"
  },
  {
    "path": "lesson77/bluebell/static/js/chunk-vendors.57f9e9d6.js",
    "content": "(window[\"webpackJsonp\"]=window[\"webpackJsonp\"]||[]).push([[\"chunk-vendors\"],{\"00ee\":function(t,e,n){var r=n(\"b622\"),o=r(\"toStringTag\"),i={};i[o]=\"z\",t.exports=\"[object z]\"===String(i)},\"0366\":function(t,e,n){var r=n(\"1c0b\");t.exports=function(t,e,n){if(r(t),void 0===e)return t;switch(n){case 0:return function(){return t.call(e)};case 1:return function(n){return t.call(e,n)};case 2:return function(n,r){return t.call(e,n,r)};case 3:return function(n,r,o){return t.call(e,n,r,o)}}return function(){return t.apply(e,arguments)}}},\"06cf\":function(t,e,n){var r=n(\"83ab\"),o=n(\"d1e7\"),i=n(\"5c6c\"),a=n(\"fc6a\"),s=n(\"c04e\"),c=n(\"5135\"),u=n(\"0cfb\"),f=Object.getOwnPropertyDescriptor;e.f=r?f:function(t,e){if(t=a(t),e=s(e,!0),u)try{return f(t,e)}catch(n){}if(c(t,e))return i(!o.f.call(t,e),t[e])}},\"0a06\":function(t,e,n){\"use strict\";var r=n(\"c532\"),o=n(\"30b5\"),i=n(\"f6b4\"),a=n(\"5270\"),s=n(\"4a7b\");function c(t){this.defaults=t,this.interceptors={request:new i,response:new i}}c.prototype.request=function(t){\"string\"===typeof t?(t=arguments[1]||{},t.url=arguments[0]):t=t||{},t=s(this.defaults,t),t.method?t.method=t.method.toLowerCase():this.defaults.method?t.method=this.defaults.method.toLowerCase():t.method=\"get\";var e=[a,void 0],n=Promise.resolve(t);this.interceptors.request.forEach((function(t){e.unshift(t.fulfilled,t.rejected)})),this.interceptors.response.forEach((function(t){e.push(t.fulfilled,t.rejected)}));while(e.length)n=n.then(e.shift(),e.shift());return n},c.prototype.getUri=function(t){return t=s(this.defaults,t),o(t.url,t.params,t.paramsSerializer).replace(/^\\?/,\"\")},r.forEach([\"delete\",\"get\",\"head\",\"options\"],(function(t){c.prototype[t]=function(e,n){return this.request(r.merge(n||{},{method:t,url:e}))}})),r.forEach([\"post\",\"put\",\"patch\"],(function(t){c.prototype[t]=function(e,n,o){return this.request(r.merge(o||{},{method:t,url:e,data:n}))}})),t.exports=c},\"0cfb\":function(t,e,n){var r=n(\"83ab\"),o=n(\"d039\"),i=n(\"cc12\");t.exports=!r&&!o((function(){return 7!=Object.defineProperty(i(\"div\"),\"a\",{get:function(){return 7}}).a}))},\"0df6\":function(t,e,n){\"use strict\";t.exports=function(t){return function(e){return t.apply(null,e)}}},\"19aa\":function(t,e){t.exports=function(t,e,n){if(!(t instanceof e))throw TypeError(\"Incorrect \"+(n?n+\" \":\"\")+\"invocation\");return t}},\"1be4\":function(t,e,n){var r=n(\"d066\");t.exports=r(\"document\",\"documentElement\")},\"1c0b\":function(t,e){t.exports=function(t){if(\"function\"!=typeof t)throw TypeError(String(t)+\" is not a function\");return t}},\"1c7e\":function(t,e,n){var r=n(\"b622\"),o=r(\"iterator\"),i=!1;try{var a=0,s={next:function(){return{done:!!a++}},return:function(){i=!0}};s[o]=function(){return this},Array.from(s,(function(){throw 2}))}catch(c){}t.exports=function(t,e){if(!e&&!i)return!1;var n=!1;try{var r={};r[o]=function(){return{next:function(){return{done:n=!0}}}},t(r)}catch(c){}return n}},\"1cdc\":function(t,e,n){var r=n(\"342f\");t.exports=/(iphone|ipod|ipad).*applewebkit/i.test(r)},\"1d2b\":function(t,e,n){\"use strict\";t.exports=function(t,e){return function(){for(var n=new Array(arguments.length),r=0;r<n.length;r++)n[r]=arguments[r];return t.apply(e,n)}}},\"1d80\":function(t,e){t.exports=function(t){if(void 0==t)throw TypeError(\"Can't call method on \"+t);return t}},2266:function(t,e,n){var r=n(\"825a\"),o=n(\"e95a\"),i=n(\"50c4\"),a=n(\"0366\"),s=n(\"35a1\"),c=n(\"9bdd\"),u=function(t,e){this.stopped=t,this.result=e},f=t.exports=function(t,e,n,f,l){var p,d,h,v,y,m,g,b=a(e,n,f?2:1);if(l)p=t;else{if(d=s(t),\"function\"!=typeof d)throw TypeError(\"Target is not iterable\");if(o(d)){for(h=0,v=i(t.length);v>h;h++)if(y=f?b(r(g=t[h])[0],g[1]):b(t[h]),y&&y instanceof u)return y;return new u(!1)}p=d.call(t)}m=p.next;while(!(g=m.call(p)).done)if(y=c(p,b,g.value,f),\"object\"==typeof y&&y&&y instanceof u)return y;return new u(!1)};f.stop=function(t){return new u(!0,t)}},\"23cb\":function(t,e,n){var r=n(\"a691\"),o=Math.max,i=Math.min;t.exports=function(t,e){var n=r(t);return n<0?o(n+e,0):i(n,e)}},\"23e7\":function(t,e,n){var r=n(\"da84\"),o=n(\"06cf\").f,i=n(\"9112\"),a=n(\"6eeb\"),s=n(\"ce4e\"),c=n(\"e893\"),u=n(\"94ca\");t.exports=function(t,e){var n,f,l,p,d,h,v=t.target,y=t.global,m=t.stat;if(f=y?r:m?r[v]||s(v,{}):(r[v]||{}).prototype,f)for(l in e){if(d=e[l],t.noTargetGet?(h=o(f,l),p=h&&h.value):p=f[l],n=u(y?l:v+(m?\".\":\"#\")+l,t.forced),!n&&void 0!==p){if(typeof d===typeof p)continue;c(d,p)}(t.sham||p&&p.sham)&&i(d,\"sham\",!0),a(f,l,d,t)}}},\"241c\":function(t,e,n){var r=n(\"ca84\"),o=n(\"7839\"),i=o.concat(\"length\",\"prototype\");e.f=Object.getOwnPropertyNames||function(t){return r(t,i)}},2444:function(t,e,n){\"use strict\";(function(e){var r=n(\"c532\"),o=n(\"c8af\"),i={\"Content-Type\":\"application/x-www-form-urlencoded\"};function a(t,e){!r.isUndefined(t)&&r.isUndefined(t[\"Content-Type\"])&&(t[\"Content-Type\"]=e)}function s(){var t;return(\"undefined\"!==typeof XMLHttpRequest||\"undefined\"!==typeof e&&\"[object process]\"===Object.prototype.toString.call(e))&&(t=n(\"b50d\")),t}var c={adapter:s(),transformRequest:[function(t,e){return o(e,\"Accept\"),o(e,\"Content-Type\"),r.isFormData(t)||r.isArrayBuffer(t)||r.isBuffer(t)||r.isStream(t)||r.isFile(t)||r.isBlob(t)?t:r.isArrayBufferView(t)?t.buffer:r.isURLSearchParams(t)?(a(e,\"application/x-www-form-urlencoded;charset=utf-8\"),t.toString()):r.isObject(t)?(a(e,\"application/json;charset=utf-8\"),JSON.stringify(t)):t}],transformResponse:[function(t){if(\"string\"===typeof t)try{t=JSON.parse(t)}catch(e){}return t}],timeout:0,xsrfCookieName:\"XSRF-TOKEN\",xsrfHeaderName:\"X-XSRF-TOKEN\",maxContentLength:-1,validateStatus:function(t){return t>=200&&t<300},headers:{common:{Accept:\"application/json, text/plain, */*\"}}};r.forEach([\"delete\",\"get\",\"head\"],(function(t){c.headers[t]={}})),r.forEach([\"post\",\"put\",\"patch\"],(function(t){c.headers[t]=r.merge(i)})),t.exports=c}).call(this,n(\"4362\"))},2626:function(t,e,n){\"use strict\";var r=n(\"d066\"),o=n(\"9bf2\"),i=n(\"b622\"),a=n(\"83ab\"),s=i(\"species\");t.exports=function(t){var e=r(t),n=o.f;a&&e&&!e[s]&&n(e,s,{configurable:!0,get:function(){return this}})}},2877:function(t,e,n){\"use strict\";function r(t,e,n,r,o,i,a,s){var c,u=\"function\"===typeof t?t.options:t;if(e&&(u.render=e,u.staticRenderFns=n,u._compiled=!0),r&&(u.functional=!0),i&&(u._scopeId=\"data-v-\"+i),a?(c=function(t){t=t||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext,t||\"undefined\"===typeof __VUE_SSR_CONTEXT__||(t=__VUE_SSR_CONTEXT__),o&&o.call(this,t),t&&t._registeredComponents&&t._registeredComponents.add(a)},u._ssrRegister=c):o&&(c=s?function(){o.call(this,this.$root.$options.shadowRoot)}:o),c)if(u.functional){u._injectStyles=c;var f=u.render;u.render=function(t,e){return c.call(e),f(t,e)}}else{var l=u.beforeCreate;u.beforeCreate=l?[].concat(l,c):[c]}return{exports:t,options:u}}n.d(e,\"a\",(function(){return r}))},\"2b0e\":function(t,e,n){\"use strict\";(function(t){\n/*!\n * Vue.js v2.6.11\n * (c) 2014-2019 Evan You\n * Released under the MIT License.\n */\nvar n=Object.freeze({});function r(t){return void 0===t||null===t}function o(t){return void 0!==t&&null!==t}function i(t){return!0===t}function a(t){return!1===t}function s(t){return\"string\"===typeof t||\"number\"===typeof t||\"symbol\"===typeof t||\"boolean\"===typeof t}function c(t){return null!==t&&\"object\"===typeof t}var u=Object.prototype.toString;function f(t){return\"[object Object]\"===u.call(t)}function l(t){return\"[object RegExp]\"===u.call(t)}function p(t){var e=parseFloat(String(t));return e>=0&&Math.floor(e)===e&&isFinite(t)}function d(t){return o(t)&&\"function\"===typeof t.then&&\"function\"===typeof t.catch}function h(t){return null==t?\"\":Array.isArray(t)||f(t)&&t.toString===u?JSON.stringify(t,null,2):String(t)}function v(t){var e=parseFloat(t);return isNaN(e)?t:e}function y(t,e){for(var n=Object.create(null),r=t.split(\",\"),o=0;o<r.length;o++)n[r[o]]=!0;return e?function(t){return n[t.toLowerCase()]}:function(t){return n[t]}}y(\"slot,component\",!0);var m=y(\"key,ref,slot,slot-scope,is\");function g(t,e){if(t.length){var n=t.indexOf(e);if(n>-1)return t.splice(n,1)}}var b=Object.prototype.hasOwnProperty;function _(t,e){return b.call(t,e)}function w(t){var e=Object.create(null);return function(n){var r=e[n];return r||(e[n]=t(n))}}var x=/-(\\w)/g,C=w((function(t){return t.replace(x,(function(t,e){return e?e.toUpperCase():\"\"}))})),O=w((function(t){return t.charAt(0).toUpperCase()+t.slice(1)})),A=/\\B([A-Z])/g,k=w((function(t){return t.replace(A,\"-$1\").toLowerCase()}));function j(t,e){function n(n){var r=arguments.length;return r?r>1?t.apply(e,arguments):t.call(e,n):t.call(e)}return n._length=t.length,n}function S(t,e){return t.bind(e)}var $=Function.prototype.bind?S:j;function E(t,e){e=e||0;var n=t.length-e,r=new Array(n);while(n--)r[n]=t[n+e];return r}function T(t,e){for(var n in e)t[n]=e[n];return t}function P(t){for(var e={},n=0;n<t.length;n++)t[n]&&T(e,t[n]);return e}function R(t,e,n){}var I=function(t,e,n){return!1},M=function(t){return t};function N(t,e){if(t===e)return!0;var n=c(t),r=c(e);if(!n||!r)return!n&&!r&&String(t)===String(e);try{var o=Array.isArray(t),i=Array.isArray(e);if(o&&i)return t.length===e.length&&t.every((function(t,n){return N(t,e[n])}));if(t instanceof Date&&e instanceof Date)return t.getTime()===e.getTime();if(o||i)return!1;var a=Object.keys(t),s=Object.keys(e);return a.length===s.length&&a.every((function(n){return N(t[n],e[n])}))}catch(u){return!1}}function L(t,e){for(var n=0;n<t.length;n++)if(N(t[n],e))return n;return-1}function D(t){var e=!1;return function(){e||(e=!0,t.apply(this,arguments))}}var F=\"data-server-rendered\",U=[\"component\",\"directive\",\"filter\"],B=[\"beforeCreate\",\"created\",\"beforeMount\",\"mounted\",\"beforeUpdate\",\"updated\",\"beforeDestroy\",\"destroyed\",\"activated\",\"deactivated\",\"errorCaptured\",\"serverPrefetch\"],q={optionMergeStrategies:Object.create(null),silent:!1,productionTip:!1,devtools:!1,performance:!1,errorHandler:null,warnHandler:null,ignoredElements:[],keyCodes:Object.create(null),isReservedTag:I,isReservedAttr:I,isUnknownElement:I,getTagNamespace:R,parsePlatformTagName:M,mustUseProp:I,async:!0,_lifecycleHooks:B},H=/a-zA-Z\\u00B7\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u203F-\\u2040\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD/;function V(t){var e=(t+\"\").charCodeAt(0);return 36===e||95===e}function z(t,e,n,r){Object.defineProperty(t,e,{value:n,enumerable:!!r,writable:!0,configurable:!0})}var G=new RegExp(\"[^\"+H.source+\".$_\\\\d]\");function W(t){if(!G.test(t)){var e=t.split(\".\");return function(t){for(var n=0;n<e.length;n++){if(!t)return;t=t[e[n]]}return t}}}var K,X=\"__proto__\"in{},J=\"undefined\"!==typeof window,Y=\"undefined\"!==typeof WXEnvironment&&!!WXEnvironment.platform,Q=Y&&WXEnvironment.platform.toLowerCase(),Z=J&&window.navigator.userAgent.toLowerCase(),tt=Z&&/msie|trident/.test(Z),et=Z&&Z.indexOf(\"msie 9.0\")>0,nt=Z&&Z.indexOf(\"edge/\")>0,rt=(Z&&Z.indexOf(\"android\"),Z&&/iphone|ipad|ipod|ios/.test(Z)||\"ios\"===Q),ot=(Z&&/chrome\\/\\d+/.test(Z),Z&&/phantomjs/.test(Z),Z&&Z.match(/firefox\\/(\\d+)/)),it={}.watch,at=!1;if(J)try{var st={};Object.defineProperty(st,\"passive\",{get:function(){at=!0}}),window.addEventListener(\"test-passive\",null,st)}catch(Ca){}var ct=function(){return void 0===K&&(K=!J&&!Y&&\"undefined\"!==typeof t&&(t[\"process\"]&&\"server\"===t[\"process\"].env.VUE_ENV)),K},ut=J&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__;function ft(t){return\"function\"===typeof t&&/native code/.test(t.toString())}var lt,pt=\"undefined\"!==typeof Symbol&&ft(Symbol)&&\"undefined\"!==typeof Reflect&&ft(Reflect.ownKeys);lt=\"undefined\"!==typeof Set&&ft(Set)?Set:function(){function t(){this.set=Object.create(null)}return t.prototype.has=function(t){return!0===this.set[t]},t.prototype.add=function(t){this.set[t]=!0},t.prototype.clear=function(){this.set=Object.create(null)},t}();var dt=R,ht=0,vt=function(){this.id=ht++,this.subs=[]};vt.prototype.addSub=function(t){this.subs.push(t)},vt.prototype.removeSub=function(t){g(this.subs,t)},vt.prototype.depend=function(){vt.target&&vt.target.addDep(this)},vt.prototype.notify=function(){var t=this.subs.slice();for(var e=0,n=t.length;e<n;e++)t[e].update()},vt.target=null;var yt=[];function mt(t){yt.push(t),vt.target=t}function gt(){yt.pop(),vt.target=yt[yt.length-1]}var bt=function(t,e,n,r,o,i,a,s){this.tag=t,this.data=e,this.children=n,this.text=r,this.elm=o,this.ns=void 0,this.context=i,this.fnContext=void 0,this.fnOptions=void 0,this.fnScopeId=void 0,this.key=e&&e.key,this.componentOptions=a,this.componentInstance=void 0,this.parent=void 0,this.raw=!1,this.isStatic=!1,this.isRootInsert=!0,this.isComment=!1,this.isCloned=!1,this.isOnce=!1,this.asyncFactory=s,this.asyncMeta=void 0,this.isAsyncPlaceholder=!1},_t={child:{configurable:!0}};_t.child.get=function(){return this.componentInstance},Object.defineProperties(bt.prototype,_t);var wt=function(t){void 0===t&&(t=\"\");var e=new bt;return e.text=t,e.isComment=!0,e};function xt(t){return new bt(void 0,void 0,void 0,String(t))}function Ct(t){var e=new bt(t.tag,t.data,t.children&&t.children.slice(),t.text,t.elm,t.context,t.componentOptions,t.asyncFactory);return e.ns=t.ns,e.isStatic=t.isStatic,e.key=t.key,e.isComment=t.isComment,e.fnContext=t.fnContext,e.fnOptions=t.fnOptions,e.fnScopeId=t.fnScopeId,e.asyncMeta=t.asyncMeta,e.isCloned=!0,e}var Ot=Array.prototype,At=Object.create(Ot),kt=[\"push\",\"pop\",\"shift\",\"unshift\",\"splice\",\"sort\",\"reverse\"];kt.forEach((function(t){var e=Ot[t];z(At,t,(function(){var n=[],r=arguments.length;while(r--)n[r]=arguments[r];var o,i=e.apply(this,n),a=this.__ob__;switch(t){case\"push\":case\"unshift\":o=n;break;case\"splice\":o=n.slice(2);break}return o&&a.observeArray(o),a.dep.notify(),i}))}));var jt=Object.getOwnPropertyNames(At),St=!0;function $t(t){St=t}var Et=function(t){this.value=t,this.dep=new vt,this.vmCount=0,z(t,\"__ob__\",this),Array.isArray(t)?(X?Tt(t,At):Pt(t,At,jt),this.observeArray(t)):this.walk(t)};function Tt(t,e){t.__proto__=e}function Pt(t,e,n){for(var r=0,o=n.length;r<o;r++){var i=n[r];z(t,i,e[i])}}function Rt(t,e){var n;if(c(t)&&!(t instanceof bt))return _(t,\"__ob__\")&&t.__ob__ instanceof Et?n=t.__ob__:St&&!ct()&&(Array.isArray(t)||f(t))&&Object.isExtensible(t)&&!t._isVue&&(n=new Et(t)),e&&n&&n.vmCount++,n}function It(t,e,n,r,o){var i=new vt,a=Object.getOwnPropertyDescriptor(t,e);if(!a||!1!==a.configurable){var s=a&&a.get,c=a&&a.set;s&&!c||2!==arguments.length||(n=t[e]);var u=!o&&Rt(n);Object.defineProperty(t,e,{enumerable:!0,configurable:!0,get:function(){var e=s?s.call(t):n;return vt.target&&(i.depend(),u&&(u.dep.depend(),Array.isArray(e)&&Lt(e))),e},set:function(e){var r=s?s.call(t):n;e===r||e!==e&&r!==r||s&&!c||(c?c.call(t,e):n=e,u=!o&&Rt(e),i.notify())}})}}function Mt(t,e,n){if(Array.isArray(t)&&p(e))return t.length=Math.max(t.length,e),t.splice(e,1,n),n;if(e in t&&!(e in Object.prototype))return t[e]=n,n;var r=t.__ob__;return t._isVue||r&&r.vmCount?n:r?(It(r.value,e,n),r.dep.notify(),n):(t[e]=n,n)}function Nt(t,e){if(Array.isArray(t)&&p(e))t.splice(e,1);else{var n=t.__ob__;t._isVue||n&&n.vmCount||_(t,e)&&(delete t[e],n&&n.dep.notify())}}function Lt(t){for(var e=void 0,n=0,r=t.length;n<r;n++)e=t[n],e&&e.__ob__&&e.__ob__.dep.depend(),Array.isArray(e)&&Lt(e)}Et.prototype.walk=function(t){for(var e=Object.keys(t),n=0;n<e.length;n++)It(t,e[n])},Et.prototype.observeArray=function(t){for(var e=0,n=t.length;e<n;e++)Rt(t[e])};var Dt=q.optionMergeStrategies;function Ft(t,e){if(!e)return t;for(var n,r,o,i=pt?Reflect.ownKeys(e):Object.keys(e),a=0;a<i.length;a++)n=i[a],\"__ob__\"!==n&&(r=t[n],o=e[n],_(t,n)?r!==o&&f(r)&&f(o)&&Ft(r,o):Mt(t,n,o));return t}function Ut(t,e,n){return n?function(){var r=\"function\"===typeof e?e.call(n,n):e,o=\"function\"===typeof t?t.call(n,n):t;return r?Ft(r,o):o}:e?t?function(){return Ft(\"function\"===typeof e?e.call(this,this):e,\"function\"===typeof t?t.call(this,this):t)}:e:t}function Bt(t,e){var n=e?t?t.concat(e):Array.isArray(e)?e:[e]:t;return n?qt(n):n}function qt(t){for(var e=[],n=0;n<t.length;n++)-1===e.indexOf(t[n])&&e.push(t[n]);return e}function Ht(t,e,n,r){var o=Object.create(t||null);return e?T(o,e):o}Dt.data=function(t,e,n){return n?Ut(t,e,n):e&&\"function\"!==typeof e?t:Ut(t,e)},B.forEach((function(t){Dt[t]=Bt})),U.forEach((function(t){Dt[t+\"s\"]=Ht})),Dt.watch=function(t,e,n,r){if(t===it&&(t=void 0),e===it&&(e=void 0),!e)return Object.create(t||null);if(!t)return e;var o={};for(var i in T(o,t),e){var a=o[i],s=e[i];a&&!Array.isArray(a)&&(a=[a]),o[i]=a?a.concat(s):Array.isArray(s)?s:[s]}return o},Dt.props=Dt.methods=Dt.inject=Dt.computed=function(t,e,n,r){if(!t)return e;var o=Object.create(null);return T(o,t),e&&T(o,e),o},Dt.provide=Ut;var Vt=function(t,e){return void 0===e?t:e};function zt(t,e){var n=t.props;if(n){var r,o,i,a={};if(Array.isArray(n)){r=n.length;while(r--)o=n[r],\"string\"===typeof o&&(i=C(o),a[i]={type:null})}else if(f(n))for(var s in n)o=n[s],i=C(s),a[i]=f(o)?o:{type:o};else 0;t.props=a}}function Gt(t,e){var n=t.inject;if(n){var r=t.inject={};if(Array.isArray(n))for(var o=0;o<n.length;o++)r[n[o]]={from:n[o]};else if(f(n))for(var i in n){var a=n[i];r[i]=f(a)?T({from:i},a):{from:a}}else 0}}function Wt(t){var e=t.directives;if(e)for(var n in e){var r=e[n];\"function\"===typeof r&&(e[n]={bind:r,update:r})}}function Kt(t,e,n){if(\"function\"===typeof e&&(e=e.options),zt(e,n),Gt(e,n),Wt(e),!e._base&&(e.extends&&(t=Kt(t,e.extends,n)),e.mixins))for(var r=0,o=e.mixins.length;r<o;r++)t=Kt(t,e.mixins[r],n);var i,a={};for(i in t)s(i);for(i in e)_(t,i)||s(i);function s(r){var o=Dt[r]||Vt;a[r]=o(t[r],e[r],n,r)}return a}function Xt(t,e,n,r){if(\"string\"===typeof n){var o=t[e];if(_(o,n))return o[n];var i=C(n);if(_(o,i))return o[i];var a=O(i);if(_(o,a))return o[a];var s=o[n]||o[i]||o[a];return s}}function Jt(t,e,n,r){var o=e[t],i=!_(n,t),a=n[t],s=te(Boolean,o.type);if(s>-1)if(i&&!_(o,\"default\"))a=!1;else if(\"\"===a||a===k(t)){var c=te(String,o.type);(c<0||s<c)&&(a=!0)}if(void 0===a){a=Yt(r,o,t);var u=St;$t(!0),Rt(a),$t(u)}return a}function Yt(t,e,n){if(_(e,\"default\")){var r=e.default;return t&&t.$options.propsData&&void 0===t.$options.propsData[n]&&void 0!==t._props[n]?t._props[n]:\"function\"===typeof r&&\"Function\"!==Qt(e.type)?r.call(t):r}}function Qt(t){var e=t&&t.toString().match(/^\\s*function (\\w+)/);return e?e[1]:\"\"}function Zt(t,e){return Qt(t)===Qt(e)}function te(t,e){if(!Array.isArray(e))return Zt(e,t)?0:-1;for(var n=0,r=e.length;n<r;n++)if(Zt(e[n],t))return n;return-1}function ee(t,e,n){mt();try{if(e){var r=e;while(r=r.$parent){var o=r.$options.errorCaptured;if(o)for(var i=0;i<o.length;i++)try{var a=!1===o[i].call(r,t,e,n);if(a)return}catch(Ca){re(Ca,r,\"errorCaptured hook\")}}}re(t,e,n)}finally{gt()}}function ne(t,e,n,r,o){var i;try{i=n?t.apply(e,n):t.call(e),i&&!i._isVue&&d(i)&&!i._handled&&(i.catch((function(t){return ee(t,r,o+\" (Promise/async)\")})),i._handled=!0)}catch(Ca){ee(Ca,r,o)}return i}function re(t,e,n){if(q.errorHandler)try{return q.errorHandler.call(null,t,e,n)}catch(Ca){Ca!==t&&oe(Ca,null,\"config.errorHandler\")}oe(t,e,n)}function oe(t,e,n){if(!J&&!Y||\"undefined\"===typeof console)throw t;console.error(t)}var ie,ae=!1,se=[],ce=!1;function ue(){ce=!1;var t=se.slice(0);se.length=0;for(var e=0;e<t.length;e++)t[e]()}if(\"undefined\"!==typeof Promise&&ft(Promise)){var fe=Promise.resolve();ie=function(){fe.then(ue),rt&&setTimeout(R)},ae=!0}else if(tt||\"undefined\"===typeof MutationObserver||!ft(MutationObserver)&&\"[object MutationObserverConstructor]\"!==MutationObserver.toString())ie=\"undefined\"!==typeof setImmediate&&ft(setImmediate)?function(){setImmediate(ue)}:function(){setTimeout(ue,0)};else{var le=1,pe=new MutationObserver(ue),de=document.createTextNode(String(le));pe.observe(de,{characterData:!0}),ie=function(){le=(le+1)%2,de.data=String(le)},ae=!0}function he(t,e){var n;if(se.push((function(){if(t)try{t.call(e)}catch(Ca){ee(Ca,e,\"nextTick\")}else n&&n(e)})),ce||(ce=!0,ie()),!t&&\"undefined\"!==typeof Promise)return new Promise((function(t){n=t}))}var ve=new lt;function ye(t){me(t,ve),ve.clear()}function me(t,e){var n,r,o=Array.isArray(t);if(!(!o&&!c(t)||Object.isFrozen(t)||t instanceof bt)){if(t.__ob__){var i=t.__ob__.dep.id;if(e.has(i))return;e.add(i)}if(o){n=t.length;while(n--)me(t[n],e)}else{r=Object.keys(t),n=r.length;while(n--)me(t[r[n]],e)}}}var ge=w((function(t){var e=\"&\"===t.charAt(0);t=e?t.slice(1):t;var n=\"~\"===t.charAt(0);t=n?t.slice(1):t;var r=\"!\"===t.charAt(0);return t=r?t.slice(1):t,{name:t,once:n,capture:r,passive:e}}));function be(t,e){function n(){var t=arguments,r=n.fns;if(!Array.isArray(r))return ne(r,null,arguments,e,\"v-on handler\");for(var o=r.slice(),i=0;i<o.length;i++)ne(o[i],null,t,e,\"v-on handler\")}return n.fns=t,n}function _e(t,e,n,o,a,s){var c,u,f,l;for(c in t)u=t[c],f=e[c],l=ge(c),r(u)||(r(f)?(r(u.fns)&&(u=t[c]=be(u,s)),i(l.once)&&(u=t[c]=a(l.name,u,l.capture)),n(l.name,u,l.capture,l.passive,l.params)):u!==f&&(f.fns=u,t[c]=f));for(c in e)r(t[c])&&(l=ge(c),o(l.name,e[c],l.capture))}function we(t,e,n){var a;t instanceof bt&&(t=t.data.hook||(t.data.hook={}));var s=t[e];function c(){n.apply(this,arguments),g(a.fns,c)}r(s)?a=be([c]):o(s.fns)&&i(s.merged)?(a=s,a.fns.push(c)):a=be([s,c]),a.merged=!0,t[e]=a}function xe(t,e,n){var i=e.options.props;if(!r(i)){var a={},s=t.attrs,c=t.props;if(o(s)||o(c))for(var u in i){var f=k(u);Ce(a,c,u,f,!0)||Ce(a,s,u,f,!1)}return a}}function Ce(t,e,n,r,i){if(o(e)){if(_(e,n))return t[n]=e[n],i||delete e[n],!0;if(_(e,r))return t[n]=e[r],i||delete e[r],!0}return!1}function Oe(t){for(var e=0;e<t.length;e++)if(Array.isArray(t[e]))return Array.prototype.concat.apply([],t);return t}function Ae(t){return s(t)?[xt(t)]:Array.isArray(t)?je(t):void 0}function ke(t){return o(t)&&o(t.text)&&a(t.isComment)}function je(t,e){var n,a,c,u,f=[];for(n=0;n<t.length;n++)a=t[n],r(a)||\"boolean\"===typeof a||(c=f.length-1,u=f[c],Array.isArray(a)?a.length>0&&(a=je(a,(e||\"\")+\"_\"+n),ke(a[0])&&ke(u)&&(f[c]=xt(u.text+a[0].text),a.shift()),f.push.apply(f,a)):s(a)?ke(u)?f[c]=xt(u.text+a):\"\"!==a&&f.push(xt(a)):ke(a)&&ke(u)?f[c]=xt(u.text+a.text):(i(t._isVList)&&o(a.tag)&&r(a.key)&&o(e)&&(a.key=\"__vlist\"+e+\"_\"+n+\"__\"),f.push(a)));return f}function Se(t){var e=t.$options.provide;e&&(t._provided=\"function\"===typeof e?e.call(t):e)}function $e(t){var e=Ee(t.$options.inject,t);e&&($t(!1),Object.keys(e).forEach((function(n){It(t,n,e[n])})),$t(!0))}function Ee(t,e){if(t){for(var n=Object.create(null),r=pt?Reflect.ownKeys(t):Object.keys(t),o=0;o<r.length;o++){var i=r[o];if(\"__ob__\"!==i){var a=t[i].from,s=e;while(s){if(s._provided&&_(s._provided,a)){n[i]=s._provided[a];break}s=s.$parent}if(!s)if(\"default\"in t[i]){var c=t[i].default;n[i]=\"function\"===typeof c?c.call(e):c}else 0}}return n}}function Te(t,e){if(!t||!t.length)return{};for(var n={},r=0,o=t.length;r<o;r++){var i=t[r],a=i.data;if(a&&a.attrs&&a.attrs.slot&&delete a.attrs.slot,i.context!==e&&i.fnContext!==e||!a||null==a.slot)(n.default||(n.default=[])).push(i);else{var s=a.slot,c=n[s]||(n[s]=[]);\"template\"===i.tag?c.push.apply(c,i.children||[]):c.push(i)}}for(var u in n)n[u].every(Pe)&&delete n[u];return n}function Pe(t){return t.isComment&&!t.asyncFactory||\" \"===t.text}function Re(t,e,r){var o,i=Object.keys(e).length>0,a=t?!!t.$stable:!i,s=t&&t.$key;if(t){if(t._normalized)return t._normalized;if(a&&r&&r!==n&&s===r.$key&&!i&&!r.$hasNormal)return r;for(var c in o={},t)t[c]&&\"$\"!==c[0]&&(o[c]=Ie(e,c,t[c]))}else o={};for(var u in e)u in o||(o[u]=Me(e,u));return t&&Object.isExtensible(t)&&(t._normalized=o),z(o,\"$stable\",a),z(o,\"$key\",s),z(o,\"$hasNormal\",i),o}function Ie(t,e,n){var r=function(){var t=arguments.length?n.apply(null,arguments):n({});return t=t&&\"object\"===typeof t&&!Array.isArray(t)?[t]:Ae(t),t&&(0===t.length||1===t.length&&t[0].isComment)?void 0:t};return n.proxy&&Object.defineProperty(t,e,{get:r,enumerable:!0,configurable:!0}),r}function Me(t,e){return function(){return t[e]}}function Ne(t,e){var n,r,i,a,s;if(Array.isArray(t)||\"string\"===typeof t)for(n=new Array(t.length),r=0,i=t.length;r<i;r++)n[r]=e(t[r],r);else if(\"number\"===typeof t)for(n=new Array(t),r=0;r<t;r++)n[r]=e(r+1,r);else if(c(t))if(pt&&t[Symbol.iterator]){n=[];var u=t[Symbol.iterator](),f=u.next();while(!f.done)n.push(e(f.value,n.length)),f=u.next()}else for(a=Object.keys(t),n=new Array(a.length),r=0,i=a.length;r<i;r++)s=a[r],n[r]=e(t[s],s,r);return o(n)||(n=[]),n._isVList=!0,n}function Le(t,e,n,r){var o,i=this.$scopedSlots[t];i?(n=n||{},r&&(n=T(T({},r),n)),o=i(n)||e):o=this.$slots[t]||e;var a=n&&n.slot;return a?this.$createElement(\"template\",{slot:a},o):o}function De(t){return Xt(this.$options,\"filters\",t,!0)||M}function Fe(t,e){return Array.isArray(t)?-1===t.indexOf(e):t!==e}function Ue(t,e,n,r,o){var i=q.keyCodes[e]||n;return o&&r&&!q.keyCodes[e]?Fe(o,r):i?Fe(i,t):r?k(r)!==e:void 0}function Be(t,e,n,r,o){if(n)if(c(n)){var i;Array.isArray(n)&&(n=P(n));var a=function(a){if(\"class\"===a||\"style\"===a||m(a))i=t;else{var s=t.attrs&&t.attrs.type;i=r||q.mustUseProp(e,s,a)?t.domProps||(t.domProps={}):t.attrs||(t.attrs={})}var c=C(a),u=k(a);if(!(c in i)&&!(u in i)&&(i[a]=n[a],o)){var f=t.on||(t.on={});f[\"update:\"+a]=function(t){n[a]=t}}};for(var s in n)a(s)}else;return t}function qe(t,e){var n=this._staticTrees||(this._staticTrees=[]),r=n[t];return r&&!e||(r=n[t]=this.$options.staticRenderFns[t].call(this._renderProxy,null,this),Ve(r,\"__static__\"+t,!1)),r}function He(t,e,n){return Ve(t,\"__once__\"+e+(n?\"_\"+n:\"\"),!0),t}function Ve(t,e,n){if(Array.isArray(t))for(var r=0;r<t.length;r++)t[r]&&\"string\"!==typeof t[r]&&ze(t[r],e+\"_\"+r,n);else ze(t,e,n)}function ze(t,e,n){t.isStatic=!0,t.key=e,t.isOnce=n}function Ge(t,e){if(e)if(f(e)){var n=t.on=t.on?T({},t.on):{};for(var r in e){var o=n[r],i=e[r];n[r]=o?[].concat(o,i):i}}else;return t}function We(t,e,n,r){e=e||{$stable:!n};for(var o=0;o<t.length;o++){var i=t[o];Array.isArray(i)?We(i,e,n):i&&(i.proxy&&(i.fn.proxy=!0),e[i.key]=i.fn)}return r&&(e.$key=r),e}function Ke(t,e){for(var n=0;n<e.length;n+=2){var r=e[n];\"string\"===typeof r&&r&&(t[e[n]]=e[n+1])}return t}function Xe(t,e){return\"string\"===typeof t?e+t:t}function Je(t){t._o=He,t._n=v,t._s=h,t._l=Ne,t._t=Le,t._q=N,t._i=L,t._m=qe,t._f=De,t._k=Ue,t._b=Be,t._v=xt,t._e=wt,t._u=We,t._g=Ge,t._d=Ke,t._p=Xe}function Ye(t,e,r,o,a){var s,c=this,u=a.options;_(o,\"_uid\")?(s=Object.create(o),s._original=o):(s=o,o=o._original);var f=i(u._compiled),l=!f;this.data=t,this.props=e,this.children=r,this.parent=o,this.listeners=t.on||n,this.injections=Ee(u.inject,o),this.slots=function(){return c.$slots||Re(t.scopedSlots,c.$slots=Te(r,o)),c.$slots},Object.defineProperty(this,\"scopedSlots\",{enumerable:!0,get:function(){return Re(t.scopedSlots,this.slots())}}),f&&(this.$options=u,this.$slots=this.slots(),this.$scopedSlots=Re(t.scopedSlots,this.$slots)),u._scopeId?this._c=function(t,e,n,r){var i=ln(s,t,e,n,r,l);return i&&!Array.isArray(i)&&(i.fnScopeId=u._scopeId,i.fnContext=o),i}:this._c=function(t,e,n,r){return ln(s,t,e,n,r,l)}}function Qe(t,e,r,i,a){var s=t.options,c={},u=s.props;if(o(u))for(var f in u)c[f]=Jt(f,u,e||n);else o(r.attrs)&&tn(c,r.attrs),o(r.props)&&tn(c,r.props);var l=new Ye(r,c,a,i,t),p=s.render.call(null,l._c,l);if(p instanceof bt)return Ze(p,r,l.parent,s,l);if(Array.isArray(p)){for(var d=Ae(p)||[],h=new Array(d.length),v=0;v<d.length;v++)h[v]=Ze(d[v],r,l.parent,s,l);return h}}function Ze(t,e,n,r,o){var i=Ct(t);return i.fnContext=n,i.fnOptions=r,e.slot&&((i.data||(i.data={})).slot=e.slot),i}function tn(t,e){for(var n in e)t[C(n)]=e[n]}Je(Ye.prototype);var en={init:function(t,e){if(t.componentInstance&&!t.componentInstance._isDestroyed&&t.data.keepAlive){var n=t;en.prepatch(n,n)}else{var r=t.componentInstance=on(t,En);r.$mount(e?t.elm:void 0,e)}},prepatch:function(t,e){var n=e.componentOptions,r=e.componentInstance=t.componentInstance;Mn(r,n.propsData,n.listeners,e,n.children)},insert:function(t){var e=t.context,n=t.componentInstance;n._isMounted||(n._isMounted=!0,Fn(n,\"mounted\")),t.data.keepAlive&&(e._isMounted?Qn(n):Ln(n,!0))},destroy:function(t){var e=t.componentInstance;e._isDestroyed||(t.data.keepAlive?Dn(e,!0):e.$destroy())}},nn=Object.keys(en);function rn(t,e,n,a,s){if(!r(t)){var u=n.$options._base;if(c(t)&&(t=u.extend(t)),\"function\"===typeof t){var f;if(r(t.cid)&&(f=t,t=wn(f,u),void 0===t))return _n(f,e,n,a,s);e=e||{},wr(t),o(e.model)&&cn(t.options,e);var l=xe(e,t,s);if(i(t.options.functional))return Qe(t,l,e,n,a);var p=e.on;if(e.on=e.nativeOn,i(t.options.abstract)){var d=e.slot;e={},d&&(e.slot=d)}an(e);var h=t.options.name||s,v=new bt(\"vue-component-\"+t.cid+(h?\"-\"+h:\"\"),e,void 0,void 0,void 0,n,{Ctor:t,propsData:l,listeners:p,tag:s,children:a},f);return v}}}function on(t,e){var n={_isComponent:!0,_parentVnode:t,parent:e},r=t.data.inlineTemplate;return o(r)&&(n.render=r.render,n.staticRenderFns=r.staticRenderFns),new t.componentOptions.Ctor(n)}function an(t){for(var e=t.hook||(t.hook={}),n=0;n<nn.length;n++){var r=nn[n],o=e[r],i=en[r];o===i||o&&o._merged||(e[r]=o?sn(i,o):i)}}function sn(t,e){var n=function(n,r){t(n,r),e(n,r)};return n._merged=!0,n}function cn(t,e){var n=t.model&&t.model.prop||\"value\",r=t.model&&t.model.event||\"input\";(e.attrs||(e.attrs={}))[n]=e.model.value;var i=e.on||(e.on={}),a=i[r],s=e.model.callback;o(a)?(Array.isArray(a)?-1===a.indexOf(s):a!==s)&&(i[r]=[s].concat(a)):i[r]=s}var un=1,fn=2;function ln(t,e,n,r,o,a){return(Array.isArray(n)||s(n))&&(o=r,r=n,n=void 0),i(a)&&(o=fn),pn(t,e,n,r,o)}function pn(t,e,n,r,i){if(o(n)&&o(n.__ob__))return wt();if(o(n)&&o(n.is)&&(e=n.is),!e)return wt();var a,s,c;(Array.isArray(r)&&\"function\"===typeof r[0]&&(n=n||{},n.scopedSlots={default:r[0]},r.length=0),i===fn?r=Ae(r):i===un&&(r=Oe(r)),\"string\"===typeof e)?(s=t.$vnode&&t.$vnode.ns||q.getTagNamespace(e),a=q.isReservedTag(e)?new bt(q.parsePlatformTagName(e),n,r,void 0,void 0,t):n&&n.pre||!o(c=Xt(t.$options,\"components\",e))?new bt(e,n,r,void 0,void 0,t):rn(c,n,t,r,e)):a=rn(e,n,t,r);return Array.isArray(a)?a:o(a)?(o(s)&&dn(a,s),o(n)&&hn(n),a):wt()}function dn(t,e,n){if(t.ns=e,\"foreignObject\"===t.tag&&(e=void 0,n=!0),o(t.children))for(var a=0,s=t.children.length;a<s;a++){var c=t.children[a];o(c.tag)&&(r(c.ns)||i(n)&&\"svg\"!==c.tag)&&dn(c,e,n)}}function hn(t){c(t.style)&&ye(t.style),c(t.class)&&ye(t.class)}function vn(t){t._vnode=null,t._staticTrees=null;var e=t.$options,r=t.$vnode=e._parentVnode,o=r&&r.context;t.$slots=Te(e._renderChildren,o),t.$scopedSlots=n,t._c=function(e,n,r,o){return ln(t,e,n,r,o,!1)},t.$createElement=function(e,n,r,o){return ln(t,e,n,r,o,!0)};var i=r&&r.data;It(t,\"$attrs\",i&&i.attrs||n,null,!0),It(t,\"$listeners\",e._parentListeners||n,null,!0)}var yn,mn=null;function gn(t){Je(t.prototype),t.prototype.$nextTick=function(t){return he(t,this)},t.prototype._render=function(){var t,e=this,n=e.$options,r=n.render,o=n._parentVnode;o&&(e.$scopedSlots=Re(o.data.scopedSlots,e.$slots,e.$scopedSlots)),e.$vnode=o;try{mn=e,t=r.call(e._renderProxy,e.$createElement)}catch(Ca){ee(Ca,e,\"render\"),t=e._vnode}finally{mn=null}return Array.isArray(t)&&1===t.length&&(t=t[0]),t instanceof bt||(t=wt()),t.parent=o,t}}function bn(t,e){return(t.__esModule||pt&&\"Module\"===t[Symbol.toStringTag])&&(t=t.default),c(t)?e.extend(t):t}function _n(t,e,n,r,o){var i=wt();return i.asyncFactory=t,i.asyncMeta={data:e,context:n,children:r,tag:o},i}function wn(t,e){if(i(t.error)&&o(t.errorComp))return t.errorComp;if(o(t.resolved))return t.resolved;var n=mn;if(n&&o(t.owners)&&-1===t.owners.indexOf(n)&&t.owners.push(n),i(t.loading)&&o(t.loadingComp))return t.loadingComp;if(n&&!o(t.owners)){var a=t.owners=[n],s=!0,u=null,f=null;n.$on(\"hook:destroyed\",(function(){return g(a,n)}));var l=function(t){for(var e=0,n=a.length;e<n;e++)a[e].$forceUpdate();t&&(a.length=0,null!==u&&(clearTimeout(u),u=null),null!==f&&(clearTimeout(f),f=null))},p=D((function(n){t.resolved=bn(n,e),s?a.length=0:l(!0)})),h=D((function(e){o(t.errorComp)&&(t.error=!0,l(!0))})),v=t(p,h);return c(v)&&(d(v)?r(t.resolved)&&v.then(p,h):d(v.component)&&(v.component.then(p,h),o(v.error)&&(t.errorComp=bn(v.error,e)),o(v.loading)&&(t.loadingComp=bn(v.loading,e),0===v.delay?t.loading=!0:u=setTimeout((function(){u=null,r(t.resolved)&&r(t.error)&&(t.loading=!0,l(!1))}),v.delay||200)),o(v.timeout)&&(f=setTimeout((function(){f=null,r(t.resolved)&&h(null)}),v.timeout)))),s=!1,t.loading?t.loadingComp:t.resolved}}function xn(t){return t.isComment&&t.asyncFactory}function Cn(t){if(Array.isArray(t))for(var e=0;e<t.length;e++){var n=t[e];if(o(n)&&(o(n.componentOptions)||xn(n)))return n}}function On(t){t._events=Object.create(null),t._hasHookEvent=!1;var e=t.$options._parentListeners;e&&Sn(t,e)}function An(t,e){yn.$on(t,e)}function kn(t,e){yn.$off(t,e)}function jn(t,e){var n=yn;return function r(){var o=e.apply(null,arguments);null!==o&&n.$off(t,r)}}function Sn(t,e,n){yn=t,_e(e,n||{},An,kn,jn,t),yn=void 0}function $n(t){var e=/^hook:/;t.prototype.$on=function(t,n){var r=this;if(Array.isArray(t))for(var o=0,i=t.length;o<i;o++)r.$on(t[o],n);else(r._events[t]||(r._events[t]=[])).push(n),e.test(t)&&(r._hasHookEvent=!0);return r},t.prototype.$once=function(t,e){var n=this;function r(){n.$off(t,r),e.apply(n,arguments)}return r.fn=e,n.$on(t,r),n},t.prototype.$off=function(t,e){var n=this;if(!arguments.length)return n._events=Object.create(null),n;if(Array.isArray(t)){for(var r=0,o=t.length;r<o;r++)n.$off(t[r],e);return n}var i,a=n._events[t];if(!a)return n;if(!e)return n._events[t]=null,n;var s=a.length;while(s--)if(i=a[s],i===e||i.fn===e){a.splice(s,1);break}return n},t.prototype.$emit=function(t){var e=this,n=e._events[t];if(n){n=n.length>1?E(n):n;for(var r=E(arguments,1),o='event handler for \"'+t+'\"',i=0,a=n.length;i<a;i++)ne(n[i],e,r,e,o)}return e}}var En=null;function Tn(t){var e=En;return En=t,function(){En=e}}function Pn(t){var e=t.$options,n=e.parent;if(n&&!e.abstract){while(n.$options.abstract&&n.$parent)n=n.$parent;n.$children.push(t)}t.$parent=n,t.$root=n?n.$root:t,t.$children=[],t.$refs={},t._watcher=null,t._inactive=null,t._directInactive=!1,t._isMounted=!1,t._isDestroyed=!1,t._isBeingDestroyed=!1}function Rn(t){t.prototype._update=function(t,e){var n=this,r=n.$el,o=n._vnode,i=Tn(n);n._vnode=t,n.$el=o?n.__patch__(o,t):n.__patch__(n.$el,t,e,!1),i(),r&&(r.__vue__=null),n.$el&&(n.$el.__vue__=n),n.$vnode&&n.$parent&&n.$vnode===n.$parent._vnode&&(n.$parent.$el=n.$el)},t.prototype.$forceUpdate=function(){var t=this;t._watcher&&t._watcher.update()},t.prototype.$destroy=function(){var t=this;if(!t._isBeingDestroyed){Fn(t,\"beforeDestroy\"),t._isBeingDestroyed=!0;var e=t.$parent;!e||e._isBeingDestroyed||t.$options.abstract||g(e.$children,t),t._watcher&&t._watcher.teardown();var n=t._watchers.length;while(n--)t._watchers[n].teardown();t._data.__ob__&&t._data.__ob__.vmCount--,t._isDestroyed=!0,t.__patch__(t._vnode,null),Fn(t,\"destroyed\"),t.$off(),t.$el&&(t.$el.__vue__=null),t.$vnode&&(t.$vnode.parent=null)}}}function In(t,e,n){var r;return t.$el=e,t.$options.render||(t.$options.render=wt),Fn(t,\"beforeMount\"),r=function(){t._update(t._render(),n)},new nr(t,r,R,{before:function(){t._isMounted&&!t._isDestroyed&&Fn(t,\"beforeUpdate\")}},!0),n=!1,null==t.$vnode&&(t._isMounted=!0,Fn(t,\"mounted\")),t}function Mn(t,e,r,o,i){var a=o.data.scopedSlots,s=t.$scopedSlots,c=!!(a&&!a.$stable||s!==n&&!s.$stable||a&&t.$scopedSlots.$key!==a.$key),u=!!(i||t.$options._renderChildren||c);if(t.$options._parentVnode=o,t.$vnode=o,t._vnode&&(t._vnode.parent=o),t.$options._renderChildren=i,t.$attrs=o.data.attrs||n,t.$listeners=r||n,e&&t.$options.props){$t(!1);for(var f=t._props,l=t.$options._propKeys||[],p=0;p<l.length;p++){var d=l[p],h=t.$options.props;f[d]=Jt(d,h,e,t)}$t(!0),t.$options.propsData=e}r=r||n;var v=t.$options._parentListeners;t.$options._parentListeners=r,Sn(t,r,v),u&&(t.$slots=Te(i,o.context),t.$forceUpdate())}function Nn(t){while(t&&(t=t.$parent))if(t._inactive)return!0;return!1}function Ln(t,e){if(e){if(t._directInactive=!1,Nn(t))return}else if(t._directInactive)return;if(t._inactive||null===t._inactive){t._inactive=!1;for(var n=0;n<t.$children.length;n++)Ln(t.$children[n]);Fn(t,\"activated\")}}function Dn(t,e){if((!e||(t._directInactive=!0,!Nn(t)))&&!t._inactive){t._inactive=!0;for(var n=0;n<t.$children.length;n++)Dn(t.$children[n]);Fn(t,\"deactivated\")}}function Fn(t,e){mt();var n=t.$options[e],r=e+\" hook\";if(n)for(var o=0,i=n.length;o<i;o++)ne(n[o],t,null,t,r);t._hasHookEvent&&t.$emit(\"hook:\"+e),gt()}var Un=[],Bn=[],qn={},Hn=!1,Vn=!1,zn=0;function Gn(){zn=Un.length=Bn.length=0,qn={},Hn=Vn=!1}var Wn=0,Kn=Date.now;if(J&&!tt){var Xn=window.performance;Xn&&\"function\"===typeof Xn.now&&Kn()>document.createEvent(\"Event\").timeStamp&&(Kn=function(){return Xn.now()})}function Jn(){var t,e;for(Wn=Kn(),Vn=!0,Un.sort((function(t,e){return t.id-e.id})),zn=0;zn<Un.length;zn++)t=Un[zn],t.before&&t.before(),e=t.id,qn[e]=null,t.run();var n=Bn.slice(),r=Un.slice();Gn(),Zn(n),Yn(r),ut&&q.devtools&&ut.emit(\"flush\")}function Yn(t){var e=t.length;while(e--){var n=t[e],r=n.vm;r._watcher===n&&r._isMounted&&!r._isDestroyed&&Fn(r,\"updated\")}}function Qn(t){t._inactive=!1,Bn.push(t)}function Zn(t){for(var e=0;e<t.length;e++)t[e]._inactive=!0,Ln(t[e],!0)}function tr(t){var e=t.id;if(null==qn[e]){if(qn[e]=!0,Vn){var n=Un.length-1;while(n>zn&&Un[n].id>t.id)n--;Un.splice(n+1,0,t)}else Un.push(t);Hn||(Hn=!0,he(Jn))}}var er=0,nr=function(t,e,n,r,o){this.vm=t,o&&(t._watcher=this),t._watchers.push(this),r?(this.deep=!!r.deep,this.user=!!r.user,this.lazy=!!r.lazy,this.sync=!!r.sync,this.before=r.before):this.deep=this.user=this.lazy=this.sync=!1,this.cb=n,this.id=++er,this.active=!0,this.dirty=this.lazy,this.deps=[],this.newDeps=[],this.depIds=new lt,this.newDepIds=new lt,this.expression=\"\",\"function\"===typeof e?this.getter=e:(this.getter=W(e),this.getter||(this.getter=R)),this.value=this.lazy?void 0:this.get()};nr.prototype.get=function(){var t;mt(this);var e=this.vm;try{t=this.getter.call(e,e)}catch(Ca){if(!this.user)throw Ca;ee(Ca,e,'getter for watcher \"'+this.expression+'\"')}finally{this.deep&&ye(t),gt(),this.cleanupDeps()}return t},nr.prototype.addDep=function(t){var e=t.id;this.newDepIds.has(e)||(this.newDepIds.add(e),this.newDeps.push(t),this.depIds.has(e)||t.addSub(this))},nr.prototype.cleanupDeps=function(){var t=this.deps.length;while(t--){var e=this.deps[t];this.newDepIds.has(e.id)||e.removeSub(this)}var n=this.depIds;this.depIds=this.newDepIds,this.newDepIds=n,this.newDepIds.clear(),n=this.deps,this.deps=this.newDeps,this.newDeps=n,this.newDeps.length=0},nr.prototype.update=function(){this.lazy?this.dirty=!0:this.sync?this.run():tr(this)},nr.prototype.run=function(){if(this.active){var t=this.get();if(t!==this.value||c(t)||this.deep){var e=this.value;if(this.value=t,this.user)try{this.cb.call(this.vm,t,e)}catch(Ca){ee(Ca,this.vm,'callback for watcher \"'+this.expression+'\"')}else this.cb.call(this.vm,t,e)}}},nr.prototype.evaluate=function(){this.value=this.get(),this.dirty=!1},nr.prototype.depend=function(){var t=this.deps.length;while(t--)this.deps[t].depend()},nr.prototype.teardown=function(){if(this.active){this.vm._isBeingDestroyed||g(this.vm._watchers,this);var t=this.deps.length;while(t--)this.deps[t].removeSub(this);this.active=!1}};var rr={enumerable:!0,configurable:!0,get:R,set:R};function or(t,e,n){rr.get=function(){return this[e][n]},rr.set=function(t){this[e][n]=t},Object.defineProperty(t,n,rr)}function ir(t){t._watchers=[];var e=t.$options;e.props&&ar(t,e.props),e.methods&&hr(t,e.methods),e.data?sr(t):Rt(t._data={},!0),e.computed&&fr(t,e.computed),e.watch&&e.watch!==it&&vr(t,e.watch)}function ar(t,e){var n=t.$options.propsData||{},r=t._props={},o=t.$options._propKeys=[],i=!t.$parent;i||$t(!1);var a=function(i){o.push(i);var a=Jt(i,e,n,t);It(r,i,a),i in t||or(t,\"_props\",i)};for(var s in e)a(s);$t(!0)}function sr(t){var e=t.$options.data;e=t._data=\"function\"===typeof e?cr(e,t):e||{},f(e)||(e={});var n=Object.keys(e),r=t.$options.props,o=(t.$options.methods,n.length);while(o--){var i=n[o];0,r&&_(r,i)||V(i)||or(t,\"_data\",i)}Rt(e,!0)}function cr(t,e){mt();try{return t.call(e,e)}catch(Ca){return ee(Ca,e,\"data()\"),{}}finally{gt()}}var ur={lazy:!0};function fr(t,e){var n=t._computedWatchers=Object.create(null),r=ct();for(var o in e){var i=e[o],a=\"function\"===typeof i?i:i.get;0,r||(n[o]=new nr(t,a||R,R,ur)),o in t||lr(t,o,i)}}function lr(t,e,n){var r=!ct();\"function\"===typeof n?(rr.get=r?pr(e):dr(n),rr.set=R):(rr.get=n.get?r&&!1!==n.cache?pr(e):dr(n.get):R,rr.set=n.set||R),Object.defineProperty(t,e,rr)}function pr(t){return function(){var e=this._computedWatchers&&this._computedWatchers[t];if(e)return e.dirty&&e.evaluate(),vt.target&&e.depend(),e.value}}function dr(t){return function(){return t.call(this,this)}}function hr(t,e){t.$options.props;for(var n in e)t[n]=\"function\"!==typeof e[n]?R:$(e[n],t)}function vr(t,e){for(var n in e){var r=e[n];if(Array.isArray(r))for(var o=0;o<r.length;o++)yr(t,n,r[o]);else yr(t,n,r)}}function yr(t,e,n,r){return f(n)&&(r=n,n=n.handler),\"string\"===typeof n&&(n=t[n]),t.$watch(e,n,r)}function mr(t){var e={get:function(){return this._data}},n={get:function(){return this._props}};Object.defineProperty(t.prototype,\"$data\",e),Object.defineProperty(t.prototype,\"$props\",n),t.prototype.$set=Mt,t.prototype.$delete=Nt,t.prototype.$watch=function(t,e,n){var r=this;if(f(e))return yr(r,t,e,n);n=n||{},n.user=!0;var o=new nr(r,t,e,n);if(n.immediate)try{e.call(r,o.value)}catch(i){ee(i,r,'callback for immediate watcher \"'+o.expression+'\"')}return function(){o.teardown()}}}var gr=0;function br(t){t.prototype._init=function(t){var e=this;e._uid=gr++,e._isVue=!0,t&&t._isComponent?_r(e,t):e.$options=Kt(wr(e.constructor),t||{},e),e._renderProxy=e,e._self=e,Pn(e),On(e),vn(e),Fn(e,\"beforeCreate\"),$e(e),ir(e),Se(e),Fn(e,\"created\"),e.$options.el&&e.$mount(e.$options.el)}}function _r(t,e){var n=t.$options=Object.create(t.constructor.options),r=e._parentVnode;n.parent=e.parent,n._parentVnode=r;var o=r.componentOptions;n.propsData=o.propsData,n._parentListeners=o.listeners,n._renderChildren=o.children,n._componentTag=o.tag,e.render&&(n.render=e.render,n.staticRenderFns=e.staticRenderFns)}function wr(t){var e=t.options;if(t.super){var n=wr(t.super),r=t.superOptions;if(n!==r){t.superOptions=n;var o=xr(t);o&&T(t.extendOptions,o),e=t.options=Kt(n,t.extendOptions),e.name&&(e.components[e.name]=t)}}return e}function xr(t){var e,n=t.options,r=t.sealedOptions;for(var o in n)n[o]!==r[o]&&(e||(e={}),e[o]=n[o]);return e}function Cr(t){this._init(t)}function Or(t){t.use=function(t){var e=this._installedPlugins||(this._installedPlugins=[]);if(e.indexOf(t)>-1)return this;var n=E(arguments,1);return n.unshift(this),\"function\"===typeof t.install?t.install.apply(t,n):\"function\"===typeof t&&t.apply(null,n),e.push(t),this}}function Ar(t){t.mixin=function(t){return this.options=Kt(this.options,t),this}}function kr(t){t.cid=0;var e=1;t.extend=function(t){t=t||{};var n=this,r=n.cid,o=t._Ctor||(t._Ctor={});if(o[r])return o[r];var i=t.name||n.options.name;var a=function(t){this._init(t)};return a.prototype=Object.create(n.prototype),a.prototype.constructor=a,a.cid=e++,a.options=Kt(n.options,t),a[\"super\"]=n,a.options.props&&jr(a),a.options.computed&&Sr(a),a.extend=n.extend,a.mixin=n.mixin,a.use=n.use,U.forEach((function(t){a[t]=n[t]})),i&&(a.options.components[i]=a),a.superOptions=n.options,a.extendOptions=t,a.sealedOptions=T({},a.options),o[r]=a,a}}function jr(t){var e=t.options.props;for(var n in e)or(t.prototype,\"_props\",n)}function Sr(t){var e=t.options.computed;for(var n in e)lr(t.prototype,n,e[n])}function $r(t){U.forEach((function(e){t[e]=function(t,n){return n?(\"component\"===e&&f(n)&&(n.name=n.name||t,n=this.options._base.extend(n)),\"directive\"===e&&\"function\"===typeof n&&(n={bind:n,update:n}),this.options[e+\"s\"][t]=n,n):this.options[e+\"s\"][t]}}))}function Er(t){return t&&(t.Ctor.options.name||t.tag)}function Tr(t,e){return Array.isArray(t)?t.indexOf(e)>-1:\"string\"===typeof t?t.split(\",\").indexOf(e)>-1:!!l(t)&&t.test(e)}function Pr(t,e){var n=t.cache,r=t.keys,o=t._vnode;for(var i in n){var a=n[i];if(a){var s=Er(a.componentOptions);s&&!e(s)&&Rr(n,i,r,o)}}}function Rr(t,e,n,r){var o=t[e];!o||r&&o.tag===r.tag||o.componentInstance.$destroy(),t[e]=null,g(n,e)}br(Cr),mr(Cr),$n(Cr),Rn(Cr),gn(Cr);var Ir=[String,RegExp,Array],Mr={name:\"keep-alive\",abstract:!0,props:{include:Ir,exclude:Ir,max:[String,Number]},created:function(){this.cache=Object.create(null),this.keys=[]},destroyed:function(){for(var t in this.cache)Rr(this.cache,t,this.keys)},mounted:function(){var t=this;this.$watch(\"include\",(function(e){Pr(t,(function(t){return Tr(e,t)}))})),this.$watch(\"exclude\",(function(e){Pr(t,(function(t){return!Tr(e,t)}))}))},render:function(){var t=this.$slots.default,e=Cn(t),n=e&&e.componentOptions;if(n){var r=Er(n),o=this,i=o.include,a=o.exclude;if(i&&(!r||!Tr(i,r))||a&&r&&Tr(a,r))return e;var s=this,c=s.cache,u=s.keys,f=null==e.key?n.Ctor.cid+(n.tag?\"::\"+n.tag:\"\"):e.key;c[f]?(e.componentInstance=c[f].componentInstance,g(u,f),u.push(f)):(c[f]=e,u.push(f),this.max&&u.length>parseInt(this.max)&&Rr(c,u[0],u,this._vnode)),e.data.keepAlive=!0}return e||t&&t[0]}},Nr={KeepAlive:Mr};function Lr(t){var e={get:function(){return q}};Object.defineProperty(t,\"config\",e),t.util={warn:dt,extend:T,mergeOptions:Kt,defineReactive:It},t.set=Mt,t.delete=Nt,t.nextTick=he,t.observable=function(t){return Rt(t),t},t.options=Object.create(null),U.forEach((function(e){t.options[e+\"s\"]=Object.create(null)})),t.options._base=t,T(t.options.components,Nr),Or(t),Ar(t),kr(t),$r(t)}Lr(Cr),Object.defineProperty(Cr.prototype,\"$isServer\",{get:ct}),Object.defineProperty(Cr.prototype,\"$ssrContext\",{get:function(){return this.$vnode&&this.$vnode.ssrContext}}),Object.defineProperty(Cr,\"FunctionalRenderContext\",{value:Ye}),Cr.version=\"2.6.11\";var Dr=y(\"style,class\"),Fr=y(\"input,textarea,option,select,progress\"),Ur=function(t,e,n){return\"value\"===n&&Fr(t)&&\"button\"!==e||\"selected\"===n&&\"option\"===t||\"checked\"===n&&\"input\"===t||\"muted\"===n&&\"video\"===t},Br=y(\"contenteditable,draggable,spellcheck\"),qr=y(\"events,caret,typing,plaintext-only\"),Hr=function(t,e){return Kr(e)||\"false\"===e?\"false\":\"contenteditable\"===t&&qr(e)?e:\"true\"},Vr=y(\"allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,translate,truespeed,typemustmatch,visible\"),zr=\"http://www.w3.org/1999/xlink\",Gr=function(t){return\":\"===t.charAt(5)&&\"xlink\"===t.slice(0,5)},Wr=function(t){return Gr(t)?t.slice(6,t.length):\"\"},Kr=function(t){return null==t||!1===t};function Xr(t){var e=t.data,n=t,r=t;while(o(r.componentInstance))r=r.componentInstance._vnode,r&&r.data&&(e=Jr(r.data,e));while(o(n=n.parent))n&&n.data&&(e=Jr(e,n.data));return Yr(e.staticClass,e.class)}function Jr(t,e){return{staticClass:Qr(t.staticClass,e.staticClass),class:o(t.class)?[t.class,e.class]:e.class}}function Yr(t,e){return o(t)||o(e)?Qr(t,Zr(e)):\"\"}function Qr(t,e){return t?e?t+\" \"+e:t:e||\"\"}function Zr(t){return Array.isArray(t)?to(t):c(t)?eo(t):\"string\"===typeof t?t:\"\"}function to(t){for(var e,n=\"\",r=0,i=t.length;r<i;r++)o(e=Zr(t[r]))&&\"\"!==e&&(n&&(n+=\" \"),n+=e);return n}function eo(t){var e=\"\";for(var n in t)t[n]&&(e&&(e+=\" \"),e+=n);return e}var no={svg:\"http://www.w3.org/2000/svg\",math:\"http://www.w3.org/1998/Math/MathML\"},ro=y(\"html,body,base,head,link,meta,style,title,address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,output,progress,select,textarea,details,dialog,menu,menuitem,summary,content,element,shadow,template,blockquote,iframe,tfoot\"),oo=y(\"svg,animate,circle,clippath,cursor,defs,desc,ellipse,filter,font-face,foreignObject,g,glyph,image,line,marker,mask,missing-glyph,path,pattern,polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view\",!0),io=function(t){return ro(t)||oo(t)};function ao(t){return oo(t)?\"svg\":\"math\"===t?\"math\":void 0}var so=Object.create(null);function co(t){if(!J)return!0;if(io(t))return!1;if(t=t.toLowerCase(),null!=so[t])return so[t];var e=document.createElement(t);return t.indexOf(\"-\")>-1?so[t]=e.constructor===window.HTMLUnknownElement||e.constructor===window.HTMLElement:so[t]=/HTMLUnknownElement/.test(e.toString())}var uo=y(\"text,number,password,search,email,tel,url\");function fo(t){if(\"string\"===typeof t){var e=document.querySelector(t);return e||document.createElement(\"div\")}return t}function lo(t,e){var n=document.createElement(t);return\"select\"!==t||e.data&&e.data.attrs&&void 0!==e.data.attrs.multiple&&n.setAttribute(\"multiple\",\"multiple\"),n}function po(t,e){return document.createElementNS(no[t],e)}function ho(t){return document.createTextNode(t)}function vo(t){return document.createComment(t)}function yo(t,e,n){t.insertBefore(e,n)}function mo(t,e){t.removeChild(e)}function go(t,e){t.appendChild(e)}function bo(t){return t.parentNode}function _o(t){return t.nextSibling}function wo(t){return t.tagName}function xo(t,e){t.textContent=e}function Co(t,e){t.setAttribute(e,\"\")}var Oo=Object.freeze({createElement:lo,createElementNS:po,createTextNode:ho,createComment:vo,insertBefore:yo,removeChild:mo,appendChild:go,parentNode:bo,nextSibling:_o,tagName:wo,setTextContent:xo,setStyleScope:Co}),Ao={create:function(t,e){ko(e)},update:function(t,e){t.data.ref!==e.data.ref&&(ko(t,!0),ko(e))},destroy:function(t){ko(t,!0)}};function ko(t,e){var n=t.data.ref;if(o(n)){var r=t.context,i=t.componentInstance||t.elm,a=r.$refs;e?Array.isArray(a[n])?g(a[n],i):a[n]===i&&(a[n]=void 0):t.data.refInFor?Array.isArray(a[n])?a[n].indexOf(i)<0&&a[n].push(i):a[n]=[i]:a[n]=i}}var jo=new bt(\"\",{},[]),So=[\"create\",\"activate\",\"update\",\"remove\",\"destroy\"];function $o(t,e){return t.key===e.key&&(t.tag===e.tag&&t.isComment===e.isComment&&o(t.data)===o(e.data)&&Eo(t,e)||i(t.isAsyncPlaceholder)&&t.asyncFactory===e.asyncFactory&&r(e.asyncFactory.error))}function Eo(t,e){if(\"input\"!==t.tag)return!0;var n,r=o(n=t.data)&&o(n=n.attrs)&&n.type,i=o(n=e.data)&&o(n=n.attrs)&&n.type;return r===i||uo(r)&&uo(i)}function To(t,e,n){var r,i,a={};for(r=e;r<=n;++r)i=t[r].key,o(i)&&(a[i]=r);return a}function Po(t){var e,n,a={},c=t.modules,u=t.nodeOps;for(e=0;e<So.length;++e)for(a[So[e]]=[],n=0;n<c.length;++n)o(c[n][So[e]])&&a[So[e]].push(c[n][So[e]]);function f(t){return new bt(u.tagName(t).toLowerCase(),{},[],void 0,t)}function l(t,e){function n(){0===--n.listeners&&p(t)}return n.listeners=e,n}function p(t){var e=u.parentNode(t);o(e)&&u.removeChild(e,t)}function d(t,e,n,r,a,s,c){if(o(t.elm)&&o(s)&&(t=s[c]=Ct(t)),t.isRootInsert=!a,!h(t,e,n,r)){var f=t.data,l=t.children,p=t.tag;o(p)?(t.elm=t.ns?u.createElementNS(t.ns,p):u.createElement(p,t),x(t),b(t,l,e),o(f)&&w(t,e),g(n,t.elm,r)):i(t.isComment)?(t.elm=u.createComment(t.text),g(n,t.elm,r)):(t.elm=u.createTextNode(t.text),g(n,t.elm,r))}}function h(t,e,n,r){var a=t.data;if(o(a)){var s=o(t.componentInstance)&&a.keepAlive;if(o(a=a.hook)&&o(a=a.init)&&a(t,!1),o(t.componentInstance))return v(t,e),g(n,t.elm,r),i(s)&&m(t,e,n,r),!0}}function v(t,e){o(t.data.pendingInsert)&&(e.push.apply(e,t.data.pendingInsert),t.data.pendingInsert=null),t.elm=t.componentInstance.$el,_(t)?(w(t,e),x(t)):(ko(t),e.push(t))}function m(t,e,n,r){var i,s=t;while(s.componentInstance)if(s=s.componentInstance._vnode,o(i=s.data)&&o(i=i.transition)){for(i=0;i<a.activate.length;++i)a.activate[i](jo,s);e.push(s);break}g(n,t.elm,r)}function g(t,e,n){o(t)&&(o(n)?u.parentNode(n)===t&&u.insertBefore(t,e,n):u.appendChild(t,e))}function b(t,e,n){if(Array.isArray(e)){0;for(var r=0;r<e.length;++r)d(e[r],n,t.elm,null,!0,e,r)}else s(t.text)&&u.appendChild(t.elm,u.createTextNode(String(t.text)))}function _(t){while(t.componentInstance)t=t.componentInstance._vnode;return o(t.tag)}function w(t,n){for(var r=0;r<a.create.length;++r)a.create[r](jo,t);e=t.data.hook,o(e)&&(o(e.create)&&e.create(jo,t),o(e.insert)&&n.push(t))}function x(t){var e;if(o(e=t.fnScopeId))u.setStyleScope(t.elm,e);else{var n=t;while(n)o(e=n.context)&&o(e=e.$options._scopeId)&&u.setStyleScope(t.elm,e),n=n.parent}o(e=En)&&e!==t.context&&e!==t.fnContext&&o(e=e.$options._scopeId)&&u.setStyleScope(t.elm,e)}function C(t,e,n,r,o,i){for(;r<=o;++r)d(n[r],i,t,e,!1,n,r)}function O(t){var e,n,r=t.data;if(o(r))for(o(e=r.hook)&&o(e=e.destroy)&&e(t),e=0;e<a.destroy.length;++e)a.destroy[e](t);if(o(e=t.children))for(n=0;n<t.children.length;++n)O(t.children[n])}function A(t,e,n){for(;e<=n;++e){var r=t[e];o(r)&&(o(r.tag)?(k(r),O(r)):p(r.elm))}}function k(t,e){if(o(e)||o(t.data)){var n,r=a.remove.length+1;for(o(e)?e.listeners+=r:e=l(t.elm,r),o(n=t.componentInstance)&&o(n=n._vnode)&&o(n.data)&&k(n,e),n=0;n<a.remove.length;++n)a.remove[n](t,e);o(n=t.data.hook)&&o(n=n.remove)?n(t,e):e()}else p(t.elm)}function j(t,e,n,i,a){var s,c,f,l,p=0,h=0,v=e.length-1,y=e[0],m=e[v],g=n.length-1,b=n[0],_=n[g],w=!a;while(p<=v&&h<=g)r(y)?y=e[++p]:r(m)?m=e[--v]:$o(y,b)?($(y,b,i,n,h),y=e[++p],b=n[++h]):$o(m,_)?($(m,_,i,n,g),m=e[--v],_=n[--g]):$o(y,_)?($(y,_,i,n,g),w&&u.insertBefore(t,y.elm,u.nextSibling(m.elm)),y=e[++p],_=n[--g]):$o(m,b)?($(m,b,i,n,h),w&&u.insertBefore(t,m.elm,y.elm),m=e[--v],b=n[++h]):(r(s)&&(s=To(e,p,v)),c=o(b.key)?s[b.key]:S(b,e,p,v),r(c)?d(b,i,t,y.elm,!1,n,h):(f=e[c],$o(f,b)?($(f,b,i,n,h),e[c]=void 0,w&&u.insertBefore(t,f.elm,y.elm)):d(b,i,t,y.elm,!1,n,h)),b=n[++h]);p>v?(l=r(n[g+1])?null:n[g+1].elm,C(t,l,n,h,g,i)):h>g&&A(e,p,v)}function S(t,e,n,r){for(var i=n;i<r;i++){var a=e[i];if(o(a)&&$o(t,a))return i}}function $(t,e,n,s,c,f){if(t!==e){o(e.elm)&&o(s)&&(e=s[c]=Ct(e));var l=e.elm=t.elm;if(i(t.isAsyncPlaceholder))o(e.asyncFactory.resolved)?P(t.elm,e,n):e.isAsyncPlaceholder=!0;else if(i(e.isStatic)&&i(t.isStatic)&&e.key===t.key&&(i(e.isCloned)||i(e.isOnce)))e.componentInstance=t.componentInstance;else{var p,d=e.data;o(d)&&o(p=d.hook)&&o(p=p.prepatch)&&p(t,e);var h=t.children,v=e.children;if(o(d)&&_(e)){for(p=0;p<a.update.length;++p)a.update[p](t,e);o(p=d.hook)&&o(p=p.update)&&p(t,e)}r(e.text)?o(h)&&o(v)?h!==v&&j(l,h,v,n,f):o(v)?(o(t.text)&&u.setTextContent(l,\"\"),C(l,null,v,0,v.length-1,n)):o(h)?A(h,0,h.length-1):o(t.text)&&u.setTextContent(l,\"\"):t.text!==e.text&&u.setTextContent(l,e.text),o(d)&&o(p=d.hook)&&o(p=p.postpatch)&&p(t,e)}}}function E(t,e,n){if(i(n)&&o(t.parent))t.parent.data.pendingInsert=e;else for(var r=0;r<e.length;++r)e[r].data.hook.insert(e[r])}var T=y(\"attrs,class,staticClass,staticStyle,key\");function P(t,e,n,r){var a,s=e.tag,c=e.data,u=e.children;if(r=r||c&&c.pre,e.elm=t,i(e.isComment)&&o(e.asyncFactory))return e.isAsyncPlaceholder=!0,!0;if(o(c)&&(o(a=c.hook)&&o(a=a.init)&&a(e,!0),o(a=e.componentInstance)))return v(e,n),!0;if(o(s)){if(o(u))if(t.hasChildNodes())if(o(a=c)&&o(a=a.domProps)&&o(a=a.innerHTML)){if(a!==t.innerHTML)return!1}else{for(var f=!0,l=t.firstChild,p=0;p<u.length;p++){if(!l||!P(l,u[p],n,r)){f=!1;break}l=l.nextSibling}if(!f||l)return!1}else b(e,u,n);if(o(c)){var d=!1;for(var h in c)if(!T(h)){d=!0,w(e,n);break}!d&&c[\"class\"]&&ye(c[\"class\"])}}else t.data!==e.text&&(t.data=e.text);return!0}return function(t,e,n,s){if(!r(e)){var c=!1,l=[];if(r(t))c=!0,d(e,l);else{var p=o(t.nodeType);if(!p&&$o(t,e))$(t,e,l,null,null,s);else{if(p){if(1===t.nodeType&&t.hasAttribute(F)&&(t.removeAttribute(F),n=!0),i(n)&&P(t,e,l))return E(e,l,!0),t;t=f(t)}var h=t.elm,v=u.parentNode(h);if(d(e,l,h._leaveCb?null:v,u.nextSibling(h)),o(e.parent)){var y=e.parent,m=_(e);while(y){for(var g=0;g<a.destroy.length;++g)a.destroy[g](y);if(y.elm=e.elm,m){for(var b=0;b<a.create.length;++b)a.create[b](jo,y);var w=y.data.hook.insert;if(w.merged)for(var x=1;x<w.fns.length;x++)w.fns[x]()}else ko(y);y=y.parent}}o(v)?A([t],0,0):o(t.tag)&&O(t)}}return E(e,l,c),e.elm}o(t)&&O(t)}}var Ro={create:Io,update:Io,destroy:function(t){Io(t,jo)}};function Io(t,e){(t.data.directives||e.data.directives)&&Mo(t,e)}function Mo(t,e){var n,r,o,i=t===jo,a=e===jo,s=Lo(t.data.directives,t.context),c=Lo(e.data.directives,e.context),u=[],f=[];for(n in c)r=s[n],o=c[n],r?(o.oldValue=r.value,o.oldArg=r.arg,Fo(o,\"update\",e,t),o.def&&o.def.componentUpdated&&f.push(o)):(Fo(o,\"bind\",e,t),o.def&&o.def.inserted&&u.push(o));if(u.length){var l=function(){for(var n=0;n<u.length;n++)Fo(u[n],\"inserted\",e,t)};i?we(e,\"insert\",l):l()}if(f.length&&we(e,\"postpatch\",(function(){for(var n=0;n<f.length;n++)Fo(f[n],\"componentUpdated\",e,t)})),!i)for(n in s)c[n]||Fo(s[n],\"unbind\",t,t,a)}var No=Object.create(null);function Lo(t,e){var n,r,o=Object.create(null);if(!t)return o;for(n=0;n<t.length;n++)r=t[n],r.modifiers||(r.modifiers=No),o[Do(r)]=r,r.def=Xt(e.$options,\"directives\",r.name,!0);return o}function Do(t){return t.rawName||t.name+\".\"+Object.keys(t.modifiers||{}).join(\".\")}function Fo(t,e,n,r,o){var i=t.def&&t.def[e];if(i)try{i(n.elm,t,n,r,o)}catch(Ca){ee(Ca,n.context,\"directive \"+t.name+\" \"+e+\" hook\")}}var Uo=[Ao,Ro];function Bo(t,e){var n=e.componentOptions;if((!o(n)||!1!==n.Ctor.options.inheritAttrs)&&(!r(t.data.attrs)||!r(e.data.attrs))){var i,a,s,c=e.elm,u=t.data.attrs||{},f=e.data.attrs||{};for(i in o(f.__ob__)&&(f=e.data.attrs=T({},f)),f)a=f[i],s=u[i],s!==a&&qo(c,i,a);for(i in(tt||nt)&&f.value!==u.value&&qo(c,\"value\",f.value),u)r(f[i])&&(Gr(i)?c.removeAttributeNS(zr,Wr(i)):Br(i)||c.removeAttribute(i))}}function qo(t,e,n){t.tagName.indexOf(\"-\")>-1?Ho(t,e,n):Vr(e)?Kr(n)?t.removeAttribute(e):(n=\"allowfullscreen\"===e&&\"EMBED\"===t.tagName?\"true\":e,t.setAttribute(e,n)):Br(e)?t.setAttribute(e,Hr(e,n)):Gr(e)?Kr(n)?t.removeAttributeNS(zr,Wr(e)):t.setAttributeNS(zr,e,n):Ho(t,e,n)}function Ho(t,e,n){if(Kr(n))t.removeAttribute(e);else{if(tt&&!et&&\"TEXTAREA\"===t.tagName&&\"placeholder\"===e&&\"\"!==n&&!t.__ieph){var r=function(e){e.stopImmediatePropagation(),t.removeEventListener(\"input\",r)};t.addEventListener(\"input\",r),t.__ieph=!0}t.setAttribute(e,n)}}var Vo={create:Bo,update:Bo};function zo(t,e){var n=e.elm,i=e.data,a=t.data;if(!(r(i.staticClass)&&r(i.class)&&(r(a)||r(a.staticClass)&&r(a.class)))){var s=Xr(e),c=n._transitionClasses;o(c)&&(s=Qr(s,Zr(c))),s!==n._prevClass&&(n.setAttribute(\"class\",s),n._prevClass=s)}}var Go,Wo={create:zo,update:zo},Ko=\"__r\",Xo=\"__c\";function Jo(t){if(o(t[Ko])){var e=tt?\"change\":\"input\";t[e]=[].concat(t[Ko],t[e]||[]),delete t[Ko]}o(t[Xo])&&(t.change=[].concat(t[Xo],t.change||[]),delete t[Xo])}function Yo(t,e,n){var r=Go;return function o(){var i=e.apply(null,arguments);null!==i&&ti(t,o,n,r)}}var Qo=ae&&!(ot&&Number(ot[1])<=53);function Zo(t,e,n,r){if(Qo){var o=Wn,i=e;e=i._wrapper=function(t){if(t.target===t.currentTarget||t.timeStamp>=o||t.timeStamp<=0||t.target.ownerDocument!==document)return i.apply(this,arguments)}}Go.addEventListener(t,e,at?{capture:n,passive:r}:n)}function ti(t,e,n,r){(r||Go).removeEventListener(t,e._wrapper||e,n)}function ei(t,e){if(!r(t.data.on)||!r(e.data.on)){var n=e.data.on||{},o=t.data.on||{};Go=e.elm,Jo(n),_e(n,o,Zo,ti,Yo,e.context),Go=void 0}}var ni,ri={create:ei,update:ei};function oi(t,e){if(!r(t.data.domProps)||!r(e.data.domProps)){var n,i,a=e.elm,s=t.data.domProps||{},c=e.data.domProps||{};for(n in o(c.__ob__)&&(c=e.data.domProps=T({},c)),s)n in c||(a[n]=\"\");for(n in c){if(i=c[n],\"textContent\"===n||\"innerHTML\"===n){if(e.children&&(e.children.length=0),i===s[n])continue;1===a.childNodes.length&&a.removeChild(a.childNodes[0])}if(\"value\"===n&&\"PROGRESS\"!==a.tagName){a._value=i;var u=r(i)?\"\":String(i);ii(a,u)&&(a.value=u)}else if(\"innerHTML\"===n&&oo(a.tagName)&&r(a.innerHTML)){ni=ni||document.createElement(\"div\"),ni.innerHTML=\"<svg>\"+i+\"</svg>\";var f=ni.firstChild;while(a.firstChild)a.removeChild(a.firstChild);while(f.firstChild)a.appendChild(f.firstChild)}else if(i!==s[n])try{a[n]=i}catch(Ca){}}}}function ii(t,e){return!t.composing&&(\"OPTION\"===t.tagName||ai(t,e)||si(t,e))}function ai(t,e){var n=!0;try{n=document.activeElement!==t}catch(Ca){}return n&&t.value!==e}function si(t,e){var n=t.value,r=t._vModifiers;if(o(r)){if(r.number)return v(n)!==v(e);if(r.trim)return n.trim()!==e.trim()}return n!==e}var ci={create:oi,update:oi},ui=w((function(t){var e={},n=/;(?![^(]*\\))/g,r=/:(.+)/;return t.split(n).forEach((function(t){if(t){var n=t.split(r);n.length>1&&(e[n[0].trim()]=n[1].trim())}})),e}));function fi(t){var e=li(t.style);return t.staticStyle?T(t.staticStyle,e):e}function li(t){return Array.isArray(t)?P(t):\"string\"===typeof t?ui(t):t}function pi(t,e){var n,r={};if(e){var o=t;while(o.componentInstance)o=o.componentInstance._vnode,o&&o.data&&(n=fi(o.data))&&T(r,n)}(n=fi(t.data))&&T(r,n);var i=t;while(i=i.parent)i.data&&(n=fi(i.data))&&T(r,n);return r}var di,hi=/^--/,vi=/\\s*!important$/,yi=function(t,e,n){if(hi.test(e))t.style.setProperty(e,n);else if(vi.test(n))t.style.setProperty(k(e),n.replace(vi,\"\"),\"important\");else{var r=gi(e);if(Array.isArray(n))for(var o=0,i=n.length;o<i;o++)t.style[r]=n[o];else t.style[r]=n}},mi=[\"Webkit\",\"Moz\",\"ms\"],gi=w((function(t){if(di=di||document.createElement(\"div\").style,t=C(t),\"filter\"!==t&&t in di)return t;for(var e=t.charAt(0).toUpperCase()+t.slice(1),n=0;n<mi.length;n++){var r=mi[n]+e;if(r in di)return r}}));function bi(t,e){var n=e.data,i=t.data;if(!(r(n.staticStyle)&&r(n.style)&&r(i.staticStyle)&&r(i.style))){var a,s,c=e.elm,u=i.staticStyle,f=i.normalizedStyle||i.style||{},l=u||f,p=li(e.data.style)||{};e.data.normalizedStyle=o(p.__ob__)?T({},p):p;var d=pi(e,!0);for(s in l)r(d[s])&&yi(c,s,\"\");for(s in d)a=d[s],a!==l[s]&&yi(c,s,null==a?\"\":a)}}var _i={create:bi,update:bi},wi=/\\s+/;function xi(t,e){if(e&&(e=e.trim()))if(t.classList)e.indexOf(\" \")>-1?e.split(wi).forEach((function(e){return t.classList.add(e)})):t.classList.add(e);else{var n=\" \"+(t.getAttribute(\"class\")||\"\")+\" \";n.indexOf(\" \"+e+\" \")<0&&t.setAttribute(\"class\",(n+e).trim())}}function Ci(t,e){if(e&&(e=e.trim()))if(t.classList)e.indexOf(\" \")>-1?e.split(wi).forEach((function(e){return t.classList.remove(e)})):t.classList.remove(e),t.classList.length||t.removeAttribute(\"class\");else{var n=\" \"+(t.getAttribute(\"class\")||\"\")+\" \",r=\" \"+e+\" \";while(n.indexOf(r)>=0)n=n.replace(r,\" \");n=n.trim(),n?t.setAttribute(\"class\",n):t.removeAttribute(\"class\")}}function Oi(t){if(t){if(\"object\"===typeof t){var e={};return!1!==t.css&&T(e,Ai(t.name||\"v\")),T(e,t),e}return\"string\"===typeof t?Ai(t):void 0}}var Ai=w((function(t){return{enterClass:t+\"-enter\",enterToClass:t+\"-enter-to\",enterActiveClass:t+\"-enter-active\",leaveClass:t+\"-leave\",leaveToClass:t+\"-leave-to\",leaveActiveClass:t+\"-leave-active\"}})),ki=J&&!et,ji=\"transition\",Si=\"animation\",$i=\"transition\",Ei=\"transitionend\",Ti=\"animation\",Pi=\"animationend\";ki&&(void 0===window.ontransitionend&&void 0!==window.onwebkittransitionend&&($i=\"WebkitTransition\",Ei=\"webkitTransitionEnd\"),void 0===window.onanimationend&&void 0!==window.onwebkitanimationend&&(Ti=\"WebkitAnimation\",Pi=\"webkitAnimationEnd\"));var Ri=J?window.requestAnimationFrame?window.requestAnimationFrame.bind(window):setTimeout:function(t){return t()};function Ii(t){Ri((function(){Ri(t)}))}function Mi(t,e){var n=t._transitionClasses||(t._transitionClasses=[]);n.indexOf(e)<0&&(n.push(e),xi(t,e))}function Ni(t,e){t._transitionClasses&&g(t._transitionClasses,e),Ci(t,e)}function Li(t,e,n){var r=Fi(t,e),o=r.type,i=r.timeout,a=r.propCount;if(!o)return n();var s=o===ji?Ei:Pi,c=0,u=function(){t.removeEventListener(s,f),n()},f=function(e){e.target===t&&++c>=a&&u()};setTimeout((function(){c<a&&u()}),i+1),t.addEventListener(s,f)}var Di=/\\b(transform|all)(,|$)/;function Fi(t,e){var n,r=window.getComputedStyle(t),o=(r[$i+\"Delay\"]||\"\").split(\", \"),i=(r[$i+\"Duration\"]||\"\").split(\", \"),a=Ui(o,i),s=(r[Ti+\"Delay\"]||\"\").split(\", \"),c=(r[Ti+\"Duration\"]||\"\").split(\", \"),u=Ui(s,c),f=0,l=0;e===ji?a>0&&(n=ji,f=a,l=i.length):e===Si?u>0&&(n=Si,f=u,l=c.length):(f=Math.max(a,u),n=f>0?a>u?ji:Si:null,l=n?n===ji?i.length:c.length:0);var p=n===ji&&Di.test(r[$i+\"Property\"]);return{type:n,timeout:f,propCount:l,hasTransform:p}}function Ui(t,e){while(t.length<e.length)t=t.concat(t);return Math.max.apply(null,e.map((function(e,n){return Bi(e)+Bi(t[n])})))}function Bi(t){return 1e3*Number(t.slice(0,-1).replace(\",\",\".\"))}function qi(t,e){var n=t.elm;o(n._leaveCb)&&(n._leaveCb.cancelled=!0,n._leaveCb());var i=Oi(t.data.transition);if(!r(i)&&!o(n._enterCb)&&1===n.nodeType){var a=i.css,s=i.type,u=i.enterClass,f=i.enterToClass,l=i.enterActiveClass,p=i.appearClass,d=i.appearToClass,h=i.appearActiveClass,y=i.beforeEnter,m=i.enter,g=i.afterEnter,b=i.enterCancelled,_=i.beforeAppear,w=i.appear,x=i.afterAppear,C=i.appearCancelled,O=i.duration,A=En,k=En.$vnode;while(k&&k.parent)A=k.context,k=k.parent;var j=!A._isMounted||!t.isRootInsert;if(!j||w||\"\"===w){var S=j&&p?p:u,$=j&&h?h:l,E=j&&d?d:f,T=j&&_||y,P=j&&\"function\"===typeof w?w:m,R=j&&x||g,I=j&&C||b,M=v(c(O)?O.enter:O);0;var N=!1!==a&&!et,L=zi(P),F=n._enterCb=D((function(){N&&(Ni(n,E),Ni(n,$)),F.cancelled?(N&&Ni(n,S),I&&I(n)):R&&R(n),n._enterCb=null}));t.data.show||we(t,\"insert\",(function(){var e=n.parentNode,r=e&&e._pending&&e._pending[t.key];r&&r.tag===t.tag&&r.elm._leaveCb&&r.elm._leaveCb(),P&&P(n,F)})),T&&T(n),N&&(Mi(n,S),Mi(n,$),Ii((function(){Ni(n,S),F.cancelled||(Mi(n,E),L||(Vi(M)?setTimeout(F,M):Li(n,s,F)))}))),t.data.show&&(e&&e(),P&&P(n,F)),N||L||F()}}}function Hi(t,e){var n=t.elm;o(n._enterCb)&&(n._enterCb.cancelled=!0,n._enterCb());var i=Oi(t.data.transition);if(r(i)||1!==n.nodeType)return e();if(!o(n._leaveCb)){var a=i.css,s=i.type,u=i.leaveClass,f=i.leaveToClass,l=i.leaveActiveClass,p=i.beforeLeave,d=i.leave,h=i.afterLeave,y=i.leaveCancelled,m=i.delayLeave,g=i.duration,b=!1!==a&&!et,_=zi(d),w=v(c(g)?g.leave:g);0;var x=n._leaveCb=D((function(){n.parentNode&&n.parentNode._pending&&(n.parentNode._pending[t.key]=null),b&&(Ni(n,f),Ni(n,l)),x.cancelled?(b&&Ni(n,u),y&&y(n)):(e(),h&&h(n)),n._leaveCb=null}));m?m(C):C()}function C(){x.cancelled||(!t.data.show&&n.parentNode&&((n.parentNode._pending||(n.parentNode._pending={}))[t.key]=t),p&&p(n),b&&(Mi(n,u),Mi(n,l),Ii((function(){Ni(n,u),x.cancelled||(Mi(n,f),_||(Vi(w)?setTimeout(x,w):Li(n,s,x)))}))),d&&d(n,x),b||_||x())}}function Vi(t){return\"number\"===typeof t&&!isNaN(t)}function zi(t){if(r(t))return!1;var e=t.fns;return o(e)?zi(Array.isArray(e)?e[0]:e):(t._length||t.length)>1}function Gi(t,e){!0!==e.data.show&&qi(e)}var Wi=J?{create:Gi,activate:Gi,remove:function(t,e){!0!==t.data.show?Hi(t,e):e()}}:{},Ki=[Vo,Wo,ri,ci,_i,Wi],Xi=Ki.concat(Uo),Ji=Po({nodeOps:Oo,modules:Xi});et&&document.addEventListener(\"selectionchange\",(function(){var t=document.activeElement;t&&t.vmodel&&oa(t,\"input\")}));var Yi={inserted:function(t,e,n,r){\"select\"===n.tag?(r.elm&&!r.elm._vOptions?we(n,\"postpatch\",(function(){Yi.componentUpdated(t,e,n)})):Qi(t,e,n.context),t._vOptions=[].map.call(t.options,ea)):(\"textarea\"===n.tag||uo(t.type))&&(t._vModifiers=e.modifiers,e.modifiers.lazy||(t.addEventListener(\"compositionstart\",na),t.addEventListener(\"compositionend\",ra),t.addEventListener(\"change\",ra),et&&(t.vmodel=!0)))},componentUpdated:function(t,e,n){if(\"select\"===n.tag){Qi(t,e,n.context);var r=t._vOptions,o=t._vOptions=[].map.call(t.options,ea);if(o.some((function(t,e){return!N(t,r[e])}))){var i=t.multiple?e.value.some((function(t){return ta(t,o)})):e.value!==e.oldValue&&ta(e.value,o);i&&oa(t,\"change\")}}}};function Qi(t,e,n){Zi(t,e,n),(tt||nt)&&setTimeout((function(){Zi(t,e,n)}),0)}function Zi(t,e,n){var r=e.value,o=t.multiple;if(!o||Array.isArray(r)){for(var i,a,s=0,c=t.options.length;s<c;s++)if(a=t.options[s],o)i=L(r,ea(a))>-1,a.selected!==i&&(a.selected=i);else if(N(ea(a),r))return void(t.selectedIndex!==s&&(t.selectedIndex=s));o||(t.selectedIndex=-1)}}function ta(t,e){return e.every((function(e){return!N(e,t)}))}function ea(t){return\"_value\"in t?t._value:t.value}function na(t){t.target.composing=!0}function ra(t){t.target.composing&&(t.target.composing=!1,oa(t.target,\"input\"))}function oa(t,e){var n=document.createEvent(\"HTMLEvents\");n.initEvent(e,!0,!0),t.dispatchEvent(n)}function ia(t){return!t.componentInstance||t.data&&t.data.transition?t:ia(t.componentInstance._vnode)}var aa={bind:function(t,e,n){var r=e.value;n=ia(n);var o=n.data&&n.data.transition,i=t.__vOriginalDisplay=\"none\"===t.style.display?\"\":t.style.display;r&&o?(n.data.show=!0,qi(n,(function(){t.style.display=i}))):t.style.display=r?i:\"none\"},update:function(t,e,n){var r=e.value,o=e.oldValue;if(!r!==!o){n=ia(n);var i=n.data&&n.data.transition;i?(n.data.show=!0,r?qi(n,(function(){t.style.display=t.__vOriginalDisplay})):Hi(n,(function(){t.style.display=\"none\"}))):t.style.display=r?t.__vOriginalDisplay:\"none\"}},unbind:function(t,e,n,r,o){o||(t.style.display=t.__vOriginalDisplay)}},sa={model:Yi,show:aa},ca={name:String,appear:Boolean,css:Boolean,mode:String,type:String,enterClass:String,leaveClass:String,enterToClass:String,leaveToClass:String,enterActiveClass:String,leaveActiveClass:String,appearClass:String,appearActiveClass:String,appearToClass:String,duration:[Number,String,Object]};function ua(t){var e=t&&t.componentOptions;return e&&e.Ctor.options.abstract?ua(Cn(e.children)):t}function fa(t){var e={},n=t.$options;for(var r in n.propsData)e[r]=t[r];var o=n._parentListeners;for(var i in o)e[C(i)]=o[i];return e}function la(t,e){if(/\\d-keep-alive$/.test(e.tag))return t(\"keep-alive\",{props:e.componentOptions.propsData})}function pa(t){while(t=t.parent)if(t.data.transition)return!0}function da(t,e){return e.key===t.key&&e.tag===t.tag}var ha=function(t){return t.tag||xn(t)},va=function(t){return\"show\"===t.name},ya={name:\"transition\",props:ca,abstract:!0,render:function(t){var e=this,n=this.$slots.default;if(n&&(n=n.filter(ha),n.length)){0;var r=this.mode;0;var o=n[0];if(pa(this.$vnode))return o;var i=ua(o);if(!i)return o;if(this._leaving)return la(t,o);var a=\"__transition-\"+this._uid+\"-\";i.key=null==i.key?i.isComment?a+\"comment\":a+i.tag:s(i.key)?0===String(i.key).indexOf(a)?i.key:a+i.key:i.key;var c=(i.data||(i.data={})).transition=fa(this),u=this._vnode,f=ua(u);if(i.data.directives&&i.data.directives.some(va)&&(i.data.show=!0),f&&f.data&&!da(i,f)&&!xn(f)&&(!f.componentInstance||!f.componentInstance._vnode.isComment)){var l=f.data.transition=T({},c);if(\"out-in\"===r)return this._leaving=!0,we(l,\"afterLeave\",(function(){e._leaving=!1,e.$forceUpdate()})),la(t,o);if(\"in-out\"===r){if(xn(i))return u;var p,d=function(){p()};we(c,\"afterEnter\",d),we(c,\"enterCancelled\",d),we(l,\"delayLeave\",(function(t){p=t}))}}return o}}},ma=T({tag:String,moveClass:String},ca);delete ma.mode;var ga={props:ma,beforeMount:function(){var t=this,e=this._update;this._update=function(n,r){var o=Tn(t);t.__patch__(t._vnode,t.kept,!1,!0),t._vnode=t.kept,o(),e.call(t,n,r)}},render:function(t){for(var e=this.tag||this.$vnode.data.tag||\"span\",n=Object.create(null),r=this.prevChildren=this.children,o=this.$slots.default||[],i=this.children=[],a=fa(this),s=0;s<o.length;s++){var c=o[s];if(c.tag)if(null!=c.key&&0!==String(c.key).indexOf(\"__vlist\"))i.push(c),n[c.key]=c,(c.data||(c.data={})).transition=a;else;}if(r){for(var u=[],f=[],l=0;l<r.length;l++){var p=r[l];p.data.transition=a,p.data.pos=p.elm.getBoundingClientRect(),n[p.key]?u.push(p):f.push(p)}this.kept=t(e,null,u),this.removed=f}return t(e,null,i)},updated:function(){var t=this.prevChildren,e=this.moveClass||(this.name||\"v\")+\"-move\";t.length&&this.hasMove(t[0].elm,e)&&(t.forEach(ba),t.forEach(_a),t.forEach(wa),this._reflow=document.body.offsetHeight,t.forEach((function(t){if(t.data.moved){var n=t.elm,r=n.style;Mi(n,e),r.transform=r.WebkitTransform=r.transitionDuration=\"\",n.addEventListener(Ei,n._moveCb=function t(r){r&&r.target!==n||r&&!/transform$/.test(r.propertyName)||(n.removeEventListener(Ei,t),n._moveCb=null,Ni(n,e))})}})))},methods:{hasMove:function(t,e){if(!ki)return!1;if(this._hasMove)return this._hasMove;var n=t.cloneNode();t._transitionClasses&&t._transitionClasses.forEach((function(t){Ci(n,t)})),xi(n,e),n.style.display=\"none\",this.$el.appendChild(n);var r=Fi(n);return this.$el.removeChild(n),this._hasMove=r.hasTransform}}};function ba(t){t.elm._moveCb&&t.elm._moveCb(),t.elm._enterCb&&t.elm._enterCb()}function _a(t){t.data.newPos=t.elm.getBoundingClientRect()}function wa(t){var e=t.data.pos,n=t.data.newPos,r=e.left-n.left,o=e.top-n.top;if(r||o){t.data.moved=!0;var i=t.elm.style;i.transform=i.WebkitTransform=\"translate(\"+r+\"px,\"+o+\"px)\",i.transitionDuration=\"0s\"}}var xa={Transition:ya,TransitionGroup:ga};Cr.config.mustUseProp=Ur,Cr.config.isReservedTag=io,Cr.config.isReservedAttr=Dr,Cr.config.getTagNamespace=ao,Cr.config.isUnknownElement=co,T(Cr.options.directives,sa),T(Cr.options.components,xa),Cr.prototype.__patch__=J?Ji:R,Cr.prototype.$mount=function(t,e){return t=t&&J?fo(t):void 0,In(this,t,e)},J&&setTimeout((function(){q.devtools&&ut&&ut.emit(\"init\",Cr)}),0),e[\"a\"]=Cr}).call(this,n(\"c8ba\"))},\"2cf4\":function(t,e,n){var r,o,i,a=n(\"da84\"),s=n(\"d039\"),c=n(\"c6b6\"),u=n(\"0366\"),f=n(\"1be4\"),l=n(\"cc12\"),p=n(\"1cdc\"),d=a.location,h=a.setImmediate,v=a.clearImmediate,y=a.process,m=a.MessageChannel,g=a.Dispatch,b=0,_={},w=\"onreadystatechange\",x=function(t){if(_.hasOwnProperty(t)){var e=_[t];delete _[t],e()}},C=function(t){return function(){x(t)}},O=function(t){x(t.data)},A=function(t){a.postMessage(t+\"\",d.protocol+\"//\"+d.host)};h&&v||(h=function(t){var e=[],n=1;while(arguments.length>n)e.push(arguments[n++]);return _[++b]=function(){(\"function\"==typeof t?t:Function(t)).apply(void 0,e)},r(b),b},v=function(t){delete _[t]},\"process\"==c(y)?r=function(t){y.nextTick(C(t))}:g&&g.now?r=function(t){g.now(C(t))}:m&&!p?(o=new m,i=o.port2,o.port1.onmessage=O,r=u(i.postMessage,i,1)):!a.addEventListener||\"function\"!=typeof postMessage||a.importScripts||s(A)?r=w in l(\"script\")?function(t){f.appendChild(l(\"script\"))[w]=function(){f.removeChild(this),x(t)}}:function(t){setTimeout(C(t),0)}:(r=A,a.addEventListener(\"message\",O,!1))),t.exports={set:h,clear:v}},\"2d00\":function(t,e,n){var r,o,i=n(\"da84\"),a=n(\"342f\"),s=i.process,c=s&&s.versions,u=c&&c.v8;u?(r=u.split(\".\"),o=r[0]+r[1]):a&&(r=a.match(/Edge\\/(\\d+)/),(!r||r[1]>=74)&&(r=a.match(/Chrome\\/(\\d+)/),r&&(o=r[1]))),t.exports=o&&+o},\"2d83\":function(t,e,n){\"use strict\";var r=n(\"387f\");t.exports=function(t,e,n,o,i){var a=new Error(t);return r(a,e,n,o,i)}},\"2e67\":function(t,e,n){\"use strict\";t.exports=function(t){return!(!t||!t.__CANCEL__)}},\"2f62\":function(t,e,n){\"use strict\";(function(t){\n/**\n * vuex v3.1.3\n * (c) 2020 Evan You\n * @license MIT\n */\nfunction n(t){var e=Number(t.version.split(\".\")[0]);if(e>=2)t.mixin({beforeCreate:r});else{var n=t.prototype._init;t.prototype._init=function(t){void 0===t&&(t={}),t.init=t.init?[r].concat(t.init):r,n.call(this,t)}}function r(){var t=this.$options;t.store?this.$store=\"function\"===typeof t.store?t.store():t.store:t.parent&&t.parent.$store&&(this.$store=t.parent.$store)}}var r=\"undefined\"!==typeof window?window:\"undefined\"!==typeof t?t:{},o=r.__VUE_DEVTOOLS_GLOBAL_HOOK__;function i(t){o&&(t._devtoolHook=o,o.emit(\"vuex:init\",t),o.on(\"vuex:travel-to-state\",(function(e){t.replaceState(e)})),t.subscribe((function(t,e){o.emit(\"vuex:mutation\",t,e)})))}function a(t,e){Object.keys(t).forEach((function(n){return e(t[n],n)}))}function s(t){return null!==t&&\"object\"===typeof t}function c(t){return t&&\"function\"===typeof t.then}function u(t,e){return function(){return t(e)}}var f=function(t,e){this.runtime=e,this._children=Object.create(null),this._rawModule=t;var n=t.state;this.state=(\"function\"===typeof n?n():n)||{}},l={namespaced:{configurable:!0}};l.namespaced.get=function(){return!!this._rawModule.namespaced},f.prototype.addChild=function(t,e){this._children[t]=e},f.prototype.removeChild=function(t){delete this._children[t]},f.prototype.getChild=function(t){return this._children[t]},f.prototype.update=function(t){this._rawModule.namespaced=t.namespaced,t.actions&&(this._rawModule.actions=t.actions),t.mutations&&(this._rawModule.mutations=t.mutations),t.getters&&(this._rawModule.getters=t.getters)},f.prototype.forEachChild=function(t){a(this._children,t)},f.prototype.forEachGetter=function(t){this._rawModule.getters&&a(this._rawModule.getters,t)},f.prototype.forEachAction=function(t){this._rawModule.actions&&a(this._rawModule.actions,t)},f.prototype.forEachMutation=function(t){this._rawModule.mutations&&a(this._rawModule.mutations,t)},Object.defineProperties(f.prototype,l);var p=function(t){this.register([],t,!1)};function d(t,e,n){if(e.update(n),n.modules)for(var r in n.modules){if(!e.getChild(r))return void 0;d(t.concat(r),e.getChild(r),n.modules[r])}}p.prototype.get=function(t){return t.reduce((function(t,e){return t.getChild(e)}),this.root)},p.prototype.getNamespace=function(t){var e=this.root;return t.reduce((function(t,n){return e=e.getChild(n),t+(e.namespaced?n+\"/\":\"\")}),\"\")},p.prototype.update=function(t){d([],this.root,t)},p.prototype.register=function(t,e,n){var r=this;void 0===n&&(n=!0);var o=new f(e,n);if(0===t.length)this.root=o;else{var i=this.get(t.slice(0,-1));i.addChild(t[t.length-1],o)}e.modules&&a(e.modules,(function(e,o){r.register(t.concat(o),e,n)}))},p.prototype.unregister=function(t){var e=this.get(t.slice(0,-1)),n=t[t.length-1];e.getChild(n).runtime&&e.removeChild(n)};var h;var v=function(t){var e=this;void 0===t&&(t={}),!h&&\"undefined\"!==typeof window&&window.Vue&&$(window.Vue);var n=t.plugins;void 0===n&&(n=[]);var r=t.strict;void 0===r&&(r=!1),this._committing=!1,this._actions=Object.create(null),this._actionSubscribers=[],this._mutations=Object.create(null),this._wrappedGetters=Object.create(null),this._modules=new p(t),this._modulesNamespaceMap=Object.create(null),this._subscribers=[],this._watcherVM=new h,this._makeLocalGettersCache=Object.create(null);var o=this,a=this,s=a.dispatch,c=a.commit;this.dispatch=function(t,e){return s.call(o,t,e)},this.commit=function(t,e,n){return c.call(o,t,e,n)},this.strict=r;var u=this._modules.root.state;_(this,u,[],this._modules.root),b(this,u),n.forEach((function(t){return t(e)}));var f=void 0!==t.devtools?t.devtools:h.config.devtools;f&&i(this)},y={state:{configurable:!0}};function m(t,e){return e.indexOf(t)<0&&e.push(t),function(){var n=e.indexOf(t);n>-1&&e.splice(n,1)}}function g(t,e){t._actions=Object.create(null),t._mutations=Object.create(null),t._wrappedGetters=Object.create(null),t._modulesNamespaceMap=Object.create(null);var n=t.state;_(t,n,[],t._modules.root,!0),b(t,n,e)}function b(t,e,n){var r=t._vm;t.getters={},t._makeLocalGettersCache=Object.create(null);var o=t._wrappedGetters,i={};a(o,(function(e,n){i[n]=u(e,t),Object.defineProperty(t.getters,n,{get:function(){return t._vm[n]},enumerable:!0})}));var s=h.config.silent;h.config.silent=!0,t._vm=new h({data:{$$state:e},computed:i}),h.config.silent=s,t.strict&&k(t),r&&(n&&t._withCommit((function(){r._data.$$state=null})),h.nextTick((function(){return r.$destroy()})))}function _(t,e,n,r,o){var i=!n.length,a=t._modules.getNamespace(n);if(r.namespaced&&(t._modulesNamespaceMap[a],t._modulesNamespaceMap[a]=r),!i&&!o){var s=j(e,n.slice(0,-1)),c=n[n.length-1];t._withCommit((function(){h.set(s,c,r.state)}))}var u=r.context=w(t,a,n);r.forEachMutation((function(e,n){var r=a+n;C(t,r,e,u)})),r.forEachAction((function(e,n){var r=e.root?n:a+n,o=e.handler||e;O(t,r,o,u)})),r.forEachGetter((function(e,n){var r=a+n;A(t,r,e,u)})),r.forEachChild((function(r,i){_(t,e,n.concat(i),r,o)}))}function w(t,e,n){var r=\"\"===e,o={dispatch:r?t.dispatch:function(n,r,o){var i=S(n,r,o),a=i.payload,s=i.options,c=i.type;return s&&s.root||(c=e+c),t.dispatch(c,a)},commit:r?t.commit:function(n,r,o){var i=S(n,r,o),a=i.payload,s=i.options,c=i.type;s&&s.root||(c=e+c),t.commit(c,a,s)}};return Object.defineProperties(o,{getters:{get:r?function(){return t.getters}:function(){return x(t,e)}},state:{get:function(){return j(t.state,n)}}}),o}function x(t,e){if(!t._makeLocalGettersCache[e]){var n={},r=e.length;Object.keys(t.getters).forEach((function(o){if(o.slice(0,r)===e){var i=o.slice(r);Object.defineProperty(n,i,{get:function(){return t.getters[o]},enumerable:!0})}})),t._makeLocalGettersCache[e]=n}return t._makeLocalGettersCache[e]}function C(t,e,n,r){var o=t._mutations[e]||(t._mutations[e]=[]);o.push((function(e){n.call(t,r.state,e)}))}function O(t,e,n,r){var o=t._actions[e]||(t._actions[e]=[]);o.push((function(e){var o=n.call(t,{dispatch:r.dispatch,commit:r.commit,getters:r.getters,state:r.state,rootGetters:t.getters,rootState:t.state},e);return c(o)||(o=Promise.resolve(o)),t._devtoolHook?o.catch((function(e){throw t._devtoolHook.emit(\"vuex:error\",e),e})):o}))}function A(t,e,n,r){t._wrappedGetters[e]||(t._wrappedGetters[e]=function(t){return n(r.state,r.getters,t.state,t.getters)})}function k(t){t._vm.$watch((function(){return this._data.$$state}),(function(){0}),{deep:!0,sync:!0})}function j(t,e){return e.reduce((function(t,e){return t[e]}),t)}function S(t,e,n){return s(t)&&t.type&&(n=e,e=t,t=t.type),{type:t,payload:e,options:n}}function $(t){h&&t===h||(h=t,n(h))}y.state.get=function(){return this._vm._data.$$state},y.state.set=function(t){0},v.prototype.commit=function(t,e,n){var r=this,o=S(t,e,n),i=o.type,a=o.payload,s=(o.options,{type:i,payload:a}),c=this._mutations[i];c&&(this._withCommit((function(){c.forEach((function(t){t(a)}))})),this._subscribers.slice().forEach((function(t){return t(s,r.state)})))},v.prototype.dispatch=function(t,e){var n=this,r=S(t,e),o=r.type,i=r.payload,a={type:o,payload:i},s=this._actions[o];if(s){try{this._actionSubscribers.slice().filter((function(t){return t.before})).forEach((function(t){return t.before(a,n.state)}))}catch(u){0}var c=s.length>1?Promise.all(s.map((function(t){return t(i)}))):s[0](i);return c.then((function(t){try{n._actionSubscribers.filter((function(t){return t.after})).forEach((function(t){return t.after(a,n.state)}))}catch(u){0}return t}))}},v.prototype.subscribe=function(t){return m(t,this._subscribers)},v.prototype.subscribeAction=function(t){var e=\"function\"===typeof t?{before:t}:t;return m(e,this._actionSubscribers)},v.prototype.watch=function(t,e,n){var r=this;return this._watcherVM.$watch((function(){return t(r.state,r.getters)}),e,n)},v.prototype.replaceState=function(t){var e=this;this._withCommit((function(){e._vm._data.$$state=t}))},v.prototype.registerModule=function(t,e,n){void 0===n&&(n={}),\"string\"===typeof t&&(t=[t]),this._modules.register(t,e),_(this,this.state,t,this._modules.get(t),n.preserveState),b(this,this.state)},v.prototype.unregisterModule=function(t){var e=this;\"string\"===typeof t&&(t=[t]),this._modules.unregister(t),this._withCommit((function(){var n=j(e.state,t.slice(0,-1));h.delete(n,t[t.length-1])})),g(this)},v.prototype.hotUpdate=function(t){this._modules.update(t),g(this,!0)},v.prototype._withCommit=function(t){var e=this._committing;this._committing=!0,t(),this._committing=e},Object.defineProperties(v.prototype,y);var E=L((function(t,e){var n={};return M(e).forEach((function(e){var r=e.key,o=e.val;n[r]=function(){var e=this.$store.state,n=this.$store.getters;if(t){var r=D(this.$store,\"mapState\",t);if(!r)return;e=r.context.state,n=r.context.getters}return\"function\"===typeof o?o.call(this,e,n):e[o]},n[r].vuex=!0})),n})),T=L((function(t,e){var n={};return M(e).forEach((function(e){var r=e.key,o=e.val;n[r]=function(){var e=[],n=arguments.length;while(n--)e[n]=arguments[n];var r=this.$store.commit;if(t){var i=D(this.$store,\"mapMutations\",t);if(!i)return;r=i.context.commit}return\"function\"===typeof o?o.apply(this,[r].concat(e)):r.apply(this.$store,[o].concat(e))}})),n})),P=L((function(t,e){var n={};return M(e).forEach((function(e){var r=e.key,o=e.val;o=t+o,n[r]=function(){if(!t||D(this.$store,\"mapGetters\",t))return this.$store.getters[o]},n[r].vuex=!0})),n})),R=L((function(t,e){var n={};return M(e).forEach((function(e){var r=e.key,o=e.val;n[r]=function(){var e=[],n=arguments.length;while(n--)e[n]=arguments[n];var r=this.$store.dispatch;if(t){var i=D(this.$store,\"mapActions\",t);if(!i)return;r=i.context.dispatch}return\"function\"===typeof o?o.apply(this,[r].concat(e)):r.apply(this.$store,[o].concat(e))}})),n})),I=function(t){return{mapState:E.bind(null,t),mapGetters:P.bind(null,t),mapMutations:T.bind(null,t),mapActions:R.bind(null,t)}};function M(t){return N(t)?Array.isArray(t)?t.map((function(t){return{key:t,val:t}})):Object.keys(t).map((function(e){return{key:e,val:t[e]}})):[]}function N(t){return Array.isArray(t)||s(t)}function L(t){return function(e,n){return\"string\"!==typeof e?(n=e,e=\"\"):\"/\"!==e.charAt(e.length-1)&&(e+=\"/\"),t(e,n)}}function D(t,e,n){var r=t._modulesNamespaceMap[n];return r}var F={Store:v,install:$,version:\"3.1.3\",mapState:E,mapMutations:T,mapGetters:P,mapActions:R,createNamespacedHelpers:I};e[\"a\"]=F}).call(this,n(\"c8ba\"))},\"30b5\":function(t,e,n){\"use strict\";var r=n(\"c532\");function o(t){return encodeURIComponent(t).replace(/%40/gi,\"@\").replace(/%3A/gi,\":\").replace(/%24/g,\"$\").replace(/%2C/gi,\",\").replace(/%20/g,\"+\").replace(/%5B/gi,\"[\").replace(/%5D/gi,\"]\")}t.exports=function(t,e,n){if(!e)return t;var i;if(n)i=n(e);else if(r.isURLSearchParams(e))i=e.toString();else{var a=[];r.forEach(e,(function(t,e){null!==t&&\"undefined\"!==typeof t&&(r.isArray(t)?e+=\"[]\":t=[t],r.forEach(t,(function(t){r.isDate(t)?t=t.toISOString():r.isObject(t)&&(t=JSON.stringify(t)),a.push(o(e)+\"=\"+o(t))})))})),i=a.join(\"&\")}if(i){var s=t.indexOf(\"#\");-1!==s&&(t=t.slice(0,s)),t+=(-1===t.indexOf(\"?\")?\"?\":\"&\")+i}return t}},\"342f\":function(t,e,n){var r=n(\"d066\");t.exports=r(\"navigator\",\"userAgent\")||\"\"},\"35a1\":function(t,e,n){var r=n(\"f5df\"),o=n(\"3f8c\"),i=n(\"b622\"),a=i(\"iterator\");t.exports=function(t){if(void 0!=t)return t[a]||t[\"@@iterator\"]||o[r(t)]}},\"37e8\":function(t,e,n){var r=n(\"83ab\"),o=n(\"9bf2\"),i=n(\"825a\"),a=n(\"df75\");t.exports=r?Object.defineProperties:function(t,e){i(t);var n,r=a(e),s=r.length,c=0;while(s>c)o.f(t,n=r[c++],e[n]);return t}},\"387f\":function(t,e,n){\"use strict\";t.exports=function(t,e,n,r,o){return t.config=e,n&&(t.code=n),t.request=r,t.response=o,t.isAxiosError=!0,t.toJSON=function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:this.config,code:this.code}},t}},3934:function(t,e,n){\"use strict\";var r=n(\"c532\");t.exports=r.isStandardBrowserEnv()?function(){var t,e=/(msie|trident)/i.test(navigator.userAgent),n=document.createElement(\"a\");function o(t){var r=t;return e&&(n.setAttribute(\"href\",r),r=n.href),n.setAttribute(\"href\",r),{href:n.href,protocol:n.protocol?n.protocol.replace(/:$/,\"\"):\"\",host:n.host,search:n.search?n.search.replace(/^\\?/,\"\"):\"\",hash:n.hash?n.hash.replace(/^#/,\"\"):\"\",hostname:n.hostname,port:n.port,pathname:\"/\"===n.pathname.charAt(0)?n.pathname:\"/\"+n.pathname}}return t=o(window.location.href),function(e){var n=r.isString(e)?o(e):e;return n.protocol===t.protocol&&n.host===t.host}}():function(){return function(){return!0}}()},\"3bbe\":function(t,e,n){var r=n(\"861d\");t.exports=function(t){if(!r(t)&&null!==t)throw TypeError(\"Can't set \"+String(t)+\" as a prototype\");return t}},\"3f8c\":function(t,e){t.exports={}},\"428f\":function(t,e,n){var r=n(\"da84\");t.exports=r},4362:function(t,e,n){e.nextTick=function(t){var e=Array.prototype.slice.call(arguments);e.shift(),setTimeout((function(){t.apply(null,e)}),0)},e.platform=e.arch=e.execPath=e.title=\"browser\",e.pid=1,e.browser=!0,e.env={},e.argv=[],e.binding=function(t){throw new Error(\"No such module. (Possibly not yet loaded)\")},function(){var t,r=\"/\";e.cwd=function(){return r},e.chdir=function(e){t||(t=n(\"df7c\")),r=t.resolve(e,r)}}(),e.exit=e.kill=e.umask=e.dlopen=e.uptime=e.memoryUsage=e.uvCounters=function(){},e.features={}},\"44ad\":function(t,e,n){var r=n(\"d039\"),o=n(\"c6b6\"),i=\"\".split;t.exports=r((function(){return!Object(\"z\").propertyIsEnumerable(0)}))?function(t){return\"String\"==o(t)?i.call(t,\"\"):Object(t)}:Object},\"44d2\":function(t,e,n){var r=n(\"b622\"),o=n(\"7c73\"),i=n(\"9bf2\"),a=r(\"unscopables\"),s=Array.prototype;void 0==s[a]&&i.f(s,a,{configurable:!0,value:o(null)}),t.exports=function(t){s[a][t]=!0}},\"44de\":function(t,e,n){var r=n(\"da84\");t.exports=function(t,e){var n=r.console;n&&n.error&&(1===arguments.length?n.error(t):n.error(t,e))}},\"467f\":function(t,e,n){\"use strict\";var r=n(\"2d83\");t.exports=function(t,e,n){var o=n.config.validateStatus;!o||o(n.status)?t(n):e(r(\"Request failed with status code \"+n.status,n.config,null,n.request,n))}},4840:function(t,e,n){var r=n(\"825a\"),o=n(\"1c0b\"),i=n(\"b622\"),a=i(\"species\");t.exports=function(t,e){var n,i=r(t).constructor;return void 0===i||void 0==(n=r(i)[a])?e:o(n)}},4930:function(t,e,n){var r=n(\"d039\");t.exports=!!Object.getOwnPropertySymbols&&!r((function(){return!String(Symbol())}))},\"4a7b\":function(t,e,n){\"use strict\";var r=n(\"c532\");t.exports=function(t,e){e=e||{};var n={},o=[\"url\",\"method\",\"params\",\"data\"],i=[\"headers\",\"auth\",\"proxy\"],a=[\"baseURL\",\"url\",\"transformRequest\",\"transformResponse\",\"paramsSerializer\",\"timeout\",\"withCredentials\",\"adapter\",\"responseType\",\"xsrfCookieName\",\"xsrfHeaderName\",\"onUploadProgress\",\"onDownloadProgress\",\"maxContentLength\",\"validateStatus\",\"maxRedirects\",\"httpAgent\",\"httpsAgent\",\"cancelToken\",\"socketPath\"];r.forEach(o,(function(t){\"undefined\"!==typeof e[t]&&(n[t]=e[t])})),r.forEach(i,(function(o){r.isObject(e[o])?n[o]=r.deepMerge(t[o],e[o]):\"undefined\"!==typeof e[o]?n[o]=e[o]:r.isObject(t[o])?n[o]=r.deepMerge(t[o]):\"undefined\"!==typeof t[o]&&(n[o]=t[o])})),r.forEach(a,(function(r){\"undefined\"!==typeof e[r]?n[r]=e[r]:\"undefined\"!==typeof t[r]&&(n[r]=t[r])}));var s=o.concat(i).concat(a),c=Object.keys(e).filter((function(t){return-1===s.indexOf(t)}));return r.forEach(c,(function(r){\"undefined\"!==typeof e[r]?n[r]=e[r]:\"undefined\"!==typeof t[r]&&(n[r]=t[r])})),n}},\"4d64\":function(t,e,n){var r=n(\"fc6a\"),o=n(\"50c4\"),i=n(\"23cb\"),a=function(t){return function(e,n,a){var s,c=r(e),u=o(c.length),f=i(a,u);if(t&&n!=n){while(u>f)if(s=c[f++],s!=s)return!0}else for(;u>f;f++)if((t||f in c)&&c[f]===n)return t||f||0;return!t&&-1}};t.exports={includes:a(!0),indexOf:a(!1)}},\"50c4\":function(t,e,n){var r=n(\"a691\"),o=Math.min;t.exports=function(t){return t>0?o(r(t),9007199254740991):0}},5135:function(t,e){var n={}.hasOwnProperty;t.exports=function(t,e){return n.call(t,e)}},5270:function(t,e,n){\"use strict\";var r=n(\"c532\"),o=n(\"c401\"),i=n(\"2e67\"),a=n(\"2444\");function s(t){t.cancelToken&&t.cancelToken.throwIfRequested()}t.exports=function(t){s(t),t.headers=t.headers||{},t.data=o(t.data,t.headers,t.transformRequest),t.headers=r.merge(t.headers.common||{},t.headers[t.method]||{},t.headers),r.forEach([\"delete\",\"get\",\"head\",\"post\",\"put\",\"patch\",\"common\"],(function(e){delete t.headers[e]}));var e=t.adapter||a.adapter;return e(t).then((function(e){return s(t),e.data=o(e.data,e.headers,t.transformResponse),e}),(function(e){return i(e)||(s(t),e&&e.response&&(e.response.data=o(e.response.data,e.response.headers,t.transformResponse))),Promise.reject(e)}))}},5692:function(t,e,n){var r=n(\"c430\"),o=n(\"c6cd\");(t.exports=function(t,e){return o[t]||(o[t]=void 0!==e?e:{})})(\"versions\",[]).push({version:\"3.6.4\",mode:r?\"pure\":\"global\",copyright:\"© 2020 Denis Pushkarev (zloirock.ru)\"})},\"56ef\":function(t,e,n){var r=n(\"d066\"),o=n(\"241c\"),i=n(\"7418\"),a=n(\"825a\");t.exports=r(\"Reflect\",\"ownKeys\")||function(t){var e=o.f(a(t)),n=i.f;return n?e.concat(n(t)):e}},\"5c6c\":function(t,e){t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},\"60da\":function(t,e,n){\"use strict\";var r=n(\"83ab\"),o=n(\"d039\"),i=n(\"df75\"),a=n(\"7418\"),s=n(\"d1e7\"),c=n(\"7b0b\"),u=n(\"44ad\"),f=Object.assign,l=Object.defineProperty;t.exports=!f||o((function(){if(r&&1!==f({b:1},f(l({},\"a\",{enumerable:!0,get:function(){l(this,\"b\",{value:3,enumerable:!1})}}),{b:2})).b)return!0;var t={},e={},n=Symbol(),o=\"abcdefghijklmnopqrst\";return t[n]=7,o.split(\"\").forEach((function(t){e[t]=t})),7!=f({},t)[n]||i(f({},e)).join(\"\")!=o}))?function(t,e){var n=c(t),o=arguments.length,f=1,l=a.f,p=s.f;while(o>f){var d,h=u(arguments[f++]),v=l?i(h).concat(l(h)):i(h),y=v.length,m=0;while(y>m)d=v[m++],r&&!p.call(h,d)||(n[d]=h[d])}return n}:f},\"69f3\":function(t,e,n){var r,o,i,a=n(\"7f9a\"),s=n(\"da84\"),c=n(\"861d\"),u=n(\"9112\"),f=n(\"5135\"),l=n(\"f772\"),p=n(\"d012\"),d=s.WeakMap,h=function(t){return i(t)?o(t):r(t,{})},v=function(t){return function(e){var n;if(!c(e)||(n=o(e)).type!==t)throw TypeError(\"Incompatible receiver, \"+t+\" required\");return n}};if(a){var y=new d,m=y.get,g=y.has,b=y.set;r=function(t,e){return b.call(y,t,e),e},o=function(t){return m.call(y,t)||{}},i=function(t){return g.call(y,t)}}else{var _=l(\"state\");p[_]=!0,r=function(t,e){return u(t,_,e),e},o=function(t){return f(t,_)?t[_]:{}},i=function(t){return f(t,_)}}t.exports={set:r,get:o,has:i,enforce:h,getterFor:v}},\"6eeb\":function(t,e,n){var r=n(\"da84\"),o=n(\"9112\"),i=n(\"5135\"),a=n(\"ce4e\"),s=n(\"8925\"),c=n(\"69f3\"),u=c.get,f=c.enforce,l=String(String).split(\"String\");(t.exports=function(t,e,n,s){var c=!!s&&!!s.unsafe,u=!!s&&!!s.enumerable,p=!!s&&!!s.noTargetGet;\"function\"==typeof n&&(\"string\"!=typeof e||i(n,\"name\")||o(n,\"name\",e),f(n).source=l.join(\"string\"==typeof e?e:\"\")),t!==r?(c?!p&&t[e]&&(u=!0):delete t[e],u?t[e]=n:o(t,e,n)):u?t[e]=n:a(e,n)})(Function.prototype,\"toString\",(function(){return\"function\"==typeof this&&u(this).source||s(this)}))},7418:function(t,e){e.f=Object.getOwnPropertySymbols},7839:function(t,e){t.exports=[\"constructor\",\"hasOwnProperty\",\"isPrototypeOf\",\"propertyIsEnumerable\",\"toLocaleString\",\"toString\",\"valueOf\"]},\"7a77\":function(t,e,n){\"use strict\";function r(t){this.message=t}r.prototype.toString=function(){return\"Cancel\"+(this.message?\": \"+this.message:\"\")},r.prototype.__CANCEL__=!0,t.exports=r},\"7aac\":function(t,e,n){\"use strict\";var r=n(\"c532\");t.exports=r.isStandardBrowserEnv()?function(){return{write:function(t,e,n,o,i,a){var s=[];s.push(t+\"=\"+encodeURIComponent(e)),r.isNumber(n)&&s.push(\"expires=\"+new Date(n).toGMTString()),r.isString(o)&&s.push(\"path=\"+o),r.isString(i)&&s.push(\"domain=\"+i),!0===a&&s.push(\"secure\"),document.cookie=s.join(\"; \")},read:function(t){var e=document.cookie.match(new RegExp(\"(^|;\\\\s*)(\"+t+\")=([^;]*)\"));return e?decodeURIComponent(e[3]):null},remove:function(t){this.write(t,\"\",Date.now()-864e5)}}}():function(){return{write:function(){},read:function(){return null},remove:function(){}}}()},\"7b0b\":function(t,e,n){var r=n(\"1d80\");t.exports=function(t){return Object(r(t))}},\"7c73\":function(t,e,n){var r,o=n(\"825a\"),i=n(\"37e8\"),a=n(\"7839\"),s=n(\"d012\"),c=n(\"1be4\"),u=n(\"cc12\"),f=n(\"f772\"),l=\">\",p=\"<\",d=\"prototype\",h=\"script\",v=f(\"IE_PROTO\"),y=function(){},m=function(t){return p+h+l+t+p+\"/\"+h+l},g=function(t){t.write(m(\"\")),t.close();var e=t.parentWindow.Object;return t=null,e},b=function(){var t,e=u(\"iframe\"),n=\"java\"+h+\":\";return e.style.display=\"none\",c.appendChild(e),e.src=String(n),t=e.contentWindow.document,t.open(),t.write(m(\"document.F=Object\")),t.close(),t.F},_=function(){try{r=document.domain&&new ActiveXObject(\"htmlfile\")}catch(e){}_=r?g(r):b();var t=a.length;while(t--)delete _[d][a[t]];return _()};s[v]=!0,t.exports=Object.create||function(t,e){var n;return null!==t?(y[d]=o(t),n=new y,y[d]=null,n[v]=t):n=_(),void 0===e?n:i(n,e)}},\"7dd0\":function(t,e,n){\"use strict\";var r=n(\"23e7\"),o=n(\"9ed3\"),i=n(\"e163\"),a=n(\"d2bb\"),s=n(\"d44e\"),c=n(\"9112\"),u=n(\"6eeb\"),f=n(\"b622\"),l=n(\"c430\"),p=n(\"3f8c\"),d=n(\"ae93\"),h=d.IteratorPrototype,v=d.BUGGY_SAFARI_ITERATORS,y=f(\"iterator\"),m=\"keys\",g=\"values\",b=\"entries\",_=function(){return this};t.exports=function(t,e,n,f,d,w,x){o(n,e,f);var C,O,A,k=function(t){if(t===d&&T)return T;if(!v&&t in $)return $[t];switch(t){case m:return function(){return new n(this,t)};case g:return function(){return new n(this,t)};case b:return function(){return new n(this,t)}}return function(){return new n(this)}},j=e+\" Iterator\",S=!1,$=t.prototype,E=$[y]||$[\"@@iterator\"]||d&&$[d],T=!v&&E||k(d),P=\"Array\"==e&&$.entries||E;if(P&&(C=i(P.call(new t)),h!==Object.prototype&&C.next&&(l||i(C)===h||(a?a(C,h):\"function\"!=typeof C[y]&&c(C,y,_)),s(C,j,!0,!0),l&&(p[j]=_))),d==g&&E&&E.name!==g&&(S=!0,T=function(){return E.call(this)}),l&&!x||$[y]===T||c($,y,T),p[e]=T,d)if(O={values:k(g),keys:w?T:k(m),entries:k(b)},x)for(A in O)(v||S||!(A in $))&&u($,A,O[A]);else r({target:e,proto:!0,forced:v||S},O);return O}},\"7f9a\":function(t,e,n){var r=n(\"da84\"),o=n(\"8925\"),i=r.WeakMap;t.exports=\"function\"===typeof i&&/native code/.test(o(i))},\"825a\":function(t,e,n){var r=n(\"861d\");t.exports=function(t){if(!r(t))throw TypeError(String(t)+\" is not an object\");return t}},\"83ab\":function(t,e,n){var r=n(\"d039\");t.exports=!r((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]}))},\"83b9\":function(t,e,n){\"use strict\";var r=n(\"d925\"),o=n(\"e683\");t.exports=function(t,e){return t&&!r(e)?o(t,e):e}},\"861d\":function(t,e){t.exports=function(t){return\"object\"===typeof t?null!==t:\"function\"===typeof t}},8925:function(t,e,n){var r=n(\"c6cd\"),o=Function.toString;\"function\"!=typeof r.inspectSource&&(r.inspectSource=function(t){return o.call(t)}),t.exports=r.inspectSource},\"8c4f\":function(t,e,n){\"use strict\";\n/*!\n  * vue-router v3.1.6\n  * (c) 2020 Evan You\n  * @license MIT\n  */function r(t,e){0}function o(t){return Object.prototype.toString.call(t).indexOf(\"Error\")>-1}function i(t,e){return e instanceof t||e&&(e.name===t.name||e._name===t._name)}function a(t,e){for(var n in e)t[n]=e[n];return t}var s={name:\"RouterView\",functional:!0,props:{name:{type:String,default:\"default\"}},render:function(t,e){var n=e.props,r=e.children,o=e.parent,i=e.data;i.routerView=!0;var s=o.$createElement,u=n.name,f=o.$route,l=o._routerViewCache||(o._routerViewCache={}),p=0,d=!1;while(o&&o._routerRoot!==o){var h=o.$vnode?o.$vnode.data:{};h.routerView&&p++,h.keepAlive&&o._directInactive&&o._inactive&&(d=!0),o=o.$parent}if(i.routerViewDepth=p,d){var v=l[u],y=v&&v.component;return y?(v.configProps&&c(y,i,v.route,v.configProps),s(y,i,r)):s()}var m=f.matched[p],g=m&&m.components[u];if(!m||!g)return l[u]=null,s();l[u]={component:g},i.registerRouteInstance=function(t,e){var n=m.instances[u];(e&&n!==t||!e&&n===t)&&(m.instances[u]=e)},(i.hook||(i.hook={})).prepatch=function(t,e){m.instances[u]=e.componentInstance},i.hook.init=function(t){t.data.keepAlive&&t.componentInstance&&t.componentInstance!==m.instances[u]&&(m.instances[u]=t.componentInstance)};var b=m.props&&m.props[u];return b&&(a(l[u],{route:f,configProps:b}),c(g,i,f,b)),s(g,i,r)}};function c(t,e,n,r){var o=e.props=u(n,r);if(o){o=e.props=a({},o);var i=e.attrs=e.attrs||{};for(var s in o)t.props&&s in t.props||(i[s]=o[s],delete o[s])}}function u(t,e){switch(typeof e){case\"undefined\":return;case\"object\":return e;case\"function\":return e(t);case\"boolean\":return e?t.params:void 0;default:0}}var f=/[!'()*]/g,l=function(t){return\"%\"+t.charCodeAt(0).toString(16)},p=/%2C/g,d=function(t){return encodeURIComponent(t).replace(f,l).replace(p,\",\")},h=decodeURIComponent;function v(t,e,n){void 0===e&&(e={});var r,o=n||y;try{r=o(t||\"\")}catch(a){r={}}for(var i in e)r[i]=e[i];return r}function y(t){var e={};return t=t.trim().replace(/^(\\?|#|&)/,\"\"),t?(t.split(\"&\").forEach((function(t){var n=t.replace(/\\+/g,\" \").split(\"=\"),r=h(n.shift()),o=n.length>0?h(n.join(\"=\")):null;void 0===e[r]?e[r]=o:Array.isArray(e[r])?e[r].push(o):e[r]=[e[r],o]})),e):e}function m(t){var e=t?Object.keys(t).map((function(e){var n=t[e];if(void 0===n)return\"\";if(null===n)return d(e);if(Array.isArray(n)){var r=[];return n.forEach((function(t){void 0!==t&&(null===t?r.push(d(e)):r.push(d(e)+\"=\"+d(t)))})),r.join(\"&\")}return d(e)+\"=\"+d(n)})).filter((function(t){return t.length>0})).join(\"&\"):null;return e?\"?\"+e:\"\"}var g=/\\/?$/;function b(t,e,n,r){var o=r&&r.options.stringifyQuery,i=e.query||{};try{i=_(i)}catch(s){}var a={name:e.name||t&&t.name,meta:t&&t.meta||{},path:e.path||\"/\",hash:e.hash||\"\",query:i,params:e.params||{},fullPath:C(e,o),matched:t?x(t):[]};return n&&(a.redirectedFrom=C(n,o)),Object.freeze(a)}function _(t){if(Array.isArray(t))return t.map(_);if(t&&\"object\"===typeof t){var e={};for(var n in t)e[n]=_(t[n]);return e}return t}var w=b(null,{path:\"/\"});function x(t){var e=[];while(t)e.unshift(t),t=t.parent;return e}function C(t,e){var n=t.path,r=t.query;void 0===r&&(r={});var o=t.hash;void 0===o&&(o=\"\");var i=e||m;return(n||\"/\")+i(r)+o}function O(t,e){return e===w?t===e:!!e&&(t.path&&e.path?t.path.replace(g,\"\")===e.path.replace(g,\"\")&&t.hash===e.hash&&A(t.query,e.query):!(!t.name||!e.name)&&(t.name===e.name&&t.hash===e.hash&&A(t.query,e.query)&&A(t.params,e.params)))}function A(t,e){if(void 0===t&&(t={}),void 0===e&&(e={}),!t||!e)return t===e;var n=Object.keys(t),r=Object.keys(e);return n.length===r.length&&n.every((function(n){var r=t[n],o=e[n];return\"object\"===typeof r&&\"object\"===typeof o?A(r,o):String(r)===String(o)}))}function k(t,e){return 0===t.path.replace(g,\"/\").indexOf(e.path.replace(g,\"/\"))&&(!e.hash||t.hash===e.hash)&&j(t.query,e.query)}function j(t,e){for(var n in e)if(!(n in t))return!1;return!0}function S(t,e,n){var r=t.charAt(0);if(\"/\"===r)return t;if(\"?\"===r||\"#\"===r)return e+t;var o=e.split(\"/\");n&&o[o.length-1]||o.pop();for(var i=t.replace(/^\\//,\"\").split(\"/\"),a=0;a<i.length;a++){var s=i[a];\"..\"===s?o.pop():\".\"!==s&&o.push(s)}return\"\"!==o[0]&&o.unshift(\"\"),o.join(\"/\")}function $(t){var e=\"\",n=\"\",r=t.indexOf(\"#\");r>=0&&(e=t.slice(r),t=t.slice(0,r));var o=t.indexOf(\"?\");return o>=0&&(n=t.slice(o+1),t=t.slice(0,o)),{path:t,query:n,hash:e}}function E(t){return t.replace(/\\/\\//g,\"/\")}var T=Array.isArray||function(t){return\"[object Array]\"==Object.prototype.toString.call(t)},P=Y,R=D,I=F,M=q,N=J,L=new RegExp([\"(\\\\\\\\.)\",\"([\\\\/.])?(?:(?:\\\\:(\\\\w+)(?:\\\\(((?:\\\\\\\\.|[^\\\\\\\\()])+)\\\\))?|\\\\(((?:\\\\\\\\.|[^\\\\\\\\()])+)\\\\))([+*?])?|(\\\\*))\"].join(\"|\"),\"g\");function D(t,e){var n,r=[],o=0,i=0,a=\"\",s=e&&e.delimiter||\"/\";while(null!=(n=L.exec(t))){var c=n[0],u=n[1],f=n.index;if(a+=t.slice(i,f),i=f+c.length,u)a+=u[1];else{var l=t[i],p=n[2],d=n[3],h=n[4],v=n[5],y=n[6],m=n[7];a&&(r.push(a),a=\"\");var g=null!=p&&null!=l&&l!==p,b=\"+\"===y||\"*\"===y,_=\"?\"===y||\"*\"===y,w=n[2]||s,x=h||v;r.push({name:d||o++,prefix:p||\"\",delimiter:w,optional:_,repeat:b,partial:g,asterisk:!!m,pattern:x?V(x):m?\".*\":\"[^\"+H(w)+\"]+?\"})}}return i<t.length&&(a+=t.substr(i)),a&&r.push(a),r}function F(t,e){return q(D(t,e))}function U(t){return encodeURI(t).replace(/[\\/?#]/g,(function(t){return\"%\"+t.charCodeAt(0).toString(16).toUpperCase()}))}function B(t){return encodeURI(t).replace(/[?#]/g,(function(t){return\"%\"+t.charCodeAt(0).toString(16).toUpperCase()}))}function q(t){for(var e=new Array(t.length),n=0;n<t.length;n++)\"object\"===typeof t[n]&&(e[n]=new RegExp(\"^(?:\"+t[n].pattern+\")$\"));return function(n,r){for(var o=\"\",i=n||{},a=r||{},s=a.pretty?U:encodeURIComponent,c=0;c<t.length;c++){var u=t[c];if(\"string\"!==typeof u){var f,l=i[u.name];if(null==l){if(u.optional){u.partial&&(o+=u.prefix);continue}throw new TypeError('Expected \"'+u.name+'\" to be defined')}if(T(l)){if(!u.repeat)throw new TypeError('Expected \"'+u.name+'\" to not repeat, but received `'+JSON.stringify(l)+\"`\");if(0===l.length){if(u.optional)continue;throw new TypeError('Expected \"'+u.name+'\" to not be empty')}for(var p=0;p<l.length;p++){if(f=s(l[p]),!e[c].test(f))throw new TypeError('Expected all \"'+u.name+'\" to match \"'+u.pattern+'\", but received `'+JSON.stringify(f)+\"`\");o+=(0===p?u.prefix:u.delimiter)+f}}else{if(f=u.asterisk?B(l):s(l),!e[c].test(f))throw new TypeError('Expected \"'+u.name+'\" to match \"'+u.pattern+'\", but received \"'+f+'\"');o+=u.prefix+f}}else o+=u}return o}}function H(t){return t.replace(/([.+*?=^!:${}()[\\]|\\/\\\\])/g,\"\\\\$1\")}function V(t){return t.replace(/([=!:$\\/()])/g,\"\\\\$1\")}function z(t,e){return t.keys=e,t}function G(t){return t.sensitive?\"\":\"i\"}function W(t,e){var n=t.source.match(/\\((?!\\?)/g);if(n)for(var r=0;r<n.length;r++)e.push({name:r,prefix:null,delimiter:null,optional:!1,repeat:!1,partial:!1,asterisk:!1,pattern:null});return z(t,e)}function K(t,e,n){for(var r=[],o=0;o<t.length;o++)r.push(Y(t[o],e,n).source);var i=new RegExp(\"(?:\"+r.join(\"|\")+\")\",G(n));return z(i,e)}function X(t,e,n){return J(D(t,n),e,n)}function J(t,e,n){T(e)||(n=e||n,e=[]),n=n||{};for(var r=n.strict,o=!1!==n.end,i=\"\",a=0;a<t.length;a++){var s=t[a];if(\"string\"===typeof s)i+=H(s);else{var c=H(s.prefix),u=\"(?:\"+s.pattern+\")\";e.push(s),s.repeat&&(u+=\"(?:\"+c+u+\")*\"),u=s.optional?s.partial?c+\"(\"+u+\")?\":\"(?:\"+c+\"(\"+u+\"))?\":c+\"(\"+u+\")\",i+=u}}var f=H(n.delimiter||\"/\"),l=i.slice(-f.length)===f;return r||(i=(l?i.slice(0,-f.length):i)+\"(?:\"+f+\"(?=$))?\"),i+=o?\"$\":r&&l?\"\":\"(?=\"+f+\"|$)\",z(new RegExp(\"^\"+i,G(n)),e)}function Y(t,e,n){return T(e)||(n=e||n,e=[]),n=n||{},t instanceof RegExp?W(t,e):T(t)?K(t,e,n):X(t,e,n)}P.parse=R,P.compile=I,P.tokensToFunction=M,P.tokensToRegExp=N;var Q=Object.create(null);function Z(t,e,n){e=e||{};try{var r=Q[t]||(Q[t]=P.compile(t));return\"string\"===typeof e.pathMatch&&(e[0]=e.pathMatch),r(e,{pretty:!0})}catch(o){return\"\"}finally{delete e[0]}}function tt(t,e,n,r){var o=\"string\"===typeof t?{path:t}:t;if(o._normalized)return o;if(o.name){o=a({},t);var i=o.params;return i&&\"object\"===typeof i&&(o.params=a({},i)),o}if(!o.path&&o.params&&e){o=a({},o),o._normalized=!0;var s=a(a({},e.params),o.params);if(e.name)o.name=e.name,o.params=s;else if(e.matched.length){var c=e.matched[e.matched.length-1].path;o.path=Z(c,s,\"path \"+e.path)}else 0;return o}var u=$(o.path||\"\"),f=e&&e.path||\"/\",l=u.path?S(u.path,f,n||o.append):f,p=v(u.query,o.query,r&&r.options.parseQuery),d=o.hash||u.hash;return d&&\"#\"!==d.charAt(0)&&(d=\"#\"+d),{_normalized:!0,path:l,query:p,hash:d}}var et,nt=[String,Object],rt=[String,Array],ot=function(){},it={name:\"RouterLink\",props:{to:{type:nt,required:!0},tag:{type:String,default:\"a\"},exact:Boolean,append:Boolean,replace:Boolean,activeClass:String,exactActiveClass:String,event:{type:rt,default:\"click\"}},render:function(t){var e=this,n=this.$router,r=this.$route,o=n.resolve(this.to,r,this.append),i=o.location,s=o.route,c=o.href,u={},f=n.options.linkActiveClass,l=n.options.linkExactActiveClass,p=null==f?\"router-link-active\":f,d=null==l?\"router-link-exact-active\":l,h=null==this.activeClass?p:this.activeClass,v=null==this.exactActiveClass?d:this.exactActiveClass,y=s.redirectedFrom?b(null,tt(s.redirectedFrom),null,n):s;u[v]=O(r,y),u[h]=this.exact?u[v]:k(r,y);var m=function(t){at(t)&&(e.replace?n.replace(i,ot):n.push(i,ot))},g={click:at};Array.isArray(this.event)?this.event.forEach((function(t){g[t]=m})):g[this.event]=m;var _={class:u},w=!this.$scopedSlots.$hasNormal&&this.$scopedSlots.default&&this.$scopedSlots.default({href:c,route:s,navigate:m,isActive:u[h],isExactActive:u[v]});if(w){if(1===w.length)return w[0];if(w.length>1||!w.length)return 0===w.length?t():t(\"span\",{},w)}if(\"a\"===this.tag)_.on=g,_.attrs={href:c};else{var x=st(this.$slots.default);if(x){x.isStatic=!1;var C=x.data=a({},x.data);for(var A in C.on=C.on||{},C.on){var j=C.on[A];A in g&&(C.on[A]=Array.isArray(j)?j:[j])}for(var S in g)S in C.on?C.on[S].push(g[S]):C.on[S]=m;var $=x.data.attrs=a({},x.data.attrs);$.href=c}else _.on=g}return t(this.tag,_,this.$slots.default)}};function at(t){if(!(t.metaKey||t.altKey||t.ctrlKey||t.shiftKey)&&!t.defaultPrevented&&(void 0===t.button||0===t.button)){if(t.currentTarget&&t.currentTarget.getAttribute){var e=t.currentTarget.getAttribute(\"target\");if(/\\b_blank\\b/i.test(e))return}return t.preventDefault&&t.preventDefault(),!0}}function st(t){if(t)for(var e,n=0;n<t.length;n++){if(e=t[n],\"a\"===e.tag)return e;if(e.children&&(e=st(e.children)))return e}}function ct(t){if(!ct.installed||et!==t){ct.installed=!0,et=t;var e=function(t){return void 0!==t},n=function(t,n){var r=t.$options._parentVnode;e(r)&&e(r=r.data)&&e(r=r.registerRouteInstance)&&r(t,n)};t.mixin({beforeCreate:function(){e(this.$options.router)?(this._routerRoot=this,this._router=this.$options.router,this._router.init(this),t.util.defineReactive(this,\"_route\",this._router.history.current)):this._routerRoot=this.$parent&&this.$parent._routerRoot||this,n(this,this)},destroyed:function(){n(this)}}),Object.defineProperty(t.prototype,\"$router\",{get:function(){return this._routerRoot._router}}),Object.defineProperty(t.prototype,\"$route\",{get:function(){return this._routerRoot._route}}),t.component(\"RouterView\",s),t.component(\"RouterLink\",it);var r=t.config.optionMergeStrategies;r.beforeRouteEnter=r.beforeRouteLeave=r.beforeRouteUpdate=r.created}}var ut=\"undefined\"!==typeof window;function ft(t,e,n,r){var o=e||[],i=n||Object.create(null),a=r||Object.create(null);t.forEach((function(t){lt(o,i,a,t)}));for(var s=0,c=o.length;s<c;s++)\"*\"===o[s]&&(o.push(o.splice(s,1)[0]),c--,s--);return{pathList:o,pathMap:i,nameMap:a}}function lt(t,e,n,r,o,i){var a=r.path,s=r.name;var c=r.pathToRegexpOptions||{},u=dt(a,o,c.strict);\"boolean\"===typeof r.caseSensitive&&(c.sensitive=r.caseSensitive);var f={path:u,regex:pt(u,c),components:r.components||{default:r.component},instances:{},name:s,parent:o,matchAs:i,redirect:r.redirect,beforeEnter:r.beforeEnter,meta:r.meta||{},props:null==r.props?{}:r.components?r.props:{default:r.props}};if(r.children&&r.children.forEach((function(r){var o=i?E(i+\"/\"+r.path):void 0;lt(t,e,n,r,f,o)})),e[f.path]||(t.push(f.path),e[f.path]=f),void 0!==r.alias)for(var l=Array.isArray(r.alias)?r.alias:[r.alias],p=0;p<l.length;++p){var d=l[p];0;var h={path:d,children:r.children};lt(t,e,n,h,o,f.path||\"/\")}s&&(n[s]||(n[s]=f))}function pt(t,e){var n=P(t,[],e);return n}function dt(t,e,n){return n||(t=t.replace(/\\/$/,\"\")),\"/\"===t[0]||null==e?t:E(e.path+\"/\"+t)}function ht(t,e){var n=ft(t),r=n.pathList,o=n.pathMap,i=n.nameMap;function a(t){ft(t,r,o,i)}function s(t,n,a){var s=tt(t,n,!1,e),c=s.name;if(c){var u=i[c];if(!u)return f(null,s);var l=u.regex.keys.filter((function(t){return!t.optional})).map((function(t){return t.name}));if(\"object\"!==typeof s.params&&(s.params={}),n&&\"object\"===typeof n.params)for(var p in n.params)!(p in s.params)&&l.indexOf(p)>-1&&(s.params[p]=n.params[p]);return s.path=Z(u.path,s.params,'named route \"'+c+'\"'),f(u,s,a)}if(s.path){s.params={};for(var d=0;d<r.length;d++){var h=r[d],v=o[h];if(vt(v.regex,s.path,s.params))return f(v,s,a)}}return f(null,s)}function c(t,n){var r=t.redirect,o=\"function\"===typeof r?r(b(t,n,null,e)):r;if(\"string\"===typeof o&&(o={path:o}),!o||\"object\"!==typeof o)return f(null,n);var a=o,c=a.name,u=a.path,l=n.query,p=n.hash,d=n.params;if(l=a.hasOwnProperty(\"query\")?a.query:l,p=a.hasOwnProperty(\"hash\")?a.hash:p,d=a.hasOwnProperty(\"params\")?a.params:d,c){i[c];return s({_normalized:!0,name:c,query:l,hash:p,params:d},void 0,n)}if(u){var h=yt(u,t),v=Z(h,d,'redirect route with path \"'+h+'\"');return s({_normalized:!0,path:v,query:l,hash:p},void 0,n)}return f(null,n)}function u(t,e,n){var r=Z(n,e.params,'aliased route with path \"'+n+'\"'),o=s({_normalized:!0,path:r});if(o){var i=o.matched,a=i[i.length-1];return e.params=o.params,f(a,e)}return f(null,e)}function f(t,n,r){return t&&t.redirect?c(t,r||n):t&&t.matchAs?u(t,n,t.matchAs):b(t,n,r,e)}return{match:s,addRoutes:a}}function vt(t,e,n){var r=e.match(t);if(!r)return!1;if(!n)return!0;for(var o=1,i=r.length;o<i;++o){var a=t.keys[o-1],s=\"string\"===typeof r[o]?decodeURIComponent(r[o]):r[o];a&&(n[a.name||\"pathMatch\"]=s)}return!0}function yt(t,e){return S(t,e.parent?e.parent.path:\"/\",!0)}var mt=ut&&window.performance&&window.performance.now?window.performance:Date;function gt(){return mt.now().toFixed(3)}var bt=gt();function _t(){return bt}function wt(t){return bt=t}var xt=Object.create(null);function Ct(){var t=window.location.protocol+\"//\"+window.location.host,e=window.location.href.replace(t,\"\"),n=a({},window.history.state);n.key=_t(),window.history.replaceState(n,\"\",e),window.addEventListener(\"popstate\",(function(t){At(),t.state&&t.state.key&&wt(t.state.key)}))}function Ot(t,e,n,r){if(t.app){var o=t.options.scrollBehavior;o&&t.app.$nextTick((function(){var i=kt(),a=o.call(t,e,n,r?i:null);a&&(\"function\"===typeof a.then?a.then((function(t){Rt(t,i)})).catch((function(t){0})):Rt(a,i))}))}}function At(){var t=_t();t&&(xt[t]={x:window.pageXOffset,y:window.pageYOffset})}function kt(){var t=_t();if(t)return xt[t]}function jt(t,e){var n=document.documentElement,r=n.getBoundingClientRect(),o=t.getBoundingClientRect();return{x:o.left-r.left-e.x,y:o.top-r.top-e.y}}function St(t){return Tt(t.x)||Tt(t.y)}function $t(t){return{x:Tt(t.x)?t.x:window.pageXOffset,y:Tt(t.y)?t.y:window.pageYOffset}}function Et(t){return{x:Tt(t.x)?t.x:0,y:Tt(t.y)?t.y:0}}function Tt(t){return\"number\"===typeof t}var Pt=/^#\\d/;function Rt(t,e){var n=\"object\"===typeof t;if(n&&\"string\"===typeof t.selector){var r=Pt.test(t.selector)?document.getElementById(t.selector.slice(1)):document.querySelector(t.selector);if(r){var o=t.offset&&\"object\"===typeof t.offset?t.offset:{};o=Et(o),e=jt(r,o)}else St(t)&&(e=$t(t))}else n&&St(t)&&(e=$t(t));e&&window.scrollTo(e.x,e.y)}var It=ut&&function(){var t=window.navigator.userAgent;return(-1===t.indexOf(\"Android 2.\")&&-1===t.indexOf(\"Android 4.0\")||-1===t.indexOf(\"Mobile Safari\")||-1!==t.indexOf(\"Chrome\")||-1!==t.indexOf(\"Windows Phone\"))&&(window.history&&\"pushState\"in window.history)}();function Mt(t,e){At();var n=window.history;try{if(e){var r=a({},n.state);r.key=_t(),n.replaceState(r,\"\",t)}else n.pushState({key:wt(gt())},\"\",t)}catch(o){window.location[e?\"replace\":\"assign\"](t)}}function Nt(t){Mt(t,!0)}function Lt(t,e,n){var r=function(o){o>=t.length?n():t[o]?e(t[o],(function(){r(o+1)})):r(o+1)};r(0)}function Dt(t){return function(e,n,r){var i=!1,a=0,s=null;Ft(t,(function(t,e,n,c){if(\"function\"===typeof t&&void 0===t.cid){i=!0,a++;var u,f=Ht((function(e){qt(e)&&(e=e.default),t.resolved=\"function\"===typeof e?e:et.extend(e),n.components[c]=e,a--,a<=0&&r()})),l=Ht((function(t){var e=\"Failed to resolve async component \"+c+\": \"+t;s||(s=o(t)?t:new Error(e),r(s))}));try{u=t(f,l)}catch(d){l(d)}if(u)if(\"function\"===typeof u.then)u.then(f,l);else{var p=u.component;p&&\"function\"===typeof p.then&&p.then(f,l)}}})),i||r()}}function Ft(t,e){return Ut(t.map((function(t){return Object.keys(t.components).map((function(n){return e(t.components[n],t.instances[n],t,n)}))})))}function Ut(t){return Array.prototype.concat.apply([],t)}var Bt=\"function\"===typeof Symbol&&\"symbol\"===typeof Symbol.toStringTag;function qt(t){return t.__esModule||Bt&&\"Module\"===t[Symbol.toStringTag]}function Ht(t){var e=!1;return function(){var n=[],r=arguments.length;while(r--)n[r]=arguments[r];if(!e)return e=!0,t.apply(this,n)}}var Vt=function(t){function e(e){t.call(this),this.name=this._name=\"NavigationDuplicated\",this.message='Navigating to current location (\"'+e.fullPath+'\") is not allowed',Object.defineProperty(this,\"stack\",{value:(new t).stack,writable:!0,configurable:!0})}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e}(Error);Vt._name=\"NavigationDuplicated\";var zt=function(t,e){this.router=t,this.base=Gt(e),this.current=w,this.pending=null,this.ready=!1,this.readyCbs=[],this.readyErrorCbs=[],this.errorCbs=[]};function Gt(t){if(!t)if(ut){var e=document.querySelector(\"base\");t=e&&e.getAttribute(\"href\")||\"/\",t=t.replace(/^https?:\\/\\/[^\\/]+/,\"\")}else t=\"/\";return\"/\"!==t.charAt(0)&&(t=\"/\"+t),t.replace(/\\/$/,\"\")}function Wt(t,e){var n,r=Math.max(t.length,e.length);for(n=0;n<r;n++)if(t[n]!==e[n])break;return{updated:e.slice(0,n),activated:e.slice(n),deactivated:t.slice(n)}}function Kt(t,e,n,r){var o=Ft(t,(function(t,r,o,i){var a=Xt(t,e);if(a)return Array.isArray(a)?a.map((function(t){return n(t,r,o,i)})):n(a,r,o,i)}));return Ut(r?o.reverse():o)}function Xt(t,e){return\"function\"!==typeof t&&(t=et.extend(t)),t.options[e]}function Jt(t){return Kt(t,\"beforeRouteLeave\",Qt,!0)}function Yt(t){return Kt(t,\"beforeRouteUpdate\",Qt)}function Qt(t,e){if(e)return function(){return t.apply(e,arguments)}}function Zt(t,e,n){return Kt(t,\"beforeRouteEnter\",(function(t,r,o,i){return te(t,o,i,e,n)}))}function te(t,e,n,r,o){return function(i,a,s){return t(i,a,(function(t){\"function\"===typeof t&&r.push((function(){ee(t,e.instances,n,o)})),s(t)}))}}function ee(t,e,n,r){e[n]&&!e[n]._isBeingDestroyed?t(e[n]):r()&&setTimeout((function(){ee(t,e,n,r)}),16)}zt.prototype.listen=function(t){this.cb=t},zt.prototype.onReady=function(t,e){this.ready?t():(this.readyCbs.push(t),e&&this.readyErrorCbs.push(e))},zt.prototype.onError=function(t){this.errorCbs.push(t)},zt.prototype.transitionTo=function(t,e,n){var r=this,o=this.router.match(t,this.current);this.confirmTransition(o,(function(){r.updateRoute(o),e&&e(o),r.ensureURL(),r.ready||(r.ready=!0,r.readyCbs.forEach((function(t){t(o)})))}),(function(t){n&&n(t),t&&!r.ready&&(r.ready=!0,r.readyErrorCbs.forEach((function(e){e(t)})))}))},zt.prototype.confirmTransition=function(t,e,n){var a=this,s=this.current,c=function(t){!i(Vt,t)&&o(t)&&(a.errorCbs.length?a.errorCbs.forEach((function(e){e(t)})):(r(!1,\"uncaught error during route navigation:\"),console.error(t))),n&&n(t)};if(O(t,s)&&t.matched.length===s.matched.length)return this.ensureURL(),c(new Vt(t));var u=Wt(this.current.matched,t.matched),f=u.updated,l=u.deactivated,p=u.activated,d=[].concat(Jt(l),this.router.beforeHooks,Yt(f),p.map((function(t){return t.beforeEnter})),Dt(p));this.pending=t;var h=function(e,n){if(a.pending!==t)return c();try{e(t,s,(function(t){!1===t||o(t)?(a.ensureURL(!0),c(t)):\"string\"===typeof t||\"object\"===typeof t&&(\"string\"===typeof t.path||\"string\"===typeof t.name)?(c(),\"object\"===typeof t&&t.replace?a.replace(t):a.push(t)):n(t)}))}catch(r){c(r)}};Lt(d,h,(function(){var n=[],r=function(){return a.current===t},o=Zt(p,n,r),i=o.concat(a.router.resolveHooks);Lt(i,h,(function(){if(a.pending!==t)return c();a.pending=null,e(t),a.router.app&&a.router.app.$nextTick((function(){n.forEach((function(t){t()}))}))}))}))},zt.prototype.updateRoute=function(t){var e=this.current;this.current=t,this.cb&&this.cb(t),this.router.afterHooks.forEach((function(n){n&&n(t,e)}))};var ne=function(t){function e(e,n){var r=this;t.call(this,e,n);var o=e.options.scrollBehavior,i=It&&o;i&&Ct();var a=re(this.base);window.addEventListener(\"popstate\",(function(t){var n=r.current,o=re(r.base);r.current===w&&o===a||r.transitionTo(o,(function(t){i&&Ot(e,t,n,!0)}))}))}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.go=function(t){window.history.go(t)},e.prototype.push=function(t,e,n){var r=this,o=this,i=o.current;this.transitionTo(t,(function(t){Mt(E(r.base+t.fullPath)),Ot(r.router,t,i,!1),e&&e(t)}),n)},e.prototype.replace=function(t,e,n){var r=this,o=this,i=o.current;this.transitionTo(t,(function(t){Nt(E(r.base+t.fullPath)),Ot(r.router,t,i,!1),e&&e(t)}),n)},e.prototype.ensureURL=function(t){if(re(this.base)!==this.current.fullPath){var e=E(this.base+this.current.fullPath);t?Mt(e):Nt(e)}},e.prototype.getCurrentLocation=function(){return re(this.base)},e}(zt);function re(t){var e=decodeURI(window.location.pathname);return t&&0===e.indexOf(t)&&(e=e.slice(t.length)),(e||\"/\")+window.location.search+window.location.hash}var oe=function(t){function e(e,n,r){t.call(this,e,n),r&&ie(this.base)||ae()}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.setupListeners=function(){var t=this,e=this.router,n=e.options.scrollBehavior,r=It&&n;r&&Ct(),window.addEventListener(It?\"popstate\":\"hashchange\",(function(){var e=t.current;ae()&&t.transitionTo(se(),(function(n){r&&Ot(t.router,n,e,!0),It||fe(n.fullPath)}))}))},e.prototype.push=function(t,e,n){var r=this,o=this,i=o.current;this.transitionTo(t,(function(t){ue(t.fullPath),Ot(r.router,t,i,!1),e&&e(t)}),n)},e.prototype.replace=function(t,e,n){var r=this,o=this,i=o.current;this.transitionTo(t,(function(t){fe(t.fullPath),Ot(r.router,t,i,!1),e&&e(t)}),n)},e.prototype.go=function(t){window.history.go(t)},e.prototype.ensureURL=function(t){var e=this.current.fullPath;se()!==e&&(t?ue(e):fe(e))},e.prototype.getCurrentLocation=function(){return se()},e}(zt);function ie(t){var e=re(t);if(!/^\\/#/.test(e))return window.location.replace(E(t+\"/#\"+e)),!0}function ae(){var t=se();return\"/\"===t.charAt(0)||(fe(\"/\"+t),!1)}function se(){var t=window.location.href,e=t.indexOf(\"#\");if(e<0)return\"\";t=t.slice(e+1);var n=t.indexOf(\"?\");if(n<0){var r=t.indexOf(\"#\");t=r>-1?decodeURI(t.slice(0,r))+t.slice(r):decodeURI(t)}else t=decodeURI(t.slice(0,n))+t.slice(n);return t}function ce(t){var e=window.location.href,n=e.indexOf(\"#\"),r=n>=0?e.slice(0,n):e;return r+\"#\"+t}function ue(t){It?Mt(ce(t)):window.location.hash=t}function fe(t){It?Nt(ce(t)):window.location.replace(ce(t))}var le=function(t){function e(e,n){t.call(this,e,n),this.stack=[],this.index=-1}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.push=function(t,e,n){var r=this;this.transitionTo(t,(function(t){r.stack=r.stack.slice(0,r.index+1).concat(t),r.index++,e&&e(t)}),n)},e.prototype.replace=function(t,e,n){var r=this;this.transitionTo(t,(function(t){r.stack=r.stack.slice(0,r.index).concat(t),e&&e(t)}),n)},e.prototype.go=function(t){var e=this,n=this.index+t;if(!(n<0||n>=this.stack.length)){var r=this.stack[n];this.confirmTransition(r,(function(){e.index=n,e.updateRoute(r)}),(function(t){i(Vt,t)&&(e.index=n)}))}},e.prototype.getCurrentLocation=function(){var t=this.stack[this.stack.length-1];return t?t.fullPath:\"/\"},e.prototype.ensureURL=function(){},e}(zt),pe=function(t){void 0===t&&(t={}),this.app=null,this.apps=[],this.options=t,this.beforeHooks=[],this.resolveHooks=[],this.afterHooks=[],this.matcher=ht(t.routes||[],this);var e=t.mode||\"hash\";switch(this.fallback=\"history\"===e&&!It&&!1!==t.fallback,this.fallback&&(e=\"hash\"),ut||(e=\"abstract\"),this.mode=e,e){case\"history\":this.history=new ne(this,t.base);break;case\"hash\":this.history=new oe(this,t.base,this.fallback);break;case\"abstract\":this.history=new le(this,t.base);break;default:0}},de={currentRoute:{configurable:!0}};function he(t,e){return t.push(e),function(){var n=t.indexOf(e);n>-1&&t.splice(n,1)}}function ve(t,e,n){var r=\"hash\"===n?\"#\"+e:e;return t?E(t+\"/\"+r):r}pe.prototype.match=function(t,e,n){return this.matcher.match(t,e,n)},de.currentRoute.get=function(){return this.history&&this.history.current},pe.prototype.init=function(t){var e=this;if(this.apps.push(t),t.$once(\"hook:destroyed\",(function(){var n=e.apps.indexOf(t);n>-1&&e.apps.splice(n,1),e.app===t&&(e.app=e.apps[0]||null)})),!this.app){this.app=t;var n=this.history;if(n instanceof ne)n.transitionTo(n.getCurrentLocation());else if(n instanceof oe){var r=function(){n.setupListeners()};n.transitionTo(n.getCurrentLocation(),r,r)}n.listen((function(t){e.apps.forEach((function(e){e._route=t}))}))}},pe.prototype.beforeEach=function(t){return he(this.beforeHooks,t)},pe.prototype.beforeResolve=function(t){return he(this.resolveHooks,t)},pe.prototype.afterEach=function(t){return he(this.afterHooks,t)},pe.prototype.onReady=function(t,e){this.history.onReady(t,e)},pe.prototype.onError=function(t){this.history.onError(t)},pe.prototype.push=function(t,e,n){var r=this;if(!e&&!n&&\"undefined\"!==typeof Promise)return new Promise((function(e,n){r.history.push(t,e,n)}));this.history.push(t,e,n)},pe.prototype.replace=function(t,e,n){var r=this;if(!e&&!n&&\"undefined\"!==typeof Promise)return new Promise((function(e,n){r.history.replace(t,e,n)}));this.history.replace(t,e,n)},pe.prototype.go=function(t){this.history.go(t)},pe.prototype.back=function(){this.go(-1)},pe.prototype.forward=function(){this.go(1)},pe.prototype.getMatchedComponents=function(t){var e=t?t.matched?t:this.resolve(t).route:this.currentRoute;return e?[].concat.apply([],e.matched.map((function(t){return Object.keys(t.components).map((function(e){return t.components[e]}))}))):[]},pe.prototype.resolve=function(t,e,n){e=e||this.history.current;var r=tt(t,e,n,this),o=this.match(r,e),i=o.redirectedFrom||o.fullPath,a=this.history.base,s=ve(a,i,this.mode);return{location:r,route:o,href:s,normalizedTo:r,resolved:o}},pe.prototype.addRoutes=function(t){this.matcher.addRoutes(t),this.history.current!==w&&this.history.transitionTo(this.history.getCurrentLocation())},Object.defineProperties(pe.prototype,de),pe.install=ct,pe.version=\"3.1.6\",ut&&window.Vue&&window.Vue.use(pe),e[\"a\"]=pe},\"8df4\":function(t,e,n){\"use strict\";var r=n(\"7a77\");function o(t){if(\"function\"!==typeof t)throw new TypeError(\"executor must be a function.\");var e;this.promise=new Promise((function(t){e=t}));var n=this;t((function(t){n.reason||(n.reason=new r(t),e(n.reason))}))}o.prototype.throwIfRequested=function(){if(this.reason)throw this.reason},o.source=function(){var t,e=new o((function(e){t=e}));return{token:e,cancel:t}},t.exports=o},\"90e3\":function(t,e){var n=0,r=Math.random();t.exports=function(t){return\"Symbol(\"+String(void 0===t?\"\":t)+\")_\"+(++n+r).toString(36)}},9112:function(t,e,n){var r=n(\"83ab\"),o=n(\"9bf2\"),i=n(\"5c6c\");t.exports=r?function(t,e,n){return o.f(t,e,i(1,n))}:function(t,e,n){return t[e]=n,t}},\"94ca\":function(t,e,n){var r=n(\"d039\"),o=/#|\\.prototype\\./,i=function(t,e){var n=s[a(t)];return n==u||n!=c&&(\"function\"==typeof e?r(e):!!e)},a=i.normalize=function(t){return String(t).replace(o,\".\").toLowerCase()},s=i.data={},c=i.NATIVE=\"N\",u=i.POLYFILL=\"P\";t.exports=i},\"9bdd\":function(t,e,n){var r=n(\"825a\");t.exports=function(t,e,n,o){try{return o?e(r(n)[0],n[1]):e(n)}catch(a){var i=t[\"return\"];throw void 0!==i&&r(i.call(t)),a}}},\"9bf2\":function(t,e,n){var r=n(\"83ab\"),o=n(\"0cfb\"),i=n(\"825a\"),a=n(\"c04e\"),s=Object.defineProperty;e.f=r?s:function(t,e,n){if(i(t),e=a(e,!0),i(n),o)try{return s(t,e,n)}catch(r){}if(\"get\"in n||\"set\"in n)throw TypeError(\"Accessors not supported\");return\"value\"in n&&(t[e]=n.value),t}},\"9ed3\":function(t,e,n){\"use strict\";var r=n(\"ae93\").IteratorPrototype,o=n(\"7c73\"),i=n(\"5c6c\"),a=n(\"d44e\"),s=n(\"3f8c\"),c=function(){return this};t.exports=function(t,e,n){var u=e+\" Iterator\";return t.prototype=o(r,{next:i(1,n)}),a(t,u,!1,!0),s[u]=c,t}},a691:function(t,e){var n=Math.ceil,r=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?r:n)(t)}},a79d:function(t,e,n){\"use strict\";var r=n(\"23e7\"),o=n(\"c430\"),i=n(\"fea9\"),a=n(\"d039\"),s=n(\"d066\"),c=n(\"4840\"),u=n(\"cdf9\"),f=n(\"6eeb\"),l=!!i&&a((function(){i.prototype[\"finally\"].call({then:function(){}},(function(){}))}));r({target:\"Promise\",proto:!0,real:!0,forced:l},{finally:function(t){var e=c(this,s(\"Promise\")),n=\"function\"==typeof t;return this.then(n?function(n){return u(e,t()).then((function(){return n}))}:t,n?function(n){return u(e,t()).then((function(){throw n}))}:t)}}),o||\"function\"!=typeof i||i.prototype[\"finally\"]||f(i.prototype,\"finally\",s(\"Promise\").prototype[\"finally\"])},ae93:function(t,e,n){\"use strict\";var r,o,i,a=n(\"e163\"),s=n(\"9112\"),c=n(\"5135\"),u=n(\"b622\"),f=n(\"c430\"),l=u(\"iterator\"),p=!1,d=function(){return this};[].keys&&(i=[].keys(),\"next\"in i?(o=a(a(i)),o!==Object.prototype&&(r=o)):p=!0),void 0==r&&(r={}),f||c(r,l)||s(r,l,d),t.exports={IteratorPrototype:r,BUGGY_SAFARI_ITERATORS:p}},b041:function(t,e,n){\"use strict\";var r=n(\"00ee\"),o=n(\"f5df\");t.exports=r?{}.toString:function(){return\"[object \"+o(this)+\"]\"}},b50d:function(t,e,n){\"use strict\";var r=n(\"c532\"),o=n(\"467f\"),i=n(\"30b5\"),a=n(\"83b9\"),s=n(\"c345\"),c=n(\"3934\"),u=n(\"2d83\");t.exports=function(t){return new Promise((function(e,f){var l=t.data,p=t.headers;r.isFormData(l)&&delete p[\"Content-Type\"];var d=new XMLHttpRequest;if(t.auth){var h=t.auth.username||\"\",v=t.auth.password||\"\";p.Authorization=\"Basic \"+btoa(h+\":\"+v)}var y=a(t.baseURL,t.url);if(d.open(t.method.toUpperCase(),i(y,t.params,t.paramsSerializer),!0),d.timeout=t.timeout,d.onreadystatechange=function(){if(d&&4===d.readyState&&(0!==d.status||d.responseURL&&0===d.responseURL.indexOf(\"file:\"))){var n=\"getAllResponseHeaders\"in d?s(d.getAllResponseHeaders()):null,r=t.responseType&&\"text\"!==t.responseType?d.response:d.responseText,i={data:r,status:d.status,statusText:d.statusText,headers:n,config:t,request:d};o(e,f,i),d=null}},d.onabort=function(){d&&(f(u(\"Request aborted\",t,\"ECONNABORTED\",d)),d=null)},d.onerror=function(){f(u(\"Network Error\",t,null,d)),d=null},d.ontimeout=function(){var e=\"timeout of \"+t.timeout+\"ms exceeded\";t.timeoutErrorMessage&&(e=t.timeoutErrorMessage),f(u(e,t,\"ECONNABORTED\",d)),d=null},r.isStandardBrowserEnv()){var m=n(\"7aac\"),g=(t.withCredentials||c(y))&&t.xsrfCookieName?m.read(t.xsrfCookieName):void 0;g&&(p[t.xsrfHeaderName]=g)}if(\"setRequestHeader\"in d&&r.forEach(p,(function(t,e){\"undefined\"===typeof l&&\"content-type\"===e.toLowerCase()?delete p[e]:d.setRequestHeader(e,t)})),r.isUndefined(t.withCredentials)||(d.withCredentials=!!t.withCredentials),t.responseType)try{d.responseType=t.responseType}catch(b){if(\"json\"!==t.responseType)throw b}\"function\"===typeof t.onDownloadProgress&&d.addEventListener(\"progress\",t.onDownloadProgress),\"function\"===typeof t.onUploadProgress&&d.upload&&d.upload.addEventListener(\"progress\",t.onUploadProgress),t.cancelToken&&t.cancelToken.promise.then((function(t){d&&(d.abort(),f(t),d=null)})),void 0===l&&(l=null),d.send(l)}))}},b575:function(t,e,n){var r,o,i,a,s,c,u,f,l=n(\"da84\"),p=n(\"06cf\").f,d=n(\"c6b6\"),h=n(\"2cf4\").set,v=n(\"1cdc\"),y=l.MutationObserver||l.WebKitMutationObserver,m=l.process,g=l.Promise,b=\"process\"==d(m),_=p(l,\"queueMicrotask\"),w=_&&_.value;w||(r=function(){var t,e;b&&(t=m.domain)&&t.exit();while(o){e=o.fn,o=o.next;try{e()}catch(n){throw o?a():i=void 0,n}}i=void 0,t&&t.enter()},b?a=function(){m.nextTick(r)}:y&&!v?(s=!0,c=document.createTextNode(\"\"),new y(r).observe(c,{characterData:!0}),a=function(){c.data=s=!s}):g&&g.resolve?(u=g.resolve(void 0),f=u.then,a=function(){f.call(u,r)}):a=function(){h.call(l,r)}),t.exports=w||function(t){var e={fn:t,next:void 0};i&&(i.next=e),o||(o=e,a()),i=e}},b622:function(t,e,n){var r=n(\"da84\"),o=n(\"5692\"),i=n(\"5135\"),a=n(\"90e3\"),s=n(\"4930\"),c=n(\"fdbf\"),u=o(\"wks\"),f=r.Symbol,l=c?f:f&&f.withoutSetter||a;t.exports=function(t){return i(u,t)||(s&&i(f,t)?u[t]=f[t]:u[t]=l(\"Symbol.\"+t)),u[t]}},bc3a:function(t,e,n){t.exports=n(\"cee4\")},c04e:function(t,e,n){var r=n(\"861d\");t.exports=function(t,e){if(!r(t))return t;var n,o;if(e&&\"function\"==typeof(n=t.toString)&&!r(o=n.call(t)))return o;if(\"function\"==typeof(n=t.valueOf)&&!r(o=n.call(t)))return o;if(!e&&\"function\"==typeof(n=t.toString)&&!r(o=n.call(t)))return o;throw TypeError(\"Can't convert object to primitive value\")}},c345:function(t,e,n){\"use strict\";var r=n(\"c532\"),o=[\"age\",\"authorization\",\"content-length\",\"content-type\",\"etag\",\"expires\",\"from\",\"host\",\"if-modified-since\",\"if-unmodified-since\",\"last-modified\",\"location\",\"max-forwards\",\"proxy-authorization\",\"referer\",\"retry-after\",\"user-agent\"];t.exports=function(t){var e,n,i,a={};return t?(r.forEach(t.split(\"\\n\"),(function(t){if(i=t.indexOf(\":\"),e=r.trim(t.substr(0,i)).toLowerCase(),n=r.trim(t.substr(i+1)),e){if(a[e]&&o.indexOf(e)>=0)return;a[e]=\"set-cookie\"===e?(a[e]?a[e]:[]).concat([n]):a[e]?a[e]+\", \"+n:n}})),a):a}},c401:function(t,e,n){\"use strict\";var r=n(\"c532\");t.exports=function(t,e,n){return r.forEach(n,(function(n){t=n(t,e)})),t}},c430:function(t,e){t.exports=!1},c532:function(t,e,n){\"use strict\";var r=n(\"1d2b\"),o=Object.prototype.toString;function i(t){return\"[object Array]\"===o.call(t)}function a(t){return\"undefined\"===typeof t}function s(t){return null!==t&&!a(t)&&null!==t.constructor&&!a(t.constructor)&&\"function\"===typeof t.constructor.isBuffer&&t.constructor.isBuffer(t)}function c(t){return\"[object ArrayBuffer]\"===o.call(t)}function u(t){return\"undefined\"!==typeof FormData&&t instanceof FormData}function f(t){var e;return e=\"undefined\"!==typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(t):t&&t.buffer&&t.buffer instanceof ArrayBuffer,e}function l(t){return\"string\"===typeof t}function p(t){return\"number\"===typeof t}function d(t){return null!==t&&\"object\"===typeof t}function h(t){return\"[object Date]\"===o.call(t)}function v(t){return\"[object File]\"===o.call(t)}function y(t){return\"[object Blob]\"===o.call(t)}function m(t){return\"[object Function]\"===o.call(t)}function g(t){return d(t)&&m(t.pipe)}function b(t){return\"undefined\"!==typeof URLSearchParams&&t instanceof URLSearchParams}function _(t){return t.replace(/^\\s*/,\"\").replace(/\\s*$/,\"\")}function w(){return(\"undefined\"===typeof navigator||\"ReactNative\"!==navigator.product&&\"NativeScript\"!==navigator.product&&\"NS\"!==navigator.product)&&(\"undefined\"!==typeof window&&\"undefined\"!==typeof document)}function x(t,e){if(null!==t&&\"undefined\"!==typeof t)if(\"object\"!==typeof t&&(t=[t]),i(t))for(var n=0,r=t.length;n<r;n++)e.call(null,t[n],n,t);else for(var o in t)Object.prototype.hasOwnProperty.call(t,o)&&e.call(null,t[o],o,t)}function C(){var t={};function e(e,n){\"object\"===typeof t[n]&&\"object\"===typeof e?t[n]=C(t[n],e):t[n]=e}for(var n=0,r=arguments.length;n<r;n++)x(arguments[n],e);return t}function O(){var t={};function e(e,n){\"object\"===typeof t[n]&&\"object\"===typeof e?t[n]=O(t[n],e):t[n]=\"object\"===typeof e?O({},e):e}for(var n=0,r=arguments.length;n<r;n++)x(arguments[n],e);return t}function A(t,e,n){return x(e,(function(e,o){t[o]=n&&\"function\"===typeof e?r(e,n):e})),t}t.exports={isArray:i,isArrayBuffer:c,isBuffer:s,isFormData:u,isArrayBufferView:f,isString:l,isNumber:p,isObject:d,isUndefined:a,isDate:h,isFile:v,isBlob:y,isFunction:m,isStream:g,isURLSearchParams:b,isStandardBrowserEnv:w,forEach:x,merge:C,deepMerge:O,extend:A,trim:_}},c6b6:function(t,e){var n={}.toString;t.exports=function(t){return n.call(t).slice(8,-1)}},c6cd:function(t,e,n){var r=n(\"da84\"),o=n(\"ce4e\"),i=\"__core-js_shared__\",a=r[i]||o(i,{});t.exports=a},c8af:function(t,e,n){\"use strict\";var r=n(\"c532\");t.exports=function(t,e){r.forEach(t,(function(n,r){r!==e&&r.toUpperCase()===e.toUpperCase()&&(t[e]=n,delete t[r])}))}},c8ba:function(t,e){var n;n=function(){return this}();try{n=n||new Function(\"return this\")()}catch(r){\"object\"===typeof window&&(n=window)}t.exports=n},ca84:function(t,e,n){var r=n(\"5135\"),o=n(\"fc6a\"),i=n(\"4d64\").indexOf,a=n(\"d012\");t.exports=function(t,e){var n,s=o(t),c=0,u=[];for(n in s)!r(a,n)&&r(s,n)&&u.push(n);while(e.length>c)r(s,n=e[c++])&&(~i(u,n)||u.push(n));return u}},cc12:function(t,e,n){var r=n(\"da84\"),o=n(\"861d\"),i=r.document,a=o(i)&&o(i.createElement);t.exports=function(t){return a?i.createElement(t):{}}},cca6:function(t,e,n){var r=n(\"23e7\"),o=n(\"60da\");r({target:\"Object\",stat:!0,forced:Object.assign!==o},{assign:o})},cdf9:function(t,e,n){var r=n(\"825a\"),o=n(\"861d\"),i=n(\"f069\");t.exports=function(t,e){if(r(t),o(e)&&e.constructor===t)return e;var n=i.f(t),a=n.resolve;return a(e),n.promise}},ce4e:function(t,e,n){var r=n(\"da84\"),o=n(\"9112\");t.exports=function(t,e){try{o(r,t,e)}catch(n){r[t]=e}return e}},cee4:function(t,e,n){\"use strict\";var r=n(\"c532\"),o=n(\"1d2b\"),i=n(\"0a06\"),a=n(\"4a7b\"),s=n(\"2444\");function c(t){var e=new i(t),n=o(i.prototype.request,e);return r.extend(n,i.prototype,e),r.extend(n,e),n}var u=c(s);u.Axios=i,u.create=function(t){return c(a(u.defaults,t))},u.Cancel=n(\"7a77\"),u.CancelToken=n(\"8df4\"),u.isCancel=n(\"2e67\"),u.all=function(t){return Promise.all(t)},u.spread=n(\"0df6\"),t.exports=u,t.exports.default=u},d012:function(t,e){t.exports={}},d039:function(t,e){t.exports=function(t){try{return!!t()}catch(e){return!0}}},d066:function(t,e,n){var r=n(\"428f\"),o=n(\"da84\"),i=function(t){return\"function\"==typeof t?t:void 0};t.exports=function(t,e){return arguments.length<2?i(r[t])||i(o[t]):r[t]&&r[t][e]||o[t]&&o[t][e]}},d1e7:function(t,e,n){\"use strict\";var r={}.propertyIsEnumerable,o=Object.getOwnPropertyDescriptor,i=o&&!r.call({1:2},1);e.f=i?function(t){var e=o(this,t);return!!e&&e.enumerable}:r},d2bb:function(t,e,n){var r=n(\"825a\"),o=n(\"3bbe\");t.exports=Object.setPrototypeOf||(\"__proto__\"in{}?function(){var t,e=!1,n={};try{t=Object.getOwnPropertyDescriptor(Object.prototype,\"__proto__\").set,t.call(n,[]),e=n instanceof Array}catch(i){}return function(n,i){return r(n),o(i),e?t.call(n,i):n.__proto__=i,n}}():void 0)},d3b7:function(t,e,n){var r=n(\"00ee\"),o=n(\"6eeb\"),i=n(\"b041\");r||o(Object.prototype,\"toString\",i,{unsafe:!0})},d44e:function(t,e,n){var r=n(\"9bf2\").f,o=n(\"5135\"),i=n(\"b622\"),a=i(\"toStringTag\");t.exports=function(t,e,n){t&&!o(t=n?t:t.prototype,a)&&r(t,a,{configurable:!0,value:e})}},d925:function(t,e,n){\"use strict\";t.exports=function(t){return/^([a-z][a-z\\d\\+\\-\\.]*:)?\\/\\//i.test(t)}},da84:function(t,e,n){(function(e){var n=function(t){return t&&t.Math==Math&&t};t.exports=n(\"object\"==typeof globalThis&&globalThis)||n(\"object\"==typeof window&&window)||n(\"object\"==typeof self&&self)||n(\"object\"==typeof e&&e)||Function(\"return this\")()}).call(this,n(\"c8ba\"))},df75:function(t,e,n){var r=n(\"ca84\"),o=n(\"7839\");t.exports=Object.keys||function(t){return r(t,o)}},df7c:function(t,e,n){(function(t){function n(t,e){for(var n=0,r=t.length-1;r>=0;r--){var o=t[r];\".\"===o?t.splice(r,1):\"..\"===o?(t.splice(r,1),n++):n&&(t.splice(r,1),n--)}if(e)for(;n--;n)t.unshift(\"..\");return t}function r(t){\"string\"!==typeof t&&(t+=\"\");var e,n=0,r=-1,o=!0;for(e=t.length-1;e>=0;--e)if(47===t.charCodeAt(e)){if(!o){n=e+1;break}}else-1===r&&(o=!1,r=e+1);return-1===r?\"\":t.slice(n,r)}function o(t,e){if(t.filter)return t.filter(e);for(var n=[],r=0;r<t.length;r++)e(t[r],r,t)&&n.push(t[r]);return n}e.resolve=function(){for(var e=\"\",r=!1,i=arguments.length-1;i>=-1&&!r;i--){var a=i>=0?arguments[i]:t.cwd();if(\"string\"!==typeof a)throw new TypeError(\"Arguments to path.resolve must be strings\");a&&(e=a+\"/\"+e,r=\"/\"===a.charAt(0))}return e=n(o(e.split(\"/\"),(function(t){return!!t})),!r).join(\"/\"),(r?\"/\":\"\")+e||\".\"},e.normalize=function(t){var r=e.isAbsolute(t),a=\"/\"===i(t,-1);return t=n(o(t.split(\"/\"),(function(t){return!!t})),!r).join(\"/\"),t||r||(t=\".\"),t&&a&&(t+=\"/\"),(r?\"/\":\"\")+t},e.isAbsolute=function(t){return\"/\"===t.charAt(0)},e.join=function(){var t=Array.prototype.slice.call(arguments,0);return e.normalize(o(t,(function(t,e){if(\"string\"!==typeof t)throw new TypeError(\"Arguments to path.join must be strings\");return t})).join(\"/\"))},e.relative=function(t,n){function r(t){for(var e=0;e<t.length;e++)if(\"\"!==t[e])break;for(var n=t.length-1;n>=0;n--)if(\"\"!==t[n])break;return e>n?[]:t.slice(e,n-e+1)}t=e.resolve(t).substr(1),n=e.resolve(n).substr(1);for(var o=r(t.split(\"/\")),i=r(n.split(\"/\")),a=Math.min(o.length,i.length),s=a,c=0;c<a;c++)if(o[c]!==i[c]){s=c;break}var u=[];for(c=s;c<o.length;c++)u.push(\"..\");return u=u.concat(i.slice(s)),u.join(\"/\")},e.sep=\"/\",e.delimiter=\":\",e.dirname=function(t){if(\"string\"!==typeof t&&(t+=\"\"),0===t.length)return\".\";for(var e=t.charCodeAt(0),n=47===e,r=-1,o=!0,i=t.length-1;i>=1;--i)if(e=t.charCodeAt(i),47===e){if(!o){r=i;break}}else o=!1;return-1===r?n?\"/\":\".\":n&&1===r?\"/\":t.slice(0,r)},e.basename=function(t,e){var n=r(t);return e&&n.substr(-1*e.length)===e&&(n=n.substr(0,n.length-e.length)),n},e.extname=function(t){\"string\"!==typeof t&&(t+=\"\");for(var e=-1,n=0,r=-1,o=!0,i=0,a=t.length-1;a>=0;--a){var s=t.charCodeAt(a);if(47!==s)-1===r&&(o=!1,r=a+1),46===s?-1===e?e=a:1!==i&&(i=1):-1!==e&&(i=-1);else if(!o){n=a+1;break}}return-1===e||-1===r||0===i||1===i&&e===r-1&&e===n+1?\"\":t.slice(e,r)};var i=\"b\"===\"ab\".substr(-1)?function(t,e,n){return t.substr(e,n)}:function(t,e,n){return e<0&&(e=t.length+e),t.substr(e,n)}}).call(this,n(\"4362\"))},e163:function(t,e,n){var r=n(\"5135\"),o=n(\"7b0b\"),i=n(\"f772\"),a=n(\"e177\"),s=i(\"IE_PROTO\"),c=Object.prototype;t.exports=a?Object.getPrototypeOf:function(t){return t=o(t),r(t,s)?t[s]:\"function\"==typeof t.constructor&&t instanceof t.constructor?t.constructor.prototype:t instanceof Object?c:null}},e177:function(t,e,n){var r=n(\"d039\");t.exports=!r((function(){function t(){}return t.prototype.constructor=null,Object.getPrototypeOf(new t)!==t.prototype}))},e260:function(t,e,n){\"use strict\";var r=n(\"fc6a\"),o=n(\"44d2\"),i=n(\"3f8c\"),a=n(\"69f3\"),s=n(\"7dd0\"),c=\"Array Iterator\",u=a.set,f=a.getterFor(c);t.exports=s(Array,\"Array\",(function(t,e){u(this,{type:c,target:r(t),index:0,kind:e})}),(function(){var t=f(this),e=t.target,n=t.kind,r=t.index++;return!e||r>=e.length?(t.target=void 0,{value:void 0,done:!0}):\"keys\"==n?{value:r,done:!1}:\"values\"==n?{value:e[r],done:!1}:{value:[r,e[r]],done:!1}}),\"values\"),i.Arguments=i.Array,o(\"keys\"),o(\"values\"),o(\"entries\")},e2cc:function(t,e,n){var r=n(\"6eeb\");t.exports=function(t,e,n){for(var o in e)r(t,o,e[o],n);return t}},e667:function(t,e){t.exports=function(t){try{return{error:!1,value:t()}}catch(e){return{error:!0,value:e}}}},e683:function(t,e,n){\"use strict\";t.exports=function(t,e){return e?t.replace(/\\/+$/,\"\")+\"/\"+e.replace(/^\\/+/,\"\"):t}},e6cf:function(t,e,n){\"use strict\";var r,o,i,a,s=n(\"23e7\"),c=n(\"c430\"),u=n(\"da84\"),f=n(\"d066\"),l=n(\"fea9\"),p=n(\"6eeb\"),d=n(\"e2cc\"),h=n(\"d44e\"),v=n(\"2626\"),y=n(\"861d\"),m=n(\"1c0b\"),g=n(\"19aa\"),b=n(\"c6b6\"),_=n(\"8925\"),w=n(\"2266\"),x=n(\"1c7e\"),C=n(\"4840\"),O=n(\"2cf4\").set,A=n(\"b575\"),k=n(\"cdf9\"),j=n(\"44de\"),S=n(\"f069\"),$=n(\"e667\"),E=n(\"69f3\"),T=n(\"94ca\"),P=n(\"b622\"),R=n(\"2d00\"),I=P(\"species\"),M=\"Promise\",N=E.get,L=E.set,D=E.getterFor(M),F=l,U=u.TypeError,B=u.document,q=u.process,H=f(\"fetch\"),V=S.f,z=V,G=\"process\"==b(q),W=!!(B&&B.createEvent&&u.dispatchEvent),K=\"unhandledrejection\",X=\"rejectionhandled\",J=0,Y=1,Q=2,Z=1,tt=2,et=T(M,(function(){var t=_(F)!==String(F);if(!t){if(66===R)return!0;if(!G&&\"function\"!=typeof PromiseRejectionEvent)return!0}if(c&&!F.prototype[\"finally\"])return!0;if(R>=51&&/native code/.test(F))return!1;var e=F.resolve(1),n=function(t){t((function(){}),(function(){}))},r=e.constructor={};return r[I]=n,!(e.then((function(){}))instanceof n)})),nt=et||!x((function(t){F.all(t)[\"catch\"]((function(){}))})),rt=function(t){var e;return!(!y(t)||\"function\"!=typeof(e=t.then))&&e},ot=function(t,e,n){if(!e.notified){e.notified=!0;var r=e.reactions;A((function(){var o=e.value,i=e.state==Y,a=0;while(r.length>a){var s,c,u,f=r[a++],l=i?f.ok:f.fail,p=f.resolve,d=f.reject,h=f.domain;try{l?(i||(e.rejection===tt&&ct(t,e),e.rejection=Z),!0===l?s=o:(h&&h.enter(),s=l(o),h&&(h.exit(),u=!0)),s===f.promise?d(U(\"Promise-chain cycle\")):(c=rt(s))?c.call(s,p,d):p(s)):d(o)}catch(v){h&&!u&&h.exit(),d(v)}}e.reactions=[],e.notified=!1,n&&!e.rejection&&at(t,e)}))}},it=function(t,e,n){var r,o;W?(r=B.createEvent(\"Event\"),r.promise=e,r.reason=n,r.initEvent(t,!1,!0),u.dispatchEvent(r)):r={promise:e,reason:n},(o=u[\"on\"+t])?o(r):t===K&&j(\"Unhandled promise rejection\",n)},at=function(t,e){O.call(u,(function(){var n,r=e.value,o=st(e);if(o&&(n=$((function(){G?q.emit(\"unhandledRejection\",r,t):it(K,t,r)})),e.rejection=G||st(e)?tt:Z,n.error))throw n.value}))},st=function(t){return t.rejection!==Z&&!t.parent},ct=function(t,e){O.call(u,(function(){G?q.emit(\"rejectionHandled\",t):it(X,t,e.value)}))},ut=function(t,e,n,r){return function(o){t(e,n,o,r)}},ft=function(t,e,n,r){e.done||(e.done=!0,r&&(e=r),e.value=n,e.state=Q,ot(t,e,!0))},lt=function(t,e,n,r){if(!e.done){e.done=!0,r&&(e=r);try{if(t===n)throw U(\"Promise can't be resolved itself\");var o=rt(n);o?A((function(){var r={done:!1};try{o.call(n,ut(lt,t,r,e),ut(ft,t,r,e))}catch(i){ft(t,r,i,e)}})):(e.value=n,e.state=Y,ot(t,e,!1))}catch(i){ft(t,{done:!1},i,e)}}};et&&(F=function(t){g(this,F,M),m(t),r.call(this);var e=N(this);try{t(ut(lt,this,e),ut(ft,this,e))}catch(n){ft(this,e,n)}},r=function(t){L(this,{type:M,done:!1,notified:!1,parent:!1,reactions:[],rejection:!1,state:J,value:void 0})},r.prototype=d(F.prototype,{then:function(t,e){var n=D(this),r=V(C(this,F));return r.ok=\"function\"!=typeof t||t,r.fail=\"function\"==typeof e&&e,r.domain=G?q.domain:void 0,n.parent=!0,n.reactions.push(r),n.state!=J&&ot(this,n,!1),r.promise},catch:function(t){return this.then(void 0,t)}}),o=function(){var t=new r,e=N(t);this.promise=t,this.resolve=ut(lt,t,e),this.reject=ut(ft,t,e)},S.f=V=function(t){return t===F||t===i?new o(t):z(t)},c||\"function\"!=typeof l||(a=l.prototype.then,p(l.prototype,\"then\",(function(t,e){var n=this;return new F((function(t,e){a.call(n,t,e)})).then(t,e)}),{unsafe:!0}),\"function\"==typeof H&&s({global:!0,enumerable:!0,forced:!0},{fetch:function(t){return k(F,H.apply(u,arguments))}}))),s({global:!0,wrap:!0,forced:et},{Promise:F}),h(F,M,!1,!0),v(M),i=f(M),s({target:M,stat:!0,forced:et},{reject:function(t){var e=V(this);return e.reject.call(void 0,t),e.promise}}),s({target:M,stat:!0,forced:c||et},{resolve:function(t){return k(c&&this===i?F:this,t)}}),s({target:M,stat:!0,forced:nt},{all:function(t){var e=this,n=V(e),r=n.resolve,o=n.reject,i=$((function(){var n=m(e.resolve),i=[],a=0,s=1;w(t,(function(t){var c=a++,u=!1;i.push(void 0),s++,n.call(e,t).then((function(t){u||(u=!0,i[c]=t,--s||r(i))}),o)})),--s||r(i)}));return i.error&&o(i.value),n.promise},race:function(t){var e=this,n=V(e),r=n.reject,o=$((function(){var o=m(e.resolve);w(t,(function(t){o.call(e,t).then(n.resolve,r)}))}));return o.error&&r(o.value),n.promise}})},e893:function(t,e,n){var r=n(\"5135\"),o=n(\"56ef\"),i=n(\"06cf\"),a=n(\"9bf2\");t.exports=function(t,e){for(var n=o(e),s=a.f,c=i.f,u=0;u<n.length;u++){var f=n[u];r(t,f)||s(t,f,c(e,f))}}},e95a:function(t,e,n){var r=n(\"b622\"),o=n(\"3f8c\"),i=r(\"iterator\"),a=Array.prototype;t.exports=function(t){return void 0!==t&&(o.Array===t||a[i]===t)}},f069:function(t,e,n){\"use strict\";var r=n(\"1c0b\"),o=function(t){var e,n;this.promise=new t((function(t,r){if(void 0!==e||void 0!==n)throw TypeError(\"Bad Promise constructor\");e=t,n=r})),this.resolve=r(e),this.reject=r(n)};t.exports.f=function(t){return new o(t)}},f5df:function(t,e,n){var r=n(\"00ee\"),o=n(\"c6b6\"),i=n(\"b622\"),a=i(\"toStringTag\"),s=\"Arguments\"==o(function(){return arguments}()),c=function(t,e){try{return t[e]}catch(n){}};t.exports=r?o:function(t){var e,n,r;return void 0===t?\"Undefined\":null===t?\"Null\":\"string\"==typeof(n=c(e=Object(t),a))?n:s?o(e):\"Object\"==(r=o(e))&&\"function\"==typeof e.callee?\"Arguments\":r}},f6b4:function(t,e,n){\"use strict\";var r=n(\"c532\");function o(){this.handlers=[]}o.prototype.use=function(t,e){return this.handlers.push({fulfilled:t,rejected:e}),this.handlers.length-1},o.prototype.eject=function(t){this.handlers[t]&&(this.handlers[t]=null)},o.prototype.forEach=function(t){r.forEach(this.handlers,(function(e){null!==e&&t(e)}))},t.exports=o},f772:function(t,e,n){var r=n(\"5692\"),o=n(\"90e3\"),i=r(\"keys\");t.exports=function(t){return i[t]||(i[t]=o(t))}},fc6a:function(t,e,n){var r=n(\"44ad\"),o=n(\"1d80\");t.exports=function(t){return r(o(t))}},fdbf:function(t,e,n){var r=n(\"4930\");t.exports=r&&!Symbol.sham&&\"symbol\"==typeof Symbol.iterator},fea9:function(t,e,n){var r=n(\"da84\");t.exports=r.Promise}}]);\n//# sourceMappingURL=chunk-vendors.57f9e9d6.js.map"
  },
  {
    "path": "lesson77/bluebell/templates/index.html",
    "content": "<!DOCTYPE html><html lang=zh-CN><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content=\"IE=edge\"><meta name=viewport content=\"width=device-width,initial-scale=1\"><link rel=icon href=/static/favicon.ico><title>bluebell_frontend</title><link href=/static/css/app.6310b918.css rel=preload as=style><link href=/static/js/app.ea0d453e.js rel=preload as=script><link href=/static/js/chunk-vendors.57f9e9d6.js rel=preload as=script><link href=/static/css/app.6310b918.css rel=stylesheet></head><body><noscript><strong>We're sorry but bluebell_frontend doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/static/js/chunk-vendors.57f9e9d6.js></script><script src=/static/js/app.ea0d453e.js></script></body></html>"
  },
  {
    "path": "lesson77/bluebell/wait-for.sh",
    "content": "#!/bin/bash\n\nTIMEOUT=15\nQUIET=0\n\nADDRS=()\n\nechoerr() {\n  if [ \"$QUIET\" -ne 1 ]; then printf \"%s\\n\" \"$*\" 1>&2; fi\n}\n\nusage() {\n  exitcode=\"$1\"\n  cat << USAGE >&2\nclient:\n  $cmdname host:port [host:port] [host:port] [-t timeout] [-- command args]\n  -q | --quiet                        Do not output any status messages\n  -t TIMEOUT | --timeout=timeout      Timeout in seconds, zero for no timeout\n  -- COMMAND ARGS                     Execute command with args after the test finishes\nUSAGE\n  exit \"$exitcode\"\n}\n\nwait_for() {\n  results=()\n  for addr in ${ADDRS[@]}\n  do\n    HOST=$(printf \"%s\\n\" \"$addr\"| cut -d : -f 1)\n    PORT=$(printf \"%s\\n\" \"$addr\"| cut -d : -f 2)\n    result=1\n    for i in `seq $TIMEOUT` ; do\n      nc -z \"$HOST\" \"$PORT\" > /dev/null 2>&1\n      result=$?\n      if [ $result -ne 0 ] ; then\n        sleep 1\n\tcontinue\n      fi\n      break\n    done\n    results=(${results[@]} $result)\n  done\n  num=${#results[@]}\n  for result in ${results[@]}\n  do\n    if [ $result -eq 0 ] ; then\n\t    num=`expr $num - 1`\n    fi\n  done\n  if [ $num -eq 0 ] ; then\n    if [ $# -gt 0 ] ; then\n      exec \"$@\"\n    fi\n    exit 0\n  fi\n  echo \"Operation timed out\" >&2\n  exit 1\n}\n\nwhile [ $# -gt 0 ]\ndo\n  case \"$1\" in\n    *:* )\n    ADDRS=(${ADDRS[@]} $1)\n    shift 1\n    ;;\n    -q | --quiet)\n    QUIET=1\n    shift 1\n    ;;\n    -t)\n    TIMEOUT=\"$2\"\n    if [ \"$TIMEOUT\" = \"\" ]; then break; fi\n    shift 2\n    ;;\n    --timeout=*)\n    TIMEOUT=\"${1#*=}\"\n    shift 1\n    ;;\n    --)\n    shift\n    break\n    ;;\n    --help)\n    usage 0\n    ;;\n    *)\n    echoerr \"Unknown argument: $1\"\n    usage 1\n    ;;\n  esac\ndone\n\nif [ \"${#ADDRS[@]}\" -eq 0 ]; then\n  echoerr \"Error: you need to provide a host and port to test.\"\n  usage 2\nfi\n\nwait_for \"$@\""
  },
  {
    "path": "lesson80/bluebell/.air.conf",
    "content": "# [Air](https://github.com/cosmtrek/air) TOML 格式的配置文件\n\n# 工作目录\n# 使用 . 或绝对路径，请注意 `tmp_dir` 目录必须在 `root` 目录下\nroot = \".\"\ntmp_dir = \"tmp\"\n\n[build]\n# 只需要写你平常编译使用的shell命令。你也可以使用 `make`\ncmd = \"swag init && go build -o ./tmp/main\"\n# 由`cmd`命令得到的二进制文件名\nbin = \"tmp/main\"\n# 自定义的二进制，可以添加额外的编译标识例如添加 GIN_MODE=release\nfull_bin = \"./tmp/main ./conf/config.yaml\"\n# 监听以下文件扩展名的文件.\ninclude_ext = [\"go\", \"tpl\", \"tmpl\", \"html\", \"yaml\"]\n# 忽略这些文件扩展名或目录\nexclude_dir = [\"assets\", \"tmp\", \"vendor\", \"frontend/node_modules\"]\n# 监听以下指定目录的文件\ninclude_dir = []\n# 排除以下文件\nexclude_file = []\n# 如果文件更改过于频繁，则没有必要在每次更改时都触发构建。可以设置触发构建的延迟时间\ndelay = 1000 # ms\n# 发生构建错误时，停止运行旧的二进制文件。\nstop_on_error = true\n# air的日志文件名，该日志文件放置在你的`tmp_dir`中\nlog = \"air_errors.log\"\n\n[log]\n# 显示日志时间\ntime = true\n\n[color]\n# 自定义每个部分显示的颜色。如果找不到颜色，使用原始的应用程序日志。\nmain = \"magenta\"\nwatcher = \"cyan\"\nbuild = \"yellow\"\nrunner = \"green\"\n\n[misc]\n# 退出时删除tmp目录\nclean_on_exit = true"
  },
  {
    "path": "lesson80/bluebell/.gitignore",
    "content": ".idea/\n.DS_Store\n*.log\n"
  },
  {
    "path": "lesson80/bluebell/Dockerfile",
    "content": "FROM golang:alpine AS builder\n\n# 为我们的镜像设置必要的环境变量\nENV GO111MODULE=on \\\n    CGO_ENABLED=0 \\\n    GOOS=linux \\\n    GOARCH=amd64\n\n# 移动到工作目录：/build\nWORKDIR /build\n\n# 复制项目中的 go.mod 和 go.sum文件并下载依赖信息\nCOPY go.mod .\nCOPY go.sum .\nRUN go mod download\n\n# 将代码复制到容器中\nCOPY . .\n\n# 将我们的代码编译成二进制可执行文件 bluebell_app\nRUN go build -o bluebell_app .\n\n###################\n# 接下来创建一个小镜像\n###################\nFROM debian:stretch-slim\n\nCOPY ./wait-for.sh /\nCOPY ./templates /templates\nCOPY ./static /static\nCOPY ./conf /conf\n\n# 从builder镜像中把/dist/app 拷贝到当前目录\nCOPY --from=builder /build/bluebell_app /\n\nRUN set -eux; \\\n\tapt-get update; \\\n\tapt-get install -y \\\n\t\t--no-install-recommends \\\n\t\tnetcat; \\\n        chmod 755 wait-for.sh\n\n# 声明服务端口\nEXPOSE 8084\n\n# 需要运行的命令\n#ENTRYPOINT [\"/bluebell_app\", \"conf/config.yaml\"]"
  },
  {
    "path": "lesson80/bluebell/Dockerfile.back",
    "content": "FROM golang:alpine AS builder\n\n# 为我们的镜像设置必要的环境变量\nENV GO111MODULE=on \\\n    CGO_ENABLED=0 \\\n    GOOS=linux \\\n    GOARCH=amd64\n\n# 移动到工作目录：/build\nWORKDIR /build\n\n# 复制项目中的 go.mod 和 go.sum文件并下载依赖信息\nCOPY go.mod .\nCOPY go.sum .\nRUN go mod download\n\n# 将代码复制到容器中\nCOPY . .\n\n# 将我们的代码编译成二进制可执行文件 bluebell_app\nRUN go build -o bluebell_app .\n\n###################\n# 接下来创建一个小镜像\n###################\nFROM scratch\n\nCOPY ./templates /templates\nCOPY ./static /static\nCOPY ./conf /conf\n\n# 从builder镜像中把/dist/app 拷贝到当前目录\nCOPY --from=builder /build/bluebell_app /\n\n# 声明服务端口\nEXPOSE 8084\n\n# 需要运行的命令\nENTRYPOINT [\"/bluebell_app\", \"conf/config.yaml\"]"
  },
  {
    "path": "lesson80/bluebell/Makefile",
    "content": ".PHONY: all build run gotool clean help\n\nBINARY=\"bluebell\"\n\nall: gotool build\n\nbuild:\n\tCGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags \"-s -w\" -o ./bin/${BINARY}\n\nrun:\n\t@go run ./main.go conf/config.yaml\n\ngotool:\n\tgo fmt ./\n\tgo vet ./\n\nclean:\n\t@if [ -f ${BINARY} ] ; then rm ${BINARY} ; fi\n\nhelp:\n\t@echo \"make - 格式化 Go 代码, 并编译生成二进制文件\"\n\t@echo \"make build - 编译 Go 代码, 生成二进制文件\"\n\t@echo \"make run - 直接运行 Go 代码\"\n\t@echo \"make clean - 移除二进制文件和 vim swap files\"\n\t@echo \"make gotool - 运行 Go 工具 'fmt' and 'vet'\"\n"
  },
  {
    "path": "lesson80/bluebell/README.md",
    "content": "# 指南\n\n\n## 请按如下顺序启动项目\n\n1. 请根据你的实际情况修改 conf/config.yaml 文件中 MySQL 和 Redis 部分的配置！！！\n2. 连接上你的MySQL数据库，按顺序依次执行项目中SQL文件夹中的sql文件，完成建库、建表和导入初始数据\n    1. init.sql\n    2. bluebell_user.sql\n    3. bluebell_community.sql\n    4. bluebell_post.sql\n3. 执行 `go build -o ./bin/bluebell`，编译可执行文件至项目的bin目录\n4. 执行 `./bin/bluebell conf/config.yaml`，启动程序\n5. 打开你的浏览器输入 [http://127.0.0.1:8084](http://127.0.0.1:8084)，默认端口是 8084，你可以在配置文件中修改\n\n## 注意事项\n1. 确保你的MySQL配置是正确的\n2. 确保你的Redis配置是正确的\n3. 可点击右上角自行注册测试账号\n\n\n\n## 常见错误\n如果本地编译遇到以下报错，请在终端执行 `go get -u golang.org/x/sys` 命令。\n```bash\n# golang.org/x/sys/unix\n../../../../learngo/pkg/mod/golang.org/x/sys@v0.0.0-20200905004654-be1d3432aa8f/unix/syscall_darwin.1_13.go:29:3: //go:linkname must refer to declared function or variable\n../../../../learngo/pkg/mod/golang.org/x/sys@v0.0.0-20200905004654-be1d3432aa8f/unix/zsyscall_darwin_amd64.1_13.go:27:3: //go:linkname must refer to declared function or variable\n../../../../learngo/pkg/mod/golang.org/x/sys@v0.0.0-20200905004654-be1d3432aa8f/unix/zsyscall_darwin_amd64.1_13.go:40:3: //go:linkname must refer to declared function or variable\n../../../../learngo/pkg/mod/golang.org/x/sys@v0.0.0-20200905004654-be1d3432aa8f/unix/zsyscall_darwin_amd64.go:28:3: //go:linkname must refer to declared function or variable\n../../../../learngo/pkg/mod/golang.org/x/sys@v0.0.0-20200905004654-be1d3432aa8f/unix/zsyscall_darwin_amd64.go:43:3: //go:linkname must refer to declared function or variable\n../../../../learngo/pkg/mod/golang.org/x/sys@v0.0.0-20200905004654-be1d3432aa8f/unix/zsyscall_darwin_amd64.go:59:3: //go:linkname must refer to declared function or variable\n../../../../learngo/pkg/mod/golang.org/x/sys@v0.0.0-20200905004654-be1d3432aa8f/unix/zsyscall_darwin_amd64.go:75:3: //go:linkname must refer to declared function or variable\n../../../../learngo/pkg/mod/golang.org/x/sys@v0.0.0-20200905004654-be1d3432aa8f/unix/zsyscall_darwin_amd64.go:90:3: //go:linkname must refer to declared function or variable\n../../../../learngo/pkg/mod/golang.org/x/sys@v0.0.0-20200905004654-be1d3432aa8f/unix/zsyscall_darwin_amd64.go:105:3: //go:linkname must refer to declared function or variable\n../../../../learngo/pkg/mod/golang.org/x/sys@v0.0.0-20200905004654-be1d3432aa8f/unix/zsyscall_darwin_amd64.go:121:3: //go:linkname must refer to declared function or variable\n../../../../learngo/pkg/mod/golang.org/x/sys@v0.0.0-20200905004654-be1d3432aa8f/unix/zsyscall_darwin_amd64.go:121:3: too many errors\n\n```\n    "
  },
  {
    "path": "lesson80/bluebell/bin/.gitkeep",
    "content": ""
  },
  {
    "path": "lesson80/bluebell/conf/config.yaml",
    "content": "name: \"bluebell\"\nmode: \"release\"\nport: 8084\nversion: \"v0.0.1\"\nstart_time: \"2020-07-01\"\nmachine_id: 1\n\nauth:\n  jwt_expire: 8760\n\nlog:\n  level: \"info\"\n  filename: \"web_app.log\"\n  max_size: 200\n  max_age: 30\n  max_backups: 7\nmysql:\n  host: mysql8019\n  port: 3306\n  user: \"root\"\n  password: \"root1234\"\n  dbname: \"bluebell\"\n  max_open_conns: 200\n  max_idle_conns: 50\nredis:\n  host: redis507\n  port: 6379\n  password: \"\"\n  db: 0\n  pool_size: 100"
  },
  {
    "path": "lesson80/bluebell/controller/code.go",
    "content": "package controller\n\ntype ResCode int64\n\nconst (\n\tCodeSuccess ResCode = 1000 + iota\n\tCodeInvalidParam\n\tCodeUserExist\n\tCodeUserNotExist\n\tCodeInvalidPassword\n\tCodeServerBusy\n\n\tCodeNeedLogin\n\tCodeInvalidToken\n)\n\nvar codeMsgMap = map[ResCode]string{\n\tCodeSuccess:         \"success\",\n\tCodeInvalidParam:    \"请求参数错误\",\n\tCodeUserExist:       \"用户名已存在\",\n\tCodeUserNotExist:    \"用户名不存在\",\n\tCodeInvalidPassword: \"用户名或密码错误\",\n\tCodeServerBusy:      \"服务繁忙\",\n\n\tCodeNeedLogin:    \"需要登录\",\n\tCodeInvalidToken: \"无效的token\",\n}\n\nfunc (c ResCode) Msg() string {\n\tmsg, ok := codeMsgMap[c]\n\tif !ok {\n\t\tmsg = codeMsgMap[CodeServerBusy]\n\t}\n\treturn msg\n}\n"
  },
  {
    "path": "lesson80/bluebell/controller/community.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/logic\"\n\t\"strconv\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"go.uber.org/zap\"\n)\n\n// ---- 跟社区相关的 ----\n\nfunc CommunityHandler(c *gin.Context) {\n\t// 查询到所有的社区（community_id, community_name) 以列表的形式返回\n\tdata, err := logic.GetCommunityList()\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetCommunityList() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy) // 不轻易把服务端报错暴露给外面\n\t\treturn\n\t}\n\tResponseSuccess(c, data)\n}\n\n// CommunityDetailHandler 社区分类详情\nfunc CommunityDetailHandler(c *gin.Context) {\n\t// 1. 获取社区id\n\tidStr := c.Param(\"id\") // 获取URL参数\n\tid, err := strconv.ParseInt(idStr, 10, 64)\n\tif err != nil {\n\t\tResponseError(c, CodeInvalidParam)\n\t\treturn\n\t}\n\n\t// 2. 根据id获取社区详情\n\tdata, err := logic.GetCommunityDetail(id)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetCommunityList() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy) // 不轻易把服务端报错暴露给外面\n\t\treturn\n\t}\n\tResponseSuccess(c, data)\n}\n"
  },
  {
    "path": "lesson80/bluebell/controller/doc_response_models.go",
    "content": "package controller\n\nimport \"bluebell/models\"\n\n// 专门用来放接口文档用到的model\n// 因为我们的接口文档返回的数据格式是一致的，但是具体的data类型不一致\n\n// _ResponsePostList 帖子列表接口响应数据\ntype _ResponsePostList struct {\n\tCode    ResCode                 `json:\"code\"`    // 业务响应状态码\n\tMessage string                  `json:\"message\"` // 提示信息\n\tData    []*models.ApiPostDetail `json:\"data\"`    // 数据\n}\n"
  },
  {
    "path": "lesson80/bluebell/controller/post.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/logic\"\n\t\"bluebell/models\"\n\t\"strconv\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"go.uber.org/zap\"\n\t// swagger 嵌入文件\n)\n\n// CreatePostHandler 创建帖子的处理函数\nfunc CreatePostHandler(c *gin.Context) {\n\t// 1. 获取参数及参数的校验\n\t//c.ShouldBindJSON()  // validator --> binding tag\n\tp := new(models.Post)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\tzap.L().Debug(\"c.ShouldBindJSON(p) error\", zap.Any(\"err\", err))\n\t\tzap.L().Error(\"create post with invalid param\")\n\t\tResponseError(c, CodeInvalidParam)\n\t\treturn\n\t}\n\t// 从 c 取到当前发请求的用户的ID\n\tuserID, err := getCurrentUserID(c)\n\tif err != nil {\n\t\tResponseError(c, CodeNeedLogin)\n\t\treturn\n\t}\n\tp.AuthorID = userID\n\t// 2. 创建帖子\n\tif err := logic.CreatePost(p); err != nil {\n\t\tzap.L().Error(\"logic.CreatePost(p) failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\n\t// 3. 返回响应\n\tResponseSuccess(c, nil)\n}\n\n// GetPostDetailHandler 获取帖子详情的处理函数\nfunc GetPostDetailHandler(c *gin.Context) {\n\t// 1. 获取参数（从URL中获取帖子的id）\n\tpidStr := c.Param(\"id\")\n\tpid, err := strconv.ParseInt(pidStr, 10, 64)\n\tif err != nil {\n\t\tzap.L().Error(\"get post detail with invalid param\", zap.Error(err))\n\t\tResponseError(c, CodeInvalidParam)\n\t\treturn\n\t}\n\n\t// 2. 根据id取出帖子数据（查数据库）\n\tdata, err := logic.GetPostById(pid)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetPostById(pid) failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\t// 3. 返回响应\n\tResponseSuccess(c, data)\n}\n\n// GetPostListHandler 获取帖子列表的处理函数\nfunc GetPostListHandler(c *gin.Context) {\n\t// 获取分页参数\n\tpage, size := getPageInfo(c)\n\t// 获取数据\n\tdata, err := logic.GetPostList(page, size)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetPostList() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\tResponseSuccess(c, data)\n\t// 返回响应\n}\n\n// GetPostListHandler2 升级版帖子列表接口\n// @Summary 升级版帖子列表接口\n// @Description 可按社区按时间或分数排序查询帖子列表接口\n// @Tags 帖子相关接口(api分组展示使用的)\n// @Accept application/json\n// @Produce application/json\n// @Param Authorization header string true \"Bearer JWT\"\n// @Param object query models.ParamPostList false \"查询参数\"\n// @Security ApiKeyAuth\n// @Success 200 {object} _ResponsePostList\n// @Router /posts2 [get]\nfunc GetPostListHandler2(c *gin.Context) {\n\t// GET请求参数(query string)：/api/v1/posts2?page=1&size=10&order=time\n\t// 初始化结构体时指定初始参数\n\tp := &models.ParamPostList{\n\t\tPage:  1,\n\t\tSize:  10,\n\t\tOrder: models.OrderTime, // magic string\n\t}\n\t//c.ShouldBind()  根据请求的数据类型选择相应的方法去获取数据\n\t//c.ShouldBindJSON() 如果请求中携带的是json格式的数据，才能用这个方法获取到数据\n\tif err := c.ShouldBindQuery(p); err != nil {\n\t\tzap.L().Error(\"GetPostListHandler2 with invalid params\", zap.Error(err))\n\t\tResponseError(c, CodeInvalidParam)\n\t\treturn\n\t}\n\tdata, err := logic.GetPostListNew(p) // 更新：合二为一\n\t// 获取数据\n\tif err != nil {\n\t\tzap.L().Error(\"logic.GetPostList() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\tResponseSuccess(c, data)\n\t// 返回响应\n}\n\n// 根据社区去查询帖子列表\n//func GetCommunityPostListHandler(c *gin.Context) {\n//\t// 初始化结构体时指定初始参数\n//\tp := &models.ParamCommunityPostList{\n//\t\tParamPostList: &models.ParamPostList{\n//\t\t\tPage:  1,\n//\t\t\tSize:  10,\n//\t\t\tOrder: models.OrderTime,\n//\t\t},\n//\t}\n//\t//c.ShouldBind()  根据请求的数据类型选择相应的方法去获取数据\n//\t//c.ShouldBindJSON() 如果请求中携带的是json格式的数据，才能用这个方法获取到数据\n//\tif err := c.ShouldBindQuery(p); err != nil {\n//\t\tzap.L().Error(\"GetCommunityPostListHandler with invalid params\", zap.Error(err))\n//\t\tResponseError(c, CodeInvalidParam)\n//\t\treturn\n//\t}\n//\n//\t// 获取数据\n//\tdata, err := logic.GetCommunityPostList(p)\n//\tif err != nil {\n//\t\tzap.L().Error(\"logic.GetPostList() failed\", zap.Error(err))\n//\t\tResponseError(c, CodeServerBusy)\n//\t\treturn\n//\t}\n//\tResponseSuccess(c, data)\n//}\n"
  },
  {
    "path": "lesson80/bluebell/controller/post_test.go",
    "content": "package controller\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestCreatePostHandler(t *testing.T) {\n\n\tgin.SetMode(gin.TestMode)\n\tr := gin.Default()\n\turl := \"/api/v1/post\"\n\tr.POST(url, CreatePostHandler)\n\n\tbody := `{\n\t\t\"community_id\": 1,\n\t\t\"title\": \"test\",\n\t\t\"content\": \"just a test\"\n\t}`\n\n\treq, _ := http.NewRequest(http.MethodPost, url, bytes.NewReader([]byte(body)))\n\n\tw := httptest.NewRecorder()\n\tr.ServeHTTP(w, req)\n\n\tassert.Equal(t, 200, w.Code)\n\n\t// 判断响应的内容是不是按预期返回了需要登录的错误\n\n\t// 方法1：判断响应内容中是不是包含指定的字符串\n\t//assert.Contains(t, w.Body.String(), \"需要登录\")\n\n\t// 方法2：将响应的内容反序列化到ResponseData 然后判断字段与预期是否一致\n\tres := new(ResponseData)\n\tif err := json.Unmarshal(w.Body.Bytes(), res); err != nil {\n\t\tt.Fatalf(\"json.Unmarshal w.Body failed, err:%v\\n\", err)\n\t}\n\tassert.Equal(t, res.Code, CodeNeedLogin)\n}\n"
  },
  {
    "path": "lesson80/bluebell/controller/request.go",
    "content": "package controller\n\nimport (\n\t\"errors\"\n\t\"strconv\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\nconst CtxUserIDKey = \"userID\"\n\nvar ErrorUserNotLogin = errors.New(\"用户未登录\")\n\n// getCurrentUserID 获取当前登录的用户ID\nfunc getCurrentUserID(c *gin.Context) (userID int64, err error) {\n\tuid, ok := c.Get(CtxUserIDKey)\n\tif !ok {\n\t\terr = ErrorUserNotLogin\n\t\treturn\n\t}\n\tuserID, ok = uid.(int64)\n\tif !ok {\n\t\terr = ErrorUserNotLogin\n\t\treturn\n\t}\n\treturn\n}\n\nfunc getPageInfo(c *gin.Context) (int64, int64) {\n\tpageStr := c.Query(\"page\")\n\tsizeStr := c.Query(\"size\")\n\n\tvar (\n\t\tpage int64\n\t\tsize int64\n\t\terr  error\n\t)\n\n\tpage, err = strconv.ParseInt(pageStr, 10, 64)\n\tif err != nil {\n\t\tpage = 1\n\t}\n\tsize, err = strconv.ParseInt(sizeStr, 10, 64)\n\tif err != nil {\n\t\tsize = 10\n\t}\n\treturn page, size\n}\n"
  },
  {
    "path": "lesson80/bluebell/controller/response.go",
    "content": "package controller\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n/*\n{\n\t\"code\": 10000, // 程序中的错误码\n\t\"msg\": xx,     // 提示信息\n\t\"data\": {},    // 数据\n}\n\n*/\n\ntype ResponseData struct {\n\tCode ResCode     `json:\"code\"`\n\tMsg  interface{} `json:\"msg\"`\n\tData interface{} `json:\"data,omitempty\"`\n}\n\nfunc ResponseError(c *gin.Context, code ResCode) {\n\tc.JSON(http.StatusOK, &ResponseData{\n\t\tCode: code,\n\t\tMsg:  code.Msg(),\n\t\tData: nil,\n\t})\n}\n\nfunc ResponseErrorWithMsg(c *gin.Context, code ResCode, msg interface{}) {\n\tc.JSON(http.StatusOK, &ResponseData{\n\t\tCode: code,\n\t\tMsg:  msg,\n\t\tData: nil,\n\t})\n}\n\nfunc ResponseSuccess(c *gin.Context, data interface{}) {\n\tc.JSON(http.StatusOK, &ResponseData{\n\t\tCode: CodeSuccess,\n\t\tMsg:  CodeSuccess.Msg(),\n\t\tData: data,\n\t})\n}\n"
  },
  {
    "path": "lesson80/bluebell/controller/user.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/logic\"\n\t\"bluebell/models\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/go-playground/validator/v10\"\n\t\"go.uber.org/zap\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// SignUpHandler 处理注册请求的函数\nfunc SignUpHandler(c *gin.Context) {\n\t// 1. 获取参数和参数校验\n\tp := new(models.ParamSignUp)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\t// 请求参数有误，直接返回响应\n\t\tzap.L().Error(\"SignUp with invalid param\", zap.Error(err))\n\t\t// 判断err是不是validator.ValidationErrors 类型\n\t\terrs, ok := err.(validator.ValidationErrors)\n\t\tif !ok {\n\t\t\tResponseError(c, CodeInvalidParam)\n\t\t\treturn\n\t\t}\n\t\tResponseErrorWithMsg(c, CodeInvalidParam, removeTopStruct(errs.Translate(trans)))\n\t\treturn\n\t}\n\t// 2. 业务处理\n\tif err := logic.SignUp(p); err != nil {\n\t\tzap.L().Error(\"logic.SignUp failed\", zap.Error(err))\n\t\tif errors.Is(err, mysql.ErrorUserExist) {\n\t\t\tResponseError(c, CodeUserExist)\n\t\t\treturn\n\t\t}\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\t// 3. 返回响应\n\tResponseSuccess(c, nil)\n}\n\nfunc LoginHandler(c *gin.Context) {\n\t// 1.获取请求参数及参数校验\n\tp := new(models.ParamLogin)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\t// 请求参数有误，直接返回响应\n\t\tzap.L().Error(\"Login with invalid param\", zap.Error(err))\n\t\t// 判断err是不是validator.ValidationErrors 类型\n\t\terrs, ok := err.(validator.ValidationErrors)\n\t\tif !ok {\n\t\t\tResponseError(c, CodeInvalidParam)\n\t\t\treturn\n\t\t}\n\t\tResponseErrorWithMsg(c, CodeInvalidParam, removeTopStruct(errs.Translate(trans)))\n\t\treturn\n\t}\n\t// 2.业务逻辑处理\n\tuser, err := logic.Login(p)\n\tif err != nil {\n\t\tzap.L().Error(\"logic.Login failed\", zap.String(\"username\", p.Username), zap.Error(err))\n\t\tif errors.Is(err, mysql.ErrorUserNotExist) {\n\t\t\tResponseError(c, CodeUserNotExist)\n\t\t\treturn\n\t\t}\n\t\tResponseError(c, CodeInvalidPassword)\n\t\treturn\n\t}\n\n\t// 3.返回响应\n\tResponseSuccess(c, gin.H{\n\t\t\"user_id\":   fmt.Sprintf(\"%d\", user.UserID), // id值大于1<<53-1  int64类型的最大值是1<<63-1\n\t\t\"user_name\": user.Username,\n\t\t\"token\":     user.Token,\n\t})\n}\n"
  },
  {
    "path": "lesson80/bluebell/controller/validator.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/models\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin/binding\"\n\t\"github.com/go-playground/locales/en\"\n\t\"github.com/go-playground/locales/zh\"\n\tut \"github.com/go-playground/universal-translator\"\n\t\"github.com/go-playground/validator/v10\"\n\tenTranslations \"github.com/go-playground/validator/v10/translations/en\"\n\tzhTranslations \"github.com/go-playground/validator/v10/translations/zh\"\n)\n\n// 定义一个全局翻译器T\nvar trans ut.Translator\n\n// InitTrans 初始化翻译器\nfunc InitTrans(locale string) (err error) {\n\t// 修改gin框架中的Validator引擎属性，实现自定制\n\tif v, ok := binding.Validator.Engine().(*validator.Validate); ok {\n\n\t\t// 注册一个获取json tag的自定义方法\n\t\tv.RegisterTagNameFunc(func(fld reflect.StructField) string {\n\t\t\tname := strings.SplitN(fld.Tag.Get(\"json\"), \",\", 2)[0]\n\t\t\tif name == \"-\" {\n\t\t\t\treturn \"\"\n\t\t\t}\n\t\t\treturn name\n\t\t})\n\n\t\t// 为SignUpParam注册自定义校验方法\n\t\tv.RegisterStructValidation(SignUpParamStructLevelValidation, models.ParamSignUp{})\n\n\t\tzhT := zh.New() // 中文翻译器\n\t\tenT := en.New() // 英文翻译器\n\n\t\t// 第一个参数是备用（fallback）的语言环境\n\t\t// 后面的参数是应该支持的语言环境（支持多个）\n\t\t// uni := ut.New(zhT, zhT) 也是可以的\n\t\tuni := ut.New(enT, zhT, enT)\n\n\t\t// locale 通常取决于 http 请求头的 'Accept-Language'\n\t\tvar ok bool\n\t\t// 也可以使用 uni.FindTranslator(...) 传入多个locale进行查找\n\t\ttrans, ok = uni.GetTranslator(locale)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"uni.GetTranslator(%s) failed\", locale)\n\t\t}\n\n\t\t// 注册翻译器\n\t\tswitch locale {\n\t\tcase \"en\":\n\t\t\terr = enTranslations.RegisterDefaultTranslations(v, trans)\n\t\tcase \"zh\":\n\t\t\terr = zhTranslations.RegisterDefaultTranslations(v, trans)\n\t\tdefault:\n\t\t\terr = enTranslations.RegisterDefaultTranslations(v, trans)\n\t\t}\n\t\treturn\n\t}\n\treturn\n}\n\n// removeTopStruct 去除提示信息中的结构体名称\nfunc removeTopStruct(fields map[string]string) map[string]string {\n\tres := map[string]string{}\n\tfor field, err := range fields {\n\t\tres[field[strings.Index(field, \".\")+1:]] = err\n\t}\n\treturn res\n}\n\n// SignUpParamStructLevelValidation 自定义SignUpParam结构体校验函数\nfunc SignUpParamStructLevelValidation(sl validator.StructLevel) {\n\tsu := sl.Current().Interface().(models.ParamSignUp)\n\n\tif su.Password != su.RePassword {\n\t\t// 输出错误提示信息，最后一个参数就是传递的param\n\t\tsl.ReportError(su.RePassword, \"re_password\", \"RePassword\", \"eqfield\", \"password\")\n\t}\n}\n"
  },
  {
    "path": "lesson80/bluebell/controller/vote.go",
    "content": "package controller\n\nimport (\n\t\"bluebell/logic\"\n\t\"bluebell/models\"\n\n\t\"go.uber.org/zap\"\n\n\t\"github.com/go-playground/validator/v10\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// 投票\n\n//type VoteData struct {\n//\t// UserID 从请求中获取当前的用户\n//\tPostID    int64 `json:\"post_id,string\"`   // 贴子id\n//\tDirection int   `json:\"direction,string\"` // 赞成票(1)还是反对票(-1)\n//}\n\nfunc PostVoteController(c *gin.Context) {\n\t// 参数校验\n\tp := new(models.ParamVoteData)\n\tif err := c.ShouldBindJSON(p); err != nil {\n\t\terrs, ok := err.(validator.ValidationErrors) // 类型断言\n\t\tif !ok {\n\t\t\tResponseError(c, CodeInvalidParam)\n\t\t\treturn\n\t\t}\n\t\terrData := removeTopStruct(errs.Translate(trans)) // 翻译并去除掉错误提示中的结构体标识\n\t\tResponseErrorWithMsg(c, CodeInvalidParam, errData)\n\t\treturn\n\t}\n\t// 获取当前请求的用户的id\n\tuserID, err := getCurrentUserID(c)\n\tif err != nil {\n\t\tResponseError(c, CodeNeedLogin)\n\t\treturn\n\t}\n\t// 具体投票的业务逻辑\n\tif err := logic.VoteForPost(userID, p); err != nil {\n\t\tzap.L().Error(\"logic.VoteForPost() failed\", zap.Error(err))\n\t\tResponseError(c, CodeServerBusy)\n\t\treturn\n\t}\n\n\tResponseSuccess(c, nil)\n}\n"
  },
  {
    "path": "lesson80/bluebell/dao/mysql/community.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/models\"\n\t\"database/sql\"\n\n\t\"go.uber.org/zap\"\n)\n\nfunc GetCommunityList() (communityList []*models.Community, err error) {\n\tsqlStr := \"select community_id, community_name from community\"\n\tif err := db.Select(&communityList, sqlStr); err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\tzap.L().Warn(\"there is no community in db\")\n\t\t\terr = nil\n\t\t}\n\t}\n\treturn\n}\n\n// GetCommunityDetailByID 根据ID查询社区详情\nfunc GetCommunityDetailByID(id int64) (community *models.CommunityDetail, err error) {\n\tcommunity = new(models.CommunityDetail)\n\tsqlStr := `select \n\t\t\tcommunity_id, community_name, introduction, create_time\n\t\t\tfrom community \n\t\t\twhere community_id = ?\n\t`\n\tif err := db.Get(community, sqlStr, id); err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\terr = ErrorInvalidID\n\t\t}\n\t}\n\treturn community, err\n}\n"
  },
  {
    "path": "lesson80/bluebell/dao/mysql/error_code.go",
    "content": "package mysql\n\nimport \"errors\"\n\nvar (\n\tErrorUserExist       = errors.New(\"用户已存在\")\n\tErrorUserNotExist    = errors.New(\"用户不存在\")\n\tErrorInvalidPassword = errors.New(\"用户名或密码错误\")\n\tErrorInvalidID       = errors.New(\"无效的ID\")\n)\n"
  },
  {
    "path": "lesson80/bluebell/dao/mysql/mysql.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/setting\"\n\t\"fmt\"\n\n\t_ \"github.com/go-sql-driver/mysql\"\n\t\"github.com/jmoiron/sqlx\"\n)\n\nvar db *sqlx.DB\n\n// Init 初始化MySQL连接\nfunc Init(cfg *setting.MySQLConfig) (err error) {\n\t// \"user:password@tcp(host:port)/dbname\"\n\tdsn := fmt.Sprintf(\"%s:%s@tcp(%s:%d)/%s?parseTime=true&loc=Local\", cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.DB)\n\tdb, err = sqlx.Connect(\"mysql\", dsn)\n\tif err != nil {\n\t\treturn\n\t}\n\tdb.SetMaxOpenConns(cfg.MaxOpenConns)\n\tdb.SetMaxIdleConns(cfg.MaxIdleConns)\n\treturn\n}\n\n// Close 关闭MySQL连接\nfunc Close() {\n\t_ = db.Close()\n}\n"
  },
  {
    "path": "lesson80/bluebell/dao/mysql/post.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/models\"\n\t\"strings\"\n\n\t\"github.com/jmoiron/sqlx\"\n)\n\n// CreatePost 创建帖子\nfunc CreatePost(p *models.Post) (err error) {\n\tsqlStr := `insert into post(\n\tpost_id, title, content, author_id, community_id)\n\tvalues (?, ?, ?, ?, ?)\n\t`\n\t_, err = db.Exec(sqlStr, p.ID, p.Title, p.Content, p.AuthorID, p.CommunityID)\n\treturn\n}\n\n// GetPostById 根据id查询单个贴子数据\nfunc GetPostById(pid int64) (post *models.Post, err error) {\n\tpost = new(models.Post)\n\tsqlStr := `select\n\tpost_id, title, content, author_id, community_id, create_time\n\tfrom post\n\twhere post_id = ?\n\t`\n\terr = db.Get(post, sqlStr, pid)\n\treturn\n}\n\n// GetPostList 查询帖子列表函数\nfunc GetPostList(page, size int64) (posts []*models.Post, err error) {\n\tsqlStr := `select \n\tpost_id, title, content, author_id, community_id, create_time\n\tfrom post\n\tORDER BY create_time\n\tDESC\n\tlimit ?,?\n\t`\n\tposts = make([]*models.Post, 0, 2) // 不要写成make([]*models.Post, 2)\n\terr = db.Select(&posts, sqlStr, (page-1)*size, size)\n\treturn\n}\n\n// GetPostListByIDs 根据给定的id列表查询帖子数据\nfunc GetPostListByIDs(ids []string) (postList []*models.Post, err error) {\n\tsqlStr := `select post_id, title, content, author_id, community_id, create_time\n\tfrom post\n\twhere post_id in (?)\n\torder by FIND_IN_SET(post_id, ?)\n\t`\n\t// https: //www.liwenzhou.com/posts/Go/sqlx/\n\tquery, args, err := sqlx.In(sqlStr, ids, strings.Join(ids, \",\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tquery = db.Rebind(query)\n\terr = db.Select(&postList, query, args...) // !!!!!!\n\treturn\n}\n"
  },
  {
    "path": "lesson80/bluebell/dao/mysql/post_test.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/models\"\n\t\"bluebell/setting\"\n\t\"testing\"\n)\n\nfunc init() {\n\tdbCfg := setting.MySQLConfig{\n\t\tHost:         \"127.0.0.1\",\n\t\tUser:         \"root\",\n\t\tPassword:     \"root1234\",\n\t\tDB:           \"bluebell\",\n\t\tPort:         13306,\n\t\tMaxOpenConns: 10,\n\t\tMaxIdleConns: 10,\n\t}\n\terr := Init(&dbCfg)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc TestCreatePost(t *testing.T) {\n\tpost := models.Post{\n\t\tID:          10,\n\t\tAuthorID:    123,\n\t\tCommunityID: 1,\n\t\tTitle:       \"test\",\n\t\tContent:     \"just a test\",\n\t}\n\terr := CreatePost(&post)\n\tif err != nil {\n\t\tt.Fatalf(\"CreatePost insert record into mysql failed, err:%v\\n\", err)\n\t}\n\tt.Logf(\"CreatePost insert record into mysql success\")\n}\n"
  },
  {
    "path": "lesson80/bluebell/dao/mysql/user.go",
    "content": "package mysql\n\nimport (\n\t\"bluebell/models\"\n\t\"crypto/md5\"\n\t\"database/sql\"\n\t\"encoding/hex\"\n)\n\n// 把每一步数据库操作封装成函数\n// 待logic层根据业务需求调用\n\nconst secret = \"liwenzhou.com\"\n\n// CheckUserExist 检查指定用户名的用户是否存在\nfunc CheckUserExist(username string) (err error) {\n\tsqlStr := `select count(user_id) from user where username = ?`\n\tvar count int64\n\tif err := db.Get(&count, sqlStr, username); err != nil {\n\t\treturn err\n\t}\n\tif count > 0 {\n\t\treturn ErrorUserExist\n\t}\n\treturn\n}\n\n// InsertUser 想数据库中插入一条新的用户记录\nfunc InsertUser(user *models.User) (err error) {\n\t// 对密码进行加密\n\tuser.Password = encryptPassword(user.Password)\n\t// 执行SQL语句入库\n\tsqlStr := `insert into user(user_id, username, password) values(?,?,?)`\n\t_, err = db.Exec(sqlStr, user.UserID, user.Username, user.Password)\n\treturn\n}\n\n// encryptPassword 密码加密\nfunc encryptPassword(oPassword string) string {\n\th := md5.New()\n\th.Write([]byte(secret))\n\th.Write([]byte(oPassword))\n\treturn hex.EncodeToString(h.Sum(nil))\n}\n\nfunc Login(user *models.User) (err error) {\n\toPassword := user.Password // 用户登录的密码\n\tsqlStr := `select user_id, username, password from user where username=?`\n\terr = db.Get(user, sqlStr, user.Username)\n\tif err == sql.ErrNoRows {\n\t\treturn ErrorUserNotExist\n\t}\n\tif err != nil {\n\t\t// 查询数据库失败\n\t\treturn err\n\t}\n\t// 判断密码是否正确\n\tpassword := encryptPassword(oPassword)\n\tif password != user.Password {\n\t\treturn ErrorInvalidPassword\n\t}\n\treturn\n}\n\n// GetUserById 根据id获取用户信息\nfunc GetUserById(uid int64) (user *models.User, err error) {\n\tuser = new(models.User)\n\tsqlStr := `select user_id, username from user where user_id = ?`\n\terr = db.Get(user, sqlStr, uid)\n\treturn\n}\n"
  },
  {
    "path": "lesson80/bluebell/dao/redis/keys.go",
    "content": "package redis\n\n// redis key\n\n// redis key注意使用命名空间的方式,方便查询和拆分\n\nconst (\n\tPrefix             = \"bluebell:\"   // 项目key前缀\n\tKeyPostTimeZSet    = \"post:time\"   // zset;贴子及发帖时间\n\tKeyPostScoreZSet   = \"post:score\"  // zset;贴子及投票的分数\n\tKeyPostVotedZSetPF = \"post:voted:\" // zset;记录用户及投票类型;参数是post id\n\n\tKeyCommunitySetPF = \"community:\" // set;保存每个分区下帖子的id\n)\n\n// 给redis key加上前缀\nfunc getRedisKey(key string) string {\n\treturn Prefix + key\n}\n"
  },
  {
    "path": "lesson80/bluebell/dao/redis/post.go",
    "content": "package redis\n\nimport (\n\t\"bluebell/models\"\n\t\"context\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/go-redis/redis/v8\"\n)\n\nfunc getIDsFormKey(key string, page, size int64) ([]string, error) {\n\tstart := (page - 1) * size\n\tend := start + size - 1\n\t// 3. ZREVRANGE 按分数从大到小的顺序查询指定数量的元素\n\treturn client.ZRevRange(context.Background(), key, start, end).Result()\n}\n\nfunc GetPostIDsInOrder(p *models.ParamPostList) ([]string, error) {\n\t// 从redis获取id\n\t// 1. 根据用户请求中携带的order参数确定要查询的redis key\n\tkey := getRedisKey(KeyPostTimeZSet)\n\tif p.Order == models.OrderScore {\n\t\tkey = getRedisKey(KeyPostScoreZSet)\n\t}\n\t// 2. 确定查询的索引起始点\n\treturn getIDsFormKey(key, p.Page, p.Size)\n}\n\n// GetPostVoteData 根据ids查询每篇帖子的投赞成票的数据\nfunc GetPostVoteData(ids []string) (data []int64, err error) {\n\t//data = make([]int64, 0, len(ids))\n\t//for _, id := range ids {\n\t//\tkey := getRedisKey(KeyPostVotedZSetPF + id)\n\t//\t// 查找key中分数是1的元素的数量->统计每篇帖子的赞成票的数量\n\t//\tv := client.ZCount(key, \"1\", \"1\").Val()\n\t//\tdata = append(data, v)\n\t//}\n\t// 使用pipeline一次发送多条命令,减少RTT\n\tpipeline := client.Pipeline()\n\tfor _, id := range ids {\n\t\tkey := getRedisKey(KeyPostVotedZSetPF + id)\n\t\tpipeline.ZCount(context.Background(), key, \"1\", \"1\")\n\t}\n\tcmders, err := pipeline.Exec(context.Background())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdata = make([]int64, 0, len(cmders))\n\tfor _, cmder := range cmders {\n\t\tv := cmder.(*redis.IntCmd).Val()\n\t\tdata = append(data, v)\n\t}\n\treturn\n}\n\n// GetCommunityPostIDsInOrder 按社区查询ids\nfunc GetCommunityPostIDsInOrder(p *models.ParamPostList) ([]string, error) {\n\n\torderKey := getRedisKey(KeyPostTimeZSet)\n\tif p.Order == models.OrderScore {\n\t\torderKey = getRedisKey(KeyPostScoreZSet)\n\t}\n\n\t// 使用 zinterstore 把分区的帖子set与帖子分数的 zset 生成一个新的zset\n\t// 针对新的zset 按之前的逻辑取数据\n\n\t// 社区的key\n\tcKey := getRedisKey(KeyCommunitySetPF + strconv.Itoa(int(p.CommunityID)))\n\n\t// 利用缓存key减少zinterstore执行的次数\n\tkey := orderKey + strconv.Itoa(int(p.CommunityID))\n\tif client.Exists(context.Background(), key).Val() < 1 {\n\t\t// 不存在，需要计算\n\t\tpipeline := client.Pipeline()\n\t\tpipeline.ZInterStore(context.Background(), key, &redis.ZStore{\n\t\t\tKeys:      []string{cKey, orderKey},\n\t\t\tAggregate: \"MAX\",\n\t\t}) // zinterstore 计算\n\t\tpipeline.Expire(context.Background(), key, 60*time.Second) // 设置超时时间\n\t\t_, err := pipeline.Exec(context.Background())\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\t// 存在的话就直接根据key查询ids\n\treturn getIDsFormKey(key, p.Page, p.Size)\n}\n"
  },
  {
    "path": "lesson80/bluebell/dao/redis/redis.go",
    "content": "package redis\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/go-redis/redis/v8\"\n\n\t\"bluebell/setting\"\n)\n\n// 实际生产环境下 context.Background() 按需替换\n\nvar (\n\tclient *redis.Client\n\tNil    = redis.Nil\n)\n\n// Init 初始化连接\nfunc Init(cfg *setting.RedisConfig) (err error) {\n\tclient = redis.NewClient(&redis.Options{\n\t\tAddr:         fmt.Sprintf(\"%s:%d\", cfg.Host, cfg.Port),\n\t\tPassword:     cfg.Password, // no password set\n\t\tDB:           cfg.DB,       // use default DB\n\t\tPoolSize:     cfg.PoolSize,\n\t\tMinIdleConns: cfg.MinIdleConns,\n\t})\n\n\t_, err = client.Ping(context.Background()).Result()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc Close() {\n\t_ = client.Close()\n}\n"
  },
  {
    "path": "lesson80/bluebell/dao/redis/vote.go",
    "content": "package redis\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"math\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/go-redis/redis/v8\"\n)\n\n// 推荐阅读\n// 基于用户投票的相关算法：http://www.ruanyifeng.com/blog/algorithm/\n\n// 本项目使用简化版的投票分数\n// 投一票就加432分   86400/200  --> 200张赞成票可以给你的帖子续一天\n\n/* 投票的几种情况：\n   direction=1时，有两种情况：\n   \t1. 之前没有投过票，现在投赞成票    --> 更新分数和投票记录  差值的绝对值：1  +432\n   \t2. 之前投反对票，现在改投赞成票    --> 更新分数和投票记录  差值的绝对值：2  +432*2\n   direction=0时，有两种情况：\n   \t1. 之前投过反对票，现在要取消投票  --> 更新分数和投票记录  差值的绝对值：1  +432\n\t2. 之前投过赞成票，现在要取消投票  --> 更新分数和投票记录  差值的绝对值：1  -432\n   direction=-1时，有两种情况：\n   \t1. 之前没有投过票，现在投反对票    --> 更新分数和投票记录  差值的绝对值：1  -432\n   \t2. 之前投赞成票，现在改投反对票    --> 更新分数和投票记录  差值的绝对值：2  -432*2\n\n   投票的限制：\n   每个贴子自发表之日起一个星期之内允许用户投票，超过一个星期就不允许再投票了。\n   \t1. 到期之后将redis中保存的赞成票数及反对票数存储到mysql表中\n   \t2. 到期之后删除那个 KeyPostVotedZSetPF\n*/\n\n// 实际生产环境下 context.Background() 按需替换\n\nconst (\n\toneWeekInSeconds = 7 * 24 * 3600\n\tscorePerVote     = 432 // 每一票值多少分\n)\n\nvar (\n\tErrVoteTimeExpire = errors.New(\"投票时间已过\")\n\tErrVoteRepeated   = errors.New(\"不允许重复投票\")\n)\n\nfunc CreatePost(postID, communityID int64) error {\n\tpipeline := client.TxPipeline()\n\t// 帖子时间\n\tpipeline.ZAdd(context.Background(), getRedisKey(KeyPostTimeZSet), &redis.Z{\n\t\tScore:  float64(time.Now().Unix()),\n\t\tMember: postID,\n\t})\n\n\t// 帖子分数\n\tpipeline.ZAdd(context.Background(), getRedisKey(KeyPostScoreZSet), &redis.Z{\n\t\tScore:  float64(time.Now().Unix()),\n\t\tMember: postID,\n\t})\n\t// 更新：把帖子id加到社区的set\n\tcKey := getRedisKey(KeyCommunitySetPF + strconv.Itoa(int(communityID)))\n\tpipeline.SAdd(context.Background(), cKey, postID)\n\t_, err := pipeline.Exec(context.Background())\n\treturn err\n}\n\nfunc VoteForPost(userID, postID string, value float64) error {\n\t// 1. 判断投票限制\n\t// 去redis取帖子发布时间\n\tpostTime := client.ZScore(context.Background(), getRedisKey(KeyPostTimeZSet), postID).Val()\n\tif float64(time.Now().Unix())-postTime > oneWeekInSeconds {\n\t\treturn ErrVoteTimeExpire\n\t}\n\t// 2和3需要放到一个pipeline事务中操作\n\n\t// 2. 更新贴子的分数\n\t// 先查当前用户给当前帖子的投票记录\n\tov := client.ZScore(context.Background(), getRedisKey(KeyPostVotedZSetPF+postID), userID).Val()\n\n\t// 更新：如果这一次投票的值和之前保存的值一致，就提示不允许重复投票\n\tif value == ov {\n\t\treturn ErrVoteRepeated\n\t}\n\tvar op float64\n\tif value > ov {\n\t\top = 1\n\t} else {\n\t\top = -1\n\t}\n\tdiff := math.Abs(ov - value) // 计算两次投票的差值\n\tpipeline := client.TxPipeline()\n\tpipeline.ZIncrBy(context.Background(), getRedisKey(KeyPostScoreZSet), op*diff*scorePerVote, postID)\n\n\t// 3. 记录用户为该贴子投票的数据\n\tif value == 0 {\n\t\tpipeline.ZRem(context.Background(), getRedisKey(KeyPostVotedZSetPF+postID), userID)\n\t} else {\n\t\tpipeline.ZAdd(context.Background(), getRedisKey(KeyPostVotedZSetPF+postID), &redis.Z{\n\t\t\tScore:  value, // 赞成票还是反对票\n\t\t\tMember: userID,\n\t\t})\n\t}\n\t_, err := pipeline.Exec(context.Background())\n\treturn err\n}\n"
  },
  {
    "path": "lesson80/bluebell/docker-compose.yml",
    "content": "# yaml 配置\nversion: \"3.7\"\nservices:\n  mysql8019:\n    image: \"mysql:8.0.19\"\n    ports:\n      - \"33061:3306\"\n    command: \"--default-authentication-plugin=mysql_native_password --init-file /data/application/init.sql\"\n    environment:\n      MYSQL_ROOT_PASSWORD: \"root1234\"\n      MYSQL_DATABASE: \"bluebell\"\n      MYSQL_PASSWORD: \"root1234\"\n    volumes:\n      - ./init.sql:/data/application/init.sql\n  redis507:\n    image: \"redis:5.0.7\"\n    ports:\n      - \"26379:6379\"\n  bluebell_app:\n    build: .\n    command: sh -c \"./wait-for.sh mysql8019:3306 redis507:6379 -- ./bluebell_app ./conf/config.yaml\"\n    depends_on:\n      - mysql8019\n      - redis507\n    ports:\n      - \"8888:8084\""
  },
  {
    "path": "lesson80/bluebell/docs/docs.go",
    "content": "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n// This file was generated by swaggo/swag\n\npackage docs\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"strings\"\n\n\t\"github.com/alecthomas/template\"\n\t\"github.com/swaggo/swag\"\n)\n\nvar doc = `{\n    \"schemes\": {{ marshal .Schemes }},\n    \"swagger\": \"2.0\",\n    \"info\": {\n        \"description\": \"{{.Description}}\",\n        \"title\": \"{{.Title}}\",\n        \"contact\": {\n            \"name\": \"liwenzhou\",\n            \"url\": \"http://www.liwenzhou.com\"\n        },\n        \"license\": {},\n        \"version\": \"{{.Version}}\"\n    },\n    \"host\": \"{{.Host}}\",\n    \"basePath\": \"{{.BasePath}}\",\n    \"paths\": {\n        \"/posts2\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"ApiKeyAuth\": []\n                    }\n                ],\n                \"description\": \"可按社区按时间或分数排序查询帖子列表接口\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"帖子相关接口(api分组展示使用的)\"\n                ],\n                \"summary\": \"升级版帖子列表接口\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Bearer JWT\",\n                        \"name\": \"Authorization\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"可以为空\",\n                        \"name\": \"community_id\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"score\",\n                        \"description\": \"排序依据\",\n                        \"name\": \"order\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"example\": 1,\n                        \"description\": \"页码\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"example\": 10,\n                        \"description\": \"每页数据量\",\n                        \"name\": \"size\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"OK\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/controller._ResponsePostList\"\n                        }\n                    }\n                }\n            }\n        }\n    },\n    \"definitions\": {\n        \"controller._ResponsePostList\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"code\": {\n                    \"description\": \"业务响应状态码\",\n                    \"type\": \"integer\"\n                },\n                \"data\": {\n                    \"description\": \"数据\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/definitions/models.ApiPostDetail\"\n                    }\n                },\n                \"message\": {\n                    \"description\": \"提示信息\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"models.ApiPostDetail\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"community_id\",\n                \"content\",\n                \"title\"\n            ],\n            \"properties\": {\n                \"author_id\": {\n                    \"description\": \"作者id\",\n                    \"type\": \"integer\"\n                },\n                \"author_name\": {\n                    \"description\": \"作者\",\n                    \"type\": \"string\"\n                },\n                \"community_id\": {\n                    \"description\": \"社区id\",\n                    \"type\": \"integer\"\n                },\n                \"content\": {\n                    \"description\": \"帖子内容\",\n                    \"type\": \"string\"\n                },\n                \"create_time\": {\n                    \"type\": \"string\"\n                },\n                \"id\": {\n                    \"type\": \"integer\"\n                },\n                \"introduction\": {\n                    \"type\": \"string\"\n                },\n                \"name\": {\n                    \"type\": \"string\"\n                },\n                \"status\": {\n                    \"description\": \"帖子状态\",\n                    \"type\": \"integer\"\n                },\n                \"title\": {\n                    \"description\": \"帖子标题\",\n                    \"type\": \"string\"\n                },\n                \"vote_num\": {\n                    \"description\": \"投票数\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"models.ParamPostList\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"community_id\": {\n                    \"description\": \"可以为空\",\n                    \"type\": \"integer\"\n                },\n                \"order\": {\n                    \"description\": \"排序依据\",\n                    \"type\": \"string\",\n                    \"example\": \"score\"\n                },\n                \"page\": {\n                    \"description\": \"页码\",\n                    \"type\": \"integer\",\n                    \"example\": 1\n                },\n                \"size\": {\n                    \"description\": \"每页数据量\",\n                    \"type\": \"integer\",\n                    \"example\": 10\n                }\n            }\n        }\n    }\n}`\n\ntype swaggerInfo struct {\n\tVersion     string\n\tHost        string\n\tBasePath    string\n\tSchemes     []string\n\tTitle       string\n\tDescription string\n}\n\n// SwaggerInfo holds exported Swagger Info so clients can modify it\nvar SwaggerInfo = swaggerInfo{\n\tVersion:     \"1.0\",\n\tHost:        \"127.0.0.1:8084\",\n\tBasePath:    \"/api/v1\",\n\tSchemes:     []string{},\n\tTitle:       \"bluebell项目接口文档\",\n\tDescription: \"Go web开发进阶项目实战课程bluebell\",\n}\n\ntype s struct{}\n\nfunc (s *s) ReadDoc() string {\n\tsInfo := SwaggerInfo\n\tsInfo.Description = strings.Replace(sInfo.Description, \"\\n\", \"\\\\n\", -1)\n\n\tt, err := template.New(\"swagger_info\").Funcs(template.FuncMap{\n\t\t\"marshal\": func(v interface{}) string {\n\t\t\ta, _ := json.Marshal(v)\n\t\t\treturn string(a)\n\t\t},\n\t}).Parse(doc)\n\tif err != nil {\n\t\treturn doc\n\t}\n\n\tvar tpl bytes.Buffer\n\tif err := t.Execute(&tpl, sInfo); err != nil {\n\t\treturn doc\n\t}\n\n\treturn tpl.String()\n}\n\nfunc init() {\n\tswag.Register(swag.Name, &s{})\n}\n"
  },
  {
    "path": "lesson80/bluebell/docs/swagger.json",
    "content": "{\n    \"swagger\": \"2.0\",\n    \"info\": {\n        \"description\": \"Go web开发进阶项目实战课程bluebell\",\n        \"title\": \"bluebell项目接口文档\",\n        \"contact\": {\n            \"name\": \"liwenzhou\",\n            \"url\": \"http://www.liwenzhou.com\"\n        },\n        \"license\": {},\n        \"version\": \"1.0\"\n    },\n    \"host\": \"127.0.0.1:8084\",\n    \"basePath\": \"/api/v1\",\n    \"paths\": {\n        \"/posts2\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"ApiKeyAuth\": []\n                    }\n                ],\n                \"description\": \"可按社区按时间或分数排序查询帖子列表接口\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"帖子相关接口(api分组展示使用的)\"\n                ],\n                \"summary\": \"升级版帖子列表接口\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Bearer JWT\",\n                        \"name\": \"Authorization\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"可以为空\",\n                        \"name\": \"community_id\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"score\",\n                        \"description\": \"排序依据\",\n                        \"name\": \"order\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"example\": 1,\n                        \"description\": \"页码\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"example\": 10,\n                        \"description\": \"每页数据量\",\n                        \"name\": \"size\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"OK\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/controller._ResponsePostList\"\n                        }\n                    }\n                }\n            }\n        }\n    },\n    \"definitions\": {\n        \"controller._ResponsePostList\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"code\": {\n                    \"description\": \"业务响应状态码\",\n                    \"type\": \"integer\"\n                },\n                \"data\": {\n                    \"description\": \"数据\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/definitions/models.ApiPostDetail\"\n                    }\n                },\n                \"message\": {\n                    \"description\": \"提示信息\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"models.ApiPostDetail\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"community_id\",\n                \"content\",\n                \"title\"\n            ],\n            \"properties\": {\n                \"author_id\": {\n                    \"description\": \"作者id\",\n                    \"type\": \"integer\"\n                },\n                \"author_name\": {\n                    \"description\": \"作者\",\n                    \"type\": \"string\"\n                },\n                \"community_id\": {\n                    \"description\": \"社区id\",\n                    \"type\": \"integer\"\n                },\n                \"content\": {\n                    \"description\": \"帖子内容\",\n                    \"type\": \"string\"\n                },\n                \"create_time\": {\n                    \"type\": \"string\"\n                },\n                \"id\": {\n                    \"type\": \"integer\"\n                },\n                \"introduction\": {\n                    \"type\": \"string\"\n                },\n                \"name\": {\n                    \"type\": \"string\"\n                },\n                \"status\": {\n                    \"description\": \"帖子状态\",\n                    \"type\": \"integer\"\n                },\n                \"title\": {\n                    \"description\": \"帖子标题\",\n                    \"type\": \"string\"\n                },\n                \"vote_num\": {\n                    \"description\": \"投票数\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"models.ParamPostList\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"community_id\": {\n                    \"description\": \"可以为空\",\n                    \"type\": \"integer\"\n                },\n                \"order\": {\n                    \"description\": \"排序依据\",\n                    \"type\": \"string\",\n                    \"example\": \"score\"\n                },\n                \"page\": {\n                    \"description\": \"页码\",\n                    \"type\": \"integer\",\n                    \"example\": 1\n                },\n                \"size\": {\n                    \"description\": \"每页数据量\",\n                    \"type\": \"integer\",\n                    \"example\": 10\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "lesson80/bluebell/docs/swagger.yaml",
    "content": "basePath: /api/v1\ndefinitions:\n  controller._ResponsePostList:\n    properties:\n      code:\n        description: 业务响应状态码\n        type: integer\n      data:\n        description: 数据\n        items:\n          $ref: '#/definitions/models.ApiPostDetail'\n        type: array\n      message:\n        description: 提示信息\n        type: string\n    type: object\n  models.ApiPostDetail:\n    properties:\n      author_id:\n        description: 作者id\n        type: integer\n      author_name:\n        description: 作者\n        type: string\n      community_id:\n        description: 社区id\n        type: integer\n      content:\n        description: 帖子内容\n        type: string\n      create_time:\n        type: string\n      id:\n        type: integer\n      introduction:\n        type: string\n      name:\n        type: string\n      status:\n        description: 帖子状态\n        type: integer\n      title:\n        description: 帖子标题\n        type: string\n      vote_num:\n        description: 投票数\n        type: integer\n    required:\n    - community_id\n    - content\n    - title\n    type: object\n  models.ParamPostList:\n    properties:\n      community_id:\n        description: 可以为空\n        type: integer\n      order:\n        description: 排序依据\n        example: score\n        type: string\n      page:\n        description: 页码\n        example: 1\n        type: integer\n      size:\n        description: 每页数据量\n        example: 10\n        type: integer\n    type: object\nhost: 127.0.0.1:8084\ninfo:\n  contact:\n    name: liwenzhou\n    url: http://www.liwenzhou.com\n  description: Go web开发进阶项目实战课程bluebell\n  license: {}\n  title: bluebell项目接口文档\n  version: \"1.0\"\npaths:\n  /posts2:\n    get:\n      consumes:\n      - application/json\n      description: 可按社区按时间或分数排序查询帖子列表接口\n      parameters:\n      - description: Bearer JWT\n        in: header\n        name: Authorization\n        required: true\n        type: string\n      - description: 可以为空\n        in: query\n        name: community_id\n        type: integer\n      - description: 排序依据\n        example: score\n        in: query\n        name: order\n        type: string\n      - description: 页码\n        example: 1\n        in: query\n        name: page\n        type: integer\n      - description: 每页数据量\n        example: 10\n        in: query\n        name: size\n        type: integer\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: OK\n          schema:\n            $ref: '#/definitions/controller._ResponsePostList'\n      security:\n      - ApiKeyAuth: []\n      summary: 升级版帖子列表接口\n      tags:\n      - 帖子相关接口(api分组展示使用的)\nswagger: \"2.0\"\n"
  },
  {
    "path": "lesson80/bluebell/go.mod",
    "content": "module bluebell\n\ngo 1.18\n\nrequire (\n\tgithub.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751\n\tgithub.com/bwmarrin/snowflake v0.3.0\n\tgithub.com/dgrijalva/jwt-go v3.2.0+incompatible\n\tgithub.com/fsnotify/fsnotify v1.4.9\n\tgithub.com/gin-contrib/pprof v1.3.0\n\tgithub.com/gin-gonic/gin v1.6.3\n\tgithub.com/go-playground/locales v0.13.0\n\tgithub.com/go-playground/universal-translator v0.17.0\n\tgithub.com/go-playground/validator/v10 v10.3.0\n\tgithub.com/go-redis/redis v6.15.8+incompatible\n\tgithub.com/go-redis/redis/v8 v8.11.5\n\tgithub.com/go-sql-driver/mysql v1.5.0\n\tgithub.com/jmoiron/sqlx v1.2.0\n\tgithub.com/juju/ratelimit v1.0.1\n\tgithub.com/natefinch/lumberjack v2.0.0+incompatible\n\tgithub.com/spf13/viper v1.7.0\n\tgithub.com/stretchr/testify v1.5.1\n\tgithub.com/swaggo/gin-swagger v1.2.0\n\tgithub.com/swaggo/swag v1.6.7\n\tgo.uber.org/zap v1.15.0\n)\n\nrequire (\n\tgithub.com/KyleBanks/depth v1.2.1 // indirect\n\tgithub.com/PuerkitoBio/purell v1.1.1 // indirect\n\tgithub.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.1.2 // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect\n\tgithub.com/gin-contrib/sse v0.1.0 // indirect\n\tgithub.com/go-openapi/jsonpointer v0.19.3 // indirect\n\tgithub.com/go-openapi/jsonreference v0.19.4 // indirect\n\tgithub.com/go-openapi/spec v0.19.9 // indirect\n\tgithub.com/go-openapi/swag v0.19.9 // indirect\n\tgithub.com/golang/protobuf v1.5.2 // indirect\n\tgithub.com/hashicorp/hcl v1.0.0 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/json-iterator/go v1.1.10 // indirect\n\tgithub.com/leodido/go-urn v1.2.0 // indirect\n\tgithub.com/magiconair/properties v1.8.1 // indirect\n\tgithub.com/mailru/easyjson v0.7.6 // indirect\n\tgithub.com/mattn/go-isatty v0.0.12 // indirect\n\tgithub.com/mitchellh/mapstructure v1.1.2 // indirect\n\tgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect\n\tgithub.com/modern-go/reflect2 v1.0.1 // indirect\n\tgithub.com/pelletier/go-toml v1.2.0 // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgithub.com/spf13/afero v1.1.2 // indirect\n\tgithub.com/spf13/cast v1.3.0 // indirect\n\tgithub.com/spf13/jwalterweatherman v1.0.0 // indirect\n\tgithub.com/spf13/pflag v1.0.3 // indirect\n\tgithub.com/subosito/gotenv v1.2.0 // indirect\n\tgithub.com/ugorji/go/codec v1.1.7 // indirect\n\tgo.uber.org/atomic v1.6.0 // indirect\n\tgo.uber.org/multierr v1.5.0 // indirect\n\tgolang.org/x/net v0.0.0-20210428140749-89ef3d95e781 // indirect\n\tgolang.org/x/sys v0.3.0 // indirect\n\tgolang.org/x/text v0.3.6 // indirect\n\tgolang.org/x/tools v0.0.0-20201224043029-2b0845dc783e // indirect\n\tgoogle.golang.org/protobuf v1.26.0 // indirect\n\tgopkg.in/ini.v1 v1.51.0 // indirect\n\tgopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect\n\tgopkg.in/yaml.v2 v2.4.0 // indirect\n)\n"
  },
  {
    "path": "lesson80/bluebell/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ngithub.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=\ngithub.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=\ngithub.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=\ngithub.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=\ngithub.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=\ngithub.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=\ngithub.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=\ngithub.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=\ngithub.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=\ngithub.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=\ngithub.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=\ngithub.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=\ngithub.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=\ngithub.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=\ngithub.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=\ngithub.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0=\ngithub.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=\ngithub.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=\ngithub.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=\ngithub.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=\ngithub.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=\ngithub.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=\ngithub.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=\ngithub.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=\ngithub.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=\ngithub.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/gin-contrib/gzip v0.0.1 h1:ezvKOL6jH+jlzdHNE4h9h8q8uMpDQjyl0NN0Jd7jozc=\ngithub.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w=\ngithub.com/gin-contrib/pprof v1.3.0 h1:G9eK6HnbkSqDZBYbzG4wrjCsA4e+cvYAHUZw6W+W9K0=\ngithub.com/gin-contrib/pprof v1.3.0/go.mod h1:waMjT1H9b179t3CxuG1cV3DHpga6ybizwfBaM5OXaB0=\ngithub.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=\ngithub.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=\ngithub.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=\ngithub.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=\ngithub.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=\ngithub.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=\ngithub.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=\ngithub.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=\ngithub.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=\ngithub.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=\ngithub.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=\ngithub.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=\ngithub.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=\ngithub.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=\ngithub.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=\ngithub.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=\ngithub.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=\ngithub.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=\ngithub.com/go-openapi/jsonreference v0.19.4 h1:3Vw+rh13uq2JFNxgnMTGE1rnoieU9FmyE1gvnyylsYg=\ngithub.com/go-openapi/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=\ngithub.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=\ngithub.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=\ngithub.com/go-openapi/spec v0.19.9 h1:9z9cbFuZJ7AcvOHKIY+f6Aevb4vObNDkTEyoMfO7rAc=\ngithub.com/go-openapi/spec v0.19.9/go.mod h1:vqK/dIdLGCosfvYsQV3WfC7N3TiZSnGY2RZKoFK7X28=\ngithub.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=\ngithub.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=\ngithub.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=\ngithub.com/go-openapi/swag v0.19.9 h1:1IxuqvBUU3S2Bi4YC7tlP9SJF1gVpCvqN0T2Qof4azE=\ngithub.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY=\ngithub.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=\ngithub.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=\ngithub.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=\ngithub.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=\ngithub.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=\ngithub.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=\ngithub.com/go-playground/validator/v10 v10.3.0 h1:nZU+7q+yJoFmwvNgv/LnPUkwPal62+b2xXj0AU1Es7o=\ngithub.com/go-playground/validator/v10 v10.3.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=\ngithub.com/go-redis/redis v6.15.8+incompatible h1:BKZuG6mCnRj5AOaWJXoCgf6rqTYnYJLe4en2hxT7r9o=\ngithub.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=\ngithub.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=\ngithub.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=\ngithub.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=\ngithub.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=\ngithub.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=\ngithub.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=\ngithub.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=\ngithub.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=\ngithub.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=\ngithub.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=\ngithub.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=\ngithub.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=\ngithub.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=\ngithub.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=\ngithub.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=\ngithub.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=\ngithub.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=\ngithub.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=\ngithub.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=\ngithub.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=\ngithub.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/juju/ratelimit v1.0.1 h1:+7AIFJVQ0EQgq/K9+0Krm7m530Du7tIz0METWzN0RgY=\ngithub.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=\ngithub.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=\ngithub.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=\ngithub.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=\ngithub.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=\ngithub.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=\ngithub.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=\ngithub.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=\ngithub.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=\ngithub.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=\ngithub.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=\ngithub.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=\ngithub.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=\ngithub.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=\ngithub.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=\ngithub.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=\ngithub.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=\ngithub.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=\ngithub.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=\ngithub.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=\ngithub.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=\ngithub.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=\ngithub.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=\ngithub.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=\ngithub.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=\ngithub.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=\ngithub.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=\ngithub.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=\ngithub.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=\ngithub.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=\ngithub.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=\ngithub.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=\ngithub.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=\ngithub.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=\ngithub.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=\ngithub.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=\ngithub.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=\ngithub.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=\ngithub.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=\ngithub.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=\ngithub.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=\ngithub.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E=\ngithub.com/swaggo/gin-swagger v1.2.0 h1:YskZXEiv51fjOMTsXrOetAjrMDfFaXD79PEoQBOe2W0=\ngithub.com/swaggo/gin-swagger v1.2.0/go.mod h1:qlH2+W7zXGZkczuL+r2nEBR2JTT+/lX05Nn6vPhc7OI=\ngithub.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y=\ngithub.com/swaggo/swag v1.6.7 h1:e8GC2xDllJZr3omJkm9YfmK0Y56+rMO3cg0JBKNz09s=\ngithub.com/swaggo/swag v1.6.7/go.mod h1:xDhTyuFIujYiN3DKWC/H/83xcfHp+UE/IzWWampG7Zc=\ngithub.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=\ngithub.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=\ngithub.com/ugorji/go v1.1.5-pre/go.mod h1:FwP/aQVg39TXzItUBMwnWp9T9gPQnXw4Poh4/oBQZ/0=\ngithub.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=\ngithub.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=\ngithub.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI=\ngithub.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=\ngithub.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=\ngithub.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=\ngithub.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=\ngithub.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngo.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=\ngo.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=\ngo.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=\ngo.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=\ngo.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngo.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=\ngo.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0=\ngolang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=\ngolang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20201224043029-2b0845dc783e h1:4nW4NLDYnU28ojHaHO8OVxFHk/aQ33U01a9cjED+pzE=\ngolang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=\ngoogle.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=\ngopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=\ngopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=\ngopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=\ngopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\n"
  },
  {
    "path": "lesson80/bluebell/logger/logger.go",
    "content": "package logger\n\nimport (\n\t\"bluebell/setting\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"os\"\n\t\"runtime/debug\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/natefinch/lumberjack\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\nvar lg *zap.Logger\n\n// Init 初始化lg\nfunc Init(cfg *setting.LogConfig, mode string) (err error) {\n\twriteSyncer := getLogWriter(cfg.Filename, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge)\n\tencoder := getEncoder()\n\tvar l = new(zapcore.Level)\n\terr = l.UnmarshalText([]byte(cfg.Level))\n\tif err != nil {\n\t\treturn\n\t}\n\tvar core zapcore.Core\n\tif mode == \"dev\" {\n\t\t// 进入开发模式，日志输出到终端\n\t\tconsoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())\n\t\tcore = zapcore.NewTee(\n\t\t\tzapcore.NewCore(encoder, writeSyncer, l),\n\t\t\tzapcore.NewCore(consoleEncoder, zapcore.Lock(os.Stdout), zapcore.DebugLevel),\n\t\t)\n\t} else {\n\t\tcore = zapcore.NewCore(encoder, writeSyncer, l)\n\t}\n\n\tlg = zap.New(core, zap.AddCaller())\n\n\tzap.ReplaceGlobals(lg)\n\tzap.L().Info(\"init logger success\")\n\treturn\n}\n\nfunc getEncoder() zapcore.Encoder {\n\tencoderConfig := zap.NewProductionEncoderConfig()\n\tencoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder\n\tencoderConfig.TimeKey = \"time\"\n\tencoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder\n\tencoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder\n\tencoderConfig.EncodeCaller = zapcore.ShortCallerEncoder\n\treturn zapcore.NewJSONEncoder(encoderConfig)\n}\n\nfunc getLogWriter(filename string, maxSize, maxBackup, maxAge int) zapcore.WriteSyncer {\n\tlumberJackLogger := &lumberjack.Logger{\n\t\tFilename:   filename,\n\t\tMaxSize:    maxSize,\n\t\tMaxBackups: maxBackup,\n\t\tMaxAge:     maxAge,\n\t}\n\treturn zapcore.AddSync(lumberJackLogger)\n}\n\n// GinLogger 接收gin框架默认的日志\nfunc GinLogger() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tstart := time.Now()\n\t\tpath := c.Request.URL.Path\n\t\tquery := c.Request.URL.RawQuery\n\t\tc.Next()\n\n\t\tcost := time.Since(start)\n\t\tlg.Info(path,\n\t\t\tzap.Int(\"status\", c.Writer.Status()),\n\t\t\tzap.String(\"method\", c.Request.Method),\n\t\t\tzap.String(\"path\", path),\n\t\t\tzap.String(\"query\", query),\n\t\t\tzap.String(\"ip\", c.ClientIP()),\n\t\t\tzap.String(\"user-agent\", c.Request.UserAgent()),\n\t\t\tzap.String(\"errors\", c.Errors.ByType(gin.ErrorTypePrivate).String()),\n\t\t\tzap.Duration(\"cost\", cost),\n\t\t)\n\t}\n}\n\n// GinRecovery recover掉项目可能出现的panic，并使用zap记录相关日志\nfunc GinRecovery(stack bool) gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tdefer func() {\n\t\t\tif err := recover(); err != nil {\n\t\t\t\t// Check for a broken connection, as it is not really a\n\t\t\t\t// condition that warrants a panic stack trace.\n\t\t\t\tvar brokenPipe bool\n\t\t\t\tif ne, ok := err.(*net.OpError); ok {\n\t\t\t\t\tif se, ok := ne.Err.(*os.SyscallError); ok {\n\t\t\t\t\t\tif strings.Contains(strings.ToLower(se.Error()), \"broken pipe\") || strings.Contains(strings.ToLower(se.Error()), \"connection reset by peer\") {\n\t\t\t\t\t\t\tbrokenPipe = true\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\thttpRequest, _ := httputil.DumpRequest(c.Request, false)\n\t\t\t\tif brokenPipe {\n\t\t\t\t\tlg.Error(c.Request.URL.Path,\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t)\n\t\t\t\t\t// If the connection is dead, we can't write a status to it.\n\t\t\t\t\tc.Error(err.(error)) // nolint: errcheck\n\t\t\t\t\tc.Abort()\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif stack {\n\t\t\t\t\tlg.Error(\"[Recovery from panic]\",\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t\tzap.String(\"stack\", string(debug.Stack())),\n\t\t\t\t\t)\n\t\t\t\t} else {\n\t\t\t\t\tlg.Error(\"[Recovery from panic]\",\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tc.AbortWithStatus(http.StatusInternalServerError)\n\t\t\t}\n\t\t}()\n\t\tc.Next()\n\t}\n}\n"
  },
  {
    "path": "lesson80/bluebell/logic/community.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/models\"\n)\n\nfunc GetCommunityList() ([]*models.Community, error) {\n\t// 查数据库 查找到所有的community 并返回\n\treturn mysql.GetCommunityList()\n}\n\nfunc GetCommunityDetail(id int64) (*models.CommunityDetail, error) {\n\treturn mysql.GetCommunityDetailByID(id)\n}\n"
  },
  {
    "path": "lesson80/bluebell/logic/post.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/dao/redis\"\n\t\"bluebell/models\"\n\t\"bluebell/pkg/snowflake\"\n\n\t\"go.uber.org/zap\"\n)\n\nfunc CreatePost(p *models.Post) (err error) {\n\t// 1. 生成post id\n\tp.ID = snowflake.GenID()\n\t// 2. 保存到数据库\n\terr = mysql.CreatePost(p)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = redis.CreatePost(p.ID, p.CommunityID)\n\treturn\n\t// 3. 返回\n}\n\n// GetPostById 根据帖子id查询帖子详情数据\nfunc GetPostById(pid int64) (data *models.ApiPostDetail, err error) {\n\t// 查询并组合我们接口想用的数据\n\tpost, err := mysql.GetPostById(pid)\n\tif err != nil {\n\t\tzap.L().Error(\"mysql.GetPostById(pid) failed\",\n\t\t\tzap.Int64(\"pid\", pid),\n\t\t\tzap.Error(err))\n\t\treturn\n\t}\n\t// 根据作者id查询作者信息\n\tuser, err := mysql.GetUserById(post.AuthorID)\n\tif err != nil {\n\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\tzap.Int64(\"author_id\", post.AuthorID),\n\t\t\tzap.Error(err))\n\t\treturn\n\t}\n\t// 根据社区id查询社区详细信息\n\tcommunity, err := mysql.GetCommunityDetailByID(post.CommunityID)\n\tif err != nil {\n\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\tzap.Int64(\"community_id\", post.CommunityID),\n\t\t\tzap.Error(err))\n\t\treturn\n\t}\n\t// 接口数据拼接\n\tdata = &models.ApiPostDetail{\n\t\tAuthorName:      user.Username,\n\t\tPost:            post,\n\t\tCommunityDetail: community,\n\t}\n\treturn\n}\n\n// GetPostList 获取帖子列表\nfunc GetPostList(page, size int64) (data []*models.ApiPostDetail, err error) {\n\tposts, err := mysql.GetPostList(page, size)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdata = make([]*models.ApiPostDetail, 0, len(posts))\n\n\tfor _, post := range posts {\n\t\t// 根据作者id查询作者信息\n\t\tuser, err := mysql.GetUserById(post.AuthorID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"author_id\", post.AuthorID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\t// 根据社区id查询社区详细信息\n\t\tcommunity, err := mysql.GetCommunityDetailByID(post.CommunityID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"community_id\", post.CommunityID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\tpostDetail := &models.ApiPostDetail{\n\t\t\tAuthorName:      user.Username,\n\t\t\tPost:            post,\n\t\t\tCommunityDetail: community,\n\t\t}\n\t\tdata = append(data, postDetail)\n\t}\n\treturn\n}\n\nfunc GetPostList2(p *models.ParamPostList) (data []*models.ApiPostDetail, err error) {\n\t// 2. 去redis查询id列表\n\tids, err := redis.GetPostIDsInOrder(p)\n\tif err != nil {\n\t\treturn\n\t}\n\tif len(ids) == 0 {\n\t\tzap.L().Warn(\"redis.GetPostIDsInOrder(p) return 0 data\")\n\t\treturn\n\t}\n\tzap.L().Debug(\"GetPostList2\", zap.Any(\"ids\", ids))\n\t// 3. 根据id去MySQL数据库查询帖子详细信息\n\t// 返回的数据还要按照我给定的id的顺序返回\n\tposts, err := mysql.GetPostListByIDs(ids)\n\tif err != nil {\n\t\treturn\n\t}\n\tzap.L().Debug(\"GetPostList2\", zap.Any(\"posts\", posts))\n\t// 提前查询好每篇帖子的投票数\n\tvoteData, err := redis.GetPostVoteData(ids)\n\tif err != nil {\n\t\treturn\n\t}\n\n\t// 将帖子的作者及分区信息查询出来填充到帖子中\n\tfor idx, post := range posts {\n\t\t// 根据作者id查询作者信息\n\t\tuser, err := mysql.GetUserById(post.AuthorID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"author_id\", post.AuthorID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\t// 根据社区id查询社区详细信息\n\t\tcommunity, err := mysql.GetCommunityDetailByID(post.CommunityID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"community_id\", post.CommunityID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\tpostDetail := &models.ApiPostDetail{\n\t\t\tAuthorName:      user.Username,\n\t\t\tVoteNum:         voteData[idx],\n\t\t\tPost:            post,\n\t\t\tCommunityDetail: community,\n\t\t}\n\t\tdata = append(data, postDetail)\n\t}\n\treturn\n\n}\n\nfunc GetCommunityPostList(p *models.ParamPostList) (data []*models.ApiPostDetail, err error) {\n\t// 2. 去redis查询id列表\n\tids, err := redis.GetCommunityPostIDsInOrder(p)\n\tif err != nil {\n\t\treturn\n\t}\n\tif len(ids) == 0 {\n\t\tzap.L().Warn(\"redis.GetPostIDsInOrder(p) return 0 data\")\n\t\treturn\n\t}\n\tzap.L().Debug(\"GetCommunityPostIDsInOrder\", zap.Any(\"ids\", ids))\n\t// 3. 根据id去MySQL数据库查询帖子详细信息\n\t// 返回的数据还要按照我给定的id的顺序返回\n\tposts, err := mysql.GetPostListByIDs(ids)\n\tif err != nil {\n\t\treturn\n\t}\n\tzap.L().Debug(\"GetPostList2\", zap.Any(\"posts\", posts))\n\t// 提前查询好每篇帖子的投票数\n\tvoteData, err := redis.GetPostVoteData(ids)\n\tif err != nil {\n\t\treturn\n\t}\n\n\t// 将帖子的作者及分区信息查询出来填充到帖子中\n\tfor idx, post := range posts {\n\t\t// 根据作者id查询作者信息\n\t\tuser, err := mysql.GetUserById(post.AuthorID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"author_id\", post.AuthorID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\t// 根据社区id查询社区详细信息\n\t\tcommunity, err := mysql.GetCommunityDetailByID(post.CommunityID)\n\t\tif err != nil {\n\t\t\tzap.L().Error(\"mysql.GetUserById(post.AuthorID) failed\",\n\t\t\t\tzap.Int64(\"community_id\", post.CommunityID),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\tpostDetail := &models.ApiPostDetail{\n\t\t\tAuthorName:      user.Username,\n\t\t\tVoteNum:         voteData[idx],\n\t\t\tPost:            post,\n\t\t\tCommunityDetail: community,\n\t\t}\n\t\tdata = append(data, postDetail)\n\t}\n\treturn\n}\n\n// GetPostListNew  将两个查询帖子列表逻辑合二为一的函数\nfunc GetPostListNew(p *models.ParamPostList) (data []*models.ApiPostDetail, err error) {\n\t// 根据请求参数的不同，执行不同的逻辑。\n\tif p.CommunityID == 0 {\n\t\t// 查所有\n\t\tdata, err = GetPostList2(p)\n\t} else {\n\t\t// 根据社区id查询\n\t\tdata, err = GetCommunityPostList(p)\n\t}\n\tif err != nil {\n\t\tzap.L().Error(\"GetPostListNew failed\", zap.Error(err))\n\t\treturn nil, err\n\t}\n\treturn\n}\n"
  },
  {
    "path": "lesson80/bluebell/logic/user.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/models\"\n\t\"bluebell/pkg/jwt\"\n\t\"bluebell/pkg/snowflake\"\n)\n\n// 存放业务逻辑的代码\n\nfunc SignUp(p *models.ParamSignUp) (err error) {\n\t// 1.判断用户存不存在\n\tif err := mysql.CheckUserExist(p.Username); err != nil {\n\t\treturn err\n\t}\n\t// 2.生成UID\n\tuserID := snowflake.GenID()\n\t// 构造一个User实例\n\tuser := &models.User{\n\t\tUserID:   userID,\n\t\tUsername: p.Username,\n\t\tPassword: p.Password,\n\t}\n\t// 3.保存进数据库\n\treturn mysql.InsertUser(user)\n}\n\nfunc Login(p *models.ParamLogin) (user *models.User, err error) {\n\tuser = &models.User{\n\t\tUsername: p.Username,\n\t\tPassword: p.Password,\n\t}\n\t// 传递的是指针，就能拿到user.UserID\n\tif err := mysql.Login(user); err != nil {\n\t\treturn nil, err\n\t}\n\t// 生成JWT\n\ttoken, err := jwt.GenToken(user.UserID, user.Username)\n\tif err != nil {\n\t\treturn\n\t}\n\tuser.Token = token\n\treturn\n}\n"
  },
  {
    "path": "lesson80/bluebell/logic/vote.go",
    "content": "package logic\n\nimport (\n\t\"bluebell/dao/redis\"\n\t\"bluebell/models\"\n\t\"strconv\"\n\n\t\"go.uber.org/zap\"\n)\n\n// 推荐阅读\n// 基于用户投票的相关算法：http://www.ruanyifeng.com/blog/algorithm/\n\n// 本项目使用简化版的投票分数\n// 投一票就加432分   86400/200  --> 200张赞成票可以给你的帖子续一天\n\n/* 投票的几种情况：\ndirection=1时，有两种情况：\n\t1. 之前没有投过票，现在投赞成票    --> 更新分数和投票记录\n\t2. 之前投反对票，现在改投赞成票    --> 更新分数和投票记录\ndirection=0时，有两种情况：\n\t1. 之前投过赞成票，现在要取消投票  --> 更新分数和投票记录\n\t2. 之前投过反对票，现在要取消投票  --> 更新分数和投票记录\ndirection=-1时，有两种情况：\n\t1. 之前没有投过票，现在投反对票    --> 更新分数和投票记录\n\t2. 之前投赞成票，现在改投反对票    --> 更新分数和投票记录\n\n投票的限制：\n每个贴子自发表之日起一个星期之内允许用户投票，超过一个星期就不允许再投票了。\n\t1. 到期之后将redis中保存的赞成票数及反对票数存储到mysql表中\n\t2. 到期之后删除那个 KeyPostVotedZSetPF\n*/\n\n// VoteForPost 为帖子投票的函数\nfunc VoteForPost(userID int64, p *models.ParamVoteData) error {\n\tzap.L().Debug(\"VoteForPost\",\n\t\tzap.Int64(\"userID\", userID),\n\t\tzap.String(\"postID\", p.PostID),\n\t\tzap.Int8(\"direction\", p.Direction))\n\treturn redis.VoteForPost(strconv.Itoa(int(userID)), p.PostID, float64(p.Direction))\n}\n"
  },
  {
    "path": "lesson80/bluebell/main.go",
    "content": "package main\n\nimport (\n\t\"bluebell/controller\"\n\t\"bluebell/dao/mysql\"\n\t\"bluebell/dao/redis\"\n\t\"bluebell/logger\"\n\t\"bluebell/pkg/snowflake\"\n\t\"bluebell/router\"\n\t\"bluebell/setting\"\n\t\"fmt\"\n\t\"os\"\n)\n\n// @title bluebell项目接口文档\n// @version 1.0\n// @description Go web开发进阶项目实战课程bluebell\n\n// @contact.name liwenzhou\n// @contact.url http://www.liwenzhou.com\n\n// @host 127.0.0.1:8084\n// @BasePath /api/v1\nfunc main() {\n\tif len(os.Args) < 2 {\n\t\tfmt.Println(\"need config file.eg: bluebell config.yaml\")\n\t\treturn\n\t}\n\t// 加载配置\n\tif err := setting.Init(os.Args[1]); err != nil {\n\t\tfmt.Printf(\"load config failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tif err := logger.Init(setting.Conf.LogConfig, setting.Conf.Mode); err != nil {\n\t\tfmt.Printf(\"init logger failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tif err := mysql.Init(setting.Conf.MySQLConfig); err != nil {\n\t\tfmt.Printf(\"init mysql failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer mysql.Close() // 程序退出关闭数据库连接\n\tif err := redis.Init(setting.Conf.RedisConfig); err != nil {\n\t\tfmt.Printf(\"init redis failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer redis.Close()\n\n\tif err := snowflake.Init(setting.Conf.StartTime, setting.Conf.MachineID); err != nil {\n\t\tfmt.Printf(\"init snowflake failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\t// 初始化gin框架内置的校验器使用的翻译器\n\tif err := controller.InitTrans(\"zh\"); err != nil {\n\t\tfmt.Printf(\"init validator trans failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\t// 注册路由\n\tr := router.SetupRouter(setting.Conf.Mode)\n\terr := r.Run(fmt.Sprintf(\":%d\", setting.Conf.Port))\n\tif err != nil {\n\t\tfmt.Printf(\"run server failed, err:%v\\n\", err)\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "lesson80/bluebell/middlewares/auth.go",
    "content": "package middlewares\n\nimport (\n\t\"bluebell/controller\"\n\t\"bluebell/pkg/jwt\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// JWTAuthMiddleware 基于JWT的认证中间件\nfunc JWTAuthMiddleware() func(c *gin.Context) {\n\treturn func(c *gin.Context) {\n\t\t// 客户端携带Token有三种方式 1.放在请求头 2.放在请求体 3.放在URI\n\t\t// 这里假设Token放在Header的Authorization中，并使用Bearer开头\n\t\t// Authorization: Bearer xxxxxxx.xxx.xxx  / X-TOKEN: xxx.xxx.xx\n\t\t// 这里的具体实现方式要依据你的实际业务情况决定\n\t\tauthHeader := c.Request.Header.Get(\"Authorization\")\n\t\tif authHeader == \"\" {\n\t\t\tcontroller.ResponseError(c, controller.CodeNeedLogin)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// 按空格分割\n\t\tparts := strings.SplitN(authHeader, \" \", 2)\n\t\tif !(len(parts) == 2 && parts[0] == \"Bearer\") {\n\t\t\tcontroller.ResponseError(c, controller.CodeInvalidToken)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// parts[1]是获取到的tokenString，我们使用之前定义好的解析JWT的函数来解析它\n\t\tmc, err := jwt.ParseToken(parts[1])\n\t\tif err != nil {\n\t\t\tcontroller.ResponseError(c, controller.CodeInvalidToken)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// 将当前请求的userID信息保存到请求的上下文c上\n\t\tc.Set(controller.CtxUserIDKey, mc.UserID)\n\n\t\tc.Next() // 后续的处理请求的函数中 可以用过c.Get(CtxUserIDKey) 来获取当前请求的用户信息\n\t}\n}\n"
  },
  {
    "path": "lesson80/bluebell/middlewares/ratelimit.go",
    "content": "package middlewares\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/juju/ratelimit\"\n)\n\nfunc RateLimitMiddleware(fillInterval time.Duration, cap int64) func(c *gin.Context) {\n\tbucket := ratelimit.NewBucket(fillInterval, cap)\n\treturn func(c *gin.Context) {\n\t\t// 如果取不到令牌就返回响应\n\t\t//if bucket.Take(1) > 0 {\n\t\tif bucket.TakeAvailable(1) != 1 {\n\t\t\tc.String(http.StatusOK, \"rate limit...\")\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\t// 取到令牌就放行\n\t\tc.Next()\n\t}\n}\n"
  },
  {
    "path": "lesson80/bluebell/models/community.go",
    "content": "package models\n\nimport \"time\"\n\ntype Community struct {\n\tID   int64  `json:\"id\" db:\"community_id\"`\n\tName string `json:\"name\" db:\"community_name\"`\n}\n\ntype CommunityDetail struct {\n\tID           int64     `json:\"id\" db:\"community_id\"`\n\tName         string    `json:\"name\" db:\"community_name\"`\n\tIntroduction string    `json:\"introduction,omitempty\" db:\"introduction\"`\n\tCreateTime   time.Time `json:\"create_time\" db:\"create_time\"`\n}\n"
  },
  {
    "path": "lesson80/bluebell/models/create_table.sql",
    "content": "--\nCREATE TABLE `user` (\n    `id` bigint(20) NOT NULL AUTO_INCREMENT,\n    `user_id` bigint(20) NOT NULL,\n    `username` varchar(64) COLLATE utf8mb4_general_ci NOT NULL,\n    `password` varchar(64) COLLATE utf8mb4_general_ci NOT NULL,\n    `email` varchar(64) COLLATE utf8mb4_general_ci,\n    `gender` tinyint(4) NOT NULL DEFAULT '0',\n    `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n    `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    PRIMARY KEY (`id`),\n    UNIQUE KEY `idx_username` (`username`) USING BTREE,\n    UNIQUE KEY `idx_user_id` (`user_id`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\n\n\nDROP TABLE IF EXISTS `community`;\nCREATE TABLE `community` (\n     `id` int(11) NOT NULL AUTO_INCREMENT,\n     `community_id` int(10) unsigned NOT NULL,\n     `community_name` varchar(128) COLLATE utf8mb4_general_ci NOT NULL,\n     `introduction` varchar(256) COLLATE utf8mb4_general_ci NOT NULL,\n     `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n     `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n     PRIMARY KEY (`id`),\n     UNIQUE KEY `idx_community_id` (`community_id`),\n     UNIQUE KEY `idx_community_name` (`community_name`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\n\n\nINSERT INTO `community` VALUES ('1', '1', 'Go', 'Golang', '2016-11-01 08:10:10', '2016-11-01 08:10:10');\nINSERT INTO `community` VALUES ('2', '2', 'leetcode', '刷题刷题刷题', '2020-01-01 08:00:00', '2020-01-01 08:00:00');\nINSERT INTO `community` VALUES ('3', '3', 'CS:GO', 'Rush B。。。', '2018-08-07 08:30:00', '2018-08-07 08:30:00');\nINSERT INTO `community` VALUES ('4', '4', 'LOL', '欢迎来到英雄联盟!', '2016-01-01 08:00:00', '2016-01-01 08:00:00');\n\nDROP TABLE IF EXISTS `post`;\nCREATE TABLE `post` (\n    `id` bigint(20) NOT NULL AUTO_INCREMENT,\n    `post_id` bigint(20) NOT NULL COMMENT '帖子id',\n    `title` varchar(128) COLLATE utf8mb4_general_ci NOT NULL COMMENT '标题',\n    `content` varchar(8192) COLLATE utf8mb4_general_ci NOT NULL COMMENT '内容',\n    `author_id` bigint(20) NOT NULL COMMENT '作者的用户id',\n    `community_id` bigint(20) NOT NULL COMMENT '所属社区',\n    `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '帖子状态',\n    `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n    `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n    PRIMARY KEY (`id`),\n    UNIQUE KEY `idx_post_id` (`post_id`),\n    KEY `idx_author_id` (`author_id`),\n    KEY `idx_community_id` (`community_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;"
  },
  {
    "path": "lesson80/bluebell/models/params.go",
    "content": "package models\n\n// 定义请求的参数结构体\n\nconst (\n\tOrderTime  = \"time\"\n\tOrderScore = \"score\"\n)\n\n// ParamSignUp 注册请求参数\ntype ParamSignUp struct {\n\tUsername   string `json:\"username\" binding:\"required\"`\n\tPassword   string `json:\"password\" binding:\"required\"`\n\tRePassword string `json:\"confirm_password\" binding:\"required,eqfield=Password\"`\n}\n\n// ParamLogin 登录请求参数\ntype ParamLogin struct {\n\tUsername string `json:\"username\" binding:\"required\"`\n\tPassword string `json:\"password\" binding:\"required\"`\n}\n\n// ParamVoteData 投票数据\ntype ParamVoteData struct {\n\t// UserID 从请求中获取当前的用户\n\tPostID    string `json:\"post_id\" binding:\"required\"`               // 贴子id\n\tDirection int8   `json:\"direction,string\" binding:\"oneof=1 0 -1\" ` // 赞成票(1)还是反对票(-1)取消投票(0)\n}\n\n// ParamPostList 获取帖子列表query string参数\ntype ParamPostList struct {\n\tCommunityID int64  `json:\"community_id\" form:\"community_id\"`   // 可以为空\n\tPage        int64  `json:\"page\" form:\"page\" example:\"1\"`       // 页码\n\tSize        int64  `json:\"size\" form:\"size\" example:\"10\"`      // 每页数据量\n\tOrder       string `json:\"order\" form:\"order\" example:\"score\"` // 排序依据\n}\n"
  },
  {
    "path": "lesson80/bluebell/models/post.go",
    "content": "package models\n\nimport \"time\"\n\n// 内存对齐概念\n\ntype Post struct {\n\tID          int64     `json:\"id,string\" db:\"post_id\"`                            // 帖子id\n\tAuthorID    int64     `json:\"author_id\" db:\"author_id\"`                          // 作者id\n\tCommunityID int64     `json:\"community_id\" db:\"community_id\" binding:\"required\"` // 社区id\n\tStatus      int32     `json:\"status\" db:\"status\"`                                // 帖子状态\n\tTitle       string    `json:\"title\" db:\"title\" binding:\"required\"`               // 帖子标题\n\tContent     string    `json:\"content\" db:\"content\" binding:\"required\"`           // 帖子内容\n\tCreateTime  time.Time `json:\"create_time\" db:\"create_time\"`                      // 帖子创建时间\n}\n\n// ApiPostDetail 帖子详情接口的结构体\ntype ApiPostDetail struct {\n\tAuthorName       string             `json:\"author_name\"` // 作者\n\tVoteNum          int64              `json:\"vote_num\"`    // 投票数\n\t*Post                               // 嵌入帖子结构体\n\t*CommunityDetail `json:\"community\"` // 嵌入社区信息\n}\n"
  },
  {
    "path": "lesson80/bluebell/models/struct_test.go",
    "content": "package models\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"unsafe\"\n)\n\n// Go 内存对齐\n\ntype s1 struct {\n\ta int8   // 1\n\tb string // 3\n\tc int8   // 1\n}\n\ntype s2 struct {\n\ta int8\n\tc int8   // 1\n\tb string // 2\n}\n\nfunc TestStruct(t *testing.T) {\n\tv1 := s1{\n\t\ta: 1,\n\t\tb: \"七米\",\n\t\tc: 2,\n\t}\n\n\tv2 := s2{\n\t\ta: 1,\n\t\tb: \"七米\",\n\t\tc: 2,\n\t}\n\n\tfmt.Println(unsafe.Sizeof(v1), unsafe.Sizeof(v2))\n}\n"
  },
  {
    "path": "lesson80/bluebell/models/user.go",
    "content": "package models\n\ntype User struct {\n\tUserID   int64  `db:\"user_id\"`\n\tUsername string `db:\"username\"`\n\tPassword string `db:\"password\"`\n\tToken    string\n}\n"
  },
  {
    "path": "lesson80/bluebell/nginx.conf",
    "content": "worker_processes  1;\n\nevents {\n    worker_connections  1024;\n}\n\nhttp {\n    include       mime.types;\n    default_type  application/octet-stream;\n\n    sendfile        on;\n    keepalive_timeout  65;\n\n    server {\n        listen       80;\n        server_name  localhost;\n\n        access_log   /var/log/bluebell-access.log;\n        error_log    /var/log/bluebell-error.log;\n\n        location / {\n            proxy_pass                 http://127.0.0.1:8084;\n            proxy_redirect             off;\n            proxy_set_header           Host             $host;\n            proxy_set_header           X-Real-IP        $remote_addr;\n            proxy_set_header           X-Forwarded-For  $proxy_add_x_forwarded_for;\n        }\n    }\n}"
  },
  {
    "path": "lesson80/bluebell/nginx2.conf",
    "content": "worker_processes  1;\n\nevents {\n    worker_connections  1024;\n}\n\nhttp {\n    include       mime.types;\n    default_type  application/octet-stream;\n\n    sendfile        on;\n    keepalive_timeout  65;\n\n    server {\n        listen       80;\n        server_name  bluebell;\n\n        access_log   /var/log/bluebell-access.log;\n        error_log    /var/log/bluebell-error.log;\n\n\t\t# 静态文件请求\n        location ~ .*\\.(gif|jpg|jpeg|png|js|css|eot|ttf|woff|svg|otf)$ {\n            access_log off;\n            expires    1d;\n            root       /data/app/bluebell/static;\n        }\n\n        # index.html页面请求\n        # 因为是单页面应用这里使用 try_files 处理一下，避免刷新页面时出现404的问题\n        location / {\n            root /data/app/bluebell/templates;\n            index index.html;\n            try_files $uri $uri/ /index.html;\n        }\n\n\t\t# API请求\n        location /api/v1 {\n            proxy_pass                 http://127.0.0.1:8084;\n            proxy_redirect             off;\n            proxy_set_header           Host             $host;\n            proxy_set_header           X-Real-IP        $remote_addr;\n            proxy_set_header           X-Forwarded-For  $proxy_add_x_forwarded_for;\n        }\n    }\n}"
  },
  {
    "path": "lesson80/bluebell/pkg/jwt/jwt.go",
    "content": "package jwt\n\nimport (\n\t\"errors\"\n\t\"time\"\n\n\t\"github.com/spf13/viper\"\n\n\t\"github.com/dgrijalva/jwt-go\"\n)\n\nvar mySecret = []byte(\"夏天夏天悄悄过去\")\n\n// MyClaims 自定义声明结构体并内嵌jwt.StandardClaims\n// jwt包自带的jwt.StandardClaims只包含了官方字段\n// 我们这里需要额外记录一个username字段，所以要自定义结构体\n// 如果想要保存更多信息，都可以添加到这个结构体中\ntype MyClaims struct {\n\tUserID   int64  `json:\"user_id\"`\n\tUsername string `json:\"username\"`\n\tjwt.StandardClaims\n}\n\n// GenToken 生成JWT\nfunc GenToken(userID int64, username string) (string, error) {\n\t// 创建一个我们自己的声明的数据\n\tc := MyClaims{\n\t\tuserID,\n\t\t\"username\", // 自定义字段\n\t\tjwt.StandardClaims{\n\t\t\tExpiresAt: time.Now().Add(\n\t\t\t\ttime.Duration(viper.GetInt(\"auth.jwt_expire\")) * time.Hour).Unix(), // 过期时间\n\t\t\tIssuer: \"bluebell\", // 签发人\n\t\t},\n\t}\n\t// 使用指定的签名方法创建签名对象\n\ttoken := jwt.NewWithClaims(jwt.SigningMethodHS256, c)\n\t// 使用指定的secret签名并获得完整的编码后的字符串token\n\treturn token.SignedString(mySecret)\n}\n\n// ParseToken 解析JWT\nfunc ParseToken(tokenString string) (*MyClaims, error) {\n\t// 解析token\n\tvar mc = new(MyClaims)\n\ttoken, err := jwt.ParseWithClaims(tokenString, mc, func(token *jwt.Token) (i interface{}, err error) {\n\t\treturn mySecret, nil\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif token.Valid { // 校验token\n\t\treturn mc, nil\n\t}\n\treturn nil, errors.New(\"invalid token\")\n}\n"
  },
  {
    "path": "lesson80/bluebell/pkg/snowflake/snowflake.go",
    "content": "package snowflake\n\nimport (\n\t\"time\"\n\n\tsf \"github.com/bwmarrin/snowflake\"\n)\n\nvar node *sf.Node\n\nfunc Init(startTime string, machineID int64) (err error) {\n\tvar st time.Time\n\tst, err = time.Parse(\"2006-01-02\", startTime)\n\tif err != nil {\n\t\treturn\n\t}\n\tsf.Epoch = st.UnixNano() / 1000000\n\tnode, err = sf.NewNode(machineID)\n\treturn\n}\nfunc GenID() int64 {\n\treturn node.Generate().Int64()\n}\n"
  },
  {
    "path": "lesson80/bluebell/router/route.go",
    "content": "package router\n\nimport (\n\t\"bluebell/controller\"\n\t\"bluebell/logger\"\n\t\"bluebell/middlewares\"\n\t\"net/http\"\n\n\tginSwagger \"github.com/swaggo/gin-swagger\"\n\t\"github.com/swaggo/gin-swagger/swaggerFiles\"\n\n\t_ \"bluebell/docs\"\n\n\t\"github.com/gin-contrib/pprof\"\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc SetupRouter(mode string) *gin.Engine {\n\tif mode == gin.ReleaseMode {\n\t\tgin.SetMode(gin.ReleaseMode) // gin设置成发布模式\n\t}\n\tr := gin.New()\n\t//r.Use(logger.GinLogger(), logger.GinRecovery(true), middlewares.RateLimitMiddleware(2*time.Second, 1))\n\tr.Use(logger.GinLogger(), logger.GinRecovery(true))\n\n\tr.LoadHTMLFiles(\"./templates/index.html\")\n\tr.Static(\"/static\", \"./static\")\n\n\tr.GET(\"/\", func(c *gin.Context) {\n\t\tc.HTML(http.StatusOK, \"index.html\", nil)\n\t})\n\n\tr.GET(\"/ping\", func(c *gin.Context) {\n\t\tc.String(http.StatusOK, \"pong\")\n\t})\n\n\tr.GET(\"/swagger/*any\", ginSwagger.WrapHandler(swaggerFiles.Handler))\n\n\tv1 := r.Group(\"/api/v1\")\n\n\t// 注册\n\tv1.POST(\"/signup\", controller.SignUpHandler)\n\t// 登录\n\tv1.POST(\"/login\", controller.LoginHandler)\n\n\t// 根据时间或分数获取帖子列表\n\tv1.GET(\"/posts2\", controller.GetPostListHandler2)\n\tv1.GET(\"/posts\", controller.GetPostListHandler)\n\tv1.GET(\"/community\", controller.CommunityHandler)\n\tv1.GET(\"/community/:id\", controller.CommunityDetailHandler)\n\tv1.GET(\"/post/:id\", controller.GetPostDetailHandler)\n\n\tv1.Use(middlewares.JWTAuthMiddleware()) // 应用JWT认证中间件\n\n\t{\n\t\tv1.POST(\"/post\", controller.CreatePostHandler)\n\n\t\t// 投票\n\t\tv1.POST(\"/vote\", controller.PostVoteController)\n\t}\n\n\tpprof.Register(r) // 注册pprof相关路由\n\n\tr.NoRoute(func(c *gin.Context) {\n\t\tc.JSON(http.StatusOK, gin.H{\n\t\t\t\"msg\": \"404\",\n\t\t})\n\t})\n\treturn r\n}\n"
  },
  {
    "path": "lesson80/bluebell/setting/setting.go",
    "content": "package setting\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\t\"github.com/spf13/viper\"\n)\n\nvar Conf = new(AppConfig)\n\ntype AppConfig struct {\n\tName      string `mapstructure:\"name\"`\n\tMode      string `mapstructure:\"mode\"`\n\tVersion   string `mapstructure:\"version\"`\n\tStartTime string `mapstructure:\"start_time\"`\n\tMachineID int64  `mapstructure:\"machine_id\"`\n\tPort      int    `mapstructure:\"port\"`\n\n\t*LogConfig   `mapstructure:\"log\"`\n\t*MySQLConfig `mapstructure:\"mysql\"`\n\t*RedisConfig `mapstructure:\"redis\"`\n}\n\ntype MySQLConfig struct {\n\tHost         string `mapstructure:\"host\"`\n\tUser         string `mapstructure:\"user\"`\n\tPassword     string `mapstructure:\"password\"`\n\tDB           string `mapstructure:\"dbname\"`\n\tPort         int    `mapstructure:\"port\"`\n\tMaxOpenConns int    `mapstructure:\"max_open_conns\"`\n\tMaxIdleConns int    `mapstructure:\"max_idle_conns\"`\n}\n\ntype RedisConfig struct {\n\tHost         string `mapstructure:\"host\"`\n\tPassword     string `mapstructure:\"password\"`\n\tPort         int    `mapstructure:\"port\"`\n\tDB           int    `mapstructure:\"db\"`\n\tPoolSize     int    `mapstructure:\"pool_size\"`\n\tMinIdleConns int    `mapstructure:\"min_idle_conns\"`\n}\n\ntype LogConfig struct {\n\tLevel      string `mapstructure:\"level\"`\n\tFilename   string `mapstructure:\"filename\"`\n\tMaxSize    int    `mapstructure:\"max_size\"`\n\tMaxAge     int    `mapstructure:\"max_age\"`\n\tMaxBackups int    `mapstructure:\"max_backups\"`\n}\n\nfunc Init(filePath string) (err error) {\n\t// 方式1：直接指定配置文件路径（相对路径或者绝对路径）\n\t// 相对路径：相对执行的可执行文件的相对路径\n\t//viper.SetConfigFile(\"./conf/config.yaml\")\n\t// 绝对路径：系统中实际的文件路径\n\t//viper.SetConfigFile(\"/Users/liwenzhou/Desktop/bluebell/conf/config.yaml\")\n\n\t// 方式2：指定配置文件名和配置文件的位置，viper自行查找可用的配置文件\n\t// 配置文件名不需要带后缀\n\t// 配置文件位置可配置多个\n\t//viper.SetConfigName(\"config\") // 指定配置文件名（不带后缀）\n\t//viper.AddConfigPath(\".\") // 指定查找配置文件的路径（这里使用相对路径）\n\t//viper.AddConfigPath(\"./conf\")      // 指定查找配置文件的路径（这里使用相对路径）\n\n\t// 基本上是配合远程配置中心使用的，告诉viper当前的数据使用什么格式去解析\n\t//viper.SetConfigType(\"json\")\n\n\tviper.SetConfigFile(filePath)\n\n\terr = viper.ReadInConfig() // 读取配置信息\n\tif err != nil {\n\t\t// 读取配置信息失败\n\t\tfmt.Printf(\"viper.ReadInConfig failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\n\t// 把读取到的配置信息反序列化到 Conf 变量中\n\tif err := viper.Unmarshal(Conf); err != nil {\n\t\tfmt.Printf(\"viper.Unmarshal failed, err:%v\\n\", err)\n\t}\n\n\tviper.WatchConfig()\n\tviper.OnConfigChange(func(in fsnotify.Event) {\n\t\tfmt.Println(\"配置文件修改了...\")\n\t\tif err := viper.Unmarshal(Conf); err != nil {\n\t\t\tfmt.Printf(\"viper.Unmarshal failed, err:%v\\n\", err)\n\t\t}\n\t})\n\treturn\n}\n"
  },
  {
    "path": "lesson80/bluebell/sql/bluebell_community.sql",
    "content": "create table community\n(\n    id             int auto_increment\n        primary key,\n    community_id   int unsigned                        not null,\n    community_name varchar(128)                        not null,\n    introduction   varchar(256)                        not null,\n    create_time    timestamp default CURRENT_TIMESTAMP not null,\n    update_time    timestamp default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP,\n    constraint idx_community_id\n        unique (community_id),\n    constraint idx_community_name\n        unique (community_name)\n)\n    collate = utf8mb4_general_ci;\n\nINSERT INTO bluebell.community (id, community_id, community_name, introduction, create_time, update_time) VALUES (1, 1, 'Go', 'Golang', '2016-11-01 08:10:10', '2016-11-01 08:10:10');\nINSERT INTO bluebell.community (id, community_id, community_name, introduction, create_time, update_time) VALUES (2, 2, 'leetcode', '刷题刷题刷题', '2020-01-01 08:00:00', '2020-01-01 08:00:00');\nINSERT INTO bluebell.community (id, community_id, community_name, introduction, create_time, update_time) VALUES (3, 3, 'CS:GO', 'Rush B。。。', '2018-08-07 08:30:00', '2018-08-07 08:30:00');\nINSERT INTO bluebell.community (id, community_id, community_name, introduction, create_time, update_time) VALUES (4, 4, 'LOL', '欢迎来到英雄联盟!', '2016-01-01 08:00:00', '2016-01-01 08:00:00');"
  },
  {
    "path": "lesson80/bluebell/sql/bluebell_post.sql",
    "content": "create table post\n(\n    id           bigint auto_increment\n        primary key,\n    post_id      bigint                              not null comment '帖子id',\n    title        varchar(128)                        not null comment '标题',\n    content      varchar(8192)                       not null comment '内容',\n    author_id    bigint                              not null comment '作者的用户id',\n    community_id bigint                              not null comment '所属社区',\n    status       tinyint   default 1                 not null comment '帖子状态',\n    create_time  timestamp default CURRENT_TIMESTAMP null comment '创建时间',\n    update_time  timestamp default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP comment '更新时间',\n    constraint idx_post_id\n        unique (post_id)\n)\n    collate = utf8mb4_general_ci;\n\ncreate index idx_author_id\n    on post (author_id);\n\ncreate index idx_community_id\n    on post (community_id);\n\nINSERT INTO bluebell.post (id, post_id, title, content, author_id, community_id, status, create_time, update_time) VALUES (1, 14283784123846656, '学习使我快乐', '只有学习才能变得更强', 28018727488323585, 1, 1, '2020-08-09 09:58:39', '2020-08-09 09:58:39');\nINSERT INTO bluebell.post (id, post_id, title, content, author_id, community_id, status, create_time, update_time) VALUES (2, 14373128436191232, 'CSGO开箱子好上瘾', '花了钱不出金，我好气啊', 28018727488323585, 2, 1, '2020-08-09 15:53:40', '2020-08-09 15:53:40');\nINSERT INTO bluebell.post (id, post_id, title, content, author_id, community_id, status, create_time, update_time) VALUES (3, 14373246019309568, 'IG牛逼', '打得好啊。。。', 28018727488323585, 3, 1, '2020-08-09 15:54:08', '2020-08-09 15:54:08');\nINSERT INTO bluebell.post (id, post_id, title, content, author_id, community_id, status, create_time, update_time) VALUES (4, 19432670719119360, '投票功能真好玩', '12345', 28018727488323585, 2, 1, '2020-08-23 14:58:29', '2020-08-23 14:58:29');\nINSERT INTO bluebell.post (id, post_id, title, content, author_id, community_id, status, create_time, update_time) VALUES (5, 19433711036534784, '投票功能真好玩2', '12345', 28018727488323585, 2, 1, '2020-08-23 15:02:37', '2020-08-23 15:02:37');\nINSERT INTO bluebell.post (id, post_id, title, content, author_id, community_id, status, create_time, update_time) VALUES (6, 19434165682311168, '投票功能真好玩2', '12345', 28018727488323585, 2, 1, '2020-08-23 15:04:26', '2020-08-23 15:04:26');\nINSERT INTO bluebell.post (id, post_id, title, content, author_id, community_id, status, create_time, update_time) VALUES (7, 21810561880690688, '看图说话', '4321', 28018727488323585, 2, 1, '2020-08-30 04:27:23', '2020-08-30 04:27:23');\nINSERT INTO bluebell.post (id, post_id, title, content, author_id, community_id, status, create_time, update_time) VALUES (8, 21810685746876416, '永远不要高估自己', '做个普通人也挺难', 28018727488323585, 3, 1, '2020-08-30 04:27:52', '2020-08-30 04:27:52');\nINSERT INTO bluebell.post (id, post_id, title, content, author_id, community_id, status, create_time, update_time) VALUES (9, 21810865955147776, '你知道泛型是什么吗？', '不知道泛型是什么却一直在问泛型什么时候出', 28018727488323585, 1, 1, '2020-08-30 04:28:35', '2020-08-30 04:28:35');\nINSERT INTO bluebell.post (id, post_id, title, content, author_id, community_id, status, create_time, update_time) VALUES (10, 21810938202034176, '国庆假期哪里玩？', '走遍四海，还是威海。', 28018727488323585, 1, 1, '2020-08-30 04:28:52', '2020-08-30 04:28:52');\nINSERT INTO bluebell.post (id, post_id, title, content, author_id, community_id, status, create_time, update_time) VALUES (11, 1, 'test', 'just for test', 1, 1, 1, '2020-09-12 14:03:18', '2020-09-12 14:03:18');\nINSERT INTO bluebell.post (id, post_id, title, content, author_id, community_id, status, create_time, update_time) VALUES (12, 92636388033302528, 'test', 'just a test', 1, 1, 1, '2020-09-12 15:03:56', '2020-09-12 15:03:56');\nINSERT INTO bluebell.post (id, post_id, title, content, author_id, community_id, status, create_time, update_time) VALUES (13, 92636388142354432, 'test', 'just a test', 1, 1, 1, '2020-09-12 15:03:56', '2020-09-12 15:03:56');\nINSERT INTO bluebell.post (id, post_id, title, content, author_id, community_id, status, create_time, update_time) VALUES (15, 123, 'test', 'just a test', 1, 1, 1, '2020-09-13 03:31:50', '2020-09-13 03:31:50');\nINSERT INTO bluebell.post (id, post_id, title, content, author_id, community_id, status, create_time, update_time) VALUES (16, 10, 'test', 'just a test', 123, 1, 1, '2020-09-13 04:12:44', '2020-09-13 04:12:44');"
  },
  {
    "path": "lesson80/bluebell/sql/bluebell_user.sql",
    "content": "create table user\n(\n    id          bigint auto_increment\n        primary key,\n    user_id     bigint                              not null,\n    username    varchar(64)                         not null,\n    password    varchar(64)                         not null,\n    email       varchar(64)                         null,\n    gender      tinyint   default 0                 not null,\n    create_time timestamp default CURRENT_TIMESTAMP null,\n    update_time timestamp default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP,\n    constraint idx_user_id\n        unique (user_id),\n    constraint idx_username\n        unique (username)\n)\n    collate = utf8mb4_general_ci;\n\nINSERT INTO bluebell.user (id, user_id, username, password, email, gender, create_time, update_time) VALUES (1, 28018727488323585, 'q1mi', '313233343536639a9119599647d841b1bef6ce5ea293', null, 0, '2020-07-12 07:01:03', '2020-07-12 07:01:03');\nINSERT INTO bluebell.user (id, user_id, username, password, email, gender, create_time, update_time) VALUES (2, 4183532125556736, '七米', '313233639a9119599647d841b1bef6ce5ea293', null, 0, '2020-07-12 13:03:51', '2020-07-12 13:03:51');"
  },
  {
    "path": "lesson80/bluebell/sql/init.sql",
    "content": "-- create the databases\nCREATE DATABASE IF NOT EXISTS bluebell;"
  },
  {
    "path": "lesson80/bluebell/static/css/app.0afe9dae.css",
    "content": ".header[data-v-75d7d08c]{width:100%;height:48px;position:fixed;background:#fff;display:flex;display:-webkit-flex;align-items:center;top:0;z-index:1}.header .logo[data-v-75d7d08c]{margin-left:10px;height:32px;background:url(../../static/img/logo.da56125f.png) no-repeat;background-size:32px 32px;background-position:0;padding-left:35px;line-height:32px;flex-grow:0;margin-right:16px;cursor:pointer}.header .search[data-v-75d7d08c]{flex-grow:1;margin:0 auto;max-width:690px;position:relative;display:flex;display:-webkit-flex}.header .search .s-logo[data-v-75d7d08c]{width:18px;height:18px;background:url(../../static/img/search.8e85063d.png) no-repeat;background-size:cover;display:inline-block;position:absolute;top:50%;margin-top:-9px;left:15px}.header .search .s-input[data-v-75d7d08c]{flex-grow:1;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#f6f7f8;border-radius:4px;border:1px solid #edeff1;box-shadow:none;color:#c1c1c1;display:block;height:36px;outline:none;padding:0 16px 0 40px;width:100%}.header .btns[data-v-75d7d08c]{flex-grow:0;margin-left:16px;margin-right:10px;display:flex;display:-webkit-flex;align-items:center}.header .btns .login-btn[data-v-75d7d08c]{border:1px solid transparent;border-radius:4px;box-sizing:border-box;text-align:center;letter-spacing:1px;text-decoration:none;font-size:12px;font-weight:700;letter-spacing:.5px;line-height:24px;text-transform:uppercase;padding:3px 16px;border-color:#0079d3;color:#0079d3;fill:#0079d3;display:inline-block;cursor:pointer}.header .btns .login-btn[data-v-75d7d08c]:first-child{margin-right:5px}.header .btns .login-btn[data-v-75d7d08c]:nth-child(2){margin-right:10px}.header .btns .user[data-v-75d7d08c]{width:auto;height:24px;background:url(../../static/img/avatar.7b0a9835.png) no-repeat;background-size:24px 24px;background-position:0;padding-left:28px;display:flex;display:-webkit-flex;align-items:center;cursor:pointer;padding:12px 12px 12px 28px}.header .btns .user[data-v-75d7d08c]:after{content:\"\";width:0;height:0;border-top:5px solid #878a8c;border-right:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid transparent;margin-top:5px;margin-left:10px}.header .btns .dropdown-content[data-v-75d7d08c]{display:none;position:absolute;background-color:#f9f9f9;min-width:160px;box-shadow:0 8px 16px 0 rgba(0,0,0,.2)}.header .btns .dropdown-content a[data-v-75d7d08c]{color:#000;padding:12px 16px;text-decoration:none;display:block;cursor:pointer}.header .btns .dropdown-content a[data-v-75d7d08c]:hover{background-color:#f1f1f1}.header .btns .user-box:hover .dropdown-content[data-v-75d7d08c]{display:block}@font-face{font-family:iconfont;src:url(data:application/vnd.ms-fontobject;base64,FAwAAGwLAAABAAIAAAAAAAIABQMAAAAAAAABAJABAAAAAExQAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAA/zftSwAAAAAAAAAAAAAAAAAAAAAAABAAaQBjAG8AbgBmAG8AbgB0AAAADgBSAGUAZwB1AGwAYQByAAAAFgBWAGUAcgBzAGkAbwBuACAAMQAuADAAAAAQAGkAYwBvAG4AZgBvAG4AdAAAAAAAAAEAAAALAIAAAwAwR1NVQrD+s+0AAAE4AAAAQk9TLzI8fEi5AAABfAAAAFZjbWFwlyE39gAAAfgAAAIKZ2x5ZhJIvHEAAAQYAAAEfGhlYWQZkfxxAAAA4AAAADZoaGVhCN8EiQAAALwAAAAkaG10eCYfAAAAAAHUAAAAJGxvY2EEZgVyAAAEBAAAABRtYXhwARgAXQAAARgAAAAgbmFtZT5U/n0AAAiUAAACbXBvc3Rl44HVAAALBAAAAGUAAQAAA4D/gABcBQAAAP//BQAAAQAAAAAAAAAAAAAAAAAAAAkAAQAAAAEAAEvtN/9fDzz1AAsEAAAAAADauVw6AAAAANq5XDoAAP+ABQADgAAAAAgAAgAAAAAAAAABAAAACQBRAAUAAAAAAAIAAAAKAAoAAAD/AAAAAAAAAAEAAAAKAB4ALAABREZMVAAIAAQAAAAAAAAAAQAAAAFsaWdhAAgAAAABAAAAAQAEAAQAAAABAAgAAQAGAAAAAQAAAAAAAQQ8AZAABQAIAokCzAAAAI8CiQLMAAAB6wAyAQgAAAIABQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUGZFZABA5gbmiwOA/4AAXAOAAIAAAAABAAAAAAAABAAAAASqAAAEAAAABAAAAAR1AAAEAAAABAAAAAQAAAAFAAAAAAAABQAAAAMAAAAsAAAABAAAAZoAAQAAAAAAlAADAAEAAAAsAAMACgAAAZoABABoAAAAEgAQAAMAAuYG5gjmIuYu5kXmSeaA5ov//wAA5gbmCOYi5i7mReZJ5oDmi///AAAAAAAAAAAAAAAAAAAAAAABABIAEgASABIAEgASABIAEgAAAAEAAgAIAAQABgAHAAUAAwAAAQYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAcAAAAAAAAAAIAADmBgAA5gYAAAABAADmCAAA5ggAAAACAADmIgAA5iIAAAAIAADmLgAA5i4AAAAEAADmRQAA5kUAAAAGAADmSQAA5kkAAAAHAADmgAAA5oAAAAAFAADmiwAA5osAAAADAAAAAAAAADIAVgDYATABhgGuAdYCPgABAAD/gASrA4AAIQAAJSMRNCYrASIGFREjESMiBhURIyIGHQEhNS4BKwERLgErAQMvbRgSgBMYbasSGIASGQSrARgSewEYEqpVAwERGRgS/P8BKxgT/wAZEaurEhgB1hIYAAAAAQAA/4AD7gOAABMAAAUzPwEnNy8BIycHIw8BFwcfATMXAni5OYMyMoM5uXh4uTmDMjKDObl4KbBfmppfsFdXsF+aml+wVwAABQAA/4cD+QOAABkAJAAoACsAUAAAJSMiLgI/ATUBPgEzMhYfAR4BFRQGBwE0BgE3NCYjJyYiDwEXARcBJwE3JyUyFhURDgEHIS4BJxE+ATchHgEUBiMhDgEVERQWFyEyNjURNDYBMwcGCwsGBTMB6gsfEBYjESsRDQsL/g5gAhcIAwUsBg8BM0n+HlABZlD+WF9CAlgQFAE/LfzxLT8CAj8tAfkQFBQQ/g4QFBQQAwgQFBRbAgsUC74IAfgLCxEMMxAjFhEeDP4ABhYCUgcGCTMGBixQ/qhQAW1Y/fkWUOoUEP4kMkIBAUIyAw8yQgEFFiITARoR/PgRGgEWFgHcEBQAAAAEAAD/gARyA4AAFwAgACwANQAAASEOAQcRHgEXMxceATI2PwEzPgE3ES4BATIWFAYiJjQ2BTQ+AR4CDgEjIiYFIiY0NjIWFAYDlv1KX34DA35fw2AKHCAbCmDEX34DA3v+RiEsLEIsLP7fGiwuIgkTJhghLAI2ISwsQiwtA4ADfl/+Vl9+An0MDg4MfQJ+XwGqXn7+qixCLCxCLE0YJhMJIi0tGSwsLEIsLEEtAAAAAAEAAP+fA6ADYwAzAAABJicmJyYnLgIGFRYCBwYWFxY2Jy4BNz4BNz4BFxYXFgYHBhY3PgE3LgEnJgYHDgEHBiYCvgExKEIlKgkhIAkD2S87hJcTBQ83KRUZUhgEFw5tQRcYZQ4cFZLaBAFALwsTAgMTIgwPAVFvZGRWKSMHGBkEEbr+71Gf1RkDCwwmekA7XGQQBwxlhjh4SQsQAhPLdCuQRQ4EES9OJw4GAAABAAD/pgOLA4AAFQAABQYiJwEmNjczET4BMyEyFhcRMx4BBwI2Fz4X/sIWESSqARgSAQASGAGqJBEWPxsbAX4bJQEB1RIZGRL+KwElGwAAAAEAAP+AA4sDWgAVAAABJiIHAQYWFzMRHgEzITI2NxEzPgEnAjYXPhf+whYRJKoBGBIBABIYAaokERYDPxsb/oIbJQH+KxIZGRIB1QElGwAABQAA/4AFAAOAABAAGwAoADQAPQAAEyEeARcRDgEHIS4BJxE+ATcRMSERJQEOAS8BBzU3PgEfAQE+AR8BESEFHgEXDgEHLgEnPgEXMjY0JiIGFBZnBDIsOgEBOiz7ziw6AQE6LAQy/vf+yQ0lEeXKqQ0gD94BPRAsEOb7zgEPPlECAlE+PVECAlE9EhcXIxgYA4ABNin8wCk2AQE2KQNAKTYB/GABG/3+mQ4ECpBYeEEJAQiMAW4PAQ7cAZxiAUw5OksCAks6OUysFiEWFiEWAAAAEgDeAAEAAAAAAAAAFQAAAAEAAAAAAAEACAAVAAEAAAAAAAIABwAdAAEAAAAAAAMACAAkAAEAAAAAAAQACAAsAAEAAAAAAAUACwA0AAEAAAAAAAYACAA/AAEAAAAAAAoAKwBHAAEAAAAAAAsAEwByAAMAAQQJAAAAKgCFAAMAAQQJAAEAEACvAAMAAQQJAAIADgC/AAMAAQQJAAMAEADNAAMAAQQJAAQAEADdAAMAAQQJAAUAFgDtAAMAAQQJAAYAEAEDAAMAAQQJAAoAVgETAAMAAQQJAAsAJgFpCkNyZWF0ZWQgYnkgaWNvbmZvbnQKaWNvbmZvbnRSZWd1bGFyaWNvbmZvbnRpY29uZm9udFZlcnNpb24gMS4waWNvbmZvbnRHZW5lcmF0ZWQgYnkgc3ZnMnR0ZiBmcm9tIEZvbnRlbGxvIHByb2plY3QuaHR0cDovL2ZvbnRlbGxvLmNvbQAKAEMAcgBlAGEAdABlAGQAIABiAHkAIABpAGMAbwBuAGYAbwBuAHQACgBpAGMAbwBuAGYAbwBuAHQAUgBlAGcAdQBsAGEAcgBpAGMAbwBuAGYAbwBuAHQAaQBjAG8AbgBmAG8AbgB0AFYAZQByAHMAaQBvAG4AIAAxAC4AMABpAGMAbwBuAGYAbwBuAHQARwBlAG4AZQByAGEAdABlAGQAIABiAHkAIABzAHYAZwAyAHQAdABmACAAZgByAG8AbQAgAEYAbwBuAHQAZQBsAGwAbwAgAHAAcgBvAGoAZQBjAHQALgBoAHQAdABwADoALwAvAGYAbwBuAHQAZQBsAGwAbwAuAGMAbwBtAAAAAAIAAAAAAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQECAQMBBAEFAQYBBwEIAQkBCgADdG9wCnBvbHlnb25yZWQEZWRpdAdjb21tZW50A2hvdARkb3duAnVwBWltYWdlAAAAAAA=);src:url(data:application/vnd.ms-fontobject;base64,FAwAAGwLAAABAAIAAAAAAAIABQMAAAAAAAABAJABAAAAAExQAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAA/zftSwAAAAAAAAAAAAAAAAAAAAAAABAAaQBjAG8AbgBmAG8AbgB0AAAADgBSAGUAZwB1AGwAYQByAAAAFgBWAGUAcgBzAGkAbwBuACAAMQAuADAAAAAQAGkAYwBvAG4AZgBvAG4AdAAAAAAAAAEAAAALAIAAAwAwR1NVQrD+s+0AAAE4AAAAQk9TLzI8fEi5AAABfAAAAFZjbWFwlyE39gAAAfgAAAIKZ2x5ZhJIvHEAAAQYAAAEfGhlYWQZkfxxAAAA4AAAADZoaGVhCN8EiQAAALwAAAAkaG10eCYfAAAAAAHUAAAAJGxvY2EEZgVyAAAEBAAAABRtYXhwARgAXQAAARgAAAAgbmFtZT5U/n0AAAiUAAACbXBvc3Rl44HVAAALBAAAAGUAAQAAA4D/gABcBQAAAP//BQAAAQAAAAAAAAAAAAAAAAAAAAkAAQAAAAEAAEvtN/9fDzz1AAsEAAAAAADauVw6AAAAANq5XDoAAP+ABQADgAAAAAgAAgAAAAAAAAABAAAACQBRAAUAAAAAAAIAAAAKAAoAAAD/AAAAAAAAAAEAAAAKAB4ALAABREZMVAAIAAQAAAAAAAAAAQAAAAFsaWdhAAgAAAABAAAAAQAEAAQAAAABAAgAAQAGAAAAAQAAAAAAAQQ8AZAABQAIAokCzAAAAI8CiQLMAAAB6wAyAQgAAAIABQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUGZFZABA5gbmiwOA/4AAXAOAAIAAAAABAAAAAAAABAAAAASqAAAEAAAABAAAAAR1AAAEAAAABAAAAAQAAAAFAAAAAAAABQAAAAMAAAAsAAAABAAAAZoAAQAAAAAAlAADAAEAAAAsAAMACgAAAZoABABoAAAAEgAQAAMAAuYG5gjmIuYu5kXmSeaA5ov//wAA5gbmCOYi5i7mReZJ5oDmi///AAAAAAAAAAAAAAAAAAAAAAABABIAEgASABIAEgASABIAEgAAAAEAAgAIAAQABgAHAAUAAwAAAQYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAcAAAAAAAAAAIAADmBgAA5gYAAAABAADmCAAA5ggAAAACAADmIgAA5iIAAAAIAADmLgAA5i4AAAAEAADmRQAA5kUAAAAGAADmSQAA5kkAAAAHAADmgAAA5oAAAAAFAADmiwAA5osAAAADAAAAAAAAADIAVgDYATABhgGuAdYCPgABAAD/gASrA4AAIQAAJSMRNCYrASIGFREjESMiBhURIyIGHQEhNS4BKwERLgErAQMvbRgSgBMYbasSGIASGQSrARgSewEYEqpVAwERGRgS/P8BKxgT/wAZEaurEhgB1hIYAAAAAQAA/4AD7gOAABMAAAUzPwEnNy8BIycHIw8BFwcfATMXAni5OYMyMoM5uXh4uTmDMjKDObl4KbBfmppfsFdXsF+aml+wVwAABQAA/4cD+QOAABkAJAAoACsAUAAAJSMiLgI/ATUBPgEzMhYfAR4BFRQGBwE0BgE3NCYjJyYiDwEXARcBJwE3JyUyFhURDgEHIS4BJxE+ATchHgEUBiMhDgEVERQWFyEyNjURNDYBMwcGCwsGBTMB6gsfEBYjESsRDQsL/g5gAhcIAwUsBg8BM0n+HlABZlD+WF9CAlgQFAE/LfzxLT8CAj8tAfkQFBQQ/g4QFBQQAwgQFBRbAgsUC74IAfgLCxEMMxAjFhEeDP4ABhYCUgcGCTMGBixQ/qhQAW1Y/fkWUOoUEP4kMkIBAUIyAw8yQgEFFiITARoR/PgRGgEWFgHcEBQAAAAEAAD/gARyA4AAFwAgACwANQAAASEOAQcRHgEXMxceATI2PwEzPgE3ES4BATIWFAYiJjQ2BTQ+AR4CDgEjIiYFIiY0NjIWFAYDlv1KX34DA35fw2AKHCAbCmDEX34DA3v+RiEsLEIsLP7fGiwuIgkTJhghLAI2ISwsQiwtA4ADfl/+Vl9+An0MDg4MfQJ+XwGqXn7+qixCLCxCLE0YJhMJIi0tGSwsLEIsLEEtAAAAAAEAAP+fA6ADYwAzAAABJicmJyYnLgIGFRYCBwYWFxY2Jy4BNz4BNz4BFxYXFgYHBhY3PgE3LgEnJgYHDgEHBiYCvgExKEIlKgkhIAkD2S87hJcTBQ83KRUZUhgEFw5tQRcYZQ4cFZLaBAFALwsTAgMTIgwPAVFvZGRWKSMHGBkEEbr+71Gf1RkDCwwmekA7XGQQBwxlhjh4SQsQAhPLdCuQRQ4EES9OJw4GAAABAAD/pgOLA4AAFQAABQYiJwEmNjczET4BMyEyFhcRMx4BBwI2Fz4X/sIWESSqARgSAQASGAGqJBEWPxsbAX4bJQEB1RIZGRL+KwElGwAAAAEAAP+AA4sDWgAVAAABJiIHAQYWFzMRHgEzITI2NxEzPgEnAjYXPhf+whYRJKoBGBIBABIYAaokERYDPxsb/oIbJQH+KxIZGRIB1QElGwAABQAA/4AFAAOAABAAGwAoADQAPQAAEyEeARcRDgEHIS4BJxE+ATcRMSERJQEOAS8BBzU3PgEfAQE+AR8BESEFHgEXDgEHLgEnPgEXMjY0JiIGFBZnBDIsOgEBOiz7ziw6AQE6LAQy/vf+yQ0lEeXKqQ0gD94BPRAsEOb7zgEPPlECAlE+PVECAlE9EhcXIxgYA4ABNin8wCk2AQE2KQNAKTYB/GABG/3+mQ4ECpBYeEEJAQiMAW4PAQ7cAZxiAUw5OksCAks6OUysFiEWFiEWAAAAEgDeAAEAAAAAAAAAFQAAAAEAAAAAAAEACAAVAAEAAAAAAAIABwAdAAEAAAAAAAMACAAkAAEAAAAAAAQACAAsAAEAAAAAAAUACwA0AAEAAAAAAAYACAA/AAEAAAAAAAoAKwBHAAEAAAAAAAsAEwByAAMAAQQJAAAAKgCFAAMAAQQJAAEAEACvAAMAAQQJAAIADgC/AAMAAQQJAAMAEADNAAMAAQQJAAQAEADdAAMAAQQJAAUAFgDtAAMAAQQJAAYAEAEDAAMAAQQJAAoAVgETAAMAAQQJAAsAJgFpCkNyZWF0ZWQgYnkgaWNvbmZvbnQKaWNvbmZvbnRSZWd1bGFyaWNvbmZvbnRpY29uZm9udFZlcnNpb24gMS4waWNvbmZvbnRHZW5lcmF0ZWQgYnkgc3ZnMnR0ZiBmcm9tIEZvbnRlbGxvIHByb2plY3QuaHR0cDovL2ZvbnRlbGxvLmNvbQAKAEMAcgBlAGEAdABlAGQAIABiAHkAIABpAGMAbwBuAGYAbwBuAHQACgBpAGMAbwBuAGYAbwBuAHQAUgBlAGcAdQBsAGEAcgBpAGMAbwBuAGYAbwBuAHQAaQBjAG8AbgBmAG8AbgB0AFYAZQByAHMAaQBvAG4AIAAxAC4AMABpAGMAbwBuAGYAbwBuAHQARwBlAG4AZQByAGEAdABlAGQAIABiAHkAIABzAHYAZwAyAHQAdABmACAAZgByAG8AbQAgAEYAbwBuAHQAZQBsAGwAbwAgAHAAcgBvAGoAZQBjAHQALgBoAHQAdABwADoALwAvAGYAbwBuAHQAZQBsAGwAbwAuAGMAbwBtAAAAAAIAAAAAAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQECAQMBBAEFAQYBBwEIAQkBCgADdG9wCnBvbHlnb25yZWQEZWRpdAdjb21tZW50A2hvdARkb3duAnVwBWltYWdlAAAAAAA=#iefix) format(\"embedded-opentype\"),url(\"data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAXwAAsAAAAAC2wAAAWiAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCECgqIfIcWATYCJAMkCxQABCAFhG0HZRunCSMRdntwMpD9RYJ5wtTNaA/lYPNf05ILYQVbigOMr5PFFM7087iZ7+dDCEklBL/zmipy4tWZVixsDpsjVTs4E4G2E1cCgOf/vd23/TOmiQ10WCYBRpBJJ6zNH54B7zvXH7BR+ShHcvaTRztMu2L6shz9sZ/LsyYWir4+6cXs/Zlp+2m3tLyEXecISUSjaIRQIVIpsaEibk8x2y7uwPUAAaAtEwbNzzINUATYbggA2qZpM1AON0EED6FsZMCxlZbXQIhqLxLXAOBV7efRCzGhAAjCDuxGVY359ZD1OcmEeTcP6zAPfHFBADifBIABhAEQAHQFhs3YKIfB6OfGAs0AwGFhRFG/S/ERIV8x/nOT2w1eMjYP4XgZACGG4l8eSUQIAzSArB4K8bCjQoHPSckJ6IkUnEAAfaCNxgg4QQDMhwWyYDGcIALycIIQaALCsqGJj+EASAFaC0APMGaYIIIOVoGmNvbvVh/ZIkaA/VViD3GcQiFhPLF5By0y2GnaOAskjY8WQk6CFPUHWDAKDzB6vY7L6GidhtZotF9Fy2jraKPV2q/UoybbDGM67J89NQR8r6ze4GxRGqx0jtUoK9PWa5t0zXrdQ6atQIw649gDcz6iiflET8tWfpb2MFd2AX6rXGJyIGxwdHg5nRrC5Sr23pYp04xSz8zP6/HcXL3pOH/MeMJwcpNh59H9x4e2HaIdlgduGt/AY3M6fv4TGdYjdjO4FjROl3bKprgxF7t3Xm+bkR+adjs15qnAQh53eE5nnVQNOtPM1gdmjSGN9p2JDB5wMJ6mrWqC2b89qMNuDKqf3R7PEH1bA57EsRgzB3eGdEj3315KDt6K4w7cWSYauZtIIineUm/eF6DToPwT2ibbQfnBMbf5vGvlqDPvwpzPxLzW5DDYjbP81pgftQFJWjle2sKhB0IdXIsUeO4FiYMVUQvXMYf17NASdDhp51Mbovu4SlEZgcMtOiL1fCBUa0TkmL22JOY49dUf9q9ZxQpm3PuMQrLd8ZObbOSK6ifJIOEerbcwtKjyI31lZcP9lfoZcoM8W13vKzaWfRdlZJ/yPiUHJd4S4/5//RrvE8P+/GOyhJIk/0ix8Yp32VhIruMPbCC6uogNdlF1WGcDOruxc+D4iu0DQuTcBhQ0tr6967B25dEcftyKMpVlz4LkpbBVqpVH40PvnzH7GhyvghhVF1iXrS164znRn09LiMMLbERVyvDTTqvKp+SbhqC2SmP1WmVdZuM799dnPdukbKkvevtk9uvcW1fwY3TmqxF5233ZCK5ccfwziM+4F39NetcaQZTdhVo54FqRyx4lWANPdZmf3XaP8TG7LFQWZdmlPeQoxCym/k1ovJ4r/vTVy/rW2axLP37xnintgf93/ffZO4MxXptvVZWq1FUlt09LDrXKumiag2tY2eD7xMon4BIBALh5/D0eCZ7Hf4lG8CiuC09jk9PMg1jzL9gOUj/1N5b2U8wvY8Hbh9fVlHLzQkieBQqxQRG+86sJAcDtFl4MzCWuR/gG0CgmT0u3M+gyRMSqbdpizTSfAfimxznvEyFP3A2AUOQWEESWAkbxowgYYSDEiAMSJRNooQpPZkg9ChgqMQCEeFYAhLUTCF5OAsa6ThEw3gMhhe+AxAYDTYek52Qc9hpzemPsBEfo72AGikr0yUnv/UZ99h3bDZrwH3k2OSizwl37wohcxAHzRVciChRTgE/hZug9QWKyOEg2iaQ6z1XQM2UDhcXJG2MnOIL+DsxAUSnz5eT5/m/UZ99xBOlT+R95NmsHpUyRgPJljYlImzI8X3RFqKiAOndTAJ8URM/LBFLwVhYHyUwZiqmW0zSV1JpVT4R5vQIAGnsQcQiEkQAJEYlEiEJiRMP6oHRK5O+aIuO4EYNG9n166YBR1hPJZqRrXJ3T1oRO42IBAAA=\") format(\"woff2\"),url(data:font/woff;base64,d09GRgABAAAAAAfwAAsAAAAAC2wAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADMAAABCsP6z7U9TLzIAAAE8AAAARAAAAFY8fEi5Y21hcAAAAYAAAACQAAACCpchN/ZnbHlmAAACEAAAA6UAAAR8Eki8cWhlYWQAAAW4AAAALwAAADYZkfxxaGhlYQAABegAAAAeAAAAJAjfBIlobXR4AAAGCAAAABkAAAAkJh8AAGxvY2EAAAYkAAAAFAAAABQEZgVybWF4cAAABjgAAAAfAAAAIAEYAF1uYW1lAAAGWAAAAUUAAAJtPlT+fXBvc3QAAAegAAAATQAAAGVl44HVeJxjYGRgYOBikGPQYWB0cfMJYeBgYGGAAJAMY05meiJQDMoDyrGAaQ4gZoOIAgCKIwNPAHicY2BksWGcwMDKwMHUyXSGgYGhH0IzvmYwYuRgYGBiYGVmwAoC0lxTGByesT3rZm7438AQw9zA0AAUZgTJAQD7aAyweJzlkTsOwjAQRJ/jYCxEgThCapRbpMhNKDlC+rRUHHCuEXa9NHxuwFjP0o681moH2AHZuBg9pAcJ193c1PzMofk9V6vPnMzpVFQ1aNSkWYvWbYNf3oeS9b8f9zqq/V7Y2zQ2Sypfff+nY7tvr6r6dgNPRTWwzaEhaG/GwJPUFOB9c2AbRkvgyWsNyE++qCzQeJx1UkFsG1UQ/fO//TfOOuv9Xnt3HRcnuxvvNnhtq7vOmqSK3VgCqbQgpxJtBAhHkMIpICEOEVUtiyI4lEPFAUSFVE4IEg69tocKARdA6i1IoApOcOgBxIlGTTbMuhVwQf/vzM5o5s2fmUeAkMNRaoeNiEVI1REttwY2LwpHOGNl80fAWmhADUQiWHPTzI80c3Mnb47ypdQOmPkL+G0/w0CUzPz+IdRM7ZCUxA5GwA95kxAyrsF+xxoaIemwC167CY4nOTkwpBkIDbp1Y/FSEFxavLH179/89cHVq4Pr58491JiKOO+yPcQpkTlylNRIP3mz3aBdWIAOhIE+AxUoFrgELQ7tlut4ro1F8HjQ9qqBXhQKSFYDPNGBtlWBAncsBYqioBtWEC2IVgShxGWZp0O4K8+ouiNqYkqWY2WdGhMs7fMchCfjSh9e7sdrgx5dUwvQre//We9S2q3DnlooqLGSSDaB8jkqF+RbE3BPlkU2VB1dVLIx4To9I/FMyLnfjz/rw+bawZ7ev4uZc0EPoBewHOq0bmswLfbviWnQdbijFnCWqfG+XscZGGSW+GQBx4sdSKICRmhUIIi6EGJvuC0I9AK33VaUbnWgQhVwbDed2ImffXDw5GDI2HDw9frkkdny5Po3iXkhfsLy/Z7vx79M+w07o7mm5dNo7KuzEYbHZwdDejGrKNmLdDiA7ReG8XaS0POfMl0tY9frJX+MsFInD3d/jX3CXiIhGq6XnAblRZ1KXDf0yGtAu5NcAy2OvsTC/bhcwq64S2/BsaO96qMZazbDfmwef/tDLZ1rzxdLZ8yUoWyuGOZ55Ujx/Z9ScKIpa5RpdjYHq69tbJyddySzlBI34z9Wr+2WmJx13zxx/PkNVcqef+exrZOySrXv36hdeVxJiebTnsIfvPVTdhlnW0SycdsDN2qHyJTQCnRDhBWQaGR0jPgrXcxtI+uBIMe354TeLZdhWK4C7OZLpXxcg2r5H95fZs8meODaEmDLIW4K8aK2wDV5/4PHEDB+CwHjWgIIu2PAhP+jNMH3qaSM/G+RZUI0ZLHxX1aLY5aoggJNkBZwmDMAiRBWGuMwDKNw2EHUcm1e0F9JBf4SwJJ///YDnQriv+Jvp6ri1+8+n5rN/QzLqq/+dv825DqrlK52lhO5nDcMxzTZCKL5/S/nI0DNTqDeX4fyQfyRkpq8sra1koGJ9+DVHCh34OMX4fTi0ilKTy0tnv5Ct3S8fwMKbd/oAAAAeJxjYGRgYABi77eS5+P5bb4ycLMwgMCtnTFWCPp/AysDcwOQy8HABBIFACtoCjQAeJxjYGRgYG7438AQw8rAwPD/P5AEiqAATgByEgRyAAB4nGNhYGBgWQXEDFBcisQGYlYgBgAgPQFFAAAAAAAAAAAyAFYA2AEwAYYBrgHWAj54nGNgZGBg4GQIZGBlAAEmIOYCQgaG/2A+AwARmQF2AHicZY9NTsMwEIVf+gekEqqoYIfkBWIBKP0Rq25YVGr3XXTfpk6bKokjx63UA3AejsAJOALcgDvwSCebNpbH37x5Y08A3OAHHo7fLfeRPVwyO3INF7gXrlN/EG6QX4SbaONVuEX9TdjHM6bCbXRheYPXuGL2hHdhDx18CNdwjU/hOvUv4Qb5W7iJO/wKt9Dx6sI+5l5XuI1HL/bHVi+cXqnlQcWhySKTOb+CmV7vkoWt0uqca1vEJlODoF9JU51pW91T7NdD5yIVWZOqCas6SYzKrdnq0AUb5/JRrxeJHoQm5Vhj/rbGAo5xBYUlDowxQhhkiMro6DtVZvSvsUPCXntWPc3ndFsU1P9zhQEC9M9cU7qy0nk6T4E9XxtSdXQrbsuelDSRXs1JErJCXta2VELqATZlV44RelzRiT8oZ0j/AAlabsgAAAB4nG3BSw6AIAwFwD4+ItzSSAMkQompMd7ehVtnyNAn0b8IAwsHjwUBKyISWZWZphxPkXFydpybhl1656G2iros9zDX9K1vhYletHgQlQAAAA==) format(\"woff\"),url(data:font/ttf;base64,AAEAAAALAIAAAwAwR1NVQrD+s+0AAAE4AAAAQk9TLzI8fEi5AAABfAAAAFZjbWFwlyE39gAAAfgAAAIKZ2x5ZhJIvHEAAAQYAAAEfGhlYWQZkfxxAAAA4AAAADZoaGVhCN8EiQAAALwAAAAkaG10eCYfAAAAAAHUAAAAJGxvY2EEZgVyAAAEBAAAABRtYXhwARgAXQAAARgAAAAgbmFtZT5U/n0AAAiUAAACbXBvc3Rl44HVAAALBAAAAGUAAQAAA4D/gABcBQAAAP//BQAAAQAAAAAAAAAAAAAAAAAAAAkAAQAAAAEAAEvtGc9fDzz1AAsEAAAAAADauVw6AAAAANq5XDoAAP+ABQADgAAAAAgAAgAAAAAAAAABAAAACQBRAAUAAAAAAAIAAAAKAAoAAAD/AAAAAAAAAAEAAAAKAB4ALAABREZMVAAIAAQAAAAAAAAAAQAAAAFsaWdhAAgAAAABAAAAAQAEAAQAAAABAAgAAQAGAAAAAQAAAAAAAQQ8AZAABQAIAokCzAAAAI8CiQLMAAAB6wAyAQgAAAIABQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUGZFZABA5gbmiwOA/4AAXAOAAIAAAAABAAAAAAAABAAAAASqAAAEAAAABAAAAAR1AAAEAAAABAAAAAQAAAAFAAAAAAAABQAAAAMAAAAsAAAABAAAAZoAAQAAAAAAlAADAAEAAAAsAAMACgAAAZoABABoAAAAEgAQAAMAAuYG5gjmIuYu5kXmSeaA5ov//wAA5gbmCOYi5i7mReZJ5oDmi///AAAAAAAAAAAAAAAAAAAAAAABABIAEgASABIAEgASABIAEgAAAAEAAgAIAAQABgAHAAUAAwAAAQYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAcAAAAAAAAAAIAADmBgAA5gYAAAABAADmCAAA5ggAAAACAADmIgAA5iIAAAAIAADmLgAA5i4AAAAEAADmRQAA5kUAAAAGAADmSQAA5kkAAAAHAADmgAAA5oAAAAAFAADmiwAA5osAAAADAAAAAAAAADIAVgDYATABhgGuAdYCPgABAAD/gASrA4AAIQAAJSMRNCYrASIGFREjESMiBhURIyIGHQEhNS4BKwERLgErAQMvbRgSgBMYbasSGIASGQSrARgSewEYEqpVAwERGRgS/P8BKxgT/wAZEaurEhgB1hIYAAAAAQAA/4AD7gOAABMAAAUzPwEnNy8BIycHIw8BFwcfATMXAni5OYMyMoM5uXh4uTmDMjKDObl4KbBfmppfsFdXsF+aml+wVwAABQAA/4cD+QOAABkAJAAoACsAUAAAJSMiLgI/ATUBPgEzMhYfAR4BFRQGBwE0BgE3NCYjJyYiDwEXARcBJwE3JyUyFhURDgEHIS4BJxE+ATchHgEUBiMhDgEVERQWFyEyNjURNDYBMwcGCwsGBTMB6gsfEBYjESsRDQsL/g5gAhcIAwUsBg8BM0n+HlABZlD+WF9CAlgQFAE/LfzxLT8CAj8tAfkQFBQQ/g4QFBQQAwgQFBRbAgsUC74IAfgLCxEMMxAjFhEeDP4ABhYCUgcGCTMGBixQ/qhQAW1Y/fkWUOoUEP4kMkIBAUIyAw8yQgEFFiITARoR/PgRGgEWFgHcEBQAAAAEAAD/gARyA4AAFwAgACwANQAAASEOAQcRHgEXMxceATI2PwEzPgE3ES4BATIWFAYiJjQ2BTQ+AR4CDgEjIiYFIiY0NjIWFAYDlv1KX34DA35fw2AKHCAbCmDEX34DA3v+RiEsLEIsLP7fGiwuIgkTJhghLAI2ISwsQiwtA4ADfl/+Vl9+An0MDg4MfQJ+XwGqXn7+qixCLCxCLE0YJhMJIi0tGSwsLEIsLEEtAAAAAAEAAP+fA6ADYwAzAAABJicmJyYnLgIGFRYCBwYWFxY2Jy4BNz4BNz4BFxYXFgYHBhY3PgE3LgEnJgYHDgEHBiYCvgExKEIlKgkhIAkD2S87hJcTBQ83KRUZUhgEFw5tQRcYZQ4cFZLaBAFALwsTAgMTIgwPAVFvZGRWKSMHGBkEEbr+71Gf1RkDCwwmekA7XGQQBwxlhjh4SQsQAhPLdCuQRQ4EES9OJw4GAAABAAD/pgOLA4AAFQAABQYiJwEmNjczET4BMyEyFhcRMx4BBwI2Fz4X/sIWESSqARgSAQASGAGqJBEWPxsbAX4bJQEB1RIZGRL+KwElGwAAAAEAAP+AA4sDWgAVAAABJiIHAQYWFzMRHgEzITI2NxEzPgEnAjYXPhf+whYRJKoBGBIBABIYAaokERYDPxsb/oIbJQH+KxIZGRIB1QElGwAABQAA/4AFAAOAABAAGwAoADQAPQAAEyEeARcRDgEHIS4BJxE+ATcRMSERJQEOAS8BBzU3PgEfAQE+AR8BESEFHgEXDgEHLgEnPgEXMjY0JiIGFBZnBDIsOgEBOiz7ziw6AQE6LAQy/vf+yQ0lEeXKqQ0gD94BPRAsEOb7zgEPPlECAlE+PVECAlE9EhcXIxgYA4ABNin8wCk2AQE2KQNAKTYB/GABG/3+mQ4ECpBYeEEJAQiMAW4PAQ7cAZxiAUw5OksCAks6OUysFiEWFiEWAAAAEgDeAAEAAAAAAAAAFQAAAAEAAAAAAAEACAAVAAEAAAAAAAIABwAdAAEAAAAAAAMACAAkAAEAAAAAAAQACAAsAAEAAAAAAAUACwA0AAEAAAAAAAYACAA/AAEAAAAAAAoAKwBHAAEAAAAAAAsAEwByAAMAAQQJAAAAKgCFAAMAAQQJAAEAEACvAAMAAQQJAAIADgC/AAMAAQQJAAMAEADNAAMAAQQJAAQAEADdAAMAAQQJAAUAFgDtAAMAAQQJAAYAEAEDAAMAAQQJAAoAVgETAAMAAQQJAAsAJgFpCkNyZWF0ZWQgYnkgaWNvbmZvbnQKaWNvbmZvbnRSZWd1bGFyaWNvbmZvbnRpY29uZm9udFZlcnNpb24gMS4waWNvbmZvbnRHZW5lcmF0ZWQgYnkgc3ZnMnR0ZiBmcm9tIEZvbnRlbGxvIHByb2plY3QuaHR0cDovL2ZvbnRlbGxvLmNvbQAKAEMAcgBlAGEAdABlAGQAIABiAHkAIABpAGMAbwBuAGYAbwBuAHQACgBpAGMAbwBuAGYAbwBuAHQAUgBlAGcAdQBsAGEAcgBpAGMAbwBuAGYAbwBuAHQAaQBjAG8AbgBmAG8AbgB0AFYAZQByAHMAaQBvAG4AIAAxAC4AMABpAGMAbwBuAGYAbwBuAHQARwBlAG4AZQByAGEAdABlAGQAIABiAHkAIABzAHYAZwAyAHQAdABmACAAZgByAG8AbQAgAEYAbwBuAHQAZQBsAGwAbwAgAHAAcgBvAGoAZQBjAHQALgBoAHQAdABwADoALwAvAGYAbwBuAHQAZQBsAGwAbwAuAGMAbwBtAAAAAAIAAAAAAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQECAQMBBAEFAQYBBwEIAQkBCgADdG9wCnBvbHlnb25yZWQEZWRpdAdjb21tZW50A2hvdARkb3duAnVwBWltYWdlAAAAAAA=) format(\"truetype\"),url(../../static/img/iconfont.cdbe38a0.svg#iconfont) format(\"svg\")}.iconfont{font-family:iconfont!important;font-size:16px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.icon-top:before{content:\"\\e606\"}.icon-polygonred:before{content:\"\\e608\"}.icon-edit:before{content:\"\\e68b\"}.icon-comment:before{content:\"\\e62e\"}.icon-hot:before{content:\"\\e680\"}.icon-down:before{content:\"\\e645\"}.icon-up:before{content:\"\\e649\"}.icon-image:before{content:\"\\e622\"}body,html{width:100%;height:100%;font-family:IBMPlexSans,Arial,sans-serif;background:#eee}a,abbr,acronym,address,applet,article,aside,audio,b,big,blockquote,body,button,canvas,caption,center,cite,code,dd,del,details,dfn,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,html,i,iframe,img,input,ins,kbd,label,legend,li,mark,menu,nav,object,ol,output,p,pre,q,ruby,s,samp,section,small,span,strike,strong,sub,summary,sup,table,tbody,td,tfoot,th,thead,time,tr,tt,u,ul,var,video{margin:0;padding:0;border:0;outline:0}a .page,abbr .page,acronym .page,address .page,applet .page,article .page,aside .page,audio .page,b .page,big .page,blockquote .page,body .page,button .page,canvas .page,caption .page,center .page,cite .page,code .page,dd .page,del .page,details .page,dfn .page,div .page,dl .page,dt .page,em .page,embed .page,fieldset .page,figcaption .page,figure .page,footer .page,form .page,h1 .page,h2 .page,h3 .page,h4 .page,h5 .page,h6 .page,header .page,hgroup .page,html .page,i .page,iframe .page,img .page,input .page,ins .page,kbd .page,label .page,legend .page,li .page,mark .page,menu .page,nav .page,object .page,ol .page,output .page,p .page,pre .page,q .page,ruby .page,s .page,samp .page,section .page,small .page,span .page,strike .page,strong .page,sub .page,summary .page,sup .page,table .page,tbody .page,td .page,tfoot .page,th .page,thead .page,time .page,tr .page,tt .page,u .page,ul .page,var .page,video .page{width:100%;height:auto}.content[data-v-2baf73fc]{max-width:100%;box-sizing:border-box;display:flex;flex-direction:row;justify-content:center;margin:48px auto 0;padding:20px 24px}.content .left[data-v-2baf73fc]{width:640px;padding-bottom:10px}.content .left .c-l-title[data-v-2baf73fc]{font-size:14px;font-weight:500;line-height:18px;color:#1a1a1b;text-transform:unset;padding-bottom:10px}.content .left .c-l-header[data-v-2baf73fc]{align-items:center;background-color:#fff;border:1px solid #ccc;border-radius:4px;box-sizing:border-box;display:flex;flex-flow:row nowrap;height:56px;justify-content:flex-start;margin-bottom:16px;padding:0 12px}.content .left .c-l-header .iconfont[data-v-2baf73fc]{margin-right:4px}.content .left .c-l-header .btn-iconfont[data-v-2baf73fc]{display:flex;display:-webkit-flex}.content .left .c-l-header .active[data-v-2baf73fc]{background:#f6f7f8;color:#0079d3;fill:#0079d3;border-radius:20px;height:32px;line-height:32px;margin-right:8px;padding:0 10px}.content .left .c-l-header .new[data-v-2baf73fc]{font-size:14px;margin-right:18px}.content .left .c-l-header .top[data-v-2baf73fc]{font-size:14px}.content .left .c-l-header .btn-publish[data-v-2baf73fc]{width:64px;height:32px;line-height:32px;background-color:#54b351;color:#fff;border:1px solid transparent;border-radius:4px;box-sizing:border-box;text-align:center;margin-left:auto;cursor:pointer}.content .left .c-l-header .sort[data-v-2baf73fc]{margin-left:300px;display:flex;color:#0079d3;display:-webkit-flex;align-items:center}.content .left .c-l-header .sort .sort-triangle[data-v-2baf73fc]{width:0;height:0;border-top:5px solid #0079d3;border-right:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid transparent;margin-top:5px;margin-left:10px}.content .left .c-l-list .c-l-item[data-v-2baf73fc]{list-style:none;border-radius:4px;padding-left:40px;cursor:pointer;border:1px solid #ccc;margin-bottom:10px;background-color:hsla(0,0%,100%,.8);color:#878a8c;position:relative}.content .left .c-l-list .c-l-item .post[data-v-2baf73fc]{align-items:center;box-sizing:border-box;display:flex;flex-direction:column;height:100%;left:0;padding:8px 4px 8px 0;position:absolute;top:0;width:40px;border-left:4px solid transparent;background:#f8f9fa}.content .left .c-l-list .c-l-item .post .iconfont[data-v-2baf73fc]{margin-right:0}.content .left .c-l-list .c-l-item .post .down[data-v-2baf73fc]{transform:scaleY(-1)}.content .left .c-l-list .c-l-item .post .text[data-v-2baf73fc]{color:#1a1a1b;font-size:12px;font-weight:700;line-height:16px;pointer-events:none;word-break:normal}.content .left .c-l-list .c-l-item .l-container[data-v-2baf73fc]{padding:15px}.content .left .c-l-list .c-l-item .l-container .con-title[data-v-2baf73fc]{color:#000;font-size:18px;font-weight:500;line-height:22px;text-decoration:none;word-break:break-word}.content .left .c-l-list .c-l-item .l-container .con-memo[data-v-2baf73fc]{margin-top:10px;margin-bottom:10px}.content .left .c-l-list .c-l-item .l-container .con-cover[data-v-2baf73fc]{height:512px;width:100%;background:url(https://timgsa.baidu.com/timg?di=7e9061211c23e3ed9f0c4375bb3822dc&image=&imgtype=0&quality=80&sec=1585999647247&size=b9999_10000&src=http%3A%2F%2Fi1.hdslb.com%2Fbfs%2Farchive%2F04d8cda08e170f4a58c18c45a93c539375c22162.jpg) no-repeat;background-size:cover;margin-top:10px;margin-bottom:10px}.content .left .c-l-list .c-l-item .l-container .user-btn[data-v-2baf73fc]{font-size:14px;display:flex;display:-webkit-flex}.content .left .c-l-list .c-l-item .l-container .user-btn .btn-item[data-v-2baf73fc]{display:flex;display:-webkit-flex;margin-right:10px}.content .left .c-l-list .c-l-item .l-container .user-btn .btn-item .iconfont[data-v-2baf73fc]{margin-right:4px}.content .right[data-v-2baf73fc]{width:312px;margin-left:24px;margin-top:28px}.content .right .communities[data-v-2baf73fc]{background-color:#fff;color:#1a1a1b;border:1px solid #ccc;border-radius:4px;overflow:visible;word-wrap:break-word;margin-bottom:20px}.content .right .communities .r-c-title[data-v-2baf73fc]{background-image:linear-gradient(0deg,rgba(0,0,0,.3),transparent);background-color:#0079d3;height:80px;width:100%;color:#fff;font-size:20px;line-height:120px;padding-left:10px;box-sizing:border-box;text-align:center}.content .right .communities .r-c-content .r-c-item[data-v-2baf73fc]{align-items:center;display:flex;display:-webkit-flex;height:48px;padding:0 12px;border-bottom:thin solid #edeff1;font-size:14px}.content .right .communities .r-c-content .r-c-item .index[data-v-2baf73fc]{width:20px;color:#1c1c1c;font-size:14px;font-weight:500;line-height:18px}.content .right .communities .r-c-content .r-c-item .icon[data-v-2baf73fc]{width:32px;height:32px;background-image:url(../../static/img/avatar.7b0a9835.png);background-repeat:no-repeat;background-size:cover;margin-right:20px}.content .right .communities .r-c-content .r-c-item[data-v-2baf73fc]:last-child{border-bottom:none}.content .right .communities .view-all[data-v-2baf73fc]{background-color:#0079d3;border:1px solid transparent;border-radius:4px;box-sizing:border-box;text-align:center;letter-spacing:1px;text-decoration:none;font-size:12px;font-weight:700;letter-spacing:.5px;line-height:24px;text-transform:uppercase;padding:3px 0;width:280px;color:#fff;margin:20px 0 20px 16px}.content .right .r-trending[data-v-2baf73fc]{padding-top:16px;width:312px;background-color:#fff;color:#1a1a1b;fill:#1a1a1b;border:1px solid #ccc;border-radius:4px;overflow:visible;word-wrap:break-word}.content .right .r-trending .r-t-title[data-v-2baf73fc]{font-size:10px;font-weight:700;letter-spacing:.5px;line-height:12px;text-transform:uppercase;background-color:#fff;border-radius:3px 3px 0 0;color:#1a1a1b;display:flex;fill:#1a1a1b;padding:0 12px 12px}.content .right .r-trending .rank[data-v-2baf73fc]{padding:12px}.content .right .r-trending .rank .r-t-cell[data-v-2baf73fc]{display:flex;display:-webkit-flex;align-items:center;justify-content:space-between;margin-bottom:16px}.content .right .r-trending .rank .r-t-cell .r-t-cell-info[data-v-2baf73fc]{display:flex}.content .right .r-trending .rank .r-t-cell .avatar[data-v-2baf73fc]{width:32px;height:32px;background:url(../../static/img/avatar.7b0a9835.png) no-repeat;background-size:cover;margin-right:10px}.content .right .r-trending .rank .r-t-cell .info[data-v-2baf73fc]{margin-right:10px}.content .right .r-trending .rank .r-t-cell .info .info-title[data-v-2baf73fc]{font-size:12px;font-weight:500;line-height:16px;text-overflow:ellipsis;width:144px}.content .right .r-trending .rank .r-t-cell .info .info-num[data-v-2baf73fc]{font-size:12px;font-weight:400;line-height:16px;padding-bottom:4px}.content .right .r-trending .rank .r-t-cell .join-btn[data-v-2baf73fc]{width:106px;height:32px;line-height:32px;background-color:#0079d3;color:#fff;border:1px solid transparent;border-radius:4px;box-sizing:border-box;text-align:center}.content[data-v-4466eba1]{max-width:100%;box-sizing:border-box;display:flex;flex-direction:row;justify-content:center;margin:0 auto;padding:20px 24px;margin-top:48px}.content .left[data-v-4466eba1]{flex-grow:1;max-width:740px;border-radius:4px;word-break:break-word;background:#fff;border:#edeff1;flex:1;margin:32px;margin-right:12px;padding-bottom:30px;position:relative}.content .left .container[data-v-4466eba1]{width:100%;height:auto;position:relative}.content .left .container .post[data-v-4466eba1]{align-items:center;box-sizing:border-box;display:flex;flex-direction:column;height:100%;left:0;padding:8px 4px 8px 0;position:absolute;top:0;width:40px;border-left:4px solid transparent}.content .left .container .post .text[data-v-4466eba1]{color:#1a1a1b;font-size:12px;font-weight:700;line-height:16px;pointer-events:none;word-break:normal}.content .left .container .l-container[data-v-4466eba1]{padding:15px;margin-left:40px}.content .left .container .l-container .con-title[data-v-4466eba1]{color:#000;font-size:18px;font-weight:500;line-height:22px;text-decoration:none;word-break:break-word}.content .left .container .l-container .con-info[data-v-4466eba1]{margin:25px 0;padding:15px 0;border-bottom:1px solid grey}.content .left .container .l-container .con-cover[data-v-4466eba1]{height:512px;width:100%;background:url(https://timgsa.baidu.com/timg?di=7e9061211c23e3ed9f0c4375bb3822dc&image=&imgtype=0&quality=80&sec=1585999647247&size=b9999_10000&src=http%3A%2F%2Fi1.hdslb.com%2Fbfs%2Farchive%2F04d8cda08e170f4a58c18c45a93c539375c22162.jpg) no-repeat;background-size:cover;margin-top:10px;margin-bottom:10px}.content .left .container .l-container .user-btn[data-v-4466eba1]{font-size:12px;display:flex;display:-webkit-flex}.content .left .container .l-container .user-btn .btn-item[data-v-4466eba1]{display:flex;display:-webkit-flex;align-items:center;margin-right:10px}.content .left .container .l-container .user-btn .btn-item .iconfont[data-v-4466eba1]{margin-right:4px}.content .left .comment[data-v-4466eba1]{width:100%;height:auto;position:relative}.content .left .comment .c-left .line[data-v-4466eba1]{border-right:2px solid #edeff1;height:100%;position:absolute;left:20px}.content .left .comment .c-left .c-arrow[data-v-4466eba1]{display:flex;display:-webkit-flex;position:absolute;z-index:2;flex-direction:column;left:12px;background:#fff;padding-bottom:5px}.content .left .comment .c-right[data-v-4466eba1]{margin-left:40px;padding-right:10px}.content .left .comment .c-right .c-user-info[data-v-4466eba1]{margin-bottom:10px}.content .left .comment .c-right .c-user-info .name[data-v-4466eba1]{color:#1c1c1c;font-size:12px;font-weight:400;line-height:16px}.content .left .comment .c-right .c-user-info .num[data-v-4466eba1]{padding-left:4px;font-size:12px;font-weight:400;line-height:16px;color:#7c7c7c}.content .left .comment .c-right .c-content[data-v-4466eba1]{font-family:Noto Sans,Arial,sans-serif;font-size:14px;font-weight:400;line-height:21px;word-break:break-word;color:#1a1a1b}.content .right[data-v-4466eba1]{flex-grow:0;width:312px;margin-top:32px}.content .right .topic-info[data-v-4466eba1]{width:100%;cursor:pointer;background-color:#fff;color:#1a1a1b;border:1px solid #ccc;border-radius:4px;overflow:visible;word-wrap:break-word;padding-bottom:30px}.content .right .topic-info .t-header[data-v-4466eba1]{width:100%;height:34px;background:#0079d3}.content .right .topic-info .t-info[data-v-4466eba1]{padding:0 12px;display:flex;display:-webkit-flex;width:100%;height:54px;align-items:center}.content .right .topic-info .t-info .avatar[data-v-4466eba1]{width:54px;height:54px;background:url(../../static/img/avatar.7b0a9835.png) no-repeat;background-size:cover;margin-right:10px}.content .right .topic-info .t-info .topic-name[data-v-4466eba1]{height:100%;line-height:54px;font-size:16px;font-weight:500}.content .right .topic-info .t-desc[data-v-4466eba1]{font-family:Noto Sans,Arial,sans-serif;font-size:14px;line-height:21px;font-weight:400;word-wrap:break-word;margin-bottom:8px;padding:0 12px}.content .right .topic-info .t-num[data-v-4466eba1]{padding:0 12px 20px 12px;display:flex;display:-webkit-flex;align-items:center;border-bottom:1px solid #edeff1}.content .right .topic-info .t-num .t-num-item[data-v-4466eba1]{list-style:none;display:flex;display:-webkit-flex;flex-direction:column;width:50%}.content .right .topic-info .t-num .t-num-item .number[data-v-4466eba1]{font-size:16px;font-weight:500;line-height:20px}.content .right .topic-info .t-num .t-num-item .unit[data-v-4466eba1]{font-size:12px;font-weight:500;line-height:16px;word-break:break-word}.content .right .topic-info .date[data-v-4466eba1]{font-family:Noto Sans,Arial,sans-serif;font-size:14px;font-weight:400;line-height:18px;margin-top:20px;padding:0 12px}.content .right .topic-info .topic-btn[data-v-4466eba1]{width:286px;height:34px;line-height:34px;color:#fff;margin:12px auto 0 auto;background:#003f6d;border-radius:4px;box-sizing:border-box;margin-left:13px}.content[data-v-6236a644]{max-width:100%;box-sizing:border-box;display:flex;flex-direction:row;justify-content:center;margin:0 auto;padding:20px 24px;margin-top:48px}.content .left[data-v-6236a644]{flex-grow:1;max-width:740px;word-break:break-word;flex:1;margin:32px;margin-right:12px;padding-bottom:30px;position:relative}.content .left .post-name[data-v-6236a644]{padding:4px;margin:16px 0;border-bottom:1px solid #edeff1;display:flex;justify-content:space-between}.content .left .post-name .p-btn[data-v-6236a644]{font-size:12px;font-weight:700;letter-spacing:.5px;line-height:24px;text-transform:uppercase;border:none;padding:0;margin-left:10px;color:#0079d3}.content .left .post-name .p-num[data-v-6236a644]{font-size:12px;font-weight:400;line-height:16px;background:#878a8c;border-radius:2px;color:#fff;margin-left:4px;padding:1px 3px}.content .left .post-type[data-v-6236a644]{box-sizing:border-box;width:300px;height:40px;border-radius:4px;transition:box-shadow .2s ease;box-shadow:0 0 0 0 #fff;border:1px solid #edeff1;background-color:#fff;padding-left:10px;position:relative}.content .left .post-type .post-type-value[data-v-6236a644]{font-size:14px;font-weight:500;line-height:40px;width:100%;vertical-align:middle;color:#1c1c1c;background-color:transparent;cursor:pointer}.content .left .post-type .post-type-options[data-v-6236a644]{position:absolute;width:100%;background-color:#fff;left:0;z-index:1;border-radius:4px}.content .left .post-type .post-type-options .post-type-cell[data-v-6236a644]{margin:14px 8px 5px;font-size:14px;list-style:none;border-bottom:1px solid #edeff1;padding-bottom:8px;color:#1c1c1c;cursor:pointer}.content .left .post-type .p-icon[data-v-6236a644]{width:0;height:0;border-top:5px solid #878a8c;border-right:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid transparent;margin-left:10px;position:absolute;top:50%;right:10px;cursor:pointer}.content .left .post-content[data-v-6236a644]{background-color:#fff;margin:10px 0;padding-bottom:15px;border-radius:5px}.content .left .post-content .cat[data-v-6236a644]{display:flex;display:-webkit-flex;justify-content:space-between;align-items:center;width:100%;height:53px}.content .left .post-content .cat .cat-item[data-v-6236a644]{padding:10px 0;width:50%;height:40px;line-height:40px;text-align:center;list-style:none;border-bottom:1px solid #edeff1;border-right:1px solid #edeff1;color:#878a8c}.content .left .post-content .cat .cat-item .iconfont[data-v-6236a644]{margin-right:4px}.content .left .post-content .cat .active[data-v-6236a644]{color:#0079d3;font-weight:bolder;background:none}.content .left .post-content .post-sub-container[data-v-6236a644]{padding:16px}.content .left .post-content .post-sub-container .post-sub-header[data-v-6236a644]{position:relative}.content .left .post-content .post-sub-container .post-sub-header .post-title[data-v-6236a644]{resize:none;box-sizing:border-box;overflow:hidden;display:block;width:100%;height:40px;padding:0 0 0 10px;outline:none;border:1px solid #edeff1;border-radius:4px;color:#1c1c1c;font-size:14px;font-weight:400;line-height:40px}.content .left .post-content .post-sub-container .post-sub-header .textarea-num[data-v-6236a644]{font-size:10px;font-weight:700;letter-spacing:.5px;line-height:12px;text-transform:uppercase;bottom:12px;color:#878a8c;pointer-events:none;position:absolute;right:12px}.content .left .post-content .post-sub-container .post-text-con[data-v-6236a644]{width:100%;height:200px;border:1px solid #edeff1;margin-top:20px}.content .left .post-content .post-sub-container .post-text-con .post-content-t[data-v-6236a644]{resize:none;box-sizing:border-box;overflow:hidden;display:block;width:100%;height:200px;padding:12px 8px;outline:none;border:1px solid #edeff1;border-radius:4px;color:#1c1c1c;font-size:14px;font-weight:400;line-height:21px}.content .left .post-content .post-footer[data-v-6236a644]{display:flex;display:-webkit-flex;margin:0 16px;justify-content:flex-end}.content .left .post-content .post-footer .sign[data-v-6236a644]{display:flex;display:-webkit-flex}.content .left .post-content .post-footer .sign .sign-item[data-v-6236a644]{list-style:none;padding:5px 8px;border:1px solid #edeff1;margin-right:10px;color:#878a8c;font-size:12px;font-weight:700}.content .left .post-content .post-footer .btns .btn[data-v-6236a644]{border:1px solid transparent;border-radius:4px;box-sizing:border-box;text-align:center;text-decoration:none;font-size:12px;font-weight:700;letter-spacing:.5px;line-height:24px;text-transform:uppercase;padding:3px 16px;background:#0079d3;color:#fff;margin-left:8px;cursor:pointer}.content .left .post-content .alias[data-v-6236a644]{background-color:#f6f7f8;border-radius:0 0 6px 6px;border-top:1px solid #edeff1;display:flex;flex-flow:column;padding:8px 16px 21px;position:relative}.content .left .post-content .alias .send-post[data-v-6236a644]{font-size:14px;font-weight:500;line-height:18px;color:#1c1c1c;margin-right:4px}.content .left .post-content .alias .connect[data-v-6236a644]{font-size:14px;font-weight:500;line-height:18px;color:#0079d3;display:block;margin-right:4px;margin-top:10px}.content .right[data-v-6236a644]{flex-grow:0;width:312px;margin-top:62px}.content .right .post-rank[data-v-6236a644]{background-color:#fff;border-radius:4px;margin-top:15px;padding:12px}.content .right .post-rank .p-r-title[data-v-6236a644]{display:flex;display:-webkit-flex;font-size:16px;font-weight:500;line-height:20px;align-items:center;border-bottom:1px solid #edeff1;color:#1c1c1c;padding-bottom:10px}.content .right .post-rank .p-r-title .p-r-icon[data-v-6236a644]{width:40px;height:40px;background:url(../../static/img/avatar.7b0a9835.png) no-repeat;background-size:cover;margin-right:10px}.content .right .post-rank .p-r-content[data-v-6236a644]{display:flex;display:-webkit-flex;flex-direction:column}.content .right .post-rank .p-r-content .p-r-item[data-v-6236a644]{list-style:none;border-bottom:1px solid #edeff1;color:#1c1c1c;padding:10px 5px}.main[data-v-5d78ac92]{background:#f8f8f8;padding:150px 0}.main .container[data-v-5d78ac92]{width:600px;background:#fff;margin:0 auto;max-width:1200px;padding:20px}.main .container .form-title[data-v-5d78ac92]{margin-bottom:33px;text-align:center}.main .container .form-group[data-v-5d78ac92]{margin:15px}.main .container .form-group label[data-v-5d78ac92]{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}.main .container .form-group .form-control[data-v-5d78ac92]{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px}.main .container .form-btn[data-v-5d78ac92]{display:flex;justify-content:center}.main .container .form-btn .btn[data-v-5d78ac92]{padding:6px 20px;font-size:18px;line-height:1.3333333;border-radius:6px;display:inline-block;margin-bottom:0;font-weight:400;text-align:center;white-space:nowrap;vertical-align:middle;touch-action:manipulation;cursor:pointer;border:1px solid transparent}.main .container .form-btn .btn-info[data-v-5d78ac92]{color:#fff;background-color:#5bc0de;border-color:#46b8da}.main[data-v-61f1d9ff]{background:#f8f8f8;padding:150px 0}.main .container[data-v-61f1d9ff]{width:600px;background:#fff;margin:0 auto;max-width:1200px;padding:20px}.main .container .form-title[data-v-61f1d9ff]{margin-bottom:33px;text-align:center}.main .container .form-group[data-v-61f1d9ff]{margin:15px}.main .container .form-group label[data-v-61f1d9ff]{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}.main .container .form-group .form-control[data-v-61f1d9ff]{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px}.main .container .form-btn[data-v-61f1d9ff]{display:flex;justify-content:center}.main .container .form-btn .btn[data-v-61f1d9ff]{padding:6px 20px;font-size:18px;line-height:1.3333333;border-radius:6px;display:inline-block;margin-bottom:0;font-weight:400;text-align:center;white-space:nowrap;vertical-align:middle;touch-action:manipulation;cursor:pointer;border:1px solid transparent}.main .container .form-btn .btn-info[data-v-61f1d9ff]{color:#fff;background-color:#5bc0de;border-color:#46b8da}"
  },
  {
    "path": "lesson80/bluebell/static/js/app.9f3efa6d.js",
    "content": "(function(t){function s(s){for(var n,o,c=s[0],r=s[1],l=s[2],m=0,p=[];m<c.length;m++)o=c[m],Object.prototype.hasOwnProperty.call(i,o)&&i[o]&&p.push(i[o][0]),i[o]=0;for(n in r)Object.prototype.hasOwnProperty.call(r,n)&&(t[n]=r[n]);u&&u(s);while(p.length)p.shift()();return a.push.apply(a,l||[]),e()}function e(){for(var t,s=0;s<a.length;s++){for(var e=a[s],n=!0,c=1;c<e.length;c++){var r=e[c];0!==i[r]&&(n=!1)}n&&(a.splice(s--,1),t=o(o.s=e[0]))}return t}var n={},i={app:0},a=[];function o(s){if(n[s])return n[s].exports;var e=n[s]={i:s,l:!1,exports:{}};return t[s].call(e.exports,e,e.exports,o),e.l=!0,e.exports}o.m=t,o.c=n,o.d=function(t,s,e){o.o(t,s)||Object.defineProperty(t,s,{enumerable:!0,get:e})},o.r=function(t){\"undefined\"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:\"Module\"}),Object.defineProperty(t,\"__esModule\",{value:!0})},o.t=function(t,s){if(1&s&&(t=o(t)),8&s)return t;if(4&s&&\"object\"===typeof t&&t&&t.__esModule)return t;var e=Object.create(null);if(o.r(e),Object.defineProperty(e,\"default\",{enumerable:!0,value:t}),2&s&&\"string\"!=typeof t)for(var n in t)o.d(e,n,function(s){return t[s]}.bind(null,n));return e},o.n=function(t){var s=t&&t.__esModule?function(){return t[\"default\"]}:function(){return t};return o.d(s,\"a\",s),s},o.o=function(t,s){return Object.prototype.hasOwnProperty.call(t,s)},o.p=\"/\";var c=window[\"webpackJsonp\"]=window[\"webpackJsonp\"]||[],r=c.push.bind(c);c.push=s,c=c.slice();for(var l=0;l<c.length;l++)s(c[l]);var u=r;a.push([0,\"chunk-vendors\"]),e()})({0:function(t,s,e){t.exports=e(\"56d7\")},\"0797\":function(t,s,e){\"use strict\";var n=e(\"e355\"),i=e.n(n);i.a},2395:function(t,s,e){},3315:function(t,s,e){},\"56d7\":function(t,s,e){\"use strict\";e.r(s);e(\"e260\"),e(\"e6cf\"),e(\"cca6\"),e(\"a79d\");var n=e(\"2b0e\"),i=function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"div\",{attrs:{id:\"app\"}},[e(\"div\",{staticClass:\"page\"},[e(\"HeadBar\"),e(\"router-view\")],1)])},a=[],o=function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"header\",{staticClass:\"header\"},[e(\"span\",{staticClass:\"logo\",on:{click:t.goIndex}},[t._v(\"bluebell\")]),t._m(0),e(\"div\",{staticClass:\"btns\"},[e(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:!t.isLogin,expression:\"!isLogin\"}]},[e(\"a\",{staticClass:\"login-btn\",on:{click:t.goLogin}},[t._v(\"登录\")]),e(\"a\",{staticClass:\"login-btn\",on:{click:t.goSignUp}},[t._v(\"注册\")])]),e(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:t.isLogin,expression:\"isLogin\"}],staticClass:\"user-box\"},[e(\"span\",{staticClass:\"user\"},[t._v(t._s(t.currUsername))]),e(\"div\",{staticClass:\"dropdown-content\"},[e(\"a\",{on:{click:t.goLogout}},[t._v(\"登出\")])])])])])},c=[function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"div\",{staticClass:\"search\"},[e(\"label\",{staticClass:\"s-logo\"}),e(\"input\",{staticClass:\"s-input\",attrs:{type:\"text\",placeholder:\"搜索\"}})])}],r={name:\"HeadBar\",created:function(){this.$store.commit(\"init\")},computed:{isLogin:function(){return this.$store.getters.isLogin},currUsername:function(){return console.log(this.$store.getters.username),this.$store.getters.username}},methods:{goIndex:function(){this.$router.push({name:\"Home\"})},goLogin:function(){this.$router.push({name:\"Login\"})},goSignUp:function(){this.$router.push({name:\"SignUp\"})},goLogout:function(){this.$store.commit(\"logout\")}}},l=r,u=(e(\"aee9\"),e(\"2877\")),m=Object(u[\"a\"])(l,o,c,!1,null,\"75d7d08c\",null),p=m.exports,d={components:{HeadBar:p}},v=d,f=(e(\"7c55\"),Object(u[\"a\"])(v,i,a,!1,null,null,null)),g=f.exports,h=e(\"8c4f\"),C=function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"div\",{staticClass:\"content\"},[e(\"div\",{staticClass:\"left\"},[e(\"div\",{staticClass:\"c-l-header\"},[e(\"div\",{staticClass:\"new btn-iconfont\",class:{active:t.timeOrder},on:{click:function(s){return t.selectOrder(\"time\")}}},[e(\"i\",{staticClass:\"iconfont icon-polygonred\"}),t._v(\"New \")]),e(\"div\",{staticClass:\"top btn-iconfont\",class:{active:t.scoreOrder},on:{click:function(s){return t.selectOrder(\"score\")}}},[e(\"i\",{staticClass:\"iconfont icon-top\"}),t._v(\"Score \")]),e(\"button\",{staticClass:\"btn-publish\",on:{click:t.goPublish}},[t._v(\"发表\")])]),e(\"ul\",{staticClass:\"c-l-list\"},t._l(t.postList,(function(s){return e(\"li\",{key:s.id,staticClass:\"c-l-item\"},[e(\"div\",{staticClass:\"post\"},[e(\"a\",{staticClass:\"vote\"},[e(\"span\",{staticClass:\"iconfont icon-up\",on:{click:function(e){return t.vote(s.id,\"1\")}}})]),e(\"span\",{staticClass:\"text\"},[t._v(t._s(s.vote_num))]),e(\"a\",{staticClass:\"vote\"},[e(\"span\",{staticClass:\"iconfont icon-down\",on:{click:function(e){return t.vote(s.id,\"-1\")}}})])]),e(\"div\",{staticClass:\"l-container\",on:{click:function(e){return t.goDetail(s.id)}}},[e(\"h4\",{staticClass:\"con-title\"},[t._v(t._s(s.title))]),e(\"div\",{staticClass:\"con-memo\"},[e(\"p\",[t._v(t._s(s.content))])])])])})),0)]),t._m(0)])},_=[function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"div\",{staticClass:\"right\"},[e(\"div\",{staticClass:\"communities\"},[e(\"h2\",{staticClass:\"r-c-title\"},[t._v(\"今日火热频道排行榜\")]),e(\"ul\",{staticClass:\"r-c-content\"},[e(\"li\",{staticClass:\"r-c-item\"},[e(\"span\",{staticClass:\"index\"},[t._v(\"1\")]),e(\"i\",{staticClass:\"icon\"}),t._v(\" b/coding \")]),e(\"li\",{staticClass:\"r-c-item\"},[e(\"span\",{staticClass:\"index\"},[t._v(\"2\")]),e(\"i\",{staticClass:\"icon\"}),t._v(\" b/tree_hole \")]),e(\"li\",{staticClass:\"r-c-item\"},[e(\"span\",{staticClass:\"index\"},[t._v(\"3\")]),e(\"i\",{staticClass:\"icon\"}),t._v(\" b/job \")])]),e(\"button\",{staticClass:\"view-all\"},[t._v(\"查看所有\")])]),e(\"div\",{staticClass:\"r-trending\"},[e(\"h2\",{staticClass:\"r-t-title\"},[t._v(\"持续热门频道\")]),e(\"ul\",{staticClass:\"rank\"},[e(\"li\",{staticClass:\"r-t-cell\"},[e(\"div\",{staticClass:\"r-t-cell-info\"},[e(\"div\",{staticClass:\"avatar\"}),e(\"div\",{staticClass:\"info\"},[e(\"span\",{staticClass:\"info-title\"},[t._v(\"b/Book\")]),e(\"p\",{staticClass:\"info-num\"},[t._v(\"7.1k members\")])])]),e(\"button\",{staticClass:\"join-btn\"},[t._v(\"JOIN\")])]),e(\"li\",{staticClass:\"r-t-cell\"},[e(\"div\",{staticClass:\"r-t-cell-info\"},[e(\"div\",{staticClass:\"avatar\"}),e(\"div\",{staticClass:\"info\"},[e(\"span\",{staticClass:\"info-title\"},[t._v(\"b/coding\")]),e(\"p\",{staticClass:\"info-num\"},[t._v(\"3.2k members\")])])]),e(\"button\",{staticClass:\"join-btn\"},[t._v(\"JOIN\")])]),e(\"li\",{staticClass:\"r-t-cell\"},[e(\"div\",{staticClass:\"r-t-cell-info\"},[e(\"div\",{staticClass:\"avatar\"}),e(\"div\",{staticClass:\"info\"},[e(\"span\",{staticClass:\"info-title\"},[t._v(\"b/job\")]),e(\"p\",{staticClass:\"info-num\"},[t._v(\"2.5k members\")])])]),e(\"button\",{staticClass:\"join-btn\"},[t._v(\"JOIN\")])])])])])}],b={name:\"Home\",components:{},data:function(){return{order:\"time\",page:1,postList:[]}},methods:{selectOrder:function(t){this.order=t,this.getPostList()},goPublish:function(){this.$router.push({name:\"Publish\"})},goDetail:function(t){this.$router.push({name:\"Content\",params:{id:t}})},getPostList:function(){var t=this;this.$axios({method:\"get\",url:\"/posts2\",params:{page:this.page,order:this.order}}).then((function(s){console.log(s.data,222),1e3==s.code?t.postList=s.data:console.log(s.msg)})).catch((function(t){console.log(t)}))},vote:function(t,s){this.$axios({method:\"post\",url:\"/vote\",data:JSON.stringify({post_id:t,direction:s})}).then((function(t){1e3==t.code?console.log(\"vote success\"):console.log(t.msg)})).catch((function(t){console.log(t)}))}},mounted:function(){this.getPostList()},computed:{timeOrder:function(){return\"time\"==this.order},scoreOrder:function(){return\"score\"==this.order}}},w=b,y=(e(\"97e5\"),Object(u[\"a\"])(w,C,_,!1,null,\"2baf73fc\",null)),x=y.exports,$=function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"div\",{staticClass:\"content\"},[e(\"div\",{staticClass:\"left\"},[e(\"div\",{staticClass:\"container\"},[t._m(0),e(\"div\",{staticClass:\"l-container\"},[e(\"h4\",{staticClass:\"con-title\"},[t._v(t._s(t.post.title))]),e(\"div\",{staticClass:\"con-info\"},[t._v(t._s(t.post.content))]),t._m(1)])])]),e(\"div\",{staticClass:\"right\"},[e(\"div\",{staticClass:\"topic-info\"},[e(\"h5\",{staticClass:\"t-header\"}),e(\"div\",{staticClass:\"t-info\"},[e(\"a\",{staticClass:\"avatar\"}),e(\"span\",{staticClass:\"topic-name\"},[t._v(\"b/\"+t._s(t.post.community_name))])]),e(\"p\",{staticClass:\"t-desc\"},[t._v(\"树洞 树洞 无限树洞的树洞\")]),t._m(2),e(\"div\",{staticClass:\"date\"},[t._v(\"Created Apr 10, 2008\")]),e(\"button\",{staticClass:\"topic-btn\"},[t._v(\"JOIN\")])])])])},O=[function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"div\",{staticClass:\"post\"},[e(\"a\",{staticClass:\"vote\"},[e(\"span\",{staticClass:\"iconfont icon-up\"})]),e(\"span\",{staticClass:\"text\"},[t._v(\"50.2k\")]),e(\"a\",{staticClass:\"vote\"},[e(\"span\",{staticClass:\"iconfont icon-down\"})])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"div\",{staticClass:\"user-btn\"},[e(\"span\",{staticClass:\"btn-item\"},[e(\"i\",{staticClass:\"iconfont icon-comment\"}),t._v(\"comment \")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"ul\",{staticClass:\"t-num\"},[e(\"li\",{staticClass:\"t-num-item\"},[e(\"p\",{staticClass:\"number\"},[t._v(\"5.2m\")]),e(\"span\",{staticClass:\"unit\"},[t._v(\"Members\")])]),e(\"li\",{staticClass:\"t-num-item\"},[e(\"p\",{staticClass:\"number\"},[t._v(\"5.2m\")]),e(\"span\",{staticClass:\"unit\"},[t._v(\"Members\")])])])}],L={name:\"Content\",data:function(){return{post:{}}},methods:{getPostDetail:function(){var t=this;this.$axios({method:\"get\",url:\"/post/\"+this.$route.params.id}).then((function(s){console.log(1,s.data),1e3==s.code?t.post=s.data:console.log(s.msg)})).catch((function(t){console.log(t)}))}},mounted:function(){this.getPostDetail()}},k=L,P=(e(\"f6e4\"),Object(u[\"a\"])(k,$,O,!1,null,\"4466eba1\",null)),S=P.exports,j=function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"div\",{staticClass:\"content\"},[e(\"div\",{staticClass:\"left\"},[e(\"div\",{staticClass:\"post-name\"},[t._v(\"我好想写点什么\")]),e(\"div\",{staticClass:\"post-type\"},[e(\"input\",{directives:[{name:\"model\",rawName:\"v-model\",value:t.selectCommunity.name,expression:\"selectCommunity.name\"}],staticClass:\"post-type-value\",attrs:{type:\"text\",placeholder:\"选择一个频道\"},domProps:{value:t.selectCommunity.name},on:{click:function(s){return t.showCommunity()},input:function(s){s.target.composing||t.$set(t.selectCommunity,\"name\",s.target.value)}}}),e(\"ul\",{directives:[{name:\"show\",rawName:\"v-show\",value:t.showCommunityList,expression:\"showCommunityList\"}],staticClass:\"post-type-options\"},t._l(t.communityList,(function(s,n){return e(\"li\",{key:s.id,staticClass:\"post-type-cell\",on:{click:function(s){return t.selected(n)}}},[t._v(\" \"+t._s(s.name)+\" \")])})),0),e(\"i\",{staticClass:\"p-icon\"})]),e(\"div\",{staticClass:\"post-content\"},[t._m(0),e(\"div\",{staticClass:\"post-sub-container\"},[e(\"div\",{staticClass:\"post-sub-header\"},[e(\"textarea\",{directives:[{name:\"model\",rawName:\"v-model\",value:t.title,expression:\"title\"}],staticClass:\"post-title\",attrs:{id:\"\",cols:\"30\",rows:\"10\",placeholder:\"标题\"},domProps:{value:t.title},on:{input:function(s){s.target.composing||(t.title=s.target.value)}}}),e(\"span\",{staticClass:\"textarea-num\"},[t._v(\"0/300\")])]),e(\"div\",{staticClass:\"post-text-con\"},[e(\"textarea\",{directives:[{name:\"model\",rawName:\"v-model\",value:t.content,expression:\"content\"}],staticClass:\"post-content-t\",attrs:{id:\"\",cols:\"30\",rows:\"10\",placeholder:\"内容\"},domProps:{value:t.content},on:{input:function(s){s.target.composing||(t.content=s.target.value)}}})])]),e(\"div\",{staticClass:\"post-footer\"},[e(\"div\",{staticClass:\"btns\"},[e(\"button\",{staticClass:\"btn\"},[t._v(\"取消\")]),e(\"button\",{staticClass:\"btn\",on:{click:function(s){return t.submit()}}},[t._v(\"发表\")])])])])]),t._m(1)])},N=[function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"ul\",{staticClass:\"cat\"},[e(\"li\",{staticClass:\"cat-item active\"},[e(\"i\",{staticClass:\"iconfont icon-edit\"}),t._v(\"post \")]),e(\"li\",{staticClass:\"cat-item\"},[e(\"i\",{staticClass:\"iconfont icon-image\"}),t._v(\"image/video \")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"div\",{staticClass:\"right\"},[e(\"div\",{staticClass:\"post-rank\"},[e(\"h5\",{staticClass:\"p-r-title\"},[e(\"i\",{staticClass:\"p-r-icon\"}),t._v(\"发帖规范 \")]),e(\"ul\",{staticClass:\"p-r-content\"},[e(\"li\",{staticClass:\"p-r-item\"},[t._v(\"1.网络不是法外之地\")]),e(\"li\",{staticClass:\"p-r-item\"},[t._v(\"2.网络不是法外之地\")]),e(\"li\",{staticClass:\"p-r-item\"},[t._v(\"3.网络不是法外之地\")])])])])}],R={name:\"Publish\",data:function(){return{title:\"\",content:\"\",showCommunityList:!1,selectCommunity:{},communityList:[]}},methods:{submit:function(){var t=this;this.$axios({method:\"post\",url:\"/post\",data:JSON.stringify({title:this.title,content:this.content,community_id:this.selectCommunity.id})}).then((function(s){console.log(s.data),1e3==s.code?t.$router.push({path:t.redirect||\"/\"}):console.log(s.msg)})).catch((function(t){console.log(t)}))},getCommunityList:function(){var t=this;this.$axios({method:\"get\",url:\"/community\"}).then((function(s){console.log(s.data),1e3==s.code?t.communityList=s.data:console.log(s.msg)})).catch((function(t){console.log(t)}))},showCommunity:function(){this.showCommunityList=!this.showCommunityList},selected:function(t){this.selectCommunity=this.communityList[t],this.showCommunityList=!1,console.log(this.selectCommunity)}},mounted:function(){this.getCommunityList()}},E=R,I=(e(\"0797\"),Object(u[\"a\"])(E,j,N,!1,null,\"6236a644\",null)),J=I.exports,U=function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"div\",{staticClass:\"main\"},[e(\"div\",{staticClass:\"container\"},[e(\"h2\",{staticClass:\"form-title\"},[t._v(\"登录\")]),e(\"div\",{staticClass:\"form-group\"},[e(\"label\",{attrs:{for:\"name\"}},[t._v(\"用户名\")]),e(\"input\",{directives:[{name:\"model\",rawName:\"v-model\",value:t.username,expression:\"username\"}],staticClass:\"form-control\",attrs:{type:\"text\",name:\"name\",id:\"name\",placeholder:\"用户名\"},domProps:{value:t.username},on:{input:function(s){s.target.composing||(t.username=s.target.value)}}})]),e(\"div\",{staticClass:\"form-group\"},[e(\"label\",{attrs:{for:\"pass\"}},[t._v(\"密码\")]),e(\"input\",{directives:[{name:\"model\",rawName:\"v-model\",value:t.password,expression:\"password\"}],staticClass:\"form-control\",attrs:{type:\"password\",name:\"pass\",id:\"pass\",placeholder:\"密码\"},domProps:{value:t.password},on:{input:function(s){s.target.composing||(t.password=s.target.value)}}})]),e(\"div\",{staticClass:\"form-btn\"},[e(\"button\",{staticClass:\"btn btn-info\",attrs:{type:\"button\"},on:{click:t.submit}},[t._v(\"提交\")])])])])},H=[],M={name:\"Login\",data:function(){return{username:\"\",password:\"\",submitted:!1}},computed:{},created:function(){},methods:{submit:function(){var t=this;this.$axios({method:\"post\",url:\"/login\",data:JSON.stringify({username:this.username,password:this.password})}).then((function(s){console.log(s.data),1e3==s.code?(localStorage.setItem(\"loginResult\",JSON.stringify(s.data)),t.$store.commit(\"login\",s.data),t.$router.push({path:t.redirect||\"/\"})):console.log(s.msg)})).catch((function(t){console.log(t)}))}}},B=M,D=(e(\"a87d\"),Object(u[\"a\"])(B,U,H,!1,null,\"5d78ac92\",null)),A=D.exports,T=function(){var t=this,s=t.$createElement,e=t._self._c||s;return e(\"div\",{staticClass:\"main\"},[e(\"div\",{staticClass:\"container\"},[e(\"h2\",{staticClass:\"form-title\"},[t._v(\"注册\")]),e(\"div\",{staticClass:\"form-group\"},[e(\"label\",{attrs:{for:\"name\"}},[t._v(\"用户名\")]),e(\"input\",{directives:[{name:\"model\",rawName:\"v-model\",value:t.username,expression:\"username\"}],staticClass:\"form-control\",attrs:{type:\"text\",name:\"name\",id:\"name\",placeholder:\"用户名\"},domProps:{value:t.username},on:{input:function(s){s.target.composing||(t.username=s.target.value)}}})]),e(\"div\",{staticClass:\"form-group\"},[e(\"label\",{attrs:{for:\"pass\"}},[t._v(\"密码\")]),e(\"input\",{directives:[{name:\"model\",rawName:\"v-model\",value:t.password,expression:\"password\"}],staticClass:\"form-control\",attrs:{type:\"password\",name:\"pass\",id:\"pass\",placeholder:\"密码\"},domProps:{value:t.password},on:{input:function(s){s.target.composing||(t.password=s.target.value)}}})]),e(\"div\",{staticClass:\"form-group\"},[e(\"label\",{attrs:{for:\"re_pass\"}},[t._v(\"确认密码\")]),e(\"input\",{directives:[{name:\"model\",rawName:\"v-model\",value:t.confirm_password,expression:\"confirm_password\"}],staticClass:\"form-control\",attrs:{type:\"password\",name:\"re_pass\",id:\"re_pass\",placeholder:\"确认密码\"},domProps:{value:t.confirm_password},on:{input:function(s){s.target.composing||(t.confirm_password=s.target.value)}}})]),e(\"div\",{staticClass:\"form-btn\"},[e(\"button\",{staticClass:\"btn btn-info\",attrs:{type:\"button\"},on:{click:t.submit}},[t._v(\"提交\")])])])])},q=[],z={name:\"SignUp\",data:function(){return{username:\"\",password:\"\",confirm_password:\"\",submitted:!1}},computed:{},created:function(){},methods:{submit:function(){var t=this;this.$axios({method:\"post\",url:\"/signup\",data:JSON.stringify({username:this.username,password:this.password,confirm_password:this.confirm_password})}).then((function(s){console.log(s.data),1e3==s.code?(console.log(\"signup success\"),t.$router.push({name:\"Login\"})):console.log(s.msg)})).catch((function(t){console.log(t)}))}}},F=z,G=(e(\"fe14\"),Object(u[\"a\"])(F,T,q,!1,null,\"61f1d9ff\",null)),K=G.exports,Q=h[\"a\"].prototype.push;h[\"a\"].prototype.push=function(t){return Q.call(this,t).catch((function(t){return t}))},n[\"a\"].use(h[\"a\"]);var V=[{path:\"/\",name:\"Home\",component:x},{path:\"/post/:id\",name:\"Content\",component:S},{path:\"/publish\",name:\"Publish\",component:J,meta:{requireAuth:!0}},{path:\"/login\",name:\"Login\",component:A},{path:\"/signup\",name:\"SignUp\",component:K}],W=new h[\"a\"]({mode:\"history\",base:\"/\",routes:V}),X=W,Y=e(\"2f62\");n[\"a\"].use(Y[\"a\"]);var Z={token:null,user_id:null,user_name:null},tt=new Y[\"a\"].Store({state:{isLogin:!1,loginResult:Z},mutations:{init:function(t){var s=JSON.parse(localStorage.getItem(\"loginResult\"));console.log(localStorage.getItem(\"loginResult\")),null!=s&&(t.loginResult=s)},login:function(t,s){t.loginResult=s},logout:function(t){localStorage.removeItem(\"loginResult\"),t.loginResult=Z}},actions:{},getters:{isLogin:function(t){return null!==t.loginResult.user_id},userID:function(t){return t.loginResult.user_id},username:function(t){return t.loginResult.user_name},accessToken:function(t){return t.loginResult.token}}}),st=(e(\"d3b7\"),e(\"bc3a\")),et=e.n(st);et.a.defaults.baseURL=\"/api/v1/\",et.a.interceptors.request.use((function(t){var s=JSON.parse(localStorage.getItem(\"loginResult\"));if(s){var e=s.token;t.headers.Authorization=\"Bearer \".concat(e)}return t}),(function(t){return Promise.reject(t)})),et.a.interceptors.response.use((function(t){return 200===t.status?Promise.resolve(t.data):Promise.reject(t.data)}),(function(t){console.log(\"error\",t)}));var nt=et.a;n[\"a\"].prototype.$axios=nt,n[\"a\"].config.productionTip=!1,X.beforeEach((function(t,s,e){console.log(t),console.log(s),t.meta.requireAuth?localStorage.getItem(\"loginResult\")||\"/login\"===t.path?e():e({path:\"/login\"}):e(),\"/login\"==t.fullPath&&(localStorage.getItem(\"loginResult\")?e({path:s.fullPath}):e())})),new n[\"a\"]({router:X,store:tt,render:function(t){return t(g)}}).$mount(\"#app\")},\"7c55\":function(t,s,e){\"use strict\";var n=e(\"2395\"),i=e.n(n);i.a},\"97e5\":function(t,s,e){\"use strict\";var n=e(\"3315\"),i=e.n(n);i.a},\"9c02\":function(t,s,e){},a87d:function(t,s,e){\"use strict\";var n=e(\"d746\"),i=e.n(n);i.a},aee9:function(t,s,e){\"use strict\";var n=e(\"c255\"),i=e.n(n);i.a},b040:function(t,s,e){},c255:function(t,s,e){},d746:function(t,s,e){},e355:function(t,s,e){},f6e4:function(t,s,e){\"use strict\";var n=e(\"9c02\"),i=e.n(n);i.a},fe14:function(t,s,e){\"use strict\";var n=e(\"b040\"),i=e.n(n);i.a}});\n//# sourceMappingURL=app.9f3efa6d.js.map"
  },
  {
    "path": "lesson80/bluebell/static/js/chunk-vendors.57f9e9d6.js",
    "content": "(window[\"webpackJsonp\"]=window[\"webpackJsonp\"]||[]).push([[\"chunk-vendors\"],{\"00ee\":function(t,e,n){var r=n(\"b622\"),o=r(\"toStringTag\"),i={};i[o]=\"z\",t.exports=\"[object z]\"===String(i)},\"0366\":function(t,e,n){var r=n(\"1c0b\");t.exports=function(t,e,n){if(r(t),void 0===e)return t;switch(n){case 0:return function(){return t.call(e)};case 1:return function(n){return t.call(e,n)};case 2:return function(n,r){return t.call(e,n,r)};case 3:return function(n,r,o){return t.call(e,n,r,o)}}return function(){return t.apply(e,arguments)}}},\"06cf\":function(t,e,n){var r=n(\"83ab\"),o=n(\"d1e7\"),i=n(\"5c6c\"),a=n(\"fc6a\"),s=n(\"c04e\"),c=n(\"5135\"),u=n(\"0cfb\"),f=Object.getOwnPropertyDescriptor;e.f=r?f:function(t,e){if(t=a(t),e=s(e,!0),u)try{return f(t,e)}catch(n){}if(c(t,e))return i(!o.f.call(t,e),t[e])}},\"0a06\":function(t,e,n){\"use strict\";var r=n(\"c532\"),o=n(\"30b5\"),i=n(\"f6b4\"),a=n(\"5270\"),s=n(\"4a7b\");function c(t){this.defaults=t,this.interceptors={request:new i,response:new i}}c.prototype.request=function(t){\"string\"===typeof t?(t=arguments[1]||{},t.url=arguments[0]):t=t||{},t=s(this.defaults,t),t.method?t.method=t.method.toLowerCase():this.defaults.method?t.method=this.defaults.method.toLowerCase():t.method=\"get\";var e=[a,void 0],n=Promise.resolve(t);this.interceptors.request.forEach((function(t){e.unshift(t.fulfilled,t.rejected)})),this.interceptors.response.forEach((function(t){e.push(t.fulfilled,t.rejected)}));while(e.length)n=n.then(e.shift(),e.shift());return n},c.prototype.getUri=function(t){return t=s(this.defaults,t),o(t.url,t.params,t.paramsSerializer).replace(/^\\?/,\"\")},r.forEach([\"delete\",\"get\",\"head\",\"options\"],(function(t){c.prototype[t]=function(e,n){return this.request(r.merge(n||{},{method:t,url:e}))}})),r.forEach([\"post\",\"put\",\"patch\"],(function(t){c.prototype[t]=function(e,n,o){return this.request(r.merge(o||{},{method:t,url:e,data:n}))}})),t.exports=c},\"0cfb\":function(t,e,n){var r=n(\"83ab\"),o=n(\"d039\"),i=n(\"cc12\");t.exports=!r&&!o((function(){return 7!=Object.defineProperty(i(\"div\"),\"a\",{get:function(){return 7}}).a}))},\"0df6\":function(t,e,n){\"use strict\";t.exports=function(t){return function(e){return t.apply(null,e)}}},\"19aa\":function(t,e){t.exports=function(t,e,n){if(!(t instanceof e))throw TypeError(\"Incorrect \"+(n?n+\" \":\"\")+\"invocation\");return t}},\"1be4\":function(t,e,n){var r=n(\"d066\");t.exports=r(\"document\",\"documentElement\")},\"1c0b\":function(t,e){t.exports=function(t){if(\"function\"!=typeof t)throw TypeError(String(t)+\" is not a function\");return t}},\"1c7e\":function(t,e,n){var r=n(\"b622\"),o=r(\"iterator\"),i=!1;try{var a=0,s={next:function(){return{done:!!a++}},return:function(){i=!0}};s[o]=function(){return this},Array.from(s,(function(){throw 2}))}catch(c){}t.exports=function(t,e){if(!e&&!i)return!1;var n=!1;try{var r={};r[o]=function(){return{next:function(){return{done:n=!0}}}},t(r)}catch(c){}return n}},\"1cdc\":function(t,e,n){var r=n(\"342f\");t.exports=/(iphone|ipod|ipad).*applewebkit/i.test(r)},\"1d2b\":function(t,e,n){\"use strict\";t.exports=function(t,e){return function(){for(var n=new Array(arguments.length),r=0;r<n.length;r++)n[r]=arguments[r];return t.apply(e,n)}}},\"1d80\":function(t,e){t.exports=function(t){if(void 0==t)throw TypeError(\"Can't call method on \"+t);return t}},2266:function(t,e,n){var r=n(\"825a\"),o=n(\"e95a\"),i=n(\"50c4\"),a=n(\"0366\"),s=n(\"35a1\"),c=n(\"9bdd\"),u=function(t,e){this.stopped=t,this.result=e},f=t.exports=function(t,e,n,f,l){var p,d,h,v,y,m,g,b=a(e,n,f?2:1);if(l)p=t;else{if(d=s(t),\"function\"!=typeof d)throw TypeError(\"Target is not iterable\");if(o(d)){for(h=0,v=i(t.length);v>h;h++)if(y=f?b(r(g=t[h])[0],g[1]):b(t[h]),y&&y instanceof u)return y;return new u(!1)}p=d.call(t)}m=p.next;while(!(g=m.call(p)).done)if(y=c(p,b,g.value,f),\"object\"==typeof y&&y&&y instanceof u)return y;return new u(!1)};f.stop=function(t){return new u(!0,t)}},\"23cb\":function(t,e,n){var r=n(\"a691\"),o=Math.max,i=Math.min;t.exports=function(t,e){var n=r(t);return n<0?o(n+e,0):i(n,e)}},\"23e7\":function(t,e,n){var r=n(\"da84\"),o=n(\"06cf\").f,i=n(\"9112\"),a=n(\"6eeb\"),s=n(\"ce4e\"),c=n(\"e893\"),u=n(\"94ca\");t.exports=function(t,e){var n,f,l,p,d,h,v=t.target,y=t.global,m=t.stat;if(f=y?r:m?r[v]||s(v,{}):(r[v]||{}).prototype,f)for(l in e){if(d=e[l],t.noTargetGet?(h=o(f,l),p=h&&h.value):p=f[l],n=u(y?l:v+(m?\".\":\"#\")+l,t.forced),!n&&void 0!==p){if(typeof d===typeof p)continue;c(d,p)}(t.sham||p&&p.sham)&&i(d,\"sham\",!0),a(f,l,d,t)}}},\"241c\":function(t,e,n){var r=n(\"ca84\"),o=n(\"7839\"),i=o.concat(\"length\",\"prototype\");e.f=Object.getOwnPropertyNames||function(t){return r(t,i)}},2444:function(t,e,n){\"use strict\";(function(e){var r=n(\"c532\"),o=n(\"c8af\"),i={\"Content-Type\":\"application/x-www-form-urlencoded\"};function a(t,e){!r.isUndefined(t)&&r.isUndefined(t[\"Content-Type\"])&&(t[\"Content-Type\"]=e)}function s(){var t;return(\"undefined\"!==typeof XMLHttpRequest||\"undefined\"!==typeof e&&\"[object process]\"===Object.prototype.toString.call(e))&&(t=n(\"b50d\")),t}var c={adapter:s(),transformRequest:[function(t,e){return o(e,\"Accept\"),o(e,\"Content-Type\"),r.isFormData(t)||r.isArrayBuffer(t)||r.isBuffer(t)||r.isStream(t)||r.isFile(t)||r.isBlob(t)?t:r.isArrayBufferView(t)?t.buffer:r.isURLSearchParams(t)?(a(e,\"application/x-www-form-urlencoded;charset=utf-8\"),t.toString()):r.isObject(t)?(a(e,\"application/json;charset=utf-8\"),JSON.stringify(t)):t}],transformResponse:[function(t){if(\"string\"===typeof t)try{t=JSON.parse(t)}catch(e){}return t}],timeout:0,xsrfCookieName:\"XSRF-TOKEN\",xsrfHeaderName:\"X-XSRF-TOKEN\",maxContentLength:-1,validateStatus:function(t){return t>=200&&t<300},headers:{common:{Accept:\"application/json, text/plain, */*\"}}};r.forEach([\"delete\",\"get\",\"head\"],(function(t){c.headers[t]={}})),r.forEach([\"post\",\"put\",\"patch\"],(function(t){c.headers[t]=r.merge(i)})),t.exports=c}).call(this,n(\"4362\"))},2626:function(t,e,n){\"use strict\";var r=n(\"d066\"),o=n(\"9bf2\"),i=n(\"b622\"),a=n(\"83ab\"),s=i(\"species\");t.exports=function(t){var e=r(t),n=o.f;a&&e&&!e[s]&&n(e,s,{configurable:!0,get:function(){return this}})}},2877:function(t,e,n){\"use strict\";function r(t,e,n,r,o,i,a,s){var c,u=\"function\"===typeof t?t.options:t;if(e&&(u.render=e,u.staticRenderFns=n,u._compiled=!0),r&&(u.functional=!0),i&&(u._scopeId=\"data-v-\"+i),a?(c=function(t){t=t||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext,t||\"undefined\"===typeof __VUE_SSR_CONTEXT__||(t=__VUE_SSR_CONTEXT__),o&&o.call(this,t),t&&t._registeredComponents&&t._registeredComponents.add(a)},u._ssrRegister=c):o&&(c=s?function(){o.call(this,this.$root.$options.shadowRoot)}:o),c)if(u.functional){u._injectStyles=c;var f=u.render;u.render=function(t,e){return c.call(e),f(t,e)}}else{var l=u.beforeCreate;u.beforeCreate=l?[].concat(l,c):[c]}return{exports:t,options:u}}n.d(e,\"a\",(function(){return r}))},\"2b0e\":function(t,e,n){\"use strict\";(function(t){\n/*!\n * Vue.js v2.6.11\n * (c) 2014-2019 Evan You\n * Released under the MIT License.\n */\nvar n=Object.freeze({});function r(t){return void 0===t||null===t}function o(t){return void 0!==t&&null!==t}function i(t){return!0===t}function a(t){return!1===t}function s(t){return\"string\"===typeof t||\"number\"===typeof t||\"symbol\"===typeof t||\"boolean\"===typeof t}function c(t){return null!==t&&\"object\"===typeof t}var u=Object.prototype.toString;function f(t){return\"[object Object]\"===u.call(t)}function l(t){return\"[object RegExp]\"===u.call(t)}function p(t){var e=parseFloat(String(t));return e>=0&&Math.floor(e)===e&&isFinite(t)}function d(t){return o(t)&&\"function\"===typeof t.then&&\"function\"===typeof t.catch}function h(t){return null==t?\"\":Array.isArray(t)||f(t)&&t.toString===u?JSON.stringify(t,null,2):String(t)}function v(t){var e=parseFloat(t);return isNaN(e)?t:e}function y(t,e){for(var n=Object.create(null),r=t.split(\",\"),o=0;o<r.length;o++)n[r[o]]=!0;return e?function(t){return n[t.toLowerCase()]}:function(t){return n[t]}}y(\"slot,component\",!0);var m=y(\"key,ref,slot,slot-scope,is\");function g(t,e){if(t.length){var n=t.indexOf(e);if(n>-1)return t.splice(n,1)}}var b=Object.prototype.hasOwnProperty;function _(t,e){return b.call(t,e)}function w(t){var e=Object.create(null);return function(n){var r=e[n];return r||(e[n]=t(n))}}var x=/-(\\w)/g,C=w((function(t){return t.replace(x,(function(t,e){return e?e.toUpperCase():\"\"}))})),O=w((function(t){return t.charAt(0).toUpperCase()+t.slice(1)})),A=/\\B([A-Z])/g,k=w((function(t){return t.replace(A,\"-$1\").toLowerCase()}));function j(t,e){function n(n){var r=arguments.length;return r?r>1?t.apply(e,arguments):t.call(e,n):t.call(e)}return n._length=t.length,n}function S(t,e){return t.bind(e)}var $=Function.prototype.bind?S:j;function E(t,e){e=e||0;var n=t.length-e,r=new Array(n);while(n--)r[n]=t[n+e];return r}function T(t,e){for(var n in e)t[n]=e[n];return t}function P(t){for(var e={},n=0;n<t.length;n++)t[n]&&T(e,t[n]);return e}function R(t,e,n){}var I=function(t,e,n){return!1},M=function(t){return t};function N(t,e){if(t===e)return!0;var n=c(t),r=c(e);if(!n||!r)return!n&&!r&&String(t)===String(e);try{var o=Array.isArray(t),i=Array.isArray(e);if(o&&i)return t.length===e.length&&t.every((function(t,n){return N(t,e[n])}));if(t instanceof Date&&e instanceof Date)return t.getTime()===e.getTime();if(o||i)return!1;var a=Object.keys(t),s=Object.keys(e);return a.length===s.length&&a.every((function(n){return N(t[n],e[n])}))}catch(u){return!1}}function L(t,e){for(var n=0;n<t.length;n++)if(N(t[n],e))return n;return-1}function D(t){var e=!1;return function(){e||(e=!0,t.apply(this,arguments))}}var F=\"data-server-rendered\",U=[\"component\",\"directive\",\"filter\"],B=[\"beforeCreate\",\"created\",\"beforeMount\",\"mounted\",\"beforeUpdate\",\"updated\",\"beforeDestroy\",\"destroyed\",\"activated\",\"deactivated\",\"errorCaptured\",\"serverPrefetch\"],q={optionMergeStrategies:Object.create(null),silent:!1,productionTip:!1,devtools:!1,performance:!1,errorHandler:null,warnHandler:null,ignoredElements:[],keyCodes:Object.create(null),isReservedTag:I,isReservedAttr:I,isUnknownElement:I,getTagNamespace:R,parsePlatformTagName:M,mustUseProp:I,async:!0,_lifecycleHooks:B},H=/a-zA-Z\\u00B7\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u203F-\\u2040\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD/;function V(t){var e=(t+\"\").charCodeAt(0);return 36===e||95===e}function z(t,e,n,r){Object.defineProperty(t,e,{value:n,enumerable:!!r,writable:!0,configurable:!0})}var G=new RegExp(\"[^\"+H.source+\".$_\\\\d]\");function W(t){if(!G.test(t)){var e=t.split(\".\");return function(t){for(var n=0;n<e.length;n++){if(!t)return;t=t[e[n]]}return t}}}var K,X=\"__proto__\"in{},J=\"undefined\"!==typeof window,Y=\"undefined\"!==typeof WXEnvironment&&!!WXEnvironment.platform,Q=Y&&WXEnvironment.platform.toLowerCase(),Z=J&&window.navigator.userAgent.toLowerCase(),tt=Z&&/msie|trident/.test(Z),et=Z&&Z.indexOf(\"msie 9.0\")>0,nt=Z&&Z.indexOf(\"edge/\")>0,rt=(Z&&Z.indexOf(\"android\"),Z&&/iphone|ipad|ipod|ios/.test(Z)||\"ios\"===Q),ot=(Z&&/chrome\\/\\d+/.test(Z),Z&&/phantomjs/.test(Z),Z&&Z.match(/firefox\\/(\\d+)/)),it={}.watch,at=!1;if(J)try{var st={};Object.defineProperty(st,\"passive\",{get:function(){at=!0}}),window.addEventListener(\"test-passive\",null,st)}catch(Ca){}var ct=function(){return void 0===K&&(K=!J&&!Y&&\"undefined\"!==typeof t&&(t[\"process\"]&&\"server\"===t[\"process\"].env.VUE_ENV)),K},ut=J&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__;function ft(t){return\"function\"===typeof t&&/native code/.test(t.toString())}var lt,pt=\"undefined\"!==typeof Symbol&&ft(Symbol)&&\"undefined\"!==typeof Reflect&&ft(Reflect.ownKeys);lt=\"undefined\"!==typeof Set&&ft(Set)?Set:function(){function t(){this.set=Object.create(null)}return t.prototype.has=function(t){return!0===this.set[t]},t.prototype.add=function(t){this.set[t]=!0},t.prototype.clear=function(){this.set=Object.create(null)},t}();var dt=R,ht=0,vt=function(){this.id=ht++,this.subs=[]};vt.prototype.addSub=function(t){this.subs.push(t)},vt.prototype.removeSub=function(t){g(this.subs,t)},vt.prototype.depend=function(){vt.target&&vt.target.addDep(this)},vt.prototype.notify=function(){var t=this.subs.slice();for(var e=0,n=t.length;e<n;e++)t[e].update()},vt.target=null;var yt=[];function mt(t){yt.push(t),vt.target=t}function gt(){yt.pop(),vt.target=yt[yt.length-1]}var bt=function(t,e,n,r,o,i,a,s){this.tag=t,this.data=e,this.children=n,this.text=r,this.elm=o,this.ns=void 0,this.context=i,this.fnContext=void 0,this.fnOptions=void 0,this.fnScopeId=void 0,this.key=e&&e.key,this.componentOptions=a,this.componentInstance=void 0,this.parent=void 0,this.raw=!1,this.isStatic=!1,this.isRootInsert=!0,this.isComment=!1,this.isCloned=!1,this.isOnce=!1,this.asyncFactory=s,this.asyncMeta=void 0,this.isAsyncPlaceholder=!1},_t={child:{configurable:!0}};_t.child.get=function(){return this.componentInstance},Object.defineProperties(bt.prototype,_t);var wt=function(t){void 0===t&&(t=\"\");var e=new bt;return e.text=t,e.isComment=!0,e};function xt(t){return new bt(void 0,void 0,void 0,String(t))}function Ct(t){var e=new bt(t.tag,t.data,t.children&&t.children.slice(),t.text,t.elm,t.context,t.componentOptions,t.asyncFactory);return e.ns=t.ns,e.isStatic=t.isStatic,e.key=t.key,e.isComment=t.isComment,e.fnContext=t.fnContext,e.fnOptions=t.fnOptions,e.fnScopeId=t.fnScopeId,e.asyncMeta=t.asyncMeta,e.isCloned=!0,e}var Ot=Array.prototype,At=Object.create(Ot),kt=[\"push\",\"pop\",\"shift\",\"unshift\",\"splice\",\"sort\",\"reverse\"];kt.forEach((function(t){var e=Ot[t];z(At,t,(function(){var n=[],r=arguments.length;while(r--)n[r]=arguments[r];var o,i=e.apply(this,n),a=this.__ob__;switch(t){case\"push\":case\"unshift\":o=n;break;case\"splice\":o=n.slice(2);break}return o&&a.observeArray(o),a.dep.notify(),i}))}));var jt=Object.getOwnPropertyNames(At),St=!0;function $t(t){St=t}var Et=function(t){this.value=t,this.dep=new vt,this.vmCount=0,z(t,\"__ob__\",this),Array.isArray(t)?(X?Tt(t,At):Pt(t,At,jt),this.observeArray(t)):this.walk(t)};function Tt(t,e){t.__proto__=e}function Pt(t,e,n){for(var r=0,o=n.length;r<o;r++){var i=n[r];z(t,i,e[i])}}function Rt(t,e){var n;if(c(t)&&!(t instanceof bt))return _(t,\"__ob__\")&&t.__ob__ instanceof Et?n=t.__ob__:St&&!ct()&&(Array.isArray(t)||f(t))&&Object.isExtensible(t)&&!t._isVue&&(n=new Et(t)),e&&n&&n.vmCount++,n}function It(t,e,n,r,o){var i=new vt,a=Object.getOwnPropertyDescriptor(t,e);if(!a||!1!==a.configurable){var s=a&&a.get,c=a&&a.set;s&&!c||2!==arguments.length||(n=t[e]);var u=!o&&Rt(n);Object.defineProperty(t,e,{enumerable:!0,configurable:!0,get:function(){var e=s?s.call(t):n;return vt.target&&(i.depend(),u&&(u.dep.depend(),Array.isArray(e)&&Lt(e))),e},set:function(e){var r=s?s.call(t):n;e===r||e!==e&&r!==r||s&&!c||(c?c.call(t,e):n=e,u=!o&&Rt(e),i.notify())}})}}function Mt(t,e,n){if(Array.isArray(t)&&p(e))return t.length=Math.max(t.length,e),t.splice(e,1,n),n;if(e in t&&!(e in Object.prototype))return t[e]=n,n;var r=t.__ob__;return t._isVue||r&&r.vmCount?n:r?(It(r.value,e,n),r.dep.notify(),n):(t[e]=n,n)}function Nt(t,e){if(Array.isArray(t)&&p(e))t.splice(e,1);else{var n=t.__ob__;t._isVue||n&&n.vmCount||_(t,e)&&(delete t[e],n&&n.dep.notify())}}function Lt(t){for(var e=void 0,n=0,r=t.length;n<r;n++)e=t[n],e&&e.__ob__&&e.__ob__.dep.depend(),Array.isArray(e)&&Lt(e)}Et.prototype.walk=function(t){for(var e=Object.keys(t),n=0;n<e.length;n++)It(t,e[n])},Et.prototype.observeArray=function(t){for(var e=0,n=t.length;e<n;e++)Rt(t[e])};var Dt=q.optionMergeStrategies;function Ft(t,e){if(!e)return t;for(var n,r,o,i=pt?Reflect.ownKeys(e):Object.keys(e),a=0;a<i.length;a++)n=i[a],\"__ob__\"!==n&&(r=t[n],o=e[n],_(t,n)?r!==o&&f(r)&&f(o)&&Ft(r,o):Mt(t,n,o));return t}function Ut(t,e,n){return n?function(){var r=\"function\"===typeof e?e.call(n,n):e,o=\"function\"===typeof t?t.call(n,n):t;return r?Ft(r,o):o}:e?t?function(){return Ft(\"function\"===typeof e?e.call(this,this):e,\"function\"===typeof t?t.call(this,this):t)}:e:t}function Bt(t,e){var n=e?t?t.concat(e):Array.isArray(e)?e:[e]:t;return n?qt(n):n}function qt(t){for(var e=[],n=0;n<t.length;n++)-1===e.indexOf(t[n])&&e.push(t[n]);return e}function Ht(t,e,n,r){var o=Object.create(t||null);return e?T(o,e):o}Dt.data=function(t,e,n){return n?Ut(t,e,n):e&&\"function\"!==typeof e?t:Ut(t,e)},B.forEach((function(t){Dt[t]=Bt})),U.forEach((function(t){Dt[t+\"s\"]=Ht})),Dt.watch=function(t,e,n,r){if(t===it&&(t=void 0),e===it&&(e=void 0),!e)return Object.create(t||null);if(!t)return e;var o={};for(var i in T(o,t),e){var a=o[i],s=e[i];a&&!Array.isArray(a)&&(a=[a]),o[i]=a?a.concat(s):Array.isArray(s)?s:[s]}return o},Dt.props=Dt.methods=Dt.inject=Dt.computed=function(t,e,n,r){if(!t)return e;var o=Object.create(null);return T(o,t),e&&T(o,e),o},Dt.provide=Ut;var Vt=function(t,e){return void 0===e?t:e};function zt(t,e){var n=t.props;if(n){var r,o,i,a={};if(Array.isArray(n)){r=n.length;while(r--)o=n[r],\"string\"===typeof o&&(i=C(o),a[i]={type:null})}else if(f(n))for(var s in n)o=n[s],i=C(s),a[i]=f(o)?o:{type:o};else 0;t.props=a}}function Gt(t,e){var n=t.inject;if(n){var r=t.inject={};if(Array.isArray(n))for(var o=0;o<n.length;o++)r[n[o]]={from:n[o]};else if(f(n))for(var i in n){var a=n[i];r[i]=f(a)?T({from:i},a):{from:a}}else 0}}function Wt(t){var e=t.directives;if(e)for(var n in e){var r=e[n];\"function\"===typeof r&&(e[n]={bind:r,update:r})}}function Kt(t,e,n){if(\"function\"===typeof e&&(e=e.options),zt(e,n),Gt(e,n),Wt(e),!e._base&&(e.extends&&(t=Kt(t,e.extends,n)),e.mixins))for(var r=0,o=e.mixins.length;r<o;r++)t=Kt(t,e.mixins[r],n);var i,a={};for(i in t)s(i);for(i in e)_(t,i)||s(i);function s(r){var o=Dt[r]||Vt;a[r]=o(t[r],e[r],n,r)}return a}function Xt(t,e,n,r){if(\"string\"===typeof n){var o=t[e];if(_(o,n))return o[n];var i=C(n);if(_(o,i))return o[i];var a=O(i);if(_(o,a))return o[a];var s=o[n]||o[i]||o[a];return s}}function Jt(t,e,n,r){var o=e[t],i=!_(n,t),a=n[t],s=te(Boolean,o.type);if(s>-1)if(i&&!_(o,\"default\"))a=!1;else if(\"\"===a||a===k(t)){var c=te(String,o.type);(c<0||s<c)&&(a=!0)}if(void 0===a){a=Yt(r,o,t);var u=St;$t(!0),Rt(a),$t(u)}return a}function Yt(t,e,n){if(_(e,\"default\")){var r=e.default;return t&&t.$options.propsData&&void 0===t.$options.propsData[n]&&void 0!==t._props[n]?t._props[n]:\"function\"===typeof r&&\"Function\"!==Qt(e.type)?r.call(t):r}}function Qt(t){var e=t&&t.toString().match(/^\\s*function (\\w+)/);return e?e[1]:\"\"}function Zt(t,e){return Qt(t)===Qt(e)}function te(t,e){if(!Array.isArray(e))return Zt(e,t)?0:-1;for(var n=0,r=e.length;n<r;n++)if(Zt(e[n],t))return n;return-1}function ee(t,e,n){mt();try{if(e){var r=e;while(r=r.$parent){var o=r.$options.errorCaptured;if(o)for(var i=0;i<o.length;i++)try{var a=!1===o[i].call(r,t,e,n);if(a)return}catch(Ca){re(Ca,r,\"errorCaptured hook\")}}}re(t,e,n)}finally{gt()}}function ne(t,e,n,r,o){var i;try{i=n?t.apply(e,n):t.call(e),i&&!i._isVue&&d(i)&&!i._handled&&(i.catch((function(t){return ee(t,r,o+\" (Promise/async)\")})),i._handled=!0)}catch(Ca){ee(Ca,r,o)}return i}function re(t,e,n){if(q.errorHandler)try{return q.errorHandler.call(null,t,e,n)}catch(Ca){Ca!==t&&oe(Ca,null,\"config.errorHandler\")}oe(t,e,n)}function oe(t,e,n){if(!J&&!Y||\"undefined\"===typeof console)throw t;console.error(t)}var ie,ae=!1,se=[],ce=!1;function ue(){ce=!1;var t=se.slice(0);se.length=0;for(var e=0;e<t.length;e++)t[e]()}if(\"undefined\"!==typeof Promise&&ft(Promise)){var fe=Promise.resolve();ie=function(){fe.then(ue),rt&&setTimeout(R)},ae=!0}else if(tt||\"undefined\"===typeof MutationObserver||!ft(MutationObserver)&&\"[object MutationObserverConstructor]\"!==MutationObserver.toString())ie=\"undefined\"!==typeof setImmediate&&ft(setImmediate)?function(){setImmediate(ue)}:function(){setTimeout(ue,0)};else{var le=1,pe=new MutationObserver(ue),de=document.createTextNode(String(le));pe.observe(de,{characterData:!0}),ie=function(){le=(le+1)%2,de.data=String(le)},ae=!0}function he(t,e){var n;if(se.push((function(){if(t)try{t.call(e)}catch(Ca){ee(Ca,e,\"nextTick\")}else n&&n(e)})),ce||(ce=!0,ie()),!t&&\"undefined\"!==typeof Promise)return new Promise((function(t){n=t}))}var ve=new lt;function ye(t){me(t,ve),ve.clear()}function me(t,e){var n,r,o=Array.isArray(t);if(!(!o&&!c(t)||Object.isFrozen(t)||t instanceof bt)){if(t.__ob__){var i=t.__ob__.dep.id;if(e.has(i))return;e.add(i)}if(o){n=t.length;while(n--)me(t[n],e)}else{r=Object.keys(t),n=r.length;while(n--)me(t[r[n]],e)}}}var ge=w((function(t){var e=\"&\"===t.charAt(0);t=e?t.slice(1):t;var n=\"~\"===t.charAt(0);t=n?t.slice(1):t;var r=\"!\"===t.charAt(0);return t=r?t.slice(1):t,{name:t,once:n,capture:r,passive:e}}));function be(t,e){function n(){var t=arguments,r=n.fns;if(!Array.isArray(r))return ne(r,null,arguments,e,\"v-on handler\");for(var o=r.slice(),i=0;i<o.length;i++)ne(o[i],null,t,e,\"v-on handler\")}return n.fns=t,n}function _e(t,e,n,o,a,s){var c,u,f,l;for(c in t)u=t[c],f=e[c],l=ge(c),r(u)||(r(f)?(r(u.fns)&&(u=t[c]=be(u,s)),i(l.once)&&(u=t[c]=a(l.name,u,l.capture)),n(l.name,u,l.capture,l.passive,l.params)):u!==f&&(f.fns=u,t[c]=f));for(c in e)r(t[c])&&(l=ge(c),o(l.name,e[c],l.capture))}function we(t,e,n){var a;t instanceof bt&&(t=t.data.hook||(t.data.hook={}));var s=t[e];function c(){n.apply(this,arguments),g(a.fns,c)}r(s)?a=be([c]):o(s.fns)&&i(s.merged)?(a=s,a.fns.push(c)):a=be([s,c]),a.merged=!0,t[e]=a}function xe(t,e,n){var i=e.options.props;if(!r(i)){var a={},s=t.attrs,c=t.props;if(o(s)||o(c))for(var u in i){var f=k(u);Ce(a,c,u,f,!0)||Ce(a,s,u,f,!1)}return a}}function Ce(t,e,n,r,i){if(o(e)){if(_(e,n))return t[n]=e[n],i||delete e[n],!0;if(_(e,r))return t[n]=e[r],i||delete e[r],!0}return!1}function Oe(t){for(var e=0;e<t.length;e++)if(Array.isArray(t[e]))return Array.prototype.concat.apply([],t);return t}function Ae(t){return s(t)?[xt(t)]:Array.isArray(t)?je(t):void 0}function ke(t){return o(t)&&o(t.text)&&a(t.isComment)}function je(t,e){var n,a,c,u,f=[];for(n=0;n<t.length;n++)a=t[n],r(a)||\"boolean\"===typeof a||(c=f.length-1,u=f[c],Array.isArray(a)?a.length>0&&(a=je(a,(e||\"\")+\"_\"+n),ke(a[0])&&ke(u)&&(f[c]=xt(u.text+a[0].text),a.shift()),f.push.apply(f,a)):s(a)?ke(u)?f[c]=xt(u.text+a):\"\"!==a&&f.push(xt(a)):ke(a)&&ke(u)?f[c]=xt(u.text+a.text):(i(t._isVList)&&o(a.tag)&&r(a.key)&&o(e)&&(a.key=\"__vlist\"+e+\"_\"+n+\"__\"),f.push(a)));return f}function Se(t){var e=t.$options.provide;e&&(t._provided=\"function\"===typeof e?e.call(t):e)}function $e(t){var e=Ee(t.$options.inject,t);e&&($t(!1),Object.keys(e).forEach((function(n){It(t,n,e[n])})),$t(!0))}function Ee(t,e){if(t){for(var n=Object.create(null),r=pt?Reflect.ownKeys(t):Object.keys(t),o=0;o<r.length;o++){var i=r[o];if(\"__ob__\"!==i){var a=t[i].from,s=e;while(s){if(s._provided&&_(s._provided,a)){n[i]=s._provided[a];break}s=s.$parent}if(!s)if(\"default\"in t[i]){var c=t[i].default;n[i]=\"function\"===typeof c?c.call(e):c}else 0}}return n}}function Te(t,e){if(!t||!t.length)return{};for(var n={},r=0,o=t.length;r<o;r++){var i=t[r],a=i.data;if(a&&a.attrs&&a.attrs.slot&&delete a.attrs.slot,i.context!==e&&i.fnContext!==e||!a||null==a.slot)(n.default||(n.default=[])).push(i);else{var s=a.slot,c=n[s]||(n[s]=[]);\"template\"===i.tag?c.push.apply(c,i.children||[]):c.push(i)}}for(var u in n)n[u].every(Pe)&&delete n[u];return n}function Pe(t){return t.isComment&&!t.asyncFactory||\" \"===t.text}function Re(t,e,r){var o,i=Object.keys(e).length>0,a=t?!!t.$stable:!i,s=t&&t.$key;if(t){if(t._normalized)return t._normalized;if(a&&r&&r!==n&&s===r.$key&&!i&&!r.$hasNormal)return r;for(var c in o={},t)t[c]&&\"$\"!==c[0]&&(o[c]=Ie(e,c,t[c]))}else o={};for(var u in e)u in o||(o[u]=Me(e,u));return t&&Object.isExtensible(t)&&(t._normalized=o),z(o,\"$stable\",a),z(o,\"$key\",s),z(o,\"$hasNormal\",i),o}function Ie(t,e,n){var r=function(){var t=arguments.length?n.apply(null,arguments):n({});return t=t&&\"object\"===typeof t&&!Array.isArray(t)?[t]:Ae(t),t&&(0===t.length||1===t.length&&t[0].isComment)?void 0:t};return n.proxy&&Object.defineProperty(t,e,{get:r,enumerable:!0,configurable:!0}),r}function Me(t,e){return function(){return t[e]}}function Ne(t,e){var n,r,i,a,s;if(Array.isArray(t)||\"string\"===typeof t)for(n=new Array(t.length),r=0,i=t.length;r<i;r++)n[r]=e(t[r],r);else if(\"number\"===typeof t)for(n=new Array(t),r=0;r<t;r++)n[r]=e(r+1,r);else if(c(t))if(pt&&t[Symbol.iterator]){n=[];var u=t[Symbol.iterator](),f=u.next();while(!f.done)n.push(e(f.value,n.length)),f=u.next()}else for(a=Object.keys(t),n=new Array(a.length),r=0,i=a.length;r<i;r++)s=a[r],n[r]=e(t[s],s,r);return o(n)||(n=[]),n._isVList=!0,n}function Le(t,e,n,r){var o,i=this.$scopedSlots[t];i?(n=n||{},r&&(n=T(T({},r),n)),o=i(n)||e):o=this.$slots[t]||e;var a=n&&n.slot;return a?this.$createElement(\"template\",{slot:a},o):o}function De(t){return Xt(this.$options,\"filters\",t,!0)||M}function Fe(t,e){return Array.isArray(t)?-1===t.indexOf(e):t!==e}function Ue(t,e,n,r,o){var i=q.keyCodes[e]||n;return o&&r&&!q.keyCodes[e]?Fe(o,r):i?Fe(i,t):r?k(r)!==e:void 0}function Be(t,e,n,r,o){if(n)if(c(n)){var i;Array.isArray(n)&&(n=P(n));var a=function(a){if(\"class\"===a||\"style\"===a||m(a))i=t;else{var s=t.attrs&&t.attrs.type;i=r||q.mustUseProp(e,s,a)?t.domProps||(t.domProps={}):t.attrs||(t.attrs={})}var c=C(a),u=k(a);if(!(c in i)&&!(u in i)&&(i[a]=n[a],o)){var f=t.on||(t.on={});f[\"update:\"+a]=function(t){n[a]=t}}};for(var s in n)a(s)}else;return t}function qe(t,e){var n=this._staticTrees||(this._staticTrees=[]),r=n[t];return r&&!e||(r=n[t]=this.$options.staticRenderFns[t].call(this._renderProxy,null,this),Ve(r,\"__static__\"+t,!1)),r}function He(t,e,n){return Ve(t,\"__once__\"+e+(n?\"_\"+n:\"\"),!0),t}function Ve(t,e,n){if(Array.isArray(t))for(var r=0;r<t.length;r++)t[r]&&\"string\"!==typeof t[r]&&ze(t[r],e+\"_\"+r,n);else ze(t,e,n)}function ze(t,e,n){t.isStatic=!0,t.key=e,t.isOnce=n}function Ge(t,e){if(e)if(f(e)){var n=t.on=t.on?T({},t.on):{};for(var r in e){var o=n[r],i=e[r];n[r]=o?[].concat(o,i):i}}else;return t}function We(t,e,n,r){e=e||{$stable:!n};for(var o=0;o<t.length;o++){var i=t[o];Array.isArray(i)?We(i,e,n):i&&(i.proxy&&(i.fn.proxy=!0),e[i.key]=i.fn)}return r&&(e.$key=r),e}function Ke(t,e){for(var n=0;n<e.length;n+=2){var r=e[n];\"string\"===typeof r&&r&&(t[e[n]]=e[n+1])}return t}function Xe(t,e){return\"string\"===typeof t?e+t:t}function Je(t){t._o=He,t._n=v,t._s=h,t._l=Ne,t._t=Le,t._q=N,t._i=L,t._m=qe,t._f=De,t._k=Ue,t._b=Be,t._v=xt,t._e=wt,t._u=We,t._g=Ge,t._d=Ke,t._p=Xe}function Ye(t,e,r,o,a){var s,c=this,u=a.options;_(o,\"_uid\")?(s=Object.create(o),s._original=o):(s=o,o=o._original);var f=i(u._compiled),l=!f;this.data=t,this.props=e,this.children=r,this.parent=o,this.listeners=t.on||n,this.injections=Ee(u.inject,o),this.slots=function(){return c.$slots||Re(t.scopedSlots,c.$slots=Te(r,o)),c.$slots},Object.defineProperty(this,\"scopedSlots\",{enumerable:!0,get:function(){return Re(t.scopedSlots,this.slots())}}),f&&(this.$options=u,this.$slots=this.slots(),this.$scopedSlots=Re(t.scopedSlots,this.$slots)),u._scopeId?this._c=function(t,e,n,r){var i=ln(s,t,e,n,r,l);return i&&!Array.isArray(i)&&(i.fnScopeId=u._scopeId,i.fnContext=o),i}:this._c=function(t,e,n,r){return ln(s,t,e,n,r,l)}}function Qe(t,e,r,i,a){var s=t.options,c={},u=s.props;if(o(u))for(var f in u)c[f]=Jt(f,u,e||n);else o(r.attrs)&&tn(c,r.attrs),o(r.props)&&tn(c,r.props);var l=new Ye(r,c,a,i,t),p=s.render.call(null,l._c,l);if(p instanceof bt)return Ze(p,r,l.parent,s,l);if(Array.isArray(p)){for(var d=Ae(p)||[],h=new Array(d.length),v=0;v<d.length;v++)h[v]=Ze(d[v],r,l.parent,s,l);return h}}function Ze(t,e,n,r,o){var i=Ct(t);return i.fnContext=n,i.fnOptions=r,e.slot&&((i.data||(i.data={})).slot=e.slot),i}function tn(t,e){for(var n in e)t[C(n)]=e[n]}Je(Ye.prototype);var en={init:function(t,e){if(t.componentInstance&&!t.componentInstance._isDestroyed&&t.data.keepAlive){var n=t;en.prepatch(n,n)}else{var r=t.componentInstance=on(t,En);r.$mount(e?t.elm:void 0,e)}},prepatch:function(t,e){var n=e.componentOptions,r=e.componentInstance=t.componentInstance;Mn(r,n.propsData,n.listeners,e,n.children)},insert:function(t){var e=t.context,n=t.componentInstance;n._isMounted||(n._isMounted=!0,Fn(n,\"mounted\")),t.data.keepAlive&&(e._isMounted?Qn(n):Ln(n,!0))},destroy:function(t){var e=t.componentInstance;e._isDestroyed||(t.data.keepAlive?Dn(e,!0):e.$destroy())}},nn=Object.keys(en);function rn(t,e,n,a,s){if(!r(t)){var u=n.$options._base;if(c(t)&&(t=u.extend(t)),\"function\"===typeof t){var f;if(r(t.cid)&&(f=t,t=wn(f,u),void 0===t))return _n(f,e,n,a,s);e=e||{},wr(t),o(e.model)&&cn(t.options,e);var l=xe(e,t,s);if(i(t.options.functional))return Qe(t,l,e,n,a);var p=e.on;if(e.on=e.nativeOn,i(t.options.abstract)){var d=e.slot;e={},d&&(e.slot=d)}an(e);var h=t.options.name||s,v=new bt(\"vue-component-\"+t.cid+(h?\"-\"+h:\"\"),e,void 0,void 0,void 0,n,{Ctor:t,propsData:l,listeners:p,tag:s,children:a},f);return v}}}function on(t,e){var n={_isComponent:!0,_parentVnode:t,parent:e},r=t.data.inlineTemplate;return o(r)&&(n.render=r.render,n.staticRenderFns=r.staticRenderFns),new t.componentOptions.Ctor(n)}function an(t){for(var e=t.hook||(t.hook={}),n=0;n<nn.length;n++){var r=nn[n],o=e[r],i=en[r];o===i||o&&o._merged||(e[r]=o?sn(i,o):i)}}function sn(t,e){var n=function(n,r){t(n,r),e(n,r)};return n._merged=!0,n}function cn(t,e){var n=t.model&&t.model.prop||\"value\",r=t.model&&t.model.event||\"input\";(e.attrs||(e.attrs={}))[n]=e.model.value;var i=e.on||(e.on={}),a=i[r],s=e.model.callback;o(a)?(Array.isArray(a)?-1===a.indexOf(s):a!==s)&&(i[r]=[s].concat(a)):i[r]=s}var un=1,fn=2;function ln(t,e,n,r,o,a){return(Array.isArray(n)||s(n))&&(o=r,r=n,n=void 0),i(a)&&(o=fn),pn(t,e,n,r,o)}function pn(t,e,n,r,i){if(o(n)&&o(n.__ob__))return wt();if(o(n)&&o(n.is)&&(e=n.is),!e)return wt();var a,s,c;(Array.isArray(r)&&\"function\"===typeof r[0]&&(n=n||{},n.scopedSlots={default:r[0]},r.length=0),i===fn?r=Ae(r):i===un&&(r=Oe(r)),\"string\"===typeof e)?(s=t.$vnode&&t.$vnode.ns||q.getTagNamespace(e),a=q.isReservedTag(e)?new bt(q.parsePlatformTagName(e),n,r,void 0,void 0,t):n&&n.pre||!o(c=Xt(t.$options,\"components\",e))?new bt(e,n,r,void 0,void 0,t):rn(c,n,t,r,e)):a=rn(e,n,t,r);return Array.isArray(a)?a:o(a)?(o(s)&&dn(a,s),o(n)&&hn(n),a):wt()}function dn(t,e,n){if(t.ns=e,\"foreignObject\"===t.tag&&(e=void 0,n=!0),o(t.children))for(var a=0,s=t.children.length;a<s;a++){var c=t.children[a];o(c.tag)&&(r(c.ns)||i(n)&&\"svg\"!==c.tag)&&dn(c,e,n)}}function hn(t){c(t.style)&&ye(t.style),c(t.class)&&ye(t.class)}function vn(t){t._vnode=null,t._staticTrees=null;var e=t.$options,r=t.$vnode=e._parentVnode,o=r&&r.context;t.$slots=Te(e._renderChildren,o),t.$scopedSlots=n,t._c=function(e,n,r,o){return ln(t,e,n,r,o,!1)},t.$createElement=function(e,n,r,o){return ln(t,e,n,r,o,!0)};var i=r&&r.data;It(t,\"$attrs\",i&&i.attrs||n,null,!0),It(t,\"$listeners\",e._parentListeners||n,null,!0)}var yn,mn=null;function gn(t){Je(t.prototype),t.prototype.$nextTick=function(t){return he(t,this)},t.prototype._render=function(){var t,e=this,n=e.$options,r=n.render,o=n._parentVnode;o&&(e.$scopedSlots=Re(o.data.scopedSlots,e.$slots,e.$scopedSlots)),e.$vnode=o;try{mn=e,t=r.call(e._renderProxy,e.$createElement)}catch(Ca){ee(Ca,e,\"render\"),t=e._vnode}finally{mn=null}return Array.isArray(t)&&1===t.length&&(t=t[0]),t instanceof bt||(t=wt()),t.parent=o,t}}function bn(t,e){return(t.__esModule||pt&&\"Module\"===t[Symbol.toStringTag])&&(t=t.default),c(t)?e.extend(t):t}function _n(t,e,n,r,o){var i=wt();return i.asyncFactory=t,i.asyncMeta={data:e,context:n,children:r,tag:o},i}function wn(t,e){if(i(t.error)&&o(t.errorComp))return t.errorComp;if(o(t.resolved))return t.resolved;var n=mn;if(n&&o(t.owners)&&-1===t.owners.indexOf(n)&&t.owners.push(n),i(t.loading)&&o(t.loadingComp))return t.loadingComp;if(n&&!o(t.owners)){var a=t.owners=[n],s=!0,u=null,f=null;n.$on(\"hook:destroyed\",(function(){return g(a,n)}));var l=function(t){for(var e=0,n=a.length;e<n;e++)a[e].$forceUpdate();t&&(a.length=0,null!==u&&(clearTimeout(u),u=null),null!==f&&(clearTimeout(f),f=null))},p=D((function(n){t.resolved=bn(n,e),s?a.length=0:l(!0)})),h=D((function(e){o(t.errorComp)&&(t.error=!0,l(!0))})),v=t(p,h);return c(v)&&(d(v)?r(t.resolved)&&v.then(p,h):d(v.component)&&(v.component.then(p,h),o(v.error)&&(t.errorComp=bn(v.error,e)),o(v.loading)&&(t.loadingComp=bn(v.loading,e),0===v.delay?t.loading=!0:u=setTimeout((function(){u=null,r(t.resolved)&&r(t.error)&&(t.loading=!0,l(!1))}),v.delay||200)),o(v.timeout)&&(f=setTimeout((function(){f=null,r(t.resolved)&&h(null)}),v.timeout)))),s=!1,t.loading?t.loadingComp:t.resolved}}function xn(t){return t.isComment&&t.asyncFactory}function Cn(t){if(Array.isArray(t))for(var e=0;e<t.length;e++){var n=t[e];if(o(n)&&(o(n.componentOptions)||xn(n)))return n}}function On(t){t._events=Object.create(null),t._hasHookEvent=!1;var e=t.$options._parentListeners;e&&Sn(t,e)}function An(t,e){yn.$on(t,e)}function kn(t,e){yn.$off(t,e)}function jn(t,e){var n=yn;return function r(){var o=e.apply(null,arguments);null!==o&&n.$off(t,r)}}function Sn(t,e,n){yn=t,_e(e,n||{},An,kn,jn,t),yn=void 0}function $n(t){var e=/^hook:/;t.prototype.$on=function(t,n){var r=this;if(Array.isArray(t))for(var o=0,i=t.length;o<i;o++)r.$on(t[o],n);else(r._events[t]||(r._events[t]=[])).push(n),e.test(t)&&(r._hasHookEvent=!0);return r},t.prototype.$once=function(t,e){var n=this;function r(){n.$off(t,r),e.apply(n,arguments)}return r.fn=e,n.$on(t,r),n},t.prototype.$off=function(t,e){var n=this;if(!arguments.length)return n._events=Object.create(null),n;if(Array.isArray(t)){for(var r=0,o=t.length;r<o;r++)n.$off(t[r],e);return n}var i,a=n._events[t];if(!a)return n;if(!e)return n._events[t]=null,n;var s=a.length;while(s--)if(i=a[s],i===e||i.fn===e){a.splice(s,1);break}return n},t.prototype.$emit=function(t){var e=this,n=e._events[t];if(n){n=n.length>1?E(n):n;for(var r=E(arguments,1),o='event handler for \"'+t+'\"',i=0,a=n.length;i<a;i++)ne(n[i],e,r,e,o)}return e}}var En=null;function Tn(t){var e=En;return En=t,function(){En=e}}function Pn(t){var e=t.$options,n=e.parent;if(n&&!e.abstract){while(n.$options.abstract&&n.$parent)n=n.$parent;n.$children.push(t)}t.$parent=n,t.$root=n?n.$root:t,t.$children=[],t.$refs={},t._watcher=null,t._inactive=null,t._directInactive=!1,t._isMounted=!1,t._isDestroyed=!1,t._isBeingDestroyed=!1}function Rn(t){t.prototype._update=function(t,e){var n=this,r=n.$el,o=n._vnode,i=Tn(n);n._vnode=t,n.$el=o?n.__patch__(o,t):n.__patch__(n.$el,t,e,!1),i(),r&&(r.__vue__=null),n.$el&&(n.$el.__vue__=n),n.$vnode&&n.$parent&&n.$vnode===n.$parent._vnode&&(n.$parent.$el=n.$el)},t.prototype.$forceUpdate=function(){var t=this;t._watcher&&t._watcher.update()},t.prototype.$destroy=function(){var t=this;if(!t._isBeingDestroyed){Fn(t,\"beforeDestroy\"),t._isBeingDestroyed=!0;var e=t.$parent;!e||e._isBeingDestroyed||t.$options.abstract||g(e.$children,t),t._watcher&&t._watcher.teardown();var n=t._watchers.length;while(n--)t._watchers[n].teardown();t._data.__ob__&&t._data.__ob__.vmCount--,t._isDestroyed=!0,t.__patch__(t._vnode,null),Fn(t,\"destroyed\"),t.$off(),t.$el&&(t.$el.__vue__=null),t.$vnode&&(t.$vnode.parent=null)}}}function In(t,e,n){var r;return t.$el=e,t.$options.render||(t.$options.render=wt),Fn(t,\"beforeMount\"),r=function(){t._update(t._render(),n)},new nr(t,r,R,{before:function(){t._isMounted&&!t._isDestroyed&&Fn(t,\"beforeUpdate\")}},!0),n=!1,null==t.$vnode&&(t._isMounted=!0,Fn(t,\"mounted\")),t}function Mn(t,e,r,o,i){var a=o.data.scopedSlots,s=t.$scopedSlots,c=!!(a&&!a.$stable||s!==n&&!s.$stable||a&&t.$scopedSlots.$key!==a.$key),u=!!(i||t.$options._renderChildren||c);if(t.$options._parentVnode=o,t.$vnode=o,t._vnode&&(t._vnode.parent=o),t.$options._renderChildren=i,t.$attrs=o.data.attrs||n,t.$listeners=r||n,e&&t.$options.props){$t(!1);for(var f=t._props,l=t.$options._propKeys||[],p=0;p<l.length;p++){var d=l[p],h=t.$options.props;f[d]=Jt(d,h,e,t)}$t(!0),t.$options.propsData=e}r=r||n;var v=t.$options._parentListeners;t.$options._parentListeners=r,Sn(t,r,v),u&&(t.$slots=Te(i,o.context),t.$forceUpdate())}function Nn(t){while(t&&(t=t.$parent))if(t._inactive)return!0;return!1}function Ln(t,e){if(e){if(t._directInactive=!1,Nn(t))return}else if(t._directInactive)return;if(t._inactive||null===t._inactive){t._inactive=!1;for(var n=0;n<t.$children.length;n++)Ln(t.$children[n]);Fn(t,\"activated\")}}function Dn(t,e){if((!e||(t._directInactive=!0,!Nn(t)))&&!t._inactive){t._inactive=!0;for(var n=0;n<t.$children.length;n++)Dn(t.$children[n]);Fn(t,\"deactivated\")}}function Fn(t,e){mt();var n=t.$options[e],r=e+\" hook\";if(n)for(var o=0,i=n.length;o<i;o++)ne(n[o],t,null,t,r);t._hasHookEvent&&t.$emit(\"hook:\"+e),gt()}var Un=[],Bn=[],qn={},Hn=!1,Vn=!1,zn=0;function Gn(){zn=Un.length=Bn.length=0,qn={},Hn=Vn=!1}var Wn=0,Kn=Date.now;if(J&&!tt){var Xn=window.performance;Xn&&\"function\"===typeof Xn.now&&Kn()>document.createEvent(\"Event\").timeStamp&&(Kn=function(){return Xn.now()})}function Jn(){var t,e;for(Wn=Kn(),Vn=!0,Un.sort((function(t,e){return t.id-e.id})),zn=0;zn<Un.length;zn++)t=Un[zn],t.before&&t.before(),e=t.id,qn[e]=null,t.run();var n=Bn.slice(),r=Un.slice();Gn(),Zn(n),Yn(r),ut&&q.devtools&&ut.emit(\"flush\")}function Yn(t){var e=t.length;while(e--){var n=t[e],r=n.vm;r._watcher===n&&r._isMounted&&!r._isDestroyed&&Fn(r,\"updated\")}}function Qn(t){t._inactive=!1,Bn.push(t)}function Zn(t){for(var e=0;e<t.length;e++)t[e]._inactive=!0,Ln(t[e],!0)}function tr(t){var e=t.id;if(null==qn[e]){if(qn[e]=!0,Vn){var n=Un.length-1;while(n>zn&&Un[n].id>t.id)n--;Un.splice(n+1,0,t)}else Un.push(t);Hn||(Hn=!0,he(Jn))}}var er=0,nr=function(t,e,n,r,o){this.vm=t,o&&(t._watcher=this),t._watchers.push(this),r?(this.deep=!!r.deep,this.user=!!r.user,this.lazy=!!r.lazy,this.sync=!!r.sync,this.before=r.before):this.deep=this.user=this.lazy=this.sync=!1,this.cb=n,this.id=++er,this.active=!0,this.dirty=this.lazy,this.deps=[],this.newDeps=[],this.depIds=new lt,this.newDepIds=new lt,this.expression=\"\",\"function\"===typeof e?this.getter=e:(this.getter=W(e),this.getter||(this.getter=R)),this.value=this.lazy?void 0:this.get()};nr.prototype.get=function(){var t;mt(this);var e=this.vm;try{t=this.getter.call(e,e)}catch(Ca){if(!this.user)throw Ca;ee(Ca,e,'getter for watcher \"'+this.expression+'\"')}finally{this.deep&&ye(t),gt(),this.cleanupDeps()}return t},nr.prototype.addDep=function(t){var e=t.id;this.newDepIds.has(e)||(this.newDepIds.add(e),this.newDeps.push(t),this.depIds.has(e)||t.addSub(this))},nr.prototype.cleanupDeps=function(){var t=this.deps.length;while(t--){var e=this.deps[t];this.newDepIds.has(e.id)||e.removeSub(this)}var n=this.depIds;this.depIds=this.newDepIds,this.newDepIds=n,this.newDepIds.clear(),n=this.deps,this.deps=this.newDeps,this.newDeps=n,this.newDeps.length=0},nr.prototype.update=function(){this.lazy?this.dirty=!0:this.sync?this.run():tr(this)},nr.prototype.run=function(){if(this.active){var t=this.get();if(t!==this.value||c(t)||this.deep){var e=this.value;if(this.value=t,this.user)try{this.cb.call(this.vm,t,e)}catch(Ca){ee(Ca,this.vm,'callback for watcher \"'+this.expression+'\"')}else this.cb.call(this.vm,t,e)}}},nr.prototype.evaluate=function(){this.value=this.get(),this.dirty=!1},nr.prototype.depend=function(){var t=this.deps.length;while(t--)this.deps[t].depend()},nr.prototype.teardown=function(){if(this.active){this.vm._isBeingDestroyed||g(this.vm._watchers,this);var t=this.deps.length;while(t--)this.deps[t].removeSub(this);this.active=!1}};var rr={enumerable:!0,configurable:!0,get:R,set:R};function or(t,e,n){rr.get=function(){return this[e][n]},rr.set=function(t){this[e][n]=t},Object.defineProperty(t,n,rr)}function ir(t){t._watchers=[];var e=t.$options;e.props&&ar(t,e.props),e.methods&&hr(t,e.methods),e.data?sr(t):Rt(t._data={},!0),e.computed&&fr(t,e.computed),e.watch&&e.watch!==it&&vr(t,e.watch)}function ar(t,e){var n=t.$options.propsData||{},r=t._props={},o=t.$options._propKeys=[],i=!t.$parent;i||$t(!1);var a=function(i){o.push(i);var a=Jt(i,e,n,t);It(r,i,a),i in t||or(t,\"_props\",i)};for(var s in e)a(s);$t(!0)}function sr(t){var e=t.$options.data;e=t._data=\"function\"===typeof e?cr(e,t):e||{},f(e)||(e={});var n=Object.keys(e),r=t.$options.props,o=(t.$options.methods,n.length);while(o--){var i=n[o];0,r&&_(r,i)||V(i)||or(t,\"_data\",i)}Rt(e,!0)}function cr(t,e){mt();try{return t.call(e,e)}catch(Ca){return ee(Ca,e,\"data()\"),{}}finally{gt()}}var ur={lazy:!0};function fr(t,e){var n=t._computedWatchers=Object.create(null),r=ct();for(var o in e){var i=e[o],a=\"function\"===typeof i?i:i.get;0,r||(n[o]=new nr(t,a||R,R,ur)),o in t||lr(t,o,i)}}function lr(t,e,n){var r=!ct();\"function\"===typeof n?(rr.get=r?pr(e):dr(n),rr.set=R):(rr.get=n.get?r&&!1!==n.cache?pr(e):dr(n.get):R,rr.set=n.set||R),Object.defineProperty(t,e,rr)}function pr(t){return function(){var e=this._computedWatchers&&this._computedWatchers[t];if(e)return e.dirty&&e.evaluate(),vt.target&&e.depend(),e.value}}function dr(t){return function(){return t.call(this,this)}}function hr(t,e){t.$options.props;for(var n in e)t[n]=\"function\"!==typeof e[n]?R:$(e[n],t)}function vr(t,e){for(var n in e){var r=e[n];if(Array.isArray(r))for(var o=0;o<r.length;o++)yr(t,n,r[o]);else yr(t,n,r)}}function yr(t,e,n,r){return f(n)&&(r=n,n=n.handler),\"string\"===typeof n&&(n=t[n]),t.$watch(e,n,r)}function mr(t){var e={get:function(){return this._data}},n={get:function(){return this._props}};Object.defineProperty(t.prototype,\"$data\",e),Object.defineProperty(t.prototype,\"$props\",n),t.prototype.$set=Mt,t.prototype.$delete=Nt,t.prototype.$watch=function(t,e,n){var r=this;if(f(e))return yr(r,t,e,n);n=n||{},n.user=!0;var o=new nr(r,t,e,n);if(n.immediate)try{e.call(r,o.value)}catch(i){ee(i,r,'callback for immediate watcher \"'+o.expression+'\"')}return function(){o.teardown()}}}var gr=0;function br(t){t.prototype._init=function(t){var e=this;e._uid=gr++,e._isVue=!0,t&&t._isComponent?_r(e,t):e.$options=Kt(wr(e.constructor),t||{},e),e._renderProxy=e,e._self=e,Pn(e),On(e),vn(e),Fn(e,\"beforeCreate\"),$e(e),ir(e),Se(e),Fn(e,\"created\"),e.$options.el&&e.$mount(e.$options.el)}}function _r(t,e){var n=t.$options=Object.create(t.constructor.options),r=e._parentVnode;n.parent=e.parent,n._parentVnode=r;var o=r.componentOptions;n.propsData=o.propsData,n._parentListeners=o.listeners,n._renderChildren=o.children,n._componentTag=o.tag,e.render&&(n.render=e.render,n.staticRenderFns=e.staticRenderFns)}function wr(t){var e=t.options;if(t.super){var n=wr(t.super),r=t.superOptions;if(n!==r){t.superOptions=n;var o=xr(t);o&&T(t.extendOptions,o),e=t.options=Kt(n,t.extendOptions),e.name&&(e.components[e.name]=t)}}return e}function xr(t){var e,n=t.options,r=t.sealedOptions;for(var o in n)n[o]!==r[o]&&(e||(e={}),e[o]=n[o]);return e}function Cr(t){this._init(t)}function Or(t){t.use=function(t){var e=this._installedPlugins||(this._installedPlugins=[]);if(e.indexOf(t)>-1)return this;var n=E(arguments,1);return n.unshift(this),\"function\"===typeof t.install?t.install.apply(t,n):\"function\"===typeof t&&t.apply(null,n),e.push(t),this}}function Ar(t){t.mixin=function(t){return this.options=Kt(this.options,t),this}}function kr(t){t.cid=0;var e=1;t.extend=function(t){t=t||{};var n=this,r=n.cid,o=t._Ctor||(t._Ctor={});if(o[r])return o[r];var i=t.name||n.options.name;var a=function(t){this._init(t)};return a.prototype=Object.create(n.prototype),a.prototype.constructor=a,a.cid=e++,a.options=Kt(n.options,t),a[\"super\"]=n,a.options.props&&jr(a),a.options.computed&&Sr(a),a.extend=n.extend,a.mixin=n.mixin,a.use=n.use,U.forEach((function(t){a[t]=n[t]})),i&&(a.options.components[i]=a),a.superOptions=n.options,a.extendOptions=t,a.sealedOptions=T({},a.options),o[r]=a,a}}function jr(t){var e=t.options.props;for(var n in e)or(t.prototype,\"_props\",n)}function Sr(t){var e=t.options.computed;for(var n in e)lr(t.prototype,n,e[n])}function $r(t){U.forEach((function(e){t[e]=function(t,n){return n?(\"component\"===e&&f(n)&&(n.name=n.name||t,n=this.options._base.extend(n)),\"directive\"===e&&\"function\"===typeof n&&(n={bind:n,update:n}),this.options[e+\"s\"][t]=n,n):this.options[e+\"s\"][t]}}))}function Er(t){return t&&(t.Ctor.options.name||t.tag)}function Tr(t,e){return Array.isArray(t)?t.indexOf(e)>-1:\"string\"===typeof t?t.split(\",\").indexOf(e)>-1:!!l(t)&&t.test(e)}function Pr(t,e){var n=t.cache,r=t.keys,o=t._vnode;for(var i in n){var a=n[i];if(a){var s=Er(a.componentOptions);s&&!e(s)&&Rr(n,i,r,o)}}}function Rr(t,e,n,r){var o=t[e];!o||r&&o.tag===r.tag||o.componentInstance.$destroy(),t[e]=null,g(n,e)}br(Cr),mr(Cr),$n(Cr),Rn(Cr),gn(Cr);var Ir=[String,RegExp,Array],Mr={name:\"keep-alive\",abstract:!0,props:{include:Ir,exclude:Ir,max:[String,Number]},created:function(){this.cache=Object.create(null),this.keys=[]},destroyed:function(){for(var t in this.cache)Rr(this.cache,t,this.keys)},mounted:function(){var t=this;this.$watch(\"include\",(function(e){Pr(t,(function(t){return Tr(e,t)}))})),this.$watch(\"exclude\",(function(e){Pr(t,(function(t){return!Tr(e,t)}))}))},render:function(){var t=this.$slots.default,e=Cn(t),n=e&&e.componentOptions;if(n){var r=Er(n),o=this,i=o.include,a=o.exclude;if(i&&(!r||!Tr(i,r))||a&&r&&Tr(a,r))return e;var s=this,c=s.cache,u=s.keys,f=null==e.key?n.Ctor.cid+(n.tag?\"::\"+n.tag:\"\"):e.key;c[f]?(e.componentInstance=c[f].componentInstance,g(u,f),u.push(f)):(c[f]=e,u.push(f),this.max&&u.length>parseInt(this.max)&&Rr(c,u[0],u,this._vnode)),e.data.keepAlive=!0}return e||t&&t[0]}},Nr={KeepAlive:Mr};function Lr(t){var e={get:function(){return q}};Object.defineProperty(t,\"config\",e),t.util={warn:dt,extend:T,mergeOptions:Kt,defineReactive:It},t.set=Mt,t.delete=Nt,t.nextTick=he,t.observable=function(t){return Rt(t),t},t.options=Object.create(null),U.forEach((function(e){t.options[e+\"s\"]=Object.create(null)})),t.options._base=t,T(t.options.components,Nr),Or(t),Ar(t),kr(t),$r(t)}Lr(Cr),Object.defineProperty(Cr.prototype,\"$isServer\",{get:ct}),Object.defineProperty(Cr.prototype,\"$ssrContext\",{get:function(){return this.$vnode&&this.$vnode.ssrContext}}),Object.defineProperty(Cr,\"FunctionalRenderContext\",{value:Ye}),Cr.version=\"2.6.11\";var Dr=y(\"style,class\"),Fr=y(\"input,textarea,option,select,progress\"),Ur=function(t,e,n){return\"value\"===n&&Fr(t)&&\"button\"!==e||\"selected\"===n&&\"option\"===t||\"checked\"===n&&\"input\"===t||\"muted\"===n&&\"video\"===t},Br=y(\"contenteditable,draggable,spellcheck\"),qr=y(\"events,caret,typing,plaintext-only\"),Hr=function(t,e){return Kr(e)||\"false\"===e?\"false\":\"contenteditable\"===t&&qr(e)?e:\"true\"},Vr=y(\"allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,translate,truespeed,typemustmatch,visible\"),zr=\"http://www.w3.org/1999/xlink\",Gr=function(t){return\":\"===t.charAt(5)&&\"xlink\"===t.slice(0,5)},Wr=function(t){return Gr(t)?t.slice(6,t.length):\"\"},Kr=function(t){return null==t||!1===t};function Xr(t){var e=t.data,n=t,r=t;while(o(r.componentInstance))r=r.componentInstance._vnode,r&&r.data&&(e=Jr(r.data,e));while(o(n=n.parent))n&&n.data&&(e=Jr(e,n.data));return Yr(e.staticClass,e.class)}function Jr(t,e){return{staticClass:Qr(t.staticClass,e.staticClass),class:o(t.class)?[t.class,e.class]:e.class}}function Yr(t,e){return o(t)||o(e)?Qr(t,Zr(e)):\"\"}function Qr(t,e){return t?e?t+\" \"+e:t:e||\"\"}function Zr(t){return Array.isArray(t)?to(t):c(t)?eo(t):\"string\"===typeof t?t:\"\"}function to(t){for(var e,n=\"\",r=0,i=t.length;r<i;r++)o(e=Zr(t[r]))&&\"\"!==e&&(n&&(n+=\" \"),n+=e);return n}function eo(t){var e=\"\";for(var n in t)t[n]&&(e&&(e+=\" \"),e+=n);return e}var no={svg:\"http://www.w3.org/2000/svg\",math:\"http://www.w3.org/1998/Math/MathML\"},ro=y(\"html,body,base,head,link,meta,style,title,address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,output,progress,select,textarea,details,dialog,menu,menuitem,summary,content,element,shadow,template,blockquote,iframe,tfoot\"),oo=y(\"svg,animate,circle,clippath,cursor,defs,desc,ellipse,filter,font-face,foreignObject,g,glyph,image,line,marker,mask,missing-glyph,path,pattern,polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view\",!0),io=function(t){return ro(t)||oo(t)};function ao(t){return oo(t)?\"svg\":\"math\"===t?\"math\":void 0}var so=Object.create(null);function co(t){if(!J)return!0;if(io(t))return!1;if(t=t.toLowerCase(),null!=so[t])return so[t];var e=document.createElement(t);return t.indexOf(\"-\")>-1?so[t]=e.constructor===window.HTMLUnknownElement||e.constructor===window.HTMLElement:so[t]=/HTMLUnknownElement/.test(e.toString())}var uo=y(\"text,number,password,search,email,tel,url\");function fo(t){if(\"string\"===typeof t){var e=document.querySelector(t);return e||document.createElement(\"div\")}return t}function lo(t,e){var n=document.createElement(t);return\"select\"!==t||e.data&&e.data.attrs&&void 0!==e.data.attrs.multiple&&n.setAttribute(\"multiple\",\"multiple\"),n}function po(t,e){return document.createElementNS(no[t],e)}function ho(t){return document.createTextNode(t)}function vo(t){return document.createComment(t)}function yo(t,e,n){t.insertBefore(e,n)}function mo(t,e){t.removeChild(e)}function go(t,e){t.appendChild(e)}function bo(t){return t.parentNode}function _o(t){return t.nextSibling}function wo(t){return t.tagName}function xo(t,e){t.textContent=e}function Co(t,e){t.setAttribute(e,\"\")}var Oo=Object.freeze({createElement:lo,createElementNS:po,createTextNode:ho,createComment:vo,insertBefore:yo,removeChild:mo,appendChild:go,parentNode:bo,nextSibling:_o,tagName:wo,setTextContent:xo,setStyleScope:Co}),Ao={create:function(t,e){ko(e)},update:function(t,e){t.data.ref!==e.data.ref&&(ko(t,!0),ko(e))},destroy:function(t){ko(t,!0)}};function ko(t,e){var n=t.data.ref;if(o(n)){var r=t.context,i=t.componentInstance||t.elm,a=r.$refs;e?Array.isArray(a[n])?g(a[n],i):a[n]===i&&(a[n]=void 0):t.data.refInFor?Array.isArray(a[n])?a[n].indexOf(i)<0&&a[n].push(i):a[n]=[i]:a[n]=i}}var jo=new bt(\"\",{},[]),So=[\"create\",\"activate\",\"update\",\"remove\",\"destroy\"];function $o(t,e){return t.key===e.key&&(t.tag===e.tag&&t.isComment===e.isComment&&o(t.data)===o(e.data)&&Eo(t,e)||i(t.isAsyncPlaceholder)&&t.asyncFactory===e.asyncFactory&&r(e.asyncFactory.error))}function Eo(t,e){if(\"input\"!==t.tag)return!0;var n,r=o(n=t.data)&&o(n=n.attrs)&&n.type,i=o(n=e.data)&&o(n=n.attrs)&&n.type;return r===i||uo(r)&&uo(i)}function To(t,e,n){var r,i,a={};for(r=e;r<=n;++r)i=t[r].key,o(i)&&(a[i]=r);return a}function Po(t){var e,n,a={},c=t.modules,u=t.nodeOps;for(e=0;e<So.length;++e)for(a[So[e]]=[],n=0;n<c.length;++n)o(c[n][So[e]])&&a[So[e]].push(c[n][So[e]]);function f(t){return new bt(u.tagName(t).toLowerCase(),{},[],void 0,t)}function l(t,e){function n(){0===--n.listeners&&p(t)}return n.listeners=e,n}function p(t){var e=u.parentNode(t);o(e)&&u.removeChild(e,t)}function d(t,e,n,r,a,s,c){if(o(t.elm)&&o(s)&&(t=s[c]=Ct(t)),t.isRootInsert=!a,!h(t,e,n,r)){var f=t.data,l=t.children,p=t.tag;o(p)?(t.elm=t.ns?u.createElementNS(t.ns,p):u.createElement(p,t),x(t),b(t,l,e),o(f)&&w(t,e),g(n,t.elm,r)):i(t.isComment)?(t.elm=u.createComment(t.text),g(n,t.elm,r)):(t.elm=u.createTextNode(t.text),g(n,t.elm,r))}}function h(t,e,n,r){var a=t.data;if(o(a)){var s=o(t.componentInstance)&&a.keepAlive;if(o(a=a.hook)&&o(a=a.init)&&a(t,!1),o(t.componentInstance))return v(t,e),g(n,t.elm,r),i(s)&&m(t,e,n,r),!0}}function v(t,e){o(t.data.pendingInsert)&&(e.push.apply(e,t.data.pendingInsert),t.data.pendingInsert=null),t.elm=t.componentInstance.$el,_(t)?(w(t,e),x(t)):(ko(t),e.push(t))}function m(t,e,n,r){var i,s=t;while(s.componentInstance)if(s=s.componentInstance._vnode,o(i=s.data)&&o(i=i.transition)){for(i=0;i<a.activate.length;++i)a.activate[i](jo,s);e.push(s);break}g(n,t.elm,r)}function g(t,e,n){o(t)&&(o(n)?u.parentNode(n)===t&&u.insertBefore(t,e,n):u.appendChild(t,e))}function b(t,e,n){if(Array.isArray(e)){0;for(var r=0;r<e.length;++r)d(e[r],n,t.elm,null,!0,e,r)}else s(t.text)&&u.appendChild(t.elm,u.createTextNode(String(t.text)))}function _(t){while(t.componentInstance)t=t.componentInstance._vnode;return o(t.tag)}function w(t,n){for(var r=0;r<a.create.length;++r)a.create[r](jo,t);e=t.data.hook,o(e)&&(o(e.create)&&e.create(jo,t),o(e.insert)&&n.push(t))}function x(t){var e;if(o(e=t.fnScopeId))u.setStyleScope(t.elm,e);else{var n=t;while(n)o(e=n.context)&&o(e=e.$options._scopeId)&&u.setStyleScope(t.elm,e),n=n.parent}o(e=En)&&e!==t.context&&e!==t.fnContext&&o(e=e.$options._scopeId)&&u.setStyleScope(t.elm,e)}function C(t,e,n,r,o,i){for(;r<=o;++r)d(n[r],i,t,e,!1,n,r)}function O(t){var e,n,r=t.data;if(o(r))for(o(e=r.hook)&&o(e=e.destroy)&&e(t),e=0;e<a.destroy.length;++e)a.destroy[e](t);if(o(e=t.children))for(n=0;n<t.children.length;++n)O(t.children[n])}function A(t,e,n){for(;e<=n;++e){var r=t[e];o(r)&&(o(r.tag)?(k(r),O(r)):p(r.elm))}}function k(t,e){if(o(e)||o(t.data)){var n,r=a.remove.length+1;for(o(e)?e.listeners+=r:e=l(t.elm,r),o(n=t.componentInstance)&&o(n=n._vnode)&&o(n.data)&&k(n,e),n=0;n<a.remove.length;++n)a.remove[n](t,e);o(n=t.data.hook)&&o(n=n.remove)?n(t,e):e()}else p(t.elm)}function j(t,e,n,i,a){var s,c,f,l,p=0,h=0,v=e.length-1,y=e[0],m=e[v],g=n.length-1,b=n[0],_=n[g],w=!a;while(p<=v&&h<=g)r(y)?y=e[++p]:r(m)?m=e[--v]:$o(y,b)?($(y,b,i,n,h),y=e[++p],b=n[++h]):$o(m,_)?($(m,_,i,n,g),m=e[--v],_=n[--g]):$o(y,_)?($(y,_,i,n,g),w&&u.insertBefore(t,y.elm,u.nextSibling(m.elm)),y=e[++p],_=n[--g]):$o(m,b)?($(m,b,i,n,h),w&&u.insertBefore(t,m.elm,y.elm),m=e[--v],b=n[++h]):(r(s)&&(s=To(e,p,v)),c=o(b.key)?s[b.key]:S(b,e,p,v),r(c)?d(b,i,t,y.elm,!1,n,h):(f=e[c],$o(f,b)?($(f,b,i,n,h),e[c]=void 0,w&&u.insertBefore(t,f.elm,y.elm)):d(b,i,t,y.elm,!1,n,h)),b=n[++h]);p>v?(l=r(n[g+1])?null:n[g+1].elm,C(t,l,n,h,g,i)):h>g&&A(e,p,v)}function S(t,e,n,r){for(var i=n;i<r;i++){var a=e[i];if(o(a)&&$o(t,a))return i}}function $(t,e,n,s,c,f){if(t!==e){o(e.elm)&&o(s)&&(e=s[c]=Ct(e));var l=e.elm=t.elm;if(i(t.isAsyncPlaceholder))o(e.asyncFactory.resolved)?P(t.elm,e,n):e.isAsyncPlaceholder=!0;else if(i(e.isStatic)&&i(t.isStatic)&&e.key===t.key&&(i(e.isCloned)||i(e.isOnce)))e.componentInstance=t.componentInstance;else{var p,d=e.data;o(d)&&o(p=d.hook)&&o(p=p.prepatch)&&p(t,e);var h=t.children,v=e.children;if(o(d)&&_(e)){for(p=0;p<a.update.length;++p)a.update[p](t,e);o(p=d.hook)&&o(p=p.update)&&p(t,e)}r(e.text)?o(h)&&o(v)?h!==v&&j(l,h,v,n,f):o(v)?(o(t.text)&&u.setTextContent(l,\"\"),C(l,null,v,0,v.length-1,n)):o(h)?A(h,0,h.length-1):o(t.text)&&u.setTextContent(l,\"\"):t.text!==e.text&&u.setTextContent(l,e.text),o(d)&&o(p=d.hook)&&o(p=p.postpatch)&&p(t,e)}}}function E(t,e,n){if(i(n)&&o(t.parent))t.parent.data.pendingInsert=e;else for(var r=0;r<e.length;++r)e[r].data.hook.insert(e[r])}var T=y(\"attrs,class,staticClass,staticStyle,key\");function P(t,e,n,r){var a,s=e.tag,c=e.data,u=e.children;if(r=r||c&&c.pre,e.elm=t,i(e.isComment)&&o(e.asyncFactory))return e.isAsyncPlaceholder=!0,!0;if(o(c)&&(o(a=c.hook)&&o(a=a.init)&&a(e,!0),o(a=e.componentInstance)))return v(e,n),!0;if(o(s)){if(o(u))if(t.hasChildNodes())if(o(a=c)&&o(a=a.domProps)&&o(a=a.innerHTML)){if(a!==t.innerHTML)return!1}else{for(var f=!0,l=t.firstChild,p=0;p<u.length;p++){if(!l||!P(l,u[p],n,r)){f=!1;break}l=l.nextSibling}if(!f||l)return!1}else b(e,u,n);if(o(c)){var d=!1;for(var h in c)if(!T(h)){d=!0,w(e,n);break}!d&&c[\"class\"]&&ye(c[\"class\"])}}else t.data!==e.text&&(t.data=e.text);return!0}return function(t,e,n,s){if(!r(e)){var c=!1,l=[];if(r(t))c=!0,d(e,l);else{var p=o(t.nodeType);if(!p&&$o(t,e))$(t,e,l,null,null,s);else{if(p){if(1===t.nodeType&&t.hasAttribute(F)&&(t.removeAttribute(F),n=!0),i(n)&&P(t,e,l))return E(e,l,!0),t;t=f(t)}var h=t.elm,v=u.parentNode(h);if(d(e,l,h._leaveCb?null:v,u.nextSibling(h)),o(e.parent)){var y=e.parent,m=_(e);while(y){for(var g=0;g<a.destroy.length;++g)a.destroy[g](y);if(y.elm=e.elm,m){for(var b=0;b<a.create.length;++b)a.create[b](jo,y);var w=y.data.hook.insert;if(w.merged)for(var x=1;x<w.fns.length;x++)w.fns[x]()}else ko(y);y=y.parent}}o(v)?A([t],0,0):o(t.tag)&&O(t)}}return E(e,l,c),e.elm}o(t)&&O(t)}}var Ro={create:Io,update:Io,destroy:function(t){Io(t,jo)}};function Io(t,e){(t.data.directives||e.data.directives)&&Mo(t,e)}function Mo(t,e){var n,r,o,i=t===jo,a=e===jo,s=Lo(t.data.directives,t.context),c=Lo(e.data.directives,e.context),u=[],f=[];for(n in c)r=s[n],o=c[n],r?(o.oldValue=r.value,o.oldArg=r.arg,Fo(o,\"update\",e,t),o.def&&o.def.componentUpdated&&f.push(o)):(Fo(o,\"bind\",e,t),o.def&&o.def.inserted&&u.push(o));if(u.length){var l=function(){for(var n=0;n<u.length;n++)Fo(u[n],\"inserted\",e,t)};i?we(e,\"insert\",l):l()}if(f.length&&we(e,\"postpatch\",(function(){for(var n=0;n<f.length;n++)Fo(f[n],\"componentUpdated\",e,t)})),!i)for(n in s)c[n]||Fo(s[n],\"unbind\",t,t,a)}var No=Object.create(null);function Lo(t,e){var n,r,o=Object.create(null);if(!t)return o;for(n=0;n<t.length;n++)r=t[n],r.modifiers||(r.modifiers=No),o[Do(r)]=r,r.def=Xt(e.$options,\"directives\",r.name,!0);return o}function Do(t){return t.rawName||t.name+\".\"+Object.keys(t.modifiers||{}).join(\".\")}function Fo(t,e,n,r,o){var i=t.def&&t.def[e];if(i)try{i(n.elm,t,n,r,o)}catch(Ca){ee(Ca,n.context,\"directive \"+t.name+\" \"+e+\" hook\")}}var Uo=[Ao,Ro];function Bo(t,e){var n=e.componentOptions;if((!o(n)||!1!==n.Ctor.options.inheritAttrs)&&(!r(t.data.attrs)||!r(e.data.attrs))){var i,a,s,c=e.elm,u=t.data.attrs||{},f=e.data.attrs||{};for(i in o(f.__ob__)&&(f=e.data.attrs=T({},f)),f)a=f[i],s=u[i],s!==a&&qo(c,i,a);for(i in(tt||nt)&&f.value!==u.value&&qo(c,\"value\",f.value),u)r(f[i])&&(Gr(i)?c.removeAttributeNS(zr,Wr(i)):Br(i)||c.removeAttribute(i))}}function qo(t,e,n){t.tagName.indexOf(\"-\")>-1?Ho(t,e,n):Vr(e)?Kr(n)?t.removeAttribute(e):(n=\"allowfullscreen\"===e&&\"EMBED\"===t.tagName?\"true\":e,t.setAttribute(e,n)):Br(e)?t.setAttribute(e,Hr(e,n)):Gr(e)?Kr(n)?t.removeAttributeNS(zr,Wr(e)):t.setAttributeNS(zr,e,n):Ho(t,e,n)}function Ho(t,e,n){if(Kr(n))t.removeAttribute(e);else{if(tt&&!et&&\"TEXTAREA\"===t.tagName&&\"placeholder\"===e&&\"\"!==n&&!t.__ieph){var r=function(e){e.stopImmediatePropagation(),t.removeEventListener(\"input\",r)};t.addEventListener(\"input\",r),t.__ieph=!0}t.setAttribute(e,n)}}var Vo={create:Bo,update:Bo};function zo(t,e){var n=e.elm,i=e.data,a=t.data;if(!(r(i.staticClass)&&r(i.class)&&(r(a)||r(a.staticClass)&&r(a.class)))){var s=Xr(e),c=n._transitionClasses;o(c)&&(s=Qr(s,Zr(c))),s!==n._prevClass&&(n.setAttribute(\"class\",s),n._prevClass=s)}}var Go,Wo={create:zo,update:zo},Ko=\"__r\",Xo=\"__c\";function Jo(t){if(o(t[Ko])){var e=tt?\"change\":\"input\";t[e]=[].concat(t[Ko],t[e]||[]),delete t[Ko]}o(t[Xo])&&(t.change=[].concat(t[Xo],t.change||[]),delete t[Xo])}function Yo(t,e,n){var r=Go;return function o(){var i=e.apply(null,arguments);null!==i&&ti(t,o,n,r)}}var Qo=ae&&!(ot&&Number(ot[1])<=53);function Zo(t,e,n,r){if(Qo){var o=Wn,i=e;e=i._wrapper=function(t){if(t.target===t.currentTarget||t.timeStamp>=o||t.timeStamp<=0||t.target.ownerDocument!==document)return i.apply(this,arguments)}}Go.addEventListener(t,e,at?{capture:n,passive:r}:n)}function ti(t,e,n,r){(r||Go).removeEventListener(t,e._wrapper||e,n)}function ei(t,e){if(!r(t.data.on)||!r(e.data.on)){var n=e.data.on||{},o=t.data.on||{};Go=e.elm,Jo(n),_e(n,o,Zo,ti,Yo,e.context),Go=void 0}}var ni,ri={create:ei,update:ei};function oi(t,e){if(!r(t.data.domProps)||!r(e.data.domProps)){var n,i,a=e.elm,s=t.data.domProps||{},c=e.data.domProps||{};for(n in o(c.__ob__)&&(c=e.data.domProps=T({},c)),s)n in c||(a[n]=\"\");for(n in c){if(i=c[n],\"textContent\"===n||\"innerHTML\"===n){if(e.children&&(e.children.length=0),i===s[n])continue;1===a.childNodes.length&&a.removeChild(a.childNodes[0])}if(\"value\"===n&&\"PROGRESS\"!==a.tagName){a._value=i;var u=r(i)?\"\":String(i);ii(a,u)&&(a.value=u)}else if(\"innerHTML\"===n&&oo(a.tagName)&&r(a.innerHTML)){ni=ni||document.createElement(\"div\"),ni.innerHTML=\"<svg>\"+i+\"</svg>\";var f=ni.firstChild;while(a.firstChild)a.removeChild(a.firstChild);while(f.firstChild)a.appendChild(f.firstChild)}else if(i!==s[n])try{a[n]=i}catch(Ca){}}}}function ii(t,e){return!t.composing&&(\"OPTION\"===t.tagName||ai(t,e)||si(t,e))}function ai(t,e){var n=!0;try{n=document.activeElement!==t}catch(Ca){}return n&&t.value!==e}function si(t,e){var n=t.value,r=t._vModifiers;if(o(r)){if(r.number)return v(n)!==v(e);if(r.trim)return n.trim()!==e.trim()}return n!==e}var ci={create:oi,update:oi},ui=w((function(t){var e={},n=/;(?![^(]*\\))/g,r=/:(.+)/;return t.split(n).forEach((function(t){if(t){var n=t.split(r);n.length>1&&(e[n[0].trim()]=n[1].trim())}})),e}));function fi(t){var e=li(t.style);return t.staticStyle?T(t.staticStyle,e):e}function li(t){return Array.isArray(t)?P(t):\"string\"===typeof t?ui(t):t}function pi(t,e){var n,r={};if(e){var o=t;while(o.componentInstance)o=o.componentInstance._vnode,o&&o.data&&(n=fi(o.data))&&T(r,n)}(n=fi(t.data))&&T(r,n);var i=t;while(i=i.parent)i.data&&(n=fi(i.data))&&T(r,n);return r}var di,hi=/^--/,vi=/\\s*!important$/,yi=function(t,e,n){if(hi.test(e))t.style.setProperty(e,n);else if(vi.test(n))t.style.setProperty(k(e),n.replace(vi,\"\"),\"important\");else{var r=gi(e);if(Array.isArray(n))for(var o=0,i=n.length;o<i;o++)t.style[r]=n[o];else t.style[r]=n}},mi=[\"Webkit\",\"Moz\",\"ms\"],gi=w((function(t){if(di=di||document.createElement(\"div\").style,t=C(t),\"filter\"!==t&&t in di)return t;for(var e=t.charAt(0).toUpperCase()+t.slice(1),n=0;n<mi.length;n++){var r=mi[n]+e;if(r in di)return r}}));function bi(t,e){var n=e.data,i=t.data;if(!(r(n.staticStyle)&&r(n.style)&&r(i.staticStyle)&&r(i.style))){var a,s,c=e.elm,u=i.staticStyle,f=i.normalizedStyle||i.style||{},l=u||f,p=li(e.data.style)||{};e.data.normalizedStyle=o(p.__ob__)?T({},p):p;var d=pi(e,!0);for(s in l)r(d[s])&&yi(c,s,\"\");for(s in d)a=d[s],a!==l[s]&&yi(c,s,null==a?\"\":a)}}var _i={create:bi,update:bi},wi=/\\s+/;function xi(t,e){if(e&&(e=e.trim()))if(t.classList)e.indexOf(\" \")>-1?e.split(wi).forEach((function(e){return t.classList.add(e)})):t.classList.add(e);else{var n=\" \"+(t.getAttribute(\"class\")||\"\")+\" \";n.indexOf(\" \"+e+\" \")<0&&t.setAttribute(\"class\",(n+e).trim())}}function Ci(t,e){if(e&&(e=e.trim()))if(t.classList)e.indexOf(\" \")>-1?e.split(wi).forEach((function(e){return t.classList.remove(e)})):t.classList.remove(e),t.classList.length||t.removeAttribute(\"class\");else{var n=\" \"+(t.getAttribute(\"class\")||\"\")+\" \",r=\" \"+e+\" \";while(n.indexOf(r)>=0)n=n.replace(r,\" \");n=n.trim(),n?t.setAttribute(\"class\",n):t.removeAttribute(\"class\")}}function Oi(t){if(t){if(\"object\"===typeof t){var e={};return!1!==t.css&&T(e,Ai(t.name||\"v\")),T(e,t),e}return\"string\"===typeof t?Ai(t):void 0}}var Ai=w((function(t){return{enterClass:t+\"-enter\",enterToClass:t+\"-enter-to\",enterActiveClass:t+\"-enter-active\",leaveClass:t+\"-leave\",leaveToClass:t+\"-leave-to\",leaveActiveClass:t+\"-leave-active\"}})),ki=J&&!et,ji=\"transition\",Si=\"animation\",$i=\"transition\",Ei=\"transitionend\",Ti=\"animation\",Pi=\"animationend\";ki&&(void 0===window.ontransitionend&&void 0!==window.onwebkittransitionend&&($i=\"WebkitTransition\",Ei=\"webkitTransitionEnd\"),void 0===window.onanimationend&&void 0!==window.onwebkitanimationend&&(Ti=\"WebkitAnimation\",Pi=\"webkitAnimationEnd\"));var Ri=J?window.requestAnimationFrame?window.requestAnimationFrame.bind(window):setTimeout:function(t){return t()};function Ii(t){Ri((function(){Ri(t)}))}function Mi(t,e){var n=t._transitionClasses||(t._transitionClasses=[]);n.indexOf(e)<0&&(n.push(e),xi(t,e))}function Ni(t,e){t._transitionClasses&&g(t._transitionClasses,e),Ci(t,e)}function Li(t,e,n){var r=Fi(t,e),o=r.type,i=r.timeout,a=r.propCount;if(!o)return n();var s=o===ji?Ei:Pi,c=0,u=function(){t.removeEventListener(s,f),n()},f=function(e){e.target===t&&++c>=a&&u()};setTimeout((function(){c<a&&u()}),i+1),t.addEventListener(s,f)}var Di=/\\b(transform|all)(,|$)/;function Fi(t,e){var n,r=window.getComputedStyle(t),o=(r[$i+\"Delay\"]||\"\").split(\", \"),i=(r[$i+\"Duration\"]||\"\").split(\", \"),a=Ui(o,i),s=(r[Ti+\"Delay\"]||\"\").split(\", \"),c=(r[Ti+\"Duration\"]||\"\").split(\", \"),u=Ui(s,c),f=0,l=0;e===ji?a>0&&(n=ji,f=a,l=i.length):e===Si?u>0&&(n=Si,f=u,l=c.length):(f=Math.max(a,u),n=f>0?a>u?ji:Si:null,l=n?n===ji?i.length:c.length:0);var p=n===ji&&Di.test(r[$i+\"Property\"]);return{type:n,timeout:f,propCount:l,hasTransform:p}}function Ui(t,e){while(t.length<e.length)t=t.concat(t);return Math.max.apply(null,e.map((function(e,n){return Bi(e)+Bi(t[n])})))}function Bi(t){return 1e3*Number(t.slice(0,-1).replace(\",\",\".\"))}function qi(t,e){var n=t.elm;o(n._leaveCb)&&(n._leaveCb.cancelled=!0,n._leaveCb());var i=Oi(t.data.transition);if(!r(i)&&!o(n._enterCb)&&1===n.nodeType){var a=i.css,s=i.type,u=i.enterClass,f=i.enterToClass,l=i.enterActiveClass,p=i.appearClass,d=i.appearToClass,h=i.appearActiveClass,y=i.beforeEnter,m=i.enter,g=i.afterEnter,b=i.enterCancelled,_=i.beforeAppear,w=i.appear,x=i.afterAppear,C=i.appearCancelled,O=i.duration,A=En,k=En.$vnode;while(k&&k.parent)A=k.context,k=k.parent;var j=!A._isMounted||!t.isRootInsert;if(!j||w||\"\"===w){var S=j&&p?p:u,$=j&&h?h:l,E=j&&d?d:f,T=j&&_||y,P=j&&\"function\"===typeof w?w:m,R=j&&x||g,I=j&&C||b,M=v(c(O)?O.enter:O);0;var N=!1!==a&&!et,L=zi(P),F=n._enterCb=D((function(){N&&(Ni(n,E),Ni(n,$)),F.cancelled?(N&&Ni(n,S),I&&I(n)):R&&R(n),n._enterCb=null}));t.data.show||we(t,\"insert\",(function(){var e=n.parentNode,r=e&&e._pending&&e._pending[t.key];r&&r.tag===t.tag&&r.elm._leaveCb&&r.elm._leaveCb(),P&&P(n,F)})),T&&T(n),N&&(Mi(n,S),Mi(n,$),Ii((function(){Ni(n,S),F.cancelled||(Mi(n,E),L||(Vi(M)?setTimeout(F,M):Li(n,s,F)))}))),t.data.show&&(e&&e(),P&&P(n,F)),N||L||F()}}}function Hi(t,e){var n=t.elm;o(n._enterCb)&&(n._enterCb.cancelled=!0,n._enterCb());var i=Oi(t.data.transition);if(r(i)||1!==n.nodeType)return e();if(!o(n._leaveCb)){var a=i.css,s=i.type,u=i.leaveClass,f=i.leaveToClass,l=i.leaveActiveClass,p=i.beforeLeave,d=i.leave,h=i.afterLeave,y=i.leaveCancelled,m=i.delayLeave,g=i.duration,b=!1!==a&&!et,_=zi(d),w=v(c(g)?g.leave:g);0;var x=n._leaveCb=D((function(){n.parentNode&&n.parentNode._pending&&(n.parentNode._pending[t.key]=null),b&&(Ni(n,f),Ni(n,l)),x.cancelled?(b&&Ni(n,u),y&&y(n)):(e(),h&&h(n)),n._leaveCb=null}));m?m(C):C()}function C(){x.cancelled||(!t.data.show&&n.parentNode&&((n.parentNode._pending||(n.parentNode._pending={}))[t.key]=t),p&&p(n),b&&(Mi(n,u),Mi(n,l),Ii((function(){Ni(n,u),x.cancelled||(Mi(n,f),_||(Vi(w)?setTimeout(x,w):Li(n,s,x)))}))),d&&d(n,x),b||_||x())}}function Vi(t){return\"number\"===typeof t&&!isNaN(t)}function zi(t){if(r(t))return!1;var e=t.fns;return o(e)?zi(Array.isArray(e)?e[0]:e):(t._length||t.length)>1}function Gi(t,e){!0!==e.data.show&&qi(e)}var Wi=J?{create:Gi,activate:Gi,remove:function(t,e){!0!==t.data.show?Hi(t,e):e()}}:{},Ki=[Vo,Wo,ri,ci,_i,Wi],Xi=Ki.concat(Uo),Ji=Po({nodeOps:Oo,modules:Xi});et&&document.addEventListener(\"selectionchange\",(function(){var t=document.activeElement;t&&t.vmodel&&oa(t,\"input\")}));var Yi={inserted:function(t,e,n,r){\"select\"===n.tag?(r.elm&&!r.elm._vOptions?we(n,\"postpatch\",(function(){Yi.componentUpdated(t,e,n)})):Qi(t,e,n.context),t._vOptions=[].map.call(t.options,ea)):(\"textarea\"===n.tag||uo(t.type))&&(t._vModifiers=e.modifiers,e.modifiers.lazy||(t.addEventListener(\"compositionstart\",na),t.addEventListener(\"compositionend\",ra),t.addEventListener(\"change\",ra),et&&(t.vmodel=!0)))},componentUpdated:function(t,e,n){if(\"select\"===n.tag){Qi(t,e,n.context);var r=t._vOptions,o=t._vOptions=[].map.call(t.options,ea);if(o.some((function(t,e){return!N(t,r[e])}))){var i=t.multiple?e.value.some((function(t){return ta(t,o)})):e.value!==e.oldValue&&ta(e.value,o);i&&oa(t,\"change\")}}}};function Qi(t,e,n){Zi(t,e,n),(tt||nt)&&setTimeout((function(){Zi(t,e,n)}),0)}function Zi(t,e,n){var r=e.value,o=t.multiple;if(!o||Array.isArray(r)){for(var i,a,s=0,c=t.options.length;s<c;s++)if(a=t.options[s],o)i=L(r,ea(a))>-1,a.selected!==i&&(a.selected=i);else if(N(ea(a),r))return void(t.selectedIndex!==s&&(t.selectedIndex=s));o||(t.selectedIndex=-1)}}function ta(t,e){return e.every((function(e){return!N(e,t)}))}function ea(t){return\"_value\"in t?t._value:t.value}function na(t){t.target.composing=!0}function ra(t){t.target.composing&&(t.target.composing=!1,oa(t.target,\"input\"))}function oa(t,e){var n=document.createEvent(\"HTMLEvents\");n.initEvent(e,!0,!0),t.dispatchEvent(n)}function ia(t){return!t.componentInstance||t.data&&t.data.transition?t:ia(t.componentInstance._vnode)}var aa={bind:function(t,e,n){var r=e.value;n=ia(n);var o=n.data&&n.data.transition,i=t.__vOriginalDisplay=\"none\"===t.style.display?\"\":t.style.display;r&&o?(n.data.show=!0,qi(n,(function(){t.style.display=i}))):t.style.display=r?i:\"none\"},update:function(t,e,n){var r=e.value,o=e.oldValue;if(!r!==!o){n=ia(n);var i=n.data&&n.data.transition;i?(n.data.show=!0,r?qi(n,(function(){t.style.display=t.__vOriginalDisplay})):Hi(n,(function(){t.style.display=\"none\"}))):t.style.display=r?t.__vOriginalDisplay:\"none\"}},unbind:function(t,e,n,r,o){o||(t.style.display=t.__vOriginalDisplay)}},sa={model:Yi,show:aa},ca={name:String,appear:Boolean,css:Boolean,mode:String,type:String,enterClass:String,leaveClass:String,enterToClass:String,leaveToClass:String,enterActiveClass:String,leaveActiveClass:String,appearClass:String,appearActiveClass:String,appearToClass:String,duration:[Number,String,Object]};function ua(t){var e=t&&t.componentOptions;return e&&e.Ctor.options.abstract?ua(Cn(e.children)):t}function fa(t){var e={},n=t.$options;for(var r in n.propsData)e[r]=t[r];var o=n._parentListeners;for(var i in o)e[C(i)]=o[i];return e}function la(t,e){if(/\\d-keep-alive$/.test(e.tag))return t(\"keep-alive\",{props:e.componentOptions.propsData})}function pa(t){while(t=t.parent)if(t.data.transition)return!0}function da(t,e){return e.key===t.key&&e.tag===t.tag}var ha=function(t){return t.tag||xn(t)},va=function(t){return\"show\"===t.name},ya={name:\"transition\",props:ca,abstract:!0,render:function(t){var e=this,n=this.$slots.default;if(n&&(n=n.filter(ha),n.length)){0;var r=this.mode;0;var o=n[0];if(pa(this.$vnode))return o;var i=ua(o);if(!i)return o;if(this._leaving)return la(t,o);var a=\"__transition-\"+this._uid+\"-\";i.key=null==i.key?i.isComment?a+\"comment\":a+i.tag:s(i.key)?0===String(i.key).indexOf(a)?i.key:a+i.key:i.key;var c=(i.data||(i.data={})).transition=fa(this),u=this._vnode,f=ua(u);if(i.data.directives&&i.data.directives.some(va)&&(i.data.show=!0),f&&f.data&&!da(i,f)&&!xn(f)&&(!f.componentInstance||!f.componentInstance._vnode.isComment)){var l=f.data.transition=T({},c);if(\"out-in\"===r)return this._leaving=!0,we(l,\"afterLeave\",(function(){e._leaving=!1,e.$forceUpdate()})),la(t,o);if(\"in-out\"===r){if(xn(i))return u;var p,d=function(){p()};we(c,\"afterEnter\",d),we(c,\"enterCancelled\",d),we(l,\"delayLeave\",(function(t){p=t}))}}return o}}},ma=T({tag:String,moveClass:String},ca);delete ma.mode;var ga={props:ma,beforeMount:function(){var t=this,e=this._update;this._update=function(n,r){var o=Tn(t);t.__patch__(t._vnode,t.kept,!1,!0),t._vnode=t.kept,o(),e.call(t,n,r)}},render:function(t){for(var e=this.tag||this.$vnode.data.tag||\"span\",n=Object.create(null),r=this.prevChildren=this.children,o=this.$slots.default||[],i=this.children=[],a=fa(this),s=0;s<o.length;s++){var c=o[s];if(c.tag)if(null!=c.key&&0!==String(c.key).indexOf(\"__vlist\"))i.push(c),n[c.key]=c,(c.data||(c.data={})).transition=a;else;}if(r){for(var u=[],f=[],l=0;l<r.length;l++){var p=r[l];p.data.transition=a,p.data.pos=p.elm.getBoundingClientRect(),n[p.key]?u.push(p):f.push(p)}this.kept=t(e,null,u),this.removed=f}return t(e,null,i)},updated:function(){var t=this.prevChildren,e=this.moveClass||(this.name||\"v\")+\"-move\";t.length&&this.hasMove(t[0].elm,e)&&(t.forEach(ba),t.forEach(_a),t.forEach(wa),this._reflow=document.body.offsetHeight,t.forEach((function(t){if(t.data.moved){var n=t.elm,r=n.style;Mi(n,e),r.transform=r.WebkitTransform=r.transitionDuration=\"\",n.addEventListener(Ei,n._moveCb=function t(r){r&&r.target!==n||r&&!/transform$/.test(r.propertyName)||(n.removeEventListener(Ei,t),n._moveCb=null,Ni(n,e))})}})))},methods:{hasMove:function(t,e){if(!ki)return!1;if(this._hasMove)return this._hasMove;var n=t.cloneNode();t._transitionClasses&&t._transitionClasses.forEach((function(t){Ci(n,t)})),xi(n,e),n.style.display=\"none\",this.$el.appendChild(n);var r=Fi(n);return this.$el.removeChild(n),this._hasMove=r.hasTransform}}};function ba(t){t.elm._moveCb&&t.elm._moveCb(),t.elm._enterCb&&t.elm._enterCb()}function _a(t){t.data.newPos=t.elm.getBoundingClientRect()}function wa(t){var e=t.data.pos,n=t.data.newPos,r=e.left-n.left,o=e.top-n.top;if(r||o){t.data.moved=!0;var i=t.elm.style;i.transform=i.WebkitTransform=\"translate(\"+r+\"px,\"+o+\"px)\",i.transitionDuration=\"0s\"}}var xa={Transition:ya,TransitionGroup:ga};Cr.config.mustUseProp=Ur,Cr.config.isReservedTag=io,Cr.config.isReservedAttr=Dr,Cr.config.getTagNamespace=ao,Cr.config.isUnknownElement=co,T(Cr.options.directives,sa),T(Cr.options.components,xa),Cr.prototype.__patch__=J?Ji:R,Cr.prototype.$mount=function(t,e){return t=t&&J?fo(t):void 0,In(this,t,e)},J&&setTimeout((function(){q.devtools&&ut&&ut.emit(\"init\",Cr)}),0),e[\"a\"]=Cr}).call(this,n(\"c8ba\"))},\"2cf4\":function(t,e,n){var r,o,i,a=n(\"da84\"),s=n(\"d039\"),c=n(\"c6b6\"),u=n(\"0366\"),f=n(\"1be4\"),l=n(\"cc12\"),p=n(\"1cdc\"),d=a.location,h=a.setImmediate,v=a.clearImmediate,y=a.process,m=a.MessageChannel,g=a.Dispatch,b=0,_={},w=\"onreadystatechange\",x=function(t){if(_.hasOwnProperty(t)){var e=_[t];delete _[t],e()}},C=function(t){return function(){x(t)}},O=function(t){x(t.data)},A=function(t){a.postMessage(t+\"\",d.protocol+\"//\"+d.host)};h&&v||(h=function(t){var e=[],n=1;while(arguments.length>n)e.push(arguments[n++]);return _[++b]=function(){(\"function\"==typeof t?t:Function(t)).apply(void 0,e)},r(b),b},v=function(t){delete _[t]},\"process\"==c(y)?r=function(t){y.nextTick(C(t))}:g&&g.now?r=function(t){g.now(C(t))}:m&&!p?(o=new m,i=o.port2,o.port1.onmessage=O,r=u(i.postMessage,i,1)):!a.addEventListener||\"function\"!=typeof postMessage||a.importScripts||s(A)?r=w in l(\"script\")?function(t){f.appendChild(l(\"script\"))[w]=function(){f.removeChild(this),x(t)}}:function(t){setTimeout(C(t),0)}:(r=A,a.addEventListener(\"message\",O,!1))),t.exports={set:h,clear:v}},\"2d00\":function(t,e,n){var r,o,i=n(\"da84\"),a=n(\"342f\"),s=i.process,c=s&&s.versions,u=c&&c.v8;u?(r=u.split(\".\"),o=r[0]+r[1]):a&&(r=a.match(/Edge\\/(\\d+)/),(!r||r[1]>=74)&&(r=a.match(/Chrome\\/(\\d+)/),r&&(o=r[1]))),t.exports=o&&+o},\"2d83\":function(t,e,n){\"use strict\";var r=n(\"387f\");t.exports=function(t,e,n,o,i){var a=new Error(t);return r(a,e,n,o,i)}},\"2e67\":function(t,e,n){\"use strict\";t.exports=function(t){return!(!t||!t.__CANCEL__)}},\"2f62\":function(t,e,n){\"use strict\";(function(t){\n/**\n * vuex v3.1.3\n * (c) 2020 Evan You\n * @license MIT\n */\nfunction n(t){var e=Number(t.version.split(\".\")[0]);if(e>=2)t.mixin({beforeCreate:r});else{var n=t.prototype._init;t.prototype._init=function(t){void 0===t&&(t={}),t.init=t.init?[r].concat(t.init):r,n.call(this,t)}}function r(){var t=this.$options;t.store?this.$store=\"function\"===typeof t.store?t.store():t.store:t.parent&&t.parent.$store&&(this.$store=t.parent.$store)}}var r=\"undefined\"!==typeof window?window:\"undefined\"!==typeof t?t:{},o=r.__VUE_DEVTOOLS_GLOBAL_HOOK__;function i(t){o&&(t._devtoolHook=o,o.emit(\"vuex:init\",t),o.on(\"vuex:travel-to-state\",(function(e){t.replaceState(e)})),t.subscribe((function(t,e){o.emit(\"vuex:mutation\",t,e)})))}function a(t,e){Object.keys(t).forEach((function(n){return e(t[n],n)}))}function s(t){return null!==t&&\"object\"===typeof t}function c(t){return t&&\"function\"===typeof t.then}function u(t,e){return function(){return t(e)}}var f=function(t,e){this.runtime=e,this._children=Object.create(null),this._rawModule=t;var n=t.state;this.state=(\"function\"===typeof n?n():n)||{}},l={namespaced:{configurable:!0}};l.namespaced.get=function(){return!!this._rawModule.namespaced},f.prototype.addChild=function(t,e){this._children[t]=e},f.prototype.removeChild=function(t){delete this._children[t]},f.prototype.getChild=function(t){return this._children[t]},f.prototype.update=function(t){this._rawModule.namespaced=t.namespaced,t.actions&&(this._rawModule.actions=t.actions),t.mutations&&(this._rawModule.mutations=t.mutations),t.getters&&(this._rawModule.getters=t.getters)},f.prototype.forEachChild=function(t){a(this._children,t)},f.prototype.forEachGetter=function(t){this._rawModule.getters&&a(this._rawModule.getters,t)},f.prototype.forEachAction=function(t){this._rawModule.actions&&a(this._rawModule.actions,t)},f.prototype.forEachMutation=function(t){this._rawModule.mutations&&a(this._rawModule.mutations,t)},Object.defineProperties(f.prototype,l);var p=function(t){this.register([],t,!1)};function d(t,e,n){if(e.update(n),n.modules)for(var r in n.modules){if(!e.getChild(r))return void 0;d(t.concat(r),e.getChild(r),n.modules[r])}}p.prototype.get=function(t){return t.reduce((function(t,e){return t.getChild(e)}),this.root)},p.prototype.getNamespace=function(t){var e=this.root;return t.reduce((function(t,n){return e=e.getChild(n),t+(e.namespaced?n+\"/\":\"\")}),\"\")},p.prototype.update=function(t){d([],this.root,t)},p.prototype.register=function(t,e,n){var r=this;void 0===n&&(n=!0);var o=new f(e,n);if(0===t.length)this.root=o;else{var i=this.get(t.slice(0,-1));i.addChild(t[t.length-1],o)}e.modules&&a(e.modules,(function(e,o){r.register(t.concat(o),e,n)}))},p.prototype.unregister=function(t){var e=this.get(t.slice(0,-1)),n=t[t.length-1];e.getChild(n).runtime&&e.removeChild(n)};var h;var v=function(t){var e=this;void 0===t&&(t={}),!h&&\"undefined\"!==typeof window&&window.Vue&&$(window.Vue);var n=t.plugins;void 0===n&&(n=[]);var r=t.strict;void 0===r&&(r=!1),this._committing=!1,this._actions=Object.create(null),this._actionSubscribers=[],this._mutations=Object.create(null),this._wrappedGetters=Object.create(null),this._modules=new p(t),this._modulesNamespaceMap=Object.create(null),this._subscribers=[],this._watcherVM=new h,this._makeLocalGettersCache=Object.create(null);var o=this,a=this,s=a.dispatch,c=a.commit;this.dispatch=function(t,e){return s.call(o,t,e)},this.commit=function(t,e,n){return c.call(o,t,e,n)},this.strict=r;var u=this._modules.root.state;_(this,u,[],this._modules.root),b(this,u),n.forEach((function(t){return t(e)}));var f=void 0!==t.devtools?t.devtools:h.config.devtools;f&&i(this)},y={state:{configurable:!0}};function m(t,e){return e.indexOf(t)<0&&e.push(t),function(){var n=e.indexOf(t);n>-1&&e.splice(n,1)}}function g(t,e){t._actions=Object.create(null),t._mutations=Object.create(null),t._wrappedGetters=Object.create(null),t._modulesNamespaceMap=Object.create(null);var n=t.state;_(t,n,[],t._modules.root,!0),b(t,n,e)}function b(t,e,n){var r=t._vm;t.getters={},t._makeLocalGettersCache=Object.create(null);var o=t._wrappedGetters,i={};a(o,(function(e,n){i[n]=u(e,t),Object.defineProperty(t.getters,n,{get:function(){return t._vm[n]},enumerable:!0})}));var s=h.config.silent;h.config.silent=!0,t._vm=new h({data:{$$state:e},computed:i}),h.config.silent=s,t.strict&&k(t),r&&(n&&t._withCommit((function(){r._data.$$state=null})),h.nextTick((function(){return r.$destroy()})))}function _(t,e,n,r,o){var i=!n.length,a=t._modules.getNamespace(n);if(r.namespaced&&(t._modulesNamespaceMap[a],t._modulesNamespaceMap[a]=r),!i&&!o){var s=j(e,n.slice(0,-1)),c=n[n.length-1];t._withCommit((function(){h.set(s,c,r.state)}))}var u=r.context=w(t,a,n);r.forEachMutation((function(e,n){var r=a+n;C(t,r,e,u)})),r.forEachAction((function(e,n){var r=e.root?n:a+n,o=e.handler||e;O(t,r,o,u)})),r.forEachGetter((function(e,n){var r=a+n;A(t,r,e,u)})),r.forEachChild((function(r,i){_(t,e,n.concat(i),r,o)}))}function w(t,e,n){var r=\"\"===e,o={dispatch:r?t.dispatch:function(n,r,o){var i=S(n,r,o),a=i.payload,s=i.options,c=i.type;return s&&s.root||(c=e+c),t.dispatch(c,a)},commit:r?t.commit:function(n,r,o){var i=S(n,r,o),a=i.payload,s=i.options,c=i.type;s&&s.root||(c=e+c),t.commit(c,a,s)}};return Object.defineProperties(o,{getters:{get:r?function(){return t.getters}:function(){return x(t,e)}},state:{get:function(){return j(t.state,n)}}}),o}function x(t,e){if(!t._makeLocalGettersCache[e]){var n={},r=e.length;Object.keys(t.getters).forEach((function(o){if(o.slice(0,r)===e){var i=o.slice(r);Object.defineProperty(n,i,{get:function(){return t.getters[o]},enumerable:!0})}})),t._makeLocalGettersCache[e]=n}return t._makeLocalGettersCache[e]}function C(t,e,n,r){var o=t._mutations[e]||(t._mutations[e]=[]);o.push((function(e){n.call(t,r.state,e)}))}function O(t,e,n,r){var o=t._actions[e]||(t._actions[e]=[]);o.push((function(e){var o=n.call(t,{dispatch:r.dispatch,commit:r.commit,getters:r.getters,state:r.state,rootGetters:t.getters,rootState:t.state},e);return c(o)||(o=Promise.resolve(o)),t._devtoolHook?o.catch((function(e){throw t._devtoolHook.emit(\"vuex:error\",e),e})):o}))}function A(t,e,n,r){t._wrappedGetters[e]||(t._wrappedGetters[e]=function(t){return n(r.state,r.getters,t.state,t.getters)})}function k(t){t._vm.$watch((function(){return this._data.$$state}),(function(){0}),{deep:!0,sync:!0})}function j(t,e){return e.reduce((function(t,e){return t[e]}),t)}function S(t,e,n){return s(t)&&t.type&&(n=e,e=t,t=t.type),{type:t,payload:e,options:n}}function $(t){h&&t===h||(h=t,n(h))}y.state.get=function(){return this._vm._data.$$state},y.state.set=function(t){0},v.prototype.commit=function(t,e,n){var r=this,o=S(t,e,n),i=o.type,a=o.payload,s=(o.options,{type:i,payload:a}),c=this._mutations[i];c&&(this._withCommit((function(){c.forEach((function(t){t(a)}))})),this._subscribers.slice().forEach((function(t){return t(s,r.state)})))},v.prototype.dispatch=function(t,e){var n=this,r=S(t,e),o=r.type,i=r.payload,a={type:o,payload:i},s=this._actions[o];if(s){try{this._actionSubscribers.slice().filter((function(t){return t.before})).forEach((function(t){return t.before(a,n.state)}))}catch(u){0}var c=s.length>1?Promise.all(s.map((function(t){return t(i)}))):s[0](i);return c.then((function(t){try{n._actionSubscribers.filter((function(t){return t.after})).forEach((function(t){return t.after(a,n.state)}))}catch(u){0}return t}))}},v.prototype.subscribe=function(t){return m(t,this._subscribers)},v.prototype.subscribeAction=function(t){var e=\"function\"===typeof t?{before:t}:t;return m(e,this._actionSubscribers)},v.prototype.watch=function(t,e,n){var r=this;return this._watcherVM.$watch((function(){return t(r.state,r.getters)}),e,n)},v.prototype.replaceState=function(t){var e=this;this._withCommit((function(){e._vm._data.$$state=t}))},v.prototype.registerModule=function(t,e,n){void 0===n&&(n={}),\"string\"===typeof t&&(t=[t]),this._modules.register(t,e),_(this,this.state,t,this._modules.get(t),n.preserveState),b(this,this.state)},v.prototype.unregisterModule=function(t){var e=this;\"string\"===typeof t&&(t=[t]),this._modules.unregister(t),this._withCommit((function(){var n=j(e.state,t.slice(0,-1));h.delete(n,t[t.length-1])})),g(this)},v.prototype.hotUpdate=function(t){this._modules.update(t),g(this,!0)},v.prototype._withCommit=function(t){var e=this._committing;this._committing=!0,t(),this._committing=e},Object.defineProperties(v.prototype,y);var E=L((function(t,e){var n={};return M(e).forEach((function(e){var r=e.key,o=e.val;n[r]=function(){var e=this.$store.state,n=this.$store.getters;if(t){var r=D(this.$store,\"mapState\",t);if(!r)return;e=r.context.state,n=r.context.getters}return\"function\"===typeof o?o.call(this,e,n):e[o]},n[r].vuex=!0})),n})),T=L((function(t,e){var n={};return M(e).forEach((function(e){var r=e.key,o=e.val;n[r]=function(){var e=[],n=arguments.length;while(n--)e[n]=arguments[n];var r=this.$store.commit;if(t){var i=D(this.$store,\"mapMutations\",t);if(!i)return;r=i.context.commit}return\"function\"===typeof o?o.apply(this,[r].concat(e)):r.apply(this.$store,[o].concat(e))}})),n})),P=L((function(t,e){var n={};return M(e).forEach((function(e){var r=e.key,o=e.val;o=t+o,n[r]=function(){if(!t||D(this.$store,\"mapGetters\",t))return this.$store.getters[o]},n[r].vuex=!0})),n})),R=L((function(t,e){var n={};return M(e).forEach((function(e){var r=e.key,o=e.val;n[r]=function(){var e=[],n=arguments.length;while(n--)e[n]=arguments[n];var r=this.$store.dispatch;if(t){var i=D(this.$store,\"mapActions\",t);if(!i)return;r=i.context.dispatch}return\"function\"===typeof o?o.apply(this,[r].concat(e)):r.apply(this.$store,[o].concat(e))}})),n})),I=function(t){return{mapState:E.bind(null,t),mapGetters:P.bind(null,t),mapMutations:T.bind(null,t),mapActions:R.bind(null,t)}};function M(t){return N(t)?Array.isArray(t)?t.map((function(t){return{key:t,val:t}})):Object.keys(t).map((function(e){return{key:e,val:t[e]}})):[]}function N(t){return Array.isArray(t)||s(t)}function L(t){return function(e,n){return\"string\"!==typeof e?(n=e,e=\"\"):\"/\"!==e.charAt(e.length-1)&&(e+=\"/\"),t(e,n)}}function D(t,e,n){var r=t._modulesNamespaceMap[n];return r}var F={Store:v,install:$,version:\"3.1.3\",mapState:E,mapMutations:T,mapGetters:P,mapActions:R,createNamespacedHelpers:I};e[\"a\"]=F}).call(this,n(\"c8ba\"))},\"30b5\":function(t,e,n){\"use strict\";var r=n(\"c532\");function o(t){return encodeURIComponent(t).replace(/%40/gi,\"@\").replace(/%3A/gi,\":\").replace(/%24/g,\"$\").replace(/%2C/gi,\",\").replace(/%20/g,\"+\").replace(/%5B/gi,\"[\").replace(/%5D/gi,\"]\")}t.exports=function(t,e,n){if(!e)return t;var i;if(n)i=n(e);else if(r.isURLSearchParams(e))i=e.toString();else{var a=[];r.forEach(e,(function(t,e){null!==t&&\"undefined\"!==typeof t&&(r.isArray(t)?e+=\"[]\":t=[t],r.forEach(t,(function(t){r.isDate(t)?t=t.toISOString():r.isObject(t)&&(t=JSON.stringify(t)),a.push(o(e)+\"=\"+o(t))})))})),i=a.join(\"&\")}if(i){var s=t.indexOf(\"#\");-1!==s&&(t=t.slice(0,s)),t+=(-1===t.indexOf(\"?\")?\"?\":\"&\")+i}return t}},\"342f\":function(t,e,n){var r=n(\"d066\");t.exports=r(\"navigator\",\"userAgent\")||\"\"},\"35a1\":function(t,e,n){var r=n(\"f5df\"),o=n(\"3f8c\"),i=n(\"b622\"),a=i(\"iterator\");t.exports=function(t){if(void 0!=t)return t[a]||t[\"@@iterator\"]||o[r(t)]}},\"37e8\":function(t,e,n){var r=n(\"83ab\"),o=n(\"9bf2\"),i=n(\"825a\"),a=n(\"df75\");t.exports=r?Object.defineProperties:function(t,e){i(t);var n,r=a(e),s=r.length,c=0;while(s>c)o.f(t,n=r[c++],e[n]);return t}},\"387f\":function(t,e,n){\"use strict\";t.exports=function(t,e,n,r,o){return t.config=e,n&&(t.code=n),t.request=r,t.response=o,t.isAxiosError=!0,t.toJSON=function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:this.config,code:this.code}},t}},3934:function(t,e,n){\"use strict\";var r=n(\"c532\");t.exports=r.isStandardBrowserEnv()?function(){var t,e=/(msie|trident)/i.test(navigator.userAgent),n=document.createElement(\"a\");function o(t){var r=t;return e&&(n.setAttribute(\"href\",r),r=n.href),n.setAttribute(\"href\",r),{href:n.href,protocol:n.protocol?n.protocol.replace(/:$/,\"\"):\"\",host:n.host,search:n.search?n.search.replace(/^\\?/,\"\"):\"\",hash:n.hash?n.hash.replace(/^#/,\"\"):\"\",hostname:n.hostname,port:n.port,pathname:\"/\"===n.pathname.charAt(0)?n.pathname:\"/\"+n.pathname}}return t=o(window.location.href),function(e){var n=r.isString(e)?o(e):e;return n.protocol===t.protocol&&n.host===t.host}}():function(){return function(){return!0}}()},\"3bbe\":function(t,e,n){var r=n(\"861d\");t.exports=function(t){if(!r(t)&&null!==t)throw TypeError(\"Can't set \"+String(t)+\" as a prototype\");return t}},\"3f8c\":function(t,e){t.exports={}},\"428f\":function(t,e,n){var r=n(\"da84\");t.exports=r},4362:function(t,e,n){e.nextTick=function(t){var e=Array.prototype.slice.call(arguments);e.shift(),setTimeout((function(){t.apply(null,e)}),0)},e.platform=e.arch=e.execPath=e.title=\"browser\",e.pid=1,e.browser=!0,e.env={},e.argv=[],e.binding=function(t){throw new Error(\"No such module. (Possibly not yet loaded)\")},function(){var t,r=\"/\";e.cwd=function(){return r},e.chdir=function(e){t||(t=n(\"df7c\")),r=t.resolve(e,r)}}(),e.exit=e.kill=e.umask=e.dlopen=e.uptime=e.memoryUsage=e.uvCounters=function(){},e.features={}},\"44ad\":function(t,e,n){var r=n(\"d039\"),o=n(\"c6b6\"),i=\"\".split;t.exports=r((function(){return!Object(\"z\").propertyIsEnumerable(0)}))?function(t){return\"String\"==o(t)?i.call(t,\"\"):Object(t)}:Object},\"44d2\":function(t,e,n){var r=n(\"b622\"),o=n(\"7c73\"),i=n(\"9bf2\"),a=r(\"unscopables\"),s=Array.prototype;void 0==s[a]&&i.f(s,a,{configurable:!0,value:o(null)}),t.exports=function(t){s[a][t]=!0}},\"44de\":function(t,e,n){var r=n(\"da84\");t.exports=function(t,e){var n=r.console;n&&n.error&&(1===arguments.length?n.error(t):n.error(t,e))}},\"467f\":function(t,e,n){\"use strict\";var r=n(\"2d83\");t.exports=function(t,e,n){var o=n.config.validateStatus;!o||o(n.status)?t(n):e(r(\"Request failed with status code \"+n.status,n.config,null,n.request,n))}},4840:function(t,e,n){var r=n(\"825a\"),o=n(\"1c0b\"),i=n(\"b622\"),a=i(\"species\");t.exports=function(t,e){var n,i=r(t).constructor;return void 0===i||void 0==(n=r(i)[a])?e:o(n)}},4930:function(t,e,n){var r=n(\"d039\");t.exports=!!Object.getOwnPropertySymbols&&!r((function(){return!String(Symbol())}))},\"4a7b\":function(t,e,n){\"use strict\";var r=n(\"c532\");t.exports=function(t,e){e=e||{};var n={},o=[\"url\",\"method\",\"params\",\"data\"],i=[\"headers\",\"auth\",\"proxy\"],a=[\"baseURL\",\"url\",\"transformRequest\",\"transformResponse\",\"paramsSerializer\",\"timeout\",\"withCredentials\",\"adapter\",\"responseType\",\"xsrfCookieName\",\"xsrfHeaderName\",\"onUploadProgress\",\"onDownloadProgress\",\"maxContentLength\",\"validateStatus\",\"maxRedirects\",\"httpAgent\",\"httpsAgent\",\"cancelToken\",\"socketPath\"];r.forEach(o,(function(t){\"undefined\"!==typeof e[t]&&(n[t]=e[t])})),r.forEach(i,(function(o){r.isObject(e[o])?n[o]=r.deepMerge(t[o],e[o]):\"undefined\"!==typeof e[o]?n[o]=e[o]:r.isObject(t[o])?n[o]=r.deepMerge(t[o]):\"undefined\"!==typeof t[o]&&(n[o]=t[o])})),r.forEach(a,(function(r){\"undefined\"!==typeof e[r]?n[r]=e[r]:\"undefined\"!==typeof t[r]&&(n[r]=t[r])}));var s=o.concat(i).concat(a),c=Object.keys(e).filter((function(t){return-1===s.indexOf(t)}));return r.forEach(c,(function(r){\"undefined\"!==typeof e[r]?n[r]=e[r]:\"undefined\"!==typeof t[r]&&(n[r]=t[r])})),n}},\"4d64\":function(t,e,n){var r=n(\"fc6a\"),o=n(\"50c4\"),i=n(\"23cb\"),a=function(t){return function(e,n,a){var s,c=r(e),u=o(c.length),f=i(a,u);if(t&&n!=n){while(u>f)if(s=c[f++],s!=s)return!0}else for(;u>f;f++)if((t||f in c)&&c[f]===n)return t||f||0;return!t&&-1}};t.exports={includes:a(!0),indexOf:a(!1)}},\"50c4\":function(t,e,n){var r=n(\"a691\"),o=Math.min;t.exports=function(t){return t>0?o(r(t),9007199254740991):0}},5135:function(t,e){var n={}.hasOwnProperty;t.exports=function(t,e){return n.call(t,e)}},5270:function(t,e,n){\"use strict\";var r=n(\"c532\"),o=n(\"c401\"),i=n(\"2e67\"),a=n(\"2444\");function s(t){t.cancelToken&&t.cancelToken.throwIfRequested()}t.exports=function(t){s(t),t.headers=t.headers||{},t.data=o(t.data,t.headers,t.transformRequest),t.headers=r.merge(t.headers.common||{},t.headers[t.method]||{},t.headers),r.forEach([\"delete\",\"get\",\"head\",\"post\",\"put\",\"patch\",\"common\"],(function(e){delete t.headers[e]}));var e=t.adapter||a.adapter;return e(t).then((function(e){return s(t),e.data=o(e.data,e.headers,t.transformResponse),e}),(function(e){return i(e)||(s(t),e&&e.response&&(e.response.data=o(e.response.data,e.response.headers,t.transformResponse))),Promise.reject(e)}))}},5692:function(t,e,n){var r=n(\"c430\"),o=n(\"c6cd\");(t.exports=function(t,e){return o[t]||(o[t]=void 0!==e?e:{})})(\"versions\",[]).push({version:\"3.6.4\",mode:r?\"pure\":\"global\",copyright:\"© 2020 Denis Pushkarev (zloirock.ru)\"})},\"56ef\":function(t,e,n){var r=n(\"d066\"),o=n(\"241c\"),i=n(\"7418\"),a=n(\"825a\");t.exports=r(\"Reflect\",\"ownKeys\")||function(t){var e=o.f(a(t)),n=i.f;return n?e.concat(n(t)):e}},\"5c6c\":function(t,e){t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},\"60da\":function(t,e,n){\"use strict\";var r=n(\"83ab\"),o=n(\"d039\"),i=n(\"df75\"),a=n(\"7418\"),s=n(\"d1e7\"),c=n(\"7b0b\"),u=n(\"44ad\"),f=Object.assign,l=Object.defineProperty;t.exports=!f||o((function(){if(r&&1!==f({b:1},f(l({},\"a\",{enumerable:!0,get:function(){l(this,\"b\",{value:3,enumerable:!1})}}),{b:2})).b)return!0;var t={},e={},n=Symbol(),o=\"abcdefghijklmnopqrst\";return t[n]=7,o.split(\"\").forEach((function(t){e[t]=t})),7!=f({},t)[n]||i(f({},e)).join(\"\")!=o}))?function(t,e){var n=c(t),o=arguments.length,f=1,l=a.f,p=s.f;while(o>f){var d,h=u(arguments[f++]),v=l?i(h).concat(l(h)):i(h),y=v.length,m=0;while(y>m)d=v[m++],r&&!p.call(h,d)||(n[d]=h[d])}return n}:f},\"69f3\":function(t,e,n){var r,o,i,a=n(\"7f9a\"),s=n(\"da84\"),c=n(\"861d\"),u=n(\"9112\"),f=n(\"5135\"),l=n(\"f772\"),p=n(\"d012\"),d=s.WeakMap,h=function(t){return i(t)?o(t):r(t,{})},v=function(t){return function(e){var n;if(!c(e)||(n=o(e)).type!==t)throw TypeError(\"Incompatible receiver, \"+t+\" required\");return n}};if(a){var y=new d,m=y.get,g=y.has,b=y.set;r=function(t,e){return b.call(y,t,e),e},o=function(t){return m.call(y,t)||{}},i=function(t){return g.call(y,t)}}else{var _=l(\"state\");p[_]=!0,r=function(t,e){return u(t,_,e),e},o=function(t){return f(t,_)?t[_]:{}},i=function(t){return f(t,_)}}t.exports={set:r,get:o,has:i,enforce:h,getterFor:v}},\"6eeb\":function(t,e,n){var r=n(\"da84\"),o=n(\"9112\"),i=n(\"5135\"),a=n(\"ce4e\"),s=n(\"8925\"),c=n(\"69f3\"),u=c.get,f=c.enforce,l=String(String).split(\"String\");(t.exports=function(t,e,n,s){var c=!!s&&!!s.unsafe,u=!!s&&!!s.enumerable,p=!!s&&!!s.noTargetGet;\"function\"==typeof n&&(\"string\"!=typeof e||i(n,\"name\")||o(n,\"name\",e),f(n).source=l.join(\"string\"==typeof e?e:\"\")),t!==r?(c?!p&&t[e]&&(u=!0):delete t[e],u?t[e]=n:o(t,e,n)):u?t[e]=n:a(e,n)})(Function.prototype,\"toString\",(function(){return\"function\"==typeof this&&u(this).source||s(this)}))},7418:function(t,e){e.f=Object.getOwnPropertySymbols},7839:function(t,e){t.exports=[\"constructor\",\"hasOwnProperty\",\"isPrototypeOf\",\"propertyIsEnumerable\",\"toLocaleString\",\"toString\",\"valueOf\"]},\"7a77\":function(t,e,n){\"use strict\";function r(t){this.message=t}r.prototype.toString=function(){return\"Cancel\"+(this.message?\": \"+this.message:\"\")},r.prototype.__CANCEL__=!0,t.exports=r},\"7aac\":function(t,e,n){\"use strict\";var r=n(\"c532\");t.exports=r.isStandardBrowserEnv()?function(){return{write:function(t,e,n,o,i,a){var s=[];s.push(t+\"=\"+encodeURIComponent(e)),r.isNumber(n)&&s.push(\"expires=\"+new Date(n).toGMTString()),r.isString(o)&&s.push(\"path=\"+o),r.isString(i)&&s.push(\"domain=\"+i),!0===a&&s.push(\"secure\"),document.cookie=s.join(\"; \")},read:function(t){var e=document.cookie.match(new RegExp(\"(^|;\\\\s*)(\"+t+\")=([^;]*)\"));return e?decodeURIComponent(e[3]):null},remove:function(t){this.write(t,\"\",Date.now()-864e5)}}}():function(){return{write:function(){},read:function(){return null},remove:function(){}}}()},\"7b0b\":function(t,e,n){var r=n(\"1d80\");t.exports=function(t){return Object(r(t))}},\"7c73\":function(t,e,n){var r,o=n(\"825a\"),i=n(\"37e8\"),a=n(\"7839\"),s=n(\"d012\"),c=n(\"1be4\"),u=n(\"cc12\"),f=n(\"f772\"),l=\">\",p=\"<\",d=\"prototype\",h=\"script\",v=f(\"IE_PROTO\"),y=function(){},m=function(t){return p+h+l+t+p+\"/\"+h+l},g=function(t){t.write(m(\"\")),t.close();var e=t.parentWindow.Object;return t=null,e},b=function(){var t,e=u(\"iframe\"),n=\"java\"+h+\":\";return e.style.display=\"none\",c.appendChild(e),e.src=String(n),t=e.contentWindow.document,t.open(),t.write(m(\"document.F=Object\")),t.close(),t.F},_=function(){try{r=document.domain&&new ActiveXObject(\"htmlfile\")}catch(e){}_=r?g(r):b();var t=a.length;while(t--)delete _[d][a[t]];return _()};s[v]=!0,t.exports=Object.create||function(t,e){var n;return null!==t?(y[d]=o(t),n=new y,y[d]=null,n[v]=t):n=_(),void 0===e?n:i(n,e)}},\"7dd0\":function(t,e,n){\"use strict\";var r=n(\"23e7\"),o=n(\"9ed3\"),i=n(\"e163\"),a=n(\"d2bb\"),s=n(\"d44e\"),c=n(\"9112\"),u=n(\"6eeb\"),f=n(\"b622\"),l=n(\"c430\"),p=n(\"3f8c\"),d=n(\"ae93\"),h=d.IteratorPrototype,v=d.BUGGY_SAFARI_ITERATORS,y=f(\"iterator\"),m=\"keys\",g=\"values\",b=\"entries\",_=function(){return this};t.exports=function(t,e,n,f,d,w,x){o(n,e,f);var C,O,A,k=function(t){if(t===d&&T)return T;if(!v&&t in $)return $[t];switch(t){case m:return function(){return new n(this,t)};case g:return function(){return new n(this,t)};case b:return function(){return new n(this,t)}}return function(){return new n(this)}},j=e+\" Iterator\",S=!1,$=t.prototype,E=$[y]||$[\"@@iterator\"]||d&&$[d],T=!v&&E||k(d),P=\"Array\"==e&&$.entries||E;if(P&&(C=i(P.call(new t)),h!==Object.prototype&&C.next&&(l||i(C)===h||(a?a(C,h):\"function\"!=typeof C[y]&&c(C,y,_)),s(C,j,!0,!0),l&&(p[j]=_))),d==g&&E&&E.name!==g&&(S=!0,T=function(){return E.call(this)}),l&&!x||$[y]===T||c($,y,T),p[e]=T,d)if(O={values:k(g),keys:w?T:k(m),entries:k(b)},x)for(A in O)(v||S||!(A in $))&&u($,A,O[A]);else r({target:e,proto:!0,forced:v||S},O);return O}},\"7f9a\":function(t,e,n){var r=n(\"da84\"),o=n(\"8925\"),i=r.WeakMap;t.exports=\"function\"===typeof i&&/native code/.test(o(i))},\"825a\":function(t,e,n){var r=n(\"861d\");t.exports=function(t){if(!r(t))throw TypeError(String(t)+\" is not an object\");return t}},\"83ab\":function(t,e,n){var r=n(\"d039\");t.exports=!r((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]}))},\"83b9\":function(t,e,n){\"use strict\";var r=n(\"d925\"),o=n(\"e683\");t.exports=function(t,e){return t&&!r(e)?o(t,e):e}},\"861d\":function(t,e){t.exports=function(t){return\"object\"===typeof t?null!==t:\"function\"===typeof t}},8925:function(t,e,n){var r=n(\"c6cd\"),o=Function.toString;\"function\"!=typeof r.inspectSource&&(r.inspectSource=function(t){return o.call(t)}),t.exports=r.inspectSource},\"8c4f\":function(t,e,n){\"use strict\";\n/*!\n  * vue-router v3.1.6\n  * (c) 2020 Evan You\n  * @license MIT\n  */function r(t,e){0}function o(t){return Object.prototype.toString.call(t).indexOf(\"Error\")>-1}function i(t,e){return e instanceof t||e&&(e.name===t.name||e._name===t._name)}function a(t,e){for(var n in e)t[n]=e[n];return t}var s={name:\"RouterView\",functional:!0,props:{name:{type:String,default:\"default\"}},render:function(t,e){var n=e.props,r=e.children,o=e.parent,i=e.data;i.routerView=!0;var s=o.$createElement,u=n.name,f=o.$route,l=o._routerViewCache||(o._routerViewCache={}),p=0,d=!1;while(o&&o._routerRoot!==o){var h=o.$vnode?o.$vnode.data:{};h.routerView&&p++,h.keepAlive&&o._directInactive&&o._inactive&&(d=!0),o=o.$parent}if(i.routerViewDepth=p,d){var v=l[u],y=v&&v.component;return y?(v.configProps&&c(y,i,v.route,v.configProps),s(y,i,r)):s()}var m=f.matched[p],g=m&&m.components[u];if(!m||!g)return l[u]=null,s();l[u]={component:g},i.registerRouteInstance=function(t,e){var n=m.instances[u];(e&&n!==t||!e&&n===t)&&(m.instances[u]=e)},(i.hook||(i.hook={})).prepatch=function(t,e){m.instances[u]=e.componentInstance},i.hook.init=function(t){t.data.keepAlive&&t.componentInstance&&t.componentInstance!==m.instances[u]&&(m.instances[u]=t.componentInstance)};var b=m.props&&m.props[u];return b&&(a(l[u],{route:f,configProps:b}),c(g,i,f,b)),s(g,i,r)}};function c(t,e,n,r){var o=e.props=u(n,r);if(o){o=e.props=a({},o);var i=e.attrs=e.attrs||{};for(var s in o)t.props&&s in t.props||(i[s]=o[s],delete o[s])}}function u(t,e){switch(typeof e){case\"undefined\":return;case\"object\":return e;case\"function\":return e(t);case\"boolean\":return e?t.params:void 0;default:0}}var f=/[!'()*]/g,l=function(t){return\"%\"+t.charCodeAt(0).toString(16)},p=/%2C/g,d=function(t){return encodeURIComponent(t).replace(f,l).replace(p,\",\")},h=decodeURIComponent;function v(t,e,n){void 0===e&&(e={});var r,o=n||y;try{r=o(t||\"\")}catch(a){r={}}for(var i in e)r[i]=e[i];return r}function y(t){var e={};return t=t.trim().replace(/^(\\?|#|&)/,\"\"),t?(t.split(\"&\").forEach((function(t){var n=t.replace(/\\+/g,\" \").split(\"=\"),r=h(n.shift()),o=n.length>0?h(n.join(\"=\")):null;void 0===e[r]?e[r]=o:Array.isArray(e[r])?e[r].push(o):e[r]=[e[r],o]})),e):e}function m(t){var e=t?Object.keys(t).map((function(e){var n=t[e];if(void 0===n)return\"\";if(null===n)return d(e);if(Array.isArray(n)){var r=[];return n.forEach((function(t){void 0!==t&&(null===t?r.push(d(e)):r.push(d(e)+\"=\"+d(t)))})),r.join(\"&\")}return d(e)+\"=\"+d(n)})).filter((function(t){return t.length>0})).join(\"&\"):null;return e?\"?\"+e:\"\"}var g=/\\/?$/;function b(t,e,n,r){var o=r&&r.options.stringifyQuery,i=e.query||{};try{i=_(i)}catch(s){}var a={name:e.name||t&&t.name,meta:t&&t.meta||{},path:e.path||\"/\",hash:e.hash||\"\",query:i,params:e.params||{},fullPath:C(e,o),matched:t?x(t):[]};return n&&(a.redirectedFrom=C(n,o)),Object.freeze(a)}function _(t){if(Array.isArray(t))return t.map(_);if(t&&\"object\"===typeof t){var e={};for(var n in t)e[n]=_(t[n]);return e}return t}var w=b(null,{path:\"/\"});function x(t){var e=[];while(t)e.unshift(t),t=t.parent;return e}function C(t,e){var n=t.path,r=t.query;void 0===r&&(r={});var o=t.hash;void 0===o&&(o=\"\");var i=e||m;return(n||\"/\")+i(r)+o}function O(t,e){return e===w?t===e:!!e&&(t.path&&e.path?t.path.replace(g,\"\")===e.path.replace(g,\"\")&&t.hash===e.hash&&A(t.query,e.query):!(!t.name||!e.name)&&(t.name===e.name&&t.hash===e.hash&&A(t.query,e.query)&&A(t.params,e.params)))}function A(t,e){if(void 0===t&&(t={}),void 0===e&&(e={}),!t||!e)return t===e;var n=Object.keys(t),r=Object.keys(e);return n.length===r.length&&n.every((function(n){var r=t[n],o=e[n];return\"object\"===typeof r&&\"object\"===typeof o?A(r,o):String(r)===String(o)}))}function k(t,e){return 0===t.path.replace(g,\"/\").indexOf(e.path.replace(g,\"/\"))&&(!e.hash||t.hash===e.hash)&&j(t.query,e.query)}function j(t,e){for(var n in e)if(!(n in t))return!1;return!0}function S(t,e,n){var r=t.charAt(0);if(\"/\"===r)return t;if(\"?\"===r||\"#\"===r)return e+t;var o=e.split(\"/\");n&&o[o.length-1]||o.pop();for(var i=t.replace(/^\\//,\"\").split(\"/\"),a=0;a<i.length;a++){var s=i[a];\"..\"===s?o.pop():\".\"!==s&&o.push(s)}return\"\"!==o[0]&&o.unshift(\"\"),o.join(\"/\")}function $(t){var e=\"\",n=\"\",r=t.indexOf(\"#\");r>=0&&(e=t.slice(r),t=t.slice(0,r));var o=t.indexOf(\"?\");return o>=0&&(n=t.slice(o+1),t=t.slice(0,o)),{path:t,query:n,hash:e}}function E(t){return t.replace(/\\/\\//g,\"/\")}var T=Array.isArray||function(t){return\"[object Array]\"==Object.prototype.toString.call(t)},P=Y,R=D,I=F,M=q,N=J,L=new RegExp([\"(\\\\\\\\.)\",\"([\\\\/.])?(?:(?:\\\\:(\\\\w+)(?:\\\\(((?:\\\\\\\\.|[^\\\\\\\\()])+)\\\\))?|\\\\(((?:\\\\\\\\.|[^\\\\\\\\()])+)\\\\))([+*?])?|(\\\\*))\"].join(\"|\"),\"g\");function D(t,e){var n,r=[],o=0,i=0,a=\"\",s=e&&e.delimiter||\"/\";while(null!=(n=L.exec(t))){var c=n[0],u=n[1],f=n.index;if(a+=t.slice(i,f),i=f+c.length,u)a+=u[1];else{var l=t[i],p=n[2],d=n[3],h=n[4],v=n[5],y=n[6],m=n[7];a&&(r.push(a),a=\"\");var g=null!=p&&null!=l&&l!==p,b=\"+\"===y||\"*\"===y,_=\"?\"===y||\"*\"===y,w=n[2]||s,x=h||v;r.push({name:d||o++,prefix:p||\"\",delimiter:w,optional:_,repeat:b,partial:g,asterisk:!!m,pattern:x?V(x):m?\".*\":\"[^\"+H(w)+\"]+?\"})}}return i<t.length&&(a+=t.substr(i)),a&&r.push(a),r}function F(t,e){return q(D(t,e))}function U(t){return encodeURI(t).replace(/[\\/?#]/g,(function(t){return\"%\"+t.charCodeAt(0).toString(16).toUpperCase()}))}function B(t){return encodeURI(t).replace(/[?#]/g,(function(t){return\"%\"+t.charCodeAt(0).toString(16).toUpperCase()}))}function q(t){for(var e=new Array(t.length),n=0;n<t.length;n++)\"object\"===typeof t[n]&&(e[n]=new RegExp(\"^(?:\"+t[n].pattern+\")$\"));return function(n,r){for(var o=\"\",i=n||{},a=r||{},s=a.pretty?U:encodeURIComponent,c=0;c<t.length;c++){var u=t[c];if(\"string\"!==typeof u){var f,l=i[u.name];if(null==l){if(u.optional){u.partial&&(o+=u.prefix);continue}throw new TypeError('Expected \"'+u.name+'\" to be defined')}if(T(l)){if(!u.repeat)throw new TypeError('Expected \"'+u.name+'\" to not repeat, but received `'+JSON.stringify(l)+\"`\");if(0===l.length){if(u.optional)continue;throw new TypeError('Expected \"'+u.name+'\" to not be empty')}for(var p=0;p<l.length;p++){if(f=s(l[p]),!e[c].test(f))throw new TypeError('Expected all \"'+u.name+'\" to match \"'+u.pattern+'\", but received `'+JSON.stringify(f)+\"`\");o+=(0===p?u.prefix:u.delimiter)+f}}else{if(f=u.asterisk?B(l):s(l),!e[c].test(f))throw new TypeError('Expected \"'+u.name+'\" to match \"'+u.pattern+'\", but received \"'+f+'\"');o+=u.prefix+f}}else o+=u}return o}}function H(t){return t.replace(/([.+*?=^!:${}()[\\]|\\/\\\\])/g,\"\\\\$1\")}function V(t){return t.replace(/([=!:$\\/()])/g,\"\\\\$1\")}function z(t,e){return t.keys=e,t}function G(t){return t.sensitive?\"\":\"i\"}function W(t,e){var n=t.source.match(/\\((?!\\?)/g);if(n)for(var r=0;r<n.length;r++)e.push({name:r,prefix:null,delimiter:null,optional:!1,repeat:!1,partial:!1,asterisk:!1,pattern:null});return z(t,e)}function K(t,e,n){for(var r=[],o=0;o<t.length;o++)r.push(Y(t[o],e,n).source);var i=new RegExp(\"(?:\"+r.join(\"|\")+\")\",G(n));return z(i,e)}function X(t,e,n){return J(D(t,n),e,n)}function J(t,e,n){T(e)||(n=e||n,e=[]),n=n||{};for(var r=n.strict,o=!1!==n.end,i=\"\",a=0;a<t.length;a++){var s=t[a];if(\"string\"===typeof s)i+=H(s);else{var c=H(s.prefix),u=\"(?:\"+s.pattern+\")\";e.push(s),s.repeat&&(u+=\"(?:\"+c+u+\")*\"),u=s.optional?s.partial?c+\"(\"+u+\")?\":\"(?:\"+c+\"(\"+u+\"))?\":c+\"(\"+u+\")\",i+=u}}var f=H(n.delimiter||\"/\"),l=i.slice(-f.length)===f;return r||(i=(l?i.slice(0,-f.length):i)+\"(?:\"+f+\"(?=$))?\"),i+=o?\"$\":r&&l?\"\":\"(?=\"+f+\"|$)\",z(new RegExp(\"^\"+i,G(n)),e)}function Y(t,e,n){return T(e)||(n=e||n,e=[]),n=n||{},t instanceof RegExp?W(t,e):T(t)?K(t,e,n):X(t,e,n)}P.parse=R,P.compile=I,P.tokensToFunction=M,P.tokensToRegExp=N;var Q=Object.create(null);function Z(t,e,n){e=e||{};try{var r=Q[t]||(Q[t]=P.compile(t));return\"string\"===typeof e.pathMatch&&(e[0]=e.pathMatch),r(e,{pretty:!0})}catch(o){return\"\"}finally{delete e[0]}}function tt(t,e,n,r){var o=\"string\"===typeof t?{path:t}:t;if(o._normalized)return o;if(o.name){o=a({},t);var i=o.params;return i&&\"object\"===typeof i&&(o.params=a({},i)),o}if(!o.path&&o.params&&e){o=a({},o),o._normalized=!0;var s=a(a({},e.params),o.params);if(e.name)o.name=e.name,o.params=s;else if(e.matched.length){var c=e.matched[e.matched.length-1].path;o.path=Z(c,s,\"path \"+e.path)}else 0;return o}var u=$(o.path||\"\"),f=e&&e.path||\"/\",l=u.path?S(u.path,f,n||o.append):f,p=v(u.query,o.query,r&&r.options.parseQuery),d=o.hash||u.hash;return d&&\"#\"!==d.charAt(0)&&(d=\"#\"+d),{_normalized:!0,path:l,query:p,hash:d}}var et,nt=[String,Object],rt=[String,Array],ot=function(){},it={name:\"RouterLink\",props:{to:{type:nt,required:!0},tag:{type:String,default:\"a\"},exact:Boolean,append:Boolean,replace:Boolean,activeClass:String,exactActiveClass:String,event:{type:rt,default:\"click\"}},render:function(t){var e=this,n=this.$router,r=this.$route,o=n.resolve(this.to,r,this.append),i=o.location,s=o.route,c=o.href,u={},f=n.options.linkActiveClass,l=n.options.linkExactActiveClass,p=null==f?\"router-link-active\":f,d=null==l?\"router-link-exact-active\":l,h=null==this.activeClass?p:this.activeClass,v=null==this.exactActiveClass?d:this.exactActiveClass,y=s.redirectedFrom?b(null,tt(s.redirectedFrom),null,n):s;u[v]=O(r,y),u[h]=this.exact?u[v]:k(r,y);var m=function(t){at(t)&&(e.replace?n.replace(i,ot):n.push(i,ot))},g={click:at};Array.isArray(this.event)?this.event.forEach((function(t){g[t]=m})):g[this.event]=m;var _={class:u},w=!this.$scopedSlots.$hasNormal&&this.$scopedSlots.default&&this.$scopedSlots.default({href:c,route:s,navigate:m,isActive:u[h],isExactActive:u[v]});if(w){if(1===w.length)return w[0];if(w.length>1||!w.length)return 0===w.length?t():t(\"span\",{},w)}if(\"a\"===this.tag)_.on=g,_.attrs={href:c};else{var x=st(this.$slots.default);if(x){x.isStatic=!1;var C=x.data=a({},x.data);for(var A in C.on=C.on||{},C.on){var j=C.on[A];A in g&&(C.on[A]=Array.isArray(j)?j:[j])}for(var S in g)S in C.on?C.on[S].push(g[S]):C.on[S]=m;var $=x.data.attrs=a({},x.data.attrs);$.href=c}else _.on=g}return t(this.tag,_,this.$slots.default)}};function at(t){if(!(t.metaKey||t.altKey||t.ctrlKey||t.shiftKey)&&!t.defaultPrevented&&(void 0===t.button||0===t.button)){if(t.currentTarget&&t.currentTarget.getAttribute){var e=t.currentTarget.getAttribute(\"target\");if(/\\b_blank\\b/i.test(e))return}return t.preventDefault&&t.preventDefault(),!0}}function st(t){if(t)for(var e,n=0;n<t.length;n++){if(e=t[n],\"a\"===e.tag)return e;if(e.children&&(e=st(e.children)))return e}}function ct(t){if(!ct.installed||et!==t){ct.installed=!0,et=t;var e=function(t){return void 0!==t},n=function(t,n){var r=t.$options._parentVnode;e(r)&&e(r=r.data)&&e(r=r.registerRouteInstance)&&r(t,n)};t.mixin({beforeCreate:function(){e(this.$options.router)?(this._routerRoot=this,this._router=this.$options.router,this._router.init(this),t.util.defineReactive(this,\"_route\",this._router.history.current)):this._routerRoot=this.$parent&&this.$parent._routerRoot||this,n(this,this)},destroyed:function(){n(this)}}),Object.defineProperty(t.prototype,\"$router\",{get:function(){return this._routerRoot._router}}),Object.defineProperty(t.prototype,\"$route\",{get:function(){return this._routerRoot._route}}),t.component(\"RouterView\",s),t.component(\"RouterLink\",it);var r=t.config.optionMergeStrategies;r.beforeRouteEnter=r.beforeRouteLeave=r.beforeRouteUpdate=r.created}}var ut=\"undefined\"!==typeof window;function ft(t,e,n,r){var o=e||[],i=n||Object.create(null),a=r||Object.create(null);t.forEach((function(t){lt(o,i,a,t)}));for(var s=0,c=o.length;s<c;s++)\"*\"===o[s]&&(o.push(o.splice(s,1)[0]),c--,s--);return{pathList:o,pathMap:i,nameMap:a}}function lt(t,e,n,r,o,i){var a=r.path,s=r.name;var c=r.pathToRegexpOptions||{},u=dt(a,o,c.strict);\"boolean\"===typeof r.caseSensitive&&(c.sensitive=r.caseSensitive);var f={path:u,regex:pt(u,c),components:r.components||{default:r.component},instances:{},name:s,parent:o,matchAs:i,redirect:r.redirect,beforeEnter:r.beforeEnter,meta:r.meta||{},props:null==r.props?{}:r.components?r.props:{default:r.props}};if(r.children&&r.children.forEach((function(r){var o=i?E(i+\"/\"+r.path):void 0;lt(t,e,n,r,f,o)})),e[f.path]||(t.push(f.path),e[f.path]=f),void 0!==r.alias)for(var l=Array.isArray(r.alias)?r.alias:[r.alias],p=0;p<l.length;++p){var d=l[p];0;var h={path:d,children:r.children};lt(t,e,n,h,o,f.path||\"/\")}s&&(n[s]||(n[s]=f))}function pt(t,e){var n=P(t,[],e);return n}function dt(t,e,n){return n||(t=t.replace(/\\/$/,\"\")),\"/\"===t[0]||null==e?t:E(e.path+\"/\"+t)}function ht(t,e){var n=ft(t),r=n.pathList,o=n.pathMap,i=n.nameMap;function a(t){ft(t,r,o,i)}function s(t,n,a){var s=tt(t,n,!1,e),c=s.name;if(c){var u=i[c];if(!u)return f(null,s);var l=u.regex.keys.filter((function(t){return!t.optional})).map((function(t){return t.name}));if(\"object\"!==typeof s.params&&(s.params={}),n&&\"object\"===typeof n.params)for(var p in n.params)!(p in s.params)&&l.indexOf(p)>-1&&(s.params[p]=n.params[p]);return s.path=Z(u.path,s.params,'named route \"'+c+'\"'),f(u,s,a)}if(s.path){s.params={};for(var d=0;d<r.length;d++){var h=r[d],v=o[h];if(vt(v.regex,s.path,s.params))return f(v,s,a)}}return f(null,s)}function c(t,n){var r=t.redirect,o=\"function\"===typeof r?r(b(t,n,null,e)):r;if(\"string\"===typeof o&&(o={path:o}),!o||\"object\"!==typeof o)return f(null,n);var a=o,c=a.name,u=a.path,l=n.query,p=n.hash,d=n.params;if(l=a.hasOwnProperty(\"query\")?a.query:l,p=a.hasOwnProperty(\"hash\")?a.hash:p,d=a.hasOwnProperty(\"params\")?a.params:d,c){i[c];return s({_normalized:!0,name:c,query:l,hash:p,params:d},void 0,n)}if(u){var h=yt(u,t),v=Z(h,d,'redirect route with path \"'+h+'\"');return s({_normalized:!0,path:v,query:l,hash:p},void 0,n)}return f(null,n)}function u(t,e,n){var r=Z(n,e.params,'aliased route with path \"'+n+'\"'),o=s({_normalized:!0,path:r});if(o){var i=o.matched,a=i[i.length-1];return e.params=o.params,f(a,e)}return f(null,e)}function f(t,n,r){return t&&t.redirect?c(t,r||n):t&&t.matchAs?u(t,n,t.matchAs):b(t,n,r,e)}return{match:s,addRoutes:a}}function vt(t,e,n){var r=e.match(t);if(!r)return!1;if(!n)return!0;for(var o=1,i=r.length;o<i;++o){var a=t.keys[o-1],s=\"string\"===typeof r[o]?decodeURIComponent(r[o]):r[o];a&&(n[a.name||\"pathMatch\"]=s)}return!0}function yt(t,e){return S(t,e.parent?e.parent.path:\"/\",!0)}var mt=ut&&window.performance&&window.performance.now?window.performance:Date;function gt(){return mt.now().toFixed(3)}var bt=gt();function _t(){return bt}function wt(t){return bt=t}var xt=Object.create(null);function Ct(){var t=window.location.protocol+\"//\"+window.location.host,e=window.location.href.replace(t,\"\"),n=a({},window.history.state);n.key=_t(),window.history.replaceState(n,\"\",e),window.addEventListener(\"popstate\",(function(t){At(),t.state&&t.state.key&&wt(t.state.key)}))}function Ot(t,e,n,r){if(t.app){var o=t.options.scrollBehavior;o&&t.app.$nextTick((function(){var i=kt(),a=o.call(t,e,n,r?i:null);a&&(\"function\"===typeof a.then?a.then((function(t){Rt(t,i)})).catch((function(t){0})):Rt(a,i))}))}}function At(){var t=_t();t&&(xt[t]={x:window.pageXOffset,y:window.pageYOffset})}function kt(){var t=_t();if(t)return xt[t]}function jt(t,e){var n=document.documentElement,r=n.getBoundingClientRect(),o=t.getBoundingClientRect();return{x:o.left-r.left-e.x,y:o.top-r.top-e.y}}function St(t){return Tt(t.x)||Tt(t.y)}function $t(t){return{x:Tt(t.x)?t.x:window.pageXOffset,y:Tt(t.y)?t.y:window.pageYOffset}}function Et(t){return{x:Tt(t.x)?t.x:0,y:Tt(t.y)?t.y:0}}function Tt(t){return\"number\"===typeof t}var Pt=/^#\\d/;function Rt(t,e){var n=\"object\"===typeof t;if(n&&\"string\"===typeof t.selector){var r=Pt.test(t.selector)?document.getElementById(t.selector.slice(1)):document.querySelector(t.selector);if(r){var o=t.offset&&\"object\"===typeof t.offset?t.offset:{};o=Et(o),e=jt(r,o)}else St(t)&&(e=$t(t))}else n&&St(t)&&(e=$t(t));e&&window.scrollTo(e.x,e.y)}var It=ut&&function(){var t=window.navigator.userAgent;return(-1===t.indexOf(\"Android 2.\")&&-1===t.indexOf(\"Android 4.0\")||-1===t.indexOf(\"Mobile Safari\")||-1!==t.indexOf(\"Chrome\")||-1!==t.indexOf(\"Windows Phone\"))&&(window.history&&\"pushState\"in window.history)}();function Mt(t,e){At();var n=window.history;try{if(e){var r=a({},n.state);r.key=_t(),n.replaceState(r,\"\",t)}else n.pushState({key:wt(gt())},\"\",t)}catch(o){window.location[e?\"replace\":\"assign\"](t)}}function Nt(t){Mt(t,!0)}function Lt(t,e,n){var r=function(o){o>=t.length?n():t[o]?e(t[o],(function(){r(o+1)})):r(o+1)};r(0)}function Dt(t){return function(e,n,r){var i=!1,a=0,s=null;Ft(t,(function(t,e,n,c){if(\"function\"===typeof t&&void 0===t.cid){i=!0,a++;var u,f=Ht((function(e){qt(e)&&(e=e.default),t.resolved=\"function\"===typeof e?e:et.extend(e),n.components[c]=e,a--,a<=0&&r()})),l=Ht((function(t){var e=\"Failed to resolve async component \"+c+\": \"+t;s||(s=o(t)?t:new Error(e),r(s))}));try{u=t(f,l)}catch(d){l(d)}if(u)if(\"function\"===typeof u.then)u.then(f,l);else{var p=u.component;p&&\"function\"===typeof p.then&&p.then(f,l)}}})),i||r()}}function Ft(t,e){return Ut(t.map((function(t){return Object.keys(t.components).map((function(n){return e(t.components[n],t.instances[n],t,n)}))})))}function Ut(t){return Array.prototype.concat.apply([],t)}var Bt=\"function\"===typeof Symbol&&\"symbol\"===typeof Symbol.toStringTag;function qt(t){return t.__esModule||Bt&&\"Module\"===t[Symbol.toStringTag]}function Ht(t){var e=!1;return function(){var n=[],r=arguments.length;while(r--)n[r]=arguments[r];if(!e)return e=!0,t.apply(this,n)}}var Vt=function(t){function e(e){t.call(this),this.name=this._name=\"NavigationDuplicated\",this.message='Navigating to current location (\"'+e.fullPath+'\") is not allowed',Object.defineProperty(this,\"stack\",{value:(new t).stack,writable:!0,configurable:!0})}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e}(Error);Vt._name=\"NavigationDuplicated\";var zt=function(t,e){this.router=t,this.base=Gt(e),this.current=w,this.pending=null,this.ready=!1,this.readyCbs=[],this.readyErrorCbs=[],this.errorCbs=[]};function Gt(t){if(!t)if(ut){var e=document.querySelector(\"base\");t=e&&e.getAttribute(\"href\")||\"/\",t=t.replace(/^https?:\\/\\/[^\\/]+/,\"\")}else t=\"/\";return\"/\"!==t.charAt(0)&&(t=\"/\"+t),t.replace(/\\/$/,\"\")}function Wt(t,e){var n,r=Math.max(t.length,e.length);for(n=0;n<r;n++)if(t[n]!==e[n])break;return{updated:e.slice(0,n),activated:e.slice(n),deactivated:t.slice(n)}}function Kt(t,e,n,r){var o=Ft(t,(function(t,r,o,i){var a=Xt(t,e);if(a)return Array.isArray(a)?a.map((function(t){return n(t,r,o,i)})):n(a,r,o,i)}));return Ut(r?o.reverse():o)}function Xt(t,e){return\"function\"!==typeof t&&(t=et.extend(t)),t.options[e]}function Jt(t){return Kt(t,\"beforeRouteLeave\",Qt,!0)}function Yt(t){return Kt(t,\"beforeRouteUpdate\",Qt)}function Qt(t,e){if(e)return function(){return t.apply(e,arguments)}}function Zt(t,e,n){return Kt(t,\"beforeRouteEnter\",(function(t,r,o,i){return te(t,o,i,e,n)}))}function te(t,e,n,r,o){return function(i,a,s){return t(i,a,(function(t){\"function\"===typeof t&&r.push((function(){ee(t,e.instances,n,o)})),s(t)}))}}function ee(t,e,n,r){e[n]&&!e[n]._isBeingDestroyed?t(e[n]):r()&&setTimeout((function(){ee(t,e,n,r)}),16)}zt.prototype.listen=function(t){this.cb=t},zt.prototype.onReady=function(t,e){this.ready?t():(this.readyCbs.push(t),e&&this.readyErrorCbs.push(e))},zt.prototype.onError=function(t){this.errorCbs.push(t)},zt.prototype.transitionTo=function(t,e,n){var r=this,o=this.router.match(t,this.current);this.confirmTransition(o,(function(){r.updateRoute(o),e&&e(o),r.ensureURL(),r.ready||(r.ready=!0,r.readyCbs.forEach((function(t){t(o)})))}),(function(t){n&&n(t),t&&!r.ready&&(r.ready=!0,r.readyErrorCbs.forEach((function(e){e(t)})))}))},zt.prototype.confirmTransition=function(t,e,n){var a=this,s=this.current,c=function(t){!i(Vt,t)&&o(t)&&(a.errorCbs.length?a.errorCbs.forEach((function(e){e(t)})):(r(!1,\"uncaught error during route navigation:\"),console.error(t))),n&&n(t)};if(O(t,s)&&t.matched.length===s.matched.length)return this.ensureURL(),c(new Vt(t));var u=Wt(this.current.matched,t.matched),f=u.updated,l=u.deactivated,p=u.activated,d=[].concat(Jt(l),this.router.beforeHooks,Yt(f),p.map((function(t){return t.beforeEnter})),Dt(p));this.pending=t;var h=function(e,n){if(a.pending!==t)return c();try{e(t,s,(function(t){!1===t||o(t)?(a.ensureURL(!0),c(t)):\"string\"===typeof t||\"object\"===typeof t&&(\"string\"===typeof t.path||\"string\"===typeof t.name)?(c(),\"object\"===typeof t&&t.replace?a.replace(t):a.push(t)):n(t)}))}catch(r){c(r)}};Lt(d,h,(function(){var n=[],r=function(){return a.current===t},o=Zt(p,n,r),i=o.concat(a.router.resolveHooks);Lt(i,h,(function(){if(a.pending!==t)return c();a.pending=null,e(t),a.router.app&&a.router.app.$nextTick((function(){n.forEach((function(t){t()}))}))}))}))},zt.prototype.updateRoute=function(t){var e=this.current;this.current=t,this.cb&&this.cb(t),this.router.afterHooks.forEach((function(n){n&&n(t,e)}))};var ne=function(t){function e(e,n){var r=this;t.call(this,e,n);var o=e.options.scrollBehavior,i=It&&o;i&&Ct();var a=re(this.base);window.addEventListener(\"popstate\",(function(t){var n=r.current,o=re(r.base);r.current===w&&o===a||r.transitionTo(o,(function(t){i&&Ot(e,t,n,!0)}))}))}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.go=function(t){window.history.go(t)},e.prototype.push=function(t,e,n){var r=this,o=this,i=o.current;this.transitionTo(t,(function(t){Mt(E(r.base+t.fullPath)),Ot(r.router,t,i,!1),e&&e(t)}),n)},e.prototype.replace=function(t,e,n){var r=this,o=this,i=o.current;this.transitionTo(t,(function(t){Nt(E(r.base+t.fullPath)),Ot(r.router,t,i,!1),e&&e(t)}),n)},e.prototype.ensureURL=function(t){if(re(this.base)!==this.current.fullPath){var e=E(this.base+this.current.fullPath);t?Mt(e):Nt(e)}},e.prototype.getCurrentLocation=function(){return re(this.base)},e}(zt);function re(t){var e=decodeURI(window.location.pathname);return t&&0===e.indexOf(t)&&(e=e.slice(t.length)),(e||\"/\")+window.location.search+window.location.hash}var oe=function(t){function e(e,n,r){t.call(this,e,n),r&&ie(this.base)||ae()}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.setupListeners=function(){var t=this,e=this.router,n=e.options.scrollBehavior,r=It&&n;r&&Ct(),window.addEventListener(It?\"popstate\":\"hashchange\",(function(){var e=t.current;ae()&&t.transitionTo(se(),(function(n){r&&Ot(t.router,n,e,!0),It||fe(n.fullPath)}))}))},e.prototype.push=function(t,e,n){var r=this,o=this,i=o.current;this.transitionTo(t,(function(t){ue(t.fullPath),Ot(r.router,t,i,!1),e&&e(t)}),n)},e.prototype.replace=function(t,e,n){var r=this,o=this,i=o.current;this.transitionTo(t,(function(t){fe(t.fullPath),Ot(r.router,t,i,!1),e&&e(t)}),n)},e.prototype.go=function(t){window.history.go(t)},e.prototype.ensureURL=function(t){var e=this.current.fullPath;se()!==e&&(t?ue(e):fe(e))},e.prototype.getCurrentLocation=function(){return se()},e}(zt);function ie(t){var e=re(t);if(!/^\\/#/.test(e))return window.location.replace(E(t+\"/#\"+e)),!0}function ae(){var t=se();return\"/\"===t.charAt(0)||(fe(\"/\"+t),!1)}function se(){var t=window.location.href,e=t.indexOf(\"#\");if(e<0)return\"\";t=t.slice(e+1);var n=t.indexOf(\"?\");if(n<0){var r=t.indexOf(\"#\");t=r>-1?decodeURI(t.slice(0,r))+t.slice(r):decodeURI(t)}else t=decodeURI(t.slice(0,n))+t.slice(n);return t}function ce(t){var e=window.location.href,n=e.indexOf(\"#\"),r=n>=0?e.slice(0,n):e;return r+\"#\"+t}function ue(t){It?Mt(ce(t)):window.location.hash=t}function fe(t){It?Nt(ce(t)):window.location.replace(ce(t))}var le=function(t){function e(e,n){t.call(this,e,n),this.stack=[],this.index=-1}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.push=function(t,e,n){var r=this;this.transitionTo(t,(function(t){r.stack=r.stack.slice(0,r.index+1).concat(t),r.index++,e&&e(t)}),n)},e.prototype.replace=function(t,e,n){var r=this;this.transitionTo(t,(function(t){r.stack=r.stack.slice(0,r.index).concat(t),e&&e(t)}),n)},e.prototype.go=function(t){var e=this,n=this.index+t;if(!(n<0||n>=this.stack.length)){var r=this.stack[n];this.confirmTransition(r,(function(){e.index=n,e.updateRoute(r)}),(function(t){i(Vt,t)&&(e.index=n)}))}},e.prototype.getCurrentLocation=function(){var t=this.stack[this.stack.length-1];return t?t.fullPath:\"/\"},e.prototype.ensureURL=function(){},e}(zt),pe=function(t){void 0===t&&(t={}),this.app=null,this.apps=[],this.options=t,this.beforeHooks=[],this.resolveHooks=[],this.afterHooks=[],this.matcher=ht(t.routes||[],this);var e=t.mode||\"hash\";switch(this.fallback=\"history\"===e&&!It&&!1!==t.fallback,this.fallback&&(e=\"hash\"),ut||(e=\"abstract\"),this.mode=e,e){case\"history\":this.history=new ne(this,t.base);break;case\"hash\":this.history=new oe(this,t.base,this.fallback);break;case\"abstract\":this.history=new le(this,t.base);break;default:0}},de={currentRoute:{configurable:!0}};function he(t,e){return t.push(e),function(){var n=t.indexOf(e);n>-1&&t.splice(n,1)}}function ve(t,e,n){var r=\"hash\"===n?\"#\"+e:e;return t?E(t+\"/\"+r):r}pe.prototype.match=function(t,e,n){return this.matcher.match(t,e,n)},de.currentRoute.get=function(){return this.history&&this.history.current},pe.prototype.init=function(t){var e=this;if(this.apps.push(t),t.$once(\"hook:destroyed\",(function(){var n=e.apps.indexOf(t);n>-1&&e.apps.splice(n,1),e.app===t&&(e.app=e.apps[0]||null)})),!this.app){this.app=t;var n=this.history;if(n instanceof ne)n.transitionTo(n.getCurrentLocation());else if(n instanceof oe){var r=function(){n.setupListeners()};n.transitionTo(n.getCurrentLocation(),r,r)}n.listen((function(t){e.apps.forEach((function(e){e._route=t}))}))}},pe.prototype.beforeEach=function(t){return he(this.beforeHooks,t)},pe.prototype.beforeResolve=function(t){return he(this.resolveHooks,t)},pe.prototype.afterEach=function(t){return he(this.afterHooks,t)},pe.prototype.onReady=function(t,e){this.history.onReady(t,e)},pe.prototype.onError=function(t){this.history.onError(t)},pe.prototype.push=function(t,e,n){var r=this;if(!e&&!n&&\"undefined\"!==typeof Promise)return new Promise((function(e,n){r.history.push(t,e,n)}));this.history.push(t,e,n)},pe.prototype.replace=function(t,e,n){var r=this;if(!e&&!n&&\"undefined\"!==typeof Promise)return new Promise((function(e,n){r.history.replace(t,e,n)}));this.history.replace(t,e,n)},pe.prototype.go=function(t){this.history.go(t)},pe.prototype.back=function(){this.go(-1)},pe.prototype.forward=function(){this.go(1)},pe.prototype.getMatchedComponents=function(t){var e=t?t.matched?t:this.resolve(t).route:this.currentRoute;return e?[].concat.apply([],e.matched.map((function(t){return Object.keys(t.components).map((function(e){return t.components[e]}))}))):[]},pe.prototype.resolve=function(t,e,n){e=e||this.history.current;var r=tt(t,e,n,this),o=this.match(r,e),i=o.redirectedFrom||o.fullPath,a=this.history.base,s=ve(a,i,this.mode);return{location:r,route:o,href:s,normalizedTo:r,resolved:o}},pe.prototype.addRoutes=function(t){this.matcher.addRoutes(t),this.history.current!==w&&this.history.transitionTo(this.history.getCurrentLocation())},Object.defineProperties(pe.prototype,de),pe.install=ct,pe.version=\"3.1.6\",ut&&window.Vue&&window.Vue.use(pe),e[\"a\"]=pe},\"8df4\":function(t,e,n){\"use strict\";var r=n(\"7a77\");function o(t){if(\"function\"!==typeof t)throw new TypeError(\"executor must be a function.\");var e;this.promise=new Promise((function(t){e=t}));var n=this;t((function(t){n.reason||(n.reason=new r(t),e(n.reason))}))}o.prototype.throwIfRequested=function(){if(this.reason)throw this.reason},o.source=function(){var t,e=new o((function(e){t=e}));return{token:e,cancel:t}},t.exports=o},\"90e3\":function(t,e){var n=0,r=Math.random();t.exports=function(t){return\"Symbol(\"+String(void 0===t?\"\":t)+\")_\"+(++n+r).toString(36)}},9112:function(t,e,n){var r=n(\"83ab\"),o=n(\"9bf2\"),i=n(\"5c6c\");t.exports=r?function(t,e,n){return o.f(t,e,i(1,n))}:function(t,e,n){return t[e]=n,t}},\"94ca\":function(t,e,n){var r=n(\"d039\"),o=/#|\\.prototype\\./,i=function(t,e){var n=s[a(t)];return n==u||n!=c&&(\"function\"==typeof e?r(e):!!e)},a=i.normalize=function(t){return String(t).replace(o,\".\").toLowerCase()},s=i.data={},c=i.NATIVE=\"N\",u=i.POLYFILL=\"P\";t.exports=i},\"9bdd\":function(t,e,n){var r=n(\"825a\");t.exports=function(t,e,n,o){try{return o?e(r(n)[0],n[1]):e(n)}catch(a){var i=t[\"return\"];throw void 0!==i&&r(i.call(t)),a}}},\"9bf2\":function(t,e,n){var r=n(\"83ab\"),o=n(\"0cfb\"),i=n(\"825a\"),a=n(\"c04e\"),s=Object.defineProperty;e.f=r?s:function(t,e,n){if(i(t),e=a(e,!0),i(n),o)try{return s(t,e,n)}catch(r){}if(\"get\"in n||\"set\"in n)throw TypeError(\"Accessors not supported\");return\"value\"in n&&(t[e]=n.value),t}},\"9ed3\":function(t,e,n){\"use strict\";var r=n(\"ae93\").IteratorPrototype,o=n(\"7c73\"),i=n(\"5c6c\"),a=n(\"d44e\"),s=n(\"3f8c\"),c=function(){return this};t.exports=function(t,e,n){var u=e+\" Iterator\";return t.prototype=o(r,{next:i(1,n)}),a(t,u,!1,!0),s[u]=c,t}},a691:function(t,e){var n=Math.ceil,r=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?r:n)(t)}},a79d:function(t,e,n){\"use strict\";var r=n(\"23e7\"),o=n(\"c430\"),i=n(\"fea9\"),a=n(\"d039\"),s=n(\"d066\"),c=n(\"4840\"),u=n(\"cdf9\"),f=n(\"6eeb\"),l=!!i&&a((function(){i.prototype[\"finally\"].call({then:function(){}},(function(){}))}));r({target:\"Promise\",proto:!0,real:!0,forced:l},{finally:function(t){var e=c(this,s(\"Promise\")),n=\"function\"==typeof t;return this.then(n?function(n){return u(e,t()).then((function(){return n}))}:t,n?function(n){return u(e,t()).then((function(){throw n}))}:t)}}),o||\"function\"!=typeof i||i.prototype[\"finally\"]||f(i.prototype,\"finally\",s(\"Promise\").prototype[\"finally\"])},ae93:function(t,e,n){\"use strict\";var r,o,i,a=n(\"e163\"),s=n(\"9112\"),c=n(\"5135\"),u=n(\"b622\"),f=n(\"c430\"),l=u(\"iterator\"),p=!1,d=function(){return this};[].keys&&(i=[].keys(),\"next\"in i?(o=a(a(i)),o!==Object.prototype&&(r=o)):p=!0),void 0==r&&(r={}),f||c(r,l)||s(r,l,d),t.exports={IteratorPrototype:r,BUGGY_SAFARI_ITERATORS:p}},b041:function(t,e,n){\"use strict\";var r=n(\"00ee\"),o=n(\"f5df\");t.exports=r?{}.toString:function(){return\"[object \"+o(this)+\"]\"}},b50d:function(t,e,n){\"use strict\";var r=n(\"c532\"),o=n(\"467f\"),i=n(\"30b5\"),a=n(\"83b9\"),s=n(\"c345\"),c=n(\"3934\"),u=n(\"2d83\");t.exports=function(t){return new Promise((function(e,f){var l=t.data,p=t.headers;r.isFormData(l)&&delete p[\"Content-Type\"];var d=new XMLHttpRequest;if(t.auth){var h=t.auth.username||\"\",v=t.auth.password||\"\";p.Authorization=\"Basic \"+btoa(h+\":\"+v)}var y=a(t.baseURL,t.url);if(d.open(t.method.toUpperCase(),i(y,t.params,t.paramsSerializer),!0),d.timeout=t.timeout,d.onreadystatechange=function(){if(d&&4===d.readyState&&(0!==d.status||d.responseURL&&0===d.responseURL.indexOf(\"file:\"))){var n=\"getAllResponseHeaders\"in d?s(d.getAllResponseHeaders()):null,r=t.responseType&&\"text\"!==t.responseType?d.response:d.responseText,i={data:r,status:d.status,statusText:d.statusText,headers:n,config:t,request:d};o(e,f,i),d=null}},d.onabort=function(){d&&(f(u(\"Request aborted\",t,\"ECONNABORTED\",d)),d=null)},d.onerror=function(){f(u(\"Network Error\",t,null,d)),d=null},d.ontimeout=function(){var e=\"timeout of \"+t.timeout+\"ms exceeded\";t.timeoutErrorMessage&&(e=t.timeoutErrorMessage),f(u(e,t,\"ECONNABORTED\",d)),d=null},r.isStandardBrowserEnv()){var m=n(\"7aac\"),g=(t.withCredentials||c(y))&&t.xsrfCookieName?m.read(t.xsrfCookieName):void 0;g&&(p[t.xsrfHeaderName]=g)}if(\"setRequestHeader\"in d&&r.forEach(p,(function(t,e){\"undefined\"===typeof l&&\"content-type\"===e.toLowerCase()?delete p[e]:d.setRequestHeader(e,t)})),r.isUndefined(t.withCredentials)||(d.withCredentials=!!t.withCredentials),t.responseType)try{d.responseType=t.responseType}catch(b){if(\"json\"!==t.responseType)throw b}\"function\"===typeof t.onDownloadProgress&&d.addEventListener(\"progress\",t.onDownloadProgress),\"function\"===typeof t.onUploadProgress&&d.upload&&d.upload.addEventListener(\"progress\",t.onUploadProgress),t.cancelToken&&t.cancelToken.promise.then((function(t){d&&(d.abort(),f(t),d=null)})),void 0===l&&(l=null),d.send(l)}))}},b575:function(t,e,n){var r,o,i,a,s,c,u,f,l=n(\"da84\"),p=n(\"06cf\").f,d=n(\"c6b6\"),h=n(\"2cf4\").set,v=n(\"1cdc\"),y=l.MutationObserver||l.WebKitMutationObserver,m=l.process,g=l.Promise,b=\"process\"==d(m),_=p(l,\"queueMicrotask\"),w=_&&_.value;w||(r=function(){var t,e;b&&(t=m.domain)&&t.exit();while(o){e=o.fn,o=o.next;try{e()}catch(n){throw o?a():i=void 0,n}}i=void 0,t&&t.enter()},b?a=function(){m.nextTick(r)}:y&&!v?(s=!0,c=document.createTextNode(\"\"),new y(r).observe(c,{characterData:!0}),a=function(){c.data=s=!s}):g&&g.resolve?(u=g.resolve(void 0),f=u.then,a=function(){f.call(u,r)}):a=function(){h.call(l,r)}),t.exports=w||function(t){var e={fn:t,next:void 0};i&&(i.next=e),o||(o=e,a()),i=e}},b622:function(t,e,n){var r=n(\"da84\"),o=n(\"5692\"),i=n(\"5135\"),a=n(\"90e3\"),s=n(\"4930\"),c=n(\"fdbf\"),u=o(\"wks\"),f=r.Symbol,l=c?f:f&&f.withoutSetter||a;t.exports=function(t){return i(u,t)||(s&&i(f,t)?u[t]=f[t]:u[t]=l(\"Symbol.\"+t)),u[t]}},bc3a:function(t,e,n){t.exports=n(\"cee4\")},c04e:function(t,e,n){var r=n(\"861d\");t.exports=function(t,e){if(!r(t))return t;var n,o;if(e&&\"function\"==typeof(n=t.toString)&&!r(o=n.call(t)))return o;if(\"function\"==typeof(n=t.valueOf)&&!r(o=n.call(t)))return o;if(!e&&\"function\"==typeof(n=t.toString)&&!r(o=n.call(t)))return o;throw TypeError(\"Can't convert object to primitive value\")}},c345:function(t,e,n){\"use strict\";var r=n(\"c532\"),o=[\"age\",\"authorization\",\"content-length\",\"content-type\",\"etag\",\"expires\",\"from\",\"host\",\"if-modified-since\",\"if-unmodified-since\",\"last-modified\",\"location\",\"max-forwards\",\"proxy-authorization\",\"referer\",\"retry-after\",\"user-agent\"];t.exports=function(t){var e,n,i,a={};return t?(r.forEach(t.split(\"\\n\"),(function(t){if(i=t.indexOf(\":\"),e=r.trim(t.substr(0,i)).toLowerCase(),n=r.trim(t.substr(i+1)),e){if(a[e]&&o.indexOf(e)>=0)return;a[e]=\"set-cookie\"===e?(a[e]?a[e]:[]).concat([n]):a[e]?a[e]+\", \"+n:n}})),a):a}},c401:function(t,e,n){\"use strict\";var r=n(\"c532\");t.exports=function(t,e,n){return r.forEach(n,(function(n){t=n(t,e)})),t}},c430:function(t,e){t.exports=!1},c532:function(t,e,n){\"use strict\";var r=n(\"1d2b\"),o=Object.prototype.toString;function i(t){return\"[object Array]\"===o.call(t)}function a(t){return\"undefined\"===typeof t}function s(t){return null!==t&&!a(t)&&null!==t.constructor&&!a(t.constructor)&&\"function\"===typeof t.constructor.isBuffer&&t.constructor.isBuffer(t)}function c(t){return\"[object ArrayBuffer]\"===o.call(t)}function u(t){return\"undefined\"!==typeof FormData&&t instanceof FormData}function f(t){var e;return e=\"undefined\"!==typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(t):t&&t.buffer&&t.buffer instanceof ArrayBuffer,e}function l(t){return\"string\"===typeof t}function p(t){return\"number\"===typeof t}function d(t){return null!==t&&\"object\"===typeof t}function h(t){return\"[object Date]\"===o.call(t)}function v(t){return\"[object File]\"===o.call(t)}function y(t){return\"[object Blob]\"===o.call(t)}function m(t){return\"[object Function]\"===o.call(t)}function g(t){return d(t)&&m(t.pipe)}function b(t){return\"undefined\"!==typeof URLSearchParams&&t instanceof URLSearchParams}function _(t){return t.replace(/^\\s*/,\"\").replace(/\\s*$/,\"\")}function w(){return(\"undefined\"===typeof navigator||\"ReactNative\"!==navigator.product&&\"NativeScript\"!==navigator.product&&\"NS\"!==navigator.product)&&(\"undefined\"!==typeof window&&\"undefined\"!==typeof document)}function x(t,e){if(null!==t&&\"undefined\"!==typeof t)if(\"object\"!==typeof t&&(t=[t]),i(t))for(var n=0,r=t.length;n<r;n++)e.call(null,t[n],n,t);else for(var o in t)Object.prototype.hasOwnProperty.call(t,o)&&e.call(null,t[o],o,t)}function C(){var t={};function e(e,n){\"object\"===typeof t[n]&&\"object\"===typeof e?t[n]=C(t[n],e):t[n]=e}for(var n=0,r=arguments.length;n<r;n++)x(arguments[n],e);return t}function O(){var t={};function e(e,n){\"object\"===typeof t[n]&&\"object\"===typeof e?t[n]=O(t[n],e):t[n]=\"object\"===typeof e?O({},e):e}for(var n=0,r=arguments.length;n<r;n++)x(arguments[n],e);return t}function A(t,e,n){return x(e,(function(e,o){t[o]=n&&\"function\"===typeof e?r(e,n):e})),t}t.exports={isArray:i,isArrayBuffer:c,isBuffer:s,isFormData:u,isArrayBufferView:f,isString:l,isNumber:p,isObject:d,isUndefined:a,isDate:h,isFile:v,isBlob:y,isFunction:m,isStream:g,isURLSearchParams:b,isStandardBrowserEnv:w,forEach:x,merge:C,deepMerge:O,extend:A,trim:_}},c6b6:function(t,e){var n={}.toString;t.exports=function(t){return n.call(t).slice(8,-1)}},c6cd:function(t,e,n){var r=n(\"da84\"),o=n(\"ce4e\"),i=\"__core-js_shared__\",a=r[i]||o(i,{});t.exports=a},c8af:function(t,e,n){\"use strict\";var r=n(\"c532\");t.exports=function(t,e){r.forEach(t,(function(n,r){r!==e&&r.toUpperCase()===e.toUpperCase()&&(t[e]=n,delete t[r])}))}},c8ba:function(t,e){var n;n=function(){return this}();try{n=n||new Function(\"return this\")()}catch(r){\"object\"===typeof window&&(n=window)}t.exports=n},ca84:function(t,e,n){var r=n(\"5135\"),o=n(\"fc6a\"),i=n(\"4d64\").indexOf,a=n(\"d012\");t.exports=function(t,e){var n,s=o(t),c=0,u=[];for(n in s)!r(a,n)&&r(s,n)&&u.push(n);while(e.length>c)r(s,n=e[c++])&&(~i(u,n)||u.push(n));return u}},cc12:function(t,e,n){var r=n(\"da84\"),o=n(\"861d\"),i=r.document,a=o(i)&&o(i.createElement);t.exports=function(t){return a?i.createElement(t):{}}},cca6:function(t,e,n){var r=n(\"23e7\"),o=n(\"60da\");r({target:\"Object\",stat:!0,forced:Object.assign!==o},{assign:o})},cdf9:function(t,e,n){var r=n(\"825a\"),o=n(\"861d\"),i=n(\"f069\");t.exports=function(t,e){if(r(t),o(e)&&e.constructor===t)return e;var n=i.f(t),a=n.resolve;return a(e),n.promise}},ce4e:function(t,e,n){var r=n(\"da84\"),o=n(\"9112\");t.exports=function(t,e){try{o(r,t,e)}catch(n){r[t]=e}return e}},cee4:function(t,e,n){\"use strict\";var r=n(\"c532\"),o=n(\"1d2b\"),i=n(\"0a06\"),a=n(\"4a7b\"),s=n(\"2444\");function c(t){var e=new i(t),n=o(i.prototype.request,e);return r.extend(n,i.prototype,e),r.extend(n,e),n}var u=c(s);u.Axios=i,u.create=function(t){return c(a(u.defaults,t))},u.Cancel=n(\"7a77\"),u.CancelToken=n(\"8df4\"),u.isCancel=n(\"2e67\"),u.all=function(t){return Promise.all(t)},u.spread=n(\"0df6\"),t.exports=u,t.exports.default=u},d012:function(t,e){t.exports={}},d039:function(t,e){t.exports=function(t){try{return!!t()}catch(e){return!0}}},d066:function(t,e,n){var r=n(\"428f\"),o=n(\"da84\"),i=function(t){return\"function\"==typeof t?t:void 0};t.exports=function(t,e){return arguments.length<2?i(r[t])||i(o[t]):r[t]&&r[t][e]||o[t]&&o[t][e]}},d1e7:function(t,e,n){\"use strict\";var r={}.propertyIsEnumerable,o=Object.getOwnPropertyDescriptor,i=o&&!r.call({1:2},1);e.f=i?function(t){var e=o(this,t);return!!e&&e.enumerable}:r},d2bb:function(t,e,n){var r=n(\"825a\"),o=n(\"3bbe\");t.exports=Object.setPrototypeOf||(\"__proto__\"in{}?function(){var t,e=!1,n={};try{t=Object.getOwnPropertyDescriptor(Object.prototype,\"__proto__\").set,t.call(n,[]),e=n instanceof Array}catch(i){}return function(n,i){return r(n),o(i),e?t.call(n,i):n.__proto__=i,n}}():void 0)},d3b7:function(t,e,n){var r=n(\"00ee\"),o=n(\"6eeb\"),i=n(\"b041\");r||o(Object.prototype,\"toString\",i,{unsafe:!0})},d44e:function(t,e,n){var r=n(\"9bf2\").f,o=n(\"5135\"),i=n(\"b622\"),a=i(\"toStringTag\");t.exports=function(t,e,n){t&&!o(t=n?t:t.prototype,a)&&r(t,a,{configurable:!0,value:e})}},d925:function(t,e,n){\"use strict\";t.exports=function(t){return/^([a-z][a-z\\d\\+\\-\\.]*:)?\\/\\//i.test(t)}},da84:function(t,e,n){(function(e){var n=function(t){return t&&t.Math==Math&&t};t.exports=n(\"object\"==typeof globalThis&&globalThis)||n(\"object\"==typeof window&&window)||n(\"object\"==typeof self&&self)||n(\"object\"==typeof e&&e)||Function(\"return this\")()}).call(this,n(\"c8ba\"))},df75:function(t,e,n){var r=n(\"ca84\"),o=n(\"7839\");t.exports=Object.keys||function(t){return r(t,o)}},df7c:function(t,e,n){(function(t){function n(t,e){for(var n=0,r=t.length-1;r>=0;r--){var o=t[r];\".\"===o?t.splice(r,1):\"..\"===o?(t.splice(r,1),n++):n&&(t.splice(r,1),n--)}if(e)for(;n--;n)t.unshift(\"..\");return t}function r(t){\"string\"!==typeof t&&(t+=\"\");var e,n=0,r=-1,o=!0;for(e=t.length-1;e>=0;--e)if(47===t.charCodeAt(e)){if(!o){n=e+1;break}}else-1===r&&(o=!1,r=e+1);return-1===r?\"\":t.slice(n,r)}function o(t,e){if(t.filter)return t.filter(e);for(var n=[],r=0;r<t.length;r++)e(t[r],r,t)&&n.push(t[r]);return n}e.resolve=function(){for(var e=\"\",r=!1,i=arguments.length-1;i>=-1&&!r;i--){var a=i>=0?arguments[i]:t.cwd();if(\"string\"!==typeof a)throw new TypeError(\"Arguments to path.resolve must be strings\");a&&(e=a+\"/\"+e,r=\"/\"===a.charAt(0))}return e=n(o(e.split(\"/\"),(function(t){return!!t})),!r).join(\"/\"),(r?\"/\":\"\")+e||\".\"},e.normalize=function(t){var r=e.isAbsolute(t),a=\"/\"===i(t,-1);return t=n(o(t.split(\"/\"),(function(t){return!!t})),!r).join(\"/\"),t||r||(t=\".\"),t&&a&&(t+=\"/\"),(r?\"/\":\"\")+t},e.isAbsolute=function(t){return\"/\"===t.charAt(0)},e.join=function(){var t=Array.prototype.slice.call(arguments,0);return e.normalize(o(t,(function(t,e){if(\"string\"!==typeof t)throw new TypeError(\"Arguments to path.join must be strings\");return t})).join(\"/\"))},e.relative=function(t,n){function r(t){for(var e=0;e<t.length;e++)if(\"\"!==t[e])break;for(var n=t.length-1;n>=0;n--)if(\"\"!==t[n])break;return e>n?[]:t.slice(e,n-e+1)}t=e.resolve(t).substr(1),n=e.resolve(n).substr(1);for(var o=r(t.split(\"/\")),i=r(n.split(\"/\")),a=Math.min(o.length,i.length),s=a,c=0;c<a;c++)if(o[c]!==i[c]){s=c;break}var u=[];for(c=s;c<o.length;c++)u.push(\"..\");return u=u.concat(i.slice(s)),u.join(\"/\")},e.sep=\"/\",e.delimiter=\":\",e.dirname=function(t){if(\"string\"!==typeof t&&(t+=\"\"),0===t.length)return\".\";for(var e=t.charCodeAt(0),n=47===e,r=-1,o=!0,i=t.length-1;i>=1;--i)if(e=t.charCodeAt(i),47===e){if(!o){r=i;break}}else o=!1;return-1===r?n?\"/\":\".\":n&&1===r?\"/\":t.slice(0,r)},e.basename=function(t,e){var n=r(t);return e&&n.substr(-1*e.length)===e&&(n=n.substr(0,n.length-e.length)),n},e.extname=function(t){\"string\"!==typeof t&&(t+=\"\");for(var e=-1,n=0,r=-1,o=!0,i=0,a=t.length-1;a>=0;--a){var s=t.charCodeAt(a);if(47!==s)-1===r&&(o=!1,r=a+1),46===s?-1===e?e=a:1!==i&&(i=1):-1!==e&&(i=-1);else if(!o){n=a+1;break}}return-1===e||-1===r||0===i||1===i&&e===r-1&&e===n+1?\"\":t.slice(e,r)};var i=\"b\"===\"ab\".substr(-1)?function(t,e,n){return t.substr(e,n)}:function(t,e,n){return e<0&&(e=t.length+e),t.substr(e,n)}}).call(this,n(\"4362\"))},e163:function(t,e,n){var r=n(\"5135\"),o=n(\"7b0b\"),i=n(\"f772\"),a=n(\"e177\"),s=i(\"IE_PROTO\"),c=Object.prototype;t.exports=a?Object.getPrototypeOf:function(t){return t=o(t),r(t,s)?t[s]:\"function\"==typeof t.constructor&&t instanceof t.constructor?t.constructor.prototype:t instanceof Object?c:null}},e177:function(t,e,n){var r=n(\"d039\");t.exports=!r((function(){function t(){}return t.prototype.constructor=null,Object.getPrototypeOf(new t)!==t.prototype}))},e260:function(t,e,n){\"use strict\";var r=n(\"fc6a\"),o=n(\"44d2\"),i=n(\"3f8c\"),a=n(\"69f3\"),s=n(\"7dd0\"),c=\"Array Iterator\",u=a.set,f=a.getterFor(c);t.exports=s(Array,\"Array\",(function(t,e){u(this,{type:c,target:r(t),index:0,kind:e})}),(function(){var t=f(this),e=t.target,n=t.kind,r=t.index++;return!e||r>=e.length?(t.target=void 0,{value:void 0,done:!0}):\"keys\"==n?{value:r,done:!1}:\"values\"==n?{value:e[r],done:!1}:{value:[r,e[r]],done:!1}}),\"values\"),i.Arguments=i.Array,o(\"keys\"),o(\"values\"),o(\"entries\")},e2cc:function(t,e,n){var r=n(\"6eeb\");t.exports=function(t,e,n){for(var o in e)r(t,o,e[o],n);return t}},e667:function(t,e){t.exports=function(t){try{return{error:!1,value:t()}}catch(e){return{error:!0,value:e}}}},e683:function(t,e,n){\"use strict\";t.exports=function(t,e){return e?t.replace(/\\/+$/,\"\")+\"/\"+e.replace(/^\\/+/,\"\"):t}},e6cf:function(t,e,n){\"use strict\";var r,o,i,a,s=n(\"23e7\"),c=n(\"c430\"),u=n(\"da84\"),f=n(\"d066\"),l=n(\"fea9\"),p=n(\"6eeb\"),d=n(\"e2cc\"),h=n(\"d44e\"),v=n(\"2626\"),y=n(\"861d\"),m=n(\"1c0b\"),g=n(\"19aa\"),b=n(\"c6b6\"),_=n(\"8925\"),w=n(\"2266\"),x=n(\"1c7e\"),C=n(\"4840\"),O=n(\"2cf4\").set,A=n(\"b575\"),k=n(\"cdf9\"),j=n(\"44de\"),S=n(\"f069\"),$=n(\"e667\"),E=n(\"69f3\"),T=n(\"94ca\"),P=n(\"b622\"),R=n(\"2d00\"),I=P(\"species\"),M=\"Promise\",N=E.get,L=E.set,D=E.getterFor(M),F=l,U=u.TypeError,B=u.document,q=u.process,H=f(\"fetch\"),V=S.f,z=V,G=\"process\"==b(q),W=!!(B&&B.createEvent&&u.dispatchEvent),K=\"unhandledrejection\",X=\"rejectionhandled\",J=0,Y=1,Q=2,Z=1,tt=2,et=T(M,(function(){var t=_(F)!==String(F);if(!t){if(66===R)return!0;if(!G&&\"function\"!=typeof PromiseRejectionEvent)return!0}if(c&&!F.prototype[\"finally\"])return!0;if(R>=51&&/native code/.test(F))return!1;var e=F.resolve(1),n=function(t){t((function(){}),(function(){}))},r=e.constructor={};return r[I]=n,!(e.then((function(){}))instanceof n)})),nt=et||!x((function(t){F.all(t)[\"catch\"]((function(){}))})),rt=function(t){var e;return!(!y(t)||\"function\"!=typeof(e=t.then))&&e},ot=function(t,e,n){if(!e.notified){e.notified=!0;var r=e.reactions;A((function(){var o=e.value,i=e.state==Y,a=0;while(r.length>a){var s,c,u,f=r[a++],l=i?f.ok:f.fail,p=f.resolve,d=f.reject,h=f.domain;try{l?(i||(e.rejection===tt&&ct(t,e),e.rejection=Z),!0===l?s=o:(h&&h.enter(),s=l(o),h&&(h.exit(),u=!0)),s===f.promise?d(U(\"Promise-chain cycle\")):(c=rt(s))?c.call(s,p,d):p(s)):d(o)}catch(v){h&&!u&&h.exit(),d(v)}}e.reactions=[],e.notified=!1,n&&!e.rejection&&at(t,e)}))}},it=function(t,e,n){var r,o;W?(r=B.createEvent(\"Event\"),r.promise=e,r.reason=n,r.initEvent(t,!1,!0),u.dispatchEvent(r)):r={promise:e,reason:n},(o=u[\"on\"+t])?o(r):t===K&&j(\"Unhandled promise rejection\",n)},at=function(t,e){O.call(u,(function(){var n,r=e.value,o=st(e);if(o&&(n=$((function(){G?q.emit(\"unhandledRejection\",r,t):it(K,t,r)})),e.rejection=G||st(e)?tt:Z,n.error))throw n.value}))},st=function(t){return t.rejection!==Z&&!t.parent},ct=function(t,e){O.call(u,(function(){G?q.emit(\"rejectionHandled\",t):it(X,t,e.value)}))},ut=function(t,e,n,r){return function(o){t(e,n,o,r)}},ft=function(t,e,n,r){e.done||(e.done=!0,r&&(e=r),e.value=n,e.state=Q,ot(t,e,!0))},lt=function(t,e,n,r){if(!e.done){e.done=!0,r&&(e=r);try{if(t===n)throw U(\"Promise can't be resolved itself\");var o=rt(n);o?A((function(){var r={done:!1};try{o.call(n,ut(lt,t,r,e),ut(ft,t,r,e))}catch(i){ft(t,r,i,e)}})):(e.value=n,e.state=Y,ot(t,e,!1))}catch(i){ft(t,{done:!1},i,e)}}};et&&(F=function(t){g(this,F,M),m(t),r.call(this);var e=N(this);try{t(ut(lt,this,e),ut(ft,this,e))}catch(n){ft(this,e,n)}},r=function(t){L(this,{type:M,done:!1,notified:!1,parent:!1,reactions:[],rejection:!1,state:J,value:void 0})},r.prototype=d(F.prototype,{then:function(t,e){var n=D(this),r=V(C(this,F));return r.ok=\"function\"!=typeof t||t,r.fail=\"function\"==typeof e&&e,r.domain=G?q.domain:void 0,n.parent=!0,n.reactions.push(r),n.state!=J&&ot(this,n,!1),r.promise},catch:function(t){return this.then(void 0,t)}}),o=function(){var t=new r,e=N(t);this.promise=t,this.resolve=ut(lt,t,e),this.reject=ut(ft,t,e)},S.f=V=function(t){return t===F||t===i?new o(t):z(t)},c||\"function\"!=typeof l||(a=l.prototype.then,p(l.prototype,\"then\",(function(t,e){var n=this;return new F((function(t,e){a.call(n,t,e)})).then(t,e)}),{unsafe:!0}),\"function\"==typeof H&&s({global:!0,enumerable:!0,forced:!0},{fetch:function(t){return k(F,H.apply(u,arguments))}}))),s({global:!0,wrap:!0,forced:et},{Promise:F}),h(F,M,!1,!0),v(M),i=f(M),s({target:M,stat:!0,forced:et},{reject:function(t){var e=V(this);return e.reject.call(void 0,t),e.promise}}),s({target:M,stat:!0,forced:c||et},{resolve:function(t){return k(c&&this===i?F:this,t)}}),s({target:M,stat:!0,forced:nt},{all:function(t){var e=this,n=V(e),r=n.resolve,o=n.reject,i=$((function(){var n=m(e.resolve),i=[],a=0,s=1;w(t,(function(t){var c=a++,u=!1;i.push(void 0),s++,n.call(e,t).then((function(t){u||(u=!0,i[c]=t,--s||r(i))}),o)})),--s||r(i)}));return i.error&&o(i.value),n.promise},race:function(t){var e=this,n=V(e),r=n.reject,o=$((function(){var o=m(e.resolve);w(t,(function(t){o.call(e,t).then(n.resolve,r)}))}));return o.error&&r(o.value),n.promise}})},e893:function(t,e,n){var r=n(\"5135\"),o=n(\"56ef\"),i=n(\"06cf\"),a=n(\"9bf2\");t.exports=function(t,e){for(var n=o(e),s=a.f,c=i.f,u=0;u<n.length;u++){var f=n[u];r(t,f)||s(t,f,c(e,f))}}},e95a:function(t,e,n){var r=n(\"b622\"),o=n(\"3f8c\"),i=r(\"iterator\"),a=Array.prototype;t.exports=function(t){return void 0!==t&&(o.Array===t||a[i]===t)}},f069:function(t,e,n){\"use strict\";var r=n(\"1c0b\"),o=function(t){var e,n;this.promise=new t((function(t,r){if(void 0!==e||void 0!==n)throw TypeError(\"Bad Promise constructor\");e=t,n=r})),this.resolve=r(e),this.reject=r(n)};t.exports.f=function(t){return new o(t)}},f5df:function(t,e,n){var r=n(\"00ee\"),o=n(\"c6b6\"),i=n(\"b622\"),a=i(\"toStringTag\"),s=\"Arguments\"==o(function(){return arguments}()),c=function(t,e){try{return t[e]}catch(n){}};t.exports=r?o:function(t){var e,n,r;return void 0===t?\"Undefined\":null===t?\"Null\":\"string\"==typeof(n=c(e=Object(t),a))?n:s?o(e):\"Object\"==(r=o(e))&&\"function\"==typeof e.callee?\"Arguments\":r}},f6b4:function(t,e,n){\"use strict\";var r=n(\"c532\");function o(){this.handlers=[]}o.prototype.use=function(t,e){return this.handlers.push({fulfilled:t,rejected:e}),this.handlers.length-1},o.prototype.eject=function(t){this.handlers[t]&&(this.handlers[t]=null)},o.prototype.forEach=function(t){r.forEach(this.handlers,(function(e){null!==e&&t(e)}))},t.exports=o},f772:function(t,e,n){var r=n(\"5692\"),o=n(\"90e3\"),i=r(\"keys\");t.exports=function(t){return i[t]||(i[t]=o(t))}},fc6a:function(t,e,n){var r=n(\"44ad\"),o=n(\"1d80\");t.exports=function(t){return r(o(t))}},fdbf:function(t,e,n){var r=n(\"4930\");t.exports=r&&!Symbol.sham&&\"symbol\"==typeof Symbol.iterator},fea9:function(t,e,n){var r=n(\"da84\");t.exports=r.Promise}}]);\n//# sourceMappingURL=chunk-vendors.57f9e9d6.js.map"
  },
  {
    "path": "lesson80/bluebell/templates/index.html",
    "content": "<!DOCTYPE html><html lang=zh-CN><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content=\"IE=edge\"><meta name=viewport content=\"width=device-width,initial-scale=1\"><link rel=icon href=/static/favicon.ico><title>bluebell_frontend</title><link href=/static/css/app.0afe9dae.css rel=preload as=style><link href=/static/js/app.9f3efa6d.js rel=preload as=script><link href=/static/js/chunk-vendors.57f9e9d6.js rel=preload as=script><link href=/static/css/app.0afe9dae.css rel=stylesheet></head><body><noscript><strong>We're sorry but bluebell_frontend doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/static/js/chunk-vendors.57f9e9d6.js></script><script src=/static/js/app.9f3efa6d.js></script></body></html>"
  },
  {
    "path": "lesson80/bluebell/wait-for.sh",
    "content": "#!/bin/bash\n\nTIMEOUT=15\nQUIET=0\n\nADDRS=()\n\nechoerr() {\n  if [ \"$QUIET\" -ne 1 ]; then printf \"%s\\n\" \"$*\" 1>&2; fi\n}\n\nusage() {\n  exitcode=\"$1\"\n  cat << USAGE >&2\nclient:\n  $cmdname host:port [host:port] [host:port] [-t timeout] [-- command args]\n  -q | --quiet                        Do not output any status messages\n  -t TIMEOUT | --timeout=timeout      Timeout in seconds, zero for no timeout\n  -- COMMAND ARGS                     Execute command with args after the test finishes\nUSAGE\n  exit \"$exitcode\"\n}\n\nwait_for() {\n  results=()\n  for addr in ${ADDRS[@]}\n  do\n    HOST=$(printf \"%s\\n\" \"$addr\"| cut -d : -f 1)\n    PORT=$(printf \"%s\\n\" \"$addr\"| cut -d : -f 2)\n    result=1\n    for i in `seq $TIMEOUT` ; do\n      nc -z \"$HOST\" \"$PORT\" > /dev/null 2>&1\n      result=$?\n      if [ $result -ne 0 ] ; then\n        sleep 1\n\tcontinue\n      fi\n      break\n    done\n    results=(${results[@]} $result)\n  done\n  num=${#results[@]}\n  for result in ${results[@]}\n  do\n    if [ $result -eq 0 ] ; then\n\t    num=`expr $num - 1`\n    fi\n  done\n  if [ $num -eq 0 ] ; then\n    if [ $# -gt 0 ] ; then\n      exec \"$@\"\n    fi\n    exit 0\n  fi\n  echo \"Operation timed out\" >&2\n  exit 1\n}\n\nwhile [ $# -gt 0 ]\ndo\n  case \"$1\" in\n    *:* )\n    ADDRS=(${ADDRS[@]} $1)\n    shift 1\n    ;;\n    -q | --quiet)\n    QUIET=1\n    shift 1\n    ;;\n    -t)\n    TIMEOUT=\"$2\"\n    if [ \"$TIMEOUT\" = \"\" ]; then break; fi\n    shift 2\n    ;;\n    --timeout=*)\n    TIMEOUT=\"${1#*=}\"\n    shift 1\n    ;;\n    --)\n    shift\n    break\n    ;;\n    --help)\n    usage 0\n    ;;\n    *)\n    echoerr \"Unknown argument: $1\"\n    usage 1\n    ;;\n  esac\ndone\n\nif [ \"${#ADDRS[@]}\" -eq 0 ]; then\n  echoerr \"Error: you need to provide a host and port to test.\"\n  usage 2\nfi\n\nwait_for \"$@\""
  },
  {
    "path": "mysql_demo/go.mod",
    "content": "module mysql_demo\n\ngo 1.14\n\nrequire github.com/go-sql-driver/mysql v1.5.0\n"
  },
  {
    "path": "mysql_demo/go.sum",
    "content": "github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=\ngithub.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=\n"
  },
  {
    "path": "mysql_demo/main.go",
    "content": "package main\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\n\t_ \"github.com/go-sql-driver/mysql\" // init()\n)\n\nvar db *sql.DB\n\nfunc initMySQL() (err error) {\n\t// DSN:Data Source Name\n\tdsn := \"root:root1234@tcp(127.0.0.1:13306)/sql_demo\"\n\t// 去初始化全局的db对象而不是新声明一个db变量\n\tdb, err = sql.Open(\"mysql\", dsn)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\t// 做完错误检查之后，确保db不为nil\n\t// 尝试与数据库建立连接（校验dsn是否正确）\n\terr = db.Ping()\n\tif err != nil {\n\t\tfmt.Printf(\"connect to db failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\t// 数值需要业务具体情况来确定\n\t//db.SetConnMaxLifetime(time.Second*10)\n\tdb.SetMaxOpenConns(100) // 最大连接数\n\tdb.SetMaxIdleConns(10)  // 最大空闲连接数\n\treturn\n}\n\ntype user struct {\n\tid   int\n\tage  int\n\tname string\n}\n\n// 查询单条数据示例\nfunc queryRowDemo() {\n\tsqlStr := \"select id, name, age from user where id=?\"\n\tvar u user\n\t// 非常重要：确保QueryRow之后调用Scan方法，否则持有的数据库链接不会被释放\n\t//row := db.QueryRow(sqlStr, 1) // 并没有对其做scan操作\n\n\terr := db.QueryRow(sqlStr, 2).Scan(&u.id, &u.name, &u.age)\n\tif err != nil {\n\t\tfmt.Printf(\"scan failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tfmt.Printf(\"id:%d name:%s age:%d\\n\", u.id, u.name, u.age)\n}\n\n// 查询多条数据示例\nfunc queryMultiRowDemo() {\n\tsqlStr := \"select id, name, age from user where id > ?\"\n\trows, err := db.Query(sqlStr, 0)\n\tif err != nil {\n\t\tfmt.Printf(\"query failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\t// 非常重要：关闭rows释放持有的数据库链接\n\tdefer rows.Close()\n\n\t// 循环读取结果集中的数据\n\tfor rows.Next() {\n\t\tvar u user\n\t\terr := rows.Scan(&u.id, &u.name, &u.age)\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"scan failed, err:%v\\n\", err)\n\t\t\treturn\n\t\t}\n\t\tfmt.Printf(\"id:%d name:%s age:%d\\n\", u.id, u.name, u.age)\n\t}\n}\n\n// 插入数据\nfunc insertRowDemo() {\n\tsqlStr := \"insert into user(name, age) values (?,?)\"\n\tret, err := db.Exec(sqlStr, \"七米\", 29)\n\tif err != nil {\n\t\tfmt.Printf(\"insert failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tvar theID int64\n\ttheID, err = ret.LastInsertId() // 新插入数据的id\n\tif err != nil {\n\t\tfmt.Printf(\"get lastinsert ID failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tfmt.Printf(\"insert success, the id is %d.\\n\", theID)\n}\n\n// 更新数据\nfunc updateRowDemo() {\n\tsqlStr := \"update user set age=? where id = ?\"\n\tret, err := db.Exec(sqlStr, 39, 3)\n\tif err != nil {\n\t\tfmt.Printf(\"update failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tvar n int64\n\tn, err = ret.RowsAffected() // 操作影响的行数\n\tif err != nil {\n\t\tfmt.Printf(\"get RowsAffected failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tfmt.Printf(\"update success, affected rows:%d\\n\", n)\n}\n\n// 删除数据\nfunc deleteRowDemo() {\n\tsqlStr := \"delete from user where id = ?\"\n\tret, err := db.Exec(sqlStr, 3)\n\tif err != nil {\n\t\tfmt.Printf(\"delete failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tvar n int64\n\tn, err = ret.RowsAffected() // 操作影响的行数\n\tif err != nil {\n\t\tfmt.Printf(\"get RowsAffected failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tfmt.Printf(\"delete success, affected rows:%d\\n\", n)\n}\n\n// 预处理查询示例\nfunc prepareQueryDemo() {\n\tsqlStr := \"select id, name, age from user where id > ?\"\n\tstmt, err := db.Prepare(sqlStr)\n\tif err != nil {\n\t\tfmt.Printf(\"prepare failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer stmt.Close()\n\trows, err := stmt.Query(0)\n\tif err != nil {\n\t\tfmt.Printf(\"query failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer rows.Close()\n\t// 循环读取结果集中的数据\n\tfor rows.Next() {\n\t\tvar u user\n\t\terr := rows.Scan(&u.id, &u.name, &u.age)\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"scan failed, err:%v\\n\", err)\n\t\t\treturn\n\t\t}\n\t\tfmt.Printf(\"id:%d name:%s age:%d\\n\", u.id, u.name, u.age)\n\t}\n}\n\n// 预处理插入示例\nfunc prepareInsertDemo() {\n\tsqlStr := \"insert into user(name, age) values (?,?)\"\n\tstmt, err := db.Prepare(sqlStr)\n\tif err != nil {\n\t\tfmt.Printf(\"prepare failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer stmt.Close()\n\t_, err = stmt.Exec(\"小王子\", 18)\n\tif err != nil {\n\t\tfmt.Printf(\"insert failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\t_, err = stmt.Exec(\"七米\", 29)\n\tif err != nil {\n\t\tfmt.Printf(\"insert failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tfmt.Println(\"insert success.\")\n}\n\n// sql注入示例\nfunc sqlInjectDemo(name string) {\n\tsqlStr := fmt.Sprintf(\"select id, name, age from user where name='%s'\", name)\n\tfmt.Printf(\"SQL:%s\\n\", sqlStr)\n\tvar u user\n\terr := db.QueryRow(sqlStr).Scan(&u.id, &u.name, &u.age)\n\tif err != nil {\n\t\tfmt.Printf(\"exec failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tfmt.Printf(\"user:%#v\\n\", u)\n}\n\n// 事务操作示例\nfunc transactionDemo() {\n\ttx, err := db.Begin() // 开启事务\n\tif err != nil {\n\t\tif tx != nil {\n\t\t\ttx.Rollback() // 回滚\n\t\t}\n\t\tfmt.Printf(\"begin trans failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tsqlStr1 := \"Update user set age=30 where id=?\"\n\tret1, err := tx.Exec(sqlStr1, 2)\n\tif err != nil {\n\t\ttx.Rollback() // 回滚\n\t\tfmt.Printf(\"exec sql1 failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\taffRow1, err := ret1.RowsAffected()\n\tif err != nil {\n\t\ttx.Rollback() // 回滚\n\t\tfmt.Printf(\"exec ret1.RowsAffected() failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\n\tsqlStr2 := \"Update user set age=40 where id=?\"\n\tret2, err := tx.Exec(sqlStr2, 3)\n\tif err != nil {\n\t\ttx.Rollback() // 回滚\n\t\tfmt.Printf(\"exec sql2 failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\taffRow2, err := ret2.RowsAffected()\n\tif err != nil {\n\t\ttx.Rollback() // 回滚\n\t\tfmt.Printf(\"exec ret1.RowsAffected() failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\n\t// 当affRow1 == 1 && affRow2 == 1\n\tfmt.Println(affRow1, affRow2)\n\tif affRow1 == 1 && affRow2 == 1 {\n\t\tfmt.Println(\"事务提交啦...\")\n\t\ttx.Commit() // 提交事务\n\t} else {\n\t\ttx.Rollback()\n\t\tfmt.Println(\"事务回滚啦...\")\n\t}\n\n\tfmt.Println(\"exec trans success!\")\n}\n\nfunc main() {\n\tif err := initMySQL(); err != nil {\n\t\tfmt.Printf(\"connect to db failed, err:%v\\n\", err)\n\t}\n\t// Close() 用来释放掉数据库连接相关的资源\n\tdefer db.Close() // 注意这行代码要写在上面err判断的下面\n\tfmt.Println(\"connect to db success\")\n\t// db.xx() 去使用数据库操作。。。\n\t//queryRowDemo()\n\t//queryMultiRowDemo()\n\t//insertRowDemo()\n\t//updateRowDemo()\n\t//deleteRowDemo()\n\n\t//prepareQueryDemo()\n\t//prepareInsertDemo()\n\t//sqlInjectDemo(\"七米\")\n\t//select id, name, age from user where name='xxx ' or 1=1#'\n\t//sqlInjectDemo(\"xxx ' or 1=1#\")\n\n\ttransactionDemo()\n\tfmt.Println(\"查询结束了...\")\n}\n"
  },
  {
    "path": "ratelimit_demo/go.mod",
    "content": "module ratelimit_demo\n\ngo 1.14\n\nrequire (\n\tgithub.com/gin-gonic/gin v1.6.3\n\tgithub.com/juju/ratelimit v1.0.1\n\tgo.uber.org/atomic v1.7.0 // indirect\n\tgo.uber.org/ratelimit v0.1.0\n)\n"
  },
  {
    "path": "ratelimit_demo/go.sum",
    "content": "github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=\ngithub.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=\ngithub.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=\ngithub.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=\ngithub.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=\ngithub.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=\ngithub.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=\ngithub.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=\ngithub.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=\ngithub.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=\ngithub.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=\ngithub.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=\ngithub.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/juju/ratelimit v1.0.1 h1:+7AIFJVQ0EQgq/K9+0Krm7m530Du7tIz0METWzN0RgY=\ngithub.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=\ngithub.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=\ngithub.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=\ngithub.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=\ngithub.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=\ngithub.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=\ngithub.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=\ngo.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=\ngo.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/ratelimit v0.1.0 h1:U2AruXqeTb4Eh9sYQSTrMhH8Cb7M0Ian2ibBOnBcnAw=\ngo.uber.org/ratelimit v0.1.0/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\n"
  },
  {
    "path": "ratelimit_demo/main.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\tratelimit2 \"github.com/juju/ratelimit\"\n\tratelimit1 \"go.uber.org/ratelimit\" // 漏桶\n\t// 令牌桶\n)\n\nfunc pingHandler(c *gin.Context) {\n\tc.String(http.StatusOK, \"pong\")\n}\n\nfunc heiHandler(c *gin.Context) {\n\tc.String(http.StatusOK, \"ha\")\n}\n\n// 基于漏桶的限流中间件1\nfunc rateLimit1() func(ctx *gin.Context) {\n\t// 生成一个限流器，\n\trl := ratelimit1.New(100)\n\treturn func(c *gin.Context) {\n\t\t// 取水滴\n\t\tif rl.Take().Sub(time.Now()) > 0 {\n\t\t\t//time.Sleep(rl.Take().Sub(time.Now())) // 需要等这么长时间下一滴水才会滴下来\n\t\t\tc.String(http.StatusOK, \"rate limit...\")\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\tc.Next()\n\t}\n}\n\n// 基于令牌桶的限流中间件2\nfunc rateLimit2(fillInterval time.Duration, cap int64) func(ctx *gin.Context) {\n\trl := ratelimit2.NewBucket(fillInterval, cap)\n\treturn func(c *gin.Context) {\n\t\t//rl.Take()          // 这一次可以欠账\n\t\t// rl.TakeAvailable // 有令牌的时候才会取出令牌\n\t\tif rl.TakeAvailable(1) == 1 { // 此次取到令牌\n\t\t\tc.Next()\n\t\t\treturn\n\t\t}\n\t\tc.String(http.StatusOK, \"rate limit....\")\n\t\tc.Abort()\n\t}\n}\n\nfunc main() {\n\tr := gin.Default()\n\n\tr.GET(\"/ping\", rateLimit1(), pingHandler)\n\n\tr.GET(\"/hei\", rateLimit2(2*time.Second, 1), heiHandler)\n\n\tr.Run()\n}\n"
  },
  {
    "path": "redis_demo/go.mod",
    "content": "module redis_demo\n\ngo 1.14\n\nrequire (\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/go-redis/redis v6.15.9+incompatible // indirect\n\tgithub.com/go-redis/redis/v8 v8.3.3\n\tgithub.com/kr/pretty v0.1.0 // indirect\n\tgopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect\n)\n"
  },
  {
    "path": "redis_demo/go.sum",
    "content": "github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=\ngithub.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=\ngithub.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=\ngithub.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=\ngithub.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=\ngithub.com/go-redis/redis/v8 v8.3.3 h1:e0CL9fsFDK92pkIJH2XAeS/NwO2VuIOAoJvI6yktZFk=\ngithub.com/go-redis/redis/v8 v8.3.3/go.mod h1:jszGxBCez8QA1HWSmQxJO9Y82kNibbUmeYhKWrBejTU=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=\ngithub.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=\ngithub.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=\ngithub.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M=\ngithub.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=\ngithub.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=\ngithub.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=\ngithub.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=\ngithub.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=\ngithub.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngo.opentelemetry.io/otel v0.13.0 h1:2isEnyzjjJZq6r2EKMsFj4TxiQiexsM04AVhwbR/oBA=\ngo.opentelemetry.io/otel v0.13.0/go.mod h1:dlSNewoRYikTkotEnxdmuBHgzT+k/idJSfDv/FxEnOY=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0 h1:wBouT66WTYFXdxfVdz9sVWARVd/2vfGcmI45D2gj45M=\ngolang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "redis_demo/main.go",
    "content": "package main\n\n//\n//import (\n//\t\"errors\"\n//\t\"fmt\"\n//\t\"sync\"\n//\t\"time\"\n//\n//\t\"github.com/go-redis/redis\"\n//)\n//\n//// 声明一个全局的rdb变量\n//var rdb *redis.Client\n//\n//// 初始化连接\n//func initClient() (err error) {\n//\trdb = redis.NewClient(&redis.Options{\n//\t\tAddr:     \"localhost:6379\",\n//\t\tPassword: \"\",  // no password set\n//\t\tDB:       0,   // use default DB\n//\t\tPoolSize: 100, // 连接池大小\n//\t})\n//\n//\t_, err = rdb.Ping().Result()\n//\treturn err\n//}\n//\n//func redisExample() {\n//\terr := rdb.Set(\"score\", 100, 0).Err()\n//\tif err != nil {\n//\t\tfmt.Printf(\"set score failed, err:%v\\n\", err)\n//\t\treturn\n//\t}\n//\n//\tval, err := rdb.Get(\"score\").Result()\n//\tif err != nil {\n//\t\tfmt.Printf(\"get score failed, err:%v\\n\", err)\n//\t\treturn\n//\t}\n//\tfmt.Println(\"score\", val)\n//\n//\tval2, err := rdb.Get(\"name\").Result()\n//\tif err == redis.Nil {\n//\t\tfmt.Println(\"name does not exist\")\n//\t} else if err != nil {\n//\t\tfmt.Printf(\"get name failed, err:%v\\n\", err)\n//\t\treturn\n//\t} else {\n//\t\tfmt.Println(\"name\", val2)\n//\t}\n//}\n//\n//func hgetDemo() {\n//\tv, err := rdb.HGetAll(\"user\").Result()\n//\tif err != nil {\n//\t\t// redis.Nil\n//\t\t// 其他错误\n//\t\tfmt.Printf(\"hgetall failed, err:%v\\n\", err)\n//\t\treturn\n//\t}\n//\tfmt.Println(v)\n//\n//\tv2 := rdb.HMGet(\"user\", \"name\", \"age\").Val()\n//\tfmt.Println(v2)\n//\n//\tv3 := rdb.HGet(\"user\", \"age\").Val()\n//\tfmt.Println(v3)\n//\n//}\n//\n//func redisExample2() {\n//\tzsetKey := \"language_rank\"\n//\tlanguages := []redis.Z{\n//\t\tredis.Z{Score: 90.0, Member: \"Golang\"},\n//\t\tredis.Z{Score: 98.0, Member: \"Java\"},\n//\t\tredis.Z{Score: 95.0, Member: \"Python\"},\n//\t\tredis.Z{Score: 97.0, Member: \"JavaScript\"},\n//\t\tredis.Z{Score: 99.0, Member: \"C/C++\"},\n//\t}\n//\t// ZADD\n//\tnum, err := rdb.ZAdd(zsetKey, languages...).Result()\n//\tif err != nil {\n//\t\tfmt.Printf(\"zadd failed, err:%v\\n\", err)\n//\t\treturn\n//\t}\n//\tfmt.Printf(\"zadd %d succ.\\n\", num)\n//\n//\t// 把Golang的分数加10\n//\tnewScore, err := rdb.ZIncrBy(zsetKey, 10.0, \"Golang\").Result()\n//\tif err != nil {\n//\t\tfmt.Printf(\"zincrby failed, err:%v\\n\", err)\n//\t\treturn\n//\t}\n//\tfmt.Printf(\"Golang's score is %f now.\\n\", newScore)\n//\n//\t// 取分数最高的3个\n//\tret, err := rdb.ZRevRangeWithScores(zsetKey, 0, 2).Result()\n//\tif err != nil {\n//\t\tfmt.Printf(\"zrevrange failed, err:%v\\n\", err)\n//\t\treturn\n//\t}\n//\tfor _, z := range ret {\n//\t\tfmt.Println(z.Member, z.Score)\n//\t}\n//\n//\t// 取95~100分的\n//\top := redis.ZRangeBy{\n//\t\tMin: \"95\",\n//\t\tMax: \"100\",\n//\t}\n//\tret, err = rdb.ZRangeByScoreWithScores(zsetKey, op).Result()\n//\tif err != nil {\n//\t\tfmt.Printf(\"zrangebyscore failed, err:%v\\n\", err)\n//\t\treturn\n//\t}\n//\tfor _, z := range ret {\n//\t\tfmt.Println(z.Member, z.Score)\n//\t}\n//}\n//\n//func watchDemo() {\n//\t// 监视watch_count的值，并在值不变的前提下将其值+1\n//\tkey := \"watch_count\"\n//\terr := rdb.Watch(func(tx *redis.Tx) error {\n//\t\tn, err := tx.Get(key).Int()\n//\t\tif err != nil && err != redis.Nil {\n//\t\t\treturn err\n//\t\t}\n//\t\t_, err = tx.TxPipelined(func(pipe redis.Pipeliner) error {\n//\t\t\t// 业务逻辑\n//\t\t\ttime.Sleep(time.Second * 5)\n//\t\t\tpipe.Set(key, n+1, 0)\n//\t\t\treturn nil\n//\t\t})\n//\t\treturn err\n//\t}, key)\n//\tif err != nil {\n//\t\tfmt.Printf(\"tx exec failed, err:%v\\n\", err)\n//\t\treturn\n//\t}\n//\tfmt.Println(\"tx exec success\")\n//}\n//\n//// nilDemo 使用pipeline查询多个key时某个key有空值\n//// 使用pipeline多次执行HGetAll 不会因为某个key没有值而出现err\n//func nilDemo() {\n//\trdb.HMSet(\"nilDemo:k1\", map[string]interface{}{\"name\": \"q1mi\", \"score\": 18})\n//\trdb.HMSet(\"nilDemo:k3\", map[string]interface{}{\"name\": \"q1mi\", \"score\": 18})\n//\n//\tpipeline := rdb.Pipeline()\n//\tpipeline.HGetAll(\"nilDemo:k1\")\n//\tpipeline.HGetAll(\"nilDemo:k2\")\n//\tpipeline.HGetAll(\"nilDemo:k3\")\n//\tcmders, err := pipeline.Exec()\n//\tif err != nil {\n//\t\tfmt.Printf(\"nilDemo pipeline.Exec() failed, err:%v\\n\", err)\n//\t\treturn\n//\t}\n//\tfor _, cmder := range cmders {\n//\t\tfmt.Println(cmder == nil)\n//\t\tv, ok := cmder.(*redis.StringStringMapCmd)\n//\t\tif !ok {\n//\t\t\tfmt.Println(\"cmder.(*redis.StringStringMapCmd) failed\")\n//\t\t\tcontinue\n//\t\t}\n//\t\t// 打印值\n//\t\tfmt.Println(v.Val())\n//\t}\n//}\n//\n//// nilDemo2 使用pipeline查询多个key时某个key有空值\n//// 使用pipeline多次执行 Get 会因为某个key没有值而出现redis.Nil的err\n//func nilDemo2() {\n//\trdb.Set(\"nilDemo2:k1\", \"v1\", 0)\n//\trdb.Set(\"nilDemo2:k3\", \"v3\", 0)\n//\n//\tpipeline := rdb.Pipeline()\n//\tpipeline.Get(\"nilDemo2:k1\")\n//\tpipeline.Get(\"nilDemo2:k2\")\n//\tpipeline.Get(\"nilDemo2:k3\")\n//\tcmders, err := pipeline.Exec()\n//\tif err != nil {\n//\t\tfmt.Printf(\"nilDemo2 pipeline.Exec() failed, err:%v\\n\", err)\n//\t\treturn\n//\t}\n//\tfor _, cmder := range cmders {\n//\t\tfmt.Println(cmder == nil)\n//\t\tv, ok := cmder.(*redis.StringCmd)\n//\t\tif !ok {\n//\t\t\tfmt.Println(\"cmder.(*redis.StringCmd) failed\")\n//\t\t\tcontinue\n//\t\t}\n//\t\t// 打印值\n//\t\tfmt.Println(v.Val())\n//\t}\n//}\n//\n//// nilDemo3 使用pipeline查询多个key时某个key有空值\n//// 使用pipeline多次执行 HGet 会因为某个key没有值而出现redis.Nil的err\n//func nilDemo3() {\n//\trdb.HMSet(\"nilDemo3:k1\", map[string]interface{}{\"name\": \"q1mi\", \"score\": 18})\n//\trdb.HMSet(\"nilDemo3:k3\", map[string]interface{}{\"name\": \"q1mi\", \"score\": 18})\n//\n//\tpipeline := rdb.Pipeline()\n//\tpipeline.HGet(\"nilDemo3:k1\", \"score\")\n//\tpipeline.HGet(\"nilDemo3:k2\", \"score\")\n//\tpipeline.HGet(\"nilDemo3:k3\", \"score\")\n//\tcmders, err := pipeline.Exec()\n//\tif err != nil {\n//\t\tfmt.Printf(\"nilDemo3 pipeline.Exec() failed, err:%v\\n\", err)\n//\t\treturn\n//\t}\n//\tfor _, cmder := range cmders {\n//\t\tfmt.Println(cmder == nil)\n//\t\tv, ok := cmder.(*redis.StringCmd)\n//\t\tif !ok {\n//\t\t\tfmt.Println(\"cmder.(*redis.StringCmd) failed\")\n//\t\t\tcontinue\n//\t\t}\n//\t\t// 打印值\n//\t\tfmt.Println(v.Val())\n//\t}\n//}\n//\n//// nilDemo4 使用pipeline查询多个key中某个没有值的field\n//// 使用pipeline多次执行 HGet 会因为某个field没有值而出现redis.Nil的err\n//func nilDemo4() {\n//\trdb.HMSet(\"nilDemo4:k1\", map[string]interface{}{\"name\": \"q1mi\", \"score\": 18})\n//\trdb.HMSet(\"nilDemo4:k2\", map[string]interface{}{\"name\": \"q1mi\"})\n//\trdb.HMSet(\"nilDemo4:k3\", map[string]interface{}{\"name\": \"q1mi\", \"score\": 18})\n//\n//\tpipeline := rdb.Pipeline()\n//\tpipeline.HGet(\"nilDemo4:k1\", \"score\")\n//\tpipeline.HGet(\"nilDemo4:k2\", \"score\")\n//\tpipeline.HGet(\"nilDemo4:k3\", \"score\")\n//\tcmders, err := pipeline.Exec()\n//\tif err != nil && err != redis.Nil {\n//\t\tfmt.Printf(\"nilDemo4 pipeline.Exec() failed, err:%v\\n\", err)\n//\t\treturn\n//\t}\n//\tfor _, cmder := range cmders {\n//\t\tfmt.Println(cmder == nil)\n//\t\tv, ok := cmder.(*redis.StringCmd)\n//\t\tif !ok {\n//\t\t\tfmt.Println(\"cmder.(*redis.StringCmd) failed\")\n//\t\t\tcontinue\n//\t\t}\n//\t\t// 打印值\n//\t\tfmt.Println(v.Val())\n//\t}\n//}\n//\n//func main() {\n//\tif err := initClient(); err != nil {\n//\t\tfmt.Printf(\"init redis client failed, err:%v\\n\", err)\n//\t\treturn\n//\t}\n//\tfmt.Println(\"connect redis success...\")\n//\t// 程序退出时释放相关资源\n//\tdefer rdb.Close()\n//\n//\t//redisExample()\n//\t//hgetDemo()\n//\t//redisExample2()\n//\t//watchDemo()\n//\t//nilDemo()\n//\t//nilDemo2()\n//\t//nilDemo3()\n//\t//nilDemo4()\n//\n//\ttransDemo()\n//}\n//\n//func transDemo() {\n//\tconst routineCount = 100\n//\n//\tincrement := func(key string) error {\n//\t\ttxf := func(tx *redis.Tx) error {\n//\t\t\t// 获得当前值或零值\n//\t\t\tn, err := tx.Get(key).Int()\n//\t\t\tif err != nil && err != redis.Nil {\n//\t\t\t\treturn err\n//\t\t\t}\n//\n//\t\t\t// 实际操作（乐观锁定中的本地操作）\n//\t\t\tn++\n//\n//\t\t\t// 仅在监视的Key保持不变的情况下运行\n//\t\t\t_, err = tx.TxPipelined(func(pipe redis.Pipeliner) error {\n//\t\t\t\t// pipe 处理错误情况\n//\t\t\t\tpipe.Set(key, n, 0)\n//\t\t\t\treturn nil\n//\t\t\t})\n//\t\t\treturn err\n//\t\t}\n//\n//\t\tfor retries := routineCount; retries > 0; retries-- {\n//\t\t\terr := rdb.Watch(txf, key)\n//\t\t\tif err != redis.TxFailedErr {\n//\t\t\t\treturn err\n//\t\t\t}\n//\t\t\t// 乐观锁丢失\n//\t\t}\n//\t\treturn errors.New(\"increment reached maximum number of retries\")\n//\t}\n//\n//\tvar wg sync.WaitGroup\n//\twg.Add(routineCount)\n//\tfor i := 0; i < routineCount; i++ {\n//\t\tgo func() {\n//\t\t\tdefer wg.Done()\n//\n//\t\t\tif err := increment(\"counter3\"); err != nil {\n//\t\t\t\tfmt.Println(\"increment error:\", err)\n//\t\t\t}\n//\t\t}()\n//\t}\n//\twg.Wait()\n//\n//\tn, err := rdb.Get(\"counter3\").Int()\n//\tfmt.Println(\"ended with\", n, err)\n//}\n"
  },
  {
    "path": "redis_demo/new.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/go-redis/redis/v8\" // 注意导入的是新版本\n)\n\nvar (\n\trdb *redis.Client\n)\n\n// 初始化连接\nfunc initClient() (err error) {\n\trdb = redis.NewClient(&redis.Options{\n\t\tAddr:     \"localhost:6379\",\n\t\tPassword: \"\",  // no password set\n\t\tDB:       0,   // use default DB\n\t\tPoolSize: 100, // 连接池大小\n\t})\n\n\tctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\tdefer cancel()\n\n\t_, err = rdb.Ping(ctx).Result()\n\treturn err\n}\n\nfunc V8Example() {\n\tctx := context.Background()\n\tif err := initClient(); err != nil {\n\t\treturn\n\t}\n\n\terr := rdb.Set(ctx, \"key\", \"value\", 0).Err()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tval, err := rdb.Get(ctx, \"key\").Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Println(\"key\", val)\n\n\tval2, err := rdb.Get(ctx, \"key2\").Result()\n\tif err == redis.Nil {\n\t\tfmt.Println(\"key2 does not exist\")\n\t} else if err != nil {\n\t\tpanic(err)\n\t} else {\n\t\tfmt.Println(\"key2\", val2)\n\t}\n\t// Output: key value\n\t// key2 does not exist\n}\n\nfunc transDemo() {\n\tvar (\n\t\tmaxRetries   = 1000\n\t\troutineCount = 10\n\t)\n\tctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\tdefer cancel()\n\n\t// Increment 使用GET和SET命令以事务方式递增Key的值\n\tincrement := func(key string) error {\n\t\t// 事务函数\n\t\ttxf := func(tx *redis.Tx) error {\n\t\t\t// 获得key的当前值或零值\n\t\t\tn, err := tx.Get(ctx, key).Int()\n\t\t\tif err != nil && err != redis.Nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\t// 实际的操作代码（乐观锁定中的本地操作）\n\t\t\tn++\n\n\t\t\t// 操作仅在 Watch 的 Key 没发生变化的情况下提交\n\t\t\t_, err = tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error {\n\t\t\t\tpipe.Set(ctx, key, n, 0)\n\t\t\t\treturn nil\n\t\t\t})\n\t\t\treturn err\n\t\t}\n\n\t\t// 最多重试 maxRetries 次\n\t\tfor i := 0; i < maxRetries; i++ {\n\t\t\terr := rdb.Watch(ctx, txf, key)\n\t\t\tif err == nil {\n\t\t\t\t// 成功\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tif err == redis.TxFailedErr {\n\t\t\t\t// 乐观锁丢失 重试\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t// 返回其他的错误\n\t\t\treturn err\n\t\t}\n\n\t\treturn errors.New(\"increment reached maximum number of retries\")\n\t}\n\n\t// 模拟 routineCount 个并发同时去修改 counter3 的值\n\tvar wg sync.WaitGroup\n\twg.Add(routineCount)\n\tfor i := 0; i < routineCount; i++ {\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tif err := increment(\"counter3\"); err != nil {\n\t\t\t\tfmt.Println(\"increment error:\", err)\n\t\t\t}\n\t\t}()\n\t}\n\twg.Wait()\n\n\tn, err := rdb.Get(context.TODO(), \"counter3\").Int()\n\tfmt.Println(\"ended with\", n, err)\n}\n\nfunc incrementWithOutTX(key string) error {\n\tn, err := rdb.Get(context.Background(), key).Int()\n\tif err != nil {\n\t\treturn err\n\t}\n\tn++\n\trdb.Set(context.Background(), key, n, 0)\n\treturn nil\n}\n\nfunc main() {\n\tif err := initClient(); err != nil {\n\t\treturn\n\t}\n\t//ctx := context.Background()\n\t//iter := rdb.Scan(ctx, 0, \"prefix*\", 0).Iterator()\n\t//for iter.Next(ctx) {\n\t//\terr := rdb.Del(ctx, iter.Val()).Err()\n\t//\tif err != nil {\n\t//\t\tpanic(err)\n\t//\t}\n\t//}\n\t//if err := iter.Err(); err != nil {\n\t//\tpanic(err)\n\t//}\n\ttransDemo()\n}\n"
  },
  {
    "path": "shutdown_demo/go.mod",
    "content": "module shutdown_demo\n\ngo 1.14\n\nrequire github.com/gin-gonic/gin v1.6.3\n"
  },
  {
    "path": "shutdown_demo/go.sum",
    "content": "github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=\ngithub.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=\ngithub.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=\ngithub.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=\ngithub.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=\ngithub.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=\ngithub.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=\ngithub.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=\ngithub.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=\ngithub.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=\ngithub.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=\ngithub.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=\ngithub.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=\ngithub.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=\ngithub.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=\ngithub.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=\ngithub.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=\ngithub.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\n"
  },
  {
    "path": "shutdown_demo/main.go",
    "content": "// +build go1.8\n\npackage main\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc main() {\n\trouter := gin.Default()\n\trouter.GET(\"/\", func(c *gin.Context) {\n\t\ttime.Sleep(5 * time.Second)\n\t\tc.String(http.StatusOK, \"Welcome Gin Server\")\n\t})\n\n\tsrv := &http.Server{\n\t\tAddr:    \":8080\",\n\t\tHandler: router,\n\t}\n\n\tgo func() {\n\t\t// 开启一个goroutine启动服务\n\t\tif err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {\n\t\t\tlog.Fatalf(\"listen: %s\\n\", err)\n\t\t}\n\t}()\n\n\t// 等待中断信号来优雅地关闭服务器，为关闭服务器操作设置一个5秒的超时\n\tquit := make(chan os.Signal, 1) // 创建一个接收信号的通道\n\t// kill 默认会发送 syscall.SIGTERM 信号\n\t// kill -2 发送 syscall.SIGINT 信号，我们常用的Ctrl+C就是触发系统SIGINT信号\n\t// kill -9 发送 syscall.SIGKILL 信号，但是不能被捕获，所以不需要添加它\n\t// signal.Notify把收到的 syscall.SIGINT或syscall.SIGTERM 信号转发给quit\n\tsignal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) // 此处不会阻塞\n\t<-quit                                               // 阻塞在此，当接收到上述两种信号时才会往下执行\n\tlog.Println(\"Shutdown Server ...\")\n\t// 创建一个5秒超时的context\n\tctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\tdefer cancel()\n\t// 5秒内优雅关闭服务（将未处理完的请求处理完再关闭服务），超过5秒就超时退出\n\tif err := srv.Shutdown(ctx); err != nil {\n\t\tlog.Fatal(\"Server Shutdown: \", err)\n\t}\n\n\tlog.Println(\"Server exiting\")\n}\n"
  },
  {
    "path": "sqlx_demo/go.mod",
    "content": "module sqlx_demo\n\ngo 1.14\n\nrequire (\n\tgithub.com/jmoiron/sqlx v1.2.1-0.20200615141059-0794cb1f47ee\n\tgoogle.golang.org/appengine v1.6.6 // indirect\n)\n"
  },
  {
    "path": "sqlx_demo/go.sum",
    "content": "github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk=\ngithub.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=\ngithub.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=\ngithub.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=\ngithub.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=\ngithub.com/jmoiron/sqlx v1.2.1-0.20200615141059-0794cb1f47ee h1:59lyMGvZusByi7Rvctn8cxdVAjhiOnqCv3G5DrYApYQ=\ngithub.com/jmoiron/sqlx v1.2.1-0.20200615141059-0794cb1f47ee/go.mod h1:ClpsPFzLpSBl7MvJ+BhV0JHz4vmKRBarpvZ9644v9Oo=\ngithub.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=\ngithub.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=\ngithub.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngoogle.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=\ngoogle.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\n"
  },
  {
    "path": "sqlx_demo/main.go",
    "content": "package main\n\nimport (\n\t\"database/sql/driver\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/jmoiron/sqlx\"\n\t_ \"github.com/go-sql-driver/mysql\"\n\t\"strings\"\n)\n\n\nvar db *sqlx.DB\n\nfunc initDB() (err error) {\n\tdsn := \"root:root1234@tcp(127.0.0.1:13306)/sql_demo?charset=utf8mb4&parseTime=True\"\n\t// 也可以使用MustConnect连接不成功就panic\n\tdb, err = sqlx.Connect(\"mysql\", dsn)\n\tif err != nil {\n\t\tfmt.Printf(\"connect DB failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\n\tdb.SetMaxOpenConns(200)\n\tdb.SetMaxIdleConns(10)\n\treturn\n}\n\ntype user struct {\n\tID int  `db:\"id\"`\n\tAge int `db:\"age\"`\n\tName string  `db:\"name\"`\n}\n\nfunc (u user) Value() (driver.Value, error) {\n\treturn []interface{}{u.Name, u.Age}, nil\n}\n\n// BatchInsertUsers2 使用sqlx.In帮我们拼接语句和参数, 注意传入的参数是[]interface{}\nfunc BatchInsertUsers2(users []interface{}) error {\n\tquery, args, _ := sqlx.In(\n\t\t\"INSERT INTO user (name, age) VALUES (?), (?), (?)\",\n\t\tusers..., // 如果arg实现了 driver.Valuer, sqlx.In 会通过调用 Value()来展开它\n\t)\n\tfmt.Println(query) // 查看生成的querystring\n\tfmt.Println(args)  // 查看生成的args\n\t_, err := db.Exec(query, args...)\n\treturn err\n}\n\n// BatchInsertUsers3 使用NamedExec实现批量插入\nfunc BatchInsertUsers3(users []*user) error {\n\t_, err := db.NamedExec(\"INSERT INTO user (name, age) VALUES (:name, :age)\", users)\n\treturn err\n}\n\n// 查询单条数据示例\nfunc queryRowDemo() {\n\tsqlStr := \"select id, name, age from user where id=?\"\n\tvar u user\n\terr := db.Get(&u, sqlStr, 1)\n\tif err != nil {\n\t\tfmt.Printf(\"get failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tfmt.Printf(\"id:%d name:%s age:%d\\n\", u.ID, u.Name, u.Age)\n}\n\n// 查询多条数据示例\nfunc queryMultiRowDemo() {\n\tsqlStr := \"select id, name, age from user where id > ?\"\n\tvar users []user\n\terr := db.Select(&users, sqlStr, 0)\n\tif err != nil {\n\t\tfmt.Printf(\"query failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tfmt.Printf(\"users:%#v\\n\", users)\n}\n\n// 插入数据\nfunc insertRowDemo() {\n\tsqlStr := \"insert into user(name, age) values (?,?)\"\n\tret, err := db.Exec(sqlStr, \"沙河小王子\", 19)\n\tif err != nil {\n\t\tfmt.Printf(\"insert failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\ttheID, err := ret.LastInsertId() // 新插入数据的id\n\tif err != nil {\n\t\tfmt.Printf(\"get lastinsert ID failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tfmt.Printf(\"insert success, the id is %d.\\n\", theID)\n}\n\n// 更新数据\nfunc updateRowDemo() {\n\tsqlStr := \"update user set age=? where id = ?\"\n\tret, err := db.Exec(sqlStr, 39, 6)\n\tif err != nil {\n\t\tfmt.Printf(\"update failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tn, err := ret.RowsAffected() // 操作影响的行数\n\tif err != nil {\n\t\tfmt.Printf(\"get RowsAffected failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tfmt.Printf(\"update success, affected rows:%d\\n\", n)\n}\n\n// 删除数据\nfunc deleteRowDemo() {\n\tsqlStr := \"delete from user where id = ?\"\n\tret, err := db.Exec(sqlStr, 6)\n\tif err != nil {\n\t\tfmt.Printf(\"delete failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tn, err := ret.RowsAffected() // 操作影响的行数\n\tif err != nil {\n\t\tfmt.Printf(\"get RowsAffected failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tfmt.Printf(\"delete success, affected rows:%d\\n\", n)\n}\n\nfunc insertUserDemo()(err error){\n\tsqlStr := \"INSERT INTO user (name,age) VALUES (:name,:age)\"\n\t_, err = db.NamedExec(sqlStr,\n\t\tmap[string]interface{}{\n\t\t\t\"name\": \"七米\",\n\t\t\t\"age\": 28,\n\t\t})\n\treturn\n}\n\n\nfunc namedQuery(){\n\tsqlStr := \"SELECT * FROM user WHERE name=:name\"\n\t// 使用map做命名查询\n\trows, err := db.NamedQuery(sqlStr, map[string]interface{}{\"name\": \"七米\"})\n\tif err != nil {\n\t\tfmt.Printf(\"db.NamedQuery failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer rows.Close()\n\tfor rows.Next(){\n\t\tvar u user\n\t\terr := rows.StructScan(&u)\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"scan failed, err:%v\\n\", err)\n\t\t\tcontinue\n\t\t}\n\t\tfmt.Printf(\"user:%#v\\n\", u)\n\t}\n\n\tu := user{\n\t\tName: \"七米\",\n\t}\n\t// 使用结构体命名查询，根据结构体字段的 db tag进行映射\n\trows, err = db.NamedQuery(sqlStr, u)\n\tif err != nil {\n\t\tfmt.Printf(\"db.NamedQuery failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer rows.Close()\n\tfor rows.Next(){\n\t\tvar u user\n\t\terr := rows.StructScan(&u)\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"scan failed, err:%v\\n\", err)\n\t\t\tcontinue\n\t\t}\n\t\tfmt.Printf(\"user:%#v\\n\", u)\n\t}\n}\n\nfunc transactionDemo2()(err error) {\n\ttx, err := db.Beginx() // 开启事务\n\tif err != nil {\n\t\tfmt.Printf(\"begin trans failed, err:%v\\n\", err)\n\t\treturn err\n\t}\n\tdefer func() {\n\t\tif p := recover(); p != nil {\n\t\t\ttx.Rollback()\n\t\t\tpanic(p) // re-throw panic after Rollback\n\t\t} else if err != nil {\n\t\t\tfmt.Println(\"rollback\")\n\t\t\ttx.Rollback() // err is non-nil; don't change it\n\t\t} else {\n\t\t\terr = tx.Commit() // err is nil; if Commit returns error update err\n\t\t\tfmt.Println(\"commit\")\n\t\t}\n\t}()\n\n\tsqlStr1 := \"Update user set age=20 where id=?\"\n\n\trs, err := tx.Exec(sqlStr1, 1)\n\tif err!= nil{\n\t\treturn err\n\t}\n\tn, err := rs.RowsAffected()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif n != 1 {\n\t\treturn errors.New(\"exec sqlStr1 failed\")\n\t}\n\tsqlStr2 := \"Update user set age=50 where i=?\"\n\trs, err = tx.Exec(sqlStr2, 3)\n\tif err!=nil{\n\t\treturn err\n\t}\n\tn, err = rs.RowsAffected()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif n != 1 {\n\t\treturn errors.New(\"exec sqlStr1 failed\")\n\t}\n\treturn err\n}\n\n\n// QueryByIDs 根据给定ID查询\nfunc QueryByIDs(ids []int)(users []user, err error){\n\t// 动态填充id\n\tquery, args, err := sqlx.In(\"SELECT name, age FROM user WHERE id IN (?)\", ids)\n\tif err != nil {\n\t\treturn\n\t}\n\t// sqlx.In 返回带 `?` bindvar的查询语句, 我们使用Rebind()重新绑定它\n\tquery = db.Rebind(query)\n\n\terr = db.Select(&users, query, args...)\n\treturn\n}\n\n// QueryAndOrderByIDs 按照指定id查询并维护顺序\nfunc QueryAndOrderByIDs(ids []int)(users []user, err error){\n\t// 动态填充id\n\tstrIDs := make([]string, 0, len(ids))\n\tfor _, id := range ids {\n\t\tstrIDs = append(strIDs, fmt.Sprintf(\"%d\", id))\n\t}\n\tquery, args, err := sqlx.In(\"SELECT name, age FROM user WHERE id IN (?) ORDER BY FIND_IN_SET(id, ?)\", ids, strings.Join(strIDs, \",\"))\n\tif err != nil {\n\t\treturn\n\t}\n\n\t// sqlx.In 返回带 `?` bindvar的查询语句, 我们使用Rebind()重新绑定它\n\tquery = db.Rebind(query)\n\n\terr = db.Select(&users, query, args...)\n\treturn\n}\n\nfunc main() {\n\tif err :=initDB();err!= nil{\n\t\tfmt.Printf(\"init DB failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tfmt.Println(\"init DB success...\")\n\t//queryRowDemo()\n\t//queryMultiRowDemo()\n\t//insertUserDemo()\n\t//namedQuery()\n\t//transactionDemo2()\n\t//u1 := user{Name: \"xx\", Age: 18}\n\t//u2 := user{Name: \"xxx\", Age: 28}\n\t//u3 := user{Name: \"xxxx\", Age: 38}\n\t//users := []interface{}{u1, u2, u3}\n\t//BatchInsertUsers2(users)\n\n\tusers , err := QueryByIDs([]int{7,5,6, 1})\n\tif err != nil {\n\t\tfmt.Printf(\"QueryByIDs failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tfor _, user := range users{\n\t\tfmt.Printf(\"user:%#v\\n\", user)\n\t}\n\t// 1. 用代码去做排序\n\t// 2. 让MySQL排序\n\tfmt.Println(\"----\")\n\tusers ,err = QueryAndOrderByIDs([]int{7,5,6, 1})\n\tif err != nil {\n\t\tfmt.Printf(\"QueryByIDs failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tfor _, user := range users{\n\t\tfmt.Printf(\"user:%#v\\n\", user)\n\t}\n\n}\n"
  },
  {
    "path": "swagger_demo/docs/docs.go",
    "content": "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n// This file was generated by swaggo/swag\n\npackage docs\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"strings\"\n\n\t\"github.com/alecthomas/template\"\n\t\"github.com/swaggo/swag\"\n)\n\nvar doc = `{\n    \"schemes\": {{ marshal .Schemes }},\n    \"swagger\": \"2.0\",\n    \"info\": {\n        \"description\": \"{{.Description}}\",\n        \"title\": \"{{.Title}}\",\n        \"termsOfService\": \"http://swagger.io/terms/\",\n        \"contact\": {\n            \"name\": \"API Support\",\n            \"url\": \"http://www.swagger.io/support\",\n            \"email\": \"support@swagger.io\"\n        },\n        \"license\": {\n            \"name\": \"Apache 2.0\",\n            \"url\": \"http://www.apache.org/licenses/LICENSE-2.0.html\"\n        },\n        \"version\": \"{{.Version}}\"\n    },\n    \"host\": \"{{.Host}}\",\n    \"basePath\": \"{{.BasePath}}\",\n    \"paths\": {}\n}`\n\ntype swaggerInfo struct {\n\tVersion     string\n\tHost        string\n\tBasePath    string\n\tSchemes     []string\n\tTitle       string\n\tDescription string\n}\n\n// SwaggerInfo holds exported Swagger Info so clients can modify it\nvar SwaggerInfo = swaggerInfo{\n\tVersion:     \"1.0\",\n\tHost:        \"petstore.swagger.io\",\n\tBasePath:    \"/v2\",\n\tSchemes:     []string{},\n\tTitle:       \"Swagger Example API\",\n\tDescription: \"This is a sample server Petstore server.\",\n}\n\ntype s struct{}\n\nfunc (s *s) ReadDoc() string {\n\tsInfo := SwaggerInfo\n\tsInfo.Description = strings.Replace(sInfo.Description, \"\\n\", \"\\\\n\", -1)\n\n\tt, err := template.New(\"swagger_info\").Funcs(template.FuncMap{\n\t\t\"marshal\": func(v interface{}) string {\n\t\t\ta, _ := json.Marshal(v)\n\t\t\treturn string(a)\n\t\t},\n\t}).Parse(doc)\n\tif err != nil {\n\t\treturn doc\n\t}\n\n\tvar tpl bytes.Buffer\n\tif err := t.Execute(&tpl, sInfo); err != nil {\n\t\treturn doc\n\t}\n\n\treturn tpl.String()\n}\n\nfunc init() {\n\tswag.Register(swag.Name, &s{})\n}\n"
  },
  {
    "path": "swagger_demo/docs/swagger.json",
    "content": "{\n    \"swagger\": \"2.0\",\n    \"info\": {\n        \"description\": \"This is a sample server Petstore server.\",\n        \"title\": \"Swagger Example API\",\n        \"termsOfService\": \"http://swagger.io/terms/\",\n        \"contact\": {\n            \"name\": \"API Support\",\n            \"url\": \"http://www.swagger.io/support\",\n            \"email\": \"support@swagger.io\"\n        },\n        \"license\": {\n            \"name\": \"Apache 2.0\",\n            \"url\": \"http://www.apache.org/licenses/LICENSE-2.0.html\"\n        },\n        \"version\": \"1.0\"\n    },\n    \"host\": \"petstore.swagger.io\",\n    \"basePath\": \"/v2\",\n    \"paths\": {}\n}"
  },
  {
    "path": "swagger_demo/docs/swagger.yaml",
    "content": "basePath: /v2\nhost: petstore.swagger.io\ninfo:\n  contact:\n    email: support@swagger.io\n    name: API Support\n    url: http://www.swagger.io/support\n  description: This is a sample server Petstore server.\n  license:\n    name: Apache 2.0\n    url: http://www.apache.org/licenses/LICENSE-2.0.html\n  termsOfService: http://swagger.io/terms/\n  title: Swagger Example API\n  version: \"1.0\"\npaths: {}\nswagger: \"2.0\"\n"
  },
  {
    "path": "swagger_demo/go.mod",
    "content": "module swagger_demo\n\ngo 1.14\n\nrequire (\n\tgithub.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect\n\tgithub.com/gin-gonic/gin v1.6.3\n\tgithub.com/go-openapi/spec v0.19.9 // indirect\n\tgithub.com/go-openapi/swag v0.19.9 // indirect\n\tgithub.com/mailru/easyjson v0.7.6 // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/swaggo/files v0.0.0-20190704085106-630677cd5c14\n\tgithub.com/swaggo/gin-swagger v1.2.0\n\tgithub.com/swaggo/swag v1.6.7 // indirect\n\tgithub.com/urfave/cli v1.22.4 // indirect\n\tgithub.com/urfave/cli/v2 v2.2.0 // indirect\n\tgolang.org/x/net v0.0.0-20200904194848-62affa334b73 // indirect\n\tgolang.org/x/tools v0.0.0-20200904185747-39188db58858 // indirect\n\tgopkg.in/yaml.v2 v2.3.0 // indirect\n)\n"
  },
  {
    "path": "swagger_demo/go.sum",
    "content": "github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=\ngithub.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=\ngithub.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4=\ngithub.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=\ngithub.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=\ngithub.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=\ngithub.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=\ngithub.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=\ngithub.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/gin-contrib/gzip v0.0.1 h1:ezvKOL6jH+jlzdHNE4h9h8q8uMpDQjyl0NN0Jd7jozc=\ngithub.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w=\ngithub.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=\ngithub.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=\ngithub.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=\ngithub.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=\ngithub.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=\ngithub.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=\ngithub.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=\ngithub.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=\ngithub.com/go-openapi/jsonpointer v0.17.0 h1:nH6xp8XdXHx8dqveo0ZuJBluCO2qGrPbDNZ0dwoRHP0=\ngithub.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=\ngithub.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=\ngithub.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=\ngithub.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=\ngithub.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=\ngithub.com/go-openapi/jsonreference v0.19.0 h1:BqWKpV1dFd+AuiKlgtddwVIFQsuMpxfBDBHGfM2yNpk=\ngithub.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=\ngithub.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=\ngithub.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=\ngithub.com/go-openapi/jsonreference v0.19.4 h1:3Vw+rh13uq2JFNxgnMTGE1rnoieU9FmyE1gvnyylsYg=\ngithub.com/go-openapi/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=\ngithub.com/go-openapi/spec v0.19.0 h1:A4SZ6IWh3lnjH0rG0Z5lkxazMGBECtrZcbyYQi+64k4=\ngithub.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=\ngithub.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=\ngithub.com/go-openapi/spec v0.19.9 h1:9z9cbFuZJ7AcvOHKIY+f6Aevb4vObNDkTEyoMfO7rAc=\ngithub.com/go-openapi/spec v0.19.9/go.mod h1:vqK/dIdLGCosfvYsQV3WfC7N3TiZSnGY2RZKoFK7X28=\ngithub.com/go-openapi/swag v0.17.0 h1:iqrgMg7Q7SvtbWLlltPrkMs0UBJI6oTSs79JFRUi880=\ngithub.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=\ngithub.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=\ngithub.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=\ngithub.com/go-openapi/swag v0.19.9 h1:1IxuqvBUU3S2Bi4YC7tlP9SJF1gVpCvqN0T2Qof4azE=\ngithub.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY=\ngithub.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=\ngithub.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=\ngithub.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=\ngithub.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=\ngithub.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=\ngithub.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=\ngithub.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=\ngithub.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=\ngithub.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=\ngithub.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=\ngithub.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=\ngithub.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=\ngithub.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=\ngithub.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=\ngithub.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=\ngithub.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=\ngithub.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/swaggo/files v0.0.0-20190704085106-630677cd5c14 h1:PyYN9JH5jY9j6av01SpfRMb+1DWg/i3MbGOKPxJ2wjM=\ngithub.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E=\ngithub.com/swaggo/gin-swagger v1.2.0 h1:YskZXEiv51fjOMTsXrOetAjrMDfFaXD79PEoQBOe2W0=\ngithub.com/swaggo/gin-swagger v1.2.0/go.mod h1:qlH2+W7zXGZkczuL+r2nEBR2JTT+/lX05Nn6vPhc7OI=\ngithub.com/swaggo/swag v1.5.1 h1:2Agm8I4K5qb00620mHq0VJ05/KT4FtmALPIcQR9lEZM=\ngithub.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y=\ngithub.com/swaggo/swag v1.6.7 h1:e8GC2xDllJZr3omJkm9YfmK0Y56+rMO3cg0JBKNz09s=\ngithub.com/swaggo/swag v1.6.7/go.mod h1:xDhTyuFIujYiN3DKWC/H/83xcfHp+UE/IzWWampG7Zc=\ngithub.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=\ngithub.com/ugorji/go v1.1.5-pre/go.mod h1:FwP/aQVg39TXzItUBMwnWp9T9gPQnXw4Poh4/oBQZ/0=\ngithub.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=\ngithub.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=\ngithub.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=\ngithub.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI=\ngithub.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=\ngithub.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=\ngithub.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=\ngithub.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=\ngithub.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA=\ngithub.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=\ngithub.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k=\ngithub.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=\ngithub.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4=\ngithub.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190611141213-3f473d35a33a h1:+KkCgOMgnKSgenxTBoiwkMqTiouMIy/3o8RLdmSbGoY=\ngolang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA=\ngolang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b h1:/mJ+GKieZA6hFDQGdWZrjj4AXPl5ylY+5HusG80roy0=\ngolang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20200904185747-39188db58858 h1:xLt+iB5ksWcZVxqc+g9K41ZHy+6MKWfXCDsjSThnsPA=\ngolang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=\ngopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\n"
  },
  {
    "path": "swagger_demo/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/gin-gonic/gin\"\n\tswaggerFiles \"github.com/swaggo/files\"\n\t\"github.com/swaggo/gin-swagger\"\n\n\t_ \"github.com/swaggo/gin-swagger/example/basic/docs\" // 将Swag CLI生成的docs在这里导入\n)\n\n// @title Swagger Example API\n// @version 1.0\n// @description This is a sample server Petstore server.\n// @termsOfService http://swagger.io/terms/\n\n// @contact.name API Support\n// @contact.url http://www.swagger.io/support\n// @contact.email support@swagger.io\n\n// @license.name Apache 2.0\n// @license.url http://www.apache.org/licenses/LICENSE-2.0.html\n\n// @host petstore.swagger.io\n// @BasePath /v2\nfunc main() {\n\tr := gin.New()\n\n\turl := ginSwagger.URL(\"/swagger/doc.json\") // 指向API定义的网址\n\tr.GET(\"/swagger/*any\", ginSwagger.WrapHandler(swaggerFiles.Handler, url))\n\tr.GET(\"/swagger/*any\", ginSwagger.DisablingWrapHandler(swaggerFiles.Handler, \"NAME_OF_ENV_VARIABLE\"))\n\n\tr.Run()\n}"
  },
  {
    "path": "viper_demo/config.yaml",
    "content": "port: 8081\nversion: \"v0.0.2\"\n\nmysql:\n  host: \"127.0.0.1\"\n  port: 13306\n  dbname: \"sql_demo\"\n"
  },
  {
    "path": "viper_demo/go.mod",
    "content": "module viper_demo\n\ngo 1.14\n\nrequire (\n\tgithub.com/fsnotify/fsnotify v1.4.7\n\tgithub.com/gin-gonic/gin v1.6.3\n\tgithub.com/spf13/viper v1.7.0\n)\n"
  },
  {
    "path": "viper_demo/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ngithub.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=\ngithub.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=\ngithub.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=\ngithub.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=\ngithub.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=\ngithub.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=\ngithub.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=\ngithub.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=\ngithub.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=\ngithub.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=\ngithub.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=\ngithub.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=\ngithub.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=\ngithub.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=\ngithub.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=\ngithub.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=\ngithub.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=\ngithub.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=\ngithub.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=\ngithub.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=\ngithub.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=\ngithub.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=\ngithub.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=\ngithub.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=\ngithub.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=\ngithub.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=\ngithub.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=\ngithub.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=\ngithub.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=\ngithub.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=\ngithub.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=\ngithub.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=\ngithub.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=\ngithub.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=\ngithub.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=\ngithub.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=\ngithub.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=\ngithub.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=\ngithub.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=\ngithub.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=\ngithub.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=\ngithub.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=\ngithub.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=\ngithub.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=\ngithub.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=\ngithub.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=\ngithub.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=\ngithub.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=\ngithub.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=\ngithub.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=\ngithub.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=\ngithub.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=\ngithub.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=\ngithub.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=\ngithub.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=\ngithub.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=\ngithub.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=\ngithub.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=\ngithub.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=\ngithub.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=\ngithub.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=\ngithub.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=\ngithub.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=\ngithub.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=\ngithub.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=\ngithub.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=\ngithub.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=\ngithub.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=\ngithub.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=\ngithub.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=\ngithub.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=\ngo.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=\ngo.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=\ngopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=\ngopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\n"
  },
  {
    "path": "viper_demo/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\n\t\"github.com/spf13/viper\"\n)\n\ntype Config struct {\n\tPort        int    `mapstructure:\"port\"`\n\tVersion     string `mapstructure:\"version\"`\n\tMySQLConfig `mapstructure:\"mysql\"`\n}\n\ntype MySQLConfig struct {\n\tHost   string `mapstructure:\"host\"`\n\tDbName string `mapstructure:\"dbname\"`\n\tPort   int    `mapstructure:\"port\"`\n}\n\nfunc main() {\n\t// 设置默认值\n\tviper.SetDefault(\"fileDir\", \"./\")\n\t// 读取配置文件\n\tviper.SetConfigName(\"config\") // 配置文件名称(无扩展名)\n\tviper.SetConfigType(\"yaml\")   // 如果配置文件的名称中没有扩展名，则需要配置此项\n\t//viper.SetConfigFile(\"config.yaml\")\n\tviper.AddConfigPath(\"/etc/appname/\")  // 查找配置文件所在的路径\n\tviper.AddConfigPath(\"$HOME/.appname\") // 多次调用以添加多个搜索路径\n\tviper.AddConfigPath(\".\")              // 还可以在工作目录中查找配置\n\n\terr := viper.ReadInConfig() // 查找并读取配置文件\n\tif err != nil {             // 处理读取配置文件的错误\n\t\tpanic(fmt.Errorf(\"Fatal error config file: %s \\n\", err))\n\t}\n\n\t// 实时监控配置文件的变化\n\tviper.WatchConfig()\n\t// 当配置变化之后调用的一个回调函数\n\tviper.OnConfigChange(func(e fsnotify.Event) {\n\t\t// 配置文件发生变更之后会调用的回调函数\n\t\tfmt.Println(\"Config file changed:\", e.Name)\n\t})\n\n\tvar c Config\n\n\tif err := viper.Unmarshal(&c); err != nil {\n\t\tfmt.Printf(\"viper.Unmarshal failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tfmt.Printf(\"c:%#v\\n\", c)\n\n\t//r := gin.Default()\n\t//r.GET(\"/version\", func(c *gin.Context) {\n\t//\tc.String(http.StatusOK, viper.GetString(\"version\"))\n\t//})\n\t//r.Run()\n}\n"
  },
  {
    "path": "web_app/config.yaml",
    "content": "app:\n  name: \"web_app\"\n  mode: \"dev\"\n  port: 8081\nlog:\n  level: \"debug\"\n  filename: \"web_app.log\"\n  max_size: 200\n  max_age: 30\n  max_backups: 7\nmysql:\n  host: \"127.0.0.1\"\n  port: 13306\n  user: \"root\"\n  password: \"root1234\"\n  dbname: \"sql_demo\"\n  max_open_conns: 200\n  max_idle_conns: 50\nredis:\n  host: \"127.0.0.1\"\n  port: 16379\n  password: \"\"\n  db: 0\n  pool_size: 100"
  },
  {
    "path": "web_app/dao/mysql/mysql.go",
    "content": "package mysql\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/spf13/viper\"\n\t\"go.uber.org/zap\"\n\n\t_ \"github.com/go-sql-driver/mysql\"\n\t\"github.com/jmoiron/sqlx\"\n)\n\nvar db *sqlx.DB\n\nfunc Init() (err error) {\n\tdsn := fmt.Sprintf(\"%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True\",\n\t\tviper.GetString(\"mysql.user\"),\n\t\tviper.GetString(\"mysql.password\"),\n\t\tviper.GetString(\"mysql.host\"),\n\t\tviper.GetInt(\"mysql.port\"),\n\t\tviper.GetString(\"mysql.dbname\"),\n\t)\n\t// 也可以使用MustConnect连接不成功就panic\n\tdb, err = sqlx.Connect(\"mysql\", dsn)\n\tif err != nil {\n\t\tzap.L().Error(\"connect DB failed\", zap.Error(err))\n\t\treturn\n\t}\n\tdb.SetMaxOpenConns(viper.GetInt(\"mysql.max_open_conns\"))\n\tdb.SetMaxIdleConns(viper.GetInt(\"mysql.max_idle_conns\"))\n\treturn\n}\n\nfunc Close() {\n\t_ = db.Close()\n}\n"
  },
  {
    "path": "web_app/dao/redis/redis.go",
    "content": "package redis\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/go-redis/redis\"\n\t\"github.com/spf13/viper\"\n)\n\n// 声明一个全局的rdb变量\nvar rdb *redis.Client\n\n// Init 初始化连接\nfunc Init() (err error) {\n\trdb = redis.NewClient(&redis.Options{\n\t\tAddr: fmt.Sprintf(\"%s:%d\",\n\t\t\tviper.GetString(\"redis.host\"),\n\t\t\tviper.GetInt(\"redis.port\"),\n\t\t),\n\t\tPassword: viper.GetString(\"redis.password\"), // no password set\n\t\tDB:       viper.GetInt(\"redis.db\"),          // use default DB\n\t\tPoolSize: viper.GetInt(\"redis.pool_size\"),\n\t})\n\n\t_, err = rdb.Ping().Result()\n\treturn\n}\n\nfunc Close() {\n\t_ = rdb.Close()\n}\n"
  },
  {
    "path": "web_app/go.mod",
    "content": "module web_app\n\ngo 1.14\n\nrequire (\n\tgithub.com/fsnotify/fsnotify v1.4.7\n\tgithub.com/gin-gonic/gin v1.6.3\n\tgithub.com/go-redis/redis v6.15.8+incompatible\n\tgithub.com/go-sql-driver/mysql v1.5.0\n\tgithub.com/jmoiron/sqlx v1.2.0\n\tgithub.com/natefinch/lumberjack v2.0.0+incompatible\n\tgithub.com/spf13/viper v1.7.0\n\tgo.uber.org/zap v1.10.0\n\tgopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect\n)\n"
  },
  {
    "path": "web_app/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ngithub.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=\ngithub.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=\ngithub.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=\ngithub.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=\ngithub.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=\ngithub.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=\ngithub.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=\ngithub.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=\ngithub.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=\ngithub.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=\ngithub.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=\ngithub.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=\ngithub.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=\ngithub.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=\ngithub.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=\ngithub.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=\ngithub.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=\ngithub.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=\ngithub.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=\ngithub.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=\ngithub.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=\ngithub.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=\ngithub.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=\ngithub.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=\ngithub.com/go-redis/redis v6.15.8+incompatible h1:BKZuG6mCnRj5AOaWJXoCgf6rqTYnYJLe4en2hxT7r9o=\ngithub.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=\ngithub.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=\ngithub.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=\ngithub.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=\ngithub.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=\ngithub.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=\ngithub.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=\ngithub.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=\ngithub.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=\ngithub.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=\ngithub.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=\ngithub.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=\ngithub.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=\ngithub.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=\ngithub.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=\ngithub.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=\ngithub.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=\ngithub.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=\ngithub.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=\ngithub.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=\ngithub.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=\ngithub.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=\ngithub.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=\ngithub.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=\ngithub.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=\ngithub.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=\ngithub.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=\ngithub.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=\ngithub.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=\ngithub.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=\ngithub.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=\ngithub.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=\ngithub.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=\ngithub.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=\ngithub.com/onsi/ginkgo v1.13.0 h1:M76yO2HkZASFjXL0HSoZJ1AYEmQxNJmY41Jx1zNUq1Y=\ngithub.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=\ngithub.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=\ngithub.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=\ngithub.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=\ngithub.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=\ngithub.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=\ngithub.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=\ngithub.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=\ngithub.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=\ngithub.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=\ngithub.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=\ngithub.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=\ngithub.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=\ngithub.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=\ngithub.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=\ngithub.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=\ngithub.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=\ngithub.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=\ngithub.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=\ngithub.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=\ngithub.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=\ngithub.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=\ngithub.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=\ngithub.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=\ngithub.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=\ngithub.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=\ngithub.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=\ngithub.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=\ngithub.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=\ngo.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=\ngo.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=\ngo.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=\ngo.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=\ngo.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=\ngopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=\ngopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=\ngopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\n"
  },
  {
    "path": "web_app/logger/logger.go",
    "content": "package logger\n\nimport (\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"os\"\n\t\"runtime/debug\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/spf13/viper\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/natefinch/lumberjack\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\n// Init 初始化Logger\nfunc Init() (err error) {\n\twriteSyncer := getLogWriter(\n\t\tviper.GetString(\"log.filename\"),\n\t\tviper.GetInt(\"log.max_size\"),\n\t\tviper.GetInt(\"log.max_backups\"),\n\t\tviper.GetInt(\"log.max_age\"),\n\t)\n\tencoder := getEncoder()\n\tvar l = new(zapcore.Level)\n\terr = l.UnmarshalText([]byte(viper.GetString(\"log.level\")))\n\tif err != nil {\n\t\treturn\n\t}\n\tcore := zapcore.NewCore(encoder, writeSyncer, l)\n\n\tlg := zap.New(core, zap.AddCaller())\n\t// 替换zap库中全局的logger\n\tzap.ReplaceGlobals(lg)\n\treturn\n}\n\nfunc getEncoder() zapcore.Encoder {\n\tencoderConfig := zap.NewProductionEncoderConfig()\n\tencoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder\n\tencoderConfig.TimeKey = \"time\"\n\tencoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder\n\tencoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder\n\tencoderConfig.EncodeCaller = zapcore.ShortCallerEncoder\n\treturn zapcore.NewJSONEncoder(encoderConfig)\n}\n\nfunc getLogWriter(filename string, maxSize, maxBackup, maxAge int) zapcore.WriteSyncer {\n\tlumberJackLogger := &lumberjack.Logger{\n\t\tFilename:   filename,\n\t\tMaxSize:    maxSize,\n\t\tMaxBackups: maxBackup,\n\t\tMaxAge:     maxAge,\n\t}\n\treturn zapcore.AddSync(lumberJackLogger)\n}\n\n// GinLogger 接收gin框架默认的日志\nfunc GinLogger() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tstart := time.Now()\n\t\tpath := c.Request.URL.Path\n\t\tquery := c.Request.URL.RawQuery\n\t\tc.Next()\n\n\t\tcost := time.Since(start)\n\t\tzap.L().Info(path,\n\t\t\tzap.Int(\"status\", c.Writer.Status()),\n\t\t\tzap.String(\"method\", c.Request.Method),\n\t\t\tzap.String(\"path\", path),\n\t\t\tzap.String(\"query\", query),\n\t\t\tzap.String(\"ip\", c.ClientIP()),\n\t\t\tzap.String(\"user-agent\", c.Request.UserAgent()),\n\t\t\tzap.String(\"errors\", c.Errors.ByType(gin.ErrorTypePrivate).String()),\n\t\t\tzap.Duration(\"cost\", cost),\n\t\t)\n\t}\n}\n\n// GinRecovery recover掉项目可能出现的panic，并使用zap记录相关日志\nfunc GinRecovery(stack bool) gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tdefer func() {\n\t\t\tif err := recover(); err != nil {\n\t\t\t\t// Check for a broken connection, as it is not really a\n\t\t\t\t// condition that warrants a panic stack trace.\n\t\t\t\tvar brokenPipe bool\n\t\t\t\tif ne, ok := err.(*net.OpError); ok {\n\t\t\t\t\tif se, ok := ne.Err.(*os.SyscallError); ok {\n\t\t\t\t\t\tif strings.Contains(strings.ToLower(se.Error()), \"broken pipe\") || strings.Contains(strings.ToLower(se.Error()), \"connection reset by peer\") {\n\t\t\t\t\t\t\tbrokenPipe = true\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\thttpRequest, _ := httputil.DumpRequest(c.Request, false)\n\t\t\t\tif brokenPipe {\n\t\t\t\t\tzap.L().Error(c.Request.URL.Path,\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t)\n\t\t\t\t\t// If the connection is dead, we can't write a status to it.\n\t\t\t\t\tc.Error(err.(error)) // nolint: errcheck\n\t\t\t\t\tc.Abort()\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif stack {\n\t\t\t\t\tzap.L().Error(\"[Recovery from panic]\",\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t\tzap.String(\"stack\", string(debug.Stack())),\n\t\t\t\t\t)\n\t\t\t\t} else {\n\t\t\t\t\tzap.L().Error(\"[Recovery from panic]\",\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tc.AbortWithStatus(http.StatusInternalServerError)\n\t\t\t}\n\t\t}()\n\t\tc.Next()\n\t}\n}\n"
  },
  {
    "path": "web_app/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n\t\"time\"\n\t\"web_app/dao/mysql\"\n\t\"web_app/dao/redis\"\n\t\"web_app/logger\"\n\t\"web_app/routes\"\n\t\"web_app/settings\"\n\n\t\"github.com/spf13/viper\"\n\n\t\"go.uber.org/zap\"\n)\n\n// Go Web开发较通用的脚手架模板\n\nfunc main() {\n\t// 1. 加载配置\n\tif err := settings.Init(); err != nil {\n\t\tfmt.Printf(\"init settings failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\t// 2. 初始化日志\n\tif err := logger.Init(); err != nil {\n\t\tfmt.Printf(\"init logger failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer zap.L().Sync()\n\tzap.L().Debug(\"logger init success...\")\n\t// 3. 初始化MySQL连接\n\tif err := mysql.Init(); err != nil {\n\t\tfmt.Printf(\"init mysql failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer mysql.Close()\n\t// 4. 初始化Redis连接\n\tif err := redis.Init(); err != nil {\n\t\tfmt.Printf(\"init redis failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer redis.Close()\n\t// 5. 注册路由\n\tr := routes.Setup()\n\t// 6. 启动服务（优雅关机）\n\tsrv := &http.Server{\n\t\tAddr:    fmt.Sprintf(\":%d\", viper.GetInt(\"app.port\")),\n\t\tHandler: r,\n\t}\n\n\tgo func() {\n\t\t// 开启一个goroutine启动服务\n\t\tif err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {\n\t\t\tlog.Fatalf(\"listen: %s\\n\", err)\n\t\t}\n\t}()\n\n\t// 等待中断信号来优雅地关闭服务器，为关闭服务器操作设置一个5秒的超时\n\tquit := make(chan os.Signal, 1) // 创建一个接收信号的通道\n\t// kill 默认会发送 syscall.SIGTERM 信号\n\t// kill -2 发送 syscall.SIGINT 信号，我们常用的Ctrl+C就是触发系统SIGINT信号\n\t// kill -9 发送 syscall.SIGKILL 信号，但是不能被捕获，所以不需要添加它\n\t// signal.Notify把收到的 syscall.SIGINT或syscall.SIGTERM 信号转发给quit\n\tsignal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) // 此处不会阻塞\n\t<-quit                                               // 阻塞在此，当接收到上述两种信号时才会往下执行\n\tzap.L().Info(\"Shutdown Server ...\")\n\t// 创建一个5秒超时的context\n\tctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\tdefer cancel()\n\t// 5秒内优雅关闭服务（将未处理完的请求处理完再关闭服务），超过5秒就超时退出\n\tif err := srv.Shutdown(ctx); err != nil {\n\t\tzap.L().Fatal(\"Server Shutdown\", zap.Error(err))\n\t}\n\n\tzap.L().Info(\"Server exiting\")\n}\n"
  },
  {
    "path": "web_app/routes/routes.go",
    "content": "package routes\n\nimport (\n\t\"net/http\"\n\t\"web_app/logger\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc Setup() *gin.Engine {\n\tr := gin.New()\n\tr.Use(logger.GinLogger(), logger.GinRecovery(true))\n\n\tr.GET(\"/\", func(c *gin.Context) {\n\t\tc.String(http.StatusOK, \"ok\")\n\t})\n\treturn r\n}\n"
  },
  {
    "path": "web_app/settings/settings.go",
    "content": "package settings\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\t\"github.com/spf13/viper\"\n)\n\nfunc Init() (err error) {\n\tviper.SetConfigName(\"config\") // 指定配置文件名称（不需要带后缀）\n\tviper.SetConfigType(\"yaml\")   // 指定配置文件类型\n\tviper.AddConfigPath(\".\")      // 指定查找配置文件的路径（这里使用相对路径）\n\terr = viper.ReadInConfig()    // 读取配置信息\n\tif err != nil {\n\t\t// 读取配置信息失败\n\t\tfmt.Printf(\"viper.ReadInConfig() failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tviper.WatchConfig()\n\tviper.OnConfigChange(func(in fsnotify.Event) {\n\t\tfmt.Println(\"配置文件修改了...\")\n\t})\n\treturn\n}\n"
  },
  {
    "path": "web_app2/config.json",
    "content": "{\n  \"version\": \"v1.1.1\"\n}"
  },
  {
    "path": "web_app2/config.yaml",
    "content": "name: \"web_app\"\nmode: \"dev\"\nport: 8080\nversion: \"v0.1.4\"\nlog:\n  level: \"debug\"\n  filename: \"web_app.log\"\n  max_size: 200\n  max_age: 30\n  max_backups: 7\nmysql:\n  host: \"127.0.0.1\"\n  port: 13306\n  user: \"root\"\n  password: \"root1234\"\n  dbname: \"sql_demo\"\n  max_open_conns: 200\n  max_idle_conns: 50\nredis:\n  host: \"127.0.0.1\"\n  port: 16379\n  password: \"\"\n  db: 0\n  pool_size: 100"
  },
  {
    "path": "web_app2/dao/mysql/mysql.go",
    "content": "package mysql\n\nimport (\n\t\"fmt\"\n\t\"web_app/settings\"\n\n\t\"go.uber.org/zap\"\n\n\t_ \"github.com/go-sql-driver/mysql\"\n\t\"github.com/jmoiron/sqlx\"\n)\n\nvar db *sqlx.DB\n\nfunc Init(cfg *settings.MySQLConfig) (err error) {\n\tdsn := fmt.Sprintf(\"%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True\",\n\t\tcfg.User,\n\t\tcfg.Password,\n\t\tcfg.Host,\n\t\tcfg.Port,\n\t\tcfg.DbName,\n\t)\n\t// 也可以使用MustConnect连接不成功就panic\n\tdb, err = sqlx.Connect(\"mysql\", dsn)\n\tif err != nil {\n\t\tzap.L().Error(\"connect DB failed\", zap.Error(err))\n\t\treturn\n\t}\n\tdb.SetMaxOpenConns(cfg.MaxOpenConns)\n\tdb.SetMaxIdleConns(cfg.MaxIdleConns)\n\treturn\n}\n\nfunc Close() {\n\t_ = db.Close()\n}\n"
  },
  {
    "path": "web_app2/dao/redis/redis.go",
    "content": "package redis\n\nimport (\n\t\"fmt\"\n\t\"web_app/settings\"\n\n\t\"github.com/go-redis/redis\"\n)\n\n// 声明一个全局的rdb变量\nvar rdb *redis.Client\n\n// Init 初始化连接\nfunc Init(cfg *settings.RedisConfig) (err error) {\n\trdb = redis.NewClient(&redis.Options{\n\t\tAddr: fmt.Sprintf(\"%s:%d\",\n\t\t\tcfg.Host,\n\t\t\tcfg.Port,\n\t\t),\n\t\tPassword: cfg.Password, // no password set\n\t\tDB:       cfg.DB,       // use default DB\n\t\tPoolSize: cfg.PoolSize,\n\t})\n\n\t_, err = rdb.Ping().Result()\n\treturn\n}\n\nfunc Close() {\n\t_ = rdb.Close()\n}\n"
  },
  {
    "path": "web_app2/go.mod",
    "content": "module web_app\n\ngo 1.14\n\nrequire (\n\tgithub.com/fsnotify/fsnotify v1.4.9\n\tgithub.com/gin-gonic/gin v1.6.3\n\tgithub.com/go-redis/redis v6.15.8+incompatible\n\tgithub.com/go-sql-driver/mysql v1.5.0\n\tgithub.com/jmoiron/sqlx v1.2.0\n\tgithub.com/natefinch/lumberjack v2.0.0+incompatible\n\tgithub.com/onsi/ginkgo v1.13.0 // indirect\n\tgithub.com/spf13/viper v1.7.0\n\tgo.uber.org/zap v1.10.0\n\tgopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect\n)\n"
  },
  {
    "path": "web_app2/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ngithub.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=\ngithub.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=\ngithub.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=\ngithub.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=\ngithub.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=\ngithub.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=\ngithub.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=\ngithub.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=\ngithub.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=\ngithub.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=\ngithub.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=\ngithub.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=\ngithub.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=\ngithub.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=\ngithub.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=\ngithub.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=\ngithub.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=\ngithub.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=\ngithub.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=\ngithub.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=\ngithub.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=\ngithub.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=\ngithub.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=\ngithub.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=\ngithub.com/go-redis/redis v6.15.8+incompatible h1:BKZuG6mCnRj5AOaWJXoCgf6rqTYnYJLe4en2hxT7r9o=\ngithub.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=\ngithub.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=\ngithub.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=\ngithub.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=\ngithub.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=\ngithub.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=\ngithub.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=\ngithub.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=\ngithub.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=\ngithub.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=\ngithub.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=\ngithub.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=\ngithub.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=\ngithub.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=\ngithub.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=\ngithub.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=\ngithub.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=\ngithub.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=\ngithub.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=\ngithub.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=\ngithub.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=\ngithub.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=\ngithub.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=\ngithub.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=\ngithub.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=\ngithub.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=\ngithub.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=\ngithub.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=\ngithub.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=\ngithub.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=\ngithub.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=\ngithub.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=\ngithub.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=\ngithub.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=\ngithub.com/onsi/ginkgo v1.13.0 h1:M76yO2HkZASFjXL0HSoZJ1AYEmQxNJmY41Jx1zNUq1Y=\ngithub.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=\ngithub.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=\ngithub.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=\ngithub.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=\ngithub.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=\ngithub.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=\ngithub.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=\ngithub.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=\ngithub.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=\ngithub.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=\ngithub.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=\ngithub.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=\ngithub.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=\ngithub.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=\ngithub.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=\ngithub.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=\ngithub.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=\ngithub.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=\ngithub.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=\ngithub.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=\ngithub.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=\ngithub.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=\ngithub.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=\ngithub.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=\ngithub.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=\ngithub.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=\ngithub.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=\ngithub.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=\ngithub.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=\ngo.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=\ngo.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=\ngo.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=\ngo.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=\ngo.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=\ngopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=\ngopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=\ngopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\n"
  },
  {
    "path": "web_app2/logger/logger.go",
    "content": "package logger\n\nimport (\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"os\"\n\t\"runtime/debug\"\n\t\"strings\"\n\t\"time\"\n\t\"web_app/settings\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/natefinch/lumberjack\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\n// Init 初始化Logger\nfunc Init(cfg *settings.LogConfig) (err error) {\n\twriteSyncer := getLogWriter(\n\t\tcfg.Filename,\n\t\tcfg.MaxSize,\n\t\tcfg.MaxBackups,\n\t\tcfg.MaxAge,\n\t)\n\tencoder := getEncoder()\n\tvar l = new(zapcore.Level)\n\terr = l.UnmarshalText([]byte(cfg.Level))\n\tif err != nil {\n\t\treturn\n\t}\n\tcore := zapcore.NewCore(encoder, writeSyncer, l)\n\n\tlg := zap.New(core, zap.AddCaller())\n\t// 替换zap库中全局的logger\n\tzap.ReplaceGlobals(lg)\n\treturn\n}\n\nfunc getEncoder() zapcore.Encoder {\n\tencoderConfig := zap.NewProductionEncoderConfig()\n\tencoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder\n\tencoderConfig.TimeKey = \"time\"\n\tencoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder\n\tencoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder\n\tencoderConfig.EncodeCaller = zapcore.ShortCallerEncoder\n\treturn zapcore.NewJSONEncoder(encoderConfig)\n}\n\nfunc getLogWriter(filename string, maxSize, maxBackup, maxAge int) zapcore.WriteSyncer {\n\tlumberJackLogger := &lumberjack.Logger{\n\t\tFilename:   filename,\n\t\tMaxSize:    maxSize,\n\t\tMaxBackups: maxBackup,\n\t\tMaxAge:     maxAge,\n\t}\n\treturn zapcore.AddSync(lumberJackLogger)\n}\n\n// GinLogger 接收gin框架默认的日志\nfunc GinLogger() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tstart := time.Now()\n\t\tpath := c.Request.URL.Path\n\t\tquery := c.Request.URL.RawQuery\n\t\tc.Next()\n\n\t\tcost := time.Since(start)\n\t\tzap.L().Info(path,\n\t\t\tzap.Int(\"status\", c.Writer.Status()),\n\t\t\tzap.String(\"method\", c.Request.Method),\n\t\t\tzap.String(\"path\", path),\n\t\t\tzap.String(\"query\", query),\n\t\t\tzap.String(\"ip\", c.ClientIP()),\n\t\t\tzap.String(\"user-agent\", c.Request.UserAgent()),\n\t\t\tzap.String(\"errors\", c.Errors.ByType(gin.ErrorTypePrivate).String()),\n\t\t\tzap.Duration(\"cost\", cost),\n\t\t)\n\t}\n}\n\n// GinRecovery recover掉项目可能出现的panic，并使用zap记录相关日志\nfunc GinRecovery(stack bool) gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tdefer func() {\n\t\t\tif err := recover(); err != nil {\n\t\t\t\t// Check for a broken connection, as it is not really a\n\t\t\t\t// condition that warrants a panic stack trace.\n\t\t\t\tvar brokenPipe bool\n\t\t\t\tif ne, ok := err.(*net.OpError); ok {\n\t\t\t\t\tif se, ok := ne.Err.(*os.SyscallError); ok {\n\t\t\t\t\t\tif strings.Contains(strings.ToLower(se.Error()), \"broken pipe\") || strings.Contains(strings.ToLower(se.Error()), \"connection reset by peer\") {\n\t\t\t\t\t\t\tbrokenPipe = true\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\thttpRequest, _ := httputil.DumpRequest(c.Request, false)\n\t\t\t\tif brokenPipe {\n\t\t\t\t\tzap.L().Error(c.Request.URL.Path,\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t)\n\t\t\t\t\t// If the connection is dead, we can't write a status to it.\n\t\t\t\t\tc.Error(err.(error)) // nolint: errcheck\n\t\t\t\t\tc.Abort()\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif stack {\n\t\t\t\t\tzap.L().Error(\"[Recovery from panic]\",\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t\tzap.String(\"stack\", string(debug.Stack())),\n\t\t\t\t\t)\n\t\t\t\t} else {\n\t\t\t\t\tzap.L().Error(\"[Recovery from panic]\",\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tc.AbortWithStatus(http.StatusInternalServerError)\n\t\t\t}\n\t\t}()\n\t\tc.Next()\n\t}\n}\n"
  },
  {
    "path": "web_app2/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n\t\"time\"\n\t\"web_app/dao/mysql\"\n\t\"web_app/dao/redis\"\n\t\"web_app/logger\"\n\t\"web_app/routes\"\n\t\"web_app/settings\"\n\n\t\"go.uber.org/zap\"\n)\n\n// Go Web开发较通用的脚手架模板\n\nfunc main() {\n\t// 1. 加载配置\n\tif err := settings.Init(); err != nil {\n\t\tfmt.Printf(\"init settings failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tfmt.Println(settings.Conf)\n\tfmt.Println(settings.Conf.LogConfig == nil)\n\t// 2. 初始化日志\n\tif err := logger.Init(settings.Conf.LogConfig); err != nil {\n\t\tfmt.Printf(\"init logger failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer zap.L().Sync()\n\tzap.L().Debug(\"logger init success...\")\n\t// 3. 初始化MySQL连接\n\tif err := mysql.Init(settings.Conf.MySQLConfig); err != nil {\n\t\tfmt.Printf(\"init mysql failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer mysql.Close()\n\t// 4. 初始化Redis连接\n\tif err := redis.Init(settings.Conf.RedisConfig); err != nil {\n\t\tfmt.Printf(\"init redis failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\tdefer redis.Close()\n\t// 5. 注册路由\n\tr := routes.Setup(settings.Conf.Mode)\n\t// 6. 启动服务（优雅关机）\n\tfmt.Println(settings.Conf.Port)\n\tsrv := &http.Server{\n\t\tAddr:    fmt.Sprintf(\":%d\", settings.Conf.Port),\n\t\tHandler: r,\n\t}\n\n\tgo func() {\n\t\t// 开启一个goroutine启动服务\n\t\tif err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {\n\t\t\tlog.Fatalf(\"listen: %s\\n\", err)\n\t\t}\n\t}()\n\n\t// 等待中断信号来优雅地关闭服务器，为关闭服务器操作设置一个5秒的超时\n\tquit := make(chan os.Signal, 1) // 创建一个接收信号的通道\n\t// kill 默认会发送 syscall.SIGTERM 信号\n\t// kill -2 发送 syscall.SIGINT 信号，我们常用的Ctrl+C就是触发系统SIGINT信号\n\t// kill -9 发送 syscall.SIGKILL 信号，但是不能被捕获，所以不需要添加它\n\t// signal.Notify把收到的 syscall.SIGINT或syscall.SIGTERM 信号转发给quit\n\tsignal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) // 此处不会阻塞\n\t<-quit                                               // 阻塞在此，当接收到上述两种信号时才会往下执行\n\tzap.L().Info(\"Shutdown Server ...\")\n\t// 创建一个5秒超时的context\n\tctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\tdefer cancel()\n\t// 5秒内优雅关闭服务（将未处理完的请求处理完再关闭服务），超过5秒就超时退出\n\tif err := srv.Shutdown(ctx); err != nil {\n\t\tzap.L().Fatal(\"Server Shutdown\", zap.Error(err))\n\t}\n\n\tzap.L().Info(\"Server exiting\")\n}\n"
  },
  {
    "path": "web_app2/routes/routes.go",
    "content": "package routes\n\nimport (\n\t\"net/http\"\n\t\"web_app/logger\"\n\t\"web_app/settings\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc Setup(mode string) *gin.Engine {\n\tif mode == gin.ReleaseMode {\n\t\tgin.SetMode(gin.ReleaseMode)\n\t}\n\tr := gin.New()\n\tr.Use(logger.GinLogger(), logger.GinRecovery(true))\n\n\tr.GET(\"/version\", func(c *gin.Context) {\n\t\tc.String(http.StatusOK, settings.Conf.Version)\n\t})\n\treturn r\n}\n"
  },
  {
    "path": "web_app2/settings/settings.go",
    "content": "package settings\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\t\"github.com/spf13/viper\"\n)\n\n// Conf 全局变量，用来保存程序的所有配置信息\nvar Conf = new(AppConfig)\n\ntype AppConfig struct {\n\tName         string `mapstructure:\"name\"`\n\tMode         string `mapstructure:\"mode\"`\n\tVersion      string `mapstructure:\"version\"`\n\tPort         int    `mapstructure:\"port\"`\n\t*LogConfig   `mapstructure:\"log\"`\n\t*MySQLConfig `mapstructure:\"mysql\"`\n\t*RedisConfig `mapstructure:\"redis\"`\n}\n\ntype LogConfig struct {\n\tLevel      string `mapstructure:\"level\"`\n\tFilename   string `mapstructure:\"filename\"`\n\tMaxSize    int    `mapstructure:\"max_size\"`\n\tMaxAge     int    `mapstructure:\"max_age\"`\n\tMaxBackups int    `mapstructure:\"max_backups\"`\n}\n\ntype MySQLConfig struct {\n\tHost         string `mapstructure:\"host\"`\n\tUser         string `mapstructure:\"user\"`\n\tPassword     string `mapstructure:\"password\"`\n\tDbName       string `mapstructure:\"db_name\"`\n\tPort         int    `mapstructure:\"port\"`\n\tMaxOpenConns int    `mapstructure:\"max_open_conns\"`\n\tMaxIdleConns int    `mapstructure:\"max_idle_conns\"`\n}\n\ntype RedisConfig struct {\n\tHost     string `mapstructure:\"host\"`\n\tPassword string `mapstructure:\"password\"`\n\tPort     int    `mapstructure:\"port\"`\n\tDB       int    `mapstructure:\"db\"`\n\tPoolSize int    `mapstructure:\"pool_size\"`\n}\n\nfunc Init() (err error) {\n\n\tviper.SetConfigFile(\"config.yaml\")\n\t//viper.SetConfigName(\"config\") // 指定配置文件名称（不需要带后缀）\n\t//viper.SetConfigType(\"yaml\")   // 指定配置文件类型(专用于从远程获取配置信息时指定配置文件类型的)\n\tviper.AddConfigPath(\".\")   // 指定查找配置文件的路径（这里使用相对路径）\n\terr = viper.ReadInConfig() // 读取配置信息\n\tif err != nil {\n\t\t// 读取配置信息失败\n\t\tfmt.Printf(\"viper.ReadInConfig() failed, err:%v\\n\", err)\n\t\treturn\n\t}\n\t// 把读取到的配置信息反序列化到 Conf 变量中\n\tif err := viper.Unmarshal(Conf); err != nil {\n\t\tfmt.Printf(\"viper.Unmarshal failed, err:%v\\n\", err)\n\t}\n\tviper.WatchConfig()\n\tviper.OnConfigChange(func(in fsnotify.Event) {\n\t\tfmt.Println(\"配置文件修改了...\")\n\t\tif err := viper.Unmarshal(Conf); err != nil {\n\t\t\tfmt.Printf(\"viper.Unmarshal failed, err:%v\\n\", err)\n\t\t}\n\t})\n\treturn\n}\n"
  },
  {
    "path": "zap_demo/go.mod",
    "content": "module zap_demo\n\ngo 1.14\n\nrequire (\n\tgithub.com/gin-gonic/gin v1.6.3\n\tgithub.com/natefinch/lumberjack v2.0.0+incompatible\n\tgo.uber.org/zap v1.15.0\n\tgopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect\n)\n"
  },
  {
    "path": "zap_demo/go.sum",
    "content": "github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=\ngithub.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=\ngithub.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=\ngithub.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=\ngithub.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=\ngithub.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=\ngithub.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=\ngithub.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=\ngithub.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=\ngithub.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=\ngithub.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=\ngithub.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=\ngithub.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=\ngithub.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=\ngithub.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=\ngithub.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=\ngithub.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=\ngithub.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=\ngithub.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=\ngo.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=\ngo.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=\ngo.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=\ngo.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=\ngo.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=\ngolang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=\ngopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\nhonnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\n"
  },
  {
    "path": "zap_demo/main.go",
    "content": "package main\n\nimport (\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"os\"\n\t\"runtime/debug\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/natefinch/lumberjack\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\nvar logger *zap.Logger\nvar sugarLogger *zap.SugaredLogger\n\n//func main() {\n//\tInitLogger()\n//\tdefer logger.Sync()\n//\n//\tfor i:=0;i<10000;i++{\n//\t\tlogger.Info(\"test for log rotate...\")\n//\t}\n//\tsimpleHttpGet(\"www.sogo.com\")\n//\tsimpleHttpGet(\"http://www.sogo.com\")\n//}\n\nfunc main() {\n\tInitLogger()\n\t//r := gin.Default()\n\tr := gin.New()\n\tr.Use(GinLogger(logger), GinRecovery(logger, true))\n\tr.GET(\"/hello\", func(c *gin.Context) {\n\t\tc.String(http.StatusOK, \"hello q1mi!\")\n\t})\n\tr.Run()\n}\n\nfunc InitLogger() {\n\twriteSyncer := getLogWriter()\n\tencoder := getEncoder()\n\tcore := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)\n\n\tlogger = zap.New(core, zap.AddCaller())\n\tsugarLogger = logger.Sugar()\n}\n\nfunc getEncoder() zapcore.Encoder {\n\t//return zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())\n\tencoderConfig := zapcore.EncoderConfig{\n\t\tTimeKey:        \"ts\",\n\t\tLevelKey:       \"level\",\n\t\tNameKey:        \"logger\",\n\t\tCallerKey:      \"caller\",\n\t\tMessageKey:     \"msg\",\n\t\tStacktraceKey:  \"stacktrace\",\n\t\tLineEnding:     zapcore.DefaultLineEnding,\n\t\tEncodeLevel:    zapcore.LowercaseLevelEncoder,\n\t\tEncodeTime:     zapcore.ISO8601TimeEncoder,\n\t\tEncodeDuration: zapcore.SecondsDurationEncoder,\n\t\tEncodeCaller:   zapcore.ShortCallerEncoder,\n\t}\n\n\treturn zapcore.NewConsoleEncoder(encoderConfig)\n}\n\n//func getLogWriter() zapcore.WriteSyncer {\n//\tfile, _ := os.OpenFile(\"./test.log\", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0744)\n//\treturn zapcore.AddSync(file)\n//}\n\nfunc getLogWriter() zapcore.WriteSyncer {\n\tlumberJackLogger := &lumberjack.Logger{\n\t\tFilename:   \"./test.log\",\n\t\tMaxSize:    1,     // M\n\t\tMaxBackups: 5,     // 最大备份数量\n\t\tMaxAge:     30,    // 最大备份天数\n\t\tCompress:   false, // 是否压缩\n\t}\n\treturn zapcore.AddSync(lumberJackLogger)\n}\n\nfunc simpleHttpGet(url string) {\n\tresp, err := http.Get(url)\n\tif err != nil {\n\t\tsugarLogger.Error(\n\t\t\t\"Error fetching url..\",\n\t\t\tzap.String(\"url\", url),\n\t\t\tzap.Error(err))\n\t} else {\n\t\tsugarLogger.Info(\"Success..\",\n\t\t\tzap.String(\"statusCode\", resp.Status),\n\t\t\tzap.String(\"url\", url))\n\t\tresp.Body.Close()\n\t}\n}\n\n// GinLogger 接收gin框架默认的日志\nfunc GinLogger(logger *zap.Logger) gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tstart := time.Now()\n\t\tpath := c.Request.URL.Path\n\t\tquery := c.Request.URL.RawQuery\n\t\tc.Next()\n\n\t\tcost := time.Since(start)\n\t\tlogger.Info(path,\n\t\t\tzap.Int(\"status\", c.Writer.Status()),\n\t\t\tzap.String(\"method\", c.Request.Method),\n\t\t\tzap.String(\"path\", path),\n\t\t\tzap.String(\"query\", query),\n\t\t\tzap.String(\"ip\", c.ClientIP()),\n\t\t\tzap.String(\"user-agent\", c.Request.UserAgent()),\n\t\t\tzap.String(\"errors\", c.Errors.ByType(gin.ErrorTypePrivate).String()),\n\t\t\tzap.Duration(\"cost\", cost),\n\t\t)\n\t}\n}\n\n// GinRecovery recover掉项目可能出现的panic\nfunc GinRecovery(logger *zap.Logger, stack bool) gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tdefer func() {\n\t\t\tif err := recover(); err != nil {\n\t\t\t\t// Check for a broken connection, as it is not really a\n\t\t\t\t// condition that warrants a panic stack trace.\n\t\t\t\tvar brokenPipe bool\n\t\t\t\tif ne, ok := err.(*net.OpError); ok {\n\t\t\t\t\tif se, ok := ne.Err.(*os.SyscallError); ok {\n\t\t\t\t\t\tif strings.Contains(strings.ToLower(se.Error()), \"broken pipe\") || strings.Contains(strings.ToLower(se.Error()), \"connection reset by peer\") {\n\t\t\t\t\t\t\tbrokenPipe = true\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\thttpRequest, _ := httputil.DumpRequest(c.Request, false)\n\t\t\t\tif brokenPipe {\n\t\t\t\t\tlogger.Error(c.Request.URL.Path,\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t)\n\t\t\t\t\t// If the connection is dead, we can't write a status to it.\n\t\t\t\t\tc.Error(err.(error)) // nolint: errcheck\n\t\t\t\t\tc.Abort()\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif stack {\n\t\t\t\t\tlogger.Error(\"[Recovery from panic]\",\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t\tzap.String(\"stack\", string(debug.Stack())),\n\t\t\t\t\t)\n\t\t\t\t} else {\n\t\t\t\t\tlogger.Error(\"[Recovery from panic]\",\n\t\t\t\t\t\tzap.Any(\"error\", err),\n\t\t\t\t\t\tzap.String(\"request\", string(httpRequest)),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tc.AbortWithStatus(http.StatusInternalServerError)\n\t\t\t}\n\t\t}()\n\t\tc.Next()\n\t}\n}\n"
  }
]