├── .gitignore ├── LICENSE ├── README.md ├── client.zip ├── common ├── e │ ├── code.go │ └── error.go ├── enum │ ├── context.go │ └── status.go ├── result.go ├── retcode │ └── retcode.go └── utils │ ├── encrypt.go │ ├── generice.go │ ├── jwt.go │ └── oss.go ├── config ├── application-dev.yaml ├── application-release.yaml └── config.go ├── docker-compose.yaml ├── dockerfile ├── global ├── global.go └── tx │ ├── gormTx.go │ └── transactionManager.go ├── go.mod ├── go.sum ├── initialize ├── enter.go ├── gorm.go ├── redis.go └── router.go ├── internal ├── api │ ├── controller │ │ ├── category_controller.go │ │ ├── common_controller.go │ │ ├── dish_controller.go │ │ ├── employee_controller.go │ │ └── setmeal_controller.go │ ├── request │ │ ├── category.go │ │ ├── dish.go │ │ ├── employee.go │ │ └── setmeal.go │ └── response │ │ ├── category.go │ │ ├── dish.go │ │ ├── employee.go │ │ └── setmeal.go ├── model │ ├── category.go │ ├── dish.go │ ├── dish_flavor.go │ ├── employee.go │ ├── setmeal.go │ └── setmeal_dish.go ├── repository │ └── dao │ │ ├── category_dao.go │ │ ├── dish_dao.go │ │ ├── dish_flavor.go │ │ ├── employee_dao.go │ │ ├── setmeal_dao.go │ │ └── setmeal_dish_dao.go ├── router │ ├── admin │ │ ├── category.go │ │ ├── common.go │ │ ├── dish.go │ │ ├── employee.go │ │ └── setmeal.go │ └── router.go └── service │ ├── category_service.go │ ├── dish_service.go │ ├── employee_service.go │ └── setmeal_service.go ├── logger ├── log.go └── slog.go ├── main.go ├── middle └── jwt_middle.go └── script ├── sky.sql └── 数据库设计文档.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.idea 2 | *.log 3 | *.txt 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Wenhao Li 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 苍穹外卖Golang实现 2 | > 如果你不知道在Golang中如何进行Web开发,那么你或许可以参考该项目进行实践, 3 | > 该项目已经提供了一个初始的项目架构其思想。 4 | > 在这里你或许可以收获以下知识或经验例如: 5 | > 1. 如何在Go Web开发中选择合适的设计模式并设计架构。 6 | > 2. 如何对Gorm中的`Hook`、`Transaction`、`动态SQL`封装,以及了解`Context`在Gorm的中应用场景。 7 | > 3. 如何设计并使用`RouteGroup`解决复杂多变的中间件加载场景问题。 8 | > 4. 以及一些常规的开发经验…… 9 | 10 | **需求文档:** 11 | [苍穹外卖 (apifox.com)](https://apifox.com/apidoc/shared-93dd7a4f-adbc-4d2b-b6f5-24976908bc1c) 12 | 13 | **学习交流群** 14 | > `企鹅群`:828448599 15 | 16 | ## QuickStart 17 | 1. 切换到 web 前端目录,启动nginx.exe 18 | 2. 启动Redis 19 | 3. 启动Golang服务端 20 | 4. 访问 http:localhost 21 | 22 | ## 功能模块介绍 23 | 24 | ### 管理端 25 | 26 | 餐饮企业内部员工使用。 主要功能有: 27 | 28 | | 模块 | 描述 | 29 | | --------- | ------------------------------------------------------------ | 30 | | 登录/退出 | 内部员工必须登录后,才可以访问系统管理后台 | 31 | | 员工管理 | 管理员可以在系统后台对员工信息进行管理,包含查询、新增、编辑、禁用等功能 | 32 | | 分类管理 | 主要对当前餐厅经营的 菜品分类 或 套餐分类 进行管理维护, 包含查询、新增、修改、删除等 | 33 | | 菜品管理 | 主要维护各个分类下的菜品信息,包含查询、新增、修改、删除、启售、停售等功能 | 34 | | 套餐管理 | 主要维护当前餐厅中的套餐信息,包含查询、新增、修改、删除、启售、停售等功能 | 35 | | 订单管理 | 主要维护用户在移动端下的订单信息,包含查询、取消、派送、完成,以及订单报表下载等功能 | 36 | | 数据统计 | 主要完成对餐厅的各类数据统计,如营业额、用户数量、订单等 | 37 | 38 | ### 用户端 39 | 40 | | 模块 | 描述 | 41 | | ----------- | ------------------------------------------------------------ | 42 | | 登录/退出 | 对接微信小程序开放API接口实现微信授权登录。 | 43 | | 点餐-菜单 | 可在点餐界面选择 菜品分类\|套餐分类,并根据当前选择的分类加载其中的菜品信息,供用户查询选择 | 44 | | 点餐-购物车 | 用户选中的菜品就会加入用户的购物车,主要包含 查询购物车、加入购物车、删除购物车、清空购物车等功能 | 45 | | 订单支付 | 用户选完菜品/套餐后,可以对购物车菜品进行结算支付,这时就需要进行订单的支付 | 46 | | 个人信息 | 用户个人信息界面提供历史订单和收货地址管理,可查看历史订单信息或使用再来一单功能,收货地址可设置默认地址、新增地址、修改地址、删除地址等功能。 | 47 | 48 | **技术栈介绍** 49 | 50 | + `Gin`:Gin 是一个用 Go (Golang) 编写的轻量级 HTTP Web 框架,使用责任链模式对中间件加载进行,并且内部封装Sync.Pool、RouterGroup等多种强大的内部组件,是一个较为流行的框架,但开放度太高,容易导致一个人一个开发风格。` go get github.com/gin-gonic/gin` 51 | + Gorm:是使用较多的一个Object Relational Mapping:对象关系映射。`go get gorm.io/gorm` 52 | + go-redis:Golang中操作Redis的库 `go get github.com/go-redis/redis` 53 | + go-jwt: Golang中使用Jwt认证的库 `go get github.com/golang-jwt/jwt` 54 | + GoCron: Golang中使用的定时任务库:`go get github.com/go-co-op/gocron` 55 | 56 | ## 1.Quick Start 57 | 58 | **拉取项目** 59 | 60 | ```shell 61 | # 克隆项目到本地 62 | $ git clone https://github.com/Meng-Xin/sky-take-out-go.git 63 | # 执行 /script下的sky.sql脚本,创建数据库基础数据。 64 | $ sky.sql 65 | ``` 66 | 67 | **下载依赖** 68 | 69 | ```shell 70 | # 切换到工作目录 71 | cd /takeout/ 72 | 73 | # 下载依赖 74 | go mod tidy 75 | # 如果下载缓慢你需要去配置镜像源 76 | $ go env -w GO111MODULE=on 77 | $ go env -w GOPROXY=https://goproxy.cn,direct 78 | ``` 79 | 80 | **运行环境** 81 | 82 | + MySQL 83 | + Redis 84 | 85 | **运行服务器** 86 | 87 | ```shell 88 | # 默认启动使用dev配置 89 | $ go run main.go 90 | # 指定release配置文件启动 91 | $ go run main.go --env=release 92 | ``` 93 | ## 2.DockerCompose 启动 94 | ```shell 95 | # 下载项目 96 | $ git clone https://github.com/Meng-Xin/sky-take-out-go.git 97 | # 创建配置文件需要的共享卷 98 | $ mkdir /home/running/takeout/config 99 | $ mkdir /home/running/takeout/logs 100 | # 切换到运行目录 101 | $ cd /takeout 102 | # 拷贝配置文件到共享卷中 103 | $ cp ./config/*.yaml /home/running/takeout/config/ 104 | # 运行docker-compose 105 | $ docker-compose up -d 106 | ``` 107 | ## 项目架构图 108 | 109 | ```xquery 110 | client/ #WEB客户端 111 | common/ #存放通用内容的包 112 | |---e #存放自定义错误、错误code、code对应消息。 113 | |---code.go 114 | |---error.go 115 | |---msg.go 116 | |---enum #存放自定义枚举、常量、变量 117 | |---utils#工具包、例如jwt、limit限流、Email邮件、泛型工具函数 118 | |---result.go#自定义通用数据返回格式 119 | config/ #项目配置文件 120 | |---application-dev.yaml 121 | |---application-release.yaml 122 | |---config.go #配置文件解析类 123 | global/ #全局包,存放例如:GormDB、RedisClient、AllConfig…… 124 | |---global.go 125 | initialize/ #初始化包内部主要是需要初始化构建的组件 126 | |---enter.go 127 | |---gorm.go 128 | |---redis.go 129 | |---router.go 130 | internal/ #内部包,这里面主要实现Controller、Service、Repository层的操作。 131 | |---api/ 132 | |---model/ 133 | |---repository/ 134 | |---router/ 135 | |service/ 136 | logger/ #日志包,用来管理日志 137 | |---log.go 138 | middle/ #中间件包,主要该项目需要使用的中间件、例如身份、权限、限流、等拦截器功能。 139 | |---jwt_middle.go 140 | script/ #脚本包,主要做一些初始化脚本工作,例如MySQL数据初始化脚本、DevOps发布脚本等。 141 | 142 | go.mod #goalng的项目依赖文件,类似于java的maven 143 | main.go #入口函数,项目启动从main函数开始。 144 | 145 | ``` 146 | --- 147 | **流程实现图** 148 | ![流程实现图](http://xiaoxiangzhu.oss-cn-beijing.aliyuncs.com/doc/%E9%A1%B9%E7%9B%AE%E6%A8%A1%E5%9E%8B%E5%9B%BE.png) 149 | --- 150 | **项目架构图** 151 | ![项目架构图](http://xiaoxiangzhu.oss-cn-beijing.aliyuncs.com/doc/%E6%9E%B6%E6%9E%84%E5%9B%BE.png) 152 | --- 153 | **参考实现** 154 | ![工作台](http://xiaoxiangzhu.oss-cn-beijing.aliyuncs.com/doc/%E5%B7%A5%E4%BD%9C%E5%8F%B0.png) 155 | ![数据统计](http://xiaoxiangzhu.oss-cn-beijing.aliyuncs.com/doc/%E6%95%B0%E6%8D%AE%E7%BB%9F%E8%AE%A1.png) 156 | ![订单管理](http://xiaoxiangzhu.oss-cn-beijing.aliyuncs.com/doc/%E8%AE%A2%E5%8D%95%E7%AE%A1%E7%90%86.png) 157 | ![套餐管理](http://xiaoxiangzhu.oss-cn-beijing.aliyuncs.com/doc/%E5%A5%97%E9%A4%90%E7%AE%A1%E7%90%86.png) 158 | ![移动端购物车](http://xiaoxiangzhu.oss-cn-beijing.aliyuncs.com/doc/%E7%A7%BB%E5%8A%A8%E7%AB%AF%E8%B4%AD%E7%89%A9%E8%BD%A6.png) 159 | ![移动端历史订单](http://xiaoxiangzhu.oss-cn-beijing.aliyuncs.com/doc/%E5%8E%86%E5%8F%B2%E8%AE%A2%E5%8D%95.png) 160 | -------------------------------------------------------------------------------- /client.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Meng-Xin/take-out/d897575a8f1dc28e8dd0998eb530e25e7fda110d/client.zip -------------------------------------------------------------------------------- /common/e/code.go: -------------------------------------------------------------------------------- 1 | package e 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | "take-out/common" 7 | ) 8 | 9 | const ( 10 | SUCCESS = 1 // ok 11 | ERROR = 2 // 内部错误 12 | UNKNOW_IDENTITY = 403 // 未知身份 13 | MysqlERR = 1001 // mysql出错 14 | RedisERR = 1002 // redis出错 15 | ) 16 | 17 | var ErrMsg = map[int]string{ 18 | SUCCESS: "ok", 19 | ERROR: "内部错误", 20 | UNKNOW_IDENTITY: "未知身份", 21 | } 22 | 23 | func GetMsg(code int) string { 24 | return ErrMsg[code] 25 | } 26 | 27 | func Send(ctx *gin.Context, code int) { 28 | ctx.JSON(http.StatusOK, common.Result{Code: code, Msg: GetMsg(code)}) 29 | } 30 | -------------------------------------------------------------------------------- /common/e/error.go: -------------------------------------------------------------------------------- 1 | package e 2 | 3 | import "errors" 4 | 5 | var ( 6 | Error_PASSWORD_ERROR = errors.New("密码错误") 7 | Error_ACCOUNT_NOT_FOUND = errors.New("账号不存在") 8 | Error_ACCOUNT_LOCKED = errors.New("账号被锁定") 9 | Error_ALREADY_EXISTS = errors.New("已存在") 10 | Error_UNKNOWN_ERROR = errors.New("未知错误") 11 | Error_USER_NOT_LOGIN = errors.New("用户未登录") 12 | Error_CATEGORY_BE_RELATED_BY_SETMEAL = errors.New("当前分类关联了套餐,不能删除") 13 | Error_CATEGORY_BE_RELATED_BY_DISH = errors.New("当前分类关联了菜品,不能删除") 14 | Error_SHOPPING_CART_IS_NULL = errors.New("购物车数据为空,不能下单") 15 | Error_ADDRESS_BOOK_IS_NULL = errors.New("用户地址为空,不能下单") 16 | Error_LOGIN_FAILED = errors.New("登录失败") 17 | Error_UPLOAD_FAILED = errors.New("文件上传失败") 18 | Error_SETMEAL_ENABLE_FAILED = errors.New("套餐内包含未启售菜品,无法启售") 19 | Error_PASSWORD_EDIT_FAILED = errors.New("密码修改失败") 20 | Error_DISH_ON_SALE = errors.New("起售中的菜品不能删除") 21 | Error_SETMEAL_ON_SALE = errors.New("起售中的套餐不能删除") 22 | Error_DISH_BE_RELATED_BY_SETMEAL = errors.New("当前菜品关联了套餐,不能删除") 23 | Error_ORDER_STATUS_ERROR = errors.New("订单状态错误") 24 | Error_ORDER_NOT_FOUND = errors.New("订单不存在") 25 | ) 26 | -------------------------------------------------------------------------------- /common/enum/context.go: -------------------------------------------------------------------------------- 1 | package enum 2 | 3 | const ( 4 | CurrentId = "currentId" 5 | CurrentName = "currentName" 6 | ) 7 | 8 | type PageNum = int 9 | 10 | const ( 11 | MaxPageSize PageNum = 100 // 单页最大数量 12 | MinPageSize PageNum = 10 // 单页最小数量 13 | ) 14 | 15 | type CommonInt = int 16 | 17 | const ( 18 | MaxUrl CommonInt = 1 19 | ) 20 | -------------------------------------------------------------------------------- /common/enum/status.go: -------------------------------------------------------------------------------- 1 | package enum 2 | 3 | type CommonStatus = int 4 | 5 | const ( 6 | ENABLE CommonStatus = 1 // 启用 7 | DISABLE CommonStatus = 0 // 禁用 8 | ) 9 | -------------------------------------------------------------------------------- /common/result.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "gorm.io/gorm" 5 | "take-out/common/enum" 6 | ) 7 | 8 | type Result struct { 9 | Code int `json:"code"` 10 | Data interface{} `json:"data"` 11 | Msg string `json:"msg"` 12 | } 13 | 14 | type PageResult struct { 15 | Total int64 `json:"total"` //总记录数 16 | Records interface{} `json:"records"` //当前页数据集合 17 | } 18 | 19 | // PageVerify 分页查询 过滤器 20 | func PageVerify(page *int, pageSize *int) { 21 | // 过滤 当前页、单页数量 22 | if *page < 1 { 23 | *page = 1 24 | } 25 | switch { 26 | case *pageSize > 100: 27 | *pageSize = enum.MaxPageSize 28 | case *pageSize <= 0: 29 | *pageSize = enum.MinPageSize 30 | } 31 | } 32 | 33 | func (p *PageResult) Paginate(page *int, pageSize *int) func(*gorm.DB) *gorm.DB { 34 | return func(d *gorm.DB) *gorm.DB { 35 | // 分页校验 36 | PageVerify(page, pageSize) 37 | 38 | //// 计算总页数 Total / PageSize = Pages 39 | //p.Pages = p.Total / int64(p.PageSize) 40 | //// 如果还有余那么也可以查询 41 | //if p.Total%int64(p.PageSize) != 0 { 42 | // p.Pages++ 43 | //} 44 | 45 | // 拼接分页 46 | d.Offset((*page - 1) * *pageSize).Limit(*pageSize) 47 | return d 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /common/retcode/retcode.go: -------------------------------------------------------------------------------- 1 | package retcode 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/gin-gonic/gin" 7 | "github.com/go-sql-driver/mysql" 8 | "net/http" 9 | "take-out/common/e" 10 | ) 11 | 12 | // ErrorCodeGetter 错误接口:用于提取错误码 13 | type ErrorCodeGetter interface { 14 | GetCode() int 15 | } 16 | 17 | // Error 通用错误结构体 18 | type Error struct { 19 | ErrCode int 20 | ErrMsg string 21 | } 22 | 23 | func (e *Error) Error() string { 24 | return e.ErrMsg 25 | } 26 | 27 | func (e *Error) GetCode() int { 28 | return e.ErrCode 29 | } 30 | 31 | func NewError(code int, msg string) *Error { 32 | return &Error{ 33 | ErrCode: code, 34 | ErrMsg: msg, 35 | } 36 | } 37 | 38 | // OK 渲染成功响应 39 | func OK(c *gin.Context, output interface{}) { 40 | RenderReply(c, output) 41 | } 42 | 43 | // Fatal 渲染服务错误响应 44 | func Fatal(c *gin.Context, e error, msg string) { 45 | code := GetErrCode(e) 46 | fmt.Printf("%s %+v", msg, e) 47 | if msg == "" { 48 | msg = e.Error() 49 | } 50 | RenderErrMsg(c, code, msg) 51 | } 52 | 53 | // CustomError 渲染自定义错误 54 | func CustomError(c *gin.Context, code int, msg string) { 55 | fmt.Printf("%d %s", code, msg) 56 | RenderErrMsg(c, code, msg) 57 | } 58 | 59 | // RenderReply 60 | func RenderReply(c *gin.Context, data interface{}) { 61 | render(c, e.SUCCESS, data, nil) 62 | } 63 | 64 | // RenderErrMsg 65 | func RenderErrMsg(c *gin.Context, code int, msg string) { 66 | render(c, code, nil, errors.New(msg)) 67 | } 68 | 69 | // render 70 | func render(c *gin.Context, code int, data interface{}, err error) { 71 | var msg string 72 | if err != nil { 73 | msg = err.Error() 74 | if code == e.UNKNOW_IDENTITY { 75 | code = GetErrCode(err) 76 | } 77 | } else if defaultMsg, ok := e.ErrMsg[code]; ok { 78 | msg = defaultMsg 79 | } 80 | 81 | r := gin.H{ 82 | "code": code, 83 | "msg": msg, 84 | "data": data, 85 | } 86 | c.Set("return_code", code) 87 | c.JSON(http.StatusOK, r) 88 | } 89 | 90 | // GetErrCode 获取错误码 91 | func GetErrCode(err error) int { 92 | if errGetter, ok := err.(ErrorCodeGetter); ok { 93 | return errGetter.GetCode() 94 | } 95 | 96 | switch errType := err.(type) { 97 | case *mysql.MySQLError: 98 | return int(errType.Number) 99 | case *Error: 100 | return errType.ErrCode 101 | } 102 | 103 | return e.UNKNOW_IDENTITY 104 | } 105 | -------------------------------------------------------------------------------- /common/utils/encrypt.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | ) 7 | 8 | // MD5V 对目标字符串取Hash salt:加盐字段,iteration:hash迭代轮数。 9 | func MD5V(str string, salt string, iteration int) string { 10 | b := []byte(str) 11 | s := []byte(salt) 12 | h := md5.New() 13 | h.Write(s) // 先传入盐值,之前因为顺序错了卡了很久 14 | h.Write(b) 15 | var res []byte 16 | res = h.Sum(nil) 17 | for i := 0; i < iteration-1; i++ { 18 | h.Reset() 19 | h.Write(res) 20 | res = h.Sum(nil) 21 | } 22 | return hex.EncodeToString(res) 23 | } 24 | -------------------------------------------------------------------------------- /common/utils/generice.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | // Ordered 代表所有可比大小排序的类型 4 | type Ordered interface { 5 | Integer | Float | ~string 6 | } 7 | 8 | type Integer interface { 9 | Signed | Unsigned 10 | } 11 | 12 | type Signed interface { 13 | ~int | ~int8 | ~int16 | ~int32 | ~int64 14 | } 15 | 16 | type Unsigned interface { 17 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr 18 | } 19 | 20 | type Float interface { 21 | ~float32 | ~float64 22 | } 23 | 24 | func Abs[T Integer](input T) T { 25 | if input < 0 { 26 | return -input 27 | } 28 | return input 29 | } 30 | -------------------------------------------------------------------------------- /common/utils/jwt.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "errors" 5 | "github.com/golang-jwt/jwt/v5" 6 | "time" 7 | ) 8 | 9 | // CustomPayload 自定义载荷继承原有接口并附带自己的字段 10 | type CustomPayload struct { 11 | UserId uint64 12 | GrantScope string 13 | jwt.RegisteredClaims 14 | } 15 | 16 | // GenerateToken 生成Token uid 用户id subject 签发对象 secret 加盐 17 | func GenerateToken(uid uint64, subject string, secret string) (string, error) { 18 | claim := CustomPayload{ 19 | UserId: uid, 20 | GrantScope: subject, 21 | RegisteredClaims: jwt.RegisteredClaims{ 22 | Issuer: "Auth_Server", //签发者 23 | Subject: subject, //签发对象 24 | Audience: jwt.ClaimStrings{"PC", "Wechat_Program"}, //签发受众 25 | ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)), //过期时间 26 | NotBefore: jwt.NewNumericDate(time.Now().Add(time.Second)), //最早使用时间 27 | IssuedAt: jwt.NewNumericDate(time.Now()), //签发时间 28 | }, 29 | } 30 | token, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claim).SignedString([]byte(secret)) 31 | return token, err 32 | } 33 | 34 | func ParseToken(token string, secret string) (*CustomPayload, error) { 35 | // 解析token 36 | parseToken, err := jwt.ParseWithClaims(token, &CustomPayload{}, func(token *jwt.Token) (i interface{}, err error) { 37 | return []byte(secret), nil 38 | }) 39 | if err != nil { 40 | return nil, err 41 | } 42 | if claims, ok := parseToken.Claims.(*CustomPayload); ok && parseToken.Valid { // 校验token 43 | return claims, nil 44 | } 45 | return nil, errors.New("invalid token") 46 | } 47 | -------------------------------------------------------------------------------- /common/utils/oss.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "github.com/aliyun/aliyun-oss-go-sdk/oss" 6 | "mime/multipart" 7 | "take-out/global" 8 | ) 9 | 10 | func AliyunOss(fileName string, file *multipart.FileHeader) (string, error) { 11 | config := global.Config.AliOss 12 | client, err := oss.New(config.EndPoint, config.AccessKeyId, config.AccessKeySecret) 13 | if err != nil { 14 | return "", err 15 | } 16 | bucket, err := client.Bucket(config.BucketName) 17 | if err != nil { 18 | return "", err 19 | } 20 | 21 | fileData, err := file.Open() 22 | defer fileData.Close() 23 | 24 | err = bucket.PutObject(fileName, fileData) 25 | if err != nil { 26 | return "", err 27 | } 28 | imagePath := "https://" + config.BucketName + "." + config.EndPoint + "/" + fileName 29 | fmt.Println("文件上传到:", imagePath) 30 | return imagePath, nil 31 | } 32 | -------------------------------------------------------------------------------- /config/application-dev.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8080 3 | # debug | release | test 运行环境 4 | level: debug 5 | 6 | datasource: 7 | host: 127.0.0.1 8 | port: 3306 9 | username: root 10 | password: root 11 | db_name: sky_take_out 12 | config: charset=utf8mb4&parseTime=True&loc=Local 13 | 14 | redis: 15 | host: 127.0.0.1 16 | port: 6379 17 | password: 18 | data_base: 1 19 | 20 | log: 21 | level: debug 22 | filepath: ./logger/systemLog.txt 23 | 24 | 25 | jwt: 26 | admin: 27 | # 设置jwt签名加密时使用的秘钥 28 | secret: admin 29 | # 设置jwt过期时间 30 | ttl: 7200000 31 | # 设置前端传递过来的令牌名称 32 | name: token 33 | user: 34 | # 设置jwt签名加密时使用的秘钥 35 | secret: user 36 | # 设置jwt过期时间 37 | ttl: 7200000 38 | # 设置前端传递过来的令牌名称 39 | name: Authorization 40 | 41 | 42 | alioss: 43 | endpoint: YouOption 44 | access_key_id: YouOption 45 | access_key_secret: YouOption 46 | bucket_name: YouOption 47 | 48 | wechat: 49 | appid: YouOption 50 | secret: YouOption -------------------------------------------------------------------------------- /config/application-release.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 10000 3 | 4 | datasource: 5 | host: 127.0.0.1 6 | port: 3306 7 | username: root 8 | password: root 9 | db_name: sky-take-out 10 | 11 | redis: 12 | host: 127.0.0.1 13 | port: 6379 14 | password: 15 | data_base: 1 16 | 17 | log: 18 | level: info 19 | filepath: ./logger/systemLog.txt 20 | jwt: 21 | admin: 22 | # 设置jwt签名加密时使用的秘钥 23 | secret: admin 24 | # 设置jwt过期时间 25 | ttl: 7200000 26 | # 设置前端传递过来的令牌名称 27 | name: token 28 | user: 29 | # 设置jwt签名加密时使用的秘钥 30 | secret: user 31 | # 设置jwt过期时间 32 | ttl: 7200000 33 | # 设置前端传递过来的令牌名称 34 | name: Authorization 35 | 36 | 37 | alioss: 38 | endpoint: YouOption 39 | access_key_id: YouOption 40 | access_key_secret: YouOption 41 | bucket_name: YouOption 42 | 43 | wechat: 44 | appid: YouOption 45 | secret: YouOption -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "fmt" 5 | "github.com/spf13/pflag" 6 | "github.com/spf13/viper" 7 | ) 8 | 9 | var envPtr = pflag.String("env", "dev", "Environment: dev or prod") 10 | 11 | func InitLoadConfig() *AllConfig { 12 | // 使用pflag库来读取命令行参数,用于指定环境,默认为"dev" 13 | pflag.Parse() 14 | 15 | config := viper.New() 16 | // 设置读取路径 17 | config.AddConfigPath("./config") 18 | // 设置读取文件名字 19 | config.SetConfigName(fmt.Sprintf("application-%s", *envPtr)) 20 | // 设置读取文件类型 21 | config.SetConfigType("yaml") 22 | // 读取文件载体 23 | var configData *AllConfig 24 | // 读取配置文件 25 | err := config.ReadInConfig() 26 | if err != nil { 27 | panic(fmt.Errorf("Use Viper ReadInConfig Fatal error config err:%s \n", err)) 28 | } 29 | // 查找对应配置文件 30 | err = config.Unmarshal(&configData) 31 | if err != nil { 32 | panic(fmt.Errorf("read config file to struct err: %s\n", err)) 33 | } 34 | // 打印配置文件信息 35 | fmt.Printf("配置文件信息:%+v", configData) 36 | return configData 37 | } 38 | 39 | // AllConfig 整合Config 40 | type AllConfig struct { 41 | Server Server 42 | DataSource DataSource 43 | Redis Redis 44 | Log Log 45 | Jwt Jwt 46 | AliOss AliOss 47 | Wechat Wechat 48 | } 49 | 50 | type Server struct { 51 | Port string 52 | Level string 53 | } 54 | 55 | type DataSource struct { 56 | Host string 57 | Port string 58 | UserName string 59 | Password string 60 | DBName string `mapstructure:"db_name"` 61 | Config string 62 | } 63 | 64 | func (d *DataSource) Dsn() string { 65 | return d.UserName + ":" + d.Password + "@tcp(" + d.Host + ":" + d.Port + ")/" + d.DBName + "?" + d.Config 66 | } 67 | 68 | type Redis struct { 69 | Host string 70 | Port string 71 | Password string 72 | DataBase int `mapstructure:"data_base"` 73 | } 74 | 75 | type Log struct { 76 | Level string 77 | FilePath string 78 | } 79 | 80 | type Jwt struct { 81 | Admin JwtOption 82 | User JwtOption 83 | } 84 | 85 | type JwtOption struct { 86 | Secret string 87 | TTL string 88 | Name string 89 | } 90 | 91 | type AliOss struct { 92 | EndPoint string 93 | AccessKeyId string `mapstructure:"access_key_id"` 94 | AccessKeySecret string `mapstructure:"access_key_secret"` 95 | BucketName string `mapstructure:"bucket_name"` 96 | } 97 | 98 | type Wechat struct { 99 | AppId string 100 | Secret string 101 | } 102 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3.3' 2 | services: 3 | redis: 4 | image: redis 5 | container_name: go-redis 6 | restart: always 7 | volumes: 8 | - /data/redis/data:/data 9 | - /data/redis/conf:/redis.conf:/usr/local/etc/redis/redis.conf 10 | - /data/redis/logs:/logs 11 | command: 12 | - redis-server 13 | ports: 14 | - '6379:6379' 15 | mysql: 16 | image: mysql 17 | container_name: go-mysql 18 | restart: always 19 | environment: 20 | MYSQL_ROOT_PASSWORD: root 21 | TZ: Asia/Shanghai 22 | LANG: en_US.UTF-8 23 | ports: 24 | - '3306:3306' 25 | volumes: 26 | - /data/mysql/data:/var/lib/mysql 27 | - /data/mysql/config:/config/my.cnf:/etc/mysql/my.cnf 28 | command: 29 | --max_connections=1000 30 | --character-set-server=utf8mb4 31 | --collation-server=utf8mb4_general_ci 32 | --default-authentication-plugin=mysql_native_password 33 | web: 34 | container_name: gin-server 35 | build: 36 | context: ./ 37 | dockerfile: dockerfile 38 | restart: always 39 | volumes: 40 | # 这个共享卷是用来存放配置文件。 41 | - /home/running/takeout/config/:/app/config/ 42 | # 该共享卷用来存储日志文件 43 | - /home/running/takeout/logs/:/app/logger/ 44 | ports: 45 | - '8080-8080' 46 | -------------------------------------------------------------------------------- /dockerfile: -------------------------------------------------------------------------------- 1 | # 编译阶段:引用最小编译环境 2 | FROM golang:1.21.0 AS builder 3 | 4 | # 镜像默认工作目录 5 | WORKDIR /build 6 | 7 | 8 | # 防止多次拉取依赖 9 | ADD go.mod . 10 | ADD go.sum . 11 | # 配置镜像golang的默认配置,方便拉取依赖 12 | RUN go env -w GOPROXY=https://goproxy.cn,direct 13 | RUN go mod download 14 | 15 | # 拷贝当前目录所有文件到工作目录 16 | COPY . . 17 | 18 | # 设置编译环境并进行编译 19 | RUN GOOS=linux CGO_ENABLED=0 GOARCH=amd64 go build -o /app/gin-server . 20 | 21 | # 构建阶段:使用 alpine 最小构建 22 | FROM alpine 23 | 24 | # 设置镜像工作目录 25 | WORKDIR /app 26 | 27 | # 在builder阶段复制可执行的go二进制文件app/go-exporter 到/app/go_exporter中 28 | COPY --from=builder /app/gin-server /app/gin-server 29 | # 创建日志文件夹 30 | RUN mkdir /app/logger 31 | 32 | # 时区设置 33 | ENV TZ="Asia/Shanghai" 34 | 35 | # 开放端口 36 | EXPOSE 8080 37 | 38 | # 启动服务器 39 | CMD ["/app/gin-server"] -------------------------------------------------------------------------------- /global/global.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "github.com/go-redis/redis" 5 | "gorm.io/gorm" 6 | "take-out/config" 7 | "take-out/logger" 8 | ) 9 | 10 | var ( 11 | Config *config.AllConfig // 全局Config 12 | Log logger.ILog 13 | DB *gorm.DB 14 | Redis *redis.Client 15 | ) 16 | -------------------------------------------------------------------------------- /global/tx/gormTx.go: -------------------------------------------------------------------------------- 1 | package tx 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "gorm.io/gorm" 7 | ) 8 | 9 | var TRANSACTIONS_DB_CANNOTRESOLVE = errors.New("事务接口无法转换为具体实现类") 10 | 11 | // GormTransaction 这里我们对Gorm进行封装Transaction 12 | type GormTransaction struct { 13 | db *gorm.DB 14 | } 15 | 16 | func NewGormTransaction(db *gorm.DB, ctx context.Context) Transaction { 17 | return &GormTransaction{db: db.WithContext(ctx)} 18 | } 19 | 20 | func (g *GormTransaction) Begin() error { 21 | g.db = g.db.Begin() 22 | return g.db.Error 23 | } 24 | 25 | func (g *GormTransaction) Commit() error { 26 | return g.db.Commit().Error 27 | } 28 | 29 | func (g *GormTransaction) Rollback() { 30 | g.db.Rollback() 31 | } 32 | 33 | func GetGormDB(transactions Transaction) (*gorm.DB, error) { 34 | if gormTx, ok := transactions.(*GormTransaction); ok { 35 | return gormTx.db, nil 36 | } 37 | return nil, TRANSACTIONS_DB_CANNOTRESOLVE 38 | } 39 | -------------------------------------------------------------------------------- /global/tx/transactionManager.go: -------------------------------------------------------------------------------- 1 | package tx 2 | 3 | // Transaction 定义事务管理器接口 4 | // 因为不同数据库的事务管理方式不同,所以这里使用接口来定义事务管理器的功能 5 | // 具体的实现可以由不同的数据库驱动来实现 6 | // 例如,我们当前项目的Dao层为了引入事务破坏了 `依赖倒置` 原则,所以我们的目标是面向抽象 7 | type Transaction interface { 8 | // Begin 开始一个事务 9 | Begin() error 10 | // Commit 提交事务 11 | Commit() error 12 | // Rollback 回滚事务 13 | Rollback() 14 | } 15 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module take-out 2 | 3 | go 1.21.0 4 | 5 | require ( 6 | github.com/aliyun/aliyun-oss-go-sdk v2.2.9+incompatible 7 | github.com/gin-gonic/gin v1.9.1 8 | github.com/go-redis/redis v6.15.9+incompatible 9 | github.com/golang-jwt/jwt/v5 v5.0.0 10 | github.com/google/uuid v1.1.2 11 | github.com/sirupsen/logrus v1.9.3 12 | github.com/spf13/pflag v1.0.5 13 | github.com/spf13/viper v1.16.0 14 | golang.org/x/time v0.1.0 15 | gorm.io/driver/mysql v1.5.1 16 | gorm.io/gorm v1.25.4 17 | ) 18 | 19 | require ( 20 | github.com/bytedance/sonic v1.10.1 // indirect 21 | github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect 22 | github.com/chenzhuoyu/iasm v0.9.0 // indirect 23 | github.com/fsnotify/fsnotify v1.6.0 // indirect 24 | github.com/gabriel-vasile/mimetype v1.4.2 // indirect 25 | github.com/gin-contrib/sse v0.1.0 // indirect 26 | github.com/go-playground/locales v0.14.1 // indirect 27 | github.com/go-playground/universal-translator v0.18.1 // indirect 28 | github.com/go-playground/validator/v10 v10.15.4 // indirect 29 | github.com/go-sql-driver/mysql v1.7.0 // indirect 30 | github.com/goccy/go-json v0.10.2 // indirect 31 | github.com/hashicorp/hcl v1.0.0 // indirect 32 | github.com/jinzhu/inflection v1.0.0 // indirect 33 | github.com/jinzhu/now v1.1.5 // indirect 34 | github.com/json-iterator/go v1.1.12 // indirect 35 | github.com/klauspost/cpuid/v2 v2.2.5 // indirect 36 | github.com/leodido/go-urn v1.2.4 // indirect 37 | github.com/magiconair/properties v1.8.7 // indirect 38 | github.com/mattn/go-isatty v0.0.19 // indirect 39 | github.com/mitchellh/mapstructure v1.5.0 // indirect 40 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 41 | github.com/modern-go/reflect2 v1.0.2 // indirect 42 | github.com/onsi/ginkgo v1.16.5 // indirect 43 | github.com/onsi/gomega v1.27.10 // indirect 44 | github.com/pelletier/go-toml/v2 v2.1.0 // indirect 45 | github.com/spf13/afero v1.9.5 // indirect 46 | github.com/spf13/cast v1.5.1 // indirect 47 | github.com/spf13/jwalterweatherman v1.1.0 // indirect 48 | github.com/subosito/gotenv v1.4.2 // indirect 49 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 50 | github.com/ugorji/go/codec v1.2.11 // indirect 51 | golang.org/x/arch v0.5.0 // indirect 52 | golang.org/x/crypto v0.13.0 // indirect 53 | golang.org/x/net v0.15.0 // indirect 54 | golang.org/x/sys v0.12.0 // indirect 55 | golang.org/x/text v0.13.0 // indirect 56 | google.golang.org/protobuf v1.31.0 // indirect 57 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect 58 | gopkg.in/ini.v1 v1.67.0 // indirect 59 | gopkg.in/yaml.v3 v3.0.1 // indirect 60 | ) 61 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 7 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 8 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 9 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 10 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 11 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 12 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 13 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 14 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 15 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 16 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 17 | cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= 18 | cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= 19 | cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= 20 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 21 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 22 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 23 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 24 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 25 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 26 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 27 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 28 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 29 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 30 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 31 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 32 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 33 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 34 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 35 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 36 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 37 | cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= 38 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 39 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 40 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 41 | github.com/aliyun/aliyun-oss-go-sdk v2.2.9+incompatible h1:Sg/2xHwDrioHpxTN6WMiwbXTpUEinBpHsN7mG21Rc2k= 42 | github.com/aliyun/aliyun-oss-go-sdk v2.2.9+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= 43 | github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= 44 | github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= 45 | github.com/bytedance/sonic v1.10.1 h1:7a1wuFXL1cMy7a3f7/VFcEtriuXQnUBhtoVfOZiaysc= 46 | github.com/bytedance/sonic v1.10.1/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= 47 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 48 | github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= 49 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= 50 | github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= 51 | github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= 52 | github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo= 53 | github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= 54 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 55 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 56 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 57 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 58 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 59 | github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 60 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 61 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 62 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 63 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 64 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 65 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 66 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 67 | github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= 68 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 69 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 70 | github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= 71 | github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= 72 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 73 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 74 | github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= 75 | github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= 76 | github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= 77 | github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= 78 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 79 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 80 | github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= 81 | github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= 82 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 83 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 84 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 85 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= 86 | github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 87 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 88 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 89 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 90 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 91 | github.com/go-playground/validator/v10 v10.15.4 h1:zMXza4EpOdooxPel5xDqXEdXG5r+WggpvnAKMsalBjs= 92 | github.com/go-playground/validator/v10 v10.15.4/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= 93 | github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= 94 | github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= 95 | github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= 96 | github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= 97 | github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= 98 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 99 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 100 | github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= 101 | github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= 102 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 103 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 104 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 105 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 106 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 107 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 108 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 109 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 110 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 111 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 112 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 113 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 114 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 115 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 116 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 117 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 118 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 119 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 120 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 121 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 122 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 123 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 124 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 125 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 126 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 127 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 128 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 129 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 130 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 131 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 132 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 133 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 134 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 135 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 136 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 137 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 138 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 139 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 140 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 141 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 142 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 143 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 144 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 145 | github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 146 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 147 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 148 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 149 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 150 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 151 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 152 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 153 | github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 154 | github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 155 | github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 156 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 157 | github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= 158 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 159 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 160 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 161 | github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= 162 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 163 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 164 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 165 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 166 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 167 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 168 | github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 169 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 170 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 171 | github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= 172 | github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 173 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 174 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 175 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 176 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 177 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 178 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 179 | github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= 180 | github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= 181 | github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= 182 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 183 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 184 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 185 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 186 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 187 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 188 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 189 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 190 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 191 | github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= 192 | github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= 193 | github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= 194 | github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= 195 | github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 196 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 197 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 198 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 199 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 200 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 201 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 202 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 203 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 204 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 205 | github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= 206 | github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= 207 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 208 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 209 | github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= 210 | github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= 211 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 212 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 213 | github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= 214 | github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= 215 | github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= 216 | github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= 217 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 218 | github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 219 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 220 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 221 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 222 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 223 | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 224 | github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= 225 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 226 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 227 | github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= 228 | github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= 229 | github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= 230 | github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= 231 | github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= 232 | github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= 233 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 234 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 235 | github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= 236 | github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= 237 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 238 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 239 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 240 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 241 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 242 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 243 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 244 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 245 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 246 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 247 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 248 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 249 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 250 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 251 | github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= 252 | github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= 253 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 254 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 255 | github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= 256 | github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 257 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 258 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 259 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 260 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 261 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 262 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 263 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 264 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 265 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 266 | go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= 267 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 268 | golang.org/x/arch v0.5.0 h1:jpGode6huXQxcskEIpOCvrU+tzo81b6+oFLUYXWtH/Y= 269 | golang.org/x/arch v0.5.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 270 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 271 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 272 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 273 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 274 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 275 | golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 276 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 277 | golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= 278 | golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= 279 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 280 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 281 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 282 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 283 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 284 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 285 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 286 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 287 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 288 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 289 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 290 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 291 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 292 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 293 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 294 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 295 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 296 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 297 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 298 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 299 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 300 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 301 | golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 302 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 303 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 304 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 305 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 306 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 307 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 308 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 309 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 310 | golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 311 | golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 312 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 313 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 314 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 315 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 316 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 317 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 318 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 319 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 320 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 321 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 322 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 323 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 324 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 325 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 326 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 327 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 328 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 329 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 330 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 331 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 332 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 333 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 334 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 335 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 336 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 337 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 338 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 339 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 340 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 341 | golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 342 | golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 343 | golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 344 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 345 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 346 | golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= 347 | golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= 348 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 349 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 350 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 351 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 352 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 353 | golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 354 | golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 355 | golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 356 | golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 357 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 358 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 359 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 360 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 361 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 362 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 363 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 364 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 365 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 366 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 367 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 368 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 369 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 370 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 371 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 372 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 373 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 374 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 375 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 376 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 377 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 378 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 379 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 380 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 381 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 382 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 383 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 384 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 385 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 386 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 387 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 388 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 389 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 390 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 391 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 392 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 393 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 394 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 395 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 396 | golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 397 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 398 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 399 | golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 400 | golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 401 | golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 402 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 403 | golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 404 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 405 | golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 406 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 407 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 408 | golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 409 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 410 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 411 | golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= 412 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 413 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 414 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 415 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 416 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 417 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 418 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 419 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 420 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 421 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 422 | golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= 423 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 424 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 425 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 426 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 427 | golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= 428 | golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 429 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 430 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 431 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 432 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 433 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 434 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 435 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 436 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 437 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 438 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 439 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 440 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 441 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 442 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 443 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 444 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 445 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 446 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 447 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 448 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 449 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 450 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 451 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 452 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 453 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 454 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 455 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 456 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 457 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 458 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 459 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 460 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 461 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 462 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 463 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 464 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 465 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 466 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 467 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 468 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 469 | golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= 470 | golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 471 | golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 472 | golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 473 | golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 474 | golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 475 | golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 476 | golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 477 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 478 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 479 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 480 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 481 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 482 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 483 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 484 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 485 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 486 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 487 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 488 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 489 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 490 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 491 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 492 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 493 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 494 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 495 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 496 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 497 | google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= 498 | google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= 499 | google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= 500 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 501 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 502 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 503 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 504 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 505 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 506 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 507 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 508 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 509 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 510 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 511 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 512 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 513 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 514 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 515 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 516 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 517 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 518 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 519 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 520 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 521 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 522 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 523 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 524 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 525 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 526 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 527 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 528 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 529 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 530 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 531 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 532 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 533 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 534 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 535 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 536 | google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 537 | google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 538 | google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 539 | google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 540 | google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 541 | google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 542 | google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 543 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 544 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 545 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 546 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 547 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 548 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 549 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 550 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 551 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 552 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 553 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 554 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 555 | google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 556 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 557 | google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= 558 | google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 559 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 560 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 561 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 562 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 563 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 564 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 565 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 566 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 567 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 568 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 569 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 570 | google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= 571 | google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 572 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 573 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 574 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 575 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 576 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 577 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 578 | gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= 579 | gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 580 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 581 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 582 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 583 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 584 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 585 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 586 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 587 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 588 | gorm.io/driver/mysql v1.5.1 h1:WUEH5VF9obL/lTtzjmML/5e6VfFR/788coz2uaVCAZw= 589 | gorm.io/driver/mysql v1.5.1/go.mod h1:Jo3Xu7mMhCyj8dlrb3WoCaRd1FhsVh+yMXb1jUInf5o= 590 | gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= 591 | gorm.io/gorm v1.25.4 h1:iyNd8fNAe8W9dvtlgeRI5zSVZPsq3OpcTu37cYcpCmw= 592 | gorm.io/gorm v1.25.4/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= 593 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 594 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 595 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 596 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 597 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 598 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 599 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 600 | nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= 601 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 602 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 603 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 604 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 605 | -------------------------------------------------------------------------------- /initialize/enter.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "take-out/config" 6 | "take-out/global" 7 | "take-out/logger" 8 | ) 9 | 10 | func GlobalInit() *gin.Engine { 11 | // 配置文件初始化 12 | global.Config = config.InitLoadConfig() 13 | // Log初始化 14 | global.Log = logger.NewMySlog(global.Config.Log.Level, global.Config.Log.FilePath) 15 | // Gorm初始化 16 | global.DB = InitDatabase(global.Config.DataSource.Dsn()) 17 | // Redis初始化 18 | global.Redis = initRedis() 19 | // Router初始化 20 | router := routerInit() 21 | return router 22 | } 23 | -------------------------------------------------------------------------------- /initialize/gorm.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "errors" 5 | "github.com/gin-gonic/gin" 6 | "golang.org/x/time/rate" 7 | "gorm.io/driver/mysql" 8 | "gorm.io/gorm" 9 | "gorm.io/gorm/logger" 10 | "gorm.io/gorm/schema" 11 | "take-out/global" 12 | "time" 13 | ) 14 | 15 | var ( 16 | GormToManyRequestError = errors.New("gorm: to many request") 17 | ) 18 | 19 | func InitDatabase(dsn string) *gorm.DB { 20 | var ormLogger logger.Interface 21 | if gin.Mode() == "debug" { 22 | ormLogger = logger.Default.LogMode(logger.Info) 23 | } else { 24 | ormLogger = logger.Default 25 | } 26 | db, err := gorm.Open(mysql.New(mysql.Config{ 27 | DSN: dsn, // DSN data source name 28 | DefaultStringSize: 256, // string 类型字段的默认长度 29 | DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持 30 | DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引 31 | DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列 32 | SkipInitializeWithVersion: false, // 根据版本自动配置 33 | }), &gorm.Config{ 34 | Logger: ormLogger, 35 | NamingStrategy: schema.NamingStrategy{ 36 | SingularTable: true, 37 | }, 38 | }) 39 | if err != nil { 40 | panic(err) 41 | } 42 | 43 | sqlDB, _ := db.DB() 44 | sqlDB.SetMaxIdleConns(20) //设置连接池,空闲 45 | sqlDB.SetMaxOpenConns(100) //打开 46 | sqlDB.SetConnMaxLifetime(time.Second * 30) 47 | 48 | // 慢日志中间件 49 | SlowQueryLog(db) 50 | // 限流器中间件 51 | GormRateLimiter(db, rate.NewLimiter(500, 1000)) 52 | 53 | return db 54 | } 55 | 56 | // SlowQueryLog 慢查询日志 57 | func SlowQueryLog(db *gorm.DB) { 58 | err := db.Callback().Query().Before("*").Register("slow_query_start", func(d *gorm.DB) { 59 | now := time.Now() 60 | d.Set("start_time", now) 61 | }) 62 | if err != nil { 63 | panic(err) 64 | } 65 | 66 | err = db.Callback().Query().After("*").Register("slow_query_end", func(d *gorm.DB) { 67 | now := time.Now() 68 | start, ok := d.Get("start_time") 69 | if ok { 70 | duration := now.Sub(start.(time.Time)) 71 | // 一般认为 200 Ms 为Sql慢查询 72 | if duration > time.Millisecond*200 { 73 | global.Log.Error("慢查询", "SQL:", d.Statement.SQL.String()) 74 | } 75 | } 76 | }) 77 | if err != nil { 78 | panic(err) 79 | } 80 | } 81 | 82 | // GormRateLimiter Gorm限流器 此限流器不能终止GORM查询链。 83 | func GormRateLimiter(db *gorm.DB, r *rate.Limiter) { 84 | err := db.Callback().Query().Before("*").Register("RateLimitGormMiddleware", func(d *gorm.DB) { 85 | if !r.Allow() { 86 | d.AddError(GormToManyRequestError) 87 | global.Log.Error(GormToManyRequestError.Error()) 88 | return 89 | } 90 | }) 91 | if err != nil { 92 | panic(err) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /initialize/redis.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "fmt" 5 | "github.com/go-redis/redis" 6 | "take-out/global" 7 | ) 8 | 9 | func initRedis() *redis.Client { 10 | redisOpt := global.Config.Redis 11 | client := redis.NewClient(&redis.Options{ 12 | Addr: fmt.Sprintf("%s:%s", redisOpt.Host, redisOpt.Port), 13 | Password: redisOpt.Password, // no password set 14 | DB: redisOpt.DataBase, // use default DB 15 | }) 16 | ping := client.Ping() 17 | err := ping.Err() 18 | if err != nil { 19 | panic(err) 20 | } 21 | return client 22 | } 23 | -------------------------------------------------------------------------------- /initialize/router.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "take-out/internal/router" 6 | ) 7 | 8 | func routerInit() *gin.Engine { 9 | r := gin.Default() 10 | allRouter := router.AllRouter 11 | 12 | // admin 13 | admin := r.Group("/admin") 14 | { 15 | allRouter.EmployeeRouter.InitApiRouter(admin) 16 | allRouter.CategoryRouter.InitApiRouter(admin) 17 | allRouter.DishRouter.InitApiRouter(admin) 18 | allRouter.CommonRouter.InitApiRouter(admin) 19 | allRouter.SetMealRouter.InitApiRouter(admin) 20 | } 21 | return r 22 | } 23 | -------------------------------------------------------------------------------- /internal/api/controller/category_controller.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/spf13/cast" 6 | "strconv" 7 | "take-out/common/retcode" 8 | "take-out/global" 9 | "take-out/internal/api/request" 10 | "take-out/internal/service" 11 | ) 12 | 13 | type CategoryController struct { 14 | service service.ICategoryService 15 | } 16 | 17 | func NewCategoryController(service service.ICategoryService) *CategoryController { 18 | return &CategoryController{service: service} 19 | } 20 | 21 | func (cc *CategoryController) AddCategory(ctx *gin.Context) { 22 | var categoryDto request.CategoryDTO 23 | err := ctx.Bind(&categoryDto) 24 | if err != nil { 25 | global.Log.Debug("param CategoryDTO json failed", err.Error()) 26 | retcode.Fatal(ctx, err, "") 27 | return 28 | } 29 | if err = cc.service.AddCategory(ctx, categoryDto); err != nil { 30 | retcode.Fatal(ctx, err, "") 31 | return 32 | } 33 | retcode.OK(ctx, "") 34 | } 35 | 36 | func (cc *CategoryController) PageQuery(ctx *gin.Context) { 37 | var categoryPageDTO request.CategoryPageQueryDTO 38 | err := ctx.Bind(&categoryPageDTO) 39 | if err != nil { 40 | global.Log.Warn("Category invalid params failed ", err.Error()) 41 | retcode.Fatal(ctx, err, "") 42 | return 43 | } 44 | query, err := cc.service.PageQuery(ctx, categoryPageDTO) 45 | if err != nil { 46 | global.Log.Warn("Category PageQuery failed ", err.Error()) 47 | retcode.Fatal(ctx, err, "") 48 | return 49 | } 50 | retcode.OK(ctx, query) 51 | } 52 | 53 | func (cc *CategoryController) List(ctx *gin.Context) { 54 | cate, _ := strconv.Atoi(ctx.Query("type")) 55 | list, err := cc.service.List(ctx, cate) 56 | if err != nil { 57 | global.Log.Warn("Category List failed", err.Error()) 58 | retcode.Fatal(ctx, err, "") 59 | return 60 | } 61 | retcode.OK(ctx, list) 62 | } 63 | 64 | func (cc *CategoryController) DeleteById(ctx *gin.Context) { 65 | id := cast.ToUint64(ctx.Query("id")) 66 | err := cc.service.DeleteById(ctx, id) 67 | if err != nil { 68 | global.Log.Warn("Category DeleteById failed", err.Error()) 69 | retcode.Fatal(ctx, err, "") 70 | return 71 | } 72 | retcode.OK(ctx, "") 73 | } 74 | 75 | func (cc *CategoryController) EditCategory(ctx *gin.Context) { 76 | var categoryDTO request.CategoryDTO 77 | err := ctx.Bind(&categoryDTO) 78 | if err != nil { 79 | global.Log.Debug("param CategoryDTO failed", err.Error()) 80 | retcode.Fatal(ctx, err, "") 81 | return 82 | } 83 | err = cc.service.Update(ctx, categoryDTO) 84 | if err != nil { 85 | global.Log.Debug("Category Edit failed", err.Error()) 86 | retcode.Fatal(ctx, err, "") 87 | return 88 | } 89 | retcode.OK(ctx, "") 90 | 91 | } 92 | 93 | func (cc *CategoryController) SetStatus(ctx *gin.Context) { 94 | id, _ := strconv.ParseUint(ctx.Query("id"), 10, 64) 95 | status, _ := strconv.Atoi(ctx.Param("status")) 96 | err := cc.service.SetStatus(ctx, id, status) 97 | if err != nil { 98 | global.Log.Warn("Category SetStatus failed", err.Error()) 99 | retcode.Fatal(ctx, err, "") 100 | return 101 | } 102 | retcode.OK(ctx, "") 103 | } 104 | -------------------------------------------------------------------------------- /internal/api/controller/common_controller.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/google/uuid" 6 | "take-out/common/retcode" 7 | "take-out/common/utils" 8 | "take-out/global" 9 | ) 10 | 11 | type CommonController struct { 12 | } 13 | 14 | func (cc *CommonController) Upload(ctx *gin.Context) { 15 | // 获取前端传递的图片 16 | file, err := ctx.FormFile("file") 17 | if err != nil { 18 | return 19 | } 20 | // 拼接uuid的图片名称 21 | uuid := uuid.New() 22 | imageName := uuid.String() + file.Filename 23 | imagePath, err := utils.AliyunOss(imageName, file) 24 | if err != nil { 25 | global.Log.Warn("AliyunOss upload failed", "err", err.Error()) 26 | retcode.Fatal(ctx, err, "") 27 | return 28 | } 29 | retcode.OK(ctx, imagePath) 30 | } 31 | -------------------------------------------------------------------------------- /internal/api/controller/dish_controller.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "log/slog" 6 | "strconv" 7 | "take-out/common/retcode" 8 | "take-out/global" 9 | "take-out/internal/api/request" 10 | "take-out/internal/service" 11 | ) 12 | 13 | type DishController struct { 14 | service service.IDishService 15 | } 16 | 17 | func NewDishController(service service.IDishService) *DishController { 18 | return &DishController{service: service} 19 | } 20 | 21 | // AddDish 新增菜品数据 22 | func (dc *DishController) AddDish(ctx *gin.Context) { 23 | var dishDTO request.DishDTO 24 | err := ctx.Bind(&dishDTO) 25 | if err != nil { 26 | global.Log.Debug("param DishDTO failed", err.Error()) 27 | retcode.Fatal(ctx, err, "") 28 | return 29 | } 30 | err = dc.service.AddDishWithFlavors(ctx, dishDTO) 31 | if err != nil { 32 | global.Log.Warn("AddDish failed", err.Error()) 33 | retcode.Fatal(ctx, err, "") 34 | return 35 | } 36 | retcode.OK(ctx, "") 37 | } 38 | 39 | // PageQuery 菜品分页查询 40 | func (dc *DishController) PageQuery(ctx *gin.Context) { 41 | var dishPageQueryDTO request.DishPageQueryDTO 42 | err := ctx.Bind(&dishPageQueryDTO) 43 | if err != nil { 44 | global.Log.Error("DishController invalid params ", "Error", err.Error()) 45 | retcode.Fatal(ctx, err, "") 46 | return 47 | } 48 | //分页查询 49 | pageResult, err := dc.service.PageQuery(ctx, &dishPageQueryDTO) 50 | if err != nil { 51 | global.Log.Error("DishController PageQuery failed", "Error", err.Error()) 52 | retcode.Fatal(ctx, err, "") 53 | return 54 | } 55 | retcode.OK(ctx, pageResult) 56 | } 57 | 58 | // GetById 根据id查询菜品信息 59 | func (dc *DishController) GetById(ctx *gin.Context) { 60 | id, _ := strconv.ParseUint(ctx.Param("id"), 10, 64) 61 | // 根据id查询并获取口味数据 62 | dishVO, err := dc.service.GetByIdWithFlavors(ctx, id) 63 | if err != nil { 64 | retcode.Fatal(ctx, err, "") 65 | return 66 | } 67 | retcode.OK(ctx, dishVO) 68 | } 69 | 70 | // List 根据分类id查询菜品信息 71 | func (dc *DishController) List(ctx *gin.Context) { 72 | categoryId, _ := strconv.ParseUint(ctx.Query("categoryId"), 10, 64) 73 | // 根据id查询并获取口味数据 74 | dishVO, err := dc.service.List(ctx, categoryId) 75 | if err != nil { 76 | global.Log.Error("根据分类id查询菜品信息失败!", err.Error()) 77 | retcode.Fatal(ctx, err, "") 78 | return 79 | } 80 | retcode.OK(ctx, dishVO) 81 | } 82 | 83 | // OnOrClose 菜品启售或禁售 84 | func (dc *DishController) OnOrClose(ctx *gin.Context) { 85 | 86 | id, _ := strconv.ParseUint(ctx.Query("id"), 10, 64) 87 | status, _ := strconv.Atoi(ctx.Param("status")) 88 | // 根据id修改对应菜品的状态 89 | err := dc.service.OnOrClose(ctx, id, status) 90 | if err != nil { 91 | slog.Warn("菜品启售或禁售失败!", "Err:", err.Error()) 92 | retcode.Fatal(ctx, err, "") 93 | return 94 | } 95 | retcode.OK(ctx, "") 96 | } 97 | 98 | // Update 修改菜品信息 99 | func (dc *DishController) Update(ctx *gin.Context) { 100 | dishUpdateDTO := request.DishUpdateDTO{} 101 | err := ctx.Bind(&dishUpdateDTO) 102 | if err != nil { 103 | slog.Warn("Failed Json Bind", "Err:", err.Error()) 104 | retcode.Fatal(ctx, err, "") 105 | return 106 | } 107 | // 更新菜品以及菜品口味的关联数据 108 | err = dc.service.Update(ctx, dishUpdateDTO) 109 | if err != nil { 110 | slog.Warn("更新菜品信息失败!", "Err:", err.Error()) 111 | retcode.Fatal(ctx, err, "") 112 | return 113 | } 114 | retcode.OK(ctx, "") 115 | } 116 | 117 | // Delete 删除菜品信息 118 | func (dc *DishController) Delete(ctx *gin.Context) { 119 | ids := ctx.Query("ids") 120 | err := dc.service.Delete(ctx, ids) 121 | if err != nil { 122 | slog.Warn("删除菜品信息失败!", "Err:", err.Error()) 123 | retcode.Fatal(ctx, err, "") 124 | return 125 | } 126 | retcode.OK(ctx, "") 127 | } 128 | -------------------------------------------------------------------------------- /internal/api/controller/employee_controller.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "strconv" 6 | "take-out/common/enum" 7 | "take-out/common/retcode" 8 | "take-out/global" 9 | "take-out/internal/api/request" 10 | "take-out/internal/service" 11 | ) 12 | 13 | type EmployeeController struct { 14 | service service.IEmployeeService 15 | } 16 | 17 | func NewEmployeeController(employeeService service.IEmployeeService) *EmployeeController { 18 | return &EmployeeController{service: employeeService} 19 | } 20 | 21 | // Login 员工登录 22 | func (ec *EmployeeController) Login(ctx *gin.Context) { 23 | employeeLogin := request.EmployeeLogin{} 24 | err := ctx.Bind(&employeeLogin) 25 | if err != nil { 26 | global.Log.Debug("EmployeeController login 解析失败") 27 | retcode.Fatal(ctx, err, "") 28 | return 29 | } 30 | resp, err := ec.service.Login(ctx, employeeLogin) 31 | if err != nil { 32 | global.Log.Warn("EmployeeController login Error:", err.Error()) 33 | retcode.Fatal(ctx, err, "") 34 | return 35 | } 36 | retcode.OK(ctx, resp) 37 | } 38 | 39 | // Logout 员工退出 40 | func (ec *EmployeeController) Logout(ctx *gin.Context) { 41 | var err error 42 | err = ec.service.Logout(ctx) 43 | if err != nil { 44 | global.Log.Warn("EmployeeController login Error:", err.Error()) 45 | retcode.Fatal(ctx, err, "") 46 | return 47 | } 48 | retcode.OK(ctx, "") 49 | } 50 | 51 | // EditPassword 修改密码 52 | func (ec *EmployeeController) EditPassword(ctx *gin.Context) { 53 | var reqs request.EmployeeEditPassword 54 | var err error 55 | err = ctx.Bind(&reqs) 56 | if err != nil { 57 | global.Log.Debug("EditPassword Error:", err.Error()) 58 | retcode.Fatal(ctx, err, "") 59 | return 60 | } 61 | // 从上下文获取员工id 62 | if id, ok := ctx.Get(enum.CurrentId); ok { 63 | reqs.EmpId = id.(uint64) 64 | } 65 | err = ec.service.EditPassword(ctx, reqs) 66 | if err != nil { 67 | global.Log.Warn("EditPassword Error:", err.Error()) 68 | retcode.Fatal(ctx, err, "") 69 | } 70 | retcode.OK(ctx, "") 71 | } 72 | 73 | // AddEmployee 新增员工 74 | func (ec *EmployeeController) AddEmployee(ctx *gin.Context) { 75 | var request request.EmployeeDTO 76 | var err error 77 | err = ctx.Bind(&request) 78 | if err != nil { 79 | global.Log.Debug("AddEmployee Error:", err.Error()) 80 | retcode.Fatal(ctx, err, "") 81 | return 82 | } 83 | err = ec.service.CreateEmployee(ctx, request) 84 | if err != nil { 85 | global.Log.Warn("AddEmployee Error:", err.Error()) 86 | retcode.Fatal(ctx, err, "") 87 | return 88 | } 89 | // 正确输出 90 | retcode.OK(ctx, "") 91 | } 92 | 93 | // PageQuery 员工分页查询 94 | func (ec *EmployeeController) PageQuery(ctx *gin.Context) { 95 | var employeePageQueryDTO request.EmployeePageQueryDTO 96 | err := ctx.Bind(&employeePageQueryDTO) 97 | if err != nil { 98 | global.Log.Error("AddEmployee invalid params err:", err.Error()) 99 | retcode.Fatal(ctx, err, "") 100 | return 101 | } 102 | // 进行分页查询 103 | pageResult, err := ec.service.PageQuery(ctx, employeePageQueryDTO) 104 | if err != nil { 105 | global.Log.Warn("AddEmployee Error:", err.Error()) 106 | retcode.Fatal(ctx, err, "") 107 | return 108 | } 109 | retcode.OK(ctx, pageResult) 110 | } 111 | 112 | // OnOrOff 启用Or禁用员工状态 113 | func (ec *EmployeeController) OnOrOff(ctx *gin.Context) { 114 | id, _ := strconv.ParseUint(ctx.Query("id"), 10, 64) 115 | status, _ := strconv.Atoi(ctx.Param("status")) 116 | var err error 117 | err = ec.service.SetStatus(ctx, id, status) 118 | if err != nil { 119 | global.Log.Warn("OnOrOff Status Error:", err.Error()) 120 | retcode.Fatal(ctx, err, "") 121 | return 122 | } 123 | // 更新员工状态 124 | global.Log.Info("启用Or禁用员工状态:", "id", id, "status", status) 125 | retcode.OK(ctx, "") 126 | } 127 | 128 | // UpdateEmployee 编辑员工信息 129 | func (ec *EmployeeController) UpdateEmployee(ctx *gin.Context) { 130 | var employeeDTO request.EmployeeDTO 131 | err := ctx.Bind(&employeeDTO) 132 | if err != nil { 133 | global.Log.Debug("UpdateEmployee Error:", err.Error()) 134 | retcode.Fatal(ctx, err, "") 135 | return 136 | } 137 | // 修改员工信息 138 | err = ec.service.UpdateEmployee(ctx.Request.Context(), employeeDTO) 139 | if err != nil { 140 | global.Log.Warn("UpdateEmployee Error:", err.Error()) 141 | retcode.Fatal(ctx, err, "") 142 | return 143 | } 144 | retcode.OK(ctx, "") 145 | } 146 | 147 | // GetById 获取员工信息根据id 148 | func (ec *EmployeeController) GetById(ctx *gin.Context) { 149 | id, _ := strconv.ParseUint(ctx.Param("id"), 10, 64) 150 | employee, err := ec.service.GetById(ctx.Request.Context(), id) 151 | if err != nil { 152 | global.Log.Warn("EmployeeCtrl GetById Error", err.Error()) 153 | retcode.Fatal(ctx, err, "") 154 | return 155 | } 156 | retcode.OK(ctx, employee) 157 | } 158 | -------------------------------------------------------------------------------- /internal/api/controller/setmeal_controller.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "strconv" 6 | "take-out/common/retcode" 7 | "take-out/global" 8 | "take-out/internal/api/request" 9 | "take-out/internal/service" 10 | ) 11 | 12 | type SetMealController struct { 13 | service service.ISetMealService 14 | } 15 | 16 | func NewSetMealController(service service.ISetMealService) *SetMealController { 17 | return &SetMealController{service: service} 18 | } 19 | 20 | // SaveWithDish 保存套餐和菜品信息 21 | func (sc *SetMealController) SaveWithDish(ctx *gin.Context) { 22 | var setmealDTO request.SetMealDTO 23 | err := ctx.Bind(&setmealDTO) 24 | if err != nil { 25 | global.Log.Debug("SaveWithDish保存套餐和菜品信息 结构体解析失败!", "Err:", err.Error()) 26 | retcode.Fatal(ctx, err, "") 27 | return 28 | } 29 | err = sc.service.SaveWithDish(ctx, setmealDTO) 30 | if err != nil { 31 | global.Log.Warn("SaveWithDish保存套餐和菜品信息!", "Err:", err.Error()) 32 | retcode.Fatal(ctx, err, "") 33 | return 34 | } 35 | retcode.OK(ctx, "") 36 | } 37 | 38 | // PageQuery 套餐分页查询 39 | func (sc *SetMealController) PageQuery(ctx *gin.Context) { 40 | // 数据组装 41 | var pageQueryDTO request.SetMealPageQueryDTO 42 | err := ctx.Bind(&pageQueryDTO) 43 | if err != nil { 44 | global.Log.Error("PageQuery invalid params err:", err.Error()) 45 | retcode.Fatal(ctx, err, "") 46 | return 47 | } 48 | // 分页查询 49 | result, err := sc.service.PageQuery(ctx, pageQueryDTO) 50 | if err != nil { 51 | global.Log.Warn("PageQuery 套餐分页查询失败!", "Err:", err.Error()) 52 | retcode.Fatal(ctx, err, "") 53 | return 54 | } 55 | retcode.OK(ctx, result) 56 | } 57 | 58 | // OnOrClose 套餐启用禁用 59 | func (sc *SetMealController) OnOrClose(ctx *gin.Context) { 60 | id, _ := strconv.ParseUint(ctx.Query("id"), 10, 64) 61 | status, _ := strconv.Atoi(ctx.Param("status")) 62 | // 设置套餐状态 63 | err := sc.service.OnOrClose(ctx, id, status) 64 | if err != nil { 65 | global.Log.Warn("OnOrClose 套餐启用禁用失败!", "Err:", err.Error()) 66 | retcode.Fatal(ctx, err, "") 67 | return 68 | } 69 | retcode.OK(ctx, "") 70 | } 71 | 72 | // GetByIdWithDish 根据套餐id获取套餐和关联菜品信息 73 | func (sc *SetMealController) GetByIdWithDish(ctx *gin.Context) { 74 | setMealId, _ := strconv.ParseUint(ctx.Param("id"), 10, 64) 75 | // 获取套餐详情 76 | resp, err := sc.service.GetByIdWithDish(ctx, setMealId) 77 | if err != nil { 78 | global.Log.Warn("GetByIdWithDish 根据套餐id获取套餐和关联菜品信息", "Err:", err.Error()) 79 | retcode.Fatal(ctx, err, "") 80 | return 81 | } 82 | retcode.OK(ctx, resp) 83 | } 84 | -------------------------------------------------------------------------------- /internal/api/request/category.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | type CategoryDTO struct { 4 | Id uint64 `json:"id"` 5 | Name string `json:"name"` 6 | Sort string `json:"sort"` 7 | Cate string `json:"type"` 8 | } 9 | 10 | type CategoryPageQueryDTO struct { 11 | Name string `form:"name"` // 分页查询的name 12 | Page int `form:"page"` // 分页查询的页数 13 | PageSize int `form:"pageSize"` // 分页查询的页容量 14 | Cate int `form:"type"` // 分类类型:1为菜品分类,2为套餐分类 15 | } 16 | -------------------------------------------------------------------------------- /internal/api/request/dish.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | import ( 4 | "take-out/internal/model" 5 | ) 6 | 7 | type DishDTO struct { 8 | Id uint64 `json:"id"` 9 | Name string `json:"name"` 10 | CategoryId uint64 `json:"categoryId"` 11 | Price string `json:"price"` 12 | Image string `json:"image"` 13 | Description string `json:"description"` 14 | Status int `json:"status"` 15 | Flavors []model.DishFlavor `json:"flavors"` 16 | } 17 | 18 | type DishPageQueryDTO struct { 19 | Page int `form:"page"` // 分页查询的页数 20 | PageSize int `form:"pageSize"` // 分页查询的页容量 21 | Name string `form:"name"` // 分页查询的name 22 | CategoryId uint64 `form:"categoryId"` // 分类ID: 23 | Status int `form:"status"` // 菜品状态 24 | } 25 | 26 | type DishUpdateDTO struct { 27 | Id uint64 `json:"id" ` 28 | Name string `json:"name"` 29 | CategoryId uint64 `json:"categoryId"` 30 | Price string `json:"price"` 31 | Image string `json:"image"` 32 | Description string `json:"description"` 33 | Status int `json:"status"` 34 | Flavors []model.DishFlavor `json:"flavors"` 35 | } 36 | -------------------------------------------------------------------------------- /internal/api/request/employee.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | type EmployeeLogin struct { 4 | UserName string `json:"username" binding:"required"` 5 | Password string `json:"password" binding:"required"` 6 | } 7 | 8 | type EmployeeEditPassword struct { 9 | EmpId uint64 `json:"empId"` 10 | NewPassword string `json:"newPassword" binding:"required"` 11 | OldPassword string `json:"oldPassword" binding:"required"` 12 | } 13 | 14 | type EmployeeDTO struct { 15 | Id uint64 `json:"id"` //员工id 16 | IdNumber string `json:"idNumber" binding:"required"` //身份证 17 | Name string `json:"name" binding:"required"` //姓名 18 | Phone string `json:"phone" binding:"required"` //手机号 19 | Sex string `json:"sex" binding:"required"` //性别 20 | UserName string `json:"username" binding:"required"` //用户名 21 | } 22 | 23 | type EmployeePageQueryDTO struct { 24 | Name string `form:"name"` // 分页查询的name 25 | Page int `form:"page"` // 分页查询的页数 26 | PageSize int `form:"pageSize"` // 分页查询的页容量 27 | } 28 | -------------------------------------------------------------------------------- /internal/api/request/setmeal.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | import "take-out/internal/model" 4 | 5 | type SetMealDTO struct { 6 | Id uint64 `json:"id"` // 主键id 7 | CategoryId uint64 `json:"categoryId"` // 分类id 8 | Name string `json:"name"` // 套餐名称 9 | Price string `json:"price"` // 套餐单价 10 | Status int `json:"status"` // 套餐状态 11 | Description string `json:"description"` // 套餐描述 12 | Image string `json:"image"` // 套餐图片 13 | SetMealDishs []model.SetMealDish `json:"setmealDishes"` // 套餐菜品关系 14 | } 15 | 16 | type SetMealPageQueryDTO struct { 17 | Page int `form:"page"` // 分页查询的页数 18 | PageSize int `form:"pageSize"` // 分页查询的页容量 19 | Name string `form:"name"` // 分页查询的name 20 | CategoryId uint64 `form:"categoryId"` // 分类ID: 21 | Status int `form:"status"` // 套餐起售状态 22 | } 23 | -------------------------------------------------------------------------------- /internal/api/response/category.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | type Category struct { 4 | } 5 | -------------------------------------------------------------------------------- /internal/api/response/dish.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import ( 4 | "take-out/internal/model" 5 | "time" 6 | ) 7 | 8 | type DishPageVo struct { 9 | Id uint64 `json:"id" gorm:"primaryKey;AUTO_INCREMENT"` 10 | Name string `json:"name"` 11 | CategoryId uint64 `json:"categoryId"` 12 | Price float64 `json:"price"` 13 | Image string `json:"image"` 14 | Description string `json:"description"` 15 | Status int `json:"status"` 16 | UpdateTime time.Time `json:"updateTime"` 17 | CategoryName string `json:"categoryName"` 18 | } 19 | 20 | type DishVo struct { 21 | Id uint64 `json:"id" gorm:"primaryKey;AUTO_INCREMENT"` 22 | Name string `json:"name"` 23 | CategoryId uint64 `json:"categoryId"` 24 | Price float64 `json:"price"` 25 | Image string `json:"image"` 26 | Description string `json:"description"` 27 | Status int `json:"status"` 28 | UpdateTime time.Time `json:"updateTime"` 29 | //CategoryName string `json:"categoryName"` 30 | Flavors []model.DishFlavor `json:"flavors"` 31 | } 32 | 33 | type DishListVo struct { 34 | Id uint64 `json:"id"` 35 | Name string `json:"name"` 36 | CategoryId uint64 `json:"categoryId"` 37 | Price float64 `json:"price"` 38 | Image string `json:"image"` 39 | Description string `json:"description"` 40 | Status int `json:"status"` 41 | CreateTime time.Time `json:"createTime"` 42 | UpdateTime time.Time `json:"updateTime"` 43 | CreateUser uint64 `json:"createUser"` 44 | UpdateUser uint64 `json:"updateUser"` 45 | } 46 | -------------------------------------------------------------------------------- /internal/api/response/employee.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | type EmployeeLogin struct { 4 | Id uint64 `json:"id"` 5 | Name string `json:"name"` 6 | Token string `json:"token"` 7 | UserName string `json:"userName"` 8 | } 9 | -------------------------------------------------------------------------------- /internal/api/response/setmeal.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import ( 4 | "take-out/internal/model" 5 | "time" 6 | ) 7 | 8 | type SetMealPageQueryVo struct { 9 | Id uint64 `json:"id" ` // 主键id 10 | CategoryId uint64 `json:"categoryId"` // 分类id 11 | Name string `json:"name"` // 套餐名称 12 | Price float64 `json:"price"` // 套餐单价 13 | Status int `json:"status"` // 套餐状态 14 | Description string `json:"description"` // 套餐描述 15 | Image string `json:"image"` // 套餐图片 16 | CreateTime time.Time `json:"createTime"` // 创建时间 17 | UpdateTime time.Time `json:"updateTime"` // 更新时间 18 | CreateUser uint64 `json:"createUser"` // 创建用户 19 | UpdateUser uint64 `json:"updateUser"` // 更新用户 20 | CategoryName string `json:"categoryName"` // 分类名称 21 | } 22 | 23 | type SetMealWithDishByIdVo struct { 24 | Id uint64 `json:"id"` 25 | CategoryId uint64 `json:"categoryId"` 26 | CategoryName string `json:"categoryName"` 27 | Description string `json:"description"` 28 | Image string `json:"image"` 29 | Name string `json:"name"` 30 | Price float64 `json:"price"` 31 | SetmealDishes []model.SetMealDish `json:"setmealDishes"` 32 | Status int `json:"status"` 33 | UpdateTime time.Time `json:"updateTime"` 34 | } 35 | -------------------------------------------------------------------------------- /internal/model/category.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "gorm.io/gorm" 5 | "take-out/common/enum" 6 | "time" 7 | ) 8 | 9 | type Category struct { 10 | Id uint64 `json:"id"` 11 | Type int `json:"type"` 12 | Name string `json:"name"` 13 | Sort int `json:"sort"` 14 | Status int `json:"status"` 15 | CreateTime time.Time `json:"createTime"` 16 | UpdateTime time.Time `json:"updateTime"` 17 | CreateUser uint64 `json:"createUser"` 18 | UpdateUser uint64 `json:"updateUser"` 19 | } 20 | 21 | func (c *Category) BeforeCreate(tx *gorm.DB) error { 22 | // 自动填充 创建时间、创建人、更新时间、更新用户 23 | c.CreateTime = time.Now() 24 | c.UpdateTime = time.Now() 25 | // 从上下文获取用户信息 26 | value := tx.Statement.Context.Value(enum.CurrentId) 27 | if uid, ok := value.(uint64); ok { 28 | c.CreateUser = uid 29 | c.UpdateUser = uid 30 | } 31 | return nil 32 | } 33 | 34 | func (c *Category) BeforeUpdate(tx *gorm.DB) error { 35 | // 在更新记录千自动填充更新时间 36 | c.UpdateTime = time.Now() 37 | // 从上下文获取用户信息 38 | value := tx.Statement.Context.Value(enum.CurrentId) 39 | if uid, ok := value.(uint64); ok { 40 | c.UpdateUser = uid 41 | } 42 | return nil 43 | } 44 | -------------------------------------------------------------------------------- /internal/model/dish.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "gorm.io/gorm" 5 | "take-out/common/enum" 6 | "time" 7 | ) 8 | 9 | type Dish struct { 10 | Id uint64 `json:"id" gorm:"primaryKey;AUTO_INCREMENT"` 11 | Name string `json:"name"` 12 | CategoryId uint64 `json:"categoryId"` 13 | Price float64 `json:"price"` 14 | Image string `json:"image"` 15 | Description string `json:"description"` 16 | Status int `json:"status"` 17 | CreateTime time.Time `json:"createTime"` 18 | UpdateTime time.Time `json:"updateTime"` 19 | CreateUser uint64 `json:"createUser"` 20 | UpdateUser uint64 `json:"updateUser"` 21 | // 一对多 22 | Flavors []DishFlavor `json:"flavors"` 23 | } 24 | 25 | func (e *Dish) BeforeCreate(tx *gorm.DB) error { 26 | // 自动填充 创建时间、创建人、更新时间、更新用户 27 | e.CreateTime = time.Now() 28 | e.UpdateTime = time.Now() 29 | // 从上下文获取用户信息 30 | value := tx.Statement.Context.Value(enum.CurrentId) 31 | if uid, ok := value.(uint64); ok { 32 | e.CreateUser = uid 33 | e.UpdateUser = uid 34 | } 35 | return nil 36 | } 37 | 38 | func (e *Dish) BeforeUpdate(tx *gorm.DB) error { 39 | // 在更新记录千自动填充更新时间 40 | e.UpdateTime = time.Now() 41 | // 从上下文获取用户信息 42 | value := tx.Statement.Context.Value(enum.CurrentId) 43 | if uid, ok := value.(uint64); ok { 44 | e.UpdateUser = uid 45 | } 46 | return nil 47 | } 48 | 49 | func (e *Dish) TableName() string { 50 | return "dish" 51 | } 52 | -------------------------------------------------------------------------------- /internal/model/dish_flavor.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type DishFlavor struct { 4 | Id uint64 `json:"id"` //口味id 5 | DishId uint64 `json:"dish_id"` //菜品id 6 | Name string `json:"name"` //口味主题 温度|甜度|辣度 7 | Value string `json:"value"` //口味信息 可多个 8 | } 9 | 10 | func (d *DishFlavor) TableName() string { 11 | return "dish_flavor" 12 | } 13 | -------------------------------------------------------------------------------- /internal/model/employee.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "gorm.io/gorm" 5 | "take-out/common/enum" 6 | "time" 7 | ) 8 | 9 | type Employee struct { 10 | Id uint64 `json:"id"` 11 | Username string `json:"username"` 12 | Name string `json:"name"` 13 | Password string `json:"password"` 14 | Phone string `json:"phone"` 15 | Sex string `json:"sex"` 16 | IdNumber string `json:"idNumber"` 17 | Status int `json:"status"` 18 | CreateTime time.Time `json:"createTime"` 19 | UpdateTime time.Time `json:"updateTime"` 20 | CreateUser uint64 `json:"createUser"` 21 | UpdateUser uint64 `json:"updateUser"` 22 | } 23 | 24 | func (e *Employee) BeforeCreate(tx *gorm.DB) error { 25 | // 自动填充 创建时间、创建人、更新时间、更新用户 26 | e.CreateTime = time.Now() 27 | e.UpdateTime = time.Now() 28 | // 从上下文获取用户信息 29 | value := tx.Statement.Context.Value(enum.CurrentId) 30 | if uid, ok := value.(uint64); ok { 31 | e.CreateUser = uid 32 | e.UpdateUser = uid 33 | } 34 | return nil 35 | } 36 | 37 | func (e *Employee) BeforeUpdate(tx *gorm.DB) error { 38 | // 在更新记录千自动填充更新时间 39 | e.UpdateTime = time.Now() 40 | // 从上下文获取用户信息 41 | value := tx.Statement.Context.Value(enum.CurrentId) 42 | if uid, ok := value.(uint64); ok { 43 | e.UpdateUser = uid 44 | } 45 | return nil 46 | } 47 | 48 | func (e *Employee) AfterFind(tx *gorm.DB) error { 49 | // 格式化当前日期 50 | //e.CreateTime.Format(time.DateOnly) 51 | //e.CreateTime.Format(time.DateTime) 52 | return nil 53 | } 54 | -------------------------------------------------------------------------------- /internal/model/setmeal.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "gorm.io/gorm" 5 | "take-out/common/enum" 6 | "time" 7 | ) 8 | 9 | type SetMeal struct { 10 | Id uint64 `json:"id" gorm:"primaryKey;AUTO_INCREMENT"` // 主键id 11 | CategoryId uint64 `json:"category_id"` // 分类id 12 | Name string `json:"name"` // 套餐名称 13 | Price float64 `json:"price"` // 套餐单价 14 | Status int `json:"status"` // 套餐状态 15 | Description string `json:"description"` // 套餐描述 16 | Image string `json:"image"` // 套餐图片 17 | CreateTime time.Time `json:"create_time"` // 创建时间 18 | UpdateTime time.Time `json:"update_time"` // 更新时间 19 | CreateUser uint64 `json:"create_user"` // 创建用户 20 | UpdateUser uint64 `json:"update_user"` // 更新用户 21 | } 22 | 23 | func (e *SetMeal) BeforeCreate(tx *gorm.DB) error { 24 | // 自动填充 创建时间、创建人、更新时间、更新用户 25 | e.CreateTime = time.Now() 26 | e.UpdateTime = time.Now() 27 | // 从上下文获取用户信息 28 | value := tx.Statement.Context.Value(enum.CurrentId) 29 | if uid, ok := value.(uint64); ok { 30 | e.CreateUser = uid 31 | e.UpdateUser = uid 32 | } 33 | return nil 34 | } 35 | 36 | func (e *SetMeal) BeforeUpdate(tx *gorm.DB) error { 37 | // 在更新记录千自动填充更新时间 38 | e.UpdateTime = time.Now() 39 | // 从上下文获取用户信息 40 | value := tx.Statement.Context.Value(enum.CurrentId) 41 | if uid, ok := value.(uint64); ok { 42 | e.UpdateUser = uid 43 | } 44 | return nil 45 | } 46 | 47 | func (e *SetMeal) TableName() string { 48 | return "setmeal" 49 | } 50 | -------------------------------------------------------------------------------- /internal/model/setmeal_dish.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type SetMealDish struct { 4 | Id uint64 `json:"id"` // 中间表id 5 | SetmealId uint64 `json:"setmealId"` // 套餐id 6 | DishId uint64 `json:"dishId"` // 菜品id 7 | Name string `json:"name"` // 菜品名称冗余字段 8 | Price float64 `json:"price"` // 菜品单价冗余字段 9 | Copies int `json:"copies"` // 菜品数量冗余字段 10 | } 11 | 12 | func (e *SetMealDish) TableName() string { 13 | return "setmeal_dish" 14 | } 15 | -------------------------------------------------------------------------------- /internal/repository/dao/category_dao.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "context" 5 | "gorm.io/gorm" 6 | "take-out/common" 7 | "take-out/common/e" 8 | "take-out/common/retcode" 9 | "take-out/internal/api/request" 10 | "take-out/internal/model" 11 | ) 12 | 13 | type CategoryDao struct { 14 | db *gorm.DB 15 | } 16 | 17 | func (d *CategoryDao) SetStatus(ctx context.Context, category model.Category) error { 18 | err := d.db.WithContext(ctx).Model(&category).Update("status", category.Status).Error 19 | if err != nil { 20 | return retcode.NewError(e.MysqlERR, "SetStatus category failed") 21 | } 22 | return nil 23 | } 24 | 25 | func (d *CategoryDao) Update(ctx context.Context, category model.Category) error { 26 | err := d.db.WithContext(ctx).Model(&category).Updates(&category).Error 27 | if err != nil { 28 | return retcode.NewError(e.MysqlERR, "Update category failed") 29 | } 30 | return nil 31 | } 32 | 33 | func (d *CategoryDao) DeleteById(ctx context.Context, id uint64) error { 34 | err := d.db.WithContext(ctx).Delete(&model.Category{Id: id}).Error 35 | if err != nil { 36 | return retcode.NewError(e.MysqlERR, "Delete category failed") 37 | } 38 | return nil 39 | } 40 | 41 | func (d *CategoryDao) List(ctx context.Context, cate int) ([]model.Category, error) { 42 | var categoryList []model.Category 43 | err := d.db.WithContext(ctx).Where("type = ?", cate). 44 | Order("sort asc"). 45 | Order("create_time desc"). 46 | Find(&categoryList). 47 | Error 48 | if err != nil { 49 | return nil, retcode.NewError(e.MysqlERR, "Get category List failed") 50 | } 51 | return categoryList, nil 52 | } 53 | 54 | func (d *CategoryDao) PageQuery(ctx context.Context, dto request.CategoryPageQueryDTO) (*common.PageResult, error) { 55 | var pageResult common.PageResult 56 | var categoryList []model.Category 57 | 58 | // 构造初始查询结构 59 | query := d.db.WithContext(ctx).Model(&model.Category{}) 60 | if dto.Name != "" { 61 | query = query.Where("name like ?", "%"+dto.Name+"%") 62 | } 63 | if dto.Cate != 0 { 64 | query = query.Where("type = ?", dto.Cate) 65 | } 66 | // 计算总数 67 | if err := query.Count(&pageResult.Total).Error; err != nil { 68 | return nil, retcode.NewError(e.MysqlERR, "Get category List failed") 69 | } 70 | // 查询数据 71 | err := query.Scopes(pageResult.Paginate(&dto.Page, &dto.PageSize)). 72 | Order("create_time desc"). 73 | Find(&categoryList). 74 | Error 75 | if err != nil { 76 | return nil, retcode.NewError(e.MysqlERR, "Get category List failed") 77 | } 78 | pageResult.Records = categoryList 79 | return &pageResult, nil 80 | } 81 | 82 | func (d *CategoryDao) Insert(ctx context.Context, category model.Category) error { 83 | // 新增分类数据 84 | err := d.db.WithContext(ctx).Create(&category).Error 85 | if err != nil { 86 | return retcode.NewError(e.MysqlERR, "Create category failed") 87 | } 88 | return err 89 | } 90 | 91 | func NewCategoryDao(db *gorm.DB) *CategoryDao { 92 | return &CategoryDao{db: db} 93 | } 94 | -------------------------------------------------------------------------------- /internal/repository/dao/dish_dao.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "context" 5 | "gorm.io/gorm" 6 | "take-out/common" 7 | "take-out/common/e" 8 | "take-out/common/retcode" 9 | "take-out/internal/api/request" 10 | "take-out/internal/api/response" 11 | "take-out/internal/model" 12 | ) 13 | 14 | type DishDao struct { 15 | db *gorm.DB 16 | } 17 | 18 | func (d *DishDao) Delete(ctx context.Context, id uint64) error { 19 | err := d.db.Delete(&model.Dish{Id: id}).Error 20 | if err != nil { 21 | return retcode.NewError(e.MysqlERR, "delete dish failed") 22 | } 23 | return nil 24 | } 25 | 26 | func (d *DishDao) Update(ctx context.Context, dish model.Dish) error { 27 | err := d.db.Model(&dish).Updates(dish).Error 28 | if err != nil { 29 | return retcode.NewError(e.MysqlERR, "update dish failed") 30 | } 31 | return nil 32 | } 33 | 34 | func (d *DishDao) OnOrClose(ctx context.Context, id uint64, status int) error { 35 | err := d.db.WithContext(ctx).Model(&model.Dish{Id: id}).Update("status", status).Error 36 | if err != nil { 37 | return retcode.NewError(e.MysqlERR, "update dish failed") 38 | } 39 | return nil 40 | } 41 | 42 | func (d *DishDao) List(ctx context.Context, categoryId uint64) ([]model.Dish, error) { 43 | var dishList []model.Dish 44 | err := d.db.WithContext(ctx).Where("category_id = ?", categoryId).Find(&dishList).Error 45 | if err != nil { 46 | return nil, retcode.NewError(e.MysqlERR, "get dish list failed") 47 | } 48 | return dishList, nil 49 | } 50 | 51 | func (d *DishDao) GetById(ctx context.Context, id uint64) (*model.Dish, error) { 52 | var dish model.Dish 53 | dish.Id = id 54 | err := d.db.WithContext(ctx).Preload("Flavors").Find(&dish).Error 55 | if err != nil { 56 | return nil, retcode.NewError(e.MysqlERR, "get dish failed") 57 | } 58 | return &dish, nil 59 | } 60 | 61 | func (d *DishDao) PageQuery(ctx context.Context, dto *request.DishPageQueryDTO) (*common.PageResult, error) { 62 | var pageResult common.PageResult 63 | var dishList []response.DishPageVo 64 | // 1.动态拼接sql 65 | query := d.db.WithContext(ctx).Model(&model.Dish{}) 66 | if dto.Name != "" { 67 | query = query.Where("name LIKE ", "%"+dto.Name+"%") 68 | } 69 | if dto.Status != 0 { 70 | query = query.Where("status = ?", dto.Status) 71 | } 72 | if dto.CategoryId != 0 { 73 | query = query.Where("category_id = ?", dto.CategoryId) 74 | } 75 | // 2.动态查询Total 76 | if err := query.Count(&pageResult.Total).Error; err != nil { 77 | return nil, retcode.NewError(e.MysqlERR, "get dish failed") 78 | } 79 | // 3.通用分页查询 80 | if err := query.Scopes(pageResult.Paginate(&dto.Page, &dto.PageSize)). 81 | Select("dish.*,c.name as category_name"). 82 | Joins("LEFT OUTER JOIN category c ON c.id = dish.category_id"). 83 | Order("dish.create_time desc").Scan(&dishList).Error; err != nil { 84 | return nil, retcode.NewError(e.MysqlERR, "get dish failed") 85 | } 86 | // 构造返回结果 87 | pageResult.Records = dishList 88 | return &pageResult, nil 89 | } 90 | 91 | // Insert 插入菜品数据 92 | func (d *DishDao) Insert(ctx context.Context, dish *model.Dish) error { 93 | err := d.db.Create(dish).Error 94 | if err != nil { 95 | return retcode.NewError(e.MysqlERR, "get dish failed") 96 | } 97 | return nil 98 | } 99 | 100 | func NewDishRepo(db *gorm.DB) *DishDao { 101 | return &DishDao{db: db} 102 | } 103 | 104 | // WithTx 切换到事务模式 105 | func (d *DishDao) WithTx(db *gorm.DB) *DishDao { 106 | return &DishDao{db: db} 107 | } 108 | -------------------------------------------------------------------------------- /internal/repository/dao/dish_flavor.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "context" 5 | "gorm.io/gorm" 6 | "take-out/common/e" 7 | "take-out/common/retcode" 8 | "take-out/internal/model" 9 | ) 10 | 11 | type DishFlavorDao struct { 12 | db *gorm.DB 13 | } 14 | 15 | func (d *DishFlavorDao) Update(ctx context.Context, flavor model.DishFlavor) error { 16 | err := d.db.Model(&model.DishFlavor{Id: flavor.Id}).Updates(flavor).Error 17 | if err != nil { 18 | return retcode.NewError(e.MysqlERR, "update dish flavor failed") 19 | } 20 | return nil 21 | } 22 | 23 | func (d *DishFlavorDao) InsertBatch(ctx context.Context, flavor []model.DishFlavor) error { 24 | // 批量插入口味数据 25 | err := d.db.Create(&flavor).Error 26 | if err != nil { 27 | return retcode.NewError(e.MysqlERR, "insert dish flavor failed") 28 | } 29 | return nil 30 | } 31 | 32 | func (d *DishFlavorDao) DeleteByDishId(ctx context.Context, dishId uint64) error { 33 | // 根据dishId删除对应的口味数据 34 | err := d.db.Where("dish_id = ?", dishId).Delete(&model.DishFlavor{}).Error 35 | if err != nil { 36 | return retcode.NewError(e.MysqlERR, "delete dish flavor failed") 37 | } 38 | return nil 39 | } 40 | 41 | func (d *DishFlavorDao) GetByDishId(ctx context.Context, dishId uint64) ([]model.DishFlavor, error) { 42 | //TODO implement me 43 | panic("implement me") 44 | } 45 | 46 | // NewDishFlavorDao 创建dao实例 47 | func NewDishFlavorDao(db *gorm.DB) *DishFlavorDao { 48 | return &DishFlavorDao{db: db} 49 | } 50 | 51 | // WithTx 使用事务 52 | func (d *DishFlavorDao) WithTx(tx *gorm.DB) *DishFlavorDao { 53 | return &DishFlavorDao{db: tx} 54 | } 55 | -------------------------------------------------------------------------------- /internal/repository/dao/employee_dao.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "context" 5 | "gorm.io/gorm" 6 | "take-out/common" 7 | "take-out/common/e" 8 | "take-out/common/retcode" 9 | "take-out/internal/api/request" 10 | "take-out/internal/model" 11 | ) 12 | 13 | type EmployeeDao struct { 14 | db *gorm.DB 15 | } 16 | 17 | // UpdateStatus 动态更新包括零值 18 | func (d *EmployeeDao) UpdateStatus(ctx context.Context, employee model.Employee) error { 19 | err := d.db.WithContext(ctx).Model(&model.Employee{}).Where("id = ?", 20 | employee.Id).Update("status", employee.Status).Error 21 | if err != nil { 22 | return retcode.NewError(e.MysqlERR, "update employee failed") 23 | } 24 | return nil 25 | } 26 | 27 | func (d *EmployeeDao) PageQuery(ctx context.Context, dto request.EmployeePageQueryDTO) (*common.PageResult, error) { 28 | // 分页查询 select count(*) from employee where name = ? limit x,y 29 | var result common.PageResult 30 | var employeeList []model.Employee 31 | var err error 32 | // 动态拼接 33 | query := d.db.WithContext(ctx).Model(&model.Employee{}) 34 | if dto.Name != "" { 35 | query = query.Where("name LIKE ?", "%"+dto.Name+"%") 36 | } 37 | // 计算总数 38 | if err = query.Count(&result.Total).Error; err != nil { 39 | return nil, retcode.NewError(e.MysqlERR, "Get employee List failed") 40 | } 41 | // 分页查询 42 | err = query.Scopes(result.Paginate(&dto.Page, &dto.PageSize)).Find(&employeeList).Error 43 | if err != nil { 44 | return nil, retcode.NewError(e.MysqlERR, "Get employee List failed") 45 | } 46 | result.Records = employeeList 47 | return &result, nil 48 | } 49 | 50 | func (d *EmployeeDao) Insert(ctx context.Context, entity model.Employee) error { 51 | err := d.db.WithContext(ctx).Create(&entity).Error 52 | if err != nil { 53 | return retcode.NewError(e.MysqlERR, "Create employee failed") 54 | } 55 | return nil 56 | } 57 | 58 | func (d *EmployeeDao) GetById(ctx context.Context, id uint64) (*model.Employee, error) { 59 | var employee model.Employee 60 | err := d.db.WithContext(ctx).Where("id=?", id).First(&employee).Error 61 | if err != nil { 62 | return nil, retcode.NewError(e.MysqlERR, "Get employee failed") 63 | } 64 | return &employee, nil 65 | } 66 | 67 | func (d *EmployeeDao) Update(ctx context.Context, employee model.Employee) error { 68 | err := d.db.WithContext(ctx).Model(&employee).Updates(employee).Error 69 | if err != nil { 70 | return retcode.NewError(e.MysqlERR, "Update employee failed") 71 | } 72 | return nil 73 | } 74 | 75 | func (d *EmployeeDao) GetByUserName(ctx context.Context, userName string) (*model.Employee, error) { 76 | var employee model.Employee 77 | err := d.db.WithContext(ctx).Where("username=?", userName).First(&employee).Error 78 | if err != nil { 79 | return nil, retcode.NewError(e.MysqlERR, "Get employee failed") 80 | } 81 | return &employee, nil 82 | } 83 | 84 | func NewEmployeeDao(db *gorm.DB) *EmployeeDao { 85 | return &EmployeeDao{db: db} 86 | } 87 | -------------------------------------------------------------------------------- /internal/repository/dao/setmeal_dao.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "context" 5 | "gorm.io/gorm" 6 | "take-out/common" 7 | "take-out/common/e" 8 | "take-out/common/retcode" 9 | "take-out/internal/api/request" 10 | "take-out/internal/api/response" 11 | "take-out/internal/model" 12 | ) 13 | 14 | type SetMealDao struct { 15 | db *gorm.DB 16 | } 17 | 18 | func (s *SetMealDao) GetByIdWithDish(ctx context.Context, id uint64) (*model.SetMeal, error) { 19 | var setMeal model.SetMeal 20 | err := s.db.WithContext(ctx).First(&setMeal, id).Error 21 | if err != nil { 22 | return nil, retcode.NewError(e.MysqlERR, "Get setMeal failed") 23 | } 24 | return &setMeal, nil 25 | } 26 | 27 | func (s *SetMealDao) SetStatus(ctx context.Context, id uint64, status int) error { 28 | err := s.db.WithContext(ctx).Model(&model.SetMeal{Id: id}).Update("status", status).Error 29 | if err != nil { 30 | return retcode.NewError(e.MysqlERR, "Update setMeal failed") 31 | } 32 | return nil 33 | } 34 | 35 | func (s *SetMealDao) PageQuery(ctx context.Context, dto request.SetMealPageQueryDTO) (*common.PageResult, error) { 36 | var pageResult common.PageResult 37 | var setmealPageQueryVo []response.SetMealPageQueryVo 38 | // 构造基础查询 39 | query := s.db.WithContext(ctx).Model(&model.SetMeal{}) 40 | // 动态构造查询条件 41 | if dto.CategoryId != 0 { 42 | query = query.Where("setmeal.category_id = ?", dto.CategoryId) 43 | } 44 | if dto.Name != "" { 45 | query = query.Where("setmeal.name LIKE ?", "%"+dto.Name+"%") 46 | } 47 | if dto.Status != 0 { 48 | query = query.Where("setmeal.status = ?", dto.Status) 49 | } 50 | if err := query.Count(&pageResult.Total).Error; err != nil { 51 | return nil, err 52 | } 53 | // 分页查询构造 54 | err := query.Scopes(pageResult.Paginate(&dto.Page, &dto.PageSize)). 55 | Select("setmeal.*,c.name as category_name"). 56 | Joins("LEFT JOIN category c ON setmeal.category_id = c.id"). 57 | Order("create_time desc"). 58 | Scan(&setmealPageQueryVo).Error 59 | if err != nil { 60 | return nil, retcode.NewError(e.MysqlERR, "Get setMeal List failed") 61 | } 62 | // 整合数据下发 63 | pageResult.Records = setmealPageQueryVo 64 | return &pageResult, nil 65 | } 66 | 67 | func (s *SetMealDao) Insert(ctx context.Context, meal *model.SetMeal) error { 68 | err := s.db.WithContext(ctx).Create(&meal).Error 69 | if err != nil { 70 | return retcode.NewError(e.MysqlERR, "Create setMeal failed") 71 | } 72 | return nil 73 | } 74 | 75 | func NewSetMealDao(db *gorm.DB) *SetMealDao { 76 | return &SetMealDao{db: db} 77 | } 78 | 79 | func (s *SetMealDao) WithTx(db *gorm.DB) *SetMealDao { 80 | return &SetMealDao{db: db} 81 | } 82 | -------------------------------------------------------------------------------- /internal/repository/dao/setmeal_dish_dao.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "context" 5 | "gorm.io/gorm" 6 | "take-out/common/e" 7 | "take-out/common/retcode" 8 | "take-out/internal/model" 9 | ) 10 | 11 | type SetMealDishDao struct { 12 | db *gorm.DB 13 | } 14 | 15 | func (d *SetMealDishDao) GetBySetMealId(ctx context.Context, SetMealId uint64) ([]model.SetMealDish, error) { 16 | var dishList []model.SetMealDish 17 | err := d.db.WithContext(ctx).Where("setmeal_id = ?", SetMealId).Find(&dishList).Error 18 | if err != nil { 19 | return nil, retcode.NewError(e.MysqlERR, "delete dish failed") 20 | } 21 | return dishList, nil 22 | } 23 | 24 | func (d *SetMealDishDao) InsertBatch(ctx context.Context, setmealDishs []model.SetMealDish) error { 25 | err := d.db.WithContext(ctx).Create(&setmealDishs).Error 26 | if err != nil { 27 | return retcode.NewError(e.MysqlERR, "delete dish failed") 28 | } 29 | return nil 30 | } 31 | 32 | func NewSetMealDishDao() *SetMealDishDao { 33 | return &SetMealDishDao{} 34 | } 35 | 36 | func (d *SetMealDishDao) WithTx(db *gorm.DB) *SetMealDishDao { 37 | return &SetMealDishDao{db: db} 38 | } 39 | -------------------------------------------------------------------------------- /internal/router/admin/category.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "take-out/global" 6 | "take-out/internal/api/controller" 7 | "take-out/internal/repository/dao" 8 | "take-out/internal/service" 9 | "take-out/middle" 10 | ) 11 | 12 | type CategoryRouter struct{} 13 | 14 | func (cr *CategoryRouter) InitApiRouter(parent *gin.RouterGroup) { 15 | //publicRouter := parent.Group("category") 16 | privateRouter := parent.Group("category") 17 | // 私有路由使用jwt验证 18 | privateRouter.Use(middle.VerifyJWTAdmin()) 19 | // 依赖注入 20 | categoryCtrl := controller.NewCategoryController( 21 | service.NewCategoryService(dao.NewCategoryDao(global.DB)), 22 | ) 23 | { 24 | privateRouter.POST("", categoryCtrl.AddCategory) 25 | privateRouter.GET("/page", categoryCtrl.PageQuery) 26 | privateRouter.GET("list", categoryCtrl.List) 27 | privateRouter.DELETE("", categoryCtrl.DeleteById) 28 | privateRouter.PUT("", categoryCtrl.EditCategory) 29 | privateRouter.POST("status/:status", categoryCtrl.SetStatus) 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /internal/router/admin/common.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "take-out/internal/api/controller" 6 | "take-out/middle" 7 | ) 8 | 9 | type CommonRouter struct{} 10 | 11 | func (dr *CommonRouter) InitApiRouter(parent *gin.RouterGroup) { 12 | //publicRouter := parent.Group("category") 13 | privateRouter := parent.Group("common") 14 | // 私有路由使用jwt验证 15 | privateRouter.Use(middle.VerifyJWTAdmin()) 16 | // 依赖注入 17 | commonCtrl := new(controller.CommonController) 18 | { 19 | privateRouter.POST("upload", commonCtrl.Upload) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /internal/router/admin/dish.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "take-out/global" 6 | "take-out/internal/api/controller" 7 | "take-out/internal/repository/dao" 8 | "take-out/internal/service" 9 | "take-out/middle" 10 | ) 11 | 12 | type DishRouter struct{} 13 | 14 | func (dr *DishRouter) InitApiRouter(parent *gin.RouterGroup) { 15 | //publicRouter := parent.Group("category") 16 | privateRouter := parent.Group("dish") 17 | // 私有路由使用jwt验证 18 | privateRouter.Use(middle.VerifyJWTAdmin()) 19 | // 依赖注入 20 | dishCtrl := controller.NewDishController( 21 | service.NewDishService( 22 | dao.NewDishRepo(global.DB), 23 | dao.NewDishFlavorDao(global.DB), 24 | ), 25 | ) 26 | { 27 | privateRouter.POST("", dishCtrl.AddDish) 28 | privateRouter.GET("/page", dishCtrl.PageQuery) 29 | privateRouter.GET("/:id", dishCtrl.GetById) 30 | privateRouter.GET("/list", dishCtrl.List) 31 | privateRouter.POST("/status/:status", dishCtrl.OnOrClose) 32 | privateRouter.PUT("", dishCtrl.Update) 33 | privateRouter.DELETE("", dishCtrl.Delete) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /internal/router/admin/employee.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "take-out/global" 6 | "take-out/internal/api/controller" 7 | "take-out/internal/repository/dao" 8 | "take-out/internal/service" 9 | "take-out/middle" 10 | ) 11 | 12 | type EmployeeRouter struct{ service service.IEmployeeService } 13 | 14 | func (er *EmployeeRouter) InitApiRouter(router *gin.RouterGroup) { 15 | publicRouter := router.Group("employee") 16 | privateRouter := router.Group("employee") 17 | // 私有路由使用jwt验证 18 | privateRouter.Use(middle.VerifyJWTAdmin()) 19 | // 依赖注入 20 | er.service = service.NewEmployeeService( 21 | dao.NewEmployeeDao(global.DB), 22 | ) 23 | employeeCtl := controller.NewEmployeeController(er.service) 24 | { 25 | publicRouter.POST("/login", employeeCtl.Login) 26 | privateRouter.POST("/logout", employeeCtl.Logout) 27 | privateRouter.POST("", employeeCtl.AddEmployee) 28 | privateRouter.POST("/status/:status", employeeCtl.OnOrOff) 29 | privateRouter.PUT("/editPassword", employeeCtl.EditPassword) 30 | privateRouter.PUT("", employeeCtl.UpdateEmployee) 31 | privateRouter.GET("/page", employeeCtl.PageQuery) 32 | privateRouter.GET("/:id", employeeCtl.GetById) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /internal/router/admin/setmeal.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "take-out/global" 6 | "take-out/internal/api/controller" 7 | "take-out/internal/repository/dao" 8 | "take-out/internal/service" 9 | "take-out/middle" 10 | ) 11 | 12 | type SetMealRouter struct{} 13 | 14 | func (sr *SetMealRouter) InitApiRouter(parent *gin.RouterGroup) { 15 | //publicRouter := parent.Group("category") 16 | privateRouter := parent.Group("setmeal") 17 | // 私有路由使用jwt验证 18 | privateRouter.Use(middle.VerifyJWTAdmin()) 19 | // 依赖注入 20 | setmealCtrl := controller.NewSetMealController( 21 | service.NewSetMealService(dao.NewSetMealDao(global.DB), dao.NewSetMealDishDao()), 22 | ) 23 | { 24 | privateRouter.POST("", setmealCtrl.SaveWithDish) 25 | privateRouter.GET("/page", setmealCtrl.PageQuery) 26 | privateRouter.GET("/:id", setmealCtrl.GetByIdWithDish) 27 | privateRouter.POST("/status/:status", setmealCtrl.OnOrClose) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /internal/router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import "take-out/internal/router/admin" 4 | 5 | type RouterGroup struct { 6 | admin.EmployeeRouter 7 | admin.CategoryRouter 8 | admin.DishRouter 9 | admin.CommonRouter 10 | admin.SetMealRouter 11 | } 12 | 13 | var AllRouter = new(RouterGroup) 14 | -------------------------------------------------------------------------------- /internal/service/category_service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "strconv" 6 | "take-out/common" 7 | "take-out/common/enum" 8 | "take-out/internal/api/request" 9 | "take-out/internal/model" 10 | "take-out/internal/repository/dao" 11 | ) 12 | 13 | type ICategoryService interface { 14 | AddCategory(ctx context.Context, dto request.CategoryDTO) error 15 | PageQuery(ctx context.Context, dto request.CategoryPageQueryDTO) (*common.PageResult, error) 16 | List(ctx context.Context, cate int) ([]model.Category, error) 17 | DeleteById(ctx context.Context, id uint64) error 18 | Update(ctx context.Context, dto request.CategoryDTO) error 19 | SetStatus(ctx context.Context, id uint64, status int) error 20 | } 21 | 22 | type CategoryImpl struct { 23 | repo *dao.CategoryDao 24 | } 25 | 26 | func (c *CategoryImpl) SetStatus(ctx context.Context, id uint64, status int) error { 27 | err := c.repo.SetStatus(ctx, model.Category{ 28 | Id: id, 29 | Status: status, 30 | }) 31 | return err 32 | } 33 | 34 | func (c *CategoryImpl) Update(ctx context.Context, dto request.CategoryDTO) error { 35 | cate, _ := strconv.Atoi(dto.Cate) 36 | sort, _ := strconv.Atoi(dto.Sort) 37 | err := c.repo.Update(ctx, model.Category{ 38 | Id: dto.Id, 39 | Type: cate, 40 | Name: dto.Name, 41 | Sort: sort, 42 | }) 43 | return err 44 | } 45 | 46 | func (c *CategoryImpl) DeleteById(ctx context.Context, id uint64) error { 47 | err := c.repo.DeleteById(ctx, id) 48 | return err 49 | } 50 | 51 | func (c *CategoryImpl) List(ctx context.Context, cate int) ([]model.Category, error) { 52 | list, err := c.repo.List(ctx, cate) 53 | return list, err 54 | } 55 | 56 | func (c *CategoryImpl) PageQuery(ctx context.Context, dto request.CategoryPageQueryDTO) (*common.PageResult, error) { 57 | query, err := c.repo.PageQuery(ctx, dto) 58 | return query, err 59 | } 60 | 61 | func (c *CategoryImpl) AddCategory(ctx context.Context, dto request.CategoryDTO) error { 62 | // 前端传递的参数是错误的string类型,没办法只能强转了 63 | cate, _ := strconv.Atoi(dto.Cate) 64 | sort, _ := strconv.Atoi(dto.Sort) 65 | // 新增分类 66 | err := c.repo.Insert(ctx, model.Category{ 67 | Type: cate, 68 | Name: dto.Name, 69 | Sort: sort, 70 | Status: enum.ENABLE, 71 | }) 72 | return err 73 | } 74 | 75 | func NewCategoryService(repo *dao.CategoryDao) ICategoryService { 76 | return &CategoryImpl{repo: repo} 77 | } 78 | -------------------------------------------------------------------------------- /internal/service/dish_service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "github.com/spf13/cast" 6 | "gorm.io/gorm" 7 | "strconv" 8 | "strings" 9 | "take-out/common" 10 | "take-out/common/enum" 11 | "take-out/global" 12 | "take-out/internal/api/request" 13 | "take-out/internal/api/response" 14 | "take-out/internal/model" 15 | "take-out/internal/repository/dao" 16 | ) 17 | 18 | type IDishService interface { 19 | AddDishWithFlavors(ctx context.Context, dto request.DishDTO) error 20 | PageQuery(ctx context.Context, dto *request.DishPageQueryDTO) (*common.PageResult, error) 21 | GetByIdWithFlavors(ctx context.Context, id uint64) (response.DishVo, error) 22 | List(ctx context.Context, categoryId uint64) ([]response.DishListVo, error) 23 | OnOrClose(ctx context.Context, id uint64, status int) error 24 | Update(ctx context.Context, dto request.DishUpdateDTO) error 25 | Delete(ctx context.Context, ids string) error 26 | } 27 | 28 | type DishServiceImpl struct { 29 | repo *dao.DishDao 30 | dishFlavorRepo *dao.DishFlavorDao 31 | } 32 | 33 | func (d *DishServiceImpl) Delete(ctx context.Context, ids string) error { 34 | // ids 为多个id的组合,以,进行分割,进行批量删除 35 | idList := strings.Split(ids, ",") 36 | for _, idStr := range idList { 37 | err := global.DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error { 38 | dishId := cast.ToUint64(idStr) 39 | err := d.dishFlavorRepo.WithTx(tx).DeleteByDishId(ctx, dishId) 40 | if err != nil { 41 | return err 42 | } 43 | err = d.repo.WithTx(tx).Delete(ctx, dishId) 44 | if err != nil { 45 | return err 46 | } 47 | return nil 48 | }) 49 | if err != nil { 50 | return err 51 | } 52 | } 53 | return nil 54 | } 55 | 56 | func (d *DishServiceImpl) Update(ctx context.Context, dto request.DishUpdateDTO) error { 57 | price := cast.ToFloat64(dto.Price) 58 | dish := model.Dish{ 59 | Id: dto.Id, 60 | Name: dto.Name, 61 | CategoryId: dto.CategoryId, 62 | Price: price, 63 | Image: dto.Image, 64 | Description: dto.Description, 65 | Status: dto.Status, 66 | Flavors: dto.Flavors, 67 | } 68 | // 开启事务 69 | err := global.DB.Transaction(func(tx *gorm.DB) error { 70 | // 更新菜品信息 71 | err := d.repo.WithTx(tx).Update(ctx, dish) 72 | if err != nil { 73 | return err 74 | } 75 | // 更新菜品的口味分两步: 1.先删除原有的所有关联数据,2.再插入新的口味数据 76 | err = d.dishFlavorRepo.WithTx(tx).DeleteByDishId(ctx, dish.Id) 77 | if err != nil { 78 | return err 79 | } 80 | if len(dish.Flavors) != 0 { 81 | err = d.dishFlavorRepo.WithTx(tx).InsertBatch(ctx, dish.Flavors) 82 | if err != nil { 83 | return err 84 | } 85 | } 86 | return nil 87 | }) 88 | 89 | return err 90 | } 91 | 92 | func (d *DishServiceImpl) OnOrClose(ctx context.Context, id uint64, status int) error { 93 | err := d.repo.OnOrClose(ctx, id, status) 94 | return err 95 | } 96 | 97 | func (d *DishServiceImpl) List(ctx context.Context, categoryId uint64) ([]response.DishListVo, error) { 98 | var dishListVo []response.DishListVo 99 | dishList, err := d.repo.List(ctx, categoryId) 100 | if err != nil { 101 | return nil, err 102 | } 103 | // 这里这样的写法是 规范化Vo传输对象。 104 | for _, dish := range dishList { 105 | dishListVo = append(dishListVo, response.DishListVo{ 106 | Id: dish.Id, 107 | Name: dish.Name, 108 | CategoryId: dish.CategoryId, 109 | Price: dish.Price, 110 | Image: dish.Image, 111 | Description: dish.Description, 112 | Status: dish.Status, 113 | CreateTime: dish.CreateTime, 114 | UpdateTime: dish.UpdateTime, 115 | CreateUser: dish.CreateUser, 116 | UpdateUser: dish.UpdateUser, 117 | }) 118 | } 119 | return dishListVo, nil 120 | } 121 | 122 | func (d *DishServiceImpl) GetByIdWithFlavors(ctx context.Context, id uint64) (response.DishVo, error) { 123 | 124 | dish, err := d.repo.GetById(ctx, id) 125 | dishVo := response.DishVo{ 126 | Id: dish.Id, 127 | Name: dish.Name, 128 | CategoryId: dish.CategoryId, 129 | Price: dish.Price, 130 | Image: dish.Image, 131 | Description: dish.Description, 132 | Status: dish.Status, 133 | UpdateTime: dish.UpdateTime, 134 | Flavors: dish.Flavors, 135 | } 136 | return dishVo, err 137 | } 138 | 139 | func (d *DishServiceImpl) PageQuery(ctx context.Context, dto *request.DishPageQueryDTO) (*common.PageResult, error) { 140 | pageResult, err := d.repo.PageQuery(ctx, dto) 141 | if err != nil { 142 | return nil, err 143 | } 144 | return pageResult, err 145 | } 146 | 147 | func (d *DishServiceImpl) AddDishWithFlavors(ctx context.Context, dto request.DishDTO) error { 148 | // 1.构建dish数据 149 | price, _ := strconv.ParseFloat(dto.Price, 10) 150 | dish := model.Dish{ 151 | Id: 0, 152 | Name: dto.Name, 153 | CategoryId: dto.CategoryId, 154 | Price: price, 155 | Image: dto.Image, 156 | Description: dto.Description, 157 | Status: enum.ENABLE, 158 | } 159 | // 开启事务 160 | err := global.DB.Transaction(func(tx *gorm.DB) error { 161 | // 2.先新增菜品数据,再新增口味数据 162 | err := d.repo.WithTx(tx).Insert(ctx, &dish) 163 | if err != nil { 164 | return err 165 | } 166 | // 为口味数据附加菜品id 167 | for i, _ := range dto.Flavors { 168 | dto.Flavors[i].DishId = dish.Id 169 | } 170 | err = d.dishFlavorRepo.WithTx(tx).InsertBatch(ctx, dto.Flavors) 171 | if err != nil { 172 | return err 173 | } 174 | return nil 175 | }) 176 | return err 177 | } 178 | 179 | func NewDishService(repo *dao.DishDao, dishFlavorRepo *dao.DishFlavorDao) IDishService { 180 | return &DishServiceImpl{repo: repo, dishFlavorRepo: dishFlavorRepo} 181 | } 182 | -------------------------------------------------------------------------------- /internal/service/employee_service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "take-out/common" 6 | "take-out/common/e" 7 | "take-out/common/enum" 8 | "take-out/common/utils" 9 | "take-out/global" 10 | "take-out/internal/api/request" 11 | "take-out/internal/api/response" 12 | "take-out/internal/model" 13 | "take-out/internal/repository/dao" 14 | ) 15 | 16 | type IEmployeeService interface { 17 | Login(context.Context, request.EmployeeLogin) (*response.EmployeeLogin, error) 18 | Logout(ctx context.Context) error 19 | EditPassword(context.Context, request.EmployeeEditPassword) error 20 | CreateEmployee(ctx context.Context, employee request.EmployeeDTO) error 21 | PageQuery(ctx context.Context, dto request.EmployeePageQueryDTO) (*common.PageResult, error) 22 | SetStatus(ctx context.Context, id uint64, status int) error 23 | UpdateEmployee(ctx context.Context, dto request.EmployeeDTO) error 24 | GetById(ctx context.Context, id uint64) (*model.Employee, error) 25 | } 26 | 27 | type EmployeeImpl struct { 28 | repo *dao.EmployeeDao 29 | } 30 | 31 | func (ei *EmployeeImpl) GetById(ctx context.Context, id uint64) (*model.Employee, error) { 32 | employee, err := ei.repo.GetById(ctx, id) 33 | if err != nil { 34 | return nil, err 35 | } 36 | employee.Password = "***" 37 | return employee, err 38 | } 39 | 40 | func (ei *EmployeeImpl) UpdateEmployee(ctx context.Context, dto request.EmployeeDTO) error { 41 | // 构建model实体进行更新 42 | err := ei.repo.Update(ctx, model.Employee{ 43 | Id: dto.Id, 44 | Username: dto.UserName, 45 | Name: dto.Name, 46 | Phone: dto.Phone, 47 | Sex: dto.Sex, 48 | IdNumber: dto.IdNumber, 49 | }) 50 | return err 51 | } 52 | 53 | func (ei *EmployeeImpl) SetStatus(ctx context.Context, id uint64, status int) error { 54 | // 设置用户状态,构造实体 55 | entity := model.Employee{Id: id, Status: status} 56 | err := ei.repo.UpdateStatus(ctx, entity) 57 | return err 58 | } 59 | 60 | func (ei *EmployeeImpl) PageQuery(ctx context.Context, dto request.EmployeePageQueryDTO) (*common.PageResult, error) { 61 | // 分页查询 62 | pageResult, err := ei.repo.PageQuery(ctx, dto) 63 | // 屏蔽敏感信息 64 | if employees, ok := pageResult.Records.([]model.Employee); ok { 65 | // 替换敏感信息 66 | for key, _ := range employees { 67 | employees[key].Password = "****" 68 | employees[key].IdNumber = "****" 69 | employees[key].Phone = "****" 70 | } 71 | // 重新赋值 72 | pageResult.Records = employees 73 | } 74 | 75 | return pageResult, err 76 | } 77 | 78 | func (ei *EmployeeImpl) CreateEmployee(ctx context.Context, employeeDTO request.EmployeeDTO) error { 79 | var err error 80 | // 1.新增员工,构建员工基础信息 81 | entity := model.Employee{ 82 | Id: employeeDTO.Id, 83 | IdNumber: employeeDTO.IdNumber, 84 | Name: employeeDTO.Name, 85 | Phone: employeeDTO.Phone, 86 | Sex: employeeDTO.Sex, 87 | Username: employeeDTO.UserName, 88 | } 89 | // 新增用户为启用状态 90 | entity.Status = enum.ENABLE 91 | // 新增用户初始密码为123456 92 | entity.Password = utils.MD5V("123456", "", 0) 93 | // 新增用户 94 | err = ei.repo.Insert(ctx, entity) 95 | return err 96 | } 97 | 98 | func (ei *EmployeeImpl) EditPassword(ctx context.Context, employeeEdit request.EmployeeEditPassword) error { 99 | // 1.获取员工信息 100 | employee, err := ei.repo.GetById(ctx, employeeEdit.EmpId) 101 | if err != nil { 102 | return err 103 | 104 | } 105 | // 校验用户老密码 106 | if employee == nil { 107 | return e.Error_ACCOUNT_NOT_FOUND 108 | } 109 | oldHashPassword := utils.MD5V(employeeEdit.OldPassword, "", 0) 110 | if employee.Password != oldHashPassword { 111 | return e.Error_PASSWORD_ERROR 112 | } 113 | // 修改员工密码 114 | newHashPassword := utils.MD5V(employeeEdit.NewPassword, "", 0) // 使用新密码生成哈希值 115 | err = ei.repo.Update(ctx, model.Employee{ 116 | Id: employeeEdit.EmpId, 117 | Password: newHashPassword, 118 | }) 119 | return err 120 | } 121 | 122 | func (ei *EmployeeImpl) Logout(ctx context.Context) error { 123 | // TODO 后续扩展为单点登录模式。 1.获取上下文中当前用户 124 | // 2.如果是单点登录的话执行推出操作 125 | return nil 126 | } 127 | 128 | func (ei *EmployeeImpl) Login(ctx context.Context, employeeLogin request.EmployeeLogin) (*response.EmployeeLogin, error) { 129 | // 1.查询用户是否存在 130 | employee, err := ei.repo.GetByUserName(ctx, employeeLogin.UserName) 131 | if err != nil || employee == nil { 132 | return nil, e.Error_ACCOUNT_NOT_FOUND 133 | } 134 | // 2.校验密码 135 | password := utils.MD5V(employeeLogin.Password, "", 0) 136 | if password != employee.Password { 137 | return nil, e.Error_PASSWORD_ERROR 138 | } 139 | // 3.校验状态 140 | if employee.Status == enum.DISABLE { 141 | return nil, e.Error_ACCOUNT_LOCKED 142 | } 143 | // 生成Token 144 | jwtConfig := global.Config.Jwt.Admin 145 | token, err := utils.GenerateToken(employee.Id, jwtConfig.Name, jwtConfig.Secret) 146 | if err != nil { 147 | return nil, err 148 | } 149 | // 4.构造返回数据 150 | resp := response.EmployeeLogin{ 151 | Id: employee.Id, 152 | Name: employee.Name, 153 | Token: token, 154 | UserName: employee.Username, 155 | } 156 | return &resp, nil 157 | } 158 | 159 | func NewEmployeeService(repo *dao.EmployeeDao) IEmployeeService { 160 | return &EmployeeImpl{repo: repo} 161 | } 162 | -------------------------------------------------------------------------------- /internal/service/setmeal_service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "gorm.io/gorm" 6 | "strconv" 7 | "take-out/common" 8 | "take-out/common/enum" 9 | "take-out/global" 10 | "take-out/internal/api/request" 11 | "take-out/internal/api/response" 12 | "take-out/internal/model" 13 | "take-out/internal/repository/dao" 14 | ) 15 | 16 | type ISetMealService interface { 17 | SaveWithDish(ctx context.Context, dto request.SetMealDTO) error 18 | PageQuery(ctx context.Context, dto request.SetMealPageQueryDTO) (*common.PageResult, error) 19 | OnOrClose(ctx context.Context, id uint64, status int) error 20 | GetByIdWithDish(ctx context.Context, dishId uint64) (response.SetMealWithDishByIdVo, error) 21 | } 22 | 23 | type SetMealServiceImpl struct { 24 | repo *dao.SetMealDao 25 | setMealDishRepo *dao.SetMealDishDao 26 | } 27 | 28 | func (s *SetMealServiceImpl) GetByIdWithDish(ctx context.Context, setMealId uint64) (response.SetMealWithDishByIdVo, error) { 29 | var setMealVo response.SetMealWithDishByIdVo 30 | // 获取事务 31 | err := global.DB.Transaction(func(tx *gorm.DB) error { 32 | // 单独查询套餐 33 | setMeal, err := s.repo.WithTx(tx).GetByIdWithDish(ctx, setMealId) 34 | if err != nil { 35 | return err 36 | } 37 | // 查询中间表记录的套餐菜品信息 38 | dishList, err := s.setMealDishRepo.WithTx(tx).GetBySetMealId(ctx, setMealId) 39 | if err != nil { 40 | return err 41 | } 42 | // 数据组装 43 | setMealVo = response.SetMealWithDishByIdVo{ 44 | Id: setMeal.Id, 45 | CategoryId: setMeal.CategoryId, 46 | CategoryName: setMeal.Name, 47 | Description: setMeal.Description, 48 | Image: setMeal.Image, 49 | Name: setMeal.Name, 50 | Price: setMeal.Price, 51 | Status: setMeal.Status, 52 | SetmealDishes: dishList, 53 | UpdateTime: setMeal.UpdateTime, 54 | } 55 | return nil 56 | }) 57 | return setMealVo, err 58 | } 59 | 60 | func (s *SetMealServiceImpl) OnOrClose(ctx context.Context, id uint64, status int) error { 61 | err := s.repo.SetStatus(ctx, id, status) 62 | return err 63 | } 64 | 65 | func (s *SetMealServiceImpl) PageQuery(ctx context.Context, dto request.SetMealPageQueryDTO) (*common.PageResult, error) { 66 | result, err := s.repo.PageQuery(ctx, dto) 67 | if err != nil { 68 | return nil, err 69 | } 70 | return result, nil 71 | } 72 | 73 | // SaveWithDish 保存套餐和菜品信息 74 | func (s *SetMealServiceImpl) SaveWithDish(ctx context.Context, dto request.SetMealDTO) error { 75 | // 转换dto为model开启事务进行保存 76 | price, _ := strconv.ParseFloat(dto.Price, 10) 77 | setmeal := model.SetMeal{ 78 | Id: dto.Id, 79 | CategoryId: dto.CategoryId, 80 | Name: dto.Name, 81 | Price: price, 82 | Status: enum.ENABLE, 83 | Description: dto.Description, 84 | Image: dto.Image, 85 | } 86 | // 开启事务进行存储 87 | err := global.DB.Transaction(func(tx *gorm.DB) error { 88 | // 先插入套餐数据信息,并得到返回的主键id值 89 | err := s.repo.WithTx(tx).Insert(ctx, &setmeal) 90 | if err != nil { 91 | return err 92 | } 93 | // 再对中间表中套餐内的菜品信息附加主键id 94 | for _, setmealDish := range dto.SetMealDishs { 95 | setmealDish.SetmealId = setmeal.Id 96 | } 97 | // 向中间表插入数据 98 | err = s.setMealDishRepo.WithTx(tx).InsertBatch(ctx, dto.SetMealDishs) 99 | if err != nil { 100 | return err 101 | } 102 | return nil 103 | }) 104 | return err 105 | } 106 | 107 | func NewSetMealService(repo *dao.SetMealDao, setMealDishRepo *dao.SetMealDishDao) ISetMealService { 108 | return &SetMealServiceImpl{repo: repo, setMealDishRepo: setMealDishRepo} 109 | } 110 | -------------------------------------------------------------------------------- /logger/log.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "fmt" 5 | "github.com/sirupsen/logrus" 6 | "io" 7 | "os" 8 | ) 9 | 10 | type ILog interface { 11 | Debug(args ...interface{}) 12 | Info(args ...interface{}) 13 | Warn(args ...interface{}) 14 | Error(args ...interface{}) 15 | Fatal(args ...interface{}) 16 | } 17 | 18 | type LLogger struct { 19 | logger *logrus.Logger 20 | } 21 | 22 | type LogEmailHook struct { 23 | } 24 | 25 | // Levels 需要监控的日志等级,只有命中列表中的日志等级才会触发Hook 26 | func (l *LogEmailHook) Levels() []logrus.Level { 27 | return []logrus.Level{ 28 | logrus.PanicLevel, 29 | logrus.FatalLevel, 30 | } 31 | } 32 | 33 | // Fire 触发钩子函数,本实例为触发后发送邮件报警。 34 | func (l *LogEmailHook) Fire(entry *logrus.Entry) error { 35 | // 触发loggerHook函数调用 36 | fmt.Println("触发loggerHook函数调用") 37 | return nil 38 | } 39 | 40 | func NewLogger(level string, filePath string) ILog { 41 | parseLevel, err := logrus.ParseLevel(level) 42 | if err != nil { 43 | panic(err.Error()) 44 | } 45 | f, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) 46 | if err != nil { 47 | fmt.Println("Failed to create logfile " + filePath) 48 | panic(err) 49 | } 50 | log := &logrus.Logger{ 51 | Out: io.MultiWriter(f, os.Stdout), // 文件 + 控制台输出 52 | Level: parseLevel, // Debug日志等级 53 | Hooks: make(map[logrus.Level][]logrus.Hook), // 初始化Hook Map,否则导致Hook添加过程中的空指针引用。 54 | Formatter: &logrus.TextFormatter{ // 文本格式输出 55 | FullTimestamp: true, // 展示日期 56 | TimestampFormat: "2006-01-02 15:04:05", //日期格式 57 | ForceColors: false, // 颜色日志 58 | }, 59 | } 60 | log.AddHook(&LogEmailHook{}) 61 | log.Infof("日志开启成功") 62 | return &LLogger{logger: log} 63 | } 64 | func (l *LLogger) Debug(args ...interface{}) { 65 | l.logger.Debug(args...) 66 | } 67 | 68 | func (l *LLogger) Info(args ...interface{}) { 69 | l.logger.Info(args...) 70 | } 71 | 72 | func (l *LLogger) Warn(args ...interface{}) { 73 | l.logger.Warn(args...) 74 | } 75 | 76 | func (l *LLogger) Error(args ...interface{}) { 77 | l.logger.Error(args...) 78 | } 79 | 80 | func (l *LLogger) Fatal(args ...interface{}) { 81 | l.logger.Fatal(args...) 82 | } 83 | -------------------------------------------------------------------------------- /logger/slog.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "log/slog" 7 | "os" 8 | ) 9 | 10 | type MySLog struct { 11 | slog *slog.Logger 12 | } 13 | 14 | func NewMySlog(setLevel string, filePath string) ILog { 15 | f, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) 16 | if err != nil { 17 | fmt.Println("Failed to create logfile " + filePath) 18 | panic(err) 19 | } 20 | writer := io.MultiWriter(f, os.Stdout) // 文件 + 控制台输出 21 | level := new(slog.LevelVar) // 设置日志等级 22 | switch setLevel { 23 | case "info": 24 | level.Set(slog.LevelInfo) 25 | case "debug": 26 | level.Set(slog.LevelDebug) 27 | case "warning": 28 | level.Set(slog.LevelWarn) 29 | case "error": 30 | level.Set(slog.LevelError) 31 | } 32 | handle := slog.NewJSONHandler(writer, &slog.HandlerOptions{Level: level}) 33 | log := slog.New(handle) 34 | return &MySLog{slog: log} 35 | } 36 | 37 | func (s *MySLog) Debug(args ...interface{}) { 38 | if len(args) == 1 { 39 | s.slog.Debug(args[0].(string)) 40 | } else if len(args) > 1 { 41 | s.slog.Debug(args[0].(string), args[1:]...) 42 | } 43 | } 44 | 45 | func (s *MySLog) Info(args ...interface{}) { 46 | if len(args) == 1 { 47 | s.slog.Info(args[0].(string)) 48 | } else if len(args) > 1 { 49 | s.slog.Info(args[0].(string), args[1:]...) 50 | } 51 | } 52 | 53 | func (s *MySLog) Warn(args ...interface{}) { 54 | if len(args) == 1 { 55 | s.slog.Warn(args[0].(string)) 56 | } else if len(args) > 1 { 57 | s.slog.Warn(args[0].(string), args[1:]...) 58 | } 59 | } 60 | 61 | func (s *MySLog) Error(args ...interface{}) { 62 | if len(args) == 1 { 63 | s.slog.Error(args[0].(string)) 64 | } else if len(args) > 1 { 65 | s.slog.Error(args[0].(string), args[1:]...) 66 | } 67 | } 68 | 69 | func (s *MySLog) Fatal(args ...interface{}) { 70 | if len(args) == 1 { 71 | s.slog.Info(args[0].(string)) 72 | } else if len(args) > 1 { 73 | s.slog.Info(args[0].(string), args[1:]...) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "take-out/global" 6 | "take-out/initialize" 7 | ) 8 | 9 | func main() { 10 | // 初始化配置 11 | router := initialize.GlobalInit() 12 | 13 | // 设置运行环境 14 | gin.SetMode(global.Config.Server.Level) 15 | 16 | router.Run(":8080") 17 | } 18 | -------------------------------------------------------------------------------- /middle/jwt_middle.go: -------------------------------------------------------------------------------- 1 | package middle 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | "take-out/common" 7 | "take-out/common/e" 8 | "take-out/common/enum" 9 | "take-out/common/utils" 10 | "take-out/global" 11 | ) 12 | 13 | func VerifyJWTAdmin() gin.HandlerFunc { 14 | return func(c *gin.Context) { 15 | code := e.SUCCESS 16 | token := c.Request.Header.Get(global.Config.Jwt.Admin.Name) 17 | // 解析获取用户载荷信息 18 | payLoad, err := utils.ParseToken(token, global.Config.Jwt.Admin.Secret) 19 | if err != nil { 20 | code = e.UNKNOW_IDENTITY 21 | c.JSON(http.StatusUnauthorized, common.Result{Code: code}) 22 | c.Abort() 23 | return 24 | } 25 | // 在上下文设置载荷信息 26 | c.Set(enum.CurrentId, payLoad.UserId) 27 | c.Set(enum.CurrentName, payLoad.GrantScope) 28 | // 这里是否要通知客户端重新保存新的Token 29 | c.Next() 30 | } 31 | } 32 | 33 | func VerifyJWTUser() gin.HandlerFunc { 34 | return func(c *gin.Context) { 35 | code := e.SUCCESS 36 | token := c.Request.Header.Get(global.Config.Jwt.User.Name) 37 | // 解析获取用户载荷信息 38 | payLoad, err := utils.ParseToken(token, global.Config.Jwt.User.Secret) 39 | if err != nil { 40 | code = e.UNKNOW_IDENTITY 41 | c.JSON(http.StatusUnauthorized, common.Result{Code: code}) 42 | c.Abort() 43 | return 44 | } 45 | // 在上下文设置载荷信息 46 | c.Set(enum.CurrentId, payLoad.UserId) 47 | c.Set(enum.CurrentName, payLoad.GrantScope) 48 | // 这里是否要通知客户端重新保存新的Token 49 | c.Next() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /script/sky.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE IF NOT EXISTS `sky_take_out` ; 2 | USE `sky_take_out`; 3 | 4 | DROP TABLE IF EXISTS `address_book`; 5 | CREATE TABLE `address_book` ( 6 | `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', 7 | `user_id` bigint NOT NULL COMMENT '用户id', 8 | `consignee` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '收货人', 9 | `sex` varchar(2) COLLATE utf8_bin DEFAULT NULL COMMENT '性别', 10 | `phone` varchar(11) COLLATE utf8_bin NOT NULL COMMENT '手机号', 11 | `province_code` varchar(12) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '省级区划编号', 12 | `province_name` varchar(32) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '省级名称', 13 | `city_code` varchar(12) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '市级区划编号', 14 | `city_name` varchar(32) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '市级名称', 15 | `district_code` varchar(12) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '区级区划编号', 16 | `district_name` varchar(32) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '区级名称', 17 | `detail` varchar(200) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '详细地址', 18 | `label` varchar(100) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '标签', 19 | `is_default` tinyint(1) NOT NULL DEFAULT '0' COMMENT '默认 0 否 1是', 20 | PRIMARY KEY (`id`) 21 | ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='地址簿'; 22 | 23 | DROP TABLE IF EXISTS `category`; 24 | CREATE TABLE `category` ( 25 | `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', 26 | `type` int DEFAULT NULL COMMENT '类型 1 菜品分类 2 套餐分类', 27 | `name` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '分类名称', 28 | `sort` int NOT NULL DEFAULT '0' COMMENT '顺序', 29 | `status` int DEFAULT NULL COMMENT '分类状态 0:禁用,1:启用', 30 | `create_time` datetime DEFAULT NULL COMMENT '创建时间', 31 | `update_time` datetime DEFAULT NULL COMMENT '更新时间', 32 | `create_user` bigint DEFAULT NULL COMMENT '创建人', 33 | `update_user` bigint DEFAULT NULL COMMENT '修改人', 34 | PRIMARY KEY (`id`), 35 | UNIQUE KEY `idx_category_name` (`name`) 36 | ) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='菜品及套餐分类'; 37 | 38 | INSERT INTO `category` VALUES (11,1,'酒水饮料',10,1,'2022-06-09 22:09:18','2022-06-09 22:09:18',1,1); 39 | INSERT INTO `category` VALUES (12,1,'传统主食',9,1,'2022-06-09 22:09:32','2022-06-09 22:18:53',1,1); 40 | INSERT INTO `category` VALUES (13,2,'人气套餐',12,1,'2022-06-09 22:11:38','2022-06-10 11:04:40',1,1); 41 | INSERT INTO `category` VALUES (15,2,'商务套餐',13,1,'2022-06-09 22:14:10','2022-06-10 11:04:48',1,1); 42 | INSERT INTO `category` VALUES (16,1,'蜀味烤鱼',4,1,'2022-06-09 22:15:37','2022-08-31 14:27:25',1,1); 43 | INSERT INTO `category` VALUES (17,1,'蜀味牛蛙',5,1,'2022-06-09 22:16:14','2022-08-31 14:39:44',1,1); 44 | INSERT INTO `category` VALUES (18,1,'特色蒸菜',6,1,'2022-06-09 22:17:42','2022-06-09 22:17:42',1,1); 45 | INSERT INTO `category` VALUES (19,1,'新鲜时蔬',7,1,'2022-06-09 22:18:12','2022-06-09 22:18:28',1,1); 46 | INSERT INTO `category` VALUES (20,1,'水煮鱼',8,1,'2022-06-09 22:22:29','2022-06-09 22:23:45',1,1); 47 | INSERT INTO `category` VALUES (21,1,'汤类',11,1,'2022-06-10 10:51:47','2022-06-10 10:51:47',1,1); 48 | 49 | DROP TABLE IF EXISTS `dish`; 50 | CREATE TABLE `dish` ( 51 | `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', 52 | `name` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '菜品名称', 53 | `category_id` bigint NOT NULL COMMENT '菜品分类id', 54 | `price` decimal(10,2) DEFAULT NULL COMMENT '菜品价格', 55 | `image` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '图片', 56 | `description` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '描述信息', 57 | `status` int DEFAULT '1' COMMENT '0 停售 1 起售', 58 | `create_time` datetime DEFAULT NULL COMMENT '创建时间', 59 | `update_time` datetime DEFAULT NULL COMMENT '更新时间', 60 | `create_user` bigint DEFAULT NULL COMMENT '创建人', 61 | `update_user` bigint DEFAULT NULL COMMENT '修改人', 62 | PRIMARY KEY (`id`), 63 | UNIQUE KEY `idx_dish_name` (`name`) 64 | ) ENGINE=InnoDB AUTO_INCREMENT=70 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='菜品'; 65 | 66 | INSERT INTO `dish` VALUES (46,'王老吉',11,6.00,'https://sky-itcast.oss-cn-beijing.aliyuncs.com/41bfcacf-7ad4-4927-8b26-df366553a94c.png','',1,'2022-06-09 22:40:47','2022-06-09 22:40:47',1,1); 67 | INSERT INTO `dish` VALUES (47,'北冰洋',11,4.00,'https://sky-itcast.oss-cn-beijing.aliyuncs.com/4451d4be-89a2-4939-9c69-3a87151cb979.png','还是小时候的味道',1,'2022-06-10 09:18:49','2022-06-10 09:18:49',1,1); 68 | INSERT INTO `dish` VALUES (48,'雪花啤酒',11,4.00,'https://sky-itcast.oss-cn-beijing.aliyuncs.com/bf8cbfc1-04d2-40e8-9826-061ee41ab87c.png','',1,'2022-06-10 09:22:54','2022-06-10 09:22:54',1,1); 69 | INSERT INTO `dish` VALUES (49,'米饭',12,2.00,'https://sky-itcast.oss-cn-beijing.aliyuncs.com/76752350-2121-44d2-b477-10791c23a8ec.png','精选五常大米',1,'2022-06-10 09:30:17','2022-06-10 09:30:17',1,1); 70 | INSERT INTO `dish` VALUES (50,'馒头',12,1.00,'https://sky-itcast.oss-cn-beijing.aliyuncs.com/475cc599-8661-4899-8f9e-121dd8ef7d02.png','优质面粉',1,'2022-06-10 09:34:28','2022-06-10 09:34:28',1,1); 71 | INSERT INTO `dish` VALUES (51,'老坛酸菜鱼',20,56.00,'https://sky-itcast.oss-cn-beijing.aliyuncs.com/4a9cefba-6a74-467e-9fde-6e687ea725d7.png','原料:汤,草鱼,酸菜',1,'2022-06-10 09:40:51','2022-06-10 09:40:51',1,1); 72 | INSERT INTO `dish` VALUES (52,'经典酸菜鮰鱼',20,66.00,'https://sky-itcast.oss-cn-beijing.aliyuncs.com/5260ff39-986c-4a97-8850-2ec8c7583efc.png','原料:酸菜,江团,鮰鱼',1,'2022-06-10 09:46:02','2022-06-10 09:46:02',1,1); 73 | INSERT INTO `dish` VALUES (53,'蜀味水煮草鱼',20,38.00,'https://sky-itcast.oss-cn-beijing.aliyuncs.com/a6953d5a-4c18-4b30-9319-4926ee77261f.png','原料:草鱼,汤',1,'2022-06-10 09:48:37','2022-06-10 09:48:37',1,1); 74 | INSERT INTO `dish` VALUES (54,'清炒小油菜',19,18.00,'https://sky-itcast.oss-cn-beijing.aliyuncs.com/3613d38e-5614-41c2-90ed-ff175bf50716.png','原料:小油菜',1,'2022-06-10 09:51:46','2022-06-10 09:51:46',1,1); 75 | INSERT INTO `dish` VALUES (55,'蒜蓉娃娃菜',19,18.00,'https://sky-itcast.oss-cn-beijing.aliyuncs.com/4879ed66-3860-4b28-ba14-306ac025fdec.png','原料:蒜,娃娃菜',1,'2022-06-10 09:53:37','2022-06-10 09:53:37',1,1); 76 | INSERT INTO `dish` VALUES (56,'清炒西兰花',19,18.00,'https://sky-itcast.oss-cn-beijing.aliyuncs.com/e9ec4ba4-4b22-4fc8-9be0-4946e6aeb937.png','原料:西兰花',1,'2022-06-10 09:55:44','2022-06-10 09:55:44',1,1); 77 | INSERT INTO `dish` VALUES (57,'炝炒圆白菜',19,18.00,'https://sky-itcast.oss-cn-beijing.aliyuncs.com/22f59feb-0d44-430e-a6cd-6a49f27453ca.png','原料:圆白菜',1,'2022-06-10 09:58:35','2022-06-10 09:58:35',1,1); 78 | INSERT INTO `dish` VALUES (58,'清蒸鲈鱼',18,98.00,'https://sky-itcast.oss-cn-beijing.aliyuncs.com/c18b5c67-3b71-466c-a75a-e63c6449f21c.png','原料:鲈鱼',1,'2022-06-10 10:12:28','2022-06-10 10:12:28',1,1); 79 | INSERT INTO `dish` VALUES (59,'东坡肘子',18,138.00,'https://sky-itcast.oss-cn-beijing.aliyuncs.com/a80a4b8c-c93e-4f43-ac8a-856b0d5cc451.png','原料:猪肘棒',1,'2022-06-10 10:24:03','2022-06-10 10:24:03',1,1); 80 | INSERT INTO `dish` VALUES (60,'梅菜扣肉',18,58.00,'https://sky-itcast.oss-cn-beijing.aliyuncs.com/6080b118-e30a-4577-aab4-45042e3f88be.png','原料:猪肉,梅菜',1,'2022-06-10 10:26:03','2022-06-10 10:26:03',1,1); 81 | INSERT INTO `dish` VALUES (61,'剁椒鱼头',18,66.00,'https://sky-itcast.oss-cn-beijing.aliyuncs.com/13da832f-ef2c-484d-8370-5934a1045a06.png','原料:鲢鱼,剁椒',1,'2022-06-10 10:28:54','2022-06-10 10:28:54',1,1); 82 | INSERT INTO `dish` VALUES (62,'金汤酸菜牛蛙',17,88.00,'https://sky-itcast.oss-cn-beijing.aliyuncs.com/7694a5d8-7938-4e9d-8b9e-2075983a2e38.png','原料:鲜活牛蛙,酸菜',1,'2022-06-10 10:33:05','2022-06-10 10:33:05',1,1); 83 | INSERT INTO `dish` VALUES (63,'香锅牛蛙',17,88.00,'https://sky-itcast.oss-cn-beijing.aliyuncs.com/f5ac8455-4793-450c-97ba-173795c34626.png','配料:鲜活牛蛙,莲藕,青笋',1,'2022-06-10 10:35:40','2022-06-10 10:35:40',1,1); 84 | INSERT INTO `dish` VALUES (64,'馋嘴牛蛙',17,88.00,'https://sky-itcast.oss-cn-beijing.aliyuncs.com/7a55b845-1f2b-41fa-9486-76d187ee9ee1.png','配料:鲜活牛蛙,丝瓜,黄豆芽',1,'2022-06-10 10:37:52','2022-06-10 10:37:52',1,1); 85 | INSERT INTO `dish` VALUES (65,'草鱼2斤',16,68.00,'https://sky-itcast.oss-cn-beijing.aliyuncs.com/b544d3ba-a1ae-4d20-a860-81cb5dec9e03.png','原料:草鱼,黄豆芽,莲藕',1,'2022-06-10 10:41:08','2022-06-10 10:41:08',1,1); 86 | INSERT INTO `dish` VALUES (66,'江团鱼2斤',16,119.00,'https://sky-itcast.oss-cn-beijing.aliyuncs.com/a101a1e9-8f8b-47b2-afa4-1abd47ea0a87.png','配料:江团鱼,黄豆芽,莲藕',1,'2022-06-10 10:42:42','2022-06-10 10:42:42',1,1); 87 | INSERT INTO `dish` VALUES (67,'鮰鱼2斤',16,72.00,'https://sky-itcast.oss-cn-beijing.aliyuncs.com/8cfcc576-4b66-4a09-ac68-ad5b273c2590.png','原料:鮰鱼,黄豆芽,莲藕',1,'2022-06-10 10:43:56','2022-06-10 10:43:56',1,1); 88 | INSERT INTO `dish` VALUES (68,'鸡蛋汤',21,4.00,'https://sky-itcast.oss-cn-beijing.aliyuncs.com/c09a0ee8-9d19-428d-81b9-746221824113.png','配料:鸡蛋,紫菜',1,'2022-06-10 10:54:25','2022-06-10 10:54:25',1,1); 89 | INSERT INTO `dish` VALUES (69,'平菇豆腐汤',21,6.00,'https://sky-itcast.oss-cn-beijing.aliyuncs.com/16d0a3d6-2253-4cfc-9b49-bf7bd9eb2ad2.png','配料:豆腐,平菇',1,'2022-06-10 10:55:02','2022-06-10 10:55:02',1,1); 90 | 91 | DROP TABLE IF EXISTS `dish_flavor`; 92 | CREATE TABLE `dish_flavor` ( 93 | `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', 94 | `dish_id` bigint NOT NULL COMMENT '菜品', 95 | `name` varchar(32) COLLATE utf8_bin DEFAULT NULL COMMENT '口味名称', 96 | `value` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '口味数据list', 97 | PRIMARY KEY (`id`) 98 | ) ENGINE=InnoDB AUTO_INCREMENT=104 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='菜品口味关系表'; 99 | 100 | INSERT INTO `dish_flavor` VALUES (40,10,'甜味','[\"无糖\",\"少糖\",\"半糖\",\"多糖\",\"全糖\"]'); 101 | INSERT INTO `dish_flavor` VALUES (41,7,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'); 102 | INSERT INTO `dish_flavor` VALUES (42,7,'温度','[\"热饮\",\"常温\",\"去冰\",\"少冰\",\"多冰\"]'); 103 | INSERT INTO `dish_flavor` VALUES (45,6,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'); 104 | INSERT INTO `dish_flavor` VALUES (46,6,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]'); 105 | INSERT INTO `dish_flavor` VALUES (47,5,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]'); 106 | INSERT INTO `dish_flavor` VALUES (48,5,'甜味','[\"无糖\",\"少糖\",\"半糖\",\"多糖\",\"全糖\"]'); 107 | INSERT INTO `dish_flavor` VALUES (49,2,'甜味','[\"无糖\",\"少糖\",\"半糖\",\"多糖\",\"全糖\"]'); 108 | INSERT INTO `dish_flavor` VALUES (50,4,'甜味','[\"无糖\",\"少糖\",\"半糖\",\"多糖\",\"全糖\"]'); 109 | INSERT INTO `dish_flavor` VALUES (51,3,'甜味','[\"无糖\",\"少糖\",\"半糖\",\"多糖\",\"全糖\"]'); 110 | INSERT INTO `dish_flavor` VALUES (52,3,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'); 111 | INSERT INTO `dish_flavor` VALUES (86,52,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'); 112 | INSERT INTO `dish_flavor` VALUES (87,52,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]'); 113 | INSERT INTO `dish_flavor` VALUES (88,51,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'); 114 | INSERT INTO `dish_flavor` VALUES (89,51,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]'); 115 | INSERT INTO `dish_flavor` VALUES (92,53,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'); 116 | INSERT INTO `dish_flavor` VALUES (93,53,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]'); 117 | INSERT INTO `dish_flavor` VALUES (94,54,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\"]'); 118 | INSERT INTO `dish_flavor` VALUES (95,56,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'); 119 | INSERT INTO `dish_flavor` VALUES (96,57,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'); 120 | INSERT INTO `dish_flavor` VALUES (97,60,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'); 121 | INSERT INTO `dish_flavor` VALUES (101,66,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]'); 122 | INSERT INTO `dish_flavor` VALUES (102,67,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]'); 123 | INSERT INTO `dish_flavor` VALUES (103,65,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]'); 124 | 125 | DROP TABLE IF EXISTS `employee`; 126 | CREATE TABLE `employee` ( 127 | `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', 128 | `name` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '姓名', 129 | `username` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '用户名', 130 | `password` varchar(64) COLLATE utf8_bin NOT NULL COMMENT '密码', 131 | `phone` varchar(11) COLLATE utf8_bin NOT NULL COMMENT '手机号', 132 | `sex` varchar(2) COLLATE utf8_bin NOT NULL COMMENT '性别', 133 | `id_number` varchar(18) COLLATE utf8_bin NOT NULL COMMENT '身份证号', 134 | `status` int NOT NULL DEFAULT '1' COMMENT '状态 0:禁用,1:启用', 135 | `create_time` datetime DEFAULT NULL COMMENT '创建时间', 136 | `update_time` datetime DEFAULT NULL COMMENT '更新时间', 137 | `create_user` bigint DEFAULT NULL COMMENT '创建人', 138 | `update_user` bigint DEFAULT NULL COMMENT '修改人', 139 | PRIMARY KEY (`id`), 140 | UNIQUE KEY `idx_username` (`username`) 141 | ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='员工信息'; 142 | 143 | INSERT INTO `employee` VALUES (1,'管理员','admin',MD5('123456'),'13812312312','1','110101199001010047',1,'2022-02-15 15:51:20','2022-02-17 09:16:20',10,1); 144 | 145 | DROP TABLE IF EXISTS `order_detail`; 146 | CREATE TABLE `order_detail` ( 147 | `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', 148 | `name` varchar(32) COLLATE utf8_bin DEFAULT NULL COMMENT '名字', 149 | `image` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '图片', 150 | `order_id` bigint NOT NULL COMMENT '订单id', 151 | `dish_id` bigint DEFAULT NULL COMMENT '菜品id', 152 | `setmeal_id` bigint DEFAULT NULL COMMENT '套餐id', 153 | `dish_flavor` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '口味', 154 | `number` int NOT NULL DEFAULT '1' COMMENT '数量', 155 | `amount` decimal(10,2) NOT NULL COMMENT '金额', 156 | PRIMARY KEY (`id`) 157 | ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='订单明细表'; 158 | 159 | DROP TABLE IF EXISTS `orders`; 160 | CREATE TABLE `orders` ( 161 | `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', 162 | `number` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '订单号', 163 | `status` int NOT NULL DEFAULT '1' COMMENT '订单状态 1待付款 2待接单 3已接单 4派送中 5已完成 6已取消 7退款', 164 | `user_id` bigint NOT NULL COMMENT '下单用户', 165 | `address_book_id` bigint NOT NULL COMMENT '地址id', 166 | `order_time` datetime NOT NULL COMMENT '下单时间', 167 | `checkout_time` datetime DEFAULT NULL COMMENT '结账时间', 168 | `pay_method` int NOT NULL DEFAULT '1' COMMENT '支付方式 1微信,2支付宝', 169 | `pay_status` tinyint NOT NULL DEFAULT '0' COMMENT '支付状态 0未支付 1已支付 2退款', 170 | `amount` decimal(10,2) NOT NULL COMMENT '实收金额', 171 | `remark` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '备注', 172 | `phone` varchar(11) COLLATE utf8_bin DEFAULT NULL COMMENT '手机号', 173 | `address` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '地址', 174 | `user_name` varchar(32) COLLATE utf8_bin DEFAULT NULL COMMENT '用户名称', 175 | `consignee` varchar(32) COLLATE utf8_bin DEFAULT NULL COMMENT '收货人', 176 | `cancel_reason` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '订单取消原因', 177 | `rejection_reason` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '订单拒绝原因', 178 | `cancel_time` datetime DEFAULT NULL COMMENT '订单取消时间', 179 | `estimated_delivery_time` datetime DEFAULT NULL COMMENT '预计送达时间', 180 | `delivery_status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '配送状态 1立即送出 0选择具体时间', 181 | `delivery_time` datetime DEFAULT NULL COMMENT '送达时间', 182 | `pack_amount` int DEFAULT NULL COMMENT '打包费', 183 | `tableware_number` int DEFAULT NULL COMMENT '餐具数量', 184 | `tableware_status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '餐具数量状态 1按餐量提供 0选择具体数量', 185 | PRIMARY KEY (`id`) 186 | ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='订单表'; 187 | 188 | DROP TABLE IF EXISTS `setmeal`; 189 | CREATE TABLE `setmeal` ( 190 | `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', 191 | `category_id` bigint NOT NULL COMMENT '菜品分类id', 192 | `name` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '套餐名称', 193 | `price` decimal(10,2) NOT NULL COMMENT '套餐价格', 194 | `status` int DEFAULT '1' COMMENT '售卖状态 0:停售 1:起售', 195 | `description` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '描述信息', 196 | `image` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '图片', 197 | `create_time` datetime DEFAULT NULL COMMENT '创建时间', 198 | `update_time` datetime DEFAULT NULL COMMENT '更新时间', 199 | `create_user` bigint DEFAULT NULL COMMENT '创建人', 200 | `update_user` bigint DEFAULT NULL COMMENT '修改人', 201 | PRIMARY KEY (`id`), 202 | UNIQUE KEY `idx_setmeal_name` (`name`) 203 | ) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='套餐'; 204 | 205 | DROP TABLE IF EXISTS `setmeal_dish`; 206 | CREATE TABLE `setmeal_dish` ( 207 | `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', 208 | `setmeal_id` bigint DEFAULT NULL COMMENT '套餐id', 209 | `dish_id` bigint DEFAULT NULL COMMENT '菜品id', 210 | `name` varchar(32) COLLATE utf8_bin DEFAULT NULL COMMENT '菜品名称 (冗余字段)', 211 | `price` decimal(10,2) DEFAULT NULL COMMENT '菜品单价(冗余字段)', 212 | `copies` int DEFAULT NULL COMMENT '菜品份数', 213 | PRIMARY KEY (`id`) 214 | ) ENGINE=InnoDB AUTO_INCREMENT=47 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='套餐菜品关系'; 215 | 216 | DROP TABLE IF EXISTS `shopping_cart`; 217 | CREATE TABLE `shopping_cart` ( 218 | `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', 219 | `name` varchar(32) COLLATE utf8_bin DEFAULT NULL COMMENT '商品名称', 220 | `image` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '图片', 221 | `user_id` bigint NOT NULL COMMENT '主键', 222 | `dish_id` bigint DEFAULT NULL COMMENT '菜品id', 223 | `setmeal_id` bigint DEFAULT NULL COMMENT '套餐id', 224 | `dish_flavor` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '口味', 225 | `number` int NOT NULL DEFAULT '1' COMMENT '数量', 226 | `amount` decimal(10,2) NOT NULL COMMENT '金额', 227 | `create_time` datetime DEFAULT NULL COMMENT '创建时间', 228 | PRIMARY KEY (`id`) 229 | ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='购物车'; 230 | 231 | DROP TABLE IF EXISTS `user`; 232 | CREATE TABLE `user` ( 233 | `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', 234 | `openid` varchar(45) COLLATE utf8_bin DEFAULT NULL COMMENT '微信用户唯一标识', 235 | `name` varchar(32) COLLATE utf8_bin DEFAULT NULL COMMENT '姓名', 236 | `phone` varchar(11) COLLATE utf8_bin DEFAULT NULL COMMENT '手机号', 237 | `sex` varchar(2) COLLATE utf8_bin DEFAULT NULL COMMENT '性别', 238 | `id_number` varchar(18) COLLATE utf8_bin DEFAULT NULL COMMENT '身份证号', 239 | `avatar` varchar(500) COLLATE utf8_bin DEFAULT NULL COMMENT '头像', 240 | `create_time` datetime DEFAULT NULL, 241 | PRIMARY KEY (`id`) 242 | ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='用户信息'; -------------------------------------------------------------------------------- /script/数据库设计文档.md: -------------------------------------------------------------------------------- 1 | ## 数据库设计文档 2 | 3 | | 序号 | 数据表名 | 中文名称 | 4 | | ---- | ------------- | -------------- | 5 | | 1 | employee | 员工表 | 6 | | 2 | category | 分类表 | 7 | | 3 | dish | 菜品表 | 8 | | 4 | dish_flavor | 菜品口味表 | 9 | | 5 | setmeal | 套餐表 | 10 | | 6 | setmeal_dish | 套餐菜品关系表 | 11 | | 7 | user | 用户表 | 12 | | 8 | address_book | 地址表 | 13 | | 9 | shopping_cart | 购物车表 | 14 | | 10 | orders | 订单表 | 15 | | 11 | order_detail | 订单明细表 | 16 | 17 | ### 1. employee 18 | 19 | employee表为员工表,用于存储商家内部的员工信息。具体表结构如下: 20 | 21 | | 字段名 | 数据类型 | 说明 | 备注 | 22 | | ----------- | ----------- | ------------ | ----------- | 23 | | id | bigint | 主键 | 自增 | 24 | | name | varchar(32) | 姓名 | | 25 | | username | varchar(32) | 用户名 | 唯一 | 26 | | password | varchar(64) | 密码 | | 27 | | phone | varchar(11) | 手机号 | | 28 | | sex | varchar(2) | 性别 | | 29 | | id_number | varchar(18) | 身份证号 | | 30 | | status | int | 账号状态 | 1正常 0锁定 | 31 | | create_time | datetime | 创建时间 | | 32 | | update_time | datetime | 最后修改时间 | | 33 | | create_user | bigint | 创建人id | | 34 | | update_user | bigint | 最后修改人id | | 35 | 36 | ### 2. category 37 | 38 | category表为分类表,用于存储商品的分类信息。具体表结构如下: 39 | 40 | | 字段名 | 数据类型 | 说明 | 备注 | 41 | | ----------- | ----------- | ------------ | -------------------- | 42 | | id | bigint | 主键 | 自增 | 43 | | name | varchar(32) | 分类名称 | 唯一 | 44 | | type | int | 分类类型 | 1菜品分类 2套餐分类 | 45 | | sort | int | 排序字段 | 用于分类数据的排序 | 46 | | status | int | 状态 | 1启用 0禁用 | 47 | | create_time | datetime | 创建时间 | | 48 | | update_time | datetime | 最后修改时间 | | 49 | | create_user | bigint | 创建人id | | 50 | | update_user | bigint | 最后修改人id | | 51 | 52 | ### 3. dish 53 | 54 | dish表为菜品表,用于存储菜品的信息。具体表结构如下: 55 | 56 | | 字段名 | 数据类型 | 说明 | 备注 | 57 | | ----------- | ------------- | ------------ | ----------- | 58 | | id | bigint | 主键 | 自增 | 59 | | name | varchar(32) | 菜品名称 | 唯一 | 60 | | category_id | bigint | 分类id | 逻辑外键 | 61 | | price | decimal(10,2) | 菜品价格 | | 62 | | image | varchar(255) | 图片路径 | | 63 | | description | varchar(255) | 菜品描述 | | 64 | | status | int | 售卖状态 | 1起售 0停售 | 65 | | create_time | datetime | 创建时间 | | 66 | | update_time | datetime | 最后修改时间 | | 67 | | create_user | bigint | 创建人id | | 68 | | update_user | bigint | 最后修改人id | | 69 | 70 | ### 4. dish_flavor 71 | 72 | dish_flavor表为菜品口味表,用于存储菜品的口味信息。具体表结构如下: 73 | 74 | | 字段名 | 数据类型 | 说明 | 备注 | 75 | | ------- | ------------ | -------- | -------- | 76 | | id | bigint | 主键 | 自增 | 77 | | dish_id | bigint | 菜品id | 逻辑外键 | 78 | | name | varchar(32) | 口味名称 | | 79 | | value | varchar(255) | 口味值 | | 80 | 81 | ### 5. setmeal 82 | 83 | setmeal表为套餐表,用于存储套餐的信息。具体表结构如下: 84 | 85 | | 字段名 | 数据类型 | 说明 | 备注 | 86 | | ----------- | ------------- | ------------ | ----------- | 87 | | id | bigint | 主键 | 自增 | 88 | | name | varchar(32) | 套餐名称 | 唯一 | 89 | | category_id | bigint | 分类id | 逻辑外键 | 90 | | price | decimal(10,2) | 套餐价格 | | 91 | | image | varchar(255) | 图片路径 | | 92 | | description | varchar(255) | 套餐描述 | | 93 | | status | int | 售卖状态 | 1起售 0停售 | 94 | | create_time | datetime | 创建时间 | | 95 | | update_time | datetime | 最后修改时间 | | 96 | | create_user | bigint | 创建人id | | 97 | | update_user | bigint | 最后修改人id | | 98 | 99 | ### 6. setmeal_dish 100 | 101 | setmeal_dish表为套餐菜品关系表,用于存储套餐和菜品的关联关系。具体表结构如下: 102 | 103 | | 字段名 | 数据类型 | 说明 | 备注 | 104 | | ---------- | ------------- | -------- | -------- | 105 | | id | bigint | 主键 | 自增 | 106 | | setmeal_id | bigint | 套餐id | 逻辑外键 | 107 | | dish_id | bigint | 菜品id | 逻辑外键 | 108 | | name | varchar(32) | 菜品名称 | 冗余字段 | 109 | | price | decimal(10,2) | 菜品单价 | 冗余字段 | 110 | | copies | int | 菜品份数 | | 111 | 112 | ### 7. user 113 | 114 | user表为用户表,用于存储C端用户的信息。具体表结构如下: 115 | 116 | | 字段名 | 数据类型 | 说明 | 备注 | 117 | | ----------- | ------------ | ------------------ | ---- | 118 | | id | bigint | 主键 | 自增 | 119 | | openid | varchar(45) | 微信用户的唯一标识 | | 120 | | name | varchar(32) | 用户姓名 | | 121 | | phone | varchar(11) | 手机号 | | 122 | | sex | varchar(2) | 性别 | | 123 | | id_number | varchar(18) | 身份证号 | | 124 | | avatar | varchar(500) | 微信用户头像路径 | | 125 | | create_time | datetime | 注册时间 | | 126 | 127 | ### 8. address_book 128 | 129 | address_book表为地址表,用于存储C端用户的收货地址信息。具体表结构如下: 130 | 131 | | 字段名 | 数据类型 | 说明 | 备注 | 132 | | ------------- | ------------ | ------------ | -------------- | 133 | | id | bigint | 主键 | 自增 | 134 | | user_id | bigint | 用户id | 逻辑外键 | 135 | | consignee | varchar(50) | 收货人 | | 136 | | sex | varchar(2) | 性别 | | 137 | | phone | varchar(11) | 手机号 | | 138 | | province_code | varchar(12) | 省份编码 | | 139 | | province_name | varchar(32) | 省份名称 | | 140 | | city_code | varchar(12) | 城市编码 | | 141 | | city_name | varchar(32) | 城市名称 | | 142 | | district_code | varchar(12) | 区县编码 | | 143 | | district_name | varchar(32) | 区县名称 | | 144 | | detail | varchar(200) | 详细地址信息 | 具体到门牌号 | 145 | | label | varchar(100) | 标签 | 公司、家、学校 | 146 | | is_default | tinyint(1) | 是否默认地址 | 1是 0否 | 147 | 148 | ### 9. shopping_cart 149 | 150 | shopping_cart表为购物车表,用于存储C端用户的购物车信息。具体表结构如下: 151 | 152 | | 字段名 | 数据类型 | 说明 | 备注 | 153 | | ----------- | ------------- | ------------ | -------- | 154 | | id | bigint | 主键 | 自增 | 155 | | name | varchar(32) | 商品名称 | | 156 | | image | varchar(255) | 商品图片路径 | | 157 | | user_id | bigint | 用户id | 逻辑外键 | 158 | | dish_id | bigint | 菜品id | 逻辑外键 | 159 | | setmeal_id | bigint | 套餐id | 逻辑外键 | 160 | | dish_flavor | varchar(50) | 菜品口味 | | 161 | | number | int | 商品数量 | | 162 | | amount | decimal(10,2) | 商品单价 | | 163 | | create_time | datetime | 创建时间 | | 164 | 165 | ### 10. orders 166 | 167 | orders表为订单表,用于存储C端用户的订单数据。具体表结构如下: 168 | 169 | | 字段名 | 数据类型 | 说明 | 备注 | 170 | | ----------------------- | ------------- | ------------ | ----------------------------------------------- | 171 | | id | bigint | 主键 | 自增 | 172 | | number | varchar(50) | 订单号 | | 173 | | status | int | 订单状态 | 1待付款 2待接单 3已接单 4派送中 5已完成 6已取消 | 174 | | user_id | bigint | 用户id | 逻辑外键 | 175 | | address_book_id | bigint | 地址id | 逻辑外键 | 176 | | order_time | datetime | 下单时间 | | 177 | | checkout_time | datetime | 付款时间 | | 178 | | pay_method | int | 支付方式 | 1微信支付 2支付宝支付 | 179 | | pay_status | tinyint | 支付状态 | 0未支付 1已支付 2退款 | 180 | | amount | decimal(10,2) | 订单金额 | | 181 | | remark | varchar(100) | 备注信息 | | 182 | | phone | varchar(11) | 手机号 | | 183 | | address | varchar(255) | 详细地址信息 | | 184 | | user_name | varchar(32) | 用户姓名 | | 185 | | consignee | varchar(32) | 收货人 | | 186 | | cancel_reason | varchar(255) | 订单取消原因 | | 187 | | rejection_reason | varchar(255) | 拒单原因 | | 188 | | cancel_time | datetime | 订单取消时间 | | 189 | | estimated_delivery_time | datetime | 预计送达时间 | | 190 | | delivery_status | tinyint | 配送状态 | 1立即送出 0选择具体时间 | 191 | | delivery_time | datetime | 送达时间 | | 192 | | pack_amount | int | 打包费 | | 193 | | tableware_number | int | 餐具数量 | | 194 | | tableware_status | tinyint | 餐具数量状态 | 1按餐量提供 0选择具体数量 | 195 | 196 | ### 11. order_detail 197 | 198 | order_detail表为订单明细表,用于存储C端用户的订单明细数据。具体表结构如下: 199 | 200 | | 字段名 | 数据类型 | 说明 | 备注 | 201 | | ----------- | ------------- | ------------ | -------- | 202 | | id | bigint | 主键 | 自增 | 203 | | name | varchar(32) | 商品名称 | | 204 | | image | varchar(255) | 商品图片路径 | | 205 | | order_id | bigint | 订单id | 逻辑外键 | 206 | | dish_id | bigint | 菜品id | 逻辑外键 | 207 | | setmeal_id | bigint | 套餐id | 逻辑外键 | 208 | | dish_flavor | varchar(50) | 菜品口味 | | 209 | | number | int | 商品数量 | | 210 | | amount | decimal(10,2) | 商品单价 | | 211 | 212 | --------------------------------------------------------------------------------