├── .gitignore ├── LICENSE ├── Queue.md ├── README.md ├── app ├── AdminUser │ ├── Bapi │ │ └── AdminUser.go │ ├── Model │ │ └── AdminUser.go │ └── Router │ │ └── AdminUser.go ├── Common │ ├── Bapi │ │ └── Common.go │ └── Model │ │ └── Model.go ├── Menu │ ├── Bapi │ │ └── Menu.go │ ├── Model │ │ └── Menu.go │ └── Router │ │ └── Menu.go ├── Notice │ ├── Api │ │ └── Notice.go │ ├── Bapi │ │ └── Notice.go │ ├── Model │ │ └── Notice.go │ └── Router │ │ └── Notice.go ├── Role │ ├── Bapi │ │ └── Role.go │ ├── Model │ │ ├── Role.go │ │ └── RoleMenu.go │ └── Router │ │ └── Role.go ├── System │ └── Bapi │ │ └── System.go ├── Test │ └── Test.go ├── Upload │ ├── Api │ │ ├── ImgUpload.go │ │ └── VideoUpload.go │ └── Router │ │ └── Upload.go ├── User │ ├── Api │ │ └── User.go │ ├── Model │ │ └── User.go │ └── Router │ │ └── User.go └── Ws │ ├── Ws.go │ └── testWs.html ├── asynq.yml ├── bootstrap └── bootstrap.go ├── config.yaml ├── core ├── Tpl │ ├── Api │ │ └── Tpl.go │ ├── Bapi │ │ └── Tpl.go │ ├── Model │ │ └── Tpl.go │ └── Router │ │ └── Tpl.go ├── config │ └── config.go ├── db │ └── db.go ├── global │ └── app.go ├── helper │ └── helper.go ├── log │ └── log.go ├── middle │ ├── RateLimiter.go │ ├── RequestLogger.go │ ├── auth │ │ ├── Claims.go │ │ ├── Func.go │ │ └── JwtAuth.go │ └── casbin │ │ ├── Check.go │ │ └── Enforcer.go ├── queue │ └── queue.go ├── redis │ └── redis.go ├── response │ └── response.go ├── router │ └── router.go ├── snow │ └── snow.go ├── trans │ └── trans.go └── validate │ └── validate.go ├── go.mod ├── go.sum ├── go_study.sql ├── job ├── TestJob.go └── job.go ├── lang ├── en │ └── en.json └── zh-cn │ └── zh-cn.json ├── main.go ├── rbac.conf ├── route └── route.go ├── storage ├── .gitignore └── logs │ └── .gitignore └── vTools ├── tools └── makeCurd.go └── vTools.go /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /.vscode 3 | /storage/logs/* 4 | /*.exe 5 | /*.exe~ 6 | /*.log 7 | /configs/* 8 | vgo -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 闫春浩 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 | -------------------------------------------------------------------------------- /Queue.md: -------------------------------------------------------------------------------- 1 | ## 使用 asynq 命令行工具 2 | ```text 3 | https://github.com/hibiken/asynq/blob/master/tools/asynq/README.md 4 | ``` 5 | ```text 6 | go install github.com/hibiken/asynq/tools/asynq@latest 7 | ``` 8 | ```text 9 | asynq dash --config .\asynq.yml 10 | ``` 11 | 12 | #### 使用 asynq 可视化工具 【框架未内置,使用只需在app内定义方法路由即可】 13 | ```text 14 | https://github.com/hibiken/asynqmon?tab=readme-ov-file#import-as-a-library 15 | ``` 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vgo 2 | 3 | ### 📖对go较熟悉的小朋友,可以直接拉取prod分支进行学习。 4 | ### 介绍 📖 5 | 6 | Vgo 是一款基于 Gin 开发的开源服务端框架。 7 | - 使用了Redis、Mysql、JWT、队列、等技术栈。 8 | - 比较适合Golang初学者作为学习项目学习。 9 | - 当前框架内实现了基本的Rbac权限管理(使用casbin)、队列、websocket、文件日志等功能。 10 | - 后续将陆续增加更多功能,欢迎大家共同参与进来。 11 | - 本项目主要是为了学习 Golang 而开发的,所以代码中难免有不足之处,还请大家多多包涵。 12 | 13 | ### 代码仓库 ⭐(记得 Star⭐) 14 | 15 | - Vgo-Github:https://github.com/xuewuzhiijngych/vgo.git 16 | - Vgo-Gitee:https://gitee.com/yan_chunhao_admin/vgo.git 17 | - 18 | - VgoAdmin-Github:https://github.com/xuewuzhiijngych/vgo-admin.git 19 | - VgoAdmin-Gitee:https://gitee.com/yan_chunhao_admin/vgo-admin.git 20 | 21 | ### 安装使用步骤 📔 22 | 23 | - **下载:** 24 | 25 | ```shell 26 | git clone https://github.com/xuewuzhiijngych/vgo.git 27 | ``` 28 | 29 | - **安装:** 30 | 31 | - 使用Mysql8,导入根目录的go_study.sql文件。 32 | - 准备redis 33 | - 修改根目录下的config.yaml文件,配置Mysql、Redis、JWT等信息。 34 | - 根目录的asynq.yml文件是配置asynq命令工具的,不使用,忽略即可。 35 | - 执行以下命令安装依赖包: 36 | 37 | ```shell 38 | go mod tidy 39 | ``` 40 | 41 | - **运行:** 42 | 43 | ```shell 44 | go run main.go 45 | ``` 46 | 47 | ### 项目目录 📚 48 | 49 | ```shell 50 | Vgo 51 | ├─ app # Vite 配置项 52 | ├─ AdminUser # 模块 53 | │ ├─ Bapi # Bapi 后台接口 54 | │ ├─ Api # Api 前台接口 55 | │ ├─ Model # Model 模型 56 | │ ├─ Router # Router 路由 57 | ├─ bootstrap # 框架启动文件 58 | ├─ core # 框架核心文件 59 | │ ├─ ... # 后续出详细介绍(亦可以自己通过源码了解) 60 | ├─ job # 队列 61 | ├─ lang # 翻译字典 62 | ├─ route # 路由 63 | ├─ storage # 日志或静态资源 64 | │ ├─ logs # 日志 65 | ├─ vTools # 命令行工具 66 | ├─ asynq.yml # asynq配置文件 67 | ├─ config.yaml # 框架配置文件 68 | ``` 69 | 70 | ### 前台使用 🌎 71 | - 基于本框架的接口,实现了一个拥有简单Rbac的后台管理系统,具体使用方法请参考VgoAdmin项目。 72 | - 前端项目地址:https://github.com/xuewuzhiijngych/vgo-admin.git 73 | 74 | ## 功能 75 | ### 代码生成 🏢 76 | - 开发时Go版本:go version go1.22.5 windows/amd64 77 | - 版本兼容未测试,预想问题不大。 78 | - 生成基本增删改查的golang代码,可使用命令: 79 | - Curd: 80 | ```shell 81 | go run vTools/vTools.go --method=Curd --module=Product --note=产品 82 | ``` 83 | - 执行代码后,会在app目录下生成一个Product模块,里面包含了增删改查的相关代码。 84 | - 随后需要自己在根目录route/router.go文件中注册路由。【后期实现自动注册】 85 | 86 | ### 权限管理 🔐 87 | - 基于casbin实现的权限管理,可实现用户角色、角色权限、用户权限等多种权限控制。 88 | - 具体使用方法请参考VgoAdmin项目。 89 | 90 | ### Map结合模型验证 🏭 91 | - 见app/User/Api/User.go 的 Register方法 92 | 93 | ### 自定义翻译器 📝 94 | - 翻译文字定义在根目录lang下 95 | - 使用: 96 | ```shell 97 | trans.Trans("手机号不能为空", "哈哈哈",666), 98 | ``` 99 | - 具体见:app/User/Api/User.go 的 Register方法 100 | 101 | ### 后续计划 🔮 102 | - 后续将陆续增加更多功能,欢迎大家共同参与进来。 103 | - 如有任何问题,请联系作者:<601266867@qq.com> -------------------------------------------------------------------------------- /app/AdminUser/Bapi/AdminUser.go: -------------------------------------------------------------------------------- 1 | package AdminUser 2 | 3 | import ( 4 | "errors" 5 | "github.com/gin-gonic/gin" 6 | "github.com/go-sql-driver/mysql" 7 | "golang.org/x/crypto/bcrypt" 8 | "gorm.io/gorm" 9 | "strconv" 10 | AdminUserModel "vgo/app/AdminUser/Model" 11 | "vgo/core/db" 12 | "vgo/core/helper" 13 | "vgo/core/middle/auth" 14 | "vgo/core/middle/casbin" 15 | "vgo/core/response" 16 | ) 17 | 18 | // Index 列表 19 | func Index(ctx *gin.Context) { 20 | var res []AdminUserModel.AdminUser 21 | var total int64 22 | pageNo, err := strconv.Atoi(ctx.DefaultQuery("pageNum", "1")) 23 | if err != nil { 24 | response.Fail(ctx, "页码参数无效", nil) 25 | return 26 | } 27 | Size, err := strconv.Atoi(ctx.DefaultQuery("pageSize", "10")) 28 | if err != nil { 29 | response.Fail(ctx, "每页大小参数无效", nil) 30 | return 31 | } 32 | if err := db.Con().Model(&AdminUserModel.AdminUser{}).Count(&total).Error; err != nil { 33 | response.Fail(ctx, "数据库查询失败", err.Error()) 34 | return 35 | } 36 | if err := db.Con().Order("id desc").Offset((pageNo-1)*Size).Limit(Size).Where("super = ?", 2).Find(&res).Error; err != nil { 37 | response.Fail(ctx, "数据库查询失败", err.Error()) 38 | return 39 | } 40 | totalPage := int(total) / Size 41 | if int(total)%Size != 0 { 42 | totalPage++ 43 | } 44 | response.Success(ctx, "成功", gin.H{ 45 | "pageNum": pageNo, 46 | "total": total, 47 | "pageSize": Size, 48 | "list": res, 49 | }, nil) 50 | } 51 | 52 | // SetRole 设置角色 53 | func SetRole(ctx *gin.Context) { 54 | var codes struct { 55 | ID uint64 `json:"id"` 56 | Roles []string `json:"roles"` 57 | } 58 | if err := helper.VgoShouldBindJSON(ctx, &codes); err != nil { 59 | response.Fail(ctx, "参数错误", err.Error(), nil) 60 | return 61 | } 62 | userID := codes.ID 63 | enforcer := casbin.SetupCasbin() 64 | _, err := enforcer.DeleteRolesForUser(strconv.FormatUint(userID, 10)) 65 | if err != nil { 66 | response.Fail(ctx, err.Error(), nil) 67 | return 68 | } 69 | for _, role := range codes.Roles { 70 | _, err2 := enforcer.AddRoleForUser(strconv.FormatUint(userID, 10), role) 71 | if err2 != nil { 72 | response.Fail(ctx, err2.Error(), nil) 73 | return 74 | } 75 | } 76 | response.Success(ctx, "设置成功", nil, nil) 77 | } 78 | 79 | // GetRole 获取角色 80 | func GetRole(ctx *gin.Context) { 81 | var codes struct { 82 | ID uint64 `json:"id"` 83 | } 84 | if err := helper.VgoShouldBindJSON(ctx, &codes); err != nil { 85 | response.Fail(ctx, "参数错误", err.Error(), nil) 86 | return 87 | } 88 | userID := codes.ID 89 | enforcer := casbin.SetupCasbin() 90 | roles, err := enforcer.GetRolesForUser(strconv.FormatUint(userID, 10)) 91 | if err != nil { 92 | response.Fail(ctx, "获取错误", err.Error(), nil) 93 | return 94 | } 95 | response.Success(ctx, "获取成功", roles, nil) 96 | } 97 | 98 | // Create 创建用户 99 | func Create(ctx *gin.Context) { 100 | var user AdminUserModel.AdminUser 101 | if err := helper.VgoShouldBindJSON(ctx, &user); err != nil { 102 | response.Fail(ctx, "参数错误", err.Error(), nil) 103 | return 104 | } 105 | // 查询已存在的用户 106 | var existingUser AdminUserModel.AdminUser 107 | if err := db.Con().Where("username = ?", user.UserName).First(&existingUser).Error; err != nil { 108 | if !errors.Is(err, gorm.ErrRecordNotFound) { 109 | response.Fail(ctx, "查询用户失败", err.Error(), nil) 110 | return 111 | } 112 | } else { 113 | response.Fail(ctx, "用户名已存在", nil, nil) 114 | return 115 | } 116 | // 对密码进行哈希处理 117 | hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost) 118 | if err != nil { 119 | response.Fail(ctx, "密码哈希失败", err.Error(), nil) 120 | return 121 | } 122 | user.Password = string(hashedPassword) 123 | // 插入用户数据 124 | if err := db.Con().Create(&user).Error; err != nil { 125 | // 检查是否是唯一键冲突错误 126 | var mysqlErr *mysql.MySQLError 127 | if errors.As(err, &mysqlErr) && mysqlErr.Number == 1062 { 128 | response.Fail(ctx, "用户名已存在", err.Error(), nil) 129 | } 130 | return 131 | } 132 | response.Success(ctx, "成功", user, nil) 133 | } 134 | 135 | // Login 登录 136 | func Login(ctx *gin.Context) { 137 | var user AdminUserModel.AdminUser 138 | if err := helper.VgoShouldBindJSON(ctx, &user); err != nil { 139 | response.Fail(ctx, "参数错误", err.Error(), nil) 140 | return 141 | } 142 | // 查询用户数据 143 | var dbUser AdminUserModel.AdminUser 144 | if err := db.Con().Where("username = ?", user.UserName).First(&dbUser).Error; err != nil { 145 | response.Fail(ctx, "用户名或密码错误001", err.Error(), nil) 146 | return 147 | } 148 | // 验证密码 149 | if err := bcrypt.CompareHashAndPassword([]byte(dbUser.Password), []byte(user.Password)); err != nil { 150 | response.Fail(ctx, "用户名或密码错误002", err.Error(), nil) 151 | return 152 | } 153 | // 获取角色 154 | enforcer := casbin.SetupCasbin() 155 | roles, err := enforcer.GetRolesForUser(strconv.FormatUint(dbUser.ID, 10)) 156 | if err != nil { 157 | response.Fail(ctx, "获取错误", err.Error(), nil) 158 | return 159 | } 160 | // 生成token 161 | res, err := auth.GenAdminToken(ctx, dbUser.ID, roles, dbUser.Super) 162 | if err != nil { 163 | response.Fail(ctx, err.Error(), nil) 164 | return 165 | } 166 | response.Success(ctx, "登录成功", res) 167 | } 168 | 169 | // LogOut 退出登录 170 | func LogOut(ctx *gin.Context) { 171 | userID := ctx.GetUint64("userID") 172 | err := auth.DelAdminToken(ctx, userID) 173 | if err != nil { 174 | return 175 | } 176 | response.Success(ctx, "退出成功", nil) 177 | } 178 | 179 | // Update 更新 180 | func Update(ctx *gin.Context) { 181 | var notice AdminUserModel.AdminUser 182 | if err := helper.VgoShouldBindJSON(ctx, ¬ice); err != nil { 183 | response.Fail(ctx, "参数错误", err.Error(), nil) 184 | return 185 | } 186 | if err := db.Con().Model(&AdminUserModel.AdminUser{}).Where("id = ?", notice.ID).Updates(notice).Error; err != nil { 187 | response.Fail(ctx, "更新失败", err.Error()) 188 | return 189 | } 190 | response.Success(ctx, "成功", nil, nil) 191 | } 192 | 193 | // Delete 删除 194 | func Delete(ctx *gin.Context) { 195 | var ids struct { 196 | ID []uint64 `json:"id"` 197 | } 198 | if err := helper.VgoShouldBindJSON(ctx, &ids); err != nil { 199 | response.Fail(ctx, "参数错误", err.Error(), nil) 200 | return 201 | } 202 | // 删除token 203 | for _, values := range ids.ID { 204 | err := auth.DelAdminToken(ctx, values) 205 | if err != nil { 206 | continue 207 | } 208 | } 209 | if err := db.Con().Delete(&AdminUserModel.AdminUser{}, "id in (?)", ids.ID).Error; err != nil { 210 | response.Fail(ctx, "删除失败", err.Error()) 211 | return 212 | } 213 | response.Success(ctx, "成功", nil, nil) 214 | } 215 | 216 | // UserInfo 用户信息 217 | func UserInfo(ctx *gin.Context) { 218 | userID := ctx.GetUint64("userID") 219 | role := ctx.GetString("Role") 220 | response.Success(ctx, "成功", map[string]interface{}{ 221 | "userID": userID, 222 | "role": role, 223 | "message": "pong", 224 | }, nil) 225 | } 226 | -------------------------------------------------------------------------------- /app/AdminUser/Model/AdminUser.go: -------------------------------------------------------------------------------- 1 | package AdminUser 2 | 3 | import ( 4 | Common "vgo/app/Common/Model" 5 | ) 6 | 7 | // AdminUser 管理员用户 8 | type AdminUser struct { 9 | Common.Model 10 | UserName string `gorm:"column:username;unique;index;default:'';type:varchar(20);not null;comment:用户名" form:"username" json:"username"` 11 | Password string `gorm:"column:password;default:'';not null;comment:密码" form:"password" json:"password"` 12 | UserType string `gorm:"column:user_type;default:'100';not null;comment:用户类型:(100系统用户)" form:"user_type" json:"user_type"` 13 | Nickname string `gorm:"column:nickname;default:'';type:varchar(30);not null;comment:用户昵称" form:"nickname" json:"nickname"` 14 | Phone string `gorm:"column:phone;default:'';type:varchar(11);not null;comment:手机" form:"phone" json:"phone"` 15 | Email string `gorm:"column:email;default:'';type:varchar(50);not null;comment:用户邮箱" form:"email" json:"email"` 16 | Avatar string `gorm:"column:avatar;default:'';type:varchar(255);not null;comment:用户头像" form:"avatar" json:"avatar"` 17 | Signed string `gorm:"column:signed;default:'';type:varchar(255);not null;comment:个人签名" form:"signed" json:"signed"` 18 | Dashboard string `gorm:"column:dashboard;default:'';type:varchar(100);not null;comment:后台首页类型" form:"dashboard" json:"dashboard"` 19 | Status int `gorm:"column:status;default:1;not null;comment:状态 (1正常 2停用)" form:"status" json:"status"` 20 | Super int `gorm:"column:super;default:2;not null;comment:是否超级管理员 (1是 2否)" form:"super" json:"super"` 21 | LoginIP string `gorm:"column:login_ip;default:'';type:varchar(45);not null;comment:最后登陆IP" form:"login_ip" json:"login_ip"` 22 | BackendSetting string `gorm:"column:backend_setting;default:'';type:varchar(500);not null;comment:后台设置数据" form:"backend_setting" json:"backend_setting"` 23 | Remark string `gorm:"column:remark;default:'';type:varchar(255);not null;comment:备注" form:"remark" json:"remark"` 24 | } 25 | -------------------------------------------------------------------------------- /app/AdminUser/Router/AdminUser.go: -------------------------------------------------------------------------------- 1 | package AdminUser 2 | 3 | import ( 4 | AdminUser "vgo/app/AdminUser/Bapi" 5 | "vgo/core/router" 6 | ) 7 | 8 | func CollectRoutes() []router.BaseRoute { 9 | return []router.BaseRoute{ 10 | {"GET", "/admin_user", AdminUser.Index}, 11 | {"POST", "/admin_user/create", AdminUser.Create}, 12 | {"POST", "/admin_user/set/role", AdminUser.SetRole}, 13 | {"POST", "/admin_user/get/role", AdminUser.GetRole}, 14 | {"POST", "/admin_user/delete", AdminUser.Delete}, 15 | {"POST", "/admin_user/logout", AdminUser.LogOut}, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/Common/Bapi/Common.go: -------------------------------------------------------------------------------- 1 | package Common 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "vgo/core/response" 6 | ) 7 | 8 | // GetGender 获取性别选项 9 | func GetGender(ctx *gin.Context) { 10 | seed := []map[string]interface{}{ 11 | { 12 | "label": "男", 13 | "value": 1, 14 | }, 15 | { 16 | "label": "女", 17 | "value": 1, 18 | }, 19 | } 20 | data := make([]map[string]interface{}, len(seed)) 21 | for k, item := range seed { 22 | data[k] = map[string]interface{}{ 23 | "genderLabel": item["label"], 24 | "genderValue": item["value"], 25 | } 26 | } 27 | response.Success(ctx, "成功", data, nil) 28 | } 29 | -------------------------------------------------------------------------------- /app/Common/Model/Model.go: -------------------------------------------------------------------------------- 1 | package Common 2 | 3 | import ( 4 | "gorm.io/gorm" 5 | "time" 6 | ) 7 | 8 | // Model 基模型 9 | type Model struct { 10 | ID uint64 `gorm:"primarykey" json:"id"` 11 | CreatedAt time.Time `json:"created_at"` 12 | UpdatedAt time.Time `json:"updated_at"` 13 | DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at"` 14 | } 15 | -------------------------------------------------------------------------------- /app/Menu/Bapi/Menu.go: -------------------------------------------------------------------------------- 1 | package Menu 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "strconv" 6 | MenuModel "vgo/app/Menu/Model" 7 | Role "vgo/app/Role/Model" 8 | "vgo/core/db" 9 | "vgo/core/helper" 10 | "vgo/core/middle/casbin" 11 | "vgo/core/response" 12 | ) 13 | 14 | // Index 无限极分类菜单结构 15 | func Index(ctx *gin.Context) { 16 | var menus []MenuModel.Menu 17 | var err error 18 | if ctx.GetInt("super") == 1 { // 超级管理员 19 | err = db.Con().Order("sort desc").Find(&menus).Error 20 | } else { 21 | menuIDs := Role.GetMenuIdsByRoleId(ctx.GetUint64("userID")) 22 | err = db.Con().Order("sort desc").Find(&menus, "id in (?)", menuIDs).Error 23 | } 24 | if err != nil { 25 | response.Fail(ctx, "数据库查询失败", err) 26 | return 27 | } 28 | menuTree := MenuModel.BuildMenuTree(menus, 0) 29 | response.Success(ctx, "成功", menuTree, nil) 30 | } 31 | 32 | // Buttons 操作按钮 33 | func Buttons(ctx *gin.Context) { 34 | var data = make(map[string][]string) 35 | var menus []MenuModel.Menu 36 | var err error 37 | 38 | if ctx.GetInt("super") == 1 { // 超级管理员 39 | if err = db.Con().Where("type = ?", 1).Find(&menus).Error; err != nil { 40 | response.Fail(ctx, "数据库查询失败", err) 41 | return 42 | } 43 | for _, menu := range menus { 44 | var buttonMenus []MenuModel.Menu 45 | if err = db.Con().Where("parent_id = ? and type = ?", menu.ID, 2).Find(&buttonMenus).Error; err != nil { 46 | response.Fail(ctx, "数据库查询失败", err) 47 | return 48 | } 49 | if len(buttonMenus) > 0 { 50 | bbt := make([]string, len(buttonMenus)) 51 | for i, buttonMenu := range buttonMenus { 52 | bbt[i] = buttonMenu.Name 53 | } 54 | data[menu.Name] = bbt 55 | } 56 | } 57 | response.Success(ctx, "成功", data, nil) 58 | return 59 | } 60 | menuIDs := Role.GetMenuIdsByRoleId(ctx.GetUint64("userID")) 61 | if err = db.Con().Where("type = ? and id in (?)", 1, menuIDs).Find(&menus).Error; err != nil { 62 | response.Fail(ctx, "数据库查询失败", err) 63 | return 64 | } 65 | // 获取角色 66 | enforcer := casbin.SetupCasbin() 67 | roles, err := enforcer.GetRolesForUser(strconv.FormatUint(ctx.GetUint64("userID"), 10)) 68 | if err != nil { 69 | response.Fail(ctx, "角色获取失败", err) 70 | return 71 | } 72 | for _, menu := range menus { 73 | var buttonMenus []MenuModel.Menu 74 | if err = db.Con().Where("parent_id = ? and type = ?", menu.ID, 2).Find(&buttonMenus).Error; err != nil { 75 | response.Fail(ctx, "数据库查询失败", err) 76 | return 77 | } 78 | if len(buttonMenus) == 0 { 79 | continue 80 | } 81 | bbt := make([]string, 0, len(buttonMenus)) 82 | for _, buttonMenu := range buttonMenus { 83 | if buttonMenu.Name == "" { 84 | continue 85 | } 86 | // 判断按钮是否有权限 87 | hasPermission := false 88 | for _, role := range roles { 89 | if policy, err := enforcer.HasPolicy(role, buttonMenu.Api); err == nil && policy { 90 | hasPermission = true 91 | break 92 | } 93 | } 94 | if hasPermission { 95 | bbt = append(bbt, buttonMenu.Name) 96 | } 97 | } 98 | if len(bbt) > 0 { 99 | data[menu.Name] = bbt 100 | } 101 | } 102 | response.Success(ctx, "成功", data, nil) 103 | } 104 | 105 | // MenuSelect 下拉树结构 106 | type MenuSelect struct { 107 | Value uint64 `json:"value"` 108 | Label string `json:"label"` 109 | Children []MenuSelect `json:"children"` 110 | } 111 | 112 | // convertToMenuSelect 将Menu结构体转换为MenuSelect结构体 113 | func convertToMenuSelect(menu MenuModel.Menu) MenuSelect { 114 | menuSelect := MenuSelect{ 115 | Value: menu.ID, // 假设Menu结构体中有ID字段 116 | Label: menu.Title, // 假设Menu结构体中有Name字段 117 | Children: []MenuSelect{}, 118 | } 119 | for _, child := range menu.Children { 120 | menuSelect.Children = append(menuSelect.Children, convertToMenuSelect(child)) 121 | } 122 | return menuSelect 123 | } 124 | 125 | // GetSelectTree 获取下拉树结构 126 | func GetSelectTree(ctx *gin.Context) { 127 | var menus []MenuModel.Menu 128 | if err := db.Con().Order("sort desc").Find(&menus).Error; err != nil { 129 | response.Fail(ctx, "数据库查询失败", err) 130 | return 131 | } 132 | menuTree := MenuModel.BuildMenuTree(menus, 0) 133 | menuSelects := make([]MenuSelect, len(menuTree)) 134 | for i, menu := range menuTree { 135 | menuSelects[i] = convertToMenuSelect(menu) 136 | } 137 | response.Success(ctx, "查询成功", menuSelects, nil) 138 | } 139 | 140 | // Create 创建 141 | func Create(ctx *gin.Context) { 142 | var product MenuModel.Menu 143 | if err := helper.VgoShouldBindJSON(ctx, &product); err != nil { 144 | response.Fail(ctx, "参数错误", err.Error(), nil) 145 | return 146 | } 147 | db.Con().Create(&product) 148 | response.Success(ctx, "成功", product, nil) 149 | } 150 | 151 | // Update 更新 152 | func Update(ctx *gin.Context) { 153 | var notice MenuModel.Menu 154 | if err := helper.VgoShouldBindJSON(ctx, ¬ice); err != nil { 155 | response.Fail(ctx, "参数错误", err.Error(), nil) 156 | return 157 | } 158 | if err := db.Con().Model(&MenuModel.Menu{}).Where("id = ?", notice.ID).Updates(notice).Error; err != nil { 159 | response.Fail(ctx, "更新失败", err.Error()) 160 | return 161 | } 162 | response.Success(ctx, "成功", nil, nil) 163 | } 164 | 165 | // Delete 删除 166 | func Delete(ctx *gin.Context) { 167 | var ids struct { 168 | ID []int64 `json:"id"` 169 | } 170 | if err := helper.VgoShouldBindJSON(ctx, &ids); err != nil { 171 | response.Fail(ctx, "参数错误", err.Error(), nil) 172 | return 173 | } 174 | if err := db.Con().Delete(&MenuModel.Menu{}, "parent_id in (?)", ids.ID).Error; err != nil { 175 | response.Fail(ctx, "删除失败", err.Error()) 176 | return 177 | } 178 | if err := db.Con().Delete(&MenuModel.Menu{}, "id in (?)", ids.ID).Error; err != nil { 179 | response.Fail(ctx, "删除失败", err.Error()) 180 | return 181 | } 182 | response.Success(ctx, "成功", nil, nil) 183 | } 184 | -------------------------------------------------------------------------------- /app/Menu/Model/Menu.go: -------------------------------------------------------------------------------- 1 | package Menu 2 | 3 | import ( 4 | "gorm.io/gorm" 5 | Common "vgo/app/Common/Model" 6 | ) 7 | 8 | // Menu 菜单 9 | type Menu struct { 10 | Common.Model 11 | ParentId uint64 `gorm:"column:parent_id;type:bigint;not null;default:0;comment:父ID" json:"parent_id"` 12 | Path string `gorm:"column:path;type:varchar(50);not null;default:'';comment:路由访问路径" json:"path"` 13 | Name string `gorm:"column:name;type:varchar(50);not null;default:'';comment:路由name" json:"name"` 14 | Redirect string `gorm:"column:redirect;type:varchar(255);not null;default:'';comment:路由重定向地址" json:"redirect"` 15 | Api string `gorm:"column:api;type:varchar(255);not null;default:'';comment:请求接口地址" json:"api"` 16 | Component string `gorm:"column:component;type:varchar(255);not null;default:'';comment:视图文件路径" json:"component"` 17 | Icon string `gorm:"column:icon;type:varchar(50);not null;default:'';comment:菜单和面包屑对应的图标" json:"icon"` 18 | Title string `gorm:"column:title;type:varchar(50);not null;default:'';comment:路由标题(菜单名称)" json:"title"` 19 | ActiveMenu string `gorm:"column:activeMenu;type:varchar(500);not null;default:'';comment:是否在菜单中隐藏,需要高亮的path" json:"activeMenu"` 20 | IsLink string `gorm:"column:isLink;type:varchar(500);not null;default:'';comment:路由外链时填写的访问地址" json:"isLink"` 21 | IsHide int `gorm:"column:isHide;type:smallint;not null;default:2;comment:是否在菜单中隐藏 (1是 2否)" json:"isHide"` 22 | IsFull int `gorm:"column:isFull;type:smallint;not null;default:2;comment:菜单是否全屏 (1是 2否)" json:"isFull"` 23 | IsAffix int `gorm:"column:isAffix;type:smallint;not null;default:1;comment:菜单是否固定在标签页中 (1是 2否)" json:"isAffix"` 24 | IsKeepAlive int `gorm:"column:isKeepAlive;type:smallint;not null;default:2;comment:当前路由是否缓存 (1是 2否)" json:"isKeepAlive"` 25 | Status int `gorm:"column:status;type:smallint;not null;default:1;comment:状态 (1正常 2停用)" json:"status"` 26 | Type int `gorm:"column:type;type:smallint;not null;default:1;comment:类型 (1菜单2按钮3外链4Iframe)" json:"type"` 27 | Sort int `gorm:"column:sort;type:int;not null;default:0;comment:排序" json:"sort"` 28 | Meta map[string]interface{} `gorm:"-" json:"meta"` // 忽略数据库字段,使用Getter方法 29 | Children []Menu `gorm:"foreignKey:ParentId" json:"children"` 30 | } 31 | 32 | // AfterFind 回调函数,在查询后执行 33 | func (m *Menu) AfterFind(tx *gorm.DB) (err error) { 34 | m.Meta = m.getMetaAttribute() 35 | return 36 | } 37 | 38 | // GetMetaAttribute 获取菜单元数据属性 39 | func (m *Menu) getMetaAttribute() map[string]interface{} { 40 | meta := map[string]interface{}{ 41 | "icon": m.Icon, 42 | "title": m.Title, 43 | "activeMenu": m.ActiveMenu, 44 | "isLink": m.IsLink, 45 | "isHide": m.IsHide == 1, 46 | "isFull": m.IsFull == 1, 47 | "isAffix": m.IsAffix == 1, 48 | "isKeepAlive": m.IsKeepAlive == 1, 49 | } 50 | return meta 51 | } 52 | 53 | // BuildMenuTree 构建菜单树 54 | func BuildMenuTree(menus []Menu, parentID uint64) []Menu { 55 | var tree []Menu 56 | for _, menu := range menus { 57 | if menu.ParentId == parentID { 58 | children := BuildMenuTree(menus, menu.ID) 59 | if len(children) > 0 { 60 | menu.Children = children 61 | } 62 | tree = append(tree, menu) 63 | } 64 | } 65 | return tree 66 | } 67 | -------------------------------------------------------------------------------- /app/Menu/Router/Menu.go: -------------------------------------------------------------------------------- 1 | package Menu 2 | 3 | import ( 4 | Menu "vgo/app/Menu/Bapi" 5 | "vgo/core/router" 6 | ) 7 | 8 | func CollectRoutes() []router.BaseRoute { 9 | return []router.BaseRoute{ 10 | {"GET", "/menu/list", Menu.Index}, 11 | {"GET", "/menu/selectTreeDataSource", Menu.GetSelectTree}, 12 | {"POST", "/menu/create", Menu.Create}, 13 | {"POST", "/menu/update", Menu.Update}, 14 | {"POST", "/menu/delete", Menu.Delete}, 15 | {"GET", "/button/list", Menu.Buttons}, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/Notice/Api/Notice.go: -------------------------------------------------------------------------------- 1 | package Notice 2 | -------------------------------------------------------------------------------- /app/Notice/Bapi/Notice.go: -------------------------------------------------------------------------------- 1 | package Notice 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "strconv" 6 | NoticeModel "vgo/app/Notice/Model" 7 | "vgo/core/db" 8 | "vgo/core/helper" 9 | "vgo/core/response" 10 | ) 11 | 12 | // Index 列表 13 | func Index(ctx *gin.Context) { 14 | var res []NoticeModel.Notice 15 | var total int64 16 | pageNo, err := strconv.Atoi(ctx.DefaultQuery("pageNum", "1")) 17 | if err != nil { 18 | response.Fail(ctx, "页码参数无效", nil) 19 | return 20 | } 21 | Size, err := strconv.Atoi(ctx.DefaultQuery("pageSize", "10")) 22 | if err != nil { 23 | response.Fail(ctx, "每页大小参数无效", nil) 24 | return 25 | } 26 | if err := db.Con().Model(&NoticeModel.Notice{}).Count(&total).Error; err != nil { 27 | response.Fail(ctx, "数据库查询失败", err.Error()) 28 | return 29 | } 30 | if err := db.Con().Order("id desc").Offset((pageNo - 1) * Size).Limit(Size).Find(&res).Error; err != nil { 31 | response.Fail(ctx, "数据库查询失败", err.Error()) 32 | return 33 | } 34 | totalPage := int(total) / Size 35 | if int(total)%Size != 0 { 36 | totalPage++ 37 | } 38 | response.Success(ctx, "成功", gin.H{ 39 | "pageNum": pageNo, 40 | "total": total, 41 | "pageSize": Size, 42 | "list": res, 43 | }, nil) 44 | } 45 | 46 | // Create 创建 47 | func Create(ctx *gin.Context) { 48 | var product NoticeModel.Notice 49 | if err := helper.VgoShouldBindJSON(ctx, &product); err != nil { 50 | response.Fail(ctx, "参数错误", err.Error(), nil) 51 | return 52 | } 53 | db.Con().Create(&product) 54 | response.Success(ctx, "成功", product, nil) 55 | } 56 | 57 | // Update 更新 58 | func Update(ctx *gin.Context) { 59 | var notice NoticeModel.Notice 60 | if err := helper.VgoShouldBindJSON(ctx, ¬ice); err != nil { 61 | response.Fail(ctx, "参数错误", err.Error(), nil) 62 | return 63 | } 64 | if err := db.Con().Model(&NoticeModel.Notice{}).Where("id = ?", notice.ID).Updates(notice).Error; err != nil { 65 | response.Fail(ctx, "更新失败", err.Error()) 66 | return 67 | } 68 | response.Success(ctx, "成功", nil, nil) 69 | } 70 | 71 | // Delete 删除 72 | func Delete(ctx *gin.Context) { 73 | var ids struct { 74 | ID []int64 `json:"id"` 75 | } 76 | if err := helper.VgoShouldBindJSON(ctx, &ids); err != nil { 77 | response.Fail(ctx, "参数错误", err.Error(), nil) 78 | return 79 | } 80 | if err := db.Con().Delete(&NoticeModel.Notice{}, "id in (?)", ids.ID).Error; err != nil { 81 | response.Fail(ctx, "删除失败", err.Error()) 82 | return 83 | } 84 | response.Success(ctx, "成功", nil, nil) 85 | } 86 | 87 | // Change 改变状态 88 | func Change(ctx *gin.Context) { 89 | var notice NoticeModel.Notice 90 | if err := helper.VgoShouldBindJSON(ctx, ¬ice); err != nil { 91 | response.Fail(ctx, "参数错误", err.Error(), nil) 92 | return 93 | } 94 | if err := db.Con().Model(&NoticeModel.Notice{}).Where("id = ?", notice.ID).Updates(notice).Error; err != nil { 95 | response.Fail(ctx, "更新失败", err.Error()) 96 | return 97 | } 98 | response.Success(ctx, "成功", nil, nil) 99 | 100 | } 101 | -------------------------------------------------------------------------------- /app/Notice/Model/Notice.go: -------------------------------------------------------------------------------- 1 | package Notice 2 | 3 | import Common "vgo/app/Common/Model" 4 | 5 | // Notice 公告 6 | type Notice struct { 7 | Common.Model 8 | Title string `gorm:"type:varchar(255);not null;column:title;default:'';comment:标题;" json:"title"` 9 | Type int `gorm:"type:smallint;not null;column:type;default:0;comment:公告类型(1通知 2公告);" json:"type"` 10 | Status int `gorm:"column:status;type:smallint;not null;default:1;comment:状态 (1启用 2禁用)" json:"status"` 11 | Content string `gorm:"type:text;column:content;comment:公告内容;" json:"content"` 12 | Remark string `gorm:"type:varchar(255);column:remark;default:'';comment:备注;" json:"remark"` 13 | } 14 | -------------------------------------------------------------------------------- /app/Notice/Router/Notice.go: -------------------------------------------------------------------------------- 1 | package Notice 2 | 3 | import ( 4 | Notice "vgo/app/Notice/Bapi" 5 | "vgo/core/router" 6 | ) 7 | 8 | func CollectRoutes() []router.BaseRoute { 9 | return []router.BaseRoute{ 10 | {"GET", "/notice", Notice.Index}, 11 | {"POST", "/notice/create", Notice.Create}, 12 | {"POST", "/notice/update", Notice.Update}, 13 | {"POST", "/notice/change", Notice.Change}, 14 | {"POST", "/notice/delete", Notice.Delete}, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/Role/Bapi/Role.go: -------------------------------------------------------------------------------- 1 | package Role 2 | 3 | import ( 4 | "errors" 5 | "github.com/gin-gonic/gin" 6 | "github.com/go-sql-driver/mysql" 7 | "strconv" 8 | Menu "vgo/app/Menu/Model" 9 | RoleModel "vgo/app/Role/Model" 10 | "vgo/core/db" 11 | "vgo/core/helper" 12 | "vgo/core/middle/casbin" 13 | "vgo/core/response" 14 | ) 15 | 16 | // Index 列表 17 | func Index(ctx *gin.Context) { 18 | var res []RoleModel.Role 19 | var total int64 20 | pageNo, err := strconv.Atoi(ctx.DefaultQuery("pageNum", "1")) 21 | if err != nil { 22 | response.Fail(ctx, "页码参数无效", nil) 23 | return 24 | } 25 | Size, err := strconv.Atoi(ctx.DefaultQuery("pageSize", "10")) 26 | if err != nil { 27 | response.Fail(ctx, "每页大小参数无效", nil) 28 | return 29 | } 30 | if err := db.Con().Model(&RoleModel.Role{}).Count(&total).Error; err != nil { 31 | response.Fail(ctx, "数据库查询失败", err.Error()) 32 | return 33 | } 34 | if err := db.Con().Order("id desc").Offset((pageNo - 1) * Size).Limit(Size).Find(&res).Error; err != nil { 35 | response.Fail(ctx, "数据库查询失败", err.Error()) 36 | return 37 | } 38 | totalPage := int(total) / Size 39 | if int(total)%Size != 0 { 40 | totalPage++ 41 | } 42 | response.Success(ctx, "成功", gin.H{ 43 | "pageNum": pageNo, 44 | "total": total, 45 | "pageSize": Size, 46 | "list": res, 47 | }, nil) 48 | } 49 | 50 | // GetAll 获取全部 51 | func GetAll(ctx *gin.Context) { 52 | var res []RoleModel.Role 53 | if err := db.Con().Order("id desc").Find(&res).Error; err != nil { 54 | response.Fail(ctx, "数据库查询失败", err.Error()) 55 | return 56 | } 57 | response.Success(ctx, "成功", res, nil) 58 | } 59 | 60 | // Create 创建 61 | func Create(ctx *gin.Context) { 62 | var role RoleModel.Role 63 | if err := helper.VgoShouldBindJSON(ctx, &role); err != nil { 64 | response.Fail(ctx, "参数错误", err.Error(), nil) 65 | return 66 | } 67 | // 插入数据 68 | if err := db.Con().Create(&role).Error; err != nil { 69 | // 检查是否是唯一键冲突错误 70 | var mysqlErr *mysql.MySQLError 71 | if errors.As(err, &mysqlErr) && mysqlErr.Number == 1062 { 72 | response.Fail(ctx, "角色标识已存在", err.Error(), nil) 73 | } 74 | return 75 | } 76 | response.Success(ctx, "成功", role, nil) 77 | } 78 | 79 | // SetMenu 设置菜单 80 | func SetMenu(ctx *gin.Context) { 81 | var codes struct { 82 | ID uint64 `json:"id"` 83 | Menus []uint64 `json:"menus"` 84 | } 85 | if err := helper.VgoShouldBindJSON(ctx, &codes); err != nil { 86 | response.Fail(ctx, "参数错误", err.Error(), nil) 87 | return 88 | } 89 | roleID := codes.ID 90 | // 清理原有菜单 91 | if err := db.Con().Delete(&RoleModel.RoleMenu{}, "role_id = ?", roleID).Error; err != nil { 92 | response.Fail(ctx, "菜单清理失败", err.Error()) 93 | return 94 | } 95 | // 角色信息 96 | var role RoleModel.Role 97 | db.Con().First(&role, roleID) 98 | // 清理原有策略 99 | enforcer := casbin.SetupCasbin() 100 | _, err := enforcer.RemoveFilteredPolicy(0, role.Code) 101 | if err != nil { 102 | response.Fail(ctx, "策略清理失败", err.Error()) 103 | return 104 | } 105 | // 写入菜单 106 | var dbMenus []Menu.Menu 107 | db.Con().Where("id IN ?", codes.Menus).Find(&dbMenus) 108 | menuMap := make(map[int]Menu.Menu) 109 | for _, dbMenu := range dbMenus { 110 | menuMap[int(dbMenu.ID)] = dbMenu 111 | } 112 | // 写入角色菜单关联和策略 113 | for _, menu := range codes.Menus { 114 | var item RoleModel.RoleMenu 115 | item.RoleId = roleID 116 | item.MenuId = menu 117 | dbMenu, exists := menuMap[int(menu)] 118 | if exists { 119 | // 写入策略 120 | if dbMenu.Api != "" { 121 | _, err2 := enforcer.AddPolicy(role.Code, dbMenu.Api) 122 | if err2 != nil { 123 | response.Fail(ctx, "策略写入失败", err2.Error()) 124 | return 125 | } 126 | } 127 | } 128 | if err := db.Con().Create(&item).Error; err != nil { 129 | response.Fail(ctx, err.Error(), err.Error()) 130 | return 131 | } 132 | } 133 | response.Success(ctx, "设置成功", nil, nil) 134 | } 135 | 136 | // GetMenu 获取菜单 137 | func GetMenu(ctx *gin.Context) { 138 | var codes struct { 139 | ID uint64 `json:"id"` 140 | } 141 | if err := helper.VgoShouldBindJSON(ctx, &codes); err != nil { 142 | response.Fail(ctx, "参数错误", err.Error(), nil) 143 | return 144 | } 145 | var res []RoleModel.RoleMenu 146 | if err := db.Con().Where("role_id = ?", codes.ID).Find(&res).Error; err != nil { 147 | response.Fail(ctx, "数据库查询失败", err.Error()) 148 | return 149 | } 150 | response.Success(ctx, "成功", res, nil) 151 | } 152 | 153 | // Update 更新 154 | func Update(ctx *gin.Context) { 155 | var notice RoleModel.Role 156 | if err := helper.VgoShouldBindJSON(ctx, ¬ice); err != nil { 157 | response.Fail(ctx, "参数错误", err.Error(), nil) 158 | return 159 | } 160 | if err := db.Con().Model(&RoleModel.Role{}).Where("id = ?", notice.ID).Updates(notice).Error; err != nil { 161 | response.Fail(ctx, "更新失败", err.Error()) 162 | return 163 | } 164 | response.Success(ctx, "成功", nil, nil) 165 | } 166 | 167 | // Delete 删除 168 | func Delete(ctx *gin.Context) { 169 | var ids struct { 170 | ID []int64 `json:"id"` 171 | } 172 | if err := helper.VgoShouldBindJSON(ctx, &ids); err != nil { 173 | response.Fail(ctx, "参数错误", err.Error(), nil) 174 | return 175 | } 176 | if err := db.Con().Delete(&RoleModel.Role{}, "id in (?)", ids.ID).Error; err != nil { 177 | response.Fail(ctx, "删除失败", err.Error()) 178 | return 179 | } 180 | response.Success(ctx, "成功", nil, nil) 181 | } 182 | 183 | // Change 改变状态 184 | func Change(ctx *gin.Context) { 185 | var notice RoleModel.Role 186 | if err := helper.VgoShouldBindJSON(ctx, ¬ice); err != nil { 187 | response.Fail(ctx, "参数错误", err.Error(), nil) 188 | return 189 | } 190 | if err := db.Con().Model(&RoleModel.Role{}).Where("id = ?", notice.ID).Updates(notice).Error; err != nil { 191 | response.Fail(ctx, "更新失败", err.Error()) 192 | return 193 | } 194 | response.Success(ctx, "成功", nil, nil) 195 | 196 | } 197 | -------------------------------------------------------------------------------- /app/Role/Model/Role.go: -------------------------------------------------------------------------------- 1 | package Role 2 | 3 | import ( 4 | Common "vgo/app/Common/Model" 5 | ) 6 | 7 | // Role 角色 8 | type Role struct { 9 | Common.Model 10 | Name string `gorm:"column:name;type:varchar(50);not null;default:'';comment:角色名称" json:"name"` 11 | Code string `gorm:"column:code;type:varchar(50);unique;not null;default:'';comment:角色代码" json:"code"` 12 | } 13 | -------------------------------------------------------------------------------- /app/Role/Model/RoleMenu.go: -------------------------------------------------------------------------------- 1 | package Role 2 | 3 | import ( 4 | "strconv" 5 | Common "vgo/app/Common/Model" 6 | "vgo/core/db" 7 | "vgo/core/middle/casbin" 8 | ) 9 | 10 | // RoleMenu 角色菜单 11 | type RoleMenu struct { 12 | Common.Model 13 | RoleId uint64 `gorm:"column:role_id;not null;default:0;comment:角色ID" json:"role_id"` 14 | MenuId uint64 `gorm:"column:menu_id;not null;default:0;comment:菜单ID" json:"menu_id"` 15 | } 16 | 17 | // GetMenuIdsByRoleId 获取角色菜单ID列表 18 | func GetMenuIdsByRoleId(userID uint64) []uint64 { 19 | // 获取角色 20 | enforcer := casbin.SetupCasbin() 21 | roles, err := enforcer.GetRolesForUser(strconv.FormatUint(userID, 10)) 22 | if err != nil { 23 | return nil 24 | } 25 | var dbRoles []Role 26 | db.Con().Find(&dbRoles, "code in (?)", roles) 27 | roleIDs := make([]uint64, 0) 28 | for _, rid := range dbRoles { 29 | roleIDs = append(roleIDs, rid.ID) 30 | } 31 | // 获取角色菜单关联 32 | var roleMenu []RoleMenu 33 | db.Con().Find(&roleMenu, "role_id in (?)", roleIDs) 34 | menuIDs := make([]uint64, 0) 35 | for _, roleMenu := range roleMenu { 36 | menuIDs = append(menuIDs, roleMenu.MenuId) 37 | } 38 | return menuIDs 39 | } 40 | -------------------------------------------------------------------------------- /app/Role/Router/Role.go: -------------------------------------------------------------------------------- 1 | package Role 2 | 3 | import ( 4 | Role "vgo/app/Role/Bapi" 5 | "vgo/core/router" 6 | ) 7 | 8 | func CollectRoutes() []router.BaseRoute { 9 | return []router.BaseRoute{ 10 | {"GET", "/role", Role.Index}, 11 | {"POST", "/role/create", Role.Create}, 12 | {"POST", "/role/update", Role.Update}, 13 | {"POST", "/role/delete", Role.Delete}, 14 | {"GET", "/role/allDataSource", Role.GetAll}, 15 | {"POST", "/role/set/menu", Role.SetMenu}, 16 | {"POST", "/role/get/menu", Role.GetMenu}, 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/System/Bapi/System.go: -------------------------------------------------------------------------------- 1 | package System 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/gin-gonic/gin" 6 | "io" 7 | "net/http" 8 | "vgo/core/response" 9 | ) 10 | 11 | // GetBingBackgroundImage 获取必应每日背景图 12 | func GetBingBackgroundImage(ctx *gin.Context) { 13 | isAbroad := false 14 | var url string 15 | if isAbroad { 16 | url = "https://bing.com/th?id=OHR.TateishiPark_ZH-CN9903501398_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&pid=hp" 17 | } else { 18 | url = "https://cn.bing.com/th?id=OHR.TateishiPark_ZH-CN9903501398_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&pid=hp" 19 | } 20 | response.Success(ctx, "成功", map[string]string{"url": url}, nil) 21 | } 22 | 23 | // getBingImageURL 联网获取必应每日背景图URL 24 | func getBingImageURL(isAbroad bool, defaultURL string) (string, error) { 25 | var domain string 26 | if isAbroad { 27 | domain = "https://cn.bing.com" 28 | } else { 29 | domain = "https://bing.com" 30 | } 31 | resp, err := http.Get(domain + "/HPImageArchive.aspx?format=js&idx=0&n=1") 32 | if err != nil { 33 | return defaultURL, err 34 | } 35 | defer resp.Body.Close() 36 | body, err := io.ReadAll(resp.Body) 37 | if err != nil { 38 | return defaultURL, err 39 | } 40 | var content struct { 41 | Images []struct { 42 | URL string `json:"url"` 43 | } `json:"images"` 44 | } 45 | if err := json.Unmarshal(body, &content); err != nil { 46 | return defaultURL, err 47 | } 48 | if len(content.Images) > 0 && content.Images[0].URL != "" { 49 | return domain + content.Images[0].URL, nil 50 | } 51 | return defaultURL, nil 52 | } 53 | -------------------------------------------------------------------------------- /app/Test/Test.go: -------------------------------------------------------------------------------- 1 | package Test 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "vgo/core/response" 6 | "vgo/core/snow" 7 | ) 8 | 9 | func Index(ctx *gin.Context) { 10 | //err := db.Con().AutoMigrate(&User.User{}) 11 | //if err != nil { 12 | // return 13 | //} 14 | //response.Success(ctx, "666", nil) 15 | //return 16 | response.Success(ctx, "666", nil) 17 | } 18 | 19 | func Index2(ctx *gin.Context) { 20 | id := snow.Node().Generate() 21 | response.Success(ctx, "Generated ID", id) 22 | } 23 | -------------------------------------------------------------------------------- /app/Upload/Api/ImgUpload.go: -------------------------------------------------------------------------------- 1 | package Upload 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gabriel-vasile/mimetype" 6 | "github.com/gin-gonic/gin" 7 | "io" 8 | "os" 9 | "path/filepath" 10 | "strconv" 11 | "time" 12 | "vgo/core/global" 13 | "vgo/core/response" 14 | "vgo/core/snow" 15 | ) 16 | 17 | // ImgUpload 图片上传 18 | func ImgUpload(ctx *gin.Context) { 19 | // 获取上传的文件 20 | file, err := ctx.FormFile("file") 21 | if err != nil { 22 | response.Fail(ctx, "获取文件失败", err.Error(), nil) 23 | return 24 | } 25 | 26 | // 检查文件大小 27 | const maxFileSize = 10 * 1024 * 1024 // 10MB 28 | if file.Size > maxFileSize { 29 | response.Fail(ctx, fmt.Sprintf("文件大小不能超过 %d MB", maxFileSize/(1024*1024)), nil) 30 | return 31 | } 32 | 33 | // 打开文件 34 | src, err := file.Open() 35 | if err != nil { 36 | response.Fail(ctx, "无法打开文件", err.Error()) 37 | return 38 | } 39 | defer src.Close() 40 | 41 | // 读取文件内容 42 | fileContent, err := io.ReadAll(src) 43 | if err != nil { 44 | response.Fail(ctx, "无法读取文件内容", err.Error()) 45 | return 46 | } 47 | 48 | // 使用 mimetype 库获取文件的真实类型 49 | mime := mimetype.Detect(fileContent) 50 | fileType := mime.String() 51 | 52 | // 检查文件类型 53 | allowedTypes := map[string]bool{ 54 | "image/jpeg": true, 55 | "image/png": true, 56 | "image/gif": true, 57 | //..... 58 | } 59 | 60 | if !allowedTypes[fileType] { 61 | response.Fail(ctx, "不支持的文件类型", nil) 62 | return 63 | } 64 | 65 | // 获取当前日期 66 | now := time.Now() 67 | timeStr := now.Format("20060101") 68 | 69 | // 创建保存文件的目标路径,包含年月日子文件夹 70 | dstDir := filepath.Join("storage", "uploads", "img", timeStr) 71 | 72 | // 判断文件夹是否存在,如果不存在则创建 73 | if _, err := os.Stat(dstDir); os.IsNotExist(err) { 74 | err := os.MkdirAll(dstDir, os.ModePerm) 75 | if err != nil { 76 | response.Fail(ctx, "创建文件夹失败", err.Error()) 77 | return 78 | } 79 | } 80 | 81 | // 文件重命名 82 | id := snow.Node().Generate() 83 | ext := filepath.Ext(file.Filename) // 获取文件的后缀 84 | newFileName := strconv.FormatInt(int64(id), 10) + ext 85 | dst := filepath.Join(dstDir, newFileName) 86 | respUrl := "/storage/uploads/img/" + timeStr + "/" + newFileName 87 | 88 | // 打开上传的文件【上一次的读取操作会将文件的读写指针移到文件末尾】 89 | src, err = file.Open() 90 | if err != nil { 91 | response.Fail(ctx, "打开文件失败", err.Error()) 92 | return 93 | } 94 | defer src.Close() 95 | 96 | // 创建目标文件 97 | out, err := os.Create(dst) 98 | if err != nil { 99 | response.Fail(ctx, "创建目标文件失败", err.Error()) 100 | return 101 | } 102 | defer out.Close() 103 | 104 | // 将上传的文件内容复制到目标文件 105 | _, err = io.Copy(out, src) 106 | if err != nil { 107 | response.Fail(ctx, "保存文件失败", err.Error()) 108 | return 109 | } 110 | imgDomain := global.App.Config.App.ImgDomain 111 | response.Success(ctx, "文件上传成功", gin.H{ 112 | "dbUrl": respUrl, 113 | "fileUrl": imgDomain + respUrl, 114 | }, nil) 115 | 116 | } 117 | -------------------------------------------------------------------------------- /app/Upload/Api/VideoUpload.go: -------------------------------------------------------------------------------- 1 | package Upload 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gabriel-vasile/mimetype" 6 | "github.com/gin-gonic/gin" 7 | "io" 8 | "os" 9 | "path/filepath" 10 | "strconv" 11 | "time" 12 | "vgo/core/global" 13 | "vgo/core/response" 14 | "vgo/core/snow" 15 | ) 16 | 17 | // VideoUpload 视频上传 18 | func VideoUpload(ctx *gin.Context) { 19 | // 获取上传的文件 20 | file, err := ctx.FormFile("file") 21 | if err != nil { 22 | response.Fail(ctx, "获取文件失败", err.Error(), nil) 23 | return 24 | } 25 | 26 | // 检查文件大小 27 | const maxFileSize = 50 * 1024 * 1024 // 50MB 28 | if file.Size > maxFileSize { 29 | response.Fail(ctx, fmt.Sprintf("文件大小不能超过 %d MB", maxFileSize/(1024*1024)), nil) 30 | return 31 | } 32 | 33 | // 打开文件 34 | src, err := file.Open() 35 | if err != nil { 36 | response.Fail(ctx, "无法打开文件", err.Error()) 37 | return 38 | } 39 | defer src.Close() 40 | 41 | // 读取 42 | fileContent, err := io.ReadAll(src) 43 | if err != nil { 44 | response.Fail(ctx, "无法读取文件内容", err.Error()) 45 | return 46 | } 47 | 48 | // 获取文件的真实类型 49 | mime := mimetype.Detect(fileContent) 50 | fileType := mime.String() 51 | 52 | // 检查文件类型 53 | allowedTypes := map[string]bool{ 54 | "video/mp4": true, 55 | //..... 56 | } 57 | 58 | if !allowedTypes[fileType] { 59 | response.Fail(ctx, "不支持的文件类型", nil) 60 | return 61 | } 62 | 63 | // 获取当前日期 64 | now := time.Now() 65 | timeStr := now.Format("20060101") 66 | 67 | // 创建保存文件的目标路径,包含年月日子文件夹 68 | dstDir := filepath.Join("storage", "uploads", "video", timeStr) 69 | 70 | // 判断文件夹是否存在,如果不存在则创建 71 | if _, err := os.Stat(dstDir); os.IsNotExist(err) { 72 | err := os.MkdirAll(dstDir, os.ModePerm) 73 | if err != nil { 74 | response.Fail(ctx, "创建文件夹失败", err.Error()) 75 | return 76 | } 77 | } 78 | 79 | // 文件重命名 80 | id := snow.Node().Generate() 81 | ext := filepath.Ext(file.Filename) // 获取文件的后缀 82 | newFileName := strconv.FormatInt(int64(id), 10) + ext 83 | dst := filepath.Join(dstDir, newFileName) 84 | respUrl := "/storage/uploads/video/" + timeStr + "/" + newFileName 85 | 86 | // 打开上传的文件【上一次的读取操作会将文件的读写指针移到文件末尾】 87 | src, err = file.Open() 88 | if err != nil { 89 | response.Fail(ctx, "打开文件失败", err.Error()) 90 | return 91 | } 92 | defer src.Close() 93 | 94 | // 创建目标文件 95 | out, err := os.Create(dst) 96 | if err != nil { 97 | response.Fail(ctx, "创建目标文件失败", err.Error()) 98 | return 99 | } 100 | defer out.Close() 101 | 102 | // 将上传的文件内容复制到目标文件 103 | _, err = io.Copy(out, src) 104 | if err != nil { 105 | response.Fail(ctx, "保存文件失败", err.Error()) 106 | return 107 | } 108 | imgDomain := global.App.Config.App.ImgDomain 109 | response.Success(ctx, "文件上传成功", gin.H{ 110 | "dbUrl": respUrl, 111 | "fileUrl": imgDomain + respUrl, 112 | }, nil) 113 | 114 | } 115 | -------------------------------------------------------------------------------- /app/Upload/Router/Upload.go: -------------------------------------------------------------------------------- 1 | package Upload 2 | 3 | import ( 4 | Upload "vgo/app/Upload/Api" 5 | "vgo/core/router" 6 | ) 7 | 8 | func CollectRoutes() []router.BaseRoute { 9 | return []router.BaseRoute{ 10 | {"POST", "/upload/img", Upload.ImgUpload}, // 图片上传 11 | {"POST", "/upload/video", Upload.VideoUpload}, // 视频上传 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/User/Api/User.go: -------------------------------------------------------------------------------- 1 | package User 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | User "vgo/app/User/Model" 6 | "vgo/core/db" 7 | "vgo/core/helper" 8 | "vgo/core/middle/auth" 9 | "vgo/core/response" 10 | "vgo/core/trans" 11 | "vgo/core/validate" 12 | ) 13 | 14 | // Register 注册 15 | func Register(ctx *gin.Context) { 16 | var user User.User 17 | if err := helper.VgoShouldBindJSON(ctx, &user); err != nil { 18 | response.Fail(ctx, "参数错误", err.Error(), nil) 19 | return 20 | } 21 | 22 | // 验证规则 23 | rules := map[string]map[string]string{ 24 | "Phone": { 25 | "required": trans.Trans("手机号不能为空", "哈哈哈", 666), 26 | }, 27 | "Password": { 28 | "required": "密码不能为空66666", 29 | }, 30 | } 31 | // 验证 32 | if res, err := validate.Do(user, rules); !res { 33 | response.Fail(ctx, err, nil) 34 | return 35 | } 36 | 37 | // 插入数据 38 | if err := db.Con().Create(&user).Error; err != nil { 39 | response.Fail(ctx, "注册失败", err.Error(), nil) 40 | return 41 | } 42 | response.Success(ctx, "成功", user, nil) 43 | } 44 | 45 | // GetToken 获取token 46 | func GetToken(ctx *gin.Context) { 47 | res, err := auth.GenUserToken(ctx, 12) 48 | if err != nil { 49 | response.Fail(ctx, "获取失败", nil) 50 | } 51 | response.Success(ctx, "成功", res) 52 | } 53 | 54 | // Setback 设置黑名单 55 | func Setback(ctx *gin.Context) { 56 | back := ctx.PostForm("back") 57 | auth.PutApiTokenInvalidateToken(ctx, back) 58 | response.Success(ctx, "成功", nil) 59 | } 60 | 61 | // Personal 用户信息 62 | func Personal(ctx *gin.Context) { 63 | userID := ctx.GetString("userID") 64 | response.Success(ctx, "成功", map[string]interface{}{ 65 | "userID": userID, 66 | "message": "pong", 67 | }, nil) 68 | } 69 | -------------------------------------------------------------------------------- /app/User/Model/User.go: -------------------------------------------------------------------------------- 1 | package User 2 | 3 | import ( 4 | Common "vgo/app/Common/Model" 5 | ) 6 | 7 | // User 用户 8 | type User struct { 9 | Common.Model 10 | Phone string `gorm:"column:phone;default:'';type:varchar(11);not null;comment:手机" validate:"required" json:"phone"` 11 | Password string `gorm:"column:password;default:'';not null;comment:密码" validate:"required" json:"password"` 12 | PId uint64 `gorm:"column:pid;type:bigint;not null;default:0;comment:父ID" validate:"required" json:"pid"` 13 | RealName string `gorm:"column:real_name;default:'';type:varchar(255);not null;comment:真实姓名" validate:"required" json:"real_name"` 14 | IdCard string `gorm:"column:id_card;default:'';type:varchar(255);not null;comment:身份证号码" validate:"required" json:"id_card"` 15 | } 16 | -------------------------------------------------------------------------------- /app/User/Router/User.go: -------------------------------------------------------------------------------- 1 | package User 2 | 3 | import ( 4 | User "vgo/app/User/Api" 5 | "vgo/core/router" 6 | ) 7 | 8 | func CollectRoutes() []router.BaseRoute { 9 | return []router.BaseRoute{ 10 | {"GET", "/user/personal", User.Personal}, 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /app/Ws/Ws.go: -------------------------------------------------------------------------------- 1 | package Ws 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/gin-gonic/gin" 7 | "net/http" 8 | "strconv" 9 | "strings" 10 | "sync" 11 | "time" 12 | "vgo/core/global" 13 | "vgo/core/helper" 14 | "vgo/core/response" 15 | "vgo/core/snow" 16 | 17 | "github.com/gorilla/websocket" 18 | ) 19 | 20 | var ( 21 | upgrader = websocket.Upgrader{ 22 | HandshakeTimeout: time.Second * 10, 23 | ReadBufferSize: 1024, 24 | WriteBufferSize: 1024, 25 | CheckOrigin: func(r *http.Request) bool { 26 | origins := global.App.Config.App.ApiOrigins 27 | allowedOrigins := strings.Split(origins, ",") 28 | // 逗号分隔的字符串 29 | origin := r.Header.Get("Origin") 30 | for _, allowedOrigin := range allowedOrigins { 31 | if origin == allowedOrigin { 32 | return true 33 | } 34 | } 35 | return false 36 | }, 37 | } 38 | Connections = make(map[int64]*websocket.Conn) 39 | mu sync.Mutex 40 | ) 41 | 42 | // Link 链接WebSocket 43 | func Link(ctx *gin.Context) { 44 | ws, err := upgrader.Upgrade(ctx.Writer, ctx.Request, nil) 45 | if err != nil { 46 | fmt.Println(err) 47 | return 48 | } 49 | defer func() { 50 | err := ws.Close() 51 | if err != nil { 52 | fmt.Println(err) 53 | } 54 | }() 55 | 56 | // 生成唯一ID 57 | id := snow.Node().Generate() 58 | 59 | // 存储连接 60 | mu.Lock() 61 | Connections[int64(id)] = ws 62 | mu.Unlock() 63 | 64 | // 通知客户端其ID 65 | err = ws.WriteMessage(websocket.TextMessage, []byte("Your ID: "+strconv.FormatInt(int64(id), 10))) 66 | if err != nil { 67 | fmt.Println(err) 68 | return 69 | } 70 | 71 | // 处理WebSocket消息 72 | for { 73 | messageType, p, err := ws.ReadMessage() 74 | if err != nil { 75 | fmt.Println(err) 76 | mu.Lock() 77 | delete(Connections, int64(id)) 78 | mu.Unlock() 79 | return 80 | } 81 | switch messageType { 82 | case websocket.TextMessage: 83 | fmt.Printf("处理文本消息, %s\n", string(p)) 84 | err := ws.WriteMessage(websocket.TextMessage, p) 85 | if err != nil { 86 | fmt.Println(err) 87 | return 88 | } 89 | case websocket.BinaryMessage: 90 | fmt.Println("处理二进制消息") 91 | case websocket.CloseMessage: 92 | fmt.Println("关闭websocket连接") 93 | mu.Lock() 94 | delete(Connections, int64(id)) 95 | mu.Unlock() 96 | return 97 | case websocket.PingMessage: 98 | fmt.Println("处理ping消息") 99 | err := ws.WriteMessage(websocket.PongMessage, []byte("ping")) 100 | if err != nil { 101 | fmt.Println(err) 102 | return 103 | } 104 | case websocket.PongMessage: 105 | fmt.Println("处理pong消息") 106 | err := ws.WriteMessage(websocket.PongMessage, []byte("pong")) 107 | if err != nil { 108 | fmt.Println(err) 109 | return 110 | } 111 | default: 112 | fmt.Printf("未知消息类型: %d\n", messageType) 113 | return 114 | } 115 | } 116 | } 117 | 118 | // sendMessageToClient 发送消息给客户端 119 | func sendMessageToClient(id int64, message []byte) error { 120 | mu.Lock() 121 | defer mu.Unlock() 122 | if conn, ok := Connections[id]; ok { 123 | return conn.WriteMessage(websocket.TextMessage, message) 124 | } 125 | return fmt.Errorf("连接ID %v 不存在", id) 126 | } 127 | 128 | // Send 发送消息 129 | func Send(ctx *gin.Context) { 130 | var params struct { 131 | ID int64 `json:"id"` 132 | Message string `json:"message"` 133 | } 134 | if err := helper.VgoShouldBindJSON(ctx, ¶ms); err != nil { 135 | response.Fail(ctx, "参数错误", err.Error(), nil) 136 | return 137 | } 138 | err := sendMessageToClient(params.ID, []byte(params.Message)) 139 | if err != nil { 140 | response.Fail(ctx, err.Error(), nil) 141 | return 142 | } else { 143 | response.Success(ctx, "发送成功", nil) 144 | return 145 | } 146 | } 147 | 148 | // SendToAll 发送所有消息 149 | func SendToAll(ctx *gin.Context) { 150 | var params struct { 151 | Type string `json:"type"` 152 | KeyWords string `json:"key_words"` 153 | TimeOut uint64 `json:"time_out"` 154 | } 155 | if err := helper.VgoShouldBindJSON(ctx, ¶ms); err != nil { 156 | response.Fail(ctx, "参数错误", err.Error(), nil) 157 | return 158 | } 159 | 160 | byteParams, err := json.Marshal(params) 161 | if err != nil { 162 | response.Fail(ctx, "参数错误", err.Error(), nil) 163 | return 164 | } 165 | 166 | mu.Lock() 167 | defer mu.Unlock() 168 | for id, conn := range Connections { 169 | if err := conn.WriteMessage(websocket.TextMessage, byteParams); err != nil { 170 | fmt.Printf("发送消息到客户端 %d 失败: %v\n", id, err) 171 | } 172 | } 173 | 174 | response.Success(ctx, "发送成功", nil) 175 | } 176 | -------------------------------------------------------------------------------- /app/Ws/testWs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | WebSocket 客户端 8 | 31 | 32 | 33 | 34 |

WebSocket 客户端

35 |
36 | 37 | 38 | 39 | 40 |
41 |
42 |
43 | 44 |

群控:

45 | 46 | 47 | 51 |
52 |
53 | 54 | 55 |
56 |
57 | 58 | 59 | 60 | 61 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /asynq.yml: -------------------------------------------------------------------------------- 1 | uri: 127.0.0.1:6379 2 | db: 12 3 | username: 4 | password: 5 | -------------------------------------------------------------------------------- /bootstrap/bootstrap.go: -------------------------------------------------------------------------------- 1 | package bootstrap 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gin-gonic/gin" 6 | "runtime" 7 | "strconv" 8 | "time" 9 | "vgo/core/db" 10 | "vgo/core/global" 11 | "vgo/core/log" 12 | "vgo/core/middle" 13 | "vgo/core/middle/auth" 14 | "vgo/core/queue" 15 | "vgo/core/redis" 16 | "vgo/core/snow" 17 | "vgo/route" 18 | ) 19 | 20 | func Start() { 21 | global.App.Config.InitConfig() 22 | appConfig := global.App.Config 23 | 24 | appConf := appConfig.App 25 | cpuNum, _ := strconv.Atoi(appConfig.App.CpuNum) 26 | realCpuNum := runtime.NumCPU() 27 | if cpuNum > realCpuNum { 28 | cpuNum = realCpuNum 29 | } 30 | if appConf.Env == "dev" { 31 | if cpuNum > 0 { 32 | runtime.GOMAXPROCS(cpuNum) 33 | fmt.Printf("计算机核数: %v个,调用:%v个\n", realCpuNum, cpuNum) 34 | } else { 35 | runtime.GOMAXPROCS(realCpuNum) 36 | fmt.Printf("计算机核数: %v个,调用:%v个\n", realCpuNum, cpuNum) 37 | } 38 | } 39 | 40 | // 初始化数据库 41 | db.InitCon() 42 | 43 | // 初始化redis 44 | redis.InitCon() 45 | 46 | // 初始化日志 47 | go func() { 48 | log.InitLog() 49 | }() 50 | 51 | // 初始化雪花算法 52 | go func() { 53 | snow.InitSnowflake() 54 | }() 55 | 56 | // 是否需要队列 57 | queueConf := appConfig.QueueConf 58 | if queueConf.Enable == 1 { 59 | // 运行 Asynq 任务队列 60 | go func() { 61 | queue.InitQueue() 62 | }() 63 | } 64 | 65 | // jwt相关配置 66 | jwtConf := appConfig.JwtConf 67 | auth.AdminTokenExpireDuration = time.Duration(jwtConf.AdminTimeout) * time.Hour 68 | auth.ApiTokenExpireDuration = time.Duration(jwtConf.ApiTimeout) * time.Hour 69 | auth.AdminSecret = []byte(jwtConf.AdminKey) 70 | auth.ApiSecret = []byte(jwtConf.ApiKey) 71 | 72 | app := gin.Default() 73 | // 请求日志 74 | if appConf.RequestLog == 1 { 75 | app.Use(middle.RequestLogger()) 76 | } 77 | if appConf.Env == "pro" { 78 | gin.SetMode(gin.ReleaseMode) 79 | } 80 | 81 | // 静态资源 82 | app.Static("/storage", "storage") 83 | 84 | // 收集路由 85 | route.CollectRoute(app) 86 | err := app.Run(appConf.Host + ":" + appConf.Port) 87 | if err != nil { 88 | fmt.Println(err) 89 | return 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /config.yaml: -------------------------------------------------------------------------------- 1 | #App配置 2 | app: 3 | host: 0.0.0.0 4 | env: dev #环境状态:dev=开发,pro=生产 5 | port: 8080 6 | version: 1.0.0 7 | requestLog: 0 8 | lang: zh-cn 9 | cpuNum: 12 10 | imgDomain: http://localhost:8080 11 | videoDomain: http://localhost:8080 12 | apiOrigins: http://localhost:8080,http://127.0.0.1:5500 13 | #Mysql数据库配置 14 | dbConf: 15 | driver: mysql 16 | hostname: 127.0.0.1 17 | hostPort: 3306 18 | database: go_study 19 | username: root 20 | password: 110112... 21 | #Redis数据库配置 22 | redisConf: 23 | hostname: 127.0.0.1 24 | hostPort: 6379 25 | username: 26 | password: 27 | db: 11 28 | #队列配置 29 | queueConf: 30 | enable: 0 31 | hostname: 127.0.0.1 32 | hostPort: 6379 33 | username: 34 | password: 35 | db: 12 36 | concurrency: 10 37 | #JWT配置 38 | jwtConf: 39 | adminKey: pDxCHgMzlarDuWFeDVSlQNHyZVoxeBfJsGVz 40 | adminSingleLogin: 1 41 | apikey: pDxCHgMzlarDuWFeDVSlQNHyZVssssssssszxz 42 | apiSingleLogin: 1 43 | adminTimeout: 24 44 | apiTimeout: 24 45 | #Snowflake配置 46 | snowflakeConf: 47 | node: 1 -------------------------------------------------------------------------------- /core/Tpl/Api/Tpl.go: -------------------------------------------------------------------------------- 1 | package Tpl 2 | -------------------------------------------------------------------------------- /core/Tpl/Bapi/Tpl.go: -------------------------------------------------------------------------------- 1 | package Tpl 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "strconv" 6 | TplModel "vgo/core/Tpl/Model" 7 | "vgo/core/db" 8 | "vgo/core/response" 9 | ) 10 | 11 | // Index 列表 12 | func Index(ctx *gin.Context) { 13 | var res []TplModel.Tpl 14 | var total int64 15 | pageNo, err := strconv.Atoi(ctx.DefaultQuery("pageNum", "1")) 16 | if err != nil { 17 | response.Fail(ctx, "页码参数无效", nil) 18 | return 19 | } 20 | Size, err := strconv.Atoi(ctx.DefaultQuery("pageSize", "10")) 21 | if err != nil { 22 | response.Fail(ctx, "每页大小参数无效", nil) 23 | return 24 | } 25 | if err := db.Con().Model(&TplModel.Tpl{}).Count(&total).Error; err != nil { 26 | response.Fail(ctx, "数据库查询失败", err.Error()) 27 | return 28 | } 29 | if err := db.Con().Order("id desc").Offset((pageNo - 1) * Size).Limit(Size).Find(&res).Error; err != nil { 30 | response.Fail(ctx, "数据库查询失败", err.Error()) 31 | return 32 | } 33 | totalPage := int(total) / Size 34 | if int(total)%Size != 0 { 35 | totalPage++ 36 | } 37 | response.Success(ctx, "成功", gin.H{ 38 | "pageNum": pageNo, 39 | "total": total, 40 | "pageSize": Size, 41 | "list": res, 42 | }, nil) 43 | } 44 | 45 | // Create 创建 46 | func Create(ctx *gin.Context) { 47 | var product TplModel.Tpl 48 | if err := helper.VgoShouldBindJSON(&product); err != nil { 49 | response.Fail(ctx, "参数错误", err.Error(), nil) 50 | return 51 | } 52 | db.Con().Create(&product) 53 | response.Success(ctx, "成功", product, nil) 54 | } 55 | 56 | // Update 更新 57 | func Update(ctx *gin.Context) { 58 | var tpl TplModel.Tpl 59 | if err := helper.VgoShouldBindJSON(&tpl); err != nil { 60 | response.Fail(ctx, "参数错误", err.Error(), nil) 61 | return 62 | } 63 | if err := db.Con().Model(&TplModel.Tpl{}).Where("id = ?", tpl.ID).Updates(tpl).Error; err != nil { 64 | response.Fail(ctx, "更新失败", err.Error()) 65 | return 66 | } 67 | response.Success(ctx, "成功", nil, nil) 68 | } 69 | 70 | // Delete 删除 71 | func Delete(ctx *gin.Context) { 72 | var ids struct { 73 | ID []int64 `json:"id"` 74 | } 75 | if err := helper.VgoShouldBindJSON(&ids); err != nil { 76 | response.Fail(ctx, "参数错误", err.Error(), nil) 77 | return 78 | } 79 | if err := db.Con().Delete(&TplModel.Tpl{}, "id in (?)", ids.ID).Error; err != nil { 80 | response.Fail(ctx, "删除失败", err.Error()) 81 | return 82 | } 83 | response.Success(ctx, "成功", nil, nil) 84 | } 85 | -------------------------------------------------------------------------------- /core/Tpl/Model/Tpl.go: -------------------------------------------------------------------------------- 1 | package Tpl 2 | 3 | import Common "vgo/app/Common/Model" 4 | 5 | // Tpl %模型名% 6 | type Tpl struct { 7 | Common.Model 8 | } 9 | -------------------------------------------------------------------------------- /core/Tpl/Router/Tpl.go: -------------------------------------------------------------------------------- 1 | package Tpl 2 | 3 | import ( 4 | "vgo/core/Tpl/Bapi" 5 | "vgo/core/router" 6 | ) 7 | 8 | func CollectRoutes() []router.BaseRoute { 9 | return []router.BaseRoute{ 10 | {"GET", "/tpl", Tpl.Index}, 11 | {"POST", "/tpl/create", Tpl.Create}, 12 | {"POST", "/tpl/update", Tpl.Update}, 13 | {"POST", "/tpl/delete", Tpl.Delete}, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /core/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "fmt" 5 | "github.com/fsnotify/fsnotify" 6 | "github.com/spf13/viper" 7 | "os" 8 | ) 9 | 10 | // App 应用配置 11 | type App struct { 12 | Host string `yaml:"host"` 13 | Port string `yaml:"port"` 14 | CouNum string `yaml:"cpu_num"` 15 | Version string `yaml:"version"` 16 | Env string `yaml:"env"` 17 | RequestLog int `yaml:"requestLog"` 18 | Lang string `yaml:"lang"` 19 | CpuNum string `yaml:"cpuNum"` 20 | ImgDomain string `yaml:"imgDomain"` 21 | VideoDomain string `yaml:"videoDomain"` 22 | ApiOrigins string `yaml:"apiOrigins"` 23 | } 24 | 25 | // DbConf 数据数据库配置 26 | type DbConf struct { 27 | Driver string `yaml:"driver"` 28 | Hostname string `yaml:"hostname"` 29 | HostPort string `yaml:"hostPort"` 30 | Username string `yaml:"username"` 31 | Password string `yaml:"password"` 32 | Database string `yaml:"database"` 33 | } 34 | 35 | // RedisConf Redis配置 36 | type RedisConf struct { 37 | Hostname string `yaml:"hostname"` 38 | HostPort string `yaml:"hostPort"` 39 | UserName string `yaml:"username"` 40 | Password string `yaml:"password"` 41 | DB int `yaml:"db"` 42 | } 43 | 44 | // QueueConf Queue配置 45 | type QueueConf struct { 46 | Enable int `yaml:"enable"` 47 | Hostname string `yaml:"hostname"` 48 | HostPort string `yaml:"hostPort"` 49 | UserName string `yaml:"username"` 50 | Password string `yaml:"password"` 51 | DB int `yaml:"db"` 52 | Concurrency int `yaml:"concurrency"` 53 | } 54 | 55 | // JwtConf Jwt配置 56 | type JwtConf struct { 57 | AdminKey string `yaml:"adminKey"` 58 | AdminSingleLogin int `yaml:"adminSingleLogin"` 59 | ApiKey string `yaml:"apikey"` 60 | ApiSingleLogin int `yaml:"apiSingleLogin"` 61 | AdminTimeout int64 `yaml:"adminTimeout"` 62 | ApiTimeout int64 `yaml:"apiTimeout"` 63 | } 64 | 65 | // SnowflakeConf Snowflake配置 66 | type SnowflakeConf struct { 67 | Node int64 `yaml:"node"` 68 | } 69 | 70 | type Config struct { 71 | DbConf DbConf `yaml:"dbConf"` 72 | RedisConf RedisConf `yaml:"redisConf"` 73 | QueueConf QueueConf `yaml:"queueConf"` 74 | JwtConf JwtConf `yaml:"jwtConf"` 75 | SnowflakeConf SnowflakeConf `yaml:"snowflakeConf"` 76 | App App `yaml:"app"` 77 | } 78 | 79 | func (config *Config) InitConfig() *Config { 80 | path, err := os.Getwd() 81 | if err != nil { 82 | panic(err) 83 | } 84 | 85 | vp := viper.New() 86 | vp.AddConfigPath(path) //设置读取的文件路径 87 | vp.SetConfigName("config") //设置读取的文件名 88 | vp.SetConfigType("yaml") //设置文件的类型 89 | if err := vp.ReadInConfig(); err != nil { //尝试进行配置读取 90 | panic(err) 91 | } 92 | // 监听配置文件 93 | vp.WatchConfig() 94 | vp.OnConfigChange(func(in fsnotify.Event) { 95 | fmt.Println("检测到配置文件变化:", in.Name) 96 | if err := vp.Unmarshal(&config); err != nil { // 重载配置 97 | fmt.Println(err) 98 | } 99 | }) 100 | err = vp.Unmarshal(&config) 101 | if err != nil { 102 | panic(err) 103 | } 104 | return config 105 | 106 | } 107 | -------------------------------------------------------------------------------- /core/db/db.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "fmt" 5 | "gorm.io/driver/mysql" 6 | "gorm.io/gorm" 7 | "sync" 8 | "time" 9 | "vgo/core/global" 10 | ) 11 | 12 | // db 数据库连接 13 | var ( 14 | db *gorm.DB 15 | dbLock sync.RWMutex 16 | ) 17 | 18 | // InitCon 初始化数据库连接 19 | func InitCon() { 20 | dbConf := global.App.Config.DbConf 21 | dsn := fmt.Sprintf("%v:%v@tcp(%v:%v)/%v?charset=utf8mb4&parseTime=True&loc=Local", 22 | dbConf.Username, dbConf.Password, dbConf.Hostname, dbConf.HostPort, dbConf.Database) 23 | d, err := gorm.Open(mysql.Open(dsn), &gorm.Config{ 24 | DisableForeignKeyConstraintWhenMigrating: true, // 禁用外键约束 25 | }) 26 | if err != nil { 27 | fmt.Println("数据库连接错误:,", err) 28 | panic("请检查数据库连接!!") 29 | } 30 | db = d 31 | sqlDB, err := db.DB() 32 | if err != nil { 33 | fmt.Println("获取数据库连接池失败:", err) 34 | panic("请检查数据库连接池配置!!") 35 | } 36 | sqlDB.SetMaxIdleConns(10) 37 | sqlDB.SetMaxOpenConns(500) 38 | sqlDB.SetConnMaxLifetime(time.Hour) 39 | } 40 | 41 | // Con 获取数据连接 42 | func Con() *gorm.DB { 43 | dbLock.RLock() 44 | defer dbLock.RUnlock() 45 | return db 46 | } 47 | -------------------------------------------------------------------------------- /core/global/app.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "github.com/spf13/viper" 5 | "vgo/core/config" 6 | ) 7 | 8 | type Application struct { 9 | ConfigViper *viper.Viper 10 | Config config.Config 11 | } 12 | 13 | var App = new(Application) 14 | -------------------------------------------------------------------------------- /core/helper/helper.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "github.com/gin-gonic/gin" 7 | "io" 8 | "strings" 9 | ) 10 | 11 | // VgoShouldBindJSON 处理请求参数 12 | func VgoShouldBindJSON(ctx *gin.Context, obj interface{}) error { 13 | data, err := ctx.GetRawData() 14 | if err != nil { 15 | return err 16 | } 17 | data, err = trimSpaces(data) 18 | if err != nil { 19 | return err 20 | } 21 | ctx.Request.Body = io.NopCloser(bytes.NewBuffer(data)) 22 | if err := ctx.ShouldBindJSON(obj); err != nil { 23 | return err 24 | } 25 | return nil 26 | } 27 | 28 | // trimSpaces 去除请求参数中的空格 29 | func trimSpaces(data []byte) ([]byte, error) { 30 | var m map[string]interface{} 31 | if err := json.Unmarshal(data, &m); err != nil { 32 | return nil, err 33 | } 34 | for k, v := range m { 35 | switch v := v.(type) { 36 | case string: 37 | m[k] = strings.TrimSpace(v) 38 | } 39 | } 40 | return json.Marshal(m) 41 | } 42 | -------------------------------------------------------------------------------- /core/log/log.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "fmt" 5 | "go.uber.org/zap" 6 | "go.uber.org/zap/zapcore" 7 | "os" 8 | ) 9 | 10 | // logger 日志类 11 | var logger *zap.Logger 12 | 13 | // InitLog 初始化日志 14 | func InitLog() { 15 | config := zap.NewProductionEncoderConfig() 16 | config.EncodeTime = zapcore.ISO8601TimeEncoder 17 | fileEncoder := zapcore.NewJSONEncoder(config) 18 | defaultLogLevel := zapcore.DebugLevel 19 | path, err := os.Getwd() 20 | if err != nil { 21 | panic("日志目录获取失败" + err.Error()) 22 | } 23 | logFile, _ := os.OpenFile(path+"/storage/logs/vgo.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 06666) 24 | writer := zapcore.AddSync(logFile) 25 | lg := zap.New( 26 | zapcore.NewCore(fileEncoder, writer, defaultLogLevel), 27 | zap.AddCaller(), 28 | zap.AddStacktrace(zapcore.ErrorLevel), 29 | ) 30 | defer func(lg *zap.Logger) { 31 | err := lg.Sync() 32 | if err != nil { 33 | fmt.Println("日志初始化错误", err) 34 | } 35 | }(lg) 36 | logger = lg 37 | } 38 | 39 | // GetLogger 获取日志类 40 | func GetLogger() *zap.Logger { 41 | return logger 42 | } 43 | -------------------------------------------------------------------------------- /core/middle/RateLimiter.go: -------------------------------------------------------------------------------- 1 | package middle 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "golang.org/x/time/rate" 6 | "time" 7 | "vgo/core/response" 8 | ) 9 | 10 | // RateLimiter 限流中间件 11 | func RateLimiter(maxRequests int, timeWindow time.Duration) gin.HandlerFunc { 12 | limiter := rate.NewLimiter(rate.Every(timeWindow/time.Duration(maxRequests)), maxRequests) 13 | return func(c *gin.Context) { 14 | if limiter.Allow() == false { 15 | response.TooManyRequests(c, "请求频繁!", nil) 16 | c.Abort() 17 | return 18 | } 19 | c.Next() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /core/middle/RequestLogger.go: -------------------------------------------------------------------------------- 1 | package middle 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "go.uber.org/zap" 6 | "time" 7 | "vgo/core/log" 8 | ) 9 | 10 | // RequestLogger 请求日志 11 | func RequestLogger() gin.HandlerFunc { 12 | return func(c *gin.Context) { 13 | start := time.Now() 14 | 15 | defer func() { 16 | end := time.Now() 17 | latency := end.Sub(start).String() 18 | 19 | requestParams := make(map[string]interface{}) 20 | 21 | for key, values := range c.Request.URL.Query() { 22 | if len(values) > 0 { 23 | requestParams[key] = values[0] 24 | } 25 | } 26 | 27 | err := c.Request.ParseForm() 28 | if err == nil { 29 | for key, values := range c.Request.PostForm { 30 | if len(values) > 0 { 31 | requestParams[key] = values[0] 32 | } 33 | } 34 | } 35 | 36 | clientIP := c.ClientIP() 37 | 38 | fields := []zap.Field{ 39 | zap.String("clientIP", clientIP), 40 | zap.String("method", c.Request.Method), 41 | zap.String("path", c.Request.URL.Path), 42 | zap.Int("status", c.Writer.Status()), 43 | zap.Any("params", requestParams), 44 | zap.String("latency", latency), 45 | } 46 | log.GetLogger().Info("Request handled", fields...) 47 | }() 48 | c.Next() 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /core/middle/auth/Claims.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "github.com/golang-jwt/jwt/v5" 5 | ) 6 | 7 | // AdminClaims 后台管理用户的Claims 8 | type AdminClaims struct { 9 | UserID uint64 `json:"user_id"` 10 | Role []string `json:"role"` 11 | Super int `json:"super"` 12 | jwt.RegisteredClaims 13 | } 14 | 15 | // UserClaims 前台用户的Claims 16 | type UserClaims struct { 17 | UserID uint64 `json:"user_id"` 18 | jwt.RegisteredClaims 19 | } 20 | -------------------------------------------------------------------------------- /core/middle/auth/Func.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/golang-jwt/jwt/v5" 6 | "strconv" 7 | "strings" 8 | "time" 9 | "vgo/core/global" 10 | "vgo/core/redis" 11 | ) 12 | 13 | var ( 14 | AdminTokenExpireDuration time.Duration 15 | ApiTokenExpireDuration time.Duration 16 | AdminSecret []byte 17 | ApiSecret []byte 18 | AdminTokenBlacklist = map[string]bool{} // 不使用redis设置黑名单时使用 19 | ApiTokenBlacklist = map[string]bool{} // 不使用redis设置黑名单时使用 20 | ) 21 | 22 | // GenAdminToken 生成后台管理用户的JWT Token 23 | func GenAdminToken(ctx *gin.Context, userID uint64, role []string, super int) (map[string]string, error) { 24 | claims := AdminClaims{ 25 | UserID: userID, 26 | Role: role, 27 | Super: super, 28 | RegisteredClaims: jwt.RegisteredClaims{ 29 | ExpiresAt: jwt.NewNumericDate(time.Now().Add(AdminTokenExpireDuration)), 30 | Issuer: "vgo-admin", 31 | }, 32 | } 33 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 34 | tokenString, _ := token.SignedString(AdminSecret) 35 | 36 | JwtConf := global.App.Config.JwtConf 37 | // 是否单设备登录 38 | if JwtConf.AdminSingleLogin == 1 { 39 | err := redis.Con().Set(ctx, "admin_token"+strconv.Itoa(int(userID)), tokenString, AdminTokenExpireDuration).Err() 40 | if err != nil { 41 | return nil, err 42 | } 43 | } else { 44 | err := redis.Con().LPush(ctx, "admin_token_list"+strconv.Itoa(int(userID)), tokenString).Err() 45 | if err != nil { 46 | return nil, err 47 | } 48 | err = redis.Con().Expire(ctx, "admin_token_list"+strconv.Itoa(int(userID)), AdminTokenExpireDuration).Err() 49 | if err != nil { 50 | return nil, err 51 | } 52 | } 53 | // 格式化时间为目标格式 54 | formattedTime := claims.RegisteredClaims.ExpiresAt.Time.Format("2006-01-02 15:04:05") 55 | return map[string]string{ 56 | "expires_at": formattedTime, 57 | "access_token": tokenString, 58 | }, nil 59 | } 60 | 61 | // DelAdminToken 删除后台管理用户的JWT Token 62 | func DelAdminToken(ctx *gin.Context, userID uint64) error { 63 | JwtConf := global.App.Config.JwtConf 64 | // 是否单设备登录 65 | if JwtConf.AdminSingleLogin == 1 { 66 | err := redis.Con().Del(ctx, "admin_token"+strconv.Itoa(int(userID))).Err() 67 | if err != nil { 68 | return nil 69 | } 70 | // 同时删除其他token 71 | redis.Con().Del(ctx, "admin_token_list"+strconv.Itoa(int(userID))) 72 | } else { 73 | authHeader := ctx.GetHeader("Authorization") 74 | parts := strings.SplitN(authHeader, " ", 2) 75 | if len(parts) != 2 || parts[0] != "Bearer" { 76 | return nil 77 | } 78 | tokenString := parts[1] 79 | err := redis.Con().LRem(ctx, "admin_token_list"+strconv.Itoa(int(userID)), 0, tokenString).Err() 80 | if err != nil { 81 | return nil 82 | } 83 | } 84 | return nil 85 | } 86 | 87 | // ParseAdminToken 解析后台管理用户的JWT Token 88 | func ParseAdminToken(tokenString string) (*AdminClaims, error) { 89 | token, err := jwt.ParseWithClaims(tokenString, &AdminClaims{}, func(token *jwt.Token) (interface{}, error) { 90 | return AdminSecret, nil 91 | }) 92 | if err != nil { 93 | return nil, err 94 | } 95 | if claims, ok := token.Claims.(*AdminClaims); ok && token.Valid { 96 | return claims, nil 97 | } 98 | return nil, jwt.ErrSignatureInvalid 99 | } 100 | 101 | // PutAdminInvalidateToken 将token加入黑名单 102 | func PutAdminInvalidateToken(ctx *gin.Context, tokenString string) { 103 | //AdminTokenBlacklist[tokenString] = true // 不使用redis 104 | redis.Con().Set(ctx, "admin_blacklist"+tokenString, tokenString, AdminTokenExpireDuration) 105 | } 106 | 107 | // AssertIsTokenInvalid 检查token是否在黑名单中 108 | func AssertIsTokenInvalid(ctx *gin.Context, tokenString string) bool { 109 | //return AdminTokenBlacklist[tokenString] // 不使用redis 110 | val := redis.Con().Get(ctx, "admin_blacklist"+tokenString) 111 | return val.Val() == tokenString 112 | } 113 | 114 | // GenUserToken 生成前台用户的JWT Token 115 | func GenUserToken(ctx *gin.Context, userID uint64) (map[string]string, error) { 116 | claims := UserClaims{ 117 | UserID: userID, 118 | RegisteredClaims: jwt.RegisteredClaims{ 119 | ExpiresAt: jwt.NewNumericDate(time.Now().Add(ApiTokenExpireDuration)), 120 | Issuer: "user", 121 | }, 122 | } 123 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 124 | tokenString, _ := token.SignedString(ApiSecret) 125 | JwtConf := global.App.Config.JwtConf 126 | // 是否单设备登录 127 | if JwtConf.ApiSingleLogin == 1 { 128 | err := redis.Con().Set(ctx, "api_token"+strconv.Itoa(int(userID)), tokenString, ApiTokenExpireDuration).Err() 129 | if err != nil { 130 | return nil, err 131 | } 132 | } else { 133 | err := redis.Con().LPush(ctx, "api_token_list"+strconv.Itoa(int(userID)), tokenString).Err() 134 | if err != nil { 135 | return nil, err 136 | } 137 | err = redis.Con().Expire(ctx, "api_token_list"+strconv.Itoa(int(userID)), AdminTokenExpireDuration).Err() 138 | if err != nil { 139 | return nil, err 140 | } 141 | } 142 | // 格式化时间为目标格式 143 | formattedTime := claims.RegisteredClaims.ExpiresAt.Time.Format("2006-01-02 15:04:05") 144 | return map[string]string{ 145 | "expires_at": formattedTime, 146 | "token": tokenString, 147 | }, nil 148 | } 149 | 150 | // ParseUserToken 解析前台用户的JWT Token 151 | func ParseUserToken(tokenString string) (*UserClaims, error) { 152 | token, err := jwt.ParseWithClaims(tokenString, &UserClaims{}, func(token *jwt.Token) (interface{}, error) { 153 | return ApiSecret, nil 154 | }) 155 | if err != nil { 156 | return nil, err 157 | } 158 | if claims, ok := token.Claims.(*UserClaims); ok && token.Valid { 159 | return claims, nil 160 | } 161 | return nil, jwt.ErrSignatureInvalid 162 | } 163 | 164 | // PutApiTokenInvalidateToken 将token加入黑名单 165 | func PutApiTokenInvalidateToken(ctx *gin.Context, tokenString string) { 166 | //ApiTokenBlacklist[tokenString] = true // 不使用redis 167 | redis.Con().Set(ctx, "api_blacklist"+tokenString, tokenString, ApiTokenExpireDuration) 168 | } 169 | 170 | // AssertApiTokenIsInvalid 检查token是否在黑名单中 171 | func AssertApiTokenIsInvalid(ctx *gin.Context, tokenString string) bool { 172 | //return ApiTokenBlacklist[tokenString] // 不使用redis 173 | val := redis.Con().Get(ctx, "api_blacklist"+tokenString) 174 | return val.Val() == tokenString 175 | } 176 | -------------------------------------------------------------------------------- /core/middle/auth/JwtAuth.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "strconv" 6 | "strings" 7 | "vgo/core/global" 8 | "vgo/core/redis" 9 | "vgo/core/response" 10 | "vgo/core/trans" 11 | ) 12 | 13 | // AdminAuthMiddleware JWT验证中间件 14 | func AdminAuthMiddleware() gin.HandlerFunc { 15 | return func(c *gin.Context) { 16 | authHeader := c.GetHeader("Authorization") 17 | parts := strings.SplitN(authHeader, " ", 2) 18 | if !(len(parts) == 2 && parts[0] == "Bearer") { 19 | // Header format is invalid 20 | response.NotLogin(c, trans.Trans("Token无效")+"00", nil) 21 | c.Abort() 22 | return 23 | } 24 | tokenString := parts[1] 25 | if tokenString == "" { 26 | // Header is missing 27 | response.NotLogin(c, trans.Trans("Token无效")+"01", nil) 28 | c.Abort() 29 | return 30 | } 31 | claims, err := ParseAdminToken(tokenString) 32 | if err != nil { 33 | //解析Fail 34 | response.NotLogin(c, trans.Trans("Token无效")+"02", nil) 35 | c.Abort() 36 | return 37 | } 38 | 39 | if AssertIsTokenInvalid(c, tokenString) { 40 | //黑名单 41 | response.NotLogin(c, trans.Trans("Token无效")+"03", nil) 42 | c.Abort() 43 | return 44 | } 45 | JwtConf := global.App.Config.JwtConf 46 | // 是否单设备登录 47 | if JwtConf.AdminSingleLogin == 1 { 48 | redisToken := redis.Con().Get(c, "admin_token"+strconv.Itoa(int(claims.UserID))) 49 | if redisToken == nil || redisToken.Val() != tokenString { 50 | // Token is invalid --Bapi-- redis00 51 | response.NotLogin(c, trans.Trans("Token无效")+"04", nil) 52 | c.Abort() 53 | return 54 | } 55 | } else { 56 | redisToken := redis.Con().LRange(c, "admin_token_list"+strconv.Itoa(int(claims.UserID)), 0, -1) 57 | if redisToken == nil || len(redisToken.Val()) == 0 { 58 | response.NotLogin(c, trans.Trans("Token无效")+"05", nil) 59 | c.Abort() 60 | return 61 | } 62 | if !strings.Contains(strings.Join(redisToken.Val(), ""), tokenString) { 63 | response.NotLogin(c, trans.Trans("Token无效")+"06", nil) 64 | c.Abort() 65 | return 66 | } 67 | } 68 | c.Set("userID", claims.UserID) 69 | c.Set("role", claims.Role) 70 | c.Set("super", claims.Super) 71 | c.Next() 72 | } 73 | } 74 | 75 | // UserAuthMiddleware 前台用户的JWT验证中间件 76 | func UserAuthMiddleware() gin.HandlerFunc { 77 | return func(c *gin.Context) { 78 | authHeader := c.GetHeader("Authorization") 79 | parts := strings.SplitN(authHeader, " ", 2) 80 | if !(len(parts) == 2 && parts[0] == "Bearer") { 81 | // Header format is invalid 82 | response.NotLogin(c, trans.Trans("Token无效")+"00", nil) 83 | c.Abort() 84 | return 85 | } 86 | tokenString := parts[1] 87 | if tokenString == "" { 88 | // Header is missing 89 | response.NotLogin(c, trans.Trans("Token无效")+"01", nil) 90 | c.Abort() 91 | return 92 | } 93 | 94 | claims, err := ParseUserToken(tokenString) 95 | if err != nil { 96 | // 解析Fail 97 | response.NotLogin(c, trans.Trans("Token无效")+"02", nil) 98 | c.Abort() 99 | return 100 | } 101 | 102 | if AssertApiTokenIsInvalid(c, tokenString) { 103 | // 黑名单 104 | response.NotLogin(c, trans.Trans("Token无效")+"03", nil) 105 | c.Abort() 106 | return 107 | } 108 | 109 | JwtConf := global.App.Config.JwtConf 110 | // 是否单设备登录 111 | if JwtConf.ApiSingleLogin == 1 { 112 | redisToken := redis.Con().Get(c, "api_token"+strconv.FormatInt(int64(claims.UserID), 10)) 113 | if redisToken == nil || redisToken.Val() != tokenString { 114 | //--api-- redis00 115 | response.NotLogin(c, trans.Trans("Token无效")+"04", nil) 116 | c.Abort() 117 | return 118 | } 119 | } else { 120 | redisToken := redis.Con().LRange(c, "api_token_list"+strconv.FormatInt(int64(claims.UserID), 10), 0, -1) 121 | if redisToken == nil || len(redisToken.Val()) == 0 { 122 | //--api-- redis0 123 | response.NotLogin(c, trans.Trans("Token无效")+"05", nil) 124 | c.Abort() 125 | return 126 | } 127 | if !strings.Contains(strings.Join(redisToken.Val(), ""), tokenString) { 128 | // --api-- redis02 129 | response.NotLogin(c, trans.Trans("Token无效")+"06", nil) 130 | c.Abort() 131 | return 132 | } 133 | } 134 | c.Set("userID", claims.UserID) 135 | c.Next() 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /core/middle/casbin/Check.go: -------------------------------------------------------------------------------- 1 | package casbin 2 | 3 | import ( 4 | "github.com/casbin/casbin/v2" 5 | "github.com/gin-gonic/gin" 6 | "regexp" 7 | "vgo/core/response" 8 | ) 9 | 10 | // CheckMiddleware 权限检查中间件 11 | func CheckMiddleware(e *casbin.Enforcer) gin.HandlerFunc { 12 | return func(c *gin.Context) { 13 | super := c.GetInt("super") // 超级管理员 14 | if super == 1 { 15 | c.Next() 16 | return 17 | } 18 | obj := c.Request.URL.Path 19 | // 排除数据源权限判断 20 | re := regexp.MustCompile(`DataSource`) 21 | if re.MatchString(obj) { 22 | c.Next() 23 | return 24 | } 25 | sub := c.GetStringSlice("role") // 获取角色 26 | for _, v := range sub { 27 | if ok, _ := e.Enforce(v, obj); ok { 28 | c.Next() 29 | return 30 | } 31 | } 32 | response.Forbidden(c, "无权限访问", nil) 33 | c.Abort() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /core/middle/casbin/Enforcer.go: -------------------------------------------------------------------------------- 1 | package casbin 2 | 3 | import ( 4 | "fmt" 5 | "github.com/casbin/casbin/v2" 6 | gormadapter "github.com/casbin/gorm-adapter/v3" 7 | rediswatcher "github.com/casbin/redis-watcher/v2" 8 | rds "github.com/redis/go-redis/v9" 9 | "os" 10 | "vgo/core/db" 11 | "vgo/core/global" 12 | ) 13 | 14 | // SetupCasbin 初始化Casbin 15 | func SetupCasbin() *casbin.Enforcer { 16 | adapter, err := gormadapter.NewAdapterByDB(db.Con()) 17 | if err != nil { 18 | panic(err) 19 | } 20 | path, err := os.Getwd() 21 | if err != nil { 22 | panic(err) 23 | } 24 | enforcer, err := casbin.NewEnforcer(path+"/rbac.conf", adapter) 25 | if err != nil { 26 | panic(err) 27 | } 28 | 29 | // 初始化Redis Watcher 30 | redisConf := global.App.Config.RedisConf 31 | addr := fmt.Sprintf("%v:%v", redisConf.Hostname, redisConf.HostPort) 32 | watcher, err := rediswatcher.NewWatcher(addr, rediswatcher.WatcherOptions{ 33 | Channel: "/vgo-casbin", // 频道名称 34 | Options: rds.Options{ 35 | Username: redisConf.UserName, // 用户名 36 | Password: redisConf.Password, // 密码 37 | }, 38 | }) 39 | 40 | // 设置Watcher 41 | err = enforcer.SetWatcher(watcher) 42 | 43 | // 设置回调函数,当策略发生变化时重新加载策略 44 | err = watcher.SetUpdateCallback(func(string) { 45 | err = enforcer.LoadPolicy() 46 | }) 47 | 48 | // 加载策略 49 | err = enforcer.LoadPolicy() 50 | if err != nil { 51 | panic(err) 52 | } 53 | return enforcer 54 | } 55 | -------------------------------------------------------------------------------- /core/queue/queue.go: -------------------------------------------------------------------------------- 1 | package queue 2 | 3 | import ( 4 | "fmt" 5 | "github.com/hibiken/asynq" 6 | "vgo/core/global" 7 | "vgo/job" 8 | ) 9 | 10 | // InitQueue 初始化队列 11 | func InitQueue() { 12 | queueConf := global.App.Config.QueueConf 13 | redisAddr := fmt.Sprintf("%v:%v", queueConf.Hostname, queueConf.HostPort) 14 | srv := asynq.NewServer( 15 | asynq.RedisClientOpt{ 16 | Addr: redisAddr, 17 | Username: queueConf.UserName, 18 | Password: queueConf.Password, 19 | DB: queueConf.DB, 20 | }, 21 | asynq.Config{ 22 | Concurrency: queueConf.Concurrency, 23 | Queues: map[string]int{ 24 | "critical": 6, 25 | "default": 3, 26 | "low": 1, 27 | }, 28 | }, 29 | ) 30 | mux := asynq.NewServeMux() 31 | SetupJobHandlers(mux) 32 | if err := srv.Run(mux); err != nil { 33 | fmt.Println("Failed to start server:", err) 34 | } 35 | } 36 | 37 | // SetupJobHandlers 注册任务处理器 38 | func SetupJobHandlers(mux *asynq.ServeMux) { 39 | for jobName, handler := range job.JobMaps { 40 | mux.HandleFunc(jobName, handler) 41 | } 42 | } 43 | 44 | // NewRedisClient 创建一个新的 asynq.Client 并返回 45 | func NewRedisClient() (*asynq.Client, error) { 46 | queueConf := global.App.Config.QueueConf 47 | redisAddr := fmt.Sprintf("%v:%v", queueConf.Hostname, queueConf.HostPort) 48 | client := asynq.NewClient(asynq.RedisClientOpt{ 49 | Addr: redisAddr, 50 | Username: queueConf.UserName, 51 | Password: queueConf.Password, 52 | DB: queueConf.DB, 53 | }) 54 | return client, nil 55 | } 56 | 57 | // CloseRedisClient 关闭 asynq.Client 58 | func CloseRedisClient(client *asynq.Client) { 59 | if err := client.Close(); err != nil { 60 | fmt.Println("Failed to close client:", err) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /core/redis/redis.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | import ( 4 | "fmt" 5 | goRedis "github.com/redis/go-redis/v9" 6 | "sync" 7 | "vgo/core/global" 8 | ) 9 | 10 | // redis redis连接 11 | var ( 12 | redis *goRedis.Client 13 | redisLock sync.RWMutex 14 | ) 15 | 16 | // InitCon 初始化redis连接 17 | func InitCon() { 18 | redisConf := global.App.Config.RedisConf 19 | addr := fmt.Sprintf("%v:%v", redisConf.Hostname, redisConf.HostPort) 20 | d := goRedis.NewClient(&goRedis.Options{ 21 | Addr: addr, 22 | Username: redisConf.UserName, 23 | Password: redisConf.Password, 24 | DB: redisConf.DB, 25 | }) 26 | redis = d 27 | } 28 | 29 | // Con 获取redis连接 30 | func Con() *goRedis.Client { 31 | redisLock.RLock() 32 | defer redisLock.RUnlock() 33 | return redis 34 | } 35 | -------------------------------------------------------------------------------- /core/response/response.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | "time" 7 | ) 8 | 9 | func Success(ctx *gin.Context, msg string, data interface{}, extraData ...interface{}) { 10 | ctx.JSON(http.StatusOK, gin.H{ 11 | "code": http.StatusOK, 12 | "msg": msg, 13 | "data": data, 14 | "ext": extraData, 15 | "time": time.Now().Unix(), 16 | "success": true, 17 | }) 18 | } 19 | 20 | func Fail(ctx *gin.Context, msg string, data interface{}, extraData ...interface{}) { 21 | ctx.JSON(http.StatusOK, gin.H{ 22 | "code": http.StatusInternalServerError, 23 | "msg": msg, 24 | "data": data, 25 | "ext": extraData, 26 | "time": time.Now().Unix(), 27 | "success": false, 28 | }) 29 | } 30 | 31 | func NotLogin(ctx *gin.Context, msg string, data interface{}, extraData ...interface{}) { 32 | ctx.JSON(http.StatusOK, gin.H{ 33 | "code": http.StatusUnauthorized, 34 | "msg": msg, 35 | "data": data, 36 | "ext": extraData, 37 | "time": time.Now().Unix(), 38 | "success": false, 39 | }) 40 | } 41 | 42 | func Forbidden(ctx *gin.Context, msg string, data interface{}, extraData ...interface{}) { 43 | ctx.JSON(http.StatusForbidden, gin.H{ 44 | "code": http.StatusForbidden, 45 | "msg": msg, 46 | "data": data, 47 | "ext": extraData, 48 | "time": time.Now().Unix(), 49 | "success": false, 50 | }) 51 | } 52 | 53 | func TooManyRequests(ctx *gin.Context, msg string, data interface{}, extraData ...interface{}) { 54 | ctx.JSON(http.StatusTooManyRequests, gin.H{ 55 | "code": http.StatusTooManyRequests, 56 | "msg": msg, 57 | "data": data, 58 | "ext": extraData, 59 | "time": time.Now().Unix(), 60 | "success": false, 61 | }) 62 | } 63 | -------------------------------------------------------------------------------- /core/router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | // BaseRoute 路由定义 6 | type BaseRoute struct { 7 | Method string 8 | Path string 9 | Handler gin.HandlerFunc 10 | } 11 | 12 | // CollectRoutesFromModules 从多个模块收集路由 13 | func CollectRoutesFromModules(modules ...func() []BaseRoute) []BaseRoute { 14 | var allRoutes []BaseRoute 15 | for _, module := range modules { 16 | allRoutes = append(allRoutes, module()...) 17 | } 18 | return allRoutes 19 | } 20 | -------------------------------------------------------------------------------- /core/snow/snow.go: -------------------------------------------------------------------------------- 1 | package snow 2 | 3 | import ( 4 | "fmt" 5 | "github.com/bwmarrin/snowflake" 6 | "vgo/core/global" 7 | ) 8 | 9 | var node *snowflake.Node 10 | 11 | // InitSnowflake 初始化雪花算法 12 | func InitSnowflake() { 13 | snowflakeConf := global.App.Config.SnowflakeConf 14 | // 创建雪花节点 15 | n, err := snowflake.NewNode(snowflakeConf.Node) 16 | if err != nil { 17 | fmt.Println("雪花Node创建失败:", err.Error()) 18 | panic("雪花Node创建失败!!") 19 | } 20 | node = n 21 | } 22 | 23 | // Node 获取雪花算法节点 24 | func Node() *snowflake.Node { 25 | return node 26 | } 27 | -------------------------------------------------------------------------------- /core/trans/trans.go: -------------------------------------------------------------------------------- 1 | package trans 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "os" 8 | "strings" 9 | "sync" 10 | "vgo/core/global" 11 | ) 12 | 13 | var ( 14 | translationsCache = make(map[string]map[string]string) 15 | cacheMutex sync.RWMutex 16 | ) 17 | 18 | // Trans 翻译 19 | func Trans(key string, values ...interface{}) string { 20 | appConf := global.App.Config.App 21 | lang := appConf.Lang 22 | // 使用读锁读取缓存 23 | cacheMutex.RLock() 24 | translations, exists := translationsCache[lang] 25 | cacheMutex.RUnlock() 26 | 27 | if !exists { 28 | // 缓存中没有,则加载文件并解析 29 | path := buildFilePath(lang) 30 | translations = loadTranslations(path) 31 | // 写锁更新缓存 32 | cacheMutex.Lock() 33 | translationsCache[lang] = translations 34 | cacheMutex.Unlock() 35 | } 36 | // 查找key对应的值 37 | if val, ok := translations[key]; ok { 38 | return fmt.Sprintf(val, values...) 39 | } 40 | fmt.Println("获取翻译出错!") 41 | return "" 42 | } 43 | 44 | // buildFilePath 构建文件路径 45 | func buildFilePath(lang string) string { 46 | var builder strings.Builder 47 | builder.WriteString("lang\\") 48 | builder.WriteString(lang) 49 | builder.WriteString("\\") 50 | builder.WriteString(lang) 51 | builder.WriteString(".json") 52 | return builder.String() 53 | } 54 | 55 | // loadTranslations 加载并解析翻译文件 56 | func loadTranslations(path string) map[string]string { 57 | file, err := os.Open(path) 58 | if err != nil { 59 | fmt.Println("打开文件失败:", err.Error()) 60 | return nil 61 | } 62 | defer file.Close() 63 | 64 | // 读取文件内容 65 | bytes, err := io.ReadAll(file) 66 | if err != nil { 67 | fmt.Println("读取文件内容失败:", err.Error()) 68 | return nil 69 | } 70 | 71 | // 解析JSON 72 | var translations map[string]string 73 | if err := json.Unmarshal(bytes, &translations); err != nil { 74 | fmt.Println("解析JSON失败:", err.Error()) 75 | return nil 76 | } 77 | 78 | return translations 79 | } 80 | -------------------------------------------------------------------------------- /core/validate/validate.go: -------------------------------------------------------------------------------- 1 | package validate 2 | 3 | import ( 4 | "errors" 5 | "github.com/go-playground/validator/v10" 6 | ) 7 | 8 | var validate *validator.Validate 9 | 10 | func init() { 11 | validate = validator.New() 12 | } 13 | 14 | // GetInstance 获取验证器实例 15 | func GetInstance() *validator.Validate { 16 | return validate 17 | } 18 | 19 | // Do 验证 20 | func Do(model interface{}, rules map[string]map[string]string) (bool, string) { 21 | if err := validate.Struct(model); err != nil { 22 | var errs validator.ValidationErrors 23 | errors.As(err, &errs) 24 | var errMsg string 25 | for _, e := range errs { 26 | fieldName := e.Field() 27 | tag := e.Tag() 28 | errMsg = rules[fieldName][tag] 29 | if errMsg == "" { 30 | errMsg = e.Error() 31 | } 32 | break 33 | } 34 | return false, errMsg 35 | } 36 | return true, "" 37 | } 38 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module vgo 2 | 3 | go 1.21.5 4 | 5 | require ( 6 | github.com/bwmarrin/snowflake v0.3.0 7 | github.com/casbin/casbin/v2 v2.99.0 8 | github.com/casbin/gorm-adapter/v3 v3.28.0 9 | github.com/casbin/redis-watcher/v2 v2.5.0 10 | github.com/fsnotify/fsnotify v1.7.0 11 | github.com/gabriel-vasile/mimetype v1.4.5 12 | github.com/gin-contrib/cors v1.7.2 13 | github.com/gin-gonic/gin v1.10.0 14 | github.com/go-playground/validator/v10 v10.22.1 15 | github.com/go-sql-driver/mysql v1.8.1 16 | github.com/golang-jwt/jwt/v5 v5.2.1 17 | github.com/gorilla/websocket v1.5.3 18 | github.com/hibiken/asynq v0.24.1 19 | github.com/redis/go-redis/v9 v9.6.1 20 | github.com/spf13/viper v1.18.2 21 | go.uber.org/zap v1.26.0 22 | golang.org/x/crypto v0.27.0 23 | golang.org/x/time v0.6.0 24 | gorm.io/driver/mysql v1.5.7 25 | gorm.io/gorm v1.25.11 26 | ) 27 | 28 | require ( 29 | filippo.io/edwards25519 v1.1.0 // indirect 30 | github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect 31 | github.com/bytedance/sonic v1.12.2 // indirect 32 | github.com/bytedance/sonic/loader v0.2.0 // indirect 33 | github.com/casbin/govaluate v1.2.0 // indirect 34 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 35 | github.com/cloudwego/base64x v0.1.4 // indirect 36 | github.com/cloudwego/iasm v0.2.0 // indirect 37 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect 38 | github.com/dustin/go-humanize v1.0.1 // indirect 39 | github.com/gin-contrib/sse v0.1.0 // indirect 40 | github.com/glebarez/go-sqlite v1.22.0 // indirect 41 | github.com/glebarez/sqlite v1.11.0 // indirect 42 | github.com/go-playground/locales v0.14.1 // indirect 43 | github.com/go-playground/universal-translator v0.18.1 // indirect 44 | github.com/goccy/go-json v0.10.3 // indirect 45 | github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect 46 | github.com/golang-sql/sqlexp v0.1.0 // indirect 47 | github.com/golang/protobuf v1.5.4 // indirect 48 | github.com/google/go-cmp v0.6.0 // indirect 49 | github.com/google/uuid v1.6.0 // indirect 50 | github.com/hashicorp/hcl v1.0.0 // indirect 51 | github.com/jackc/pgpassfile v1.0.0 // indirect 52 | github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect 53 | github.com/jackc/pgx/v5 v5.6.0 // indirect 54 | github.com/jackc/puddle/v2 v2.2.1 // indirect 55 | github.com/jinzhu/inflection v1.0.0 // indirect 56 | github.com/jinzhu/now v1.1.5 // indirect 57 | github.com/json-iterator/go v1.1.12 // indirect 58 | github.com/klauspost/cpuid/v2 v2.2.8 // indirect 59 | github.com/leodido/go-urn v1.4.0 // indirect 60 | github.com/magiconair/properties v1.8.7 // indirect 61 | github.com/mattn/go-isatty v0.0.20 // indirect 62 | github.com/microsoft/go-mssqldb v1.7.2 // indirect 63 | github.com/mitchellh/mapstructure v1.5.0 // indirect 64 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 65 | github.com/modern-go/reflect2 v1.0.2 // indirect 66 | github.com/ncruces/go-strftime v0.1.9 // indirect 67 | github.com/pelletier/go-toml/v2 v2.2.3 // indirect 68 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect 69 | github.com/robfig/cron/v3 v3.0.1 // indirect 70 | github.com/sagikazarmark/locafero v0.4.0 // indirect 71 | github.com/sagikazarmark/slog-shim v0.1.0 // indirect 72 | github.com/sourcegraph/conc v0.3.0 // indirect 73 | github.com/spf13/afero v1.11.0 // indirect 74 | github.com/spf13/cast v1.7.0 // indirect 75 | github.com/spf13/pflag v1.0.5 // indirect 76 | github.com/subosito/gotenv v1.6.0 // indirect 77 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 78 | github.com/ugorji/go/codec v1.2.12 // indirect 79 | go.uber.org/multierr v1.10.0 // indirect 80 | golang.org/x/arch v0.10.0 // indirect 81 | golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect 82 | golang.org/x/net v0.29.0 // indirect 83 | golang.org/x/sync v0.8.0 // indirect 84 | golang.org/x/sys v0.25.0 // indirect 85 | golang.org/x/text v0.18.0 // indirect 86 | google.golang.org/protobuf v1.34.2 // indirect 87 | gopkg.in/ini.v1 v1.67.0 // indirect 88 | gopkg.in/yaml.v3 v3.0.1 // indirect 89 | gorm.io/driver/postgres v1.5.9 // indirect 90 | gorm.io/driver/sqlserver v1.5.3 // indirect 91 | gorm.io/plugin/dbresolver v1.5.2 // indirect 92 | modernc.org/libc v1.60.1 // indirect 93 | modernc.org/mathutil v1.6.0 // indirect 94 | modernc.org/memory v1.8.0 // indirect 95 | modernc.org/sqlite v1.32.0 // indirect 96 | ) 97 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= 2 | filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= 3 | github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0/go.mod h1:ON4tFdPTwRcgWEaVDrN3584Ef+b7GgSJaXxe5fW9t4M= 4 | github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= 5 | github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= 6 | github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= 7 | github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 h1:lGlwhPtrX6EVml1hO0ivjkUxsSyl4dsiw9qcA1k/3IQ= 8 | github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1/go.mod h1:RKUqNu35KJYcVG/fqTRqmuXJZYNhYkBrnC/hX7yGbTA= 9 | github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U= 10 | github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 h1:sO0/P7g68FrryJzljemN+6GTssUXdANk6aJ7T1ZxnsQ= 11 | github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1/go.mod h1:h8hyGFDsU5HMivxiS2iYFZsgDbU9OnnJ163x5UGVKYo= 12 | github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= 13 | github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= 14 | github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= 15 | github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 h1:6oNBlSdi1QqM1PNW7FPA6xOGA5UNsXnkaYZz9vdPGhA= 16 | github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI= 17 | github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0/go.mod h1:Q28U+75mpCaSCDowNEmhIo/rmgdkqmkmzI7N6TGR4UY= 18 | github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1 h1:MyVTgWR8qd/Jw1Le0NZebGBUCLbtak3bJ3z1OlqZBpw= 19 | github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1/go.mod h1:GpPjLhVR9dnUoJMyHWSPy71xY9/lcmpzIPZXmF0FCVY= 20 | github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0/go.mod h1:cw4zVQgBby0Z5f2v0itn6se2dDP17nTjbZFXW5uPyHA= 21 | github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 h1:D3occbWoio4EBLkbkevetNMAVX197GkzbUMtqjGWn80= 22 | github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0/go.mod h1:bTSOgj05NGRuHHhQwAdPnYr9TOdNmKlZTgGLL6nyAdI= 23 | github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o= 24 | github.com/AzureAD/microsoft-authentication-library-for-go v1.1.0/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= 25 | github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 h1:DzHpqpoJVaCgOUdVHxE8QB52S6NiVdDQvGlny1qvPqA= 26 | github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= 27 | github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I= 28 | github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= 29 | github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w= 30 | github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= 31 | github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= 32 | github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= 33 | github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= 34 | github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= 35 | github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0= 36 | github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE= 37 | github.com/bytedance/sonic v1.12.2 h1:oaMFuRTpMHYLpCntGca65YWt5ny+wAceDERTkT2L9lg= 38 | github.com/bytedance/sonic v1.12.2/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= 39 | github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= 40 | github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM= 41 | github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= 42 | github.com/casbin/casbin/v2 v2.99.0 h1:Y993vfRenh8Xtb4XVaK8KeYJTjD4Zn1XVewGszhzk1E= 43 | github.com/casbin/casbin/v2 v2.99.0/go.mod h1:LO7YPez4dX3LgoTCqSQAleQDo0S0BeZBDxYnPUl95Ng= 44 | github.com/casbin/gorm-adapter/v3 v3.28.0 h1:ORF8prF6SfaipdgT1fud+r1Tp5J0uul8QaKJHqCPY/o= 45 | github.com/casbin/gorm-adapter/v3 v3.28.0/go.mod h1:aftWi0cla0CC1bHQVrSFzBcX/98IFK28AvuPppCQgTs= 46 | github.com/casbin/govaluate v1.2.0 h1:wXCXFmqyY+1RwiKfYo3jMKyrtZmOL3kHwaqDyCPOYak= 47 | github.com/casbin/govaluate v1.2.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A= 48 | github.com/casbin/redis-watcher/v2 v2.5.0 h1:a0922GOKYDSSiD7hEQxmLh/psea2eLZtf1V12XzLI5w= 49 | github.com/casbin/redis-watcher/v2 v2.5.0/go.mod h1:lgtjnQrfbo+xZIwMPtLu9is/XpnCfAT94SLgMzY7HGk= 50 | github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 51 | github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= 52 | github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 53 | github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= 54 | github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= 55 | github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= 56 | github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= 57 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 58 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 59 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= 60 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 61 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= 62 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= 63 | github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= 64 | github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= 65 | github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= 66 | github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= 67 | github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= 68 | github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= 69 | github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= 70 | github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= 71 | github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4= 72 | github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4= 73 | github.com/gin-contrib/cors v1.7.2 h1:oLDHxdg8W/XDoN/8zamqk/Drgt4oVZDvaV0YmvVICQw= 74 | github.com/gin-contrib/cors v1.7.2/go.mod h1:SUJVARKgQ40dmrzgXEVxj2m7Ig1v1qIboQkPDTQ9t2E= 75 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 76 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 77 | github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= 78 | github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= 79 | github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ= 80 | github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc= 81 | github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw= 82 | github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ= 83 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= 84 | github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 85 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 86 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 87 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 88 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 89 | github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA= 90 | github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= 91 | github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= 92 | github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= 93 | github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= 94 | github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= 95 | github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= 96 | github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= 97 | github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= 98 | github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= 99 | github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= 100 | github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= 101 | github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= 102 | github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= 103 | github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= 104 | github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= 105 | github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= 106 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 107 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 108 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 109 | github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= 110 | github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 111 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 112 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 113 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 114 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 115 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 116 | github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= 117 | github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= 118 | github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 119 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 120 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 121 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 122 | github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= 123 | github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= 124 | github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= 125 | github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 126 | github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 127 | github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 128 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 129 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 130 | github.com/hibiken/asynq v0.24.1 h1:+5iIEAyA9K/lcSPvx3qoPtsKJeKI5u9aOIvUmSsazEw= 131 | github.com/hibiken/asynq v0.24.1/go.mod h1:u5qVeSbrnfT+vtG5Mq8ZPzQu/BmCKMHvTGb91uy9Tts= 132 | github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= 133 | github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= 134 | github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= 135 | github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= 136 | github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY= 137 | github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw= 138 | github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= 139 | github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= 140 | github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= 141 | github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= 142 | github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= 143 | github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= 144 | github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= 145 | github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= 146 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 147 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 148 | github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= 149 | github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 150 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 151 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 152 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 153 | github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= 154 | github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= 155 | github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= 156 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 157 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 158 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 159 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 160 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 161 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 162 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 163 | github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= 164 | github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 165 | github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= 166 | github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= 167 | github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= 168 | github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 169 | github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= 170 | github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= 171 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 172 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 173 | github.com/microsoft/go-mssqldb v1.6.0/go.mod h1:00mDtPbeQCRGC1HwOOR5K/gr30P1NcEG0vx6Kbv2aJU= 174 | github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA= 175 | github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA= 176 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 177 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 178 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 179 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 180 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 181 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 182 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 183 | github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= 184 | github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= 185 | github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= 186 | github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= 187 | github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= 188 | github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= 189 | github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= 190 | github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= 191 | github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= 192 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 193 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= 194 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 195 | github.com/redis/go-redis/v9 v9.0.3/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= 196 | github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4= 197 | github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= 198 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= 199 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= 200 | github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= 201 | github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= 202 | github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= 203 | github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= 204 | github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= 205 | github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= 206 | github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= 207 | github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= 208 | github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= 209 | github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= 210 | github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= 211 | github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= 212 | github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 213 | github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= 214 | github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= 215 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 216 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 217 | github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= 218 | github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= 219 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 220 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 221 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 222 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 223 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 224 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 225 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 226 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 227 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 228 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 229 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 230 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 231 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 232 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 233 | github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= 234 | github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= 235 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 236 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 237 | github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= 238 | github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 239 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 240 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 241 | go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= 242 | go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= 243 | go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= 244 | go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= 245 | go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 246 | go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= 247 | go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= 248 | golang.org/x/arch v0.10.0 h1:S3huipmSclq3PJMNe76NGwkBR504WFkQ5dhzWzP8ZW8= 249 | golang.org/x/arch v0.10.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= 250 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 251 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 252 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 253 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 254 | golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= 255 | golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= 256 | golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= 257 | golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= 258 | golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= 259 | golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= 260 | golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= 261 | golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= 262 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 263 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 264 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 265 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 266 | golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= 267 | golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 268 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 269 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 270 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 271 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 272 | golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 273 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 274 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 275 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 276 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 277 | golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 278 | golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= 279 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 280 | golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= 281 | golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= 282 | golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= 283 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 284 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 285 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 286 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 287 | golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= 288 | golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 289 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 290 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 291 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 292 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 293 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 294 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 295 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 296 | golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 297 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 298 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 299 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 300 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 301 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 302 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 303 | golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 304 | golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= 305 | golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 306 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 307 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 308 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 309 | golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= 310 | golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= 311 | golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= 312 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 313 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 314 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 315 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 316 | golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 317 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 318 | golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 319 | golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= 320 | golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= 321 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 322 | golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= 323 | golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 324 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 325 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 326 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 327 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 328 | golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 329 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 330 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 331 | golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= 332 | golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= 333 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 334 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 335 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 336 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 337 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 338 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 339 | google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= 340 | google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= 341 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 342 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 343 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 344 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 345 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 346 | gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= 347 | gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 348 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 349 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 350 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 351 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 352 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 353 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 354 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 355 | gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= 356 | gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= 357 | gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= 358 | gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8= 359 | gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI= 360 | gorm.io/driver/sqlserver v1.5.3 h1:rjupPS4PVw+rjJkfvr8jn2lJ8BMhT4UW5FwuJY0P3Z0= 361 | gorm.io/driver/sqlserver v1.5.3/go.mod h1:B+CZ0/7oFJ6tAlefsKoyxdgDCXJKSgwS2bMOQZT0I00= 362 | gorm.io/gorm v1.25.7-0.20240204074919-46816ad31dde/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= 363 | gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= 364 | gorm.io/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg= 365 | gorm.io/gorm v1.25.11/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= 366 | gorm.io/plugin/dbresolver v1.5.2 h1:Iut7lW4TXNoVs++I+ra3zxjSxTRj4ocIeFEVp4lLhII= 367 | gorm.io/plugin/dbresolver v1.5.2/go.mod h1:jPh59GOQbO7v7v28ZKZPd45tr+u3vyT+8tHdfdfOWcU= 368 | modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ= 369 | modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= 370 | modernc.org/ccgo/v4 v4.21.0 h1:kKPI3dF7RIag8YcToh5ZwDcVMIv6VGa0ED5cvh0LMW4= 371 | modernc.org/ccgo/v4 v4.21.0/go.mod h1:h6kt6H/A2+ew/3MW/p6KEoQmrq/i3pr0J/SiwiaF/g0= 372 | modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= 373 | modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= 374 | modernc.org/gc/v2 v2.5.0 h1:bJ9ChznK1L1mUtAQtxi0wi5AtAs5jQuw4PrPHO5pb6M= 375 | modernc.org/gc/v2 v2.5.0/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= 376 | modernc.org/libc v1.60.1 h1:at373l8IFRTkJIkAU85BIuUoBM4T1b51ds0E1ovPG2s= 377 | modernc.org/libc v1.60.1/go.mod h1:xJuobKuNxKH3RUatS7GjR+suWj+5c2K7bi4m/S5arOY= 378 | modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= 379 | modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= 380 | modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= 381 | modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= 382 | modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= 383 | modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= 384 | modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= 385 | modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= 386 | modernc.org/sqlite v1.32.0 h1:6BM4uGza7bWypsw4fdLRsLxut6bHe4c58VeqjRgST8s= 387 | modernc.org/sqlite v1.32.0/go.mod h1:UqoylwmTb9F+IqXERT8bW9zzOWN8qwAIcLdzeBZs4hA= 388 | modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= 389 | modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= 390 | modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= 391 | modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= 392 | nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= 393 | -------------------------------------------------------------------------------- /go_study.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat Premium Dump SQL 3 | 4 | Source Server : 本机mysql 5 | Source Server Type : MySQL 6 | Source Server Version : 80031 (8.0.31) 7 | Source Host : localhost:3306 8 | Source Schema : go_study 9 | 10 | Target Server Type : MySQL 11 | Target Server Version : 80031 (8.0.31) 12 | File Encoding : 65001 13 | 14 | Date: 10/09/2024 13:43:26 15 | */ 16 | 17 | SET NAMES utf8mb4; 18 | SET FOREIGN_KEY_CHECKS = 0; 19 | 20 | -- ---------------------------- 21 | -- Table structure for admin_users 22 | -- ---------------------------- 23 | DROP TABLE IF EXISTS `admin_users`; 24 | CREATE TABLE `admin_users` ( 25 | `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, 26 | `created_at` datetime(3) NULL DEFAULT NULL, 27 | `updated_at` datetime(3) NULL DEFAULT NULL, 28 | `deleted_at` datetime(3) NULL DEFAULT NULL, 29 | `username` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名', 30 | `password` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '密码', 31 | `user_type` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '100' COMMENT '用户类型:(100系统用户)', 32 | `nickname` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '用户昵称', 33 | `phone` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '手机', 34 | `email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '用户邮箱', 35 | `avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '用户头像', 36 | `signed` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '个人签名', 37 | `dashboard` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '后台首页类型', 38 | `status` bigint NULL DEFAULT 1 COMMENT '状态 (1正常 2停用)', 39 | `login_ip` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '最后登陆IP', 40 | `backend_setting` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '后台设置数据', 41 | `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '备注', 42 | `super` int NOT NULL DEFAULT 2 COMMENT '是否超级管理员 (1是 2否)', 43 | PRIMARY KEY (`id`) USING BTREE, 44 | UNIQUE INDEX `username`(`username` ASC) USING BTREE, 45 | INDEX `idx_admin_users_deleted_at`(`deleted_at` ASC) USING BTREE, 46 | INDEX `idx_admin_users_user_name`(`username` ASC) USING BTREE 47 | ) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; 48 | 49 | -- ---------------------------- 50 | -- Records of admin_users 51 | -- ---------------------------- 52 | INSERT INTO `admin_users` VALUES (1, '2024-07-11 00:00:00.000', '2024-08-19 17:25:46.000', NULL, 'superAdmin', '$2y$12$zpo8rhu.QGhb5Ty7bFTfaOYy7FfecukttS1Qn6oA2XfZ9MKTMNXJe', '100', 'superAdmin', '13888888888', 'admin@qq.com', '', '行胜于言,质胜于华。', 'statistics', 1, '127.0.0.1', '{\"mode\":\"light\",\"tag\":true,\"menuCollapse\":false,\"menuWidth\":230,\"layout\":\"classic\",\"skin\":\"mine\",\"i18n\":false,\"language\":\"zh_CN\",\"animation\":\"ma-slide-down\",\"color\":\"#165dff\",\"ws\":false}', '', 1); 53 | INSERT INTO `admin_users` VALUES (3, '2024-09-02 10:49:13.713', '2024-09-02 10:49:13.713', '2024-09-02 10:54:09.085', '3332232', '$2a$10$ec3PR7Wqc0O0uIEgxvlpB.MBHz4vAStXJ5sOyfTVgv3kQ/spJ5Lvm', '100', '', '', '', '', '', '', 1, '', '', '', 2); 54 | INSERT INTO `admin_users` VALUES (4, '2024-09-02 17:16:47.131', '2024-09-02 17:16:47.131', '2024-09-02 17:16:49.784', '水电费水电费', '$2a$10$V4WSM7B1.M8cbNbgzokQxOnEVMvXxmwAmpF.jmI27onJn2NOLshQ.', '100', '', '', '', '', '', '', 1, '', '', '', 2); 55 | INSERT INTO `admin_users` VALUES (5, '2024-09-03 09:10:49.273', '2024-09-03 09:10:49.273', NULL, 'admin', '$2a$10$iQ6n2rRNw05QNJ35kHeiuuhl7h1IxfDj7e3oOEbT9l3MsXkVllTGm', '100', '', '', '', '', '', '', 1, '', '', '', 2); 56 | INSERT INTO `admin_users` VALUES (6, '2024-09-03 12:33:29.626', '2024-09-03 12:33:29.626', NULL, 'test', '$2a$10$5XVzMvqOt42IOlPHjeLGVuDdR5TOuYeqCpGtSp8tRBpdbJJov47XC', '100', '', '', '', '', '', '', 1, '', '', '', 2); 57 | 58 | -- ---------------------------- 59 | -- Table structure for casbin_rule 60 | -- ---------------------------- 61 | DROP TABLE IF EXISTS `casbin_rule`; 62 | CREATE TABLE `casbin_rule` ( 63 | `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, 64 | `ptype` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, 65 | `v0` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, 66 | `v1` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, 67 | `v2` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, 68 | `v3` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, 69 | `v4` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, 70 | `v5` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, 71 | PRIMARY KEY (`id`) USING BTREE, 72 | UNIQUE INDEX `idx_casbin_rule`(`ptype` ASC, `v0` ASC, `v1` ASC, `v2` ASC, `v3` ASC, `v4` ASC, `v5` ASC) USING BTREE 73 | ) ENGINE = InnoDB AUTO_INCREMENT = 286 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; 74 | 75 | -- ---------------------------- 76 | -- Records of casbin_rule 77 | -- ---------------------------- 78 | INSERT INTO `casbin_rule` VALUES (56, 'g', '5', 'admin', '', '', '', ''); 79 | INSERT INTO `casbin_rule` VALUES (45, 'g', '6', 'test2', '', '', '', ''); 80 | INSERT INTO `casbin_rule` VALUES (273, 'p', 'admin', '/admin/admin/notice/view', '', '', '', ''); 81 | INSERT INTO `casbin_rule` VALUES (274, 'p', 'admin', '/admin/admin_user', '', '', '', ''); 82 | INSERT INTO `casbin_rule` VALUES (275, 'p', 'admin', '/admin/admin_user/create', '', '', '', ''); 83 | INSERT INTO `casbin_rule` VALUES (277, 'p', 'admin', '/admin/admin_user/delete', '', '', '', ''); 84 | INSERT INTO `casbin_rule` VALUES (278, 'p', 'admin', '/admin/admin_user/get/role', '', '', '', ''); 85 | INSERT INTO `casbin_rule` VALUES (279, 'p', 'admin', '/admin/admin_user/set/role', '', '', '', ''); 86 | INSERT INTO `casbin_rule` VALUES (276, 'p', 'admin', '/admin/admin_user/update', '', '', '', ''); 87 | INSERT INTO `casbin_rule` VALUES (259, 'p', 'admin', '/admin/button/list', '', '', '', ''); 88 | INSERT INTO `casbin_rule` VALUES (270, 'p', 'admin', '/admin/menu/create', '', '', '', ''); 89 | INSERT INTO `casbin_rule` VALUES (272, 'p', 'admin', '/admin/menu/delete', '', '', '', ''); 90 | INSERT INTO `casbin_rule` VALUES (258, 'p', 'admin', '/admin/menu/list', '', '', '', ''); 91 | INSERT INTO `casbin_rule` VALUES (271, 'p', 'admin', '/admin/menu/update', '', '', '', ''); 92 | INSERT INTO `casbin_rule` VALUES (260, 'p', 'admin', '/admin/notice', '', '', '', ''); 93 | INSERT INTO `casbin_rule` VALUES (265, 'p', 'admin', '/admin/notice/change', '', '', '', ''); 94 | INSERT INTO `casbin_rule` VALUES (261, 'p', 'admin', '/admin/notice/create', '', '', '', ''); 95 | INSERT INTO `casbin_rule` VALUES (263, 'p', 'admin', '/admin/notice/delete', '', '', '', ''); 96 | INSERT INTO `casbin_rule` VALUES (264, 'p', 'admin', '/admin/notice/export', '', '', '', ''); 97 | INSERT INTO `casbin_rule` VALUES (262, 'p', 'admin', '/admin/notice/update', '', '', '', ''); 98 | INSERT INTO `casbin_rule` VALUES (266, 'p', 'admin', '/admin/notice/view', '', '', '', ''); 99 | INSERT INTO `casbin_rule` VALUES (280, 'p', 'admin', '/admin/role', '', '', '', ''); 100 | INSERT INTO `casbin_rule` VALUES (281, 'p', 'admin', '/admin/role/create', '', '', '', ''); 101 | INSERT INTO `casbin_rule` VALUES (283, 'p', 'admin', '/admin/role/delete', '', '', '', ''); 102 | INSERT INTO `casbin_rule` VALUES (285, 'p', 'admin', '/admin/role/get/menu', '', '', '', ''); 103 | INSERT INTO `casbin_rule` VALUES (284, 'p', 'admin', '/admin/role/set/menu', '', '', '', ''); 104 | INSERT INTO `casbin_rule` VALUES (282, 'p', 'admin', '/admin/role/update', '', '', '', ''); 105 | INSERT INTO `casbin_rule` VALUES (267, 'p', 'admin', '/admin/upload', '', '', '', ''); 106 | INSERT INTO `casbin_rule` VALUES (268, 'p', 'admin', '/admin/upload/img', '', '', '', ''); 107 | INSERT INTO `casbin_rule` VALUES (269, 'p', 'admin', '/admin/upload/video', '', '', '', ''); 108 | 109 | -- ---------------------------- 110 | -- Table structure for menus 111 | -- ---------------------------- 112 | DROP TABLE IF EXISTS `menus`; 113 | CREATE TABLE `menus` ( 114 | `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, 115 | `created_at` datetime(3) NULL DEFAULT NULL, 116 | `updated_at` datetime(3) NULL DEFAULT NULL, 117 | `deleted_at` datetime(3) NULL DEFAULT NULL, 118 | `parent_id` bigint UNSIGNED NOT NULL DEFAULT 0 COMMENT '父ID', 119 | `path` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '路由访问路径', 120 | `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '路由name', 121 | `redirect` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '路由重定向地址', 122 | `api` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '请求接口地址', 123 | `component` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '视图文件路径', 124 | `icon` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '菜单和面包屑对应的图标', 125 | `title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '路由标题(菜单名称)', 126 | `activeMenu` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '是否在菜单中隐藏,需要高亮的path', 127 | `isLink` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '路由外链时填写的访问地址', 128 | `isHide` smallint NOT NULL DEFAULT 2 COMMENT '是否在菜单中隐藏 (1是 2否)', 129 | `isFull` smallint NOT NULL DEFAULT 2 COMMENT '菜单是否全屏 (1是 2否)', 130 | `isAffix` smallint NOT NULL DEFAULT 1 COMMENT '菜单是否固定在标签页中 (1是 2否)', 131 | `isKeepAlive` smallint NOT NULL DEFAULT 2 COMMENT '当前路由是否缓存 (1是 2否)', 132 | `status` smallint NOT NULL DEFAULT 1 COMMENT '状态 (1正常 2停用)', 133 | `type` smallint NOT NULL DEFAULT 1 COMMENT '类型 (1菜单2按钮3外链4Iframe)', 134 | `sort` bigint NOT NULL DEFAULT 0 COMMENT '排序', 135 | PRIMARY KEY (`id`) USING BTREE, 136 | INDEX `idx_menus_deleted_at`(`deleted_at` ASC) USING BTREE 137 | ) ENGINE = InnoDB AUTO_INCREMENT = 55 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; 138 | 139 | -- ---------------------------- 140 | -- Records of menus 141 | -- ---------------------------- 142 | INSERT INTO `menus` VALUES (1, NULL, NULL, NULL, 0, '/home/index', 'home', '', '', '/home/index', 'HomeFilled', '首页', '', '', 2, 2, 1, 2, 1, 1, 99); 143 | INSERT INTO `menus` VALUES (2, NULL, NULL, '2024-09-03 16:50:46.507', 0, '/dataScreen', 'dataScreen', '', '', '/dataScreen/index', 'Histogram', '数据大屏', '', '', 2, 1, 2, 2, 1, 1, 0); 144 | INSERT INTO `menus` VALUES (3, NULL, NULL, NULL, 0, '/cms', 'cms', '/cms/notice', '', '', 'Lollipop', '内容管理', '', '', 2, 2, 2, 2, 1, 1, 0); 145 | INSERT INTO `menus` VALUES (4, NULL, '2024-09-03 10:24:23.566', NULL, 3, '/cms/notice', 'notice', '', '/admin/notice', '/cms/notice/index', 'Menu', '公告管理', '', '', 2, 2, 2, 2, 1, 1, 0); 146 | INSERT INTO `menus` VALUES (5, NULL, NULL, NULL, 4, '/cms/notice/detail/:id', 'noticeDetail', '', '', '/cms/notice/detail', 'Menu', '公告 详情', '/cms/notice', '', 1, 2, 2, 2, 1, 1, 0); 147 | INSERT INTO `menus` VALUES (6, NULL, '2024-09-03 10:24:58.883', NULL, 4, '', 'create', '', '/admin/notice/create', '', '', '添加', '', '', 1, 2, 2, 2, 1, 2, 0); 148 | INSERT INTO `menus` VALUES (7, NULL, NULL, NULL, 4, '', 'update', '', '/admin/notice/update', '', '', '编辑', '', '', 1, 2, 2, 2, 1, 2, 0); 149 | INSERT INTO `menus` VALUES (8, NULL, NULL, NULL, 4, '', 'delete', '', '/admin/notice/delete', '', '', '删除', '', '', 1, 2, 2, 2, 1, 2, 0); 150 | INSERT INTO `menus` VALUES (9, NULL, NULL, NULL, 4, '', 'export', '', '/admin/notice/export', '', '', '导出', '', '', 1, 2, 2, 2, 1, 2, 0); 151 | INSERT INTO `menus` VALUES (10, NULL, NULL, NULL, 4, '', 'change', '', '/admin/notice/change', '', '', '修改状态', '', '', 1, 2, 2, 2, 1, 2, 0); 152 | INSERT INTO `menus` VALUES (11, NULL, NULL, NULL, 4, '', 'view', '', '/admin/notice/view', '', '', '查看', '', '', 1, 2, 2, 2, 1, 2, 0); 153 | INSERT INTO `menus` VALUES (12, NULL, '2024-09-02 15:59:39.192', NULL, 0, '/authority', 'authority', '/authority/menu', '', '', 'Lock', '权限管理', '', '', 2, 2, 2, 2, 1, 1, 0); 154 | INSERT INTO `menus` VALUES (13, NULL, '2024-09-02 17:18:24.867', NULL, 12, '/authority/menu', 'menuAuthority', '', '', '/authority/menu/index', 'Menu', '菜单权限', '', '', 2, 2, 2, 2, 1, 1, 0); 155 | INSERT INTO `menus` VALUES (30, '2024-09-02 10:35:03.467', '2024-09-02 10:35:03.467', NULL, 12, '/authority/admin_user', 'adminUser', '', '/admin/admin_user', '/authority/admin_user/index', 'Avatar', '管理员', '', '', 2, 2, 2, 2, 1, 1, 0); 156 | INSERT INTO `menus` VALUES (31, '2024-09-02 16:10:02.983', '2024-09-02 16:10:02.983', NULL, 12, '/authority/role', 'role', '', '/admin/role', '/authority/role/index', 'Stamp', '角色管理', '', '', 2, 2, 2, 2, 1, 1, 0); 157 | INSERT INTO `menus` VALUES (34, '2024-09-03 10:28:22.793', '2024-09-03 10:29:17.200', NULL, 1, '', 'none', '', '/admin/menu/list', '', '', '用户菜单数据源', '', '', 1, 2, 2, 2, 1, 1, 0); 158 | INSERT INTO `menus` VALUES (35, '2024-09-03 10:28:53.299', '2024-09-03 10:29:24.284', NULL, 1, '', 'none', '', '/admin/button/list', '', '', '用户按钮数据源', '', '', 1, 2, 2, 2, 1, 1, 0); 159 | INSERT INTO `menus` VALUES (36, NULL, '2024-09-03 10:24:58.883', NULL, 13, '', 'create', '', '/admin/menu/create', '', '', '添加', '', '', 1, 2, 2, 2, 1, 2, 0); 160 | INSERT INTO `menus` VALUES (37, NULL, NULL, NULL, 13, '', 'update', '', '/admin/menu/update', '', '', '编辑', '', '', 1, 2, 2, 2, 1, 2, 0); 161 | INSERT INTO `menus` VALUES (38, NULL, NULL, NULL, 13, '', 'delete', '', '/admin/menu/delete', '', '', '删除', '', '', 1, 2, 2, 2, 1, 2, 0); 162 | INSERT INTO `menus` VALUES (41, NULL, NULL, NULL, 13, '', 'view', '', '/admin/admin/notice/view', '', '', '查看', '', '', 1, 2, 2, 2, 1, 2, 0); 163 | INSERT INTO `menus` VALUES (42, NULL, '2024-09-03 10:24:58.883', NULL, 30, '', 'create', '', '/admin/admin_user/create', '', '', '添加', '', '', 1, 2, 2, 2, 1, 2, 0); 164 | INSERT INTO `menus` VALUES (43, NULL, NULL, NULL, 30, '', 'update', '', '/admin/admin_user/update', '', '', '编辑', '', '', 1, 2, 2, 2, 1, 2, 0); 165 | INSERT INTO `menus` VALUES (44, NULL, NULL, NULL, 30, '', 'delete', '', '/admin/admin_user/delete', '', '', '删除', '', '', 1, 2, 2, 2, 1, 2, 0); 166 | INSERT INTO `menus` VALUES (45, NULL, NULL, NULL, 30, '', 'role', '', '/admin/admin_user/get/role', '', '', '查看角色', '', '', 1, 2, 2, 2, 1, 2, 0); 167 | INSERT INTO `menus` VALUES (46, NULL, NULL, NULL, 30, '', 'role', '', '/admin/admin_user/set/role', '', '', '设置角色', '', '', 1, 2, 2, 2, 1, 2, 0); 168 | INSERT INTO `menus` VALUES (47, NULL, '2024-09-03 10:24:58.883', NULL, 31, '', 'create', '', '/admin/role/create', '', '', '添加', '', '', 1, 2, 2, 2, 1, 2, 0); 169 | INSERT INTO `menus` VALUES (48, NULL, NULL, NULL, 31, '', 'update', '', '/admin/role/update', '', '', '编辑', '', '', 1, 2, 2, 2, 1, 2, 0); 170 | INSERT INTO `menus` VALUES (49, NULL, NULL, NULL, 31, '', 'delete', '', '/admin/role/delete', '', '', '删除', '', '', 1, 2, 2, 2, 1, 2, 0); 171 | INSERT INTO `menus` VALUES (50, NULL, NULL, NULL, 31, '', 'authority', '', '/admin/role/set/menu', '', '', '查看角色', '', '', 1, 2, 2, 2, 1, 2, 0); 172 | INSERT INTO `menus` VALUES (51, NULL, NULL, NULL, 31, '', 'authority', '', '/admin/role/get/menu', '', '', '设置角色', '', '', 1, 2, 2, 2, 1, 2, 0); 173 | INSERT INTO `menus` VALUES (52, NULL, '2024-09-10 09:24:24.375', NULL, 3, '/cms/upload', 'upload', '', '/admin/upload', '/cms/upload/index', 'Upload', '文件上传', '', '', 2, 2, 2, 2, 1, 1, 0); 174 | INSERT INTO `menus` VALUES (53, NULL, '2024-09-03 10:24:58.883', NULL, 52, '', 'upload-img', '', '/admin/upload/img', '', '', '图片上传', '', '', 1, 2, 2, 2, 1, 2, 0); 175 | INSERT INTO `menus` VALUES (54, NULL, '2024-09-03 10:24:58.883', NULL, 52, '', 'upload-video', '', '/admin/upload/video', '', '', '视频上传', '', '', 1, 2, 2, 2, 1, 2, 0); 176 | 177 | -- ---------------------------- 178 | -- Table structure for notices 179 | -- ---------------------------- 180 | DROP TABLE IF EXISTS `notices`; 181 | CREATE TABLE `notices` ( 182 | `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, 183 | `created_at` datetime(3) NULL DEFAULT NULL, 184 | `updated_at` datetime(3) NULL DEFAULT NULL, 185 | `deleted_at` datetime(3) NULL DEFAULT NULL, 186 | `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '标题', 187 | `type` smallint NOT NULL DEFAULT 0 COMMENT '公告类型(1通知 2公告)', 188 | `status` smallint NOT NULL DEFAULT 1 COMMENT '状态 (1启用 2禁用)', 189 | `content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '公告内容', 190 | `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '备注', 191 | PRIMARY KEY (`id`) USING BTREE, 192 | INDEX `idx_notices_deleted_at`(`deleted_at` ASC) USING BTREE 193 | ) ENGINE = InnoDB AUTO_INCREMENT = 12 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; 194 | 195 | -- ---------------------------- 196 | -- Records of notices 197 | -- ---------------------------- 198 | INSERT INTO `notices` VALUES (1, '2024-08-30 09:55:59.657', '2024-08-30 10:15:43.317', '2024-09-02 10:53:13.157', '公告1111', 0, 1, '

公告1

', ''); 199 | INSERT INTO `notices` VALUES (2, '2024-08-30 10:11:10.500', '2024-09-02 16:43:15.248', '2024-09-02 17:09:33.554', '公告222', 0, 1, '

公告222公告222公告222公告222公告222

', ''); 200 | INSERT INTO `notices` VALUES (3, '2024-08-30 10:11:30.438', '2024-08-30 10:11:30.438', '2024-08-30 10:11:33.181', '哈哈哈哈', 0, 1, '

哈哈哈哈哈哈哈哈哈哈哈哈

', ''); 201 | INSERT INTO `notices` VALUES (4, '2024-08-30 10:12:53.230', '2024-08-30 10:12:53.230', '2024-08-30 10:12:56.246', '山东省', 0, 1, '

山东省山东省山东省

', ''); 202 | INSERT INTO `notices` VALUES (5, '2024-09-02 16:48:58.452', '2024-09-02 16:48:58.452', '2024-09-02 16:49:01.192', '颠三倒四多', 0, 1, '

是的是的

', ''); 203 | INSERT INTO `notices` VALUES (6, '2024-09-02 16:52:58.634', '2024-09-02 16:52:58.634', '2024-09-02 17:09:44.025', '大啊大大', 0, 1, '

颠三倒四多

', ''); 204 | INSERT INTO `notices` VALUES (7, '2024-09-02 17:09:41.514', '2024-09-02 17:09:41.514', '2024-09-02 17:10:50.537', '防守打法收到', 0, 1, '

防守打法收到防守打法收到防守打法收到防守打法收到防守打法收到

', ''); 205 | INSERT INTO `notices` VALUES (8, '2024-09-02 17:10:48.682', '2024-09-02 17:10:48.682', '2024-09-02 17:16:03.498', '4443434', 0, 1, '

都是

', ''); 206 | INSERT INTO `notices` VALUES (9, '2024-09-02 17:16:01.679', '2024-09-09 08:53:50.063', NULL, '是的是的', 0, 1, '

颠三倒四

', ''); 207 | INSERT INTO `notices` VALUES (10, '2024-09-02 17:17:11.721', '2024-09-02 17:17:11.721', '2024-09-02 17:17:13.786', '撒旦法水电费', 0, 1, '

 发的范德萨发的

', ''); 208 | INSERT INTO `notices` VALUES (11, '2024-09-03 11:45:46.071', '2024-09-03 11:45:46.071', '2024-09-03 11:45:48.224', '是多少颠三倒四', 0, 1, '

但是时代大厦

', ''); 209 | 210 | -- ---------------------------- 211 | -- Table structure for role_menus 212 | -- ---------------------------- 213 | DROP TABLE IF EXISTS `role_menus`; 214 | CREATE TABLE `role_menus` ( 215 | `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, 216 | `created_at` datetime(3) NULL DEFAULT NULL, 217 | `updated_at` datetime(3) NULL DEFAULT NULL, 218 | `deleted_at` datetime(3) NULL DEFAULT NULL, 219 | `role_id` bigint UNSIGNED NOT NULL DEFAULT 0 COMMENT '角色ID', 220 | `menu_id` bigint UNSIGNED NOT NULL DEFAULT 0 COMMENT '菜单ID', 221 | PRIMARY KEY (`id`) USING BTREE, 222 | INDEX `idx_role_menus_deleted_at`(`deleted_at` ASC) USING BTREE 223 | ) ENGINE = InnoDB AUTO_INCREMENT = 481 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; 224 | 225 | -- ---------------------------- 226 | -- Records of role_menus 227 | -- ---------------------------- 228 | INSERT INTO `role_menus` VALUES (1, '2024-09-03 15:08:36.527', '2024-09-03 15:08:36.527', '2024-09-03 15:25:37.773', 4, 1); 229 | INSERT INTO `role_menus` VALUES (2, '2024-09-03 15:25:37.780', '2024-09-03 15:25:37.780', '2024-09-03 15:25:52.072', 4, 1); 230 | INSERT INTO `role_menus` VALUES (3, '2024-09-03 15:25:52.074', '2024-09-03 15:25:52.074', '2024-09-03 15:27:12.192', 4, 1); 231 | INSERT INTO `role_menus` VALUES (4, '2024-09-03 15:27:12.194', '2024-09-03 15:27:12.194', '2024-09-03 15:27:44.558', 4, 1); 232 | INSERT INTO `role_menus` VALUES (5, '2024-09-03 15:27:44.576', '2024-09-03 15:27:44.576', '2024-09-03 15:28:01.480', 4, 1); 233 | INSERT INTO `role_menus` VALUES (6, '2024-09-03 15:27:44.577', '2024-09-03 15:27:44.577', '2024-09-03 15:28:01.480', 4, 2); 234 | INSERT INTO `role_menus` VALUES (7, '2024-09-03 15:28:01.482', '2024-09-03 15:28:01.482', '2024-09-03 15:29:06.654', 4, 1); 235 | INSERT INTO `role_menus` VALUES (8, '2024-09-03 15:28:01.484', '2024-09-03 15:28:01.484', '2024-09-03 15:29:06.654', 4, 2); 236 | INSERT INTO `role_menus` VALUES (9, '2024-09-03 15:28:01.485', '2024-09-03 15:28:01.485', '2024-09-03 15:29:06.654', 4, 3); 237 | INSERT INTO `role_menus` VALUES (10, '2024-09-03 15:28:01.486', '2024-09-03 15:28:01.486', '2024-09-03 15:29:06.654', 4, 4); 238 | INSERT INTO `role_menus` VALUES (11, '2024-09-03 15:28:01.488', '2024-09-03 15:28:01.488', '2024-09-03 15:29:06.654', 4, 5); 239 | INSERT INTO `role_menus` VALUES (12, '2024-09-03 15:28:01.489', '2024-09-03 15:28:01.489', '2024-09-03 15:29:06.654', 4, 12); 240 | INSERT INTO `role_menus` VALUES (13, '2024-09-03 15:28:01.490', '2024-09-03 15:28:01.490', '2024-09-03 15:29:06.654', 4, 13); 241 | INSERT INTO `role_menus` VALUES (14, '2024-09-03 15:28:01.491', '2024-09-03 15:28:01.491', '2024-09-03 15:29:06.654', 4, 30); 242 | INSERT INTO `role_menus` VALUES (15, '2024-09-03 15:28:01.492', '2024-09-03 15:28:01.492', '2024-09-03 15:29:06.654', 4, 31); 243 | INSERT INTO `role_menus` VALUES (16, '2024-09-03 15:28:28.346', '2024-09-03 15:28:28.346', '2024-09-03 15:47:22.679', 1, 1); 244 | INSERT INTO `role_menus` VALUES (17, '2024-09-03 15:28:28.348', '2024-09-03 15:28:28.348', '2024-09-03 15:47:22.679', 1, 34); 245 | INSERT INTO `role_menus` VALUES (18, '2024-09-03 15:28:28.349', '2024-09-03 15:28:28.349', '2024-09-03 15:47:22.679', 1, 35); 246 | INSERT INTO `role_menus` VALUES (19, '2024-09-03 15:28:28.350', '2024-09-03 15:28:28.350', '2024-09-03 15:47:22.679', 1, 2); 247 | INSERT INTO `role_menus` VALUES (20, '2024-09-03 15:28:28.351', '2024-09-03 15:28:28.351', '2024-09-03 15:47:22.679', 1, 3); 248 | INSERT INTO `role_menus` VALUES (21, '2024-09-03 15:28:28.352', '2024-09-03 15:28:28.352', '2024-09-03 15:47:22.679', 1, 4); 249 | INSERT INTO `role_menus` VALUES (22, '2024-09-03 15:28:28.354', '2024-09-03 15:28:28.354', '2024-09-03 15:47:22.679', 1, 5); 250 | INSERT INTO `role_menus` VALUES (23, '2024-09-03 15:28:28.355', '2024-09-03 15:28:28.355', '2024-09-03 15:47:22.679', 1, 12); 251 | INSERT INTO `role_menus` VALUES (24, '2024-09-03 15:28:28.356', '2024-09-03 15:28:28.356', '2024-09-03 15:47:22.679', 1, 13); 252 | INSERT INTO `role_menus` VALUES (25, '2024-09-03 15:28:28.357', '2024-09-03 15:28:28.357', '2024-09-03 15:47:22.679', 1, 30); 253 | INSERT INTO `role_menus` VALUES (26, '2024-09-03 15:28:28.358', '2024-09-03 15:28:28.358', '2024-09-03 15:47:22.679', 1, 31); 254 | INSERT INTO `role_menus` VALUES (27, '2024-09-03 15:29:06.656', '2024-09-03 15:29:06.656', '2024-09-03 15:39:10.875', 4, 1); 255 | INSERT INTO `role_menus` VALUES (28, '2024-09-03 15:29:06.657', '2024-09-03 15:29:06.657', '2024-09-03 15:39:10.875', 4, 34); 256 | INSERT INTO `role_menus` VALUES (29, '2024-09-03 15:29:06.658', '2024-09-03 15:29:06.658', '2024-09-03 15:39:10.875', 4, 35); 257 | INSERT INTO `role_menus` VALUES (30, '2024-09-03 15:29:06.660', '2024-09-03 15:29:06.660', '2024-09-03 15:39:10.875', 4, 2); 258 | INSERT INTO `role_menus` VALUES (31, '2024-09-03 15:29:06.661', '2024-09-03 15:29:06.661', '2024-09-03 15:39:10.875', 4, 3); 259 | INSERT INTO `role_menus` VALUES (32, '2024-09-03 15:29:06.662', '2024-09-03 15:29:06.662', '2024-09-03 15:39:10.875', 4, 4); 260 | INSERT INTO `role_menus` VALUES (33, '2024-09-03 15:29:06.663', '2024-09-03 15:29:06.663', '2024-09-03 15:39:10.875', 4, 5); 261 | INSERT INTO `role_menus` VALUES (34, '2024-09-03 15:29:06.664', '2024-09-03 15:29:06.664', '2024-09-03 15:39:10.875', 4, 12); 262 | INSERT INTO `role_menus` VALUES (35, '2024-09-03 15:29:06.666', '2024-09-03 15:29:06.666', '2024-09-03 15:39:10.875', 4, 13); 263 | INSERT INTO `role_menus` VALUES (36, '2024-09-03 15:29:06.667', '2024-09-03 15:29:06.667', '2024-09-03 15:39:10.875', 4, 30); 264 | INSERT INTO `role_menus` VALUES (37, '2024-09-03 15:29:06.668', '2024-09-03 15:29:06.668', '2024-09-03 15:39:10.875', 4, 31); 265 | INSERT INTO `role_menus` VALUES (38, '2024-09-03 15:39:10.896', '2024-09-03 15:39:10.896', '2024-09-03 15:39:21.631', 4, 1); 266 | INSERT INTO `role_menus` VALUES (39, '2024-09-03 15:39:10.899', '2024-09-03 15:39:10.899', '2024-09-03 15:39:21.631', 4, 34); 267 | INSERT INTO `role_menus` VALUES (40, '2024-09-03 15:39:10.900', '2024-09-03 15:39:10.900', '2024-09-03 15:39:21.631', 4, 35); 268 | INSERT INTO `role_menus` VALUES (41, '2024-09-03 15:39:10.901', '2024-09-03 15:39:10.901', '2024-09-03 15:39:21.631', 4, 2); 269 | INSERT INTO `role_menus` VALUES (42, '2024-09-03 15:39:10.902', '2024-09-03 15:39:10.902', '2024-09-03 15:39:21.631', 4, 3); 270 | INSERT INTO `role_menus` VALUES (43, '2024-09-03 15:39:10.903', '2024-09-03 15:39:10.903', '2024-09-03 15:39:21.631', 4, 4); 271 | INSERT INTO `role_menus` VALUES (44, '2024-09-03 15:39:10.905', '2024-09-03 15:39:10.905', '2024-09-03 15:39:21.631', 4, 5); 272 | INSERT INTO `role_menus` VALUES (45, '2024-09-03 15:39:10.906', '2024-09-03 15:39:10.906', '2024-09-03 15:39:21.631', 4, 6); 273 | INSERT INTO `role_menus` VALUES (46, '2024-09-03 15:39:10.907', '2024-09-03 15:39:10.907', '2024-09-03 15:39:21.631', 4, 7); 274 | INSERT INTO `role_menus` VALUES (47, '2024-09-03 15:39:10.908', '2024-09-03 15:39:10.908', '2024-09-03 15:39:21.631', 4, 8); 275 | INSERT INTO `role_menus` VALUES (48, '2024-09-03 15:39:10.909', '2024-09-03 15:39:10.909', '2024-09-03 15:39:21.631', 4, 9); 276 | INSERT INTO `role_menus` VALUES (49, '2024-09-03 15:39:10.911', '2024-09-03 15:39:10.911', '2024-09-03 15:39:21.631', 4, 10); 277 | INSERT INTO `role_menus` VALUES (50, '2024-09-03 15:39:10.912', '2024-09-03 15:39:10.912', '2024-09-03 15:39:21.631', 4, 11); 278 | INSERT INTO `role_menus` VALUES (51, '2024-09-03 15:39:10.913', '2024-09-03 15:39:10.913', '2024-09-03 15:39:21.631', 4, 12); 279 | INSERT INTO `role_menus` VALUES (52, '2024-09-03 15:39:10.915', '2024-09-03 15:39:10.915', '2024-09-03 15:39:21.631', 4, 13); 280 | INSERT INTO `role_menus` VALUES (53, '2024-09-03 15:39:10.916', '2024-09-03 15:39:10.916', '2024-09-03 15:39:21.631', 4, 30); 281 | INSERT INTO `role_menus` VALUES (54, '2024-09-03 15:39:10.917', '2024-09-03 15:39:10.917', '2024-09-03 15:39:21.631', 4, 31); 282 | INSERT INTO `role_menus` VALUES (55, '2024-09-03 15:39:21.633', '2024-09-03 15:39:21.633', NULL, 4, 1); 283 | INSERT INTO `role_menus` VALUES (56, '2024-09-03 15:39:21.634', '2024-09-03 15:39:21.634', NULL, 4, 34); 284 | INSERT INTO `role_menus` VALUES (57, '2024-09-03 15:39:21.635', '2024-09-03 15:39:21.635', NULL, 4, 35); 285 | INSERT INTO `role_menus` VALUES (58, '2024-09-03 15:39:21.636', '2024-09-03 15:39:21.636', NULL, 4, 2); 286 | INSERT INTO `role_menus` VALUES (59, '2024-09-03 15:39:21.637', '2024-09-03 15:39:21.637', NULL, 4, 3); 287 | INSERT INTO `role_menus` VALUES (60, '2024-09-03 15:39:21.639', '2024-09-03 15:39:21.639', NULL, 4, 4); 288 | INSERT INTO `role_menus` VALUES (61, '2024-09-03 15:39:21.640', '2024-09-03 15:39:21.640', NULL, 4, 5); 289 | INSERT INTO `role_menus` VALUES (62, '2024-09-03 15:39:21.641', '2024-09-03 15:39:21.641', NULL, 4, 6); 290 | INSERT INTO `role_menus` VALUES (63, '2024-09-03 15:39:21.642', '2024-09-03 15:39:21.642', NULL, 4, 7); 291 | INSERT INTO `role_menus` VALUES (64, '2024-09-03 15:39:21.644', '2024-09-03 15:39:21.644', NULL, 4, 8); 292 | INSERT INTO `role_menus` VALUES (65, '2024-09-03 15:39:21.645', '2024-09-03 15:39:21.645', NULL, 4, 9); 293 | INSERT INTO `role_menus` VALUES (66, '2024-09-03 15:39:21.646', '2024-09-03 15:39:21.646', NULL, 4, 10); 294 | INSERT INTO `role_menus` VALUES (67, '2024-09-03 15:39:21.647', '2024-09-03 15:39:21.647', NULL, 4, 11); 295 | INSERT INTO `role_menus` VALUES (68, '2024-09-03 15:39:21.648', '2024-09-03 15:39:21.648', NULL, 4, 12); 296 | INSERT INTO `role_menus` VALUES (69, '2024-09-03 15:39:21.649', '2024-09-03 15:39:21.649', NULL, 4, 13); 297 | INSERT INTO `role_menus` VALUES (70, '2024-09-03 15:39:21.650', '2024-09-03 15:39:21.650', NULL, 4, 30); 298 | INSERT INTO `role_menus` VALUES (71, '2024-09-03 15:39:21.652', '2024-09-03 15:39:21.652', NULL, 4, 31); 299 | INSERT INTO `role_menus` VALUES (72, '2024-09-03 15:47:22.692', '2024-09-03 15:47:22.692', '2024-09-03 16:03:20.769', 1, 1); 300 | INSERT INTO `role_menus` VALUES (73, '2024-09-03 15:47:22.694', '2024-09-03 15:47:22.694', '2024-09-03 16:03:20.769', 1, 34); 301 | INSERT INTO `role_menus` VALUES (74, '2024-09-03 15:47:22.696', '2024-09-03 15:47:22.696', '2024-09-03 16:03:20.769', 1, 35); 302 | INSERT INTO `role_menus` VALUES (75, '2024-09-03 15:47:22.697', '2024-09-03 15:47:22.697', '2024-09-03 16:03:20.769', 1, 2); 303 | INSERT INTO `role_menus` VALUES (76, '2024-09-03 15:47:22.699', '2024-09-03 15:47:22.699', '2024-09-03 16:03:20.769', 1, 3); 304 | INSERT INTO `role_menus` VALUES (77, '2024-09-03 15:47:22.700', '2024-09-03 15:47:22.700', '2024-09-03 16:03:20.769', 1, 4); 305 | INSERT INTO `role_menus` VALUES (78, '2024-09-03 15:47:22.701', '2024-09-03 15:47:22.701', '2024-09-03 16:03:20.769', 1, 5); 306 | INSERT INTO `role_menus` VALUES (79, '2024-09-03 15:47:22.702', '2024-09-03 15:47:22.702', '2024-09-03 16:03:20.769', 1, 6); 307 | INSERT INTO `role_menus` VALUES (80, '2024-09-03 15:47:22.703', '2024-09-03 15:47:22.703', '2024-09-03 16:03:20.769', 1, 7); 308 | INSERT INTO `role_menus` VALUES (81, '2024-09-03 15:47:22.704', '2024-09-03 15:47:22.704', '2024-09-03 16:03:20.769', 1, 12); 309 | INSERT INTO `role_menus` VALUES (82, '2024-09-03 15:47:22.705', '2024-09-03 15:47:22.705', '2024-09-03 16:03:20.769', 1, 13); 310 | INSERT INTO `role_menus` VALUES (83, '2024-09-03 15:47:22.707', '2024-09-03 15:47:22.707', '2024-09-03 16:03:20.769', 1, 30); 311 | INSERT INTO `role_menus` VALUES (84, '2024-09-03 15:47:22.708', '2024-09-03 15:47:22.708', '2024-09-03 16:03:20.769', 1, 31); 312 | INSERT INTO `role_menus` VALUES (85, '2024-09-03 16:03:20.782', '2024-09-03 16:03:20.782', '2024-09-03 16:04:47.726', 1, 1); 313 | INSERT INTO `role_menus` VALUES (86, '2024-09-03 16:03:20.786', '2024-09-03 16:03:20.786', '2024-09-03 16:04:47.726', 1, 34); 314 | INSERT INTO `role_menus` VALUES (87, '2024-09-03 16:03:20.789', '2024-09-03 16:03:20.789', '2024-09-03 16:04:47.726', 1, 35); 315 | INSERT INTO `role_menus` VALUES (88, '2024-09-03 16:03:20.791', '2024-09-03 16:03:20.791', '2024-09-03 16:04:47.726', 1, 2); 316 | INSERT INTO `role_menus` VALUES (89, '2024-09-03 16:03:20.792', '2024-09-03 16:03:20.792', '2024-09-03 16:04:47.726', 1, 3); 317 | INSERT INTO `role_menus` VALUES (90, '2024-09-03 16:03:20.794', '2024-09-03 16:03:20.794', '2024-09-03 16:04:47.726', 1, 4); 318 | INSERT INTO `role_menus` VALUES (91, '2024-09-03 16:03:20.796', '2024-09-03 16:03:20.796', '2024-09-03 16:04:47.726', 1, 5); 319 | INSERT INTO `role_menus` VALUES (92, '2024-09-03 16:03:20.799', '2024-09-03 16:03:20.799', '2024-09-03 16:04:47.726', 1, 6); 320 | INSERT INTO `role_menus` VALUES (93, '2024-09-03 16:03:20.801', '2024-09-03 16:03:20.801', '2024-09-03 16:04:47.726', 1, 7); 321 | INSERT INTO `role_menus` VALUES (94, '2024-09-03 16:03:20.803', '2024-09-03 16:03:20.803', '2024-09-03 16:04:47.726', 1, 8); 322 | INSERT INTO `role_menus` VALUES (95, '2024-09-03 16:03:20.806', '2024-09-03 16:03:20.806', '2024-09-03 16:04:47.726', 1, 9); 323 | INSERT INTO `role_menus` VALUES (96, '2024-09-03 16:03:20.808', '2024-09-03 16:03:20.808', '2024-09-03 16:04:47.726', 1, 10); 324 | INSERT INTO `role_menus` VALUES (97, '2024-09-03 16:03:20.810', '2024-09-03 16:03:20.810', '2024-09-03 16:04:47.726', 1, 11); 325 | INSERT INTO `role_menus` VALUES (98, '2024-09-03 16:03:20.811', '2024-09-03 16:03:20.811', '2024-09-03 16:04:47.726', 1, 12); 326 | INSERT INTO `role_menus` VALUES (99, '2024-09-03 16:03:20.813', '2024-09-03 16:03:20.813', '2024-09-03 16:04:47.726', 1, 13); 327 | INSERT INTO `role_menus` VALUES (100, '2024-09-03 16:03:20.814', '2024-09-03 16:03:20.814', '2024-09-03 16:04:47.726', 1, 30); 328 | INSERT INTO `role_menus` VALUES (101, '2024-09-03 16:03:20.815', '2024-09-03 16:03:20.815', '2024-09-03 16:04:47.726', 1, 31); 329 | INSERT INTO `role_menus` VALUES (102, '2024-09-03 16:04:47.741', '2024-09-03 16:04:47.741', '2024-09-03 16:06:39.977', 1, 1); 330 | INSERT INTO `role_menus` VALUES (103, '2024-09-03 16:04:47.743', '2024-09-03 16:04:47.743', '2024-09-03 16:06:39.977', 1, 34); 331 | INSERT INTO `role_menus` VALUES (104, '2024-09-03 16:04:47.746', '2024-09-03 16:04:47.746', '2024-09-03 16:06:39.977', 1, 35); 332 | INSERT INTO `role_menus` VALUES (105, '2024-09-03 16:04:47.748', '2024-09-03 16:04:47.748', '2024-09-03 16:06:39.977', 1, 2); 333 | INSERT INTO `role_menus` VALUES (106, '2024-09-03 16:04:47.749', '2024-09-03 16:04:47.749', '2024-09-03 16:06:39.977', 1, 3); 334 | INSERT INTO `role_menus` VALUES (107, '2024-09-03 16:04:47.751', '2024-09-03 16:04:47.751', '2024-09-03 16:06:39.977', 1, 4); 335 | INSERT INTO `role_menus` VALUES (108, '2024-09-03 16:04:47.752', '2024-09-03 16:04:47.752', '2024-09-03 16:06:39.977', 1, 5); 336 | INSERT INTO `role_menus` VALUES (109, '2024-09-03 16:04:47.755', '2024-09-03 16:04:47.755', '2024-09-03 16:06:39.977', 1, 6); 337 | INSERT INTO `role_menus` VALUES (110, '2024-09-03 16:04:47.758', '2024-09-03 16:04:47.758', '2024-09-03 16:06:39.977', 1, 8); 338 | INSERT INTO `role_menus` VALUES (111, '2024-09-03 16:04:47.760', '2024-09-03 16:04:47.760', '2024-09-03 16:06:39.977', 1, 9); 339 | INSERT INTO `role_menus` VALUES (112, '2024-09-03 16:04:47.763', '2024-09-03 16:04:47.763', '2024-09-03 16:06:39.977', 1, 10); 340 | INSERT INTO `role_menus` VALUES (113, '2024-09-03 16:04:47.764', '2024-09-03 16:04:47.764', '2024-09-03 16:06:39.977', 1, 11); 341 | INSERT INTO `role_menus` VALUES (114, '2024-09-03 16:04:47.765', '2024-09-03 16:04:47.765', '2024-09-03 16:06:39.977', 1, 12); 342 | INSERT INTO `role_menus` VALUES (115, '2024-09-03 16:04:47.766', '2024-09-03 16:04:47.766', '2024-09-03 16:06:39.977', 1, 13); 343 | INSERT INTO `role_menus` VALUES (116, '2024-09-03 16:04:47.768', '2024-09-03 16:04:47.768', '2024-09-03 16:06:39.977', 1, 30); 344 | INSERT INTO `role_menus` VALUES (117, '2024-09-03 16:04:47.769', '2024-09-03 16:04:47.769', '2024-09-03 16:06:39.977', 1, 31); 345 | INSERT INTO `role_menus` VALUES (118, '2024-09-03 16:06:39.994', '2024-09-03 16:06:39.994', '2024-09-03 16:07:27.371', 1, 1); 346 | INSERT INTO `role_menus` VALUES (119, '2024-09-03 16:06:39.997', '2024-09-03 16:06:39.997', '2024-09-03 16:07:27.371', 1, 34); 347 | INSERT INTO `role_menus` VALUES (120, '2024-09-03 16:06:40.000', '2024-09-03 16:06:40.000', '2024-09-03 16:07:27.371', 1, 35); 348 | INSERT INTO `role_menus` VALUES (121, '2024-09-03 16:06:40.002', '2024-09-03 16:06:40.002', '2024-09-03 16:07:27.371', 1, 2); 349 | INSERT INTO `role_menus` VALUES (122, '2024-09-03 16:06:40.003', '2024-09-03 16:06:40.003', '2024-09-03 16:07:27.371', 1, 3); 350 | INSERT INTO `role_menus` VALUES (123, '2024-09-03 16:06:40.006', '2024-09-03 16:06:40.006', '2024-09-03 16:07:27.371', 1, 4); 351 | INSERT INTO `role_menus` VALUES (124, '2024-09-03 16:06:40.007', '2024-09-03 16:06:40.007', '2024-09-03 16:07:27.371', 1, 5); 352 | INSERT INTO `role_menus` VALUES (125, '2024-09-03 16:06:40.009', '2024-09-03 16:06:40.009', '2024-09-03 16:07:27.371', 1, 6); 353 | INSERT INTO `role_menus` VALUES (126, '2024-09-03 16:06:40.012', '2024-09-03 16:06:40.012', '2024-09-03 16:07:27.371', 1, 8); 354 | INSERT INTO `role_menus` VALUES (127, '2024-09-03 16:06:40.015', '2024-09-03 16:06:40.015', '2024-09-03 16:07:27.371', 1, 9); 355 | INSERT INTO `role_menus` VALUES (128, '2024-09-03 16:06:40.017', '2024-09-03 16:06:40.017', '2024-09-03 16:07:27.371', 1, 10); 356 | INSERT INTO `role_menus` VALUES (129, '2024-09-03 16:06:40.019', '2024-09-03 16:06:40.019', '2024-09-03 16:07:27.371', 1, 11); 357 | INSERT INTO `role_menus` VALUES (130, '2024-09-03 16:06:40.020', '2024-09-03 16:06:40.020', '2024-09-03 16:07:27.371', 1, 12); 358 | INSERT INTO `role_menus` VALUES (131, '2024-09-03 16:06:40.021', '2024-09-03 16:06:40.021', '2024-09-03 16:07:27.371', 1, 13); 359 | INSERT INTO `role_menus` VALUES (132, '2024-09-03 16:06:40.023', '2024-09-03 16:06:40.023', '2024-09-03 16:07:27.371', 1, 30); 360 | INSERT INTO `role_menus` VALUES (133, '2024-09-03 16:06:40.024', '2024-09-03 16:06:40.024', '2024-09-03 16:07:27.371', 1, 31); 361 | INSERT INTO `role_menus` VALUES (134, '2024-09-03 16:07:27.383', '2024-09-03 16:07:27.383', '2024-09-03 16:08:55.049', 1, 1); 362 | INSERT INTO `role_menus` VALUES (135, '2024-09-03 16:07:27.386', '2024-09-03 16:07:27.386', '2024-09-03 16:08:55.049', 1, 34); 363 | INSERT INTO `role_menus` VALUES (136, '2024-09-03 16:07:27.388', '2024-09-03 16:07:27.388', '2024-09-03 16:08:55.049', 1, 35); 364 | INSERT INTO `role_menus` VALUES (137, '2024-09-03 16:07:27.390', '2024-09-03 16:07:27.390', '2024-09-03 16:08:55.049', 1, 2); 365 | INSERT INTO `role_menus` VALUES (138, '2024-09-03 16:07:27.391', '2024-09-03 16:07:27.391', '2024-09-03 16:08:55.049', 1, 3); 366 | INSERT INTO `role_menus` VALUES (139, '2024-09-03 16:07:27.393', '2024-09-03 16:07:27.393', '2024-09-03 16:08:55.049', 1, 4); 367 | INSERT INTO `role_menus` VALUES (140, '2024-09-03 16:07:27.394', '2024-09-03 16:07:27.394', '2024-09-03 16:08:55.049', 1, 5); 368 | INSERT INTO `role_menus` VALUES (141, '2024-09-03 16:07:27.396', '2024-09-03 16:07:27.396', '2024-09-03 16:08:55.049', 1, 6); 369 | INSERT INTO `role_menus` VALUES (142, '2024-09-03 16:07:27.399', '2024-09-03 16:07:27.399', '2024-09-03 16:08:55.049', 1, 7); 370 | INSERT INTO `role_menus` VALUES (143, '2024-09-03 16:07:27.402', '2024-09-03 16:07:27.402', '2024-09-03 16:08:55.049', 1, 8); 371 | INSERT INTO `role_menus` VALUES (144, '2024-09-03 16:07:27.404', '2024-09-03 16:07:27.404', '2024-09-03 16:08:55.049', 1, 9); 372 | INSERT INTO `role_menus` VALUES (145, '2024-09-03 16:07:27.406', '2024-09-03 16:07:27.406', '2024-09-03 16:08:55.049', 1, 10); 373 | INSERT INTO `role_menus` VALUES (146, '2024-09-03 16:07:27.407', '2024-09-03 16:07:27.407', '2024-09-03 16:08:55.049', 1, 11); 374 | INSERT INTO `role_menus` VALUES (147, '2024-09-03 16:07:27.408', '2024-09-03 16:07:27.408', '2024-09-03 16:08:55.049', 1, 12); 375 | INSERT INTO `role_menus` VALUES (148, '2024-09-03 16:07:27.409', '2024-09-03 16:07:27.409', '2024-09-03 16:08:55.049', 1, 13); 376 | INSERT INTO `role_menus` VALUES (149, '2024-09-03 16:07:27.411', '2024-09-03 16:07:27.411', '2024-09-03 16:08:55.049', 1, 30); 377 | INSERT INTO `role_menus` VALUES (150, '2024-09-03 16:07:27.412', '2024-09-03 16:07:27.412', '2024-09-03 16:08:55.049', 1, 31); 378 | INSERT INTO `role_menus` VALUES (151, '2024-09-03 16:08:55.063', '2024-09-03 16:08:55.063', '2024-09-03 16:09:10.714', 1, 1); 379 | INSERT INTO `role_menus` VALUES (152, '2024-09-03 16:08:55.065', '2024-09-03 16:08:55.065', '2024-09-03 16:09:10.714', 1, 34); 380 | INSERT INTO `role_menus` VALUES (153, '2024-09-03 16:08:55.068', '2024-09-03 16:08:55.068', '2024-09-03 16:09:10.714', 1, 35); 381 | INSERT INTO `role_menus` VALUES (154, '2024-09-03 16:08:55.070', '2024-09-03 16:08:55.070', '2024-09-03 16:09:10.714', 1, 2); 382 | INSERT INTO `role_menus` VALUES (155, '2024-09-03 16:08:55.071', '2024-09-03 16:08:55.071', '2024-09-03 16:09:10.714', 1, 3); 383 | INSERT INTO `role_menus` VALUES (156, '2024-09-03 16:08:55.073', '2024-09-03 16:08:55.073', '2024-09-03 16:09:10.714', 1, 4); 384 | INSERT INTO `role_menus` VALUES (157, '2024-09-03 16:08:55.074', '2024-09-03 16:08:55.074', '2024-09-03 16:09:10.714', 1, 5); 385 | INSERT INTO `role_menus` VALUES (158, '2024-09-03 16:08:55.077', '2024-09-03 16:08:55.077', '2024-09-03 16:09:10.714', 1, 6); 386 | INSERT INTO `role_menus` VALUES (159, '2024-09-03 16:08:55.079', '2024-09-03 16:08:55.079', '2024-09-03 16:09:10.714', 1, 8); 387 | INSERT INTO `role_menus` VALUES (160, '2024-09-03 16:08:55.081', '2024-09-03 16:08:55.081', '2024-09-03 16:09:10.714', 1, 9); 388 | INSERT INTO `role_menus` VALUES (161, '2024-09-03 16:08:55.083', '2024-09-03 16:08:55.083', '2024-09-03 16:09:10.714', 1, 10); 389 | INSERT INTO `role_menus` VALUES (162, '2024-09-03 16:08:55.084', '2024-09-03 16:08:55.084', '2024-09-03 16:09:10.714', 1, 11); 390 | INSERT INTO `role_menus` VALUES (163, '2024-09-03 16:08:55.086', '2024-09-03 16:08:55.086', '2024-09-03 16:09:10.714', 1, 12); 391 | INSERT INTO `role_menus` VALUES (164, '2024-09-03 16:08:55.087', '2024-09-03 16:08:55.087', '2024-09-03 16:09:10.714', 1, 13); 392 | INSERT INTO `role_menus` VALUES (165, '2024-09-03 16:08:55.088', '2024-09-03 16:08:55.088', '2024-09-03 16:09:10.714', 1, 30); 393 | INSERT INTO `role_menus` VALUES (166, '2024-09-03 16:08:55.089', '2024-09-03 16:08:55.089', '2024-09-03 16:09:10.714', 1, 31); 394 | INSERT INTO `role_menus` VALUES (167, '2024-09-03 16:09:10.726', '2024-09-03 16:09:10.726', '2024-09-03 16:15:20.728', 1, 1); 395 | INSERT INTO `role_menus` VALUES (168, '2024-09-03 16:09:10.728', '2024-09-03 16:09:10.728', '2024-09-03 16:15:20.728', 1, 34); 396 | INSERT INTO `role_menus` VALUES (169, '2024-09-03 16:09:10.731', '2024-09-03 16:09:10.731', '2024-09-03 16:15:20.728', 1, 35); 397 | INSERT INTO `role_menus` VALUES (170, '2024-09-03 16:09:10.733', '2024-09-03 16:09:10.733', '2024-09-03 16:15:20.728', 1, 2); 398 | INSERT INTO `role_menus` VALUES (171, '2024-09-03 16:09:10.734', '2024-09-03 16:09:10.734', '2024-09-03 16:15:20.728', 1, 3); 399 | INSERT INTO `role_menus` VALUES (172, '2024-09-03 16:09:10.736', '2024-09-03 16:09:10.736', '2024-09-03 16:15:20.728', 1, 4); 400 | INSERT INTO `role_menus` VALUES (173, '2024-09-03 16:09:10.738', '2024-09-03 16:09:10.738', '2024-09-03 16:15:20.728', 1, 5); 401 | INSERT INTO `role_menus` VALUES (174, '2024-09-03 16:09:10.740', '2024-09-03 16:09:10.740', '2024-09-03 16:15:20.728', 1, 6); 402 | INSERT INTO `role_menus` VALUES (175, '2024-09-03 16:09:10.742', '2024-09-03 16:09:10.742', '2024-09-03 16:15:20.728', 1, 7); 403 | INSERT INTO `role_menus` VALUES (176, '2024-09-03 16:09:10.744', '2024-09-03 16:09:10.744', '2024-09-03 16:15:20.728', 1, 8); 404 | INSERT INTO `role_menus` VALUES (177, '2024-09-03 16:09:10.746', '2024-09-03 16:09:10.746', '2024-09-03 16:15:20.728', 1, 9); 405 | INSERT INTO `role_menus` VALUES (178, '2024-09-03 16:09:10.748', '2024-09-03 16:09:10.748', '2024-09-03 16:15:20.728', 1, 10); 406 | INSERT INTO `role_menus` VALUES (179, '2024-09-03 16:09:10.749', '2024-09-03 16:09:10.749', '2024-09-03 16:15:20.728', 1, 11); 407 | INSERT INTO `role_menus` VALUES (180, '2024-09-03 16:09:10.750', '2024-09-03 16:09:10.750', '2024-09-03 16:15:20.728', 1, 12); 408 | INSERT INTO `role_menus` VALUES (181, '2024-09-03 16:09:10.751', '2024-09-03 16:09:10.751', '2024-09-03 16:15:20.728', 1, 13); 409 | INSERT INTO `role_menus` VALUES (182, '2024-09-03 16:09:10.753', '2024-09-03 16:09:10.753', '2024-09-03 16:15:20.728', 1, 30); 410 | INSERT INTO `role_menus` VALUES (183, '2024-09-03 16:09:10.754', '2024-09-03 16:09:10.754', '2024-09-03 16:15:20.728', 1, 31); 411 | INSERT INTO `role_menus` VALUES (184, '2024-09-03 16:15:20.747', '2024-09-03 16:15:20.747', '2024-09-03 16:32:20.058', 1, 1); 412 | INSERT INTO `role_menus` VALUES (185, '2024-09-03 16:15:20.751', '2024-09-03 16:15:20.751', '2024-09-03 16:32:20.058', 1, 34); 413 | INSERT INTO `role_menus` VALUES (186, '2024-09-03 16:15:20.753', '2024-09-03 16:15:20.753', '2024-09-03 16:32:20.058', 1, 35); 414 | INSERT INTO `role_menus` VALUES (187, '2024-09-03 16:15:20.754', '2024-09-03 16:15:20.754', '2024-09-03 16:32:20.058', 1, 2); 415 | INSERT INTO `role_menus` VALUES (188, '2024-09-03 16:15:20.756', '2024-09-03 16:15:20.756', '2024-09-03 16:32:20.058', 1, 12); 416 | INSERT INTO `role_menus` VALUES (189, '2024-09-03 16:15:20.757', '2024-09-03 16:15:20.757', '2024-09-03 16:32:20.058', 1, 13); 417 | INSERT INTO `role_menus` VALUES (190, '2024-09-03 16:15:20.758', '2024-09-03 16:15:20.758', '2024-09-03 16:32:20.058', 1, 30); 418 | INSERT INTO `role_menus` VALUES (191, '2024-09-03 16:15:20.761', '2024-09-03 16:15:20.761', '2024-09-03 16:32:20.058', 1, 31); 419 | INSERT INTO `role_menus` VALUES (192, '2024-09-03 16:32:20.075', '2024-09-03 16:32:20.075', '2024-09-03 16:48:17.137', 1, 1); 420 | INSERT INTO `role_menus` VALUES (193, '2024-09-03 16:32:20.078', '2024-09-03 16:32:20.078', '2024-09-03 16:48:17.137', 1, 34); 421 | INSERT INTO `role_menus` VALUES (194, '2024-09-03 16:32:20.081', '2024-09-03 16:32:20.081', '2024-09-03 16:48:17.137', 1, 35); 422 | INSERT INTO `role_menus` VALUES (195, '2024-09-03 16:32:20.083', '2024-09-03 16:32:20.083', '2024-09-03 16:48:17.137', 1, 2); 423 | INSERT INTO `role_menus` VALUES (196, '2024-09-03 16:32:20.084', '2024-09-03 16:32:20.084', '2024-09-03 16:48:17.137', 1, 3); 424 | INSERT INTO `role_menus` VALUES (197, '2024-09-03 16:32:20.087', '2024-09-03 16:32:20.087', '2024-09-03 16:48:17.137', 1, 4); 425 | INSERT INTO `role_menus` VALUES (198, '2024-09-03 16:32:20.088', '2024-09-03 16:32:20.088', '2024-09-03 16:48:17.137', 1, 5); 426 | INSERT INTO `role_menus` VALUES (199, '2024-09-03 16:32:20.091', '2024-09-03 16:32:20.091', '2024-09-03 16:48:17.137', 1, 7); 427 | INSERT INTO `role_menus` VALUES (200, '2024-09-03 16:32:20.093', '2024-09-03 16:32:20.093', '2024-09-03 16:48:17.137', 1, 8); 428 | INSERT INTO `role_menus` VALUES (201, '2024-09-03 16:32:20.095', '2024-09-03 16:32:20.095', '2024-09-03 16:48:17.137', 1, 9); 429 | INSERT INTO `role_menus` VALUES (202, '2024-09-03 16:32:20.097', '2024-09-03 16:32:20.097', '2024-09-03 16:48:17.137', 1, 10); 430 | INSERT INTO `role_menus` VALUES (203, '2024-09-03 16:32:20.098', '2024-09-03 16:32:20.098', '2024-09-03 16:48:17.137', 1, 11); 431 | INSERT INTO `role_menus` VALUES (204, '2024-09-03 16:32:20.099', '2024-09-03 16:32:20.099', '2024-09-03 16:48:17.137', 1, 12); 432 | INSERT INTO `role_menus` VALUES (205, '2024-09-03 16:32:20.101', '2024-09-03 16:32:20.101', '2024-09-03 16:48:17.137', 1, 13); 433 | INSERT INTO `role_menus` VALUES (206, '2024-09-03 16:32:20.102', '2024-09-03 16:32:20.102', '2024-09-03 16:48:17.137', 1, 30); 434 | INSERT INTO `role_menus` VALUES (207, '2024-09-03 16:32:20.103', '2024-09-03 16:32:20.103', '2024-09-03 16:48:17.137', 1, 31); 435 | INSERT INTO `role_menus` VALUES (208, '2024-09-03 16:48:17.150', '2024-09-03 16:48:17.150', '2024-09-03 16:51:15.512', 1, 1); 436 | INSERT INTO `role_menus` VALUES (209, '2024-09-03 16:48:17.153', '2024-09-03 16:48:17.153', '2024-09-03 16:51:15.512', 1, 34); 437 | INSERT INTO `role_menus` VALUES (210, '2024-09-03 16:48:17.156', '2024-09-03 16:48:17.156', '2024-09-03 16:51:15.512', 1, 35); 438 | INSERT INTO `role_menus` VALUES (211, '2024-09-03 16:48:17.157', '2024-09-03 16:48:17.157', '2024-09-03 16:51:15.512', 1, 2); 439 | INSERT INTO `role_menus` VALUES (212, '2024-09-03 16:48:17.158', '2024-09-03 16:48:17.158', '2024-09-03 16:51:15.512', 1, 3); 440 | INSERT INTO `role_menus` VALUES (213, '2024-09-03 16:48:17.162', '2024-09-03 16:48:17.162', '2024-09-03 16:51:15.512', 1, 4); 441 | INSERT INTO `role_menus` VALUES (214, '2024-09-03 16:48:17.163', '2024-09-03 16:48:17.163', '2024-09-03 16:51:15.512', 1, 5); 442 | INSERT INTO `role_menus` VALUES (215, '2024-09-03 16:48:17.166', '2024-09-03 16:48:17.166', '2024-09-03 16:51:15.512', 1, 7); 443 | INSERT INTO `role_menus` VALUES (216, '2024-09-03 16:48:17.170', '2024-09-03 16:48:17.170', '2024-09-03 16:51:15.512', 1, 8); 444 | INSERT INTO `role_menus` VALUES (217, '2024-09-03 16:48:17.173', '2024-09-03 16:48:17.173', '2024-09-03 16:51:15.512', 1, 9); 445 | INSERT INTO `role_menus` VALUES (218, '2024-09-03 16:48:17.176', '2024-09-03 16:48:17.176', '2024-09-03 16:51:15.512', 1, 10); 446 | INSERT INTO `role_menus` VALUES (219, '2024-09-03 16:48:17.178', '2024-09-03 16:48:17.178', '2024-09-03 16:51:15.512', 1, 11); 447 | INSERT INTO `role_menus` VALUES (220, '2024-09-03 16:48:17.180', '2024-09-03 16:48:17.180', '2024-09-03 16:51:15.512', 1, 12); 448 | INSERT INTO `role_menus` VALUES (221, '2024-09-03 16:48:17.181', '2024-09-03 16:48:17.181', '2024-09-03 16:51:15.512', 1, 13); 449 | INSERT INTO `role_menus` VALUES (222, '2024-09-03 16:48:17.182', '2024-09-03 16:48:17.182', '2024-09-03 16:51:15.512', 1, 30); 450 | INSERT INTO `role_menus` VALUES (223, '2024-09-03 16:48:17.183', '2024-09-03 16:48:17.183', '2024-09-03 16:51:15.512', 1, 31); 451 | INSERT INTO `role_menus` VALUES (224, '2024-09-03 16:51:15.525', '2024-09-03 16:51:15.525', '2024-09-03 16:51:23.574', 1, 2); 452 | INSERT INTO `role_menus` VALUES (225, '2024-09-03 16:51:15.527', '2024-09-03 16:51:15.527', '2024-09-03 16:51:23.574', 1, 1); 453 | INSERT INTO `role_menus` VALUES (226, '2024-09-03 16:51:15.530', '2024-09-03 16:51:15.530', '2024-09-03 16:51:23.574', 1, 34); 454 | INSERT INTO `role_menus` VALUES (227, '2024-09-03 16:51:15.532', '2024-09-03 16:51:15.532', '2024-09-03 16:51:23.574', 1, 35); 455 | INSERT INTO `role_menus` VALUES (228, '2024-09-03 16:51:15.534', '2024-09-03 16:51:15.534', '2024-09-03 16:51:23.574', 1, 3); 456 | INSERT INTO `role_menus` VALUES (229, '2024-09-03 16:51:15.537', '2024-09-03 16:51:15.537', '2024-09-03 16:51:23.574', 1, 4); 457 | INSERT INTO `role_menus` VALUES (230, '2024-09-03 16:51:15.539', '2024-09-03 16:51:15.539', '2024-09-03 16:51:23.574', 1, 5); 458 | INSERT INTO `role_menus` VALUES (231, '2024-09-03 16:51:15.542', '2024-09-03 16:51:15.542', '2024-09-03 16:51:23.574', 1, 6); 459 | INSERT INTO `role_menus` VALUES (232, '2024-09-03 16:51:15.545', '2024-09-03 16:51:15.545', '2024-09-03 16:51:23.574', 1, 7); 460 | INSERT INTO `role_menus` VALUES (233, '2024-09-03 16:51:15.548', '2024-09-03 16:51:15.548', '2024-09-03 16:51:23.574', 1, 8); 461 | INSERT INTO `role_menus` VALUES (234, '2024-09-03 16:51:15.551', '2024-09-03 16:51:15.551', '2024-09-03 16:51:23.574', 1, 9); 462 | INSERT INTO `role_menus` VALUES (235, '2024-09-03 16:51:15.555', '2024-09-03 16:51:15.555', '2024-09-03 16:51:23.574', 1, 10); 463 | INSERT INTO `role_menus` VALUES (236, '2024-09-03 16:51:15.558', '2024-09-03 16:51:15.558', '2024-09-03 16:51:23.574', 1, 11); 464 | INSERT INTO `role_menus` VALUES (237, '2024-09-03 16:51:15.560', '2024-09-03 16:51:15.560', '2024-09-03 16:51:23.574', 1, 12); 465 | INSERT INTO `role_menus` VALUES (238, '2024-09-03 16:51:15.561', '2024-09-03 16:51:15.561', '2024-09-03 16:51:23.574', 1, 13); 466 | INSERT INTO `role_menus` VALUES (239, '2024-09-03 16:51:15.562', '2024-09-03 16:51:15.562', '2024-09-03 16:51:23.574', 1, 30); 467 | INSERT INTO `role_menus` VALUES (240, '2024-09-03 16:51:15.563', '2024-09-03 16:51:15.563', '2024-09-03 16:51:23.574', 1, 31); 468 | INSERT INTO `role_menus` VALUES (241, '2024-09-03 16:51:23.586', '2024-09-03 16:51:23.586', '2024-09-03 16:57:00.306', 1, 1); 469 | INSERT INTO `role_menus` VALUES (242, '2024-09-03 16:51:23.589', '2024-09-03 16:51:23.589', '2024-09-03 16:57:00.306', 1, 34); 470 | INSERT INTO `role_menus` VALUES (243, '2024-09-03 16:51:23.593', '2024-09-03 16:51:23.593', '2024-09-03 16:57:00.306', 1, 35); 471 | INSERT INTO `role_menus` VALUES (244, '2024-09-03 16:51:23.594', '2024-09-03 16:51:23.594', '2024-09-03 16:57:00.306', 1, 3); 472 | INSERT INTO `role_menus` VALUES (245, '2024-09-03 16:51:23.597', '2024-09-03 16:51:23.597', '2024-09-03 16:57:00.306', 1, 4); 473 | INSERT INTO `role_menus` VALUES (246, '2024-09-03 16:51:23.598', '2024-09-03 16:51:23.598', '2024-09-03 16:57:00.306', 1, 5); 474 | INSERT INTO `role_menus` VALUES (247, '2024-09-03 16:51:23.601', '2024-09-03 16:51:23.601', '2024-09-03 16:57:00.306', 1, 6); 475 | INSERT INTO `role_menus` VALUES (248, '2024-09-03 16:51:23.604', '2024-09-03 16:51:23.604', '2024-09-03 16:57:00.306', 1, 7); 476 | INSERT INTO `role_menus` VALUES (249, '2024-09-03 16:51:23.608', '2024-09-03 16:51:23.608', '2024-09-03 16:57:00.306', 1, 8); 477 | INSERT INTO `role_menus` VALUES (250, '2024-09-03 16:51:23.611', '2024-09-03 16:51:23.611', '2024-09-03 16:57:00.306', 1, 9); 478 | INSERT INTO `role_menus` VALUES (251, '2024-09-03 16:51:23.614', '2024-09-03 16:51:23.614', '2024-09-03 16:57:00.306', 1, 10); 479 | INSERT INTO `role_menus` VALUES (252, '2024-09-03 16:51:23.616', '2024-09-03 16:51:23.616', '2024-09-03 16:57:00.306', 1, 11); 480 | INSERT INTO `role_menus` VALUES (253, '2024-09-03 16:51:23.617', '2024-09-03 16:51:23.617', '2024-09-03 16:57:00.306', 1, 12); 481 | INSERT INTO `role_menus` VALUES (254, '2024-09-03 16:51:23.618', '2024-09-03 16:51:23.618', '2024-09-03 16:57:00.306', 1, 13); 482 | INSERT INTO `role_menus` VALUES (255, '2024-09-03 16:51:23.620', '2024-09-03 16:51:23.620', '2024-09-03 16:57:00.306', 1, 30); 483 | INSERT INTO `role_menus` VALUES (256, '2024-09-03 16:51:23.621', '2024-09-03 16:51:23.621', '2024-09-03 16:57:00.306', 1, 31); 484 | INSERT INTO `role_menus` VALUES (257, '2024-09-03 16:57:00.322', '2024-09-03 16:57:00.322', '2024-09-03 16:58:02.062', 1, 1); 485 | INSERT INTO `role_menus` VALUES (258, '2024-09-03 16:57:00.325', '2024-09-03 16:57:00.325', '2024-09-03 16:58:02.062', 1, 34); 486 | INSERT INTO `role_menus` VALUES (259, '2024-09-03 16:57:00.329', '2024-09-03 16:57:00.329', '2024-09-03 16:58:02.062', 1, 35); 487 | INSERT INTO `role_menus` VALUES (260, '2024-09-03 16:57:00.330', '2024-09-03 16:57:00.330', '2024-09-03 16:58:02.062', 1, 12); 488 | INSERT INTO `role_menus` VALUES (261, '2024-09-03 16:57:00.332', '2024-09-03 16:57:00.332', '2024-09-03 16:58:02.062', 1, 13); 489 | INSERT INTO `role_menus` VALUES (262, '2024-09-03 16:57:00.333', '2024-09-03 16:57:00.333', '2024-09-03 16:58:02.062', 1, 30); 490 | INSERT INTO `role_menus` VALUES (263, '2024-09-03 16:57:00.334', '2024-09-03 16:57:00.334', '2024-09-03 16:58:02.062', 1, 31); 491 | INSERT INTO `role_menus` VALUES (264, '2024-09-03 16:58:02.075', '2024-09-03 16:58:02.075', '2024-09-04 09:56:14.748', 1, 1); 492 | INSERT INTO `role_menus` VALUES (265, '2024-09-03 16:58:02.079', '2024-09-03 16:58:02.079', '2024-09-04 09:56:14.748', 1, 34); 493 | INSERT INTO `role_menus` VALUES (266, '2024-09-03 16:58:02.082', '2024-09-03 16:58:02.082', '2024-09-04 09:56:14.748', 1, 35); 494 | INSERT INTO `role_menus` VALUES (267, '2024-09-03 16:58:02.084', '2024-09-03 16:58:02.084', '2024-09-04 09:56:14.748', 1, 3); 495 | INSERT INTO `role_menus` VALUES (268, '2024-09-03 16:58:02.087', '2024-09-03 16:58:02.087', '2024-09-04 09:56:14.748', 1, 4); 496 | INSERT INTO `role_menus` VALUES (269, '2024-09-03 16:58:02.089', '2024-09-03 16:58:02.089', '2024-09-04 09:56:14.748', 1, 5); 497 | INSERT INTO `role_menus` VALUES (270, '2024-09-03 16:58:02.092', '2024-09-03 16:58:02.092', '2024-09-04 09:56:14.748', 1, 6); 498 | INSERT INTO `role_menus` VALUES (271, '2024-09-03 16:58:02.097', '2024-09-03 16:58:02.097', '2024-09-04 09:56:14.748', 1, 8); 499 | INSERT INTO `role_menus` VALUES (272, '2024-09-03 16:58:02.101', '2024-09-03 16:58:02.101', '2024-09-04 09:56:14.748', 1, 9); 500 | INSERT INTO `role_menus` VALUES (273, '2024-09-03 16:58:02.107', '2024-09-03 16:58:02.107', '2024-09-04 09:56:14.748', 1, 10); 501 | INSERT INTO `role_menus` VALUES (274, '2024-09-03 16:58:02.110', '2024-09-03 16:58:02.110', '2024-09-04 09:56:14.748', 1, 11); 502 | INSERT INTO `role_menus` VALUES (275, '2024-09-03 16:58:02.111', '2024-09-03 16:58:02.111', '2024-09-04 09:56:14.748', 1, 12); 503 | INSERT INTO `role_menus` VALUES (276, '2024-09-03 16:58:02.113', '2024-09-03 16:58:02.113', '2024-09-04 09:56:14.748', 1, 13); 504 | INSERT INTO `role_menus` VALUES (277, '2024-09-03 16:58:02.114', '2024-09-03 16:58:02.114', '2024-09-04 09:56:14.748', 1, 30); 505 | INSERT INTO `role_menus` VALUES (278, '2024-09-03 16:58:02.116', '2024-09-03 16:58:02.116', '2024-09-04 09:56:14.748', 1, 31); 506 | INSERT INTO `role_menus` VALUES (279, '2024-09-04 09:56:14.780', '2024-09-04 09:56:14.780', '2024-09-04 09:57:06.913', 1, 1); 507 | INSERT INTO `role_menus` VALUES (280, '2024-09-04 09:56:14.785', '2024-09-04 09:56:14.785', '2024-09-04 09:57:06.913', 1, 34); 508 | INSERT INTO `role_menus` VALUES (281, '2024-09-04 09:56:14.789', '2024-09-04 09:56:14.789', '2024-09-04 09:57:06.913', 1, 35); 509 | INSERT INTO `role_menus` VALUES (282, '2024-09-04 09:56:14.791', '2024-09-04 09:56:14.791', '2024-09-04 09:57:06.913', 1, 3); 510 | INSERT INTO `role_menus` VALUES (283, '2024-09-04 09:56:14.793', '2024-09-04 09:56:14.793', '2024-09-04 09:57:06.913', 1, 4); 511 | INSERT INTO `role_menus` VALUES (284, '2024-09-04 09:56:14.795', '2024-09-04 09:56:14.795', '2024-09-04 09:57:06.913', 1, 5); 512 | INSERT INTO `role_menus` VALUES (285, '2024-09-04 09:56:14.798', '2024-09-04 09:56:14.798', '2024-09-04 09:57:06.913', 1, 6); 513 | INSERT INTO `role_menus` VALUES (286, '2024-09-04 09:56:14.801', '2024-09-04 09:56:14.801', '2024-09-04 09:57:06.913', 1, 7); 514 | INSERT INTO `role_menus` VALUES (287, '2024-09-04 09:56:14.805', '2024-09-04 09:56:14.805', '2024-09-04 09:57:06.913', 1, 8); 515 | INSERT INTO `role_menus` VALUES (288, '2024-09-04 09:56:14.808', '2024-09-04 09:56:14.808', '2024-09-04 09:57:06.913', 1, 9); 516 | INSERT INTO `role_menus` VALUES (289, '2024-09-04 09:56:14.812', '2024-09-04 09:56:14.812', '2024-09-04 09:57:06.913', 1, 10); 517 | INSERT INTO `role_menus` VALUES (290, '2024-09-04 09:56:14.814', '2024-09-04 09:56:14.814', '2024-09-04 09:57:06.913', 1, 11); 518 | INSERT INTO `role_menus` VALUES (291, '2024-09-04 09:56:14.815', '2024-09-04 09:56:14.815', '2024-09-04 09:57:06.913', 1, 12); 519 | INSERT INTO `role_menus` VALUES (292, '2024-09-04 09:56:14.816', '2024-09-04 09:56:14.816', '2024-09-04 09:57:06.913', 1, 13); 520 | INSERT INTO `role_menus` VALUES (293, '2024-09-04 09:56:14.818', '2024-09-04 09:56:14.818', '2024-09-04 09:57:06.913', 1, 30); 521 | INSERT INTO `role_menus` VALUES (294, '2024-09-04 09:56:14.819', '2024-09-04 09:56:14.819', '2024-09-04 09:57:06.913', 1, 31); 522 | INSERT INTO `role_menus` VALUES (295, '2024-09-04 09:57:06.928', '2024-09-04 09:57:06.928', '2024-09-04 10:12:10.807', 1, 1); 523 | INSERT INTO `role_menus` VALUES (296, '2024-09-04 09:57:06.931', '2024-09-04 09:57:06.931', '2024-09-04 10:12:10.807', 1, 34); 524 | INSERT INTO `role_menus` VALUES (297, '2024-09-04 09:57:06.933', '2024-09-04 09:57:06.933', '2024-09-04 10:12:10.807', 1, 35); 525 | INSERT INTO `role_menus` VALUES (298, '2024-09-04 09:57:06.935', '2024-09-04 09:57:06.935', '2024-09-04 10:12:10.807', 1, 3); 526 | INSERT INTO `role_menus` VALUES (299, '2024-09-04 09:57:06.938', '2024-09-04 09:57:06.938', '2024-09-04 10:12:10.807', 1, 4); 527 | INSERT INTO `role_menus` VALUES (300, '2024-09-04 09:57:06.940', '2024-09-04 09:57:06.940', '2024-09-04 10:12:10.807', 1, 5); 528 | INSERT INTO `role_menus` VALUES (301, '2024-09-04 09:57:06.942', '2024-09-04 09:57:06.942', '2024-09-04 10:12:10.807', 1, 6); 529 | INSERT INTO `role_menus` VALUES (302, '2024-09-04 09:57:06.945', '2024-09-04 09:57:06.945', '2024-09-04 10:12:10.807', 1, 7); 530 | INSERT INTO `role_menus` VALUES (303, '2024-09-04 09:57:06.948', '2024-09-04 09:57:06.948', '2024-09-04 10:12:10.807', 1, 8); 531 | INSERT INTO `role_menus` VALUES (304, '2024-09-04 09:57:06.951', '2024-09-04 09:57:06.951', '2024-09-04 10:12:10.807', 1, 9); 532 | INSERT INTO `role_menus` VALUES (305, '2024-09-04 09:57:06.954', '2024-09-04 09:57:06.954', '2024-09-04 10:12:10.807', 1, 10); 533 | INSERT INTO `role_menus` VALUES (306, '2024-09-04 09:57:06.957', '2024-09-04 09:57:06.957', '2024-09-04 10:12:10.807', 1, 11); 534 | INSERT INTO `role_menus` VALUES (307, '2024-09-04 09:57:06.960', '2024-09-04 09:57:06.960', '2024-09-04 10:12:10.807', 1, 12); 535 | INSERT INTO `role_menus` VALUES (308, '2024-09-04 09:57:06.961', '2024-09-04 09:57:06.961', '2024-09-04 10:12:10.807', 1, 13); 536 | INSERT INTO `role_menus` VALUES (309, '2024-09-04 09:57:06.962', '2024-09-04 09:57:06.962', '2024-09-04 10:12:10.807', 1, 30); 537 | INSERT INTO `role_menus` VALUES (310, '2024-09-04 09:57:06.963', '2024-09-04 09:57:06.963', '2024-09-04 10:12:10.807', 1, 31); 538 | INSERT INTO `role_menus` VALUES (311, '2024-09-04 10:12:10.826', '2024-09-04 10:12:10.826', '2024-09-04 10:13:30.246', 1, 1); 539 | INSERT INTO `role_menus` VALUES (312, '2024-09-04 10:12:10.829', '2024-09-04 10:12:10.829', '2024-09-04 10:13:30.246', 1, 34); 540 | INSERT INTO `role_menus` VALUES (313, '2024-09-04 10:12:10.834', '2024-09-04 10:12:10.834', '2024-09-04 10:13:30.246', 1, 35); 541 | INSERT INTO `role_menus` VALUES (314, '2024-09-04 10:12:10.836', '2024-09-04 10:12:10.836', '2024-09-04 10:13:30.246', 1, 3); 542 | INSERT INTO `role_menus` VALUES (315, '2024-09-04 10:12:10.839', '2024-09-04 10:12:10.839', '2024-09-04 10:13:30.246', 1, 4); 543 | INSERT INTO `role_menus` VALUES (316, '2024-09-04 10:12:10.841', '2024-09-04 10:12:10.841', '2024-09-04 10:13:30.246', 1, 5); 544 | INSERT INTO `role_menus` VALUES (317, '2024-09-04 10:12:10.844', '2024-09-04 10:12:10.844', '2024-09-04 10:13:30.246', 1, 6); 545 | INSERT INTO `role_menus` VALUES (318, '2024-09-04 10:12:10.847', '2024-09-04 10:12:10.847', '2024-09-04 10:13:30.246', 1, 7); 546 | INSERT INTO `role_menus` VALUES (319, '2024-09-04 10:12:10.851', '2024-09-04 10:12:10.851', '2024-09-04 10:13:30.246', 1, 8); 547 | INSERT INTO `role_menus` VALUES (320, '2024-09-04 10:12:10.856', '2024-09-04 10:12:10.856', '2024-09-04 10:13:30.246', 1, 9); 548 | INSERT INTO `role_menus` VALUES (321, '2024-09-04 10:12:10.859', '2024-09-04 10:12:10.859', '2024-09-04 10:13:30.246', 1, 10); 549 | INSERT INTO `role_menus` VALUES (322, '2024-09-04 10:12:10.862', '2024-09-04 10:12:10.862', '2024-09-04 10:13:30.246', 1, 11); 550 | INSERT INTO `role_menus` VALUES (323, '2024-09-04 10:12:10.863', '2024-09-04 10:12:10.863', '2024-09-04 10:13:30.246', 1, 12); 551 | INSERT INTO `role_menus` VALUES (324, '2024-09-04 10:12:10.865', '2024-09-04 10:12:10.865', '2024-09-04 10:13:30.246', 1, 13); 552 | INSERT INTO `role_menus` VALUES (325, '2024-09-04 10:12:10.868', '2024-09-04 10:12:10.868', '2024-09-04 10:13:30.246', 1, 36); 553 | INSERT INTO `role_menus` VALUES (326, '2024-09-04 10:12:10.873', '2024-09-04 10:12:10.873', '2024-09-04 10:13:30.246', 1, 37); 554 | INSERT INTO `role_menus` VALUES (327, '2024-09-04 10:12:10.877', '2024-09-04 10:12:10.877', '2024-09-04 10:13:30.246', 1, 38); 555 | INSERT INTO `role_menus` VALUES (328, '2024-09-04 10:12:10.881', '2024-09-04 10:12:10.881', '2024-09-04 10:13:30.246', 1, 41); 556 | INSERT INTO `role_menus` VALUES (329, '2024-09-04 10:12:10.883', '2024-09-04 10:12:10.883', '2024-09-04 10:13:30.246', 1, 30); 557 | INSERT INTO `role_menus` VALUES (330, '2024-09-04 10:12:10.886', '2024-09-04 10:12:10.886', '2024-09-04 10:13:30.246', 1, 42); 558 | INSERT INTO `role_menus` VALUES (331, '2024-09-04 10:12:10.891', '2024-09-04 10:12:10.891', '2024-09-04 10:13:30.246', 1, 43); 559 | INSERT INTO `role_menus` VALUES (332, '2024-09-04 10:12:10.894', '2024-09-04 10:12:10.894', '2024-09-04 10:13:30.246', 1, 44); 560 | INSERT INTO `role_menus` VALUES (333, '2024-09-04 10:12:10.899', '2024-09-04 10:12:10.899', '2024-09-04 10:13:30.246', 1, 45); 561 | INSERT INTO `role_menus` VALUES (334, '2024-09-04 10:12:10.903', '2024-09-04 10:12:10.903', '2024-09-04 10:13:30.246', 1, 46); 562 | INSERT INTO `role_menus` VALUES (335, '2024-09-04 10:12:10.905', '2024-09-04 10:12:10.905', '2024-09-04 10:13:30.246', 1, 31); 563 | INSERT INTO `role_menus` VALUES (336, '2024-09-04 10:12:10.908', '2024-09-04 10:12:10.908', '2024-09-04 10:13:30.246', 1, 47); 564 | INSERT INTO `role_menus` VALUES (337, '2024-09-04 10:12:10.912', '2024-09-04 10:12:10.912', '2024-09-04 10:13:30.246', 1, 48); 565 | INSERT INTO `role_menus` VALUES (338, '2024-09-04 10:12:10.916', '2024-09-04 10:12:10.916', '2024-09-04 10:13:30.246', 1, 49); 566 | INSERT INTO `role_menus` VALUES (339, '2024-09-04 10:12:10.920', '2024-09-04 10:12:10.920', '2024-09-04 10:13:30.246', 1, 50); 567 | INSERT INTO `role_menus` VALUES (340, '2024-09-04 10:12:10.925', '2024-09-04 10:12:10.925', '2024-09-04 10:13:30.246', 1, 51); 568 | INSERT INTO `role_menus` VALUES (341, '2024-09-04 10:13:30.260', '2024-09-04 10:13:30.260', '2024-09-04 10:14:15.988', 1, 1); 569 | INSERT INTO `role_menus` VALUES (342, '2024-09-04 10:13:30.263', '2024-09-04 10:13:30.263', '2024-09-04 10:14:15.988', 1, 34); 570 | INSERT INTO `role_menus` VALUES (343, '2024-09-04 10:13:30.267', '2024-09-04 10:13:30.267', '2024-09-04 10:14:15.988', 1, 35); 571 | INSERT INTO `role_menus` VALUES (344, '2024-09-04 10:13:30.269', '2024-09-04 10:13:30.269', '2024-09-04 10:14:15.988', 1, 3); 572 | INSERT INTO `role_menus` VALUES (345, '2024-09-04 10:13:30.272', '2024-09-04 10:13:30.272', '2024-09-04 10:14:15.988', 1, 4); 573 | INSERT INTO `role_menus` VALUES (346, '2024-09-04 10:13:30.273', '2024-09-04 10:13:30.273', '2024-09-04 10:14:15.988', 1, 5); 574 | INSERT INTO `role_menus` VALUES (347, '2024-09-04 10:13:30.276', '2024-09-04 10:13:30.276', '2024-09-04 10:14:15.988', 1, 6); 575 | INSERT INTO `role_menus` VALUES (348, '2024-09-04 10:13:30.280', '2024-09-04 10:13:30.280', '2024-09-04 10:14:15.988', 1, 7); 576 | INSERT INTO `role_menus` VALUES (349, '2024-09-04 10:13:30.283', '2024-09-04 10:13:30.283', '2024-09-04 10:14:15.988', 1, 8); 577 | INSERT INTO `role_menus` VALUES (350, '2024-09-04 10:13:30.287', '2024-09-04 10:13:30.287', '2024-09-04 10:14:15.988', 1, 9); 578 | INSERT INTO `role_menus` VALUES (351, '2024-09-04 10:13:30.293', '2024-09-04 10:13:30.293', '2024-09-04 10:14:15.988', 1, 10); 579 | INSERT INTO `role_menus` VALUES (352, '2024-09-04 10:13:30.297', '2024-09-04 10:13:30.297', '2024-09-04 10:14:15.988', 1, 11); 580 | INSERT INTO `role_menus` VALUES (353, '2024-09-04 10:13:30.300', '2024-09-04 10:13:30.300', '2024-09-04 10:14:15.988', 1, 12); 581 | INSERT INTO `role_menus` VALUES (354, '2024-09-04 10:13:30.302', '2024-09-04 10:13:30.302', '2024-09-04 10:14:15.988', 1, 13); 582 | INSERT INTO `role_menus` VALUES (355, '2024-09-04 10:13:30.305', '2024-09-04 10:13:30.305', '2024-09-04 10:14:15.988', 1, 36); 583 | INSERT INTO `role_menus` VALUES (356, '2024-09-04 10:13:30.309', '2024-09-04 10:13:30.309', '2024-09-04 10:14:15.988', 1, 37); 584 | INSERT INTO `role_menus` VALUES (357, '2024-09-04 10:13:30.313', '2024-09-04 10:13:30.313', '2024-09-04 10:14:15.988', 1, 38); 585 | INSERT INTO `role_menus` VALUES (358, '2024-09-04 10:13:30.318', '2024-09-04 10:13:30.318', '2024-09-04 10:14:15.988', 1, 41); 586 | INSERT INTO `role_menus` VALUES (359, '2024-09-04 10:13:30.321', '2024-09-04 10:13:30.321', '2024-09-04 10:14:15.988', 1, 30); 587 | INSERT INTO `role_menus` VALUES (360, '2024-09-04 10:13:30.324', '2024-09-04 10:13:30.324', '2024-09-04 10:14:15.988', 1, 42); 588 | INSERT INTO `role_menus` VALUES (361, '2024-09-04 10:13:30.328', '2024-09-04 10:13:30.328', '2024-09-04 10:14:15.988', 1, 43); 589 | INSERT INTO `role_menus` VALUES (362, '2024-09-04 10:13:30.333', '2024-09-04 10:13:30.333', '2024-09-04 10:14:15.988', 1, 44); 590 | INSERT INTO `role_menus` VALUES (363, '2024-09-04 10:13:30.339', '2024-09-04 10:13:30.339', '2024-09-04 10:14:15.988', 1, 45); 591 | INSERT INTO `role_menus` VALUES (364, '2024-09-04 10:13:30.344', '2024-09-04 10:13:30.344', '2024-09-04 10:14:15.988', 1, 46); 592 | INSERT INTO `role_menus` VALUES (365, '2024-09-04 10:13:30.346', '2024-09-04 10:13:30.346', '2024-09-04 10:14:15.988', 1, 31); 593 | INSERT INTO `role_menus` VALUES (366, '2024-09-04 10:13:30.352', '2024-09-04 10:13:30.352', '2024-09-04 10:14:15.988', 1, 47); 594 | INSERT INTO `role_menus` VALUES (367, '2024-09-04 10:13:30.356', '2024-09-04 10:13:30.356', '2024-09-04 10:14:15.988', 1, 48); 595 | INSERT INTO `role_menus` VALUES (368, '2024-09-04 10:13:30.360', '2024-09-04 10:13:30.360', '2024-09-04 10:14:15.988', 1, 49); 596 | INSERT INTO `role_menus` VALUES (369, '2024-09-04 10:13:30.367', '2024-09-04 10:13:30.367', '2024-09-04 10:14:15.988', 1, 50); 597 | INSERT INTO `role_menus` VALUES (370, '2024-09-04 10:13:30.372', '2024-09-04 10:13:30.372', '2024-09-04 10:14:15.988', 1, 51); 598 | INSERT INTO `role_menus` VALUES (371, '2024-09-04 10:14:16.003', '2024-09-04 10:14:16.003', '2024-09-07 17:01:31.705', 1, 1); 599 | INSERT INTO `role_menus` VALUES (372, '2024-09-04 10:14:16.006', '2024-09-04 10:14:16.006', '2024-09-07 17:01:31.705', 1, 34); 600 | INSERT INTO `role_menus` VALUES (373, '2024-09-04 10:14:16.010', '2024-09-04 10:14:16.010', '2024-09-07 17:01:31.705', 1, 35); 601 | INSERT INTO `role_menus` VALUES (374, '2024-09-04 10:14:16.012', '2024-09-04 10:14:16.012', '2024-09-07 17:01:31.705', 1, 3); 602 | INSERT INTO `role_menus` VALUES (375, '2024-09-04 10:14:16.015', '2024-09-04 10:14:16.015', '2024-09-07 17:01:31.705', 1, 4); 603 | INSERT INTO `role_menus` VALUES (376, '2024-09-04 10:14:16.017', '2024-09-04 10:14:16.017', '2024-09-07 17:01:31.705', 1, 5); 604 | INSERT INTO `role_menus` VALUES (377, '2024-09-04 10:14:16.020', '2024-09-04 10:14:16.020', '2024-09-07 17:01:31.705', 1, 6); 605 | INSERT INTO `role_menus` VALUES (378, '2024-09-04 10:14:16.023', '2024-09-04 10:14:16.023', '2024-09-07 17:01:31.705', 1, 7); 606 | INSERT INTO `role_menus` VALUES (379, '2024-09-04 10:14:16.027', '2024-09-04 10:14:16.027', '2024-09-07 17:01:31.705', 1, 8); 607 | INSERT INTO `role_menus` VALUES (380, '2024-09-04 10:14:16.031', '2024-09-04 10:14:16.031', '2024-09-07 17:01:31.705', 1, 9); 608 | INSERT INTO `role_menus` VALUES (381, '2024-09-04 10:14:16.036', '2024-09-04 10:14:16.036', '2024-09-07 17:01:31.705', 1, 10); 609 | INSERT INTO `role_menus` VALUES (382, '2024-09-04 10:14:16.040', '2024-09-04 10:14:16.040', '2024-09-07 17:01:31.705', 1, 11); 610 | INSERT INTO `role_menus` VALUES (383, '2024-09-04 10:14:16.042', '2024-09-04 10:14:16.042', '2024-09-07 17:01:31.705', 1, 12); 611 | INSERT INTO `role_menus` VALUES (384, '2024-09-04 10:14:16.044', '2024-09-04 10:14:16.044', '2024-09-07 17:01:31.705', 1, 13); 612 | INSERT INTO `role_menus` VALUES (385, '2024-09-04 10:14:16.046', '2024-09-04 10:14:16.046', '2024-09-07 17:01:31.705', 1, 36); 613 | INSERT INTO `role_menus` VALUES (386, '2024-09-04 10:14:16.051', '2024-09-04 10:14:16.051', '2024-09-07 17:01:31.705', 1, 37); 614 | INSERT INTO `role_menus` VALUES (387, '2024-09-04 10:14:16.055', '2024-09-04 10:14:16.055', '2024-09-07 17:01:31.705', 1, 38); 615 | INSERT INTO `role_menus` VALUES (388, '2024-09-04 10:14:16.058', '2024-09-04 10:14:16.058', '2024-09-07 17:01:31.705', 1, 41); 616 | INSERT INTO `role_menus` VALUES (389, '2024-09-04 10:14:16.062', '2024-09-04 10:14:16.062', '2024-09-07 17:01:31.705', 1, 30); 617 | INSERT INTO `role_menus` VALUES (390, '2024-09-04 10:14:16.067', '2024-09-04 10:14:16.067', '2024-09-07 17:01:31.705', 1, 42); 618 | INSERT INTO `role_menus` VALUES (391, '2024-09-04 10:14:16.071', '2024-09-04 10:14:16.071', '2024-09-07 17:01:31.705', 1, 43); 619 | INSERT INTO `role_menus` VALUES (392, '2024-09-04 10:14:16.075', '2024-09-04 10:14:16.075', '2024-09-07 17:01:31.705', 1, 44); 620 | INSERT INTO `role_menus` VALUES (393, '2024-09-04 10:14:16.080', '2024-09-04 10:14:16.080', '2024-09-07 17:01:31.705', 1, 45); 621 | INSERT INTO `role_menus` VALUES (394, '2024-09-04 10:14:16.085', '2024-09-04 10:14:16.085', '2024-09-07 17:01:31.705', 1, 46); 622 | INSERT INTO `role_menus` VALUES (395, '2024-09-04 10:14:16.091', '2024-09-04 10:14:16.091', '2024-09-07 17:01:31.705', 1, 31); 623 | INSERT INTO `role_menus` VALUES (396, '2024-09-04 10:14:16.096', '2024-09-04 10:14:16.096', '2024-09-07 17:01:31.705', 1, 47); 624 | INSERT INTO `role_menus` VALUES (397, '2024-09-04 10:14:16.101', '2024-09-04 10:14:16.101', '2024-09-07 17:01:31.705', 1, 48); 625 | INSERT INTO `role_menus` VALUES (398, '2024-09-04 10:14:16.106', '2024-09-04 10:14:16.106', '2024-09-07 17:01:31.705', 1, 49); 626 | INSERT INTO `role_menus` VALUES (399, '2024-09-04 10:14:16.110', '2024-09-04 10:14:16.110', '2024-09-07 17:01:31.705', 1, 50); 627 | INSERT INTO `role_menus` VALUES (400, '2024-09-04 10:14:16.117', '2024-09-04 10:14:16.117', '2024-09-07 17:01:31.705', 1, 51); 628 | INSERT INTO `role_menus` VALUES (401, '2024-09-07 17:01:31.735', '2024-09-07 17:01:31.735', '2024-09-07 17:02:16.649', 1, 1); 629 | INSERT INTO `role_menus` VALUES (402, '2024-09-07 17:01:31.738', '2024-09-07 17:01:31.738', '2024-09-07 17:02:16.649', 1, 34); 630 | INSERT INTO `role_menus` VALUES (403, '2024-09-07 17:01:31.741', '2024-09-07 17:01:31.741', '2024-09-07 17:02:16.649', 1, 35); 631 | INSERT INTO `role_menus` VALUES (404, '2024-09-07 17:01:31.742', '2024-09-07 17:01:31.742', '2024-09-07 17:02:16.649', 1, 12); 632 | INSERT INTO `role_menus` VALUES (405, '2024-09-07 17:01:31.744', '2024-09-07 17:01:31.744', '2024-09-07 17:02:16.649', 1, 13); 633 | INSERT INTO `role_menus` VALUES (406, '2024-09-07 17:01:31.746', '2024-09-07 17:01:31.746', '2024-09-07 17:02:16.649', 1, 36); 634 | INSERT INTO `role_menus` VALUES (407, '2024-09-07 17:01:31.748', '2024-09-07 17:01:31.748', '2024-09-07 17:02:16.649', 1, 37); 635 | INSERT INTO `role_menus` VALUES (408, '2024-09-07 17:01:31.751', '2024-09-07 17:01:31.751', '2024-09-07 17:02:16.649', 1, 38); 636 | INSERT INTO `role_menus` VALUES (409, '2024-09-07 17:01:31.753', '2024-09-07 17:01:31.753', '2024-09-07 17:02:16.649', 1, 41); 637 | INSERT INTO `role_menus` VALUES (410, '2024-09-07 17:01:31.756', '2024-09-07 17:01:31.756', '2024-09-07 17:02:16.649', 1, 30); 638 | INSERT INTO `role_menus` VALUES (411, '2024-09-07 17:01:31.758', '2024-09-07 17:01:31.758', '2024-09-07 17:02:16.649', 1, 42); 639 | INSERT INTO `role_menus` VALUES (412, '2024-09-07 17:01:31.761', '2024-09-07 17:01:31.761', '2024-09-07 17:02:16.649', 1, 43); 640 | INSERT INTO `role_menus` VALUES (413, '2024-09-07 17:01:31.763', '2024-09-07 17:01:31.763', '2024-09-07 17:02:16.649', 1, 44); 641 | INSERT INTO `role_menus` VALUES (414, '2024-09-07 17:01:31.766', '2024-09-07 17:01:31.766', '2024-09-07 17:02:16.649', 1, 45); 642 | INSERT INTO `role_menus` VALUES (415, '2024-09-07 17:01:31.768', '2024-09-07 17:01:31.768', '2024-09-07 17:02:16.649', 1, 46); 643 | INSERT INTO `role_menus` VALUES (416, '2024-09-07 17:01:31.770', '2024-09-07 17:01:31.770', '2024-09-07 17:02:16.649', 1, 31); 644 | INSERT INTO `role_menus` VALUES (417, '2024-09-07 17:01:31.773', '2024-09-07 17:01:31.773', '2024-09-07 17:02:16.649', 1, 47); 645 | INSERT INTO `role_menus` VALUES (418, '2024-09-07 17:01:31.775', '2024-09-07 17:01:31.775', '2024-09-07 17:02:16.649', 1, 48); 646 | INSERT INTO `role_menus` VALUES (419, '2024-09-07 17:01:31.778', '2024-09-07 17:01:31.778', '2024-09-07 17:02:16.649', 1, 49); 647 | INSERT INTO `role_menus` VALUES (420, '2024-09-07 17:01:31.781', '2024-09-07 17:01:31.781', '2024-09-07 17:02:16.649', 1, 50); 648 | INSERT INTO `role_menus` VALUES (421, '2024-09-07 17:01:31.783', '2024-09-07 17:01:31.783', '2024-09-07 17:02:16.649', 1, 51); 649 | INSERT INTO `role_menus` VALUES (422, '2024-09-07 17:02:16.666', '2024-09-07 17:02:16.666', '2024-09-10 11:18:28.128', 1, 1); 650 | INSERT INTO `role_menus` VALUES (423, '2024-09-07 17:02:16.668', '2024-09-07 17:02:16.668', '2024-09-10 11:18:28.128', 1, 34); 651 | INSERT INTO `role_menus` VALUES (424, '2024-09-07 17:02:16.671', '2024-09-07 17:02:16.671', '2024-09-10 11:18:28.128', 1, 35); 652 | INSERT INTO `role_menus` VALUES (425, '2024-09-07 17:02:16.672', '2024-09-07 17:02:16.672', '2024-09-10 11:18:28.128', 1, 3); 653 | INSERT INTO `role_menus` VALUES (426, '2024-09-07 17:02:16.675', '2024-09-07 17:02:16.675', '2024-09-10 11:18:28.128', 1, 4); 654 | INSERT INTO `role_menus` VALUES (427, '2024-09-07 17:02:16.677', '2024-09-07 17:02:16.677', '2024-09-10 11:18:28.128', 1, 5); 655 | INSERT INTO `role_menus` VALUES (428, '2024-09-07 17:02:16.681', '2024-09-07 17:02:16.681', '2024-09-10 11:18:28.128', 1, 10); 656 | INSERT INTO `role_menus` VALUES (429, '2024-09-07 17:02:16.683', '2024-09-07 17:02:16.683', '2024-09-10 11:18:28.128', 1, 11); 657 | INSERT INTO `role_menus` VALUES (430, '2024-09-07 17:02:16.684', '2024-09-07 17:02:16.684', '2024-09-10 11:18:28.128', 1, 12); 658 | INSERT INTO `role_menus` VALUES (431, '2024-09-07 17:02:16.685', '2024-09-07 17:02:16.685', '2024-09-10 11:18:28.128', 1, 13); 659 | INSERT INTO `role_menus` VALUES (432, '2024-09-07 17:02:16.688', '2024-09-07 17:02:16.688', '2024-09-10 11:18:28.128', 1, 36); 660 | INSERT INTO `role_menus` VALUES (433, '2024-09-07 17:02:16.691', '2024-09-07 17:02:16.691', '2024-09-10 11:18:28.128', 1, 37); 661 | INSERT INTO `role_menus` VALUES (434, '2024-09-07 17:02:16.693', '2024-09-07 17:02:16.693', '2024-09-10 11:18:28.128', 1, 38); 662 | INSERT INTO `role_menus` VALUES (435, '2024-09-07 17:02:16.696', '2024-09-07 17:02:16.696', '2024-09-10 11:18:28.128', 1, 41); 663 | INSERT INTO `role_menus` VALUES (436, '2024-09-07 17:02:16.698', '2024-09-07 17:02:16.698', '2024-09-10 11:18:28.128', 1, 30); 664 | INSERT INTO `role_menus` VALUES (437, '2024-09-07 17:02:16.701', '2024-09-07 17:02:16.701', '2024-09-10 11:18:28.128', 1, 42); 665 | INSERT INTO `role_menus` VALUES (438, '2024-09-07 17:02:16.707', '2024-09-07 17:02:16.707', '2024-09-10 11:18:28.128', 1, 43); 666 | INSERT INTO `role_menus` VALUES (439, '2024-09-07 17:02:16.711', '2024-09-07 17:02:16.711', '2024-09-10 11:18:28.128', 1, 44); 667 | INSERT INTO `role_menus` VALUES (440, '2024-09-07 17:02:16.715', '2024-09-07 17:02:16.715', '2024-09-10 11:18:28.128', 1, 45); 668 | INSERT INTO `role_menus` VALUES (441, '2024-09-07 17:02:16.718', '2024-09-07 17:02:16.718', '2024-09-10 11:18:28.128', 1, 46); 669 | INSERT INTO `role_menus` VALUES (442, '2024-09-07 17:02:16.721', '2024-09-07 17:02:16.721', '2024-09-10 11:18:28.128', 1, 31); 670 | INSERT INTO `role_menus` VALUES (443, '2024-09-07 17:02:16.724', '2024-09-07 17:02:16.724', '2024-09-10 11:18:28.128', 1, 47); 671 | INSERT INTO `role_menus` VALUES (444, '2024-09-07 17:02:16.727', '2024-09-07 17:02:16.727', '2024-09-10 11:18:28.128', 1, 48); 672 | INSERT INTO `role_menus` VALUES (445, '2024-09-07 17:02:16.729', '2024-09-07 17:02:16.729', '2024-09-10 11:18:28.128', 1, 49); 673 | INSERT INTO `role_menus` VALUES (446, '2024-09-07 17:02:16.732', '2024-09-07 17:02:16.732', '2024-09-10 11:18:28.128', 1, 50); 674 | INSERT INTO `role_menus` VALUES (447, '2024-09-07 17:02:16.735', '2024-09-07 17:02:16.735', '2024-09-10 11:18:28.128', 1, 51); 675 | INSERT INTO `role_menus` VALUES (448, '2024-09-10 11:18:28.150', '2024-09-10 11:18:28.150', NULL, 1, 1); 676 | INSERT INTO `role_menus` VALUES (449, '2024-09-10 11:18:28.154', '2024-09-10 11:18:28.154', NULL, 1, 34); 677 | INSERT INTO `role_menus` VALUES (450, '2024-09-10 11:18:28.157', '2024-09-10 11:18:28.157', NULL, 1, 35); 678 | INSERT INTO `role_menus` VALUES (451, '2024-09-10 11:18:28.158', '2024-09-10 11:18:28.158', NULL, 1, 3); 679 | INSERT INTO `role_menus` VALUES (452, '2024-09-10 11:18:28.160', '2024-09-10 11:18:28.160', NULL, 1, 4); 680 | INSERT INTO `role_menus` VALUES (453, '2024-09-10 11:18:28.161', '2024-09-10 11:18:28.161', NULL, 1, 5); 681 | INSERT INTO `role_menus` VALUES (454, '2024-09-10 11:18:28.164', '2024-09-10 11:18:28.164', NULL, 1, 6); 682 | INSERT INTO `role_menus` VALUES (455, '2024-09-10 11:18:28.166', '2024-09-10 11:18:28.166', NULL, 1, 7); 683 | INSERT INTO `role_menus` VALUES (456, '2024-09-10 11:18:28.168', '2024-09-10 11:18:28.168', NULL, 1, 8); 684 | INSERT INTO `role_menus` VALUES (457, '2024-09-10 11:18:28.171', '2024-09-10 11:18:28.171', NULL, 1, 9); 685 | INSERT INTO `role_menus` VALUES (458, '2024-09-10 11:18:28.173', '2024-09-10 11:18:28.173', NULL, 1, 10); 686 | INSERT INTO `role_menus` VALUES (459, '2024-09-10 11:18:28.176', '2024-09-10 11:18:28.176', NULL, 1, 11); 687 | INSERT INTO `role_menus` VALUES (460, '2024-09-10 11:18:28.178', '2024-09-10 11:18:28.178', NULL, 1, 52); 688 | INSERT INTO `role_menus` VALUES (461, '2024-09-10 11:18:28.181', '2024-09-10 11:18:28.181', NULL, 1, 53); 689 | INSERT INTO `role_menus` VALUES (462, '2024-09-10 11:18:28.183', '2024-09-10 11:18:28.183', NULL, 1, 54); 690 | INSERT INTO `role_menus` VALUES (463, '2024-09-10 11:18:28.184', '2024-09-10 11:18:28.184', NULL, 1, 12); 691 | INSERT INTO `role_menus` VALUES (464, '2024-09-10 11:18:28.185', '2024-09-10 11:18:28.185', NULL, 1, 13); 692 | INSERT INTO `role_menus` VALUES (465, '2024-09-10 11:18:28.187', '2024-09-10 11:18:28.187', NULL, 1, 36); 693 | INSERT INTO `role_menus` VALUES (466, '2024-09-10 11:18:28.190', '2024-09-10 11:18:28.190', NULL, 1, 37); 694 | INSERT INTO `role_menus` VALUES (467, '2024-09-10 11:18:28.193', '2024-09-10 11:18:28.193', NULL, 1, 38); 695 | INSERT INTO `role_menus` VALUES (468, '2024-09-10 11:18:28.196', '2024-09-10 11:18:28.196', NULL, 1, 41); 696 | INSERT INTO `role_menus` VALUES (469, '2024-09-10 11:18:28.198', '2024-09-10 11:18:28.198', NULL, 1, 30); 697 | INSERT INTO `role_menus` VALUES (470, '2024-09-10 11:18:28.200', '2024-09-10 11:18:28.200', NULL, 1, 42); 698 | INSERT INTO `role_menus` VALUES (471, '2024-09-10 11:18:28.203', '2024-09-10 11:18:28.203', NULL, 1, 43); 699 | INSERT INTO `role_menus` VALUES (472, '2024-09-10 11:18:28.206', '2024-09-10 11:18:28.206', NULL, 1, 44); 700 | INSERT INTO `role_menus` VALUES (473, '2024-09-10 11:18:28.209', '2024-09-10 11:18:28.209', NULL, 1, 45); 701 | INSERT INTO `role_menus` VALUES (474, '2024-09-10 11:18:28.211', '2024-09-10 11:18:28.211', NULL, 1, 46); 702 | INSERT INTO `role_menus` VALUES (475, '2024-09-10 11:18:28.214', '2024-09-10 11:18:28.214', NULL, 1, 31); 703 | INSERT INTO `role_menus` VALUES (476, '2024-09-10 11:18:28.216', '2024-09-10 11:18:28.216', NULL, 1, 47); 704 | INSERT INTO `role_menus` VALUES (477, '2024-09-10 11:18:28.219', '2024-09-10 11:18:28.219', NULL, 1, 48); 705 | INSERT INTO `role_menus` VALUES (478, '2024-09-10 11:18:28.221', '2024-09-10 11:18:28.221', NULL, 1, 49); 706 | INSERT INTO `role_menus` VALUES (479, '2024-09-10 11:18:28.224', '2024-09-10 11:18:28.224', NULL, 1, 50); 707 | INSERT INTO `role_menus` VALUES (480, '2024-09-10 11:18:28.227', '2024-09-10 11:18:28.227', NULL, 1, 51); 708 | 709 | -- ---------------------------- 710 | -- Table structure for roles 711 | -- ---------------------------- 712 | DROP TABLE IF EXISTS `roles`; 713 | CREATE TABLE `roles` ( 714 | `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, 715 | `created_at` datetime(3) NULL DEFAULT NULL, 716 | `updated_at` datetime(3) NULL DEFAULT NULL, 717 | `deleted_at` datetime(3) NULL DEFAULT NULL, 718 | `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '角色名称', 719 | `code` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '角色代码', 720 | PRIMARY KEY (`id`) USING BTREE, 721 | UNIQUE INDEX `uni_roles_code`(`code` ASC) USING BTREE, 722 | INDEX `idx_roles_deleted_at`(`deleted_at` ASC) USING BTREE 723 | ) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; 724 | 725 | -- ---------------------------- 726 | -- Records of roles 727 | -- ---------------------------- 728 | INSERT INTO `roles` VALUES (1, '2024-09-03 10:32:38.239', '2024-09-03 10:32:38.239', NULL, '管理员', 'admin'); 729 | INSERT INTO `roles` VALUES (2, '2024-09-03 11:22:58.693', '2024-09-03 11:22:58.693', NULL, '测试', 'test'); 730 | INSERT INTO `roles` VALUES (4, '2024-09-03 11:23:11.209', '2024-09-03 11:23:11.209', NULL, '测试2', 'test2'); 731 | 732 | -- ---------------------------- 733 | -- Table structure for users 734 | -- ---------------------------- 735 | DROP TABLE IF EXISTS `users`; 736 | CREATE TABLE `users` ( 737 | `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, 738 | `created_at` datetime(3) NULL DEFAULT NULL, 739 | `updated_at` datetime(3) NULL DEFAULT NULL, 740 | `deleted_at` datetime(3) NULL DEFAULT NULL, 741 | `phone` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机', 742 | `password` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '密码', 743 | `pid` bigint NOT NULL DEFAULT 0 COMMENT '父ID', 744 | `real_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '真实姓名', 745 | `id_card` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '身份证号码', 746 | PRIMARY KEY (`id`) USING BTREE, 747 | INDEX `idx_users_deleted_at`(`deleted_at` ASC) USING BTREE 748 | ) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; 749 | 750 | -- ---------------------------- 751 | -- Records of users 752 | -- ---------------------------- 753 | 754 | SET FOREIGN_KEY_CHECKS = 1; 755 | -------------------------------------------------------------------------------- /job/TestJob.go: -------------------------------------------------------------------------------- 1 | package job 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/hibiken/asynq" 8 | "go.uber.org/zap" 9 | "vgo/core/log" 10 | ) 11 | 12 | const ( 13 | TestJob = "test:job" 14 | ) 15 | 16 | // TestJobPayload 任务参数 17 | type TestJobPayload struct { 18 | UserID int 19 | TemplateID string 20 | } 21 | 22 | // NewTestJob 创建一个新的测试任务 23 | func NewTestJob(userID int, tmplID string) (*asynq.Task, error) { 24 | payload, err := json.Marshal(TestJobPayload{UserID: userID, TemplateID: tmplID}) 25 | if err != nil { 26 | return nil, err 27 | } 28 | return asynq.NewTask(TestJob, payload), nil 29 | } 30 | 31 | // HandleTestJobTask 处理测试任务 32 | func HandleTestJobTask(ctx context.Context, t *asynq.Task) error { 33 | var p TestJobPayload 34 | if err := json.Unmarshal(t.Payload(), &p); err != nil { 35 | log.GetLogger().Error("test:job-json.Unmarshal failed: %v", zap.Any("task-err", err), zap.Any("task-asynq", asynq.SkipRetry)) 36 | } 37 | fmt.Println(fmt.Sprintf("test:job to User: user_id=%d, template_id=%s", p.UserID, p.TemplateID)) 38 | return nil 39 | } 40 | 41 | // 使用示例: 42 | //func Index(ctx *gin.Context) { 43 | // client, err := queue.NewRedisClient() 44 | // if err != nil { 45 | // fmt.Println("Failed to create redis client:", err) 46 | // return 47 | // } 48 | // defer queue.CloseRedisClient(client) 49 | // task, err := job.NewTestJob(42, "666666") 50 | // if err != nil { 51 | // log.GetLogger().Error(fmt.Sprintf("could not create task: %v", err)) 52 | // } 53 | // info, err := client.Enqueue(task) 54 | // if err != nil { 55 | // log.GetLogger().Error(fmt.Sprintf("could not enqueue task: %v", err)) 56 | // } 57 | // log.GetLogger().Info(fmt.Sprintf("enqueued task: id=%s queue=%s", info.ID, info.Queue)) 58 | //} 59 | -------------------------------------------------------------------------------- /job/job.go: -------------------------------------------------------------------------------- 1 | package job 2 | 3 | import ( 4 | "github.com/hibiken/asynq" 5 | ) 6 | 7 | // JobMaps 任务处理器映射 8 | var JobMaps = map[string]asynq.HandlerFunc{ 9 | TestJob: HandleTestJobTask, 10 | // 添加其他任务处理对儿 11 | // "...": ..., 12 | } 13 | -------------------------------------------------------------------------------- /lang/en/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "用户名不能为空": "Username cannot be empty", 3 | "手机号不能为空" : "Phone number cannot be empty", 4 | "Token无效": "Invalid Token" 5 | } -------------------------------------------------------------------------------- /lang/zh-cn/zh-cn.json: -------------------------------------------------------------------------------- 1 | { 2 | "用户名不能为空": "用户名不能为空", 3 | "手机号不能为空" : "手机号不能为空-%s-%v", 4 | "Token无效": "Token无效" 5 | } -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "vgo/bootstrap" 5 | ) 6 | 7 | func main() { 8 | bootstrap.Start() 9 | } 10 | -------------------------------------------------------------------------------- /rbac.conf: -------------------------------------------------------------------------------- 1 | [request_definition] 2 | r = sub, obj 3 | 4 | [policy_definition] 5 | p = sub, obj 6 | 7 | [role_definition] 8 | g = _, _ 9 | 10 | [policy_effect] 11 | e = some(where (p.eft == allow)) 12 | 13 | [matchers] 14 | m = g(r.sub, p.sub) && keyMatch2(r.obj, p.obj) -------------------------------------------------------------------------------- /route/route.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | import ( 4 | "github.com/gin-contrib/cors" 5 | "github.com/gin-gonic/gin" 6 | "strings" 7 | "time" 8 | AdminUserController "vgo/app/AdminUser/Bapi" 9 | AdminUser "vgo/app/AdminUser/Router" 10 | Common "vgo/app/Common/Bapi" 11 | Menu "vgo/app/Menu/Router" 12 | Notice "vgo/app/Notice/Router" 13 | Role "vgo/app/Role/Router" 14 | System "vgo/app/System/Bapi" 15 | "vgo/app/Test" 16 | Upload "vgo/app/Upload/Router" 17 | UserController "vgo/app/User/Api" 18 | User "vgo/app/User/Router" 19 | "vgo/app/Ws" 20 | "vgo/core/global" 21 | "vgo/core/middle/auth" 22 | "vgo/core/middle/casbin" 23 | "vgo/core/response" 24 | "vgo/core/router" 25 | ) 26 | 27 | // CollectRoute 注册路由 28 | func CollectRoute(app *gin.Engine) *gin.Engine { 29 | // 全局限流 30 | //app.Use(middle.RateLimiter(60, time.Second*60)) 31 | 32 | // 跨域处理 33 | origins := global.App.Config.App.ApiOrigins 34 | allowedOrigins := strings.Split(origins, ",") 35 | app.Use(cors.New(cors.Config{ 36 | AllowOrigins: allowedOrigins, 37 | AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, 38 | AllowHeaders: []string{"Origin", "Authorization", "Content-Type"}, 39 | ExposeHeaders: []string{"Content-Length"}, 40 | AllowCredentials: true, 41 | MaxAge: 12 * time.Hour, 42 | })) 43 | 44 | // 找不到路由 45 | app.NoRoute(func(c *gin.Context) { 46 | response.Fail(c, "请求地址不存在!", nil) 47 | }) 48 | // 找不到方法 49 | app.NoMethod(func(c *gin.Context) { 50 | response.Fail(c, "请求方法不存在!", nil) 51 | }) 52 | 53 | app.GET("/test", Test.Index) 54 | app.POST("/test2", Test.Index2) 55 | 56 | app.GET("/ws/link", Ws.Link) 57 | app.POST("/ws/send", Ws.Send) 58 | app.POST("/ws/send_to_all", Ws.SendToAll) 59 | 60 | admin := app.Group("/admin") 61 | admin.GET("/common/get_gender", Common.GetGender) 62 | admin.GET("/system/getBingBackgroundImage", System.GetBingBackgroundImage) 63 | admin.POST("/admin_user/login", AdminUserController.Login) 64 | 65 | bapiRouters := router.CollectRoutesFromModules( 66 | Notice.CollectRoutes, 67 | Menu.CollectRoutes, 68 | AdminUser.CollectRoutes, 69 | Role.CollectRoutes, 70 | Upload.CollectRoutes, 71 | ) 72 | 73 | enforcer := casbin.SetupCasbin() 74 | admin.Use(auth.AdminAuthMiddleware(), casbin.CheckMiddleware(enforcer)) 75 | 76 | for _, route := range bapiRouters { 77 | admin.Handle(route.Method, route.Path, route.Handler) 78 | } 79 | 80 | api := app.Group("/api") 81 | api.POST("/user/register", UserController.Register) 82 | api.POST("/user/get_token", UserController.GetToken) 83 | api.POST("/user/set_back", UserController.Setback) 84 | apiRouters := router.CollectRoutesFromModules( 85 | User.CollectRoutes, 86 | ) 87 | api.Use(auth.UserAuthMiddleware()) 88 | { 89 | for _, route := range apiRouters { 90 | api.Handle(route.Method, route.Path, route.Handler) 91 | } 92 | } 93 | return app 94 | } 95 | -------------------------------------------------------------------------------- /storage/.gitignore: -------------------------------------------------------------------------------- 1 | !.gitignore 2 | -------------------------------------------------------------------------------- /storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | !.gitignore 2 | * -------------------------------------------------------------------------------- /vTools/tools/makeCurd.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | ) 11 | 12 | func MakeCurd(moduleHandle *string, noteHandle *string) { 13 | // 获取模块名称 14 | module := *moduleHandle 15 | // 获取注释内容 16 | note := *noteHandle 17 | // 复制app目录下的指定文件夹并重命名保存 18 | newSrc := filepath.Join("app", module) 19 | if err := copyFolder("core/Tpl", newSrc); err != nil { 20 | fmt.Println(err.Error()) 21 | return 22 | } 23 | // 打开新的文件夹 24 | entries, err := os.ReadDir(newSrc) 25 | if err != nil { 26 | fmt.Println(err.Error()) 27 | return 28 | } 29 | var renamedPaths []string // 重命名的文件路径 30 | // 修改文件名 31 | for _, entry := range entries { 32 | if entry.IsDir() { 33 | renamedPaths = append(renamedPaths, newSrc+"\\"+entry.Name()+"\\"+module+".go") 34 | dirPath := filepath.Join(newSrc, entry.Name()) 35 | entries2, err := os.ReadDir(dirPath) 36 | if err != nil { 37 | fmt.Println(err.Error()) 38 | return 39 | } 40 | // 文件重命名 41 | for _, entry2 := range entries2 { 42 | oldName := filepath.Join(dirPath, entry2.Name()) 43 | newName := filepath.Join(dirPath, module+".go") 44 | if err := os.Rename(oldName, newName); err != nil { 45 | fmt.Println(err.Error()) 46 | return 47 | } 48 | } 49 | } 50 | } 51 | 52 | // 循环 renamedPaths 数组 53 | for _, path := range renamedPaths { 54 | // 打开文件并替换指定字符串 55 | lowerModule := strings.ToLower(module) 56 | replacements := []struct { 57 | oldString string 58 | newString string 59 | }{ 60 | {"Tpl", module}, 61 | {"tpl", lowerModule}, 62 | {"%模型名%", note}, 63 | } 64 | 65 | var err error 66 | for _, r := range replacements { 67 | err = replaceInFile(path, r.oldString, r.newString) 68 | if err != nil { 69 | break 70 | } 71 | } 72 | 73 | if err != nil { 74 | fmt.Println(err.Error()) 75 | return 76 | } 77 | } 78 | fmt.Println("代码生成成功,请手动注册路由~") 79 | } 80 | 81 | func copyFolder(src, dst string) error { 82 | // 获取源文件夹的信息 83 | srcInfo, err := os.Stat(src) 84 | if err != nil { 85 | return err 86 | } 87 | // 创建目标文件夹 88 | if err := os.MkdirAll(dst, srcInfo.Mode()); err != nil { 89 | return err 90 | } 91 | // 打开源文件夹 92 | entries, err := os.ReadDir(src) 93 | if err != nil { 94 | fmt.Println(err.Error()) 95 | return err 96 | } 97 | for _, entry := range entries { 98 | srcPath := filepath.Join(src, entry.Name()) 99 | dstPath := filepath.Join(dst, entry.Name()) 100 | if entry.Name() == "代码生成模板勿动.txt" { 101 | continue 102 | } 103 | if entry.IsDir() { 104 | // 递归复制子文件夹 105 | if err := copyFolder(srcPath, dstPath); err != nil { 106 | fmt.Println(err.Error()) 107 | return err 108 | } 109 | } else { 110 | // 复制文件 111 | if err := copyFile(srcPath, dstPath); err != nil { 112 | fmt.Println(err.Error()) 113 | return err 114 | } 115 | } 116 | } 117 | return nil 118 | } 119 | 120 | func copyFile(src, dst string) error { 121 | // 打开源文件 122 | srcFile, err := os.Open(src) 123 | if err != nil { 124 | return err 125 | } 126 | defer func(srcFile *os.File) { 127 | err := srcFile.Close() 128 | if err != nil { 129 | fmt.Println(err) 130 | panic(err) 131 | } 132 | }(srcFile) 133 | // 创建目标文件 134 | dstFile, err := os.Create(dst) 135 | if err != nil { 136 | return err 137 | } 138 | defer func(dstFile *os.File) { 139 | err := dstFile.Close() 140 | if err != nil { 141 | fmt.Println(err) 142 | panic(err) 143 | } 144 | }(dstFile) 145 | // 复制文件内容 146 | if _, err := io.Copy(dstFile, srcFile); err != nil { 147 | return err 148 | } 149 | // 同步文件 150 | if err := dstFile.Sync(); err != nil { 151 | return err 152 | } 153 | return nil 154 | } 155 | 156 | func replaceInFile(filePath, oldString, newString string) error { 157 | // 打开文件 158 | file, err := os.OpenFile(filePath, os.O_RDWR, 0644) 159 | if err != nil { 160 | return err 161 | } 162 | defer file.Close() 163 | 164 | // 读取文件内容 165 | scanner := bufio.NewScanner(file) 166 | var lines []string 167 | for scanner.Scan() { 168 | line := scanner.Text() 169 | // 替换指定字符串 170 | line = strings.Replace(line, oldString, newString, -1) 171 | lines = append(lines, line) 172 | } 173 | 174 | // 检查扫描错误 175 | if err := scanner.Err(); err != nil { 176 | return err 177 | } 178 | 179 | // 清空文件内容 180 | err = file.Truncate(0) 181 | if err != nil { 182 | return err 183 | } 184 | _, err = file.Seek(0, 0) 185 | if err != nil { 186 | return err 187 | } 188 | 189 | // 写入修改后的内容 190 | writer := bufio.NewWriter(file) 191 | for _, line := range lines { 192 | _, err := writer.WriteString(line + "\n") 193 | if err != nil { 194 | return err 195 | } 196 | } 197 | 198 | // 刷新缓冲区 199 | return writer.Flush() 200 | } 201 | -------------------------------------------------------------------------------- /vTools/vTools.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "vgo/vTools/tools" 7 | ) 8 | 9 | func main() { 10 | methodHandle := flag.String("method", "", "Curd") 11 | moduleHandle := flag.String("module", "", "模块") 12 | noteHandle := flag.String("note", "", "注释") 13 | flag.Parse() 14 | method := *methodHandle 15 | if method == "" { 16 | fmt.Println("请输入操作方法:例如,Curd") 17 | return 18 | } 19 | if method == "Curd" { 20 | if moduleHandle == nil || noteHandle == nil { 21 | fmt.Println("请输入模块和注释") 22 | return 23 | } 24 | fmt.Println("创建", *moduleHandle, "模块,注释:", *noteHandle) 25 | tools.MakeCurd(moduleHandle, noteHandle) 26 | return 27 | } 28 | 29 | fmt.Println("不受支持的操作方法") 30 | return 31 | } 32 | --------------------------------------------------------------------------------