├── .gitignore ├── Dockerfile ├── README.md ├── api └── v1 │ ├── enter.go │ ├── example │ ├── advanceUpload.go │ ├── customer.go │ ├── enter.go │ ├── excel.go │ ├── fileTransfer.go │ └── simpleUpload.go │ └── system │ ├── api.go │ ├── authority.go │ ├── captcha.go │ ├── casbin.go │ ├── email.go │ ├── enter.go │ ├── initdb.go │ ├── jwt.go │ ├── menu.go │ ├── operations.go │ ├── system.go │ └── user.go ├── config.docker.yaml ├── config.yaml ├── config ├── captcha.go ├── casbin.go ├── config.go ├── email.go ├── excel.go ├── gorm.go ├── jwt.go ├── oss.go ├── redis.go ├── system.go ├── timer.go └── zap.go ├── core ├── server.go ├── server_other.go ├── server_win.go ├── viper.go └── zap.go ├── docs ├── docs.go ├── swagger.json └── swagger.yaml ├── global ├── global.go └── model.go ├── go.mod ├── initialize ├── gorm.go ├── internal │ └── logger.go ├── redis.go ├── router.go ├── timer.go └── validator.go ├── main.go ├── middleware ├── casbin_rbac.go ├── cors.go ├── email.go ├── jwt.go ├── logger.go ├── need_init.go ├── operation.go └── recover.go ├── model ├── common │ ├── request │ │ └── common.go │ └── response │ │ ├── common.go │ │ └── response.go ├── example │ ├── advanceUpload.go │ ├── customer.go │ ├── excel.go │ ├── fileTransfer.go │ ├── response │ │ ├── advanceUpload.go │ │ ├── customer.go │ │ └── fileTransfer.go │ └── simpleUpload.go └── system │ ├── api.go │ ├── authority.go │ ├── authorityMenu.go │ ├── baseMenu.go │ ├── casbin.go │ ├── initDB.go │ ├── jwt.go │ ├── operations.go │ ├── request │ ├── api.go │ ├── casbin.go │ ├── init.go │ ├── jwt.go │ ├── menu.go │ ├── operations.go │ └── user.go │ ├── response │ ├── api.go │ ├── authority.go │ ├── captcha.go │ ├── casbin.go │ ├── menu.go │ ├── system.go │ └── user.go │ ├── system.go │ ├── user.go │ └── userAuthority.go ├── packfile ├── notUsePackFile.go └── usePackFile.go ├── resource ├── excel │ ├── ExcelExport.xlsx │ ├── ExcelImport.xlsx │ └── ExcelTemplate.xlsx └── rbac_model.conf ├── router ├── enter.go ├── example │ ├── customer.go │ ├── enter.go │ ├── excel.go │ ├── fileTransfer.go │ └── simpleUpload.go └── system │ ├── api.go │ ├── authority.go │ ├── base.go │ ├── casbin.go │ ├── email.go │ ├── enter.go │ ├── initdb.go │ ├── jwt.go │ ├── menu.go │ ├── operations.go │ ├── system.go │ └── user.go ├── service ├── enter.go ├── example │ ├── advanceUpload.go │ ├── customer.go │ ├── enter.go │ ├── excel.go │ ├── fileTransfer.go │ └── simpleUpload.go └── system │ ├── api.go │ ├── authority.go │ ├── baseMenu.go │ ├── casbin.go │ ├── email.go │ ├── enter.go │ ├── initDB.go │ ├── jwt.go │ ├── menu.go │ ├── operations.go │ ├── system.go │ └── user.go ├── source ├── admin.go ├── api.go ├── authorities_menus.go ├── authority.go ├── authority_menu.go ├── casbin.go ├── data_authorities.go ├── menu.go └── user_authority.go.go └── utils ├── breakpoint_continue.go ├── captcha └── redis.go ├── clamis.go ├── constant.go ├── dbOperations.go ├── directory.go ├── email.go ├── files.go ├── formate.go ├── md5.go ├── reload.go ├── rotatelogs_unix.go ├── rotatelogs_windows.go ├── server.go ├── timer └── timed_task.go ├── upload ├── aliyun_oss.go ├── local.go ├── qiniu.go ├── tencent_cos.go └── upload.go ├── validator.go ├── verify.go └── zipfiles.go /.gitignore: -------------------------------------------------------------------------------- 1 | /log/ 2 | /.idea/ 3 | /latest_log 4 | /bin/ 5 | /*.exe 6 | 7 | 8 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine 2 | 3 | WORKDIR /go/src/FiberBoot 4 | COPY . . 5 | 6 | RUN go generate && go env && go build -o server . 7 | 8 | FROM alpine:latest 9 | 10 | WORKDIR /go/src/gin-starter 11 | 12 | COPY --from=0 /go/src/FiberBoot ./ 13 | 14 | EXPOSE 8888 15 | 16 | ENTRYPOINT ./server -c config.docker.yaml 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 技术栈 2 | ### Fiber 3 | ### GORM 4 | ### Viper 5 | ### Casbin 6 | ### Swagger 7 | 8 | ## FiberBoot项目结构 9 | 10 | ```shell 11 | ├── api 12 | │ └── v1 13 | ├── config 14 | ├── core 15 | ├── docs 16 | ├── global 17 | ├── initialize 18 | │ └── internal 19 | ├── middleware 20 | ├── model 21 | │ ├── request 22 | │ └── response 23 | ├── packfile 24 | ├── resource 25 | │ ├── excel 26 | │ ├── page 27 | │ └── template 28 | ├── router 29 | ├── service 30 | ├── source 31 | └── utils 32 | ├── timer 33 | └── upload 34 | 35 | ``` 36 | 37 | | 文件夹 | 说明 | 描述 | 38 | | ------------ | ---------------------------- | ---------------------------| 39 | | `api` | api层 | api层 | 40 | | `--v1` | v1版本接口 | v1版本接口 | 41 | | `config` | 配置包 | config.yaml对应的配置结构体 | 42 | | `core` | 核心文件 | 核心组件(zap, viper, server)的初始化 | 43 | | `docs` | swagger文档目录 | swagger文档目录 | 44 | | `global` | 全局对象 | 全局对象 | 45 | | `initialize` | 初始化 | router,redis,gorm,validator, timer的初始化 | 46 | | `--internal` | 初始化内部函数 | gorm 的 logger 自定义 | 47 | | `middleware` | 中间件层 | 用于存放中间件代码 | 48 | | `model` | 模型层 | 模型对应数据表 | 49 | | `--request` | 入参结构体 | 接收前端发送到后端的数据。 | 50 | | `--response` | 出参结构体 | 返回给前端的数据结构体 | 51 | | `packfile` | 静态文件打包 | 静态文件打包 | 52 | | `resource` | 静态资源文件夹 | 负责存放静态文件 | 53 | | `--excel` | excel导入导出默认路径 | excel导入导出默认路径 | 54 | | `router` | 路由层 | 路由层 | 55 | | `service` | service层 | 存放业务逻辑问题 | 56 | | `source` | source层 | 存放初始化数据的函数 | 57 | | `utils` | 工具包 | 工具函数封装 | 58 | | `--timer` | timer | 定时器接口封装 | 59 | | `--upload` | oss | oss接口封装 | 60 | 61 | -------------------------------------------------------------------------------- /api/v1/enter.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | "FiberBoot/api/v1/example" 5 | "FiberBoot/api/v1/system" 6 | ) 7 | 8 | type ApiGroup struct { 9 | ExampleApi example.ApiGroup 10 | SystemApi system.ApiGroup 11 | } 12 | 13 | var AppApi = new(ApiGroup) 14 | -------------------------------------------------------------------------------- /api/v1/example/advanceUpload.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/common/response" 6 | exampleRes "FiberBoot/model/example/response" 7 | "FiberBoot/utils" 8 | "github.com/gofiber/fiber/v2" 9 | "go.uber.org/zap" 10 | "io/ioutil" 11 | "mime/multipart" 12 | "strconv" 13 | ) 14 | 15 | // BreakpointContinue 16 | // 17 | // @Tags FileTransfer 18 | // @Summary 断点续传到服务器 19 | // @Security ApiKeyAuth 20 | // @accept multipart/form-data 21 | // @Produce application/json 22 | // @Param file formData file true "an example for breakpoint resume, 断点续传示例" 23 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"切片创建成功"}" 24 | // @Router /fileTransfer/breakpointContinue [post] 25 | func (u *FileTransferApi) BreakpointContinue(c *fiber.Ctx) error { 26 | fileMd5 := c.FormValue("fileMd5") 27 | fileName := c.FormValue("fileName") 28 | chunkMd5 := c.FormValue("chunkMd5") 29 | chunkNumber, _ := strconv.Atoi(c.FormValue("chunkNumber")) 30 | chunkTotal, _ := strconv.Atoi(c.FormValue("chunkTotal")) 31 | FileHeader, err := c.FormFile("file") 32 | if err != nil { 33 | global.LOG.Error("接收文件失败!", zap.Any("err", err)) 34 | return response.FailWithMessage("接收文件失败", c) 35 | } 36 | f, err := FileHeader.Open() 37 | if err != nil { 38 | global.LOG.Error("文件读取失败!", zap.Any("err", err)) 39 | return response.FailWithMessage("文件读取失败", c) 40 | } 41 | defer func(f multipart.File) { 42 | _ = f.Close() 43 | }(f) 44 | cen, _ := ioutil.ReadAll(f) 45 | if !utils.CheckMd5(cen, chunkMd5) { 46 | global.LOG.Error("检查md5失败!", zap.Any("err", err)) 47 | return response.FailWithMessage("检查md5失败", c) 48 | } 49 | err, file := fileTransferService.FindOrCreateFile(fileMd5, fileName, chunkTotal) 50 | if err != nil { 51 | global.LOG.Error("查找或创建记录失败!", zap.Any("err", err)) 52 | return response.FailWithMessage("查找或创建记录失败", c) 53 | } 54 | err, patch := utils.BreakPointContinue(cen, fileName, chunkNumber, fileMd5) 55 | if err != nil { 56 | global.LOG.Error("断点续传失败!", zap.Any("err", err)) 57 | return response.FailWithMessage("断点续传失败", c) 58 | } 59 | 60 | if err = fileTransferService.CreateFileChunk(file.ID, patch, chunkNumber); err != nil { 61 | global.LOG.Error("创建文件记录失败!", zap.Any("err", err)) 62 | return response.FailWithMessage("创建文件记录失败", c) 63 | } 64 | return response.OkWithMessage("切片创建成功", c) 65 | } 66 | 67 | // FindFile 68 | // 69 | // @Tags FileTransfer 70 | // @Summary 查找文件 71 | // @Security ApiKeyAuth 72 | // @accept multipart/form-data 73 | // @Produce application/json 74 | // @Param file formData file true "Find the file, 查找文件" 75 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"查找成功"}" 76 | // @Router /fileTransfer/findFile [post] 77 | func (u *FileTransferApi) FindFile(c *fiber.Ctx) error { 78 | fileMd5 := c.Query("fileMd5") 79 | fileName := c.Query("fileName") 80 | chunkTotal, _ := strconv.Atoi(c.Query("chunkTotal")) 81 | err, file := fileTransferService.FindOrCreateFile(fileMd5, fileName, chunkTotal) 82 | if err != nil { 83 | global.LOG.Error("查找失败!", zap.Any("err", err)) 84 | return response.FailWithMessage("查找失败", c) 85 | } else { 86 | return response.OkWithDetailed(exampleRes.FileResponse{File: file}, "查找成功", c) 87 | } 88 | } 89 | 90 | // BreakpointContinueFinish 91 | // @Tags FileTransfer 92 | // @Summary 创建文件 93 | // @Security ApiKeyAuth 94 | // @accept multipart/form-data 95 | // @Produce application/json 96 | // @Param file formData file true "上传文件完成" 97 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"file uploaded, 文件创建成功"}" 98 | // @Router /fileTransfer/findFile [post] 99 | func (u *FileTransferApi) BreakpointContinueFinish(c *fiber.Ctx) error { 100 | fileMd5 := c.Query("fileMd5") 101 | fileName := c.Query("fileName") 102 | err, filePath := utils.MakeFile(fileName, fileMd5) 103 | if err != nil { 104 | global.LOG.Error("文件创建失败!", zap.Any("err", err)) 105 | return response.FailWithDetailed(exampleRes.FilePathResponse{FilePath: filePath}, "文件创建失败", c) 106 | } else { 107 | return response.OkWithDetailed(exampleRes.FilePathResponse{FilePath: filePath}, "文件创建成功", c) 108 | } 109 | } 110 | 111 | // RemoveChunk 112 | // @Tags FileTransfer 113 | // @Summary 删除切片 114 | // @Security ApiKeyAuth 115 | // @accept multipart/form-data 116 | // @Produce application/json 117 | // @Param file formData file true "删除缓存切片" 118 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"缓存切片删除成功"}" 119 | // @Router /fileTransfer/removeChunk [post] 120 | func (u *FileTransferApi) RemoveChunk(c *fiber.Ctx) error { 121 | fileMd5 := c.Query("fileMd5") 122 | fileName := c.Query("fileName") 123 | filePath := c.Query("filePath") 124 | err := utils.RemoveChunk(fileMd5) 125 | err = fileTransferService.DeleteFileChunk(fileMd5, fileName, filePath) 126 | if err != nil { 127 | global.LOG.Error("缓存切片删除失败!", zap.Any("err", err)) 128 | return response.FailWithDetailed(exampleRes.FilePathResponse{FilePath: filePath}, "缓存切片删除失败", c) 129 | } else { 130 | return response.OkWithDetailed(exampleRes.FilePathResponse{FilePath: filePath}, "缓存切片删除成功", c) 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /api/v1/example/customer.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/common/request" 6 | "FiberBoot/model/common/response" 7 | "FiberBoot/model/example" 8 | exampleRes "FiberBoot/model/example/response" 9 | "FiberBoot/utils" 10 | "github.com/gofiber/fiber/v2" 11 | "go.uber.org/zap" 12 | ) 13 | 14 | type CustomerApi struct { 15 | } 16 | 17 | // CreateCustomer 18 | // 19 | // @Tags Customer 20 | // @Summary 创建客户 21 | // @Security ApiKeyAuth 22 | // @accept application/json 23 | // @Produce application/json 24 | // @Param data body example.Customer true "客户用户名, 客户手机号码" 25 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}" 26 | // @Router /customer/customer [post] 27 | func (e *CustomerApi) CreateCustomer(c *fiber.Ctx) error { 28 | var customer example.Customer 29 | _ = c.BodyParser(&customer) 30 | if err := utils.Verify(customer, utils.CustomerVerify); err != nil { 31 | return response.FailWithMessage(err.Error(), c) 32 | } 33 | customer.UserID = utils.GetUserID(c) 34 | customer.UserAuthorityID = utils.GetUserAuthorityId(c) 35 | if err := customerService.CreateCustomer(customer); err != nil { 36 | global.LOG.Error("创建失败!", zap.Any("err", err)) 37 | return response.FailWithMessage("创建失败", c) 38 | } else { 39 | return response.OkWithMessage("创建成功", c) 40 | } 41 | } 42 | 43 | // DeleteCustomer 44 | // @Tags Customer 45 | // @Summary 删除客户 46 | // @Security ApiKeyAuth 47 | // @accept application/json 48 | // @Produce application/json 49 | // @Param data body example.Customer true "客户ID" 50 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" 51 | // @Router /customer/customer [delete] 52 | func (e *CustomerApi) DeleteCustomer(c *fiber.Ctx) error { 53 | var customer example.Customer 54 | _ = c.BodyParser(&customer) 55 | if err := utils.Verify(customer.MODEL, utils.IdVerify); err != nil { 56 | return response.FailWithMessage(err.Error(), c) 57 | } 58 | if err := customerService.DeleteCustomer(customer); err != nil { 59 | global.LOG.Error("删除失败!", zap.Any("err", err)) 60 | return response.FailWithMessage("删除失败", c) 61 | } else { 62 | return response.OkWithMessage("删除成功", c) 63 | } 64 | } 65 | 66 | // UpdateCustomer 67 | // @Tags Customer 68 | // @Summary 更新客户信息 69 | // @Security ApiKeyAuth 70 | // @accept application/json 71 | // @Produce application/json 72 | // @Param data body example.Customer true "客户ID, 客户信息" 73 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}" 74 | // @Router /customer/customer [put] 75 | func (e *CustomerApi) UpdateCustomer(c *fiber.Ctx) error { 76 | var customer example.Customer 77 | _ = c.BodyParser(&customer) 78 | if err := utils.Verify(customer.MODEL, utils.IdVerify); err != nil { 79 | return response.FailWithMessage(err.Error(), c) 80 | } 81 | if err := utils.Verify(customer, utils.CustomerVerify); err != nil { 82 | return response.FailWithMessage(err.Error(), c) 83 | } 84 | if err := customerService.UpdateCustomer(&customer); err != nil { 85 | global.LOG.Error("更新失败!", zap.Any("err", err)) 86 | return response.FailWithMessage("更新失败", c) 87 | } else { 88 | return response.OkWithMessage("更新成功", c) 89 | } 90 | } 91 | 92 | // GetCustomer 93 | // @Tags Customer 94 | // @Summary 获取单一客户信息 95 | // @Security ApiKeyAuth 96 | // @accept application/json 97 | // @Produce application/json 98 | // @Param data body example.Customer true "客户ID" 99 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" 100 | // @Router /customer/customer [get] 101 | func (e *CustomerApi) GetCustomer(c *fiber.Ctx) error { 102 | var customer example.Customer 103 | _ = c.QueryParser(&customer) 104 | if err := utils.Verify(customer.MODEL, utils.IdVerify); err != nil { 105 | return response.FailWithMessage(err.Error(), c) 106 | } 107 | err, data := customerService.GetCustomer(customer.ID) 108 | if err != nil { 109 | global.LOG.Error("获取失败!", zap.Any("err", err)) 110 | return response.FailWithMessage("获取失败", c) 111 | } else { 112 | return response.OkWithDetailed(exampleRes.CustomerResponse{Customer: data}, "获取成功", c) 113 | } 114 | } 115 | 116 | // GetCustomerList 117 | // @Tags Customer 118 | // @Summary 分页获取权限客户列表 119 | // @Security ApiKeyAuth 120 | // @accept application/json 121 | // @Produce application/json 122 | // @Param data body request.PageInfo true "页码, 每页大小" 123 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" 124 | // @Router /customer/customerList [get] 125 | func (e *CustomerApi) GetCustomerList(c *fiber.Ctx) error { 126 | var pageInfo request.PageInfo 127 | _ = c.QueryParser(&pageInfo) 128 | if err := utils.Verify(pageInfo, utils.PageInfoVerify); err != nil { 129 | return response.FailWithMessage(err.Error(), c) 130 | } 131 | err, customerList, total := customerService.GetCustomerInfoList(utils.GetUserAuthorityId(c), pageInfo) 132 | if err != nil { 133 | global.LOG.Error("获取失败!", zap.Any("err", err)) 134 | return response.FailWithMessage("获取失败"+err.Error(), c) 135 | } else { 136 | return response.OkWithDetailed(response.PageResult{ 137 | List: customerList, 138 | Total: total, 139 | Page: pageInfo.Page, 140 | PageSize: pageInfo.PageSize, 141 | }, "获取成功", c) 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /api/v1/example/enter.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import "FiberBoot/service" 4 | 5 | type ApiGroup struct { 6 | CustomerApi 7 | ExcelApi 8 | FileTransferApi 9 | SimpleUploadApi 10 | } 11 | 12 | var fileTransferService = service.AppService.ExampleService.FileTransferService 13 | var customerService = service.AppService.ExampleService.CustomerService 14 | var excelService = service.AppService.ExampleService.ExcelService 15 | var simpleUploadService = service.AppService.ExampleService.SimpleUploadService 16 | -------------------------------------------------------------------------------- /api/v1/example/excel.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/common/response" 6 | "FiberBoot/model/example" 7 | "FiberBoot/utils" 8 | "github.com/gofiber/fiber/v2" 9 | "go.uber.org/zap" 10 | ) 11 | 12 | type ExcelApi struct { 13 | } 14 | 15 | // ExportExcel 16 | // 17 | // @Tags Excel 18 | // @Summary 导出Excel 19 | // @Security ApiKeyAuth 20 | // @accept application/json 21 | // @Produce application/octet-stream 22 | // @Param data body example.ExcelInfo true "导出Excel文件信息" 23 | // @Success 200 24 | // @Router /excel/exportExcel [post] 25 | func (e *ExcelApi) ExportExcel(c *fiber.Ctx) error { 26 | var excelInfo example.ExcelInfo 27 | _ = c.BodyParser(&excelInfo) 28 | filePath := global.CONFIG.Excel.Dir + excelInfo.FileName 29 | err := excelService.ParseInfoList2Excel(excelInfo.InfoList, filePath) 30 | if err != nil { 31 | global.LOG.Error("转换Excel失败!", zap.Any("err", err)) 32 | return response.FailWithMessage("转换Excel失败", c) 33 | } 34 | 35 | c.Response().Header.Add("success", "true") 36 | err = c.SendFile(filePath) 37 | if err != nil { 38 | return err 39 | } 40 | return nil 41 | } 42 | 43 | // ImportExcel 44 | // @Tags Excel 45 | // @Summary 导入Excel文件 46 | // @Security ApiKeyAuth 47 | // @accept multipart/form-data 48 | // @Produce application/json 49 | // @Param file formData file true "导入Excel文件" 50 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"导入成功"}" 51 | // @Router /excel/importExcel [post] 52 | func (e *ExcelApi) ImportExcel(c *fiber.Ctx) error { 53 | header, err := c.FormFile("file") 54 | if err != nil { 55 | global.LOG.Error("接收文件失败!", zap.Any("err", err)) 56 | return response.FailWithMessage("接收文件失败", c) 57 | } 58 | _ = c.SaveFile(header, global.CONFIG.Excel.Dir+"ExcelImport.xlsx") 59 | return response.OkWithMessage("导入成功", c) 60 | } 61 | 62 | // LoadExcel 63 | // @Tags Excel 64 | // @Summary 加载Excel数据 65 | // @Security ApiKeyAuth 66 | // @Produce application/json 67 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"加载数据成功"}" 68 | // @Router /excel/loadExcel [get] 69 | func (e *ExcelApi) LoadExcel(c *fiber.Ctx) error { 70 | menus, err := excelService.ParseExcel2InfoList() 71 | if err != nil { 72 | global.LOG.Error("加载数据失败!", zap.Any("err", err)) 73 | return response.FailWithMessage("加载数据失败", c) 74 | } 75 | return response.OkWithDetailed(response.PageResult{ 76 | List: menus, 77 | Total: int64(len(menus)), 78 | Page: 1, 79 | PageSize: 999, 80 | }, "加载数据成功", c) 81 | } 82 | 83 | // DownloadTemplate 84 | // @Tags Excel 85 | // @Summary 下载模板 86 | // @Security ApiKeyAuth 87 | // @accept multipart/form-data 88 | // @Produce application/json 89 | // @Param fileName query string true "模板名称" 90 | // @Success 200 91 | // @Router /excel/downloadTemplate [get] 92 | func (e *ExcelApi) DownloadTemplate(c *fiber.Ctx) error { 93 | fileName := c.Query("fileName") 94 | filePath := global.CONFIG.Excel.Dir + fileName 95 | ok, err := utils.PathExists(filePath) 96 | if !ok || err != nil { 97 | global.LOG.Error("文件不存在!", zap.Any("err", err)) 98 | return response.FailWithMessage("文件不存在", c) 99 | } 100 | c.Response().Header.Add("success", "true") 101 | err = c.SendFile(filePath) 102 | if err != nil { 103 | return err 104 | } 105 | return nil 106 | } 107 | -------------------------------------------------------------------------------- /api/v1/example/fileTransfer.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/common/request" 6 | "FiberBoot/model/common/response" 7 | "FiberBoot/model/example" 8 | exampleRes "FiberBoot/model/example/response" 9 | "github.com/gofiber/fiber/v2" 10 | "go.uber.org/zap" 11 | ) 12 | 13 | type FileTransferApi struct { 14 | } 15 | 16 | // UploadFile 17 | // @Tags FileTransfer 18 | // @Summary 上传文件示例 19 | // @Security ApiKeyAuth 20 | // @accept multipart/form-data 21 | // @Produce application/json 22 | // @Param file formData file true "上传文件示例" 23 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"上传成功"}" 24 | // @Router /fileTransfer/upload [post] 25 | func (u *FileTransferApi) UploadFile(c *fiber.Ctx) error { 26 | var file example.FileTransfer 27 | noSave := c.Query("noSave", "0") 28 | header, err := c.FormFile("file") 29 | if err != nil { 30 | global.LOG.Error("接收文件失败!", zap.Any("err", err)) 31 | return response.FailWithMessage("接收文件失败", c) 32 | } 33 | err, file = fileTransferService.UploadFile(header, noSave) // 文件上传后拿到文件路径 34 | if err != nil { 35 | global.LOG.Error("修改数据库链接失败!", zap.Any("err", err)) 36 | return response.FailWithMessage("修改数据库链接失败", c) 37 | } 38 | return response.OkWithDetailed(exampleRes.ExaFileResponse{File: file}, "上传成功", c) 39 | } 40 | 41 | // DeleteFile 42 | // @Tags FileTransfer 43 | // @Summary 删除文件 44 | // @Security ApiKeyAuth 45 | // @Produce application/json 46 | // @Param data body example.FileTransfer true "传入文件里面id即可" 47 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" 48 | // @Router /fileTransfer/deleteFile [post] 49 | func (u *FileTransferApi) DeleteFile(c *fiber.Ctx) error { 50 | var file example.FileTransfer 51 | _ = c.BodyParser(&file) 52 | if err := fileTransferService.DeleteFile(file); err != nil { 53 | global.LOG.Error("删除失败!", zap.Any("err", err)) 54 | return response.FailWithMessage("删除失败", c) 55 | } 56 | return response.OkWithMessage("删除成功", c) 57 | } 58 | 59 | // GetFileList 60 | // @Tags FileTransfer 61 | // @Summary 分页文件列表 62 | // @Security ApiKeyAuth 63 | // @accept application/json 64 | // @Produce application/json 65 | // @Param data body request.PageInfo true "页码, 每页大小" 66 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" 67 | // @Router /fileTransfer/getFileList [post] 68 | func (u *FileTransferApi) GetFileList(c *fiber.Ctx) error { 69 | var pageInfo request.PageInfo 70 | _ = c.BodyParser(&pageInfo) 71 | err, list, total := fileTransferService.GetFileRecordInfoList(pageInfo) 72 | if err != nil { 73 | global.LOG.Error("获取失败!", zap.Any("err", err)) 74 | return response.FailWithMessage("获取失败", c) 75 | } else { 76 | return response.OkWithDetailed(response.PageResult{ 77 | List: list, 78 | Total: total, 79 | Page: pageInfo.Page, 80 | PageSize: pageInfo.PageSize, 81 | }, "获取成功", c) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /api/v1/example/simpleUpload.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/common/response" 6 | "FiberBoot/model/example" 7 | "FiberBoot/utils" 8 | "github.com/gofiber/fiber/v2" 9 | "go.uber.org/zap" 10 | ) 11 | 12 | type SimpleUploadApi struct { 13 | } 14 | 15 | // SimpleUpload 16 | // 17 | // @Tags SimpleUpload 18 | // @Summary 断点续传插件版示例 19 | // @Security ApiKeyAuth 20 | // @accept multipart/form-data 21 | // @Produce application/json 22 | // @Param file formData file true "断点续传插件版示例" 23 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"切片创建成功"}" 24 | // @Router /SimpleUploadApi/upload [post] 25 | func (s *SimpleUploadApi) SimpleUpload(c *fiber.Ctx) error { 26 | var chunk example.SimpleUploader 27 | header, err := c.FormFile("file") 28 | chunk.Filename = c.FormValue("filename") 29 | chunk.ChunkNumber = c.FormValue("chunkNumber") 30 | chunk.CurrentChunkSize = c.FormValue("currentChunkSize") 31 | chunk.Identifier = c.FormValue("identifier") 32 | chunk.TotalSize = c.FormValue("totalSize") 33 | chunk.TotalChunks = c.FormValue("totalChunks") 34 | var chunkDir = "./chunk/" + chunk.Identifier + "/" 35 | hasDir, _ := utils.PathExists(chunkDir) 36 | if !hasDir { 37 | if err := utils.CreateDir(chunkDir); err != nil { 38 | global.LOG.Error("创建目录失败!", zap.Any("err", err)) 39 | } 40 | } 41 | chunkPath := chunkDir + chunk.Filename + chunk.ChunkNumber 42 | err = c.SaveFile(header, chunkPath) 43 | if err != nil { 44 | global.LOG.Error("切片创建失败!", zap.Any("err", err)) 45 | return response.FailWithMessage("切片创建失败", c) 46 | } 47 | chunk.CurrentChunkPath = chunkPath 48 | err = simpleUploadService.SaveChunk(chunk) 49 | if err != nil { 50 | global.LOG.Error("切片创建失败!", zap.Any("err", err)) 51 | return response.FailWithMessage("切片创建失败", c) 52 | } else { 53 | return response.OkWithMessage("切片创建成功", c) 54 | } 55 | } 56 | 57 | // CheckFileMd5 58 | // @Tags SimpleUpload 59 | // @Summary 断点续传插件版示例 60 | // @Security ApiKeyAuth 61 | // @Produce application/json 62 | // @Param md5 query string true "md5" 63 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}" 64 | // @Router /SimpleUploadApi/checkFileMd5 [get] 65 | func (s *SimpleUploadApi) CheckFileMd5(c *fiber.Ctx) error { 66 | md5 := c.Query("md5") 67 | err, chunks, isDone := simpleUploadService.CheckFileMd5(md5) 68 | if err != nil { 69 | global.LOG.Error("md5读取失败!", zap.Any("err", err)) 70 | return response.FailWithMessage("md5读取失败", c) 71 | } else { 72 | return response.OkWithDetailed(fiber.Map{ 73 | "chunks": chunks, 74 | "isDone": isDone, 75 | }, "查询成功", c) 76 | } 77 | } 78 | 79 | // MergeFileMd5 80 | // @Tags SimpleUpload 81 | // @Summary 合并文件 82 | // @Security ApiKeyAuth 83 | // @Produce application/json 84 | // @Param md5 query string true "md5" 85 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"合并成功"}" 86 | // @Router /SimpleUploadApi/mergeFileMd5 [get] 87 | func (s *SimpleUploadApi) MergeFileMd5(c *fiber.Ctx) error { 88 | md5 := c.Query("md5") 89 | fileName := c.Query("fileName") 90 | err := simpleUploadService.MergeFileMd5(md5, fileName) 91 | if err != nil { 92 | global.LOG.Error("md5读取失败!", zap.Any("err", err)) 93 | return response.FailWithMessage("md5读取失败", c) 94 | } else { 95 | return response.OkWithMessage("合并成功", c) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /api/v1/system/captcha.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/common/response" 6 | systemRes "FiberBoot/model/system/response" 7 | "github.com/gofiber/fiber/v2" 8 | "github.com/mojocn/base64Captcha" 9 | "go.uber.org/zap" 10 | ) 11 | 12 | // 当开启多服务器部署时,替换下面的配置,使用redis共享存储验证码 13 | // var store = captcha.NewDefaultRedisStore() 14 | var store = base64Captcha.DefaultMemStore 15 | 16 | type Base struct { 17 | } 18 | 19 | // Captcha 20 | // 21 | // @Tags Base 22 | // @Summary 生成验证码 23 | // @Security ApiKeyAuth 24 | // @accept application/json 25 | // @Produce application/json 26 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"验证码获取成功"}" 27 | // @Router /base/captcha [post] 28 | func (b *Base) Captcha(c *fiber.Ctx) error { 29 | // 字符,公式,验证码配置 30 | // 生成默认数字的driver 31 | driver := base64Captcha.NewDriverDigit(global.CONFIG.Captcha.ImgHeight, global.CONFIG.Captcha.ImgWidth, global.CONFIG.Captcha.KeyLong, 0.7, 80) 32 | cp := base64Captcha.NewCaptcha(driver, store) 33 | if id, b64s, err := cp.Generate(); err != nil { 34 | global.LOG.Error("验证码获取失败!", zap.Any("err", err)) 35 | return response.FailWithMessage("验证码获取失败", c) 36 | } else { 37 | return response.OkWithDetailed(systemRes.SysCaptchaResponse{ 38 | CaptchaId: id, 39 | PicPath: b64s, 40 | }, "验证码获取成功", c) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /api/v1/system/casbin.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/common/response" 6 | "FiberBoot/model/system/request" 7 | systemRes "FiberBoot/model/system/response" 8 | "FiberBoot/utils" 9 | "github.com/gofiber/fiber/v2" 10 | "go.uber.org/zap" 11 | ) 12 | 13 | type Casbin struct { 14 | } 15 | 16 | // UpdateCasbin 17 | // 18 | // @Tags Casbin 19 | // @Summary 更新角色api权限 20 | // @Security ApiKeyAuth 21 | // @accept application/json 22 | // @Produce application/json 23 | // @Param data body request.CasbinInReceive true "权限id, 权限模型列表" 24 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}" 25 | // @Router /casbin/UpdateCasbin [post] 26 | func (cas *Casbin) UpdateCasbin(c *fiber.Ctx) error { 27 | var cmr request.CasbinInReceive 28 | _ = c.BodyParser(&cmr) 29 | if err := utils.Verify(cmr, utils.AuthorityIdVerify); err != nil { 30 | return response.FailWithMessage(err.Error(), c) 31 | 32 | } 33 | if err := casbinService.UpdateCasbin(cmr.AuthorityId, cmr.CasbinInfos); err != nil { 34 | global.LOG.Error("更新失败!", zap.Any("err", err)) 35 | return response.FailWithMessage("更新失败", c) 36 | } else { 37 | return response.OkWithMessage("更新成功", c) 38 | } 39 | } 40 | 41 | // GetPolicyPathByAuthorityId 42 | // @Tags Casbin 43 | // @Summary 获取权限列表 44 | // @Security ApiKeyAuth 45 | // @accept application/json 46 | // @Produce application/json 47 | // @Param data body request.CasbinInReceive true "权限id, 权限模型列表" 48 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" 49 | // @Router /casbin/getPolicyPathByAuthorityId [post] 50 | func (cas *Casbin) GetPolicyPathByAuthorityId(c *fiber.Ctx) error { 51 | var casbin request.CasbinInReceive 52 | _ = c.BodyParser(&casbin) 53 | if err := utils.Verify(casbin, utils.AuthorityIdVerify); err != nil { 54 | return response.FailWithMessage(err.Error(), c) 55 | } 56 | paths := casbinService.GetPolicyPathByAuthorityId(casbin.AuthorityId) 57 | return response.OkWithDetailed(systemRes.PolicyPathResponse{Paths: paths}, "获取成功", c) 58 | } 59 | -------------------------------------------------------------------------------- /api/v1/system/email.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/common/response" 6 | "github.com/gofiber/fiber/v2" 7 | "go.uber.org/zap" 8 | ) 9 | 10 | // EmailTest 11 | // 12 | // @Tags System 13 | // @Summary 发送测试邮件 14 | // @Security ApiKeyAuth 15 | // @Produce application/json 16 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"发送成功"}" 17 | // @Router /email/emailTest [post] 18 | func (s *Systems) EmailTest(c *fiber.Ctx) error { 19 | if err := emailService.EmailTest(); err != nil { 20 | global.LOG.Error("发送失败!", zap.Any("err", err)) 21 | return response.FailWithMessage("发送失败", c) 22 | } else { 23 | return response.OkWithData("发送成功", c) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /api/v1/system/enter.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import "FiberBoot/service" 4 | 5 | type ApiGroup struct { 6 | Systems 7 | Authority 8 | Base 9 | Casbin 10 | Api 11 | DB 12 | Jwt 13 | Operations 14 | AuthorityMenu 15 | } 16 | 17 | var authorityService = service.AppService.SystemService.AuthorityService 18 | var apiService = service.AppService.SystemService.ApiService 19 | var menuService = service.AppService.SystemService.MenuService 20 | var casbinService = service.AppService.SystemService.CasbinService 21 | var emailService = service.AppService.SystemService.EmailService 22 | var initDBService = service.AppService.SystemService.InitDBService 23 | var jwtService = service.AppService.SystemService.JwtService 24 | var baseMenuService = service.AppService.SystemService.BaseMenuService 25 | var operationsService = service.AppService.SystemService.OperationsService 26 | var userService = service.AppService.SystemService.UserService 27 | var configService = service.AppService.SystemService.ConfigService 28 | -------------------------------------------------------------------------------- /api/v1/system/initdb.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/common/response" 6 | "FiberBoot/model/system/request" 7 | "go.uber.org/zap" 8 | 9 | "github.com/gofiber/fiber/v2" 10 | ) 11 | 12 | type DB struct { 13 | } 14 | 15 | // InitDB 16 | // @Tags InitDB 17 | // @Summary 初始化用户数据库 18 | // @Produce application/json 19 | // @Param data body request.InitDB true "初始化数据库参数" 20 | // @Success 200 {string} string "{"code":0,"data":{},"msg":"自动创建数据库成功"}" 21 | // @Router /init/initDB [post] 22 | func (i *DB) InitDB(c *fiber.Ctx) error { 23 | if global.DB != nil { 24 | global.LOG.Error("已存在数据库配置!") 25 | return response.FailWithMessage("已存在数据库配置", c) 26 | } 27 | var dbInfo request.InitDB 28 | if err := c.BodyParser(&dbInfo); err != nil { 29 | global.LOG.Error("参数校验不通过!", zap.Any("err", err)) 30 | return response.FailWithMessage("参数校验不通过", c) 31 | } 32 | if err := initDBService.InitDB(dbInfo); err != nil { 33 | global.LOG.Error("自动创建数据库失败!", zap.Any("err", err)) 34 | return response.FailWithMessage("自动创建数据库失败,请查看后台日志,检查后在进行初始化", c) 35 | } 36 | return response.OkWithData("自动创建数据库成功", c) 37 | } 38 | 39 | // CheckDB 40 | // @Tags CheckDB 41 | // @Summary 初始化用户数据库 42 | // @Produce application/json 43 | // @Success 200 {string} string "{"code":0,"data":{},"msg":"探测完成"}" 44 | // @Router /init/checkDB [post] 45 | func (i *DB) CheckDB(c *fiber.Ctx) error { 46 | if global.DB != nil { 47 | global.LOG.Info("数据库无需初始化") 48 | return response.OkWithDetailed(fiber.Map{"needInit": false}, "数据库无需初始化", c) 49 | } else { 50 | global.LOG.Info("前往初始化数据库") 51 | return response.OkWithDetailed(fiber.Map{"needInit": true}, "前往初始化数据库", c) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /api/v1/system/jwt.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/common/response" 6 | "FiberBoot/model/system" 7 | "github.com/gofiber/fiber/v2" 8 | "go.uber.org/zap" 9 | ) 10 | 11 | type Jwt struct { 12 | } 13 | 14 | // JsonInBlacklist 15 | // @Tags Jwt 16 | // @Summary jwt加入黑名单 17 | // @Security ApiKeyAuth 18 | // @accept application/json 19 | // @Produce application/json 20 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"拉黑成功"}" 21 | // @Router /jwt/jsonInBlacklist [post] 22 | func (j *Jwt) JsonInBlacklist(c *fiber.Ctx) error { 23 | token := c.Get("x-token") 24 | jwt := system.JwtBlacklist{Jwt: token} 25 | if err := jwtService.JsonInBlacklist(jwt); err != nil { 26 | global.LOG.Error("jwt作废失败!", zap.Any("err", err)) 27 | return response.FailWithMessage("jwt作废失败", c) 28 | } else { 29 | return response.OkWithMessage("jwt作废成功", c) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /api/v1/system/operations.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/common/request" 6 | "FiberBoot/model/common/response" 7 | "FiberBoot/model/system" 8 | systemReq "FiberBoot/model/system/request" 9 | "FiberBoot/utils" 10 | "github.com/gofiber/fiber/v2" 11 | "go.uber.org/zap" 12 | ) 13 | 14 | type Operations struct { 15 | } 16 | 17 | // CreateOperations 18 | // 19 | // @Tags Operations 20 | // @Summary 创建Operations 21 | // @Security ApiKeyAuth 22 | // @accept application/json 23 | // @Produce application/json 24 | // @Param data body system.Operations true "创建Operations" 25 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" 26 | // @Router /operations/createOperations [post] 27 | func (s *Operations) CreateOperations(c *fiber.Ctx) error { 28 | var operations system.Operations 29 | _ = c.BodyParser(&operations) 30 | if err := operationsService.CreateOperations(operations); err != nil { 31 | global.LOG.Error("创建失败!", zap.Any("err", err)) 32 | return response.FailWithMessage("创建失败", c) 33 | } else { 34 | return response.OkWithMessage("创建成功", c) 35 | } 36 | } 37 | 38 | // DeleteOperations 39 | // 40 | // @Tags Operations 41 | // @Summary 删除Operations 42 | // @Security ApiKeyAuth 43 | // @accept application/json 44 | // @Produce application/json 45 | // @Param data body system.Operations true "Operations模型" 46 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" 47 | // @Router /operations/deleteOperations [delete] 48 | func (s *Operations) DeleteOperations(c *fiber.Ctx) error { 49 | var operations system.Operations 50 | _ = c.BodyParser(&operations) 51 | if err := operationsService.DeleteOperations(operations); err != nil { 52 | global.LOG.Error("删除失败!", zap.Any("err", err)) 53 | return response.FailWithMessage("删除失败", c) 54 | } else { 55 | return response.OkWithMessage("删除成功", c) 56 | } 57 | } 58 | 59 | // DeleteOperationsByIds 60 | // @Tags Operations 61 | // @Summary 批量删除Operations 62 | // @Security ApiKeyAuth 63 | // @accept application/json 64 | // @Produce application/json 65 | // @Param data body request.IdsReq true "批量删除Operations" 66 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"批量删除成功"}" 67 | // @Router /operations/deleteOperationsByIds [delete] 68 | func (s *Operations) DeleteOperationsByIds(c *fiber.Ctx) error { 69 | var IDS request.IdsReq 70 | _ = c.BodyParser(&IDS) 71 | if err := operationsService.DeleteOperationsByIds(IDS); err != nil { 72 | global.LOG.Error("批量删除失败!", zap.Any("err", err)) 73 | return response.FailWithMessage("批量删除失败", c) 74 | } else { 75 | return response.OkWithMessage("批量删除成功", c) 76 | } 77 | } 78 | 79 | // FindOperations 80 | // 81 | // @Tags Operations 82 | // @Summary 用id查询Operations 83 | // @Security ApiKeyAuth 84 | // @accept application/json 85 | // @Produce application/json 86 | // @Param data body system.Operations true "ID" 87 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}" 88 | // @Router /operations/findOperations [get] 89 | func (s *Operations) FindOperations(c *fiber.Ctx) error { 90 | var operations system.Operations 91 | _ = c.QueryParser(&operations) 92 | if err := utils.Verify(operations, utils.IdVerify); err != nil { 93 | return response.FailWithMessage(err.Error(), c) 94 | } 95 | if err, reOperations := operationsService.GetOperations(operations.ID); err != nil { 96 | global.LOG.Error("查询失败!", zap.Any("err", err)) 97 | return response.FailWithMessage("查询失败", c) 98 | } else { 99 | return response.OkWithDetailed(fiber.Map{"reOperations": reOperations}, "查询成功", c) 100 | } 101 | } 102 | 103 | // GetOperationsList 104 | // 105 | // @Tags Operations 106 | // @Summary 分页获取Operations列表 107 | // @Security ApiKeyAuth 108 | // @accept application/json 109 | // @Produce application/json 110 | // @Param data body request.OperationsSearch true "页码, 每页大小, 搜索条件" 111 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" 112 | // @Router /operations/getOperationsList [get] 113 | func (s *Operations) GetOperationsList(c *fiber.Ctx) error { 114 | var pageInfo systemReq.OperationsSearch 115 | _ = c.QueryParser(&pageInfo) 116 | if err, list, total := operationsService.GetOperationsInfoList(pageInfo); err != nil { 117 | global.LOG.Error("获取失败!", zap.Any("err", err)) 118 | return response.FailWithMessage("获取失败", c) 119 | } else { 120 | return response.OkWithDetailed(response.PageResult{ 121 | List: list, 122 | Total: total, 123 | Page: pageInfo.Page, 124 | PageSize: pageInfo.PageSize, 125 | }, "获取成功", c) 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /api/v1/system/system.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/common/response" 6 | "FiberBoot/model/system" 7 | systemRes "FiberBoot/model/system/response" 8 | "FiberBoot/utils" 9 | 10 | "github.com/gofiber/fiber/v2" 11 | "go.uber.org/zap" 12 | ) 13 | 14 | type Systems struct { 15 | } 16 | 17 | // GetSystemConfig 18 | // @Tags System 19 | // @Summary 获取配置文件内容 20 | // @Security ApiKeyAuth 21 | // @Produce application/json 22 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" 23 | // @Router /system/getSystemConfig [post] 24 | func (s *Systems) GetSystemConfig(c *fiber.Ctx) error { 25 | if err, config := configService.GetSystemConfig(); err != nil { 26 | global.LOG.Error("获取失败!", zap.Any("err", err)) 27 | return response.FailWithMessage("获取失败", c) 28 | } else { 29 | return response.OkWithDetailed(systemRes.SysConfigResponse{Config: config}, "获取成功", c) 30 | } 31 | } 32 | 33 | // SetSystemConfig 34 | // @Tags System 35 | // @Summary 设置配置文件内容 36 | // @Security ApiKeyAuth 37 | // @Produce application/json 38 | // @Param data body system.System true "设置配置文件内容" 39 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"设置成功"}" 40 | // @Router /system/setSystemConfig [post] 41 | func (s *Systems) SetSystemConfig(c *fiber.Ctx) error { 42 | var sys system.System 43 | _ = c.BodyParser(&sys) 44 | if err := configService.SetSystemConfig(sys); err != nil { 45 | global.LOG.Error("设置失败!", zap.Any("err", err)) 46 | return response.FailWithMessage("设置失败", c) 47 | } else { 48 | return response.OkWithData("设置成功", c) 49 | } 50 | } 51 | 52 | // ReloadSystem 53 | // @Tags System 54 | // @Summary 重启系统 55 | // @Security ApiKeyAuth 56 | // @Produce application/json 57 | // @Success 200 {string} string "{"code":0,"data":{},"msg":"重启系统成功"}" 58 | // @Router /system/reloadSystem [post] 59 | func (s *Systems) ReloadSystem(c *fiber.Ctx) error { 60 | err := utils.Reload() 61 | if err != nil { 62 | global.LOG.Error("重启系统失败!", zap.Any("err", err)) 63 | return response.FailWithMessage("重启系统失败", c) 64 | } else { 65 | return response.OkWithMessage("重启系统成功", c) 66 | } 67 | } 68 | 69 | // GetServerInfo 70 | // @Tags System 71 | // @Summary 获取服务器信息 72 | // @Security ApiKeyAuth 73 | // @Produce application/json 74 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" 75 | // @Router /system/getServerInfo [post] 76 | func (s *Systems) GetServerInfo(c *fiber.Ctx) error { 77 | if server, err := configService.GetServerInfo(); err != nil { 78 | global.LOG.Error("获取失败!", zap.Any("err", err)) 79 | return response.FailWithMessage("获取失败", c) 80 | } else { 81 | return response.OkWithDetailed(fiber.Map{"server": server}, "获取成功", c) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /config.docker.yaml: -------------------------------------------------------------------------------- 1 | # gin-starter Global Configuration 2 | 3 | # jwt configuration 4 | jwt: 5 | signing-key: 'yourKeys' 6 | expires-time: 604800 7 | buffer-time: 86400 8 | 9 | # zap logger configuration 10 | zap: 11 | level: 'info' 12 | format: 'console' 13 | prefix: '[GIN-STARTER]' 14 | director: 'log' 15 | link-name: 'latest_log' 16 | show-line: true 17 | encode-level: 'LowercaseColorLevelEncoder' 18 | stacktrace-key: 'stacktrace' 19 | log-in-console: true 20 | 21 | # redis configuration 22 | redis: 23 | db: 0 24 | addr: '177.7.0.14:6379' 25 | password: '' 26 | 27 | # email configuration 28 | email: 29 | to: 'xxx@qq.com' 30 | port: 465 31 | from: 'xxx@163.com' 32 | host: 'smtp.163.com' 33 | is-ssl: true 34 | secret: 'xxx' 35 | nickname: 'test' 36 | 37 | # casbin configuration 38 | casbin: 39 | model-path: './resource/rbac_model.conf' 40 | 41 | # system configuration 42 | system: 43 | env: 'public' # Change to "develop" to skip authentication for development mode 44 | addr: 8888 45 | db-type: 'mysql' 46 | oss-type: 'local' 47 | use-multipoint: false 48 | 49 | # captcha configuration 50 | captcha: 51 | key-long: 6 52 | img-width: 240 53 | img-height: 80 54 | 55 | # mysql connect configuration 56 | mysql: 57 | path: '' 58 | config: '' 59 | db-name: '' 60 | username: '' 61 | password: '' 62 | max-idle-conns: 10 63 | max-open-conns: 100 64 | log-mode: "" 65 | log-zap: false 66 | 67 | # local configuration 68 | local: 69 | path: 'uploads/file' 70 | 71 | # qiniu configuration (请自行七牛申请对应的 公钥 私钥 bucket 和 域名地址) 72 | qiniu: 73 | zone: 'ZoneHuaDong' 74 | bucket: '' 75 | img-path: '' 76 | use-https: false 77 | access-key: '' 78 | secret-key: '' 79 | use-cdn-domains: false 80 | 81 | 82 | # aliyun oss configuration 83 | aliyun-oss: 84 | endpoint: 'yourEndpoint' 85 | access-key-id: 'yourAccessKeyId' 86 | access-key-secret: 'yourAccessKeySecret' 87 | bucket-name: 'yourBucketName' 88 | bucket-url: 'yourBucketUrl' 89 | 90 | # tencent cos configuration 91 | tencent-cos: 92 | bucket: '' 93 | region: 'ap-shanghai' 94 | secret-id: '' 95 | secret-key: '' 96 | base-url: '' 97 | path-prefix: '' 98 | 99 | # excel configuration 100 | excel: 101 | dir: './resource/excel/' 102 | 103 | 104 | # timer task db clear table 105 | Timer: 106 | start: true 107 | spec: "@daily" # 定时任务详细配置参考 https://pkg.go.dev/github.com/robfig/cron/v3 108 | detail: [ 109 | # tableName: 需要清理的表名 110 | # compareField: 需要比较时间的字段 111 | # interval: 时间间隔, 具体配置详看 time.ParseDuration() 中字符串表示 且不能为负数 112 | # 2160h = 24 * 30 * 3 -> 三个月 113 | { tableName: "operations" , compareField: "created_at", interval: "2160h" }, 114 | #{ tableName: "log2" , compareField: "created_at", interval: "2160h" } 115 | ] 116 | -------------------------------------------------------------------------------- /config.yaml: -------------------------------------------------------------------------------- 1 | aliyun-oss: 2 | endpoint: yourEndpoint 3 | access-key-id: yourAccessKeyId 4 | access-key-secret: yourAccessKeySecret 5 | bucket-name: yourBucketName 6 | bucket-url: yourBucketUrl 7 | base-path: yourBasePath 8 | captcha: 9 | key-long: 6 10 | img-width: 240 11 | img-height: 80 12 | casbin: 13 | model-path: ./resource/rbac_model.conf 14 | email: 15 | to: xxx@qq.com 16 | port: 465 17 | from: xxx@163.com 18 | host: smtp.163.com 19 | is-ssl: true 20 | secret: xxx 21 | nickname: noReply 22 | excel: 23 | dir: ./resource/excel/ 24 | jwt: 25 | signing-key: yourKeys 26 | expires-time: 604800 27 | buffer-time: 86400 28 | local: 29 | path: uploads/file 30 | mysql: 31 | path: '' 32 | config: charset=utf8mb4&parseTime=True&loc=Local 33 | db-name: '' 34 | username: '' 35 | password: '' 36 | max-idle-conns: 0 37 | max-open-conns: 0 38 | log-mode: "" 39 | log-zap: true 40 | qiniu: 41 | zone: ZoneHuaDong 42 | bucket: "" 43 | img-path: "" 44 | use-https: false 45 | access-key: "" 46 | secret-key: "" 47 | use-cdn-domains: false 48 | redis: 49 | db: 0 50 | addr: 127.0.0.1:6379 51 | password: "" 52 | system: 53 | env: public 54 | addr: 8888 55 | db-type: mysql 56 | oss-type: local 57 | use-multipoint: false 58 | tencent-cos: 59 | bucket: "" 60 | region: ap-shanghai 61 | secret-id: "" 62 | secret-key: "" 63 | base-url: "" 64 | path-prefix: "" 65 | timer: 66 | start: true 67 | spec: '@daily' 68 | detail: 69 | - tableName: operations 70 | compareField: created_at 71 | interval: 2160h 72 | zap: 73 | level: info 74 | format: console 75 | prefix: '[FiberBoot]' 76 | director: log 77 | link-name: latest_log 78 | showLine: true 79 | encode-level: LowercaseColorLevelEncoder 80 | stacktrace-key: stacktrace 81 | log-in-console: true 82 | -------------------------------------------------------------------------------- /config/captcha.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Captcha struct { 4 | KeyLong int `mapstructure:"key-long" json:"keyLong" yaml:"key-long"` // 验证码长度 5 | ImgWidth int `mapstructure:"img-width" json:"imgWidth" yaml:"img-width"` // 验证码宽度 6 | ImgHeight int `mapstructure:"img-height" json:"imgHeight" yaml:"img-height"` // 验证码高度 7 | } 8 | -------------------------------------------------------------------------------- /config/casbin.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Casbin struct { 4 | ModelPath string `mapstructure:"model-path" json:"modelPath" yaml:"model-path"` // 存放casbin模型的相对路径 5 | } 6 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Server struct { 4 | JWT JWT `mapstructure:"jwt" json:"jwt" yaml:"jwt"` 5 | Zap Zap `mapstructure:"zap" json:"zap" yaml:"zap"` 6 | Redis Redis `mapstructure:"redis" json:"redis" yaml:"redis"` 7 | Email Email `mapstructure:"email" json:"email" yaml:"email"` 8 | Casbin Casbin `mapstructure:"casbin" json:"casbin" yaml:"casbin"` 9 | System System `mapstructure:"system" json:"system" yaml:"system"` 10 | Captcha Captcha `mapstructure:"captcha" json:"captcha" yaml:"captcha"` 11 | Excel Excel `mapstructure:"excel" json:"excel" yaml:"excel"` 12 | Timer Timer `mapstructure:"timer" json:"timer" yaml:"timer"` 13 | // gorm 14 | Mysql Mysql `mapstructure:"mysql" json:"mysql" yaml:"mysql"` 15 | // oss 16 | Local Local `mapstructure:"local" json:"local" yaml:"local"` 17 | Qiniu Qiniu `mapstructure:"qiniu" json:"qiniu" yaml:"qiniu"` 18 | AliyunOSS AliyunOSS `mapstructure:"aliyun-oss" json:"aliyunOSS" yaml:"aliyun-oss"` 19 | TencentCOS TencentCOS `mapstructure:"tencent-cos" json:"tencentCOS" yaml:"tencent-cos"` 20 | } 21 | -------------------------------------------------------------------------------- /config/email.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Email struct { 4 | To string `mapstructure:"to" json:"to" yaml:"to"` // 收件人:多个以英文逗号分隔 5 | Port int `mapstructure:"port" json:"port" yaml:"port"` // 端口 6 | From string `mapstructure:"from" json:"from" yaml:"from"` // 收件人 7 | Host string `mapstructure:"host" json:"host" yaml:"host"` // 服务器地址 8 | IsSSL bool `mapstructure:"is-ssl" json:"isSSL" yaml:"is-ssl"` // 是否SSL 9 | Secret string `mapstructure:"secret" json:"secret" yaml:"secret"` // 密钥 10 | Nickname string `mapstructure:"nickname" json:"nickname" yaml:"nickname"` // 昵称 11 | } 12 | -------------------------------------------------------------------------------- /config/excel.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Excel struct { 4 | Dir string `mapstructure:"dir" json:"dir" yaml:"dir"` 5 | } 6 | -------------------------------------------------------------------------------- /config/gorm.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Mysql struct { 4 | Path string `mapstructure:"path" json:"path" yaml:"path"` // 服务器地址:端口 5 | Config string `mapstructure:"config" json:"config" yaml:"config"` // 高级配置 6 | Dbname string `mapstructure:"db-name" json:"dbname" yaml:"db-name"` // 数据库名 7 | Username string `mapstructure:"username" json:"username" yaml:"username"` // 数据库用户名 8 | Password string `mapstructure:"password" json:"password" yaml:"password"` // 数据库密码 9 | MaxIdleConns int `mapstructure:"max-idle-conns" json:"maxIdleConns" yaml:"max-idle-conns"` // 空闲中的最大连接数 10 | MaxOpenConns int `mapstructure:"max-open-conns" json:"maxOpenConns" yaml:"max-open-conns"` // 打开到数据库的最大连接数 11 | LogMode string `mapstructure:"log-mode" json:"logMode" yaml:"log-mode"` // 是否开启Gorm全局日志 12 | LogZap bool `mapstructure:"log-zap" json:"logZap" yaml:"log-zap"` // 是否通过zap写入日志文件 13 | } 14 | 15 | func (m *Mysql) Dsn() string { 16 | return m.Username + ":" + m.Password + "@tcp(" + m.Path + ")/" + m.Dbname + "?" + m.Config 17 | } 18 | -------------------------------------------------------------------------------- /config/jwt.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type JWT struct { 4 | SigningKey string `mapstructure:"signing-key" json:"signingKey" yaml:"signing-key"` // jwt签名 5 | ExpiresTime int64 `mapstructure:"expires-time" json:"expiresTime" yaml:"expires-time"` // 过期时间 6 | BufferTime int64 `mapstructure:"buffer-time" json:"bufferTime" yaml:"buffer-time"` // 缓冲时间 7 | } 8 | -------------------------------------------------------------------------------- /config/oss.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Local struct { 4 | Path string `mapstructure:"path" json:"path" yaml:"path"` // 本地文件路径 5 | } 6 | 7 | type Qiniu struct { 8 | Zone string `mapstructure:"zone" json:"zone" yaml:"zone"` // 存储区域 9 | Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"` // 空间名称 10 | ImgPath string `mapstructure:"img-path" json:"imgPath" yaml:"img-path"` // CDN加速域名 11 | UseHTTPS bool `mapstructure:"use-https" json:"useHttps" yaml:"use-https"` // 是否使用https 12 | AccessKey string `mapstructure:"access-key" json:"accessKey" yaml:"access-key"` // 秘钥AK 13 | SecretKey string `mapstructure:"secret-key" json:"secretKey" yaml:"secret-key"` // 秘钥SK 14 | UseCdnDomains bool `mapstructure:"use-cdn-domains" json:"useCdnDomains" yaml:"use-cdn-domains"` // 上传是否使用CDN上传加速 15 | } 16 | 17 | type AliyunOSS struct { 18 | Endpoint string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"` 19 | AccessKeyId string `mapstructure:"access-key-id" json:"accessKeyId" yaml:"access-key-id"` 20 | AccessKeySecret string `mapstructure:"access-key-secret" json:"accessKeySecret" yaml:"access-key-secret"` 21 | BucketName string `mapstructure:"bucket-name" json:"bucketName" yaml:"bucket-name"` 22 | BucketUrl string `mapstructure:"bucket-url" json:"bucketUrl" yaml:"bucket-url"` 23 | BasePath string `mapstructure:"base-path" json:"basePath" yaml:"base-path"` 24 | } 25 | type TencentCOS struct { 26 | Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"` 27 | Region string `mapstructure:"region" json:"region" yaml:"region"` 28 | SecretID string `mapstructure:"secret-id" json:"secretID" yaml:"secret-id"` 29 | SecretKey string `mapstructure:"secret-key" json:"secretKey" yaml:"secret-key"` 30 | BaseURL string `mapstructure:"base-url" json:"baseURL" yaml:"base-url"` 31 | PathPrefix string `mapstructure:"path-prefix" json:"pathPrefix" yaml:"path-prefix"` 32 | } 33 | -------------------------------------------------------------------------------- /config/redis.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Redis struct { 4 | DB int `mapstructure:"db" json:"db" yaml:"db"` // redis的哪个数据库 5 | Addr string `mapstructure:"addr" json:"addr" yaml:"addr"` // 服务器地址:端口 6 | Password string `mapstructure:"password" json:"password" yaml:"password"` // 密码 7 | } 8 | -------------------------------------------------------------------------------- /config/system.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type System struct { 4 | Env string `mapstructure:"env" json:"env" yaml:"env"` // 环境值 5 | Addr int `mapstructure:"addr" json:"addr" yaml:"addr"` // 端口值 6 | DbType string `mapstructure:"db-type" json:"dbType" yaml:"db-type"` // 数据库类型:mysql(默认)|sqlite|sqlserver|postgresql 7 | OssType string `mapstructure:"oss-type" json:"ossType" yaml:"oss-type"` // Oss类型 8 | UseMultipoint bool `mapstructure:"use-multipoint" json:"useMultipoint" yaml:"use-multipoint"` // 多点登录拦截 9 | } 10 | -------------------------------------------------------------------------------- /config/timer.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Timer struct { 4 | Start bool `mapstructure:"start" json:"start" yaml:"start"` // 是否启用 5 | Spec string `mapstructure:"spec" json:"spec" yaml:"spec"` // CRON表达式 6 | Detail []Detail `mapstructure:"detail" json:"detail" yaml:"detail"` 7 | } 8 | 9 | type Detail struct { 10 | TableName string `mapstructure:"tableName" json:"tableName" yaml:"tableName"` // 需要清理的表名 11 | CompareField string `mapstructure:"compareField" json:"compareField" yaml:"compareField"` // 需要比较时间的字段 12 | Interval string `mapstructure:"interval" json:"interval" yaml:"interval"` // 时间间隔 13 | } 14 | -------------------------------------------------------------------------------- /config/zap.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Zap struct { 4 | Level string `mapstructure:"level" json:"level" yaml:"level"` // 级别 5 | Format string `mapstructure:"format" json:"format" yaml:"format"` // 输出 6 | Prefix string `mapstructure:"prefix" json:"prefix" yaml:"prefix"` // 日志前缀 7 | Director string `mapstructure:"director" json:"director" yaml:"director"` // 日志文件夹 8 | LinkName string `mapstructure:"link-name" json:"linkName" yaml:"link-name"` // 软链接名称 9 | ShowLine bool `mapstructure:"show-line" json:"showLine" yaml:"showLine"` // 显示行 10 | EncodeLevel string `mapstructure:"encode-level" json:"encodeLevel" yaml:"encode-level"` // 编码级 11 | StacktraceKey string `mapstructure:"stacktrace-key" json:"stacktraceKey" yaml:"stacktrace-key"` // 栈名 12 | LogInConsole bool `mapstructure:"log-in-console" json:"logInConsole" yaml:"log-in-console"` // 输出控制台 13 | } 14 | -------------------------------------------------------------------------------- /core/server.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/initialize" 6 | "fmt" 7 | "github.com/gofiber/fiber/v2" 8 | "go.uber.org/zap" 9 | "time" 10 | ) 11 | 12 | type Server interface { 13 | ServeAsync(string, *fiber.App) error 14 | } 15 | 16 | func RunServer() { 17 | if global.CONFIG.System.UseMultipoint { 18 | // 初始化redis服务 19 | initialize.Redis() 20 | } 21 | Router := initialize.Routers() 22 | address := fmt.Sprintf(":%d", global.CONFIG.System.Addr) 23 | 24 | time.Sleep(10 * time.Microsecond) 25 | 26 | global.LOG.Info("server run success on ", zap.String("address", address)) 27 | fmt.Printf("[FiberBoot]文档地址:http://127.0.0.1%s/swagger/index.html \n", address) 28 | 29 | global.LOG.Error(newServer().ServeAsync(address, Router).Error()) 30 | } 31 | 32 | //@author: Flame 33 | //@function: NewOss 34 | //@description: OSS接口 35 | //@description: OSS的实例化方法 36 | //@return: OSS 37 | 38 | func newServer() Server { 39 | return &ServerImpl{} 40 | } 41 | -------------------------------------------------------------------------------- /core/server_other.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package core 4 | 5 | import ( 6 | "github.com/cloudflare/tableflip" 7 | "github.com/gofiber/fiber/v2" 8 | "log" 9 | "os" 10 | "os/signal" 11 | "syscall" 12 | "time" 13 | ) 14 | 15 | type ServerImpl struct{} 16 | 17 | func (*ServerImpl) ServeAsync(address string, app *fiber.App) error { 18 | return gracefulUpgrade(address, app) 19 | } 20 | 21 | func gracefulUpgrade(address string, router *fiber.App) error { 22 | upg, err := tableflip.New(tableflip.Options{}) 23 | if err != nil { 24 | return err 25 | } 26 | defer upg.Stop() 27 | 28 | // 监听系统的 SIGHUP 信号,以此信号触发进程重启 29 | go func() { 30 | sig := make(chan os.Signal, 1) 31 | signal.Notify(sig, syscall.SIGHUP) 32 | for range sig { 33 | // 核心的 Upgrade 调用 34 | err := upg.Upgrade() 35 | if err != nil { 36 | log.Println("Upgrade failed:", err) 37 | } 38 | } 39 | }() 40 | 41 | // 注意必须使用 upg.Listen 对端口进行监听 42 | ln, err := upg.Listen("tcp", address) 43 | if err != nil { 44 | log.Fatalln("Can't listen:", err) 45 | return err 46 | } 47 | 48 | // 启动server 49 | go func() { 50 | err = router.Listener(ln) 51 | if err != nil { 52 | panic(err) 53 | } 54 | }() 55 | 56 | if err = upg.Ready(); err != nil { 57 | panic(err) 58 | return err 59 | } 60 | <-upg.Exit() 61 | 62 | // 给老进程的退出设置一个 30s 的超时时间,保证老进程的退出 63 | time.AfterFunc(30*time.Second, func() { 64 | log.Println("Graceful shutdown timed out") 65 | os.Exit(1) 66 | }) 67 | return nil 68 | } 69 | -------------------------------------------------------------------------------- /core/server_win.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package core 4 | 5 | import "github.com/gofiber/fiber/v2" 6 | 7 | type ServerImpl struct{} 8 | 9 | func (*ServerImpl) ServeAsync(address string, app *fiber.App) error { 10 | return app.Listen(address) 11 | } 12 | -------------------------------------------------------------------------------- /core/viper.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "FiberBoot/global" 5 | _ "FiberBoot/packfile" 6 | "FiberBoot/utils" 7 | "flag" 8 | "fmt" 9 | "github.com/fsnotify/fsnotify" 10 | "github.com/spf13/viper" 11 | "os" 12 | ) 13 | 14 | func Viper(path ...string) *viper.Viper { 15 | var config string 16 | if len(path) == 0 { 17 | flag.StringVar(&config, "c", "", "choose config file.") 18 | flag.Parse() 19 | if config == "" { // 优先级: 命令行 > 环境变量 > 默认值 20 | if configEnv := os.Getenv(utils.ConfigEnv); configEnv == "" { 21 | config = utils.ConfigFile 22 | fmt.Printf("您正在使用config的默认值,config的路径为%v\n", utils.ConfigFile) 23 | } else { 24 | config = configEnv 25 | fmt.Printf("您正在使用CONFIG环境变量,config的路径为%v\n", config) 26 | } 27 | } else { 28 | fmt.Printf("您正在使用命令行的-c参数传递的值,config的路径为%v\n", config) 29 | } 30 | } else { 31 | config = path[0] 32 | fmt.Printf("您正在使用func Viper()传递的值,config的路径为%v\n", config) 33 | } 34 | 35 | v := viper.New() 36 | v.SetConfigFile(config) 37 | v.SetConfigType("yaml") 38 | err := v.ReadInConfig() 39 | if err != nil { 40 | panic(fmt.Errorf("Fatal error config file: %s \n", err)) 41 | } 42 | v.WatchConfig() 43 | 44 | v.OnConfigChange(func(e fsnotify.Event) { 45 | fmt.Println("config file changed:", e.Name) 46 | if err := v.Unmarshal(&global.CONFIG); err != nil { 47 | fmt.Println(err) 48 | } 49 | }) 50 | if err := v.Unmarshal(&global.CONFIG); err != nil { 51 | fmt.Println(err) 52 | } 53 | return v 54 | } 55 | -------------------------------------------------------------------------------- /core/zap.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/utils" 6 | "fmt" 7 | "go.uber.org/zap" 8 | "go.uber.org/zap/zapcore" 9 | "os" 10 | "time" 11 | ) 12 | 13 | var level zapcore.Level 14 | 15 | func Zap() (logger *zap.Logger) { 16 | if ok, _ := utils.PathExists(global.CONFIG.Zap.Director); !ok { // 判断是否有Director文件夹 17 | fmt.Printf("create %v directory\n", global.CONFIG.Zap.Director) 18 | _ = os.Mkdir(global.CONFIG.Zap.Director, os.ModePerm) 19 | } 20 | 21 | switch global.CONFIG.Zap.Level { // 初始化配置文件的Level 22 | case "debug": 23 | level = zap.DebugLevel 24 | case "info": 25 | level = zap.InfoLevel 26 | case "warn": 27 | level = zap.WarnLevel 28 | case "error": 29 | level = zap.ErrorLevel 30 | case "dpanic": 31 | level = zap.DPanicLevel 32 | case "panic": 33 | level = zap.PanicLevel 34 | case "fatal": 35 | level = zap.FatalLevel 36 | default: 37 | level = zap.InfoLevel 38 | } 39 | 40 | if level == zap.DebugLevel || level == zap.ErrorLevel { 41 | logger = zap.New(getEncoderCore(), zap.AddStacktrace(level)) 42 | } else { 43 | logger = zap.New(getEncoderCore()) 44 | } 45 | if global.CONFIG.Zap.ShowLine { 46 | logger = logger.WithOptions(zap.AddCaller()) 47 | } 48 | return logger 49 | } 50 | 51 | // getEncoderConfig 获取zapcore.EncoderConfig 52 | func getEncoderConfig() (config zapcore.EncoderConfig) { 53 | config = zapcore.EncoderConfig{ 54 | MessageKey: "message", 55 | LevelKey: "level", 56 | TimeKey: "time", 57 | NameKey: "logger", 58 | CallerKey: "caller", 59 | StacktraceKey: global.CONFIG.Zap.StacktraceKey, 60 | LineEnding: zapcore.DefaultLineEnding, 61 | EncodeLevel: zapcore.LowercaseLevelEncoder, 62 | EncodeTime: CustomTimeEncoder, 63 | EncodeDuration: zapcore.SecondsDurationEncoder, 64 | EncodeCaller: zapcore.FullCallerEncoder, 65 | } 66 | switch { 67 | case global.CONFIG.Zap.EncodeLevel == "LowercaseLevelEncoder": // 小写编码器(默认) 68 | config.EncodeLevel = zapcore.LowercaseLevelEncoder 69 | case global.CONFIG.Zap.EncodeLevel == "LowercaseColorLevelEncoder": // 小写编码器带颜色 70 | config.EncodeLevel = zapcore.LowercaseColorLevelEncoder 71 | case global.CONFIG.Zap.EncodeLevel == "CapitalLevelEncoder": // 大写编码器 72 | config.EncodeLevel = zapcore.CapitalLevelEncoder 73 | case global.CONFIG.Zap.EncodeLevel == "CapitalColorLevelEncoder": // 大写编码器带颜色 74 | config.EncodeLevel = zapcore.CapitalColorLevelEncoder 75 | default: 76 | config.EncodeLevel = zapcore.LowercaseLevelEncoder 77 | } 78 | return config 79 | } 80 | 81 | // getEncoder 获取zapcore.Encoder 82 | func getEncoder() zapcore.Encoder { 83 | if global.CONFIG.Zap.Format == "json" { 84 | return zapcore.NewJSONEncoder(getEncoderConfig()) 85 | } 86 | return zapcore.NewConsoleEncoder(getEncoderConfig()) 87 | } 88 | 89 | // getEncoderCore 获取Encoder的zapcore.Core 90 | func getEncoderCore() (core zapcore.Core) { 91 | writer, err := utils.GetWriteSyncer() // 使用file-rotateLogs进行日志分割 92 | if err != nil { 93 | fmt.Printf("Get Write Syncer Failed err:%v", err.Error()) 94 | return 95 | } 96 | return zapcore.NewCore(getEncoder(), writer, level) 97 | } 98 | 99 | // CustomTimeEncoder 自定义日志输出时间格式 100 | func CustomTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { 101 | enc.AppendString(t.Format(global.CONFIG.Zap.Prefix + "2006/01/02 - 15:04:05.000")) 102 | } 103 | -------------------------------------------------------------------------------- /global/global.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "FiberBoot/utils/timer" 5 | 6 | "golang.org/x/sync/singleflight" 7 | 8 | "go.uber.org/zap" 9 | 10 | "FiberBoot/config" 11 | 12 | "github.com/go-redis/redis/v8" 13 | "github.com/spf13/viper" 14 | "gorm.io/gorm" 15 | ) 16 | 17 | var ( 18 | DB *gorm.DB 19 | REDIS *redis.Client 20 | CONFIG config.Server 21 | VP *viper.Viper 22 | LOG *zap.Logger 23 | Timer = timer.NewTimerTask() 24 | ConcurrencyControl = &singleflight.Group{} 25 | ) 26 | -------------------------------------------------------------------------------- /global/model.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "gorm.io/gorm" 5 | "time" 6 | ) 7 | 8 | type MODEL struct { 9 | ID uint `gorm:"primaryKey"` // 主键ID 10 | CreatedAt time.Time // 创建时间 11 | UpdatedAt time.Time // 更新时间 12 | DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // 删除时间 13 | } 14 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module FiberBoot 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 7 | github.com/aliyun/aliyun-oss-go-sdk v2.2.3+incompatible 8 | github.com/arsmn/fiber-swagger/v2 v2.31.1 9 | github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect 10 | github.com/casbin/casbin/v2 v2.47.1 11 | github.com/casbin/gorm-adapter/v3 v3.7.1 12 | github.com/cloudflare/tableflip v1.2.3 13 | github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect 14 | github.com/fsnotify/fsnotify v1.5.4 15 | github.com/go-redis/redis/v8 v8.11.5 16 | github.com/go-sql-driver/mysql v1.6.0 17 | github.com/gofiber/fiber/v2 v2.33.0 18 | github.com/golang-jwt/jwt/v4 v4.4.1 19 | github.com/gookit/color v1.5.0 20 | github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect 21 | github.com/jonboulle/clockwork v0.1.0 // indirect 22 | github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible 23 | github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible 24 | github.com/lestrrat-go/strftime v1.0.3 // indirect 25 | github.com/mailru/easyjson v0.7.7 // indirect 26 | github.com/mojocn/base64Captcha v1.3.5 27 | github.com/qiniu/go-sdk/v7 v7.12.1 28 | github.com/robfig/cron/v3 v3.0.1 29 | github.com/satori/go.uuid v1.2.0 30 | github.com/shirou/gopsutil/v3 v3.22.4 31 | github.com/spf13/viper v1.11.0 32 | github.com/swaggo/swag v1.8.2 33 | github.com/tebeka/strftime v0.1.3 // indirect 34 | github.com/tencentyun/cos-go-sdk-v5 v0.7.34 35 | github.com/xuri/excelize/v2 v2.6.0 36 | go.uber.org/zap v1.21.0 37 | golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 38 | gorm.io/driver/mysql v1.3.3 39 | gorm.io/gorm v1.23.5 40 | ) 41 | -------------------------------------------------------------------------------- /initialize/gorm.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/initialize/internal" 6 | "FiberBoot/model/example" 7 | "FiberBoot/model/system" 8 | "os" 9 | 10 | "go.uber.org/zap" 11 | "gorm.io/driver/mysql" 12 | "gorm.io/gorm" 13 | "gorm.io/gorm/logger" 14 | ) 15 | 16 | //@author: Flame 17 | //@function: Gorm 18 | //@description: 初始化数据库并产生数据库全局变量 19 | //@return: *gorm.DB 20 | 21 | func Gorm() *gorm.DB { 22 | switch global.CONFIG.System.DbType { 23 | case "mysql": 24 | return GormMysql() 25 | default: 26 | return GormMysql() 27 | } 28 | } 29 | 30 | // MysqlTables 31 | //@author: Flame 32 | //@function: MysqlTables 33 | //@description: 注册数据库表专用 34 | //@param: db *gorm.DB 35 | 36 | func MysqlTables(db *gorm.DB) { 37 | err := db.AutoMigrate( 38 | system.User{}, 39 | system.Authority{}, 40 | system.Api{}, 41 | system.BaseMenu{}, 42 | system.BaseMenuParameter{}, 43 | system.JwtBlacklist{}, 44 | example.FileTransfer{}, 45 | example.File{}, 46 | example.FileChunk{}, 47 | example.SimpleUploader{}, 48 | example.Customer{}, 49 | system.Operations{}, 50 | ) 51 | if err != nil { 52 | global.LOG.Error("register table failed", zap.Any("err", err)) 53 | os.Exit(0) 54 | } 55 | global.LOG.Info("register table success") 56 | } 57 | 58 | //@author: Flame 59 | //@function: GormMysql 60 | //@description: 初始化Mysql数据库 61 | //@return: *gorm.DB 62 | 63 | func GormMysql() *gorm.DB { 64 | m := global.CONFIG.Mysql 65 | if m.Dbname == "" { 66 | return nil 67 | } 68 | dsn := m.Username + ":" + m.Password + "@tcp(" + m.Path + ")/" + m.Dbname + "?" + m.Config 69 | mysqlConfig := mysql.Config{ 70 | DSN: dsn, // DSN data source name 71 | DefaultStringSize: 256, // string 类型字段的默认长度 72 | DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持 73 | DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引 74 | DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列 75 | SkipInitializeWithVersion: false, // 根据版本自动配置 76 | } 77 | if db, err := gorm.Open(mysql.New(mysqlConfig), gormConfig()); err != nil { 78 | //global.LOG.Error("MySQL启动异常", zap.Any("err", err)) 79 | //os.Exit(0) 80 | //return nil 81 | return nil 82 | } else { 83 | sqlDB, _ := db.DB() 84 | sqlDB.SetMaxIdleConns(m.MaxIdleConns) 85 | sqlDB.SetMaxOpenConns(m.MaxOpenConns) 86 | return db 87 | } 88 | } 89 | 90 | //@author: Flame 91 | //@function: gormConfig 92 | //@description: 根据配置决定是否开启日志 93 | //@param: mod bool 94 | //@return: *gorm.Config 95 | 96 | func gormConfig() *gorm.Config { 97 | config := &gorm.Config{DisableForeignKeyConstraintWhenMigrating: true} 98 | switch global.CONFIG.Mysql.LogMode { 99 | case "silent", "Silent": 100 | config.Logger = internal.Default.LogMode(logger.Silent) 101 | case "error", "Error": 102 | config.Logger = internal.Default.LogMode(logger.Error) 103 | case "warn", "Warn": 104 | config.Logger = internal.Default.LogMode(logger.Warn) 105 | case "info", "Info": 106 | config.Logger = internal.Default.LogMode(logger.Info) 107 | default: 108 | config.Logger = internal.Default.LogMode(logger.Info) 109 | } 110 | return config 111 | } 112 | -------------------------------------------------------------------------------- /initialize/internal/logger.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "context" 6 | "fmt" 7 | "gorm.io/gorm/logger" 8 | "gorm.io/gorm/utils" 9 | "io/ioutil" 10 | "log" 11 | "os" 12 | "time" 13 | ) 14 | 15 | type config struct { 16 | SlowThreshold time.Duration 17 | Colorful bool 18 | LogLevel logger.LogLevel 19 | } 20 | 21 | var ( 22 | _ = New(log.New(ioutil.Discard, "", log.LstdFlags), config{}) 23 | Default = New(log.New(os.Stdout, "\r\n", log.LstdFlags), config{ 24 | SlowThreshold: 200 * time.Millisecond, 25 | LogLevel: logger.Warn, 26 | Colorful: true, 27 | }) 28 | _ = traceRecorder{Interface: Default, BeginAt: time.Now()} 29 | ) 30 | 31 | func New(writer logger.Writer, config config) logger.Interface { 32 | var ( 33 | infoStr = "%s\n[info] " 34 | warnStr = "%s\n[warn] " 35 | errStr = "%s\n[error] " 36 | traceStr = "%s\n[%.3fms] [rows:%v] %s\n" 37 | traceWarnStr = "%s %s\n[%.3fms] [rows:%v] %s\n" 38 | traceErrStr = "%s %s\n[%.3fms] [rows:%v] %s\n" 39 | ) 40 | 41 | if config.Colorful { 42 | infoStr = logger.Green + "%s\n" + logger.Reset + logger.Green + "[info] " + logger.Reset 43 | warnStr = logger.BlueBold + "%s\n" + logger.Reset + logger.Magenta + "[warn] " + logger.Reset 44 | errStr = logger.Magenta + "%s\n" + logger.Reset + logger.Red + "[error] " + logger.Reset 45 | traceStr = logger.Green + "%s\n" + logger.Reset + logger.Yellow + "[%.3fms] " + logger.BlueBold + "[rows:%v]" + logger.Reset + " %s\n" 46 | traceWarnStr = logger.Green + "%s " + logger.Yellow + "%s\n" + logger.Reset + logger.RedBold + "[%.3fms] " + logger.Yellow + "[rows:%v]" + logger.Magenta + " %s\n" + logger.Reset 47 | traceErrStr = logger.RedBold + "%s " + logger.MagentaBold + "%s\n" + logger.Reset + logger.Yellow + "[%.3fms] " + logger.BlueBold + "[rows:%v]" + logger.Reset + " %s\n" 48 | } 49 | 50 | return &_logger{ 51 | Writer: writer, 52 | config: config, 53 | infoStr: infoStr, 54 | warnStr: warnStr, 55 | errStr: errStr, 56 | traceStr: traceStr, 57 | traceWarnStr: traceWarnStr, 58 | traceErrStr: traceErrStr, 59 | } 60 | } 61 | 62 | type _logger struct { 63 | config 64 | logger.Writer 65 | infoStr, warnStr, errStr string 66 | traceStr, traceErrStr, traceWarnStr string 67 | } 68 | 69 | // LogMode log mode 70 | func (c *_logger) LogMode(level logger.LogLevel) logger.Interface { 71 | newLogger := *c 72 | newLogger.LogLevel = level 73 | return &newLogger 74 | } 75 | 76 | // Info print info 77 | func (c *_logger) Info(_ context.Context, message string, data ...interface{}) { 78 | if c.LogLevel >= logger.Info { 79 | c.Printf(c.infoStr+message, append([]interface{}{utils.FileWithLineNum()}, data...)...) 80 | } 81 | } 82 | 83 | // Warn print warn messages 84 | func (c *_logger) Warn(_ context.Context, message string, data ...interface{}) { 85 | if c.LogLevel >= logger.Warn { 86 | c.Printf(c.warnStr+message, append([]interface{}{utils.FileWithLineNum()}, data...)...) 87 | } 88 | } 89 | 90 | // Error print error messages 91 | func (c *_logger) Error(_ context.Context, message string, data ...interface{}) { 92 | if c.LogLevel >= logger.Error { 93 | c.Printf(c.errStr+message, append([]interface{}{utils.FileWithLineNum()}, data...)...) 94 | } 95 | } 96 | 97 | // Trace print sql message 98 | func (c *_logger) Trace(_ context.Context, begin time.Time, fc func() (string, int64), err error) { 99 | if c.LogLevel > 0 { 100 | elapsed := time.Since(begin) 101 | switch { 102 | case err != nil && c.LogLevel >= logger.Error: 103 | sql, rows := fc() 104 | if rows == -1 { 105 | c.Printf(c.traceErrStr, utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, "-", sql) 106 | } else { 107 | c.Printf(c.traceErrStr, utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, rows, sql) 108 | } 109 | case elapsed > c.SlowThreshold && c.SlowThreshold != 0 && c.LogLevel >= logger.Warn: 110 | sql, rows := fc() 111 | slowLog := fmt.Sprintf("SLOW SQL >= %v", c.SlowThreshold) 112 | if rows == -1 { 113 | c.Printf(c.traceWarnStr, utils.FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, "-", sql) 114 | } else { 115 | c.Printf(c.traceWarnStr, utils.FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, rows, sql) 116 | } 117 | case c.LogLevel >= logger.Info: 118 | sql, rows := fc() 119 | if rows == -1 { 120 | c.Printf(c.traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, "-", sql) 121 | } else { 122 | c.Printf(c.traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, rows, sql) 123 | } 124 | } 125 | } 126 | } 127 | 128 | func (c *_logger) Printf(message string, data ...interface{}) { 129 | if global.CONFIG.Mysql.LogZap { 130 | global.LOG.Info(fmt.Sprintf(message, data...)) 131 | } else { 132 | c.Writer.Printf(message, data...) 133 | } 134 | } 135 | 136 | type traceRecorder struct { 137 | logger.Interface 138 | BeginAt time.Time 139 | SQL string 140 | RowsAffected int64 141 | Err error 142 | } 143 | 144 | func (t traceRecorder) New() *traceRecorder { 145 | return &traceRecorder{Interface: t.Interface, BeginAt: time.Now()} 146 | } 147 | 148 | func (t *traceRecorder) Trace(_ context.Context, begin time.Time, fc func() (string, int64), err error) { 149 | t.BeginAt = begin 150 | t.SQL, t.RowsAffected = fc() 151 | t.Err = err 152 | } 153 | -------------------------------------------------------------------------------- /initialize/redis.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "context" 6 | 7 | "github.com/go-redis/redis/v8" 8 | "go.uber.org/zap" 9 | ) 10 | 11 | func Redis() { 12 | redisCfg := global.CONFIG.Redis 13 | client := redis.NewClient(&redis.Options{ 14 | Addr: redisCfg.Addr, 15 | Password: redisCfg.Password, // no password set 16 | DB: redisCfg.DB, // use default DB 17 | }) 18 | pong, err := client.Ping(context.Background()).Result() 19 | if err != nil { 20 | global.LOG.Error("redis connect ping failed, err:", zap.Any("err", err)) 21 | } else { 22 | global.LOG.Info("redis connect ping response:", zap.String("pong", pong)) 23 | global.REDIS = client 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /initialize/router.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | _ "FiberBoot/docs" 5 | "FiberBoot/global" 6 | "FiberBoot/middleware" 7 | "FiberBoot/router" 8 | swagger "github.com/arsmn/fiber-swagger/v2" 9 | "github.com/gofiber/fiber/v2" 10 | ) 11 | 12 | // 初始化总路由 13 | 14 | func Routers() *fiber.App { 15 | var Router = fiber.New() 16 | Router.Static(global.CONFIG.Local.Path, global.CONFIG.Local.Path) // 为用户头像和文件提供静态地址 17 | global.LOG.Info("use middleware logger") 18 | Router.Use(middleware.Logger()) 19 | 20 | global.LOG.Info("use middleware recover") 21 | Router.Use(middleware.Recover()) 22 | // 跨域 23 | global.LOG.Info("use middleware cors") 24 | Router.Use(middleware.Cors()) 25 | 26 | global.LOG.Info("register swagger handler") 27 | Router.Get("/swagger/*", swagger.HandlerDefault) 28 | // 方便统一添加路由组前缀 多服务器上线使用 29 | 30 | //获取路由组实例 31 | systemRouter := router.AppRouter.System 32 | exampleRouter := router.AppRouter.Example 33 | PublicGroup := Router.Group("") 34 | { 35 | systemRouter.InitBaseRouter(PublicGroup) // 注册基础功能路由 不做鉴权 36 | systemRouter.InitInitRouter(PublicGroup) // 自动初始化相关 37 | } 38 | PrivateGroup := Router.Group("") 39 | PrivateGroup.Use(middleware.JWTAuth()).Use(middleware.CasbinHandler()) 40 | { 41 | systemRouter.InitApiRouter(PrivateGroup) // 注册功能api路由 42 | systemRouter.InitJwtRouter(PrivateGroup) // jwt相关路由 43 | systemRouter.InitUserRouter(PrivateGroup) // 注册用户路由 44 | systemRouter.InitMenuRouter(PrivateGroup) // 注册menu路由 45 | systemRouter.InitEmailRouter(PrivateGroup) // 邮件相关路由 46 | systemRouter.InitSystemRouter(PrivateGroup) // system相关路由 47 | systemRouter.InitCasbinRouter(PrivateGroup) // 权限相关路由 48 | systemRouter.InitAuthorityRouter(PrivateGroup) // 注册角色路由 49 | systemRouter.InitOperationsRouter(PrivateGroup) // 操作记录 50 | exampleRouter.InitFileTransferRouter(PrivateGroup) // 文件上传下载功能路由 51 | exampleRouter.InitExcelRouter(PrivateGroup) // 表格导入导出 52 | exampleRouter.InitSimpleUploaderRouter(PrivateGroup) // 断点续传(插件版) 53 | exampleRouter.InitCustomerRouter(PrivateGroup) // 客户路由 54 | 55 | } 56 | global.LOG.Info("router register success") 57 | return Router 58 | } 59 | -------------------------------------------------------------------------------- /initialize/timer.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "FiberBoot/config" 5 | "FiberBoot/global" 6 | "FiberBoot/utils" 7 | "fmt" 8 | ) 9 | 10 | func Timer() { 11 | if global.CONFIG.Timer.Start { 12 | for _, detail := range global.CONFIG.Timer.Detail { 13 | go func(detail config.Detail) { 14 | _, _ = global.Timer.AddTaskByFunc("ClearDB", global.CONFIG.Timer.Spec, func() { 15 | err := utils.ClearTable(global.DB, detail.TableName, detail.CompareField, detail.Interval) 16 | if err != nil { 17 | fmt.Println("timer error:", err) 18 | } 19 | }) 20 | }(detail) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /initialize/validator.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import "FiberBoot/utils" 4 | 5 | func init() { 6 | _ = utils.RegisterRule("PageVerify", 7 | utils.Rules{ 8 | "Page": {utils.NotEmpty()}, 9 | "PageSize": {utils.NotEmpty()}, 10 | }, 11 | ) 12 | _ = utils.RegisterRule("IdVerify", 13 | utils.Rules{ 14 | "Id": {utils.NotEmpty()}, 15 | }, 16 | ) 17 | _ = utils.RegisterRule("AuthorityIdVerify", 18 | utils.Rules{ 19 | "AuthorityId": {utils.NotEmpty()}, 20 | }, 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "FiberBoot/core" 5 | "FiberBoot/global" 6 | "FiberBoot/initialize" 7 | "database/sql" 8 | ) 9 | 10 | //go:generate go env -w GO111MODULE=on 11 | //go:generate go env -w GOPROXY=https://goproxy.cn,direct 12 | //go:generate go mod tidy 13 | //go:generate go mod download 14 | 15 | // @title Swagger Example API 16 | // @version 0.0.1 17 | // @description This is a sample Server pets 18 | // @securityDefinitions.apikey ApiKeyAuth 19 | // @in header 20 | // @name x-token 21 | // @BasePath / 22 | func main() { 23 | global.VP = core.Viper() // 初始化Viper 24 | global.LOG = core.Zap() // 初始化zap日志库 25 | global.DB = initialize.Gorm() // gorm连接数据库 26 | initialize.Timer() 27 | if global.DB != nil { 28 | initialize.MysqlTables(global.DB) // 初始化表 29 | // 程序结束前关闭数据库链接 30 | db, _ := global.DB.DB() 31 | defer func(db *sql.DB) { 32 | _ = db.Close() 33 | }(db) 34 | } 35 | core.RunServer() 36 | } 37 | -------------------------------------------------------------------------------- /middleware/casbin_rbac.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/common/response" 6 | "FiberBoot/model/system/request" 7 | "FiberBoot/service" 8 | "github.com/gofiber/fiber/v2" 9 | ) 10 | 11 | var casbinService = service.AppService.SystemService.CasbinService 12 | 13 | // CasbinHandler 拦截器 14 | func CasbinHandler() fiber.Handler { 15 | return func(c *fiber.Ctx) error { 16 | claims := c.Locals("claims") 17 | waitUse := claims.(*request.CustomClaims) 18 | // 获取请求的URI 19 | obj := c.OriginalURL() 20 | // 获取请求方法 21 | act := c.Method() 22 | // 获取用户的角色 23 | sub := waitUse.AuthorityId 24 | e := casbinService.Casbin() 25 | // 判断策略中是否存在 26 | success, _ := e.Enforce(sub, obj, act) 27 | if global.CONFIG.System.Env == "develop" || success { 28 | return c.Next() 29 | } else { 30 | return response.FailWithDetailed(fiber.Map{}, "权限不足", c) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /middleware/cors.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gofiber/fiber/v2" 5 | "github.com/gofiber/fiber/v2/middleware/cors" 6 | ) 7 | 8 | // Cors 处理跨域请求,支持options访问 9 | func Cors() fiber.Handler { 10 | return cors.New(cors.Config{ 11 | AllowMethods: "POST,GET,OPTIONS,DELETE,PUT", 12 | AllowHeaders: "Content-Type,AccessToken,X-CSRF-Token,Authorization,Token,X-Token,X-User-Id", 13 | AllowCredentials: true, 14 | ExposeHeaders: "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type", 15 | MaxAge: 0, 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /middleware/email.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/system" 6 | "FiberBoot/model/system/request" 7 | "FiberBoot/service" 8 | "FiberBoot/utils" 9 | "github.com/gofiber/fiber/v2" 10 | "go.uber.org/zap" 11 | "strconv" 12 | "sync" 13 | "time" 14 | ) 15 | 16 | var userService = service.AppService.SystemService.UserService 17 | 18 | func ErrorToEmail() fiber.Handler { 19 | return func(c *fiber.Ctx) error { 20 | var username string 21 | if claims := c.Locals("claims"); claims != nil { 22 | waitUse := claims.(*request.CustomClaims) 23 | username = waitUse.Username 24 | } else { 25 | id, _ := strconv.Atoi(c.Get("x-user-id")) 26 | err, user := userService.FindUserById(id) 27 | if err != nil { 28 | username = "Unknown" 29 | } 30 | username = user.Username 31 | } 32 | body := c.Request().Body() 33 | record := system.Operations{ 34 | Ip: c.IP(), 35 | Method: c.Method(), 36 | Path: c.OriginalURL(), 37 | Agent: c.Get("User-Agent"), 38 | Body: string(body), 39 | } 40 | now := time.Now() 41 | 42 | var ( 43 | once sync.Once 44 | errHandler fiber.ErrorHandler 45 | errPadding = 15 46 | ) 47 | 48 | // Set error handler once 49 | once.Do(func() { 50 | stack := c.App().Stack() 51 | for m := range stack { 52 | for r := range stack[m] { 53 | if len(stack[m][r].Path) > errPadding { 54 | errPadding = len(stack[m][r].Path) 55 | 56 | } 57 | } 58 | } 59 | // override error handler 60 | errHandler = c.App().Config().ErrorHandler 61 | }) 62 | 63 | chainErr := c.Next() 64 | if chainErr != nil { 65 | if err := errHandler(c, chainErr); err != nil { 66 | _ = c.SendStatus(fiber.StatusInternalServerError) 67 | } 68 | record.ErrorMessage = chainErr.Error() 69 | } 70 | 71 | latency := time.Now().Sub(now) 72 | status := c.Response().StatusCode() 73 | str := "接收到的请求为" + record.Body + "\n" + "请求方式为" + record.Method + "\n" + "报错信息如下" + record.ErrorMessage + "\n" + "耗时" + latency.String() + "\n" 74 | if status != 200 { 75 | subject := username + "" + record.Ip + "调用了" + record.Path + "报错了" 76 | if err := utils.ErrorToEmail(subject, str); err != nil { 77 | global.LOG.Error("ErrorToEmail Failed, err:", zap.Any("err", err)) 78 | } 79 | } 80 | return nil 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /middleware/jwt.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/common/response" 6 | "FiberBoot/model/system" 7 | "FiberBoot/model/system/request" 8 | "FiberBoot/service" 9 | "errors" 10 | "strconv" 11 | "time" 12 | 13 | "github.com/gofiber/fiber/v2" 14 | "github.com/golang-jwt/jwt/v4" 15 | "go.uber.org/zap" 16 | ) 17 | 18 | var jwtService = service.AppService.SystemService.JwtService 19 | 20 | func JWTAuth() fiber.Handler { 21 | return func(c *fiber.Ctx) error { 22 | // 我们这里jwt鉴权取头部信息 x-token 登录时回返回token信息 这里前端需要把token存储到cookie或者本地localStorage中 不过需要跟后端协商过期时间 可以约定刷新令牌或者重新登录 23 | token := c.Get("x-token") 24 | if token == "" { 25 | return response.FailWithDetailed(fiber.Map{"reload": true}, "未登录或非法访问", c) 26 | } 27 | if jwtService.IsBlacklist(token) { 28 | return response.FailWithDetailed(fiber.Map{"reload": true}, "您的帐户异地登陆或令牌失效", c) 29 | } 30 | j := NewJWT() 31 | // parseToken 解析token包含的信息 32 | claims, err := j.ParseToken(token) 33 | if err != nil { 34 | if err == TokenExpired { 35 | return response.FailWithDetailed(fiber.Map{"reload": true}, "授权已过期", c) 36 | 37 | } 38 | return response.FailWithDetailed(fiber.Map{"reload": true}, err.Error(), c) 39 | 40 | } 41 | if err, _ = userService.FindUserByUuid(claims.UUID.String()); err != nil { 42 | _ = jwtService.JsonInBlacklist(system.JwtBlacklist{Jwt: token}) 43 | return response.FailWithDetailed(fiber.Map{"reload": true}, err.Error(), c) 44 | } 45 | if claims.ExpiresAt-time.Now().Unix() < claims.BufferTime { 46 | claims.ExpiresAt = time.Now().Unix() + global.CONFIG.JWT.ExpiresTime 47 | newToken, _ := j.CreateTokenByOldToken(token, *claims) 48 | newClaims, _ := j.ParseToken(newToken) 49 | c.Set("new-token", newToken) 50 | c.Set("new-expires-at", strconv.FormatInt(newClaims.ExpiresAt, 10)) 51 | if global.CONFIG.System.UseMultipoint { 52 | err, RedisJwtToken := jwtService.GetRedisJWT(newClaims.Username) 53 | if err != nil { 54 | global.LOG.Error("get redis jwt failed", zap.Any("err", err)) 55 | } else { // 当之前的取成功时才进行拉黑操作 56 | _ = jwtService.JsonInBlacklist(system.JwtBlacklist{Jwt: RedisJwtToken}) 57 | } 58 | // 无论如何都要记录当前的活跃状态 59 | _ = jwtService.SetRedisJWT(newToken, newClaims.Username) 60 | } 61 | } 62 | c.Locals("claims", claims) 63 | return c.Next() 64 | } 65 | } 66 | 67 | type JWT struct { 68 | SigningKey []byte 69 | } 70 | 71 | var ( 72 | TokenExpired = errors.New("Token is expired ") 73 | TokenNotValidYet = errors.New("Token not active yet ") 74 | TokenMalformed = errors.New("That's not even a token ") 75 | TokenInvalid = errors.New("Couldn't handle this token: ") 76 | ) 77 | 78 | func NewJWT() *JWT { 79 | return &JWT{ 80 | []byte(global.CONFIG.JWT.SigningKey), 81 | } 82 | } 83 | 84 | // CreateToken 创建一个token 85 | func (j *JWT) CreateToken(claims request.CustomClaims) (string, error) { 86 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 87 | return token.SignedString(j.SigningKey) 88 | } 89 | 90 | // CreateTokenByOldToken 旧token 换新token 使用归并回源避免并发问题 91 | func (j *JWT) CreateTokenByOldToken(oldToken string, claims request.CustomClaims) (string, error) { 92 | v, err, _ := global.ConcurrencyControl.Do("JWT:"+oldToken, func() (interface{}, error) { 93 | return j.CreateToken(claims) 94 | }) 95 | return v.(string), err 96 | } 97 | 98 | // ParseToken 解析 token 99 | func (j *JWT) ParseToken(tokenString string) (*request.CustomClaims, error) { 100 | token, err := jwt.ParseWithClaims(tokenString, &request.CustomClaims{}, func(token *jwt.Token) (i interface{}, e error) { 101 | return j.SigningKey, nil 102 | }) 103 | if err != nil { 104 | if ve, ok := err.(*jwt.ValidationError); ok { 105 | if ve.Errors&jwt.ValidationErrorMalformed != 0 { 106 | return nil, TokenMalformed 107 | } else if ve.Errors&jwt.ValidationErrorExpired != 0 { 108 | // Token is expired 109 | return nil, TokenExpired 110 | } else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 { 111 | return nil, TokenNotValidYet 112 | } else { 113 | return nil, TokenInvalid 114 | } 115 | } 116 | } 117 | if token != nil { 118 | if claims, ok := token.Claims.(*request.CustomClaims); ok && token.Valid { 119 | return claims, nil 120 | } 121 | return nil, TokenInvalid 122 | 123 | } else { 124 | return nil, TokenInvalid 125 | 126 | } 127 | 128 | } 129 | 130 | // 更新token 131 | //func (j *JWT) RefreshToken(tokenString string) (string, error) { 132 | // jwt.TimeFunc = func() time.Time { 133 | // return time.Unix(0, 0) 134 | // } 135 | // token, err := jwt.ParseWithClaims(tokenString, &request.CustomClaims{}, func(token *jwt.Token) (interface{}, error) { 136 | // return j.SigningKey, nil 137 | // }) 138 | // if err != nil { 139 | // return "", err 140 | // } 141 | // if claims, ok := token.Claims.(*request.CustomClaims); ok && token.Valid { 142 | // jwt.TimeFunc = time.Now 143 | // claims.StandardClaims.ExpiresAt = time.Now().Unix() + 60*60*24*7 144 | // return j.CreateToken(*claims) 145 | // } 146 | // return "", TokenInvalid 147 | //} 148 | -------------------------------------------------------------------------------- /middleware/logger.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gofiber/fiber/v2" 5 | "github.com/gofiber/fiber/v2/middleware/logger" 6 | ) 7 | 8 | func Logger() fiber.Handler { 9 | config := logger.ConfigDefault 10 | config.Format = "${time} ${status} - ${latency} ${method} ${path}" 11 | config.TimeFormat = "2006/01/02 - 15:04:05" 12 | return logger.New(config) 13 | 14 | } 15 | -------------------------------------------------------------------------------- /middleware/need_init.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/common/response" 6 | "github.com/gofiber/fiber/v2" 7 | ) 8 | 9 | // NeedInit 处理跨域请求,支持options访问 10 | func NeedInit() fiber.Handler { 11 | return func(c *fiber.Ctx) error { 12 | if global.DB == nil { 13 | return response.OkWithDetailed(fiber.Map{ 14 | "needInit": true, 15 | }, "前往初始化数据库", c) 16 | } else { 17 | return c.Next() 18 | } 19 | // 处理请求 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /middleware/operation.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/system" 6 | "FiberBoot/model/system/request" 7 | "FiberBoot/service" 8 | "github.com/gofiber/fiber/v2" 9 | "go.uber.org/zap" 10 | "strconv" 11 | "sync" 12 | "time" 13 | ) 14 | 15 | var OperationsService = service.AppService.SystemService.OperationsService 16 | 17 | func Operations() fiber.Handler { 18 | return func(c *fiber.Ctx) error { 19 | var body []byte 20 | var userId int 21 | if c.Method() != fiber.MethodGet { 22 | 23 | body = c.Request().Body() 24 | } 25 | if claims := c.Locals("claims"); claims != nil { 26 | waitUse := claims.(*request.CustomClaims) 27 | userId = int(waitUse.ID) 28 | } else { 29 | id, err := strconv.Atoi(c.Get("x-user-id")) 30 | if err != nil { 31 | userId = 0 32 | } 33 | userId = id 34 | } 35 | record := system.Operations{ 36 | Ip: c.IP(), 37 | Method: c.Method(), 38 | Path: c.OriginalURL(), 39 | Agent: c.Get("User-Agent"), 40 | Body: string(body), 41 | UserID: userId, 42 | } 43 | now := time.Now() 44 | var ( 45 | once sync.Once 46 | errHandler fiber.ErrorHandler 47 | errPadding = 15 48 | ) 49 | 50 | // Set error handler once 51 | once.Do(func() { 52 | stack := c.App().Stack() 53 | for m := range stack { 54 | for r := range stack[m] { 55 | if len(stack[m][r].Path) > errPadding { 56 | errPadding = len(stack[m][r].Path) 57 | 58 | } 59 | } 60 | } 61 | // override error handler 62 | errHandler = c.App().Config().ErrorHandler 63 | }) 64 | 65 | chainErr := c.Next() 66 | if chainErr != nil { 67 | if err := errHandler(c, chainErr); err != nil { 68 | _ = c.SendStatus(fiber.StatusInternalServerError) 69 | } 70 | record.ErrorMessage = chainErr.Error() 71 | } 72 | 73 | latency := time.Now().Sub(now) 74 | record.Status = c.Response().StatusCode() 75 | record.Latency = latency 76 | record.Resp = string(c.Response().Body()) 77 | if err := OperationsService.CreateOperations(record); err != nil { 78 | global.LOG.Error("create operation record error:", zap.Any("err", err)) 79 | } 80 | return nil 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /middleware/recover.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gofiber/fiber/v2" 5 | "github.com/gofiber/fiber/v2/middleware/recover" 6 | ) 7 | 8 | func Recover() fiber.Handler { 9 | return recover.New(recover.Config{ 10 | EnableStackTrace: true, 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /model/common/request/common.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | // PageInfo Paging common input parameter structure 4 | type PageInfo struct { 5 | Page int `json:"page" form:"page"` // 页码 6 | PageSize int `json:"pageSize" form:"pageSize"` // 每页大小 7 | } 8 | 9 | // GetById Find by id structure 10 | type GetById struct { 11 | ID float64 `json:"id" form:"id"` // 主键ID 12 | } 13 | 14 | type IdsReq struct { 15 | Ids []int `json:"ids" form:"ids"` 16 | } 17 | 18 | // GetAuthorityId Get role by id structure 19 | type GetAuthorityId struct { 20 | AuthorityId string // 角色ID 21 | } 22 | 23 | type Empty struct{} 24 | -------------------------------------------------------------------------------- /model/common/response/common.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | type PageResult struct { 4 | List interface{} `json:"list"` 5 | Total int64 `json:"total"` 6 | Page int `json:"page"` 7 | PageSize int `json:"pageSize"` 8 | } 9 | -------------------------------------------------------------------------------- /model/common/response/response.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import ( 4 | "github.com/gofiber/fiber/v2" 5 | ) 6 | 7 | type Response struct { 8 | Code int `json:"code"` 9 | Data interface{} `json:"data"` 10 | Msg string `json:"msg"` 11 | } 12 | 13 | const ( 14 | ERROR = 7 15 | SUCCESS = 0 16 | ) 17 | 18 | func Result(code int, data interface{}, msg string, c *fiber.Ctx) error { 19 | // 开始时间 20 | return c.JSON(Response{ 21 | code, 22 | data, 23 | msg, 24 | }) 25 | } 26 | 27 | func Ok(c *fiber.Ctx) error { 28 | return Result(SUCCESS, map[string]interface{}{}, "操作成功", c) 29 | } 30 | 31 | func OkWithMessage(message string, c *fiber.Ctx) error { 32 | return Result(SUCCESS, map[string]interface{}{}, message, c) 33 | } 34 | 35 | func OkWithData(data interface{}, c *fiber.Ctx) error { 36 | return Result(SUCCESS, data, "操作成功", c) 37 | } 38 | 39 | func OkWithDetailed(data interface{}, message string, c *fiber.Ctx) error { 40 | return Result(SUCCESS, data, message, c) 41 | } 42 | 43 | func Fail(c *fiber.Ctx) error { 44 | return Result(ERROR, map[string]interface{}{}, "操作失败", c) 45 | } 46 | 47 | func FailWithMessage(message string, c *fiber.Ctx) error { 48 | return Result(ERROR, map[string]interface{}{}, message, c) 49 | } 50 | 51 | func FailWithDetailed(data interface{}, message string, c *fiber.Ctx) error { 52 | return Result(ERROR, data, message, c) 53 | } 54 | -------------------------------------------------------------------------------- /model/example/advanceUpload.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "FiberBoot/global" 5 | ) 6 | 7 | // File struct, 文件结构体 8 | type File struct { 9 | global.MODEL 10 | FileName string 11 | FileMd5 string 12 | FilePath string 13 | ExaFileChunk []FileChunk 14 | ChunkTotal int 15 | IsFinish bool 16 | } 17 | 18 | // FileChunk file chunk struct, 切片结构体 19 | type FileChunk struct { 20 | global.MODEL 21 | FileID uint 22 | FileChunkNumber int 23 | FileChunkPath string 24 | } 25 | -------------------------------------------------------------------------------- /model/example/customer.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/system" 6 | ) 7 | 8 | type Customer struct { 9 | global.MODEL 10 | CustomerName string `json:"customerName" form:"customerName" gorm:"comment:客户名"` // 客户名 11 | CustomerPhoneData string `json:"customerPhoneData" form:"customerPhoneData" gorm:"comment:客户手机号"` // 客户手机号 12 | UserID uint `json:"userId" form:"userId" gorm:"comment:管理ID"` // 管理ID 13 | UserAuthorityID string `json:"userAuthorityID" form:"userAuthorityID" gorm:"comment:管理角色ID"` // 管理角色ID 14 | User system.User `json:"user" form:"user" gorm:"comment:管理详情"` // 管理详情 15 | } 16 | -------------------------------------------------------------------------------- /model/example/excel.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import "FiberBoot/model/system" 4 | 5 | type ExcelInfo struct { 6 | FileName string `json:"fileName"` // 文件名 7 | InfoList []system.BaseMenu `json:"infoList"` 8 | } 9 | -------------------------------------------------------------------------------- /model/example/fileTransfer.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "FiberBoot/global" 5 | ) 6 | 7 | type FileTransfer struct { 8 | global.MODEL 9 | Name string `json:"name" gorm:"comment:文件名"` // 文件名 10 | Url string `json:"url" gorm:"comment:文件地址"` // 文件地址 11 | Tag string `json:"tag" gorm:"comment:文件标签"` // 文件标签 12 | Key string `json:"key" gorm:"comment:编号"` // 编号 13 | } 14 | -------------------------------------------------------------------------------- /model/example/response/advanceUpload.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import "FiberBoot/model/example" 4 | 5 | type FilePathResponse struct { 6 | FilePath string `json:"filePath"` 7 | } 8 | 9 | type FileResponse struct { 10 | File example.File `json:"file"` 11 | } 12 | -------------------------------------------------------------------------------- /model/example/response/customer.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import "FiberBoot/model/example" 4 | 5 | type CustomerResponse struct { 6 | Customer example.Customer `json:"customer"` 7 | } 8 | -------------------------------------------------------------------------------- /model/example/response/fileTransfer.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import "FiberBoot/model/example" 4 | 5 | type ExaFileResponse struct { 6 | File example.FileTransfer `json:"file"` 7 | } 8 | -------------------------------------------------------------------------------- /model/example/simpleUpload.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | type SimpleUploader struct { 4 | ChunkNumber string `json:"chunkNumber" gorm:"comment:当前切片标记"` 5 | CurrentChunkSize string `json:"currentChunkSize" gorm:"comment:当前切片容量"` 6 | CurrentChunkPath string `json:"currentChunkPath" gorm:"comment:切片本地路径"` 7 | TotalSize string `json:"totalSize" gorm:"comment:总容量"` 8 | Identifier string `json:"identifier" gorm:"comment:文件标识(md5)"` 9 | Filename string `json:"filename" gorm:"comment:文件名"` 10 | TotalChunks string `json:"totalChunks" gorm:"comment:切片总数"` 11 | IsDone bool `json:"isDone" gorm:"comment:是否上传完成"` 12 | FilePath string `json:"filePath" gorm:"comment:文件本地路径"` 13 | } 14 | -------------------------------------------------------------------------------- /model/system/api.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/global" 5 | ) 6 | 7 | type Api struct { 8 | global.MODEL 9 | Path string `json:"path" gorm:"comment:api路径"` // api路径 10 | Description string `json:"description" gorm:"comment:api中文描述"` // api中文描述 11 | ApiGroup string `json:"apiGroup" gorm:"comment:api组"` // api组 12 | Method string `json:"method" gorm:"default:POST;comment:方法"` // 方法:创建POST(默认)|查看GET|更新PUT|删除DELETE 13 | } 14 | -------------------------------------------------------------------------------- /model/system/authority.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type Authority struct { 8 | CreatedAt time.Time // 创建时间 9 | UpdatedAt time.Time // 更新时间 10 | DeletedAt *time.Time `sql:"index"` 11 | AuthorityId string `json:"authorityId" gorm:"not null;unique;primary_key;comment:角色ID;size:90"` // 角色ID 12 | AuthorityName string `json:"authorityName" gorm:"comment:角色名"` // 角色名 13 | ParentId string `json:"parentId" gorm:"comment:父角色ID"` // 父角色ID 14 | DataAuthorityId []Authority `json:"dataAuthorityId" gorm:"many2many:data_authority_id"` 15 | Children []Authority `json:"children" gorm:"-"` 16 | SysBaseMenus []BaseMenu `json:"menus" gorm:"many2many:authority_menus;"` 17 | DefaultRouter string `json:"defaultRouter" gorm:"comment:默认菜单;default:dashboard"` // 默认菜单(默认dashboard) 18 | } 19 | -------------------------------------------------------------------------------- /model/system/authorityMenu.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | type Menu struct { 4 | BaseMenu 5 | MenuId string `json:"menuId" gorm:"comment:菜单ID"` 6 | AuthorityId string `json:"-" gorm:"comment:角色ID"` 7 | Children []Menu `json:"children" gorm:"-"` 8 | Parameters []BaseMenuParameter `json:"parameters" gorm:"foreignKey:BaseMenuID;references:MenuId"` 9 | } 10 | 11 | func (s Menu) TableName() string { 12 | return "authority_menu" 13 | } 14 | -------------------------------------------------------------------------------- /model/system/baseMenu.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/global" 5 | ) 6 | 7 | type BaseMenu struct { 8 | global.MODEL 9 | MenuLevel uint `json:"-"` 10 | ParentId string `json:"parentId" gorm:"comment:父菜单ID"` // 父菜单ID 11 | Path string `json:"path" gorm:"comment:路由path"` // 路由path 12 | Name string `json:"name" gorm:"comment:路由name"` // 路由name 13 | Hidden bool `json:"hidden" gorm:"comment:是否在列表隐藏"` // 是否在列表隐藏 14 | Component string `json:"component" gorm:"comment:对应前端文件路径"` // 对应前端文件路径 15 | Sort int `json:"sort" gorm:"comment:排序标记"` // 排序标记 16 | Meta `json:"meta" gorm:"comment:附加属性"` // 附加属性 17 | SysAuthorities []Authority `json:"authorities" gorm:"many2many:authority_menus;"` 18 | Children []BaseMenu `json:"children" gorm:"-"` 19 | Parameters []BaseMenuParameter `json:"parameters"` 20 | } 21 | 22 | type Meta struct { 23 | KeepAlive bool `json:"keepAlive" gorm:"comment:是否缓存"` // 是否缓存 24 | DefaultMenu bool `json:"defaultMenu" gorm:"comment:是否是基础路由(开发中)"` // 是否是基础路由(开发中) 25 | Title string `json:"title" gorm:"comment:菜单名"` // 菜单名 26 | Icon string `json:"icon" gorm:"comment:菜单图标"` // 菜单图标 27 | CloseTab bool `json:"closeTab" gorm:"comment:自动关闭tab"` // 自动关闭tab 28 | } 29 | 30 | type BaseMenuParameter struct { 31 | global.MODEL 32 | BaseMenuID uint 33 | Type string `json:"type" gorm:"comment:地址栏携带参数为params还是query"` // 地址栏携带参数为params还是query 34 | Key string `json:"key" gorm:"comment:地址栏携带参数的key"` // 地址栏携带参数的key 35 | Value string `json:"value" gorm:"comment:地址栏携带参数的值"` // 地址栏携带参数的值 36 | } 37 | -------------------------------------------------------------------------------- /model/system/casbin.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | type CasbinModel struct { 4 | Ptype string `json:"ptype" gorm:"column:ptype"` 5 | AuthorityId string `json:"roleName" gorm:"column:v0"` 6 | Path string `json:"path" gorm:"column:v1"` 7 | Method string `json:"method" gorm:"column:v2"` 8 | } 9 | -------------------------------------------------------------------------------- /model/system/initDB.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | type InitDBFunc interface { 4 | Init() (err error) 5 | } 6 | -------------------------------------------------------------------------------- /model/system/jwt.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/global" 5 | ) 6 | 7 | type JwtBlacklist struct { 8 | global.MODEL 9 | Jwt string `gorm:"type:text;comment:jwt"` 10 | } 11 | -------------------------------------------------------------------------------- /model/system/operations.go: -------------------------------------------------------------------------------- 1 | // Package system 自动生成模板Operations 2 | package system 3 | 4 | import ( 5 | "FiberBoot/global" 6 | "time" 7 | ) 8 | 9 | // Operations 如果含有time.Time 请自行import time包 10 | type Operations struct { 11 | global.MODEL 12 | Ip string `json:"ip" form:"ip" gorm:"column:ip;comment:请求ip"` // 请求ip 13 | Method string `json:"method" form:"method" gorm:"column:method;comment:请求方法"` // 请求方法 14 | Path string `json:"path" form:"path" gorm:"column:path;comment:请求路径"` // 请求路径 15 | Status int `json:"status" form:"status" gorm:"column:status;comment:请求状态"` // 请求状态 16 | Latency time.Duration `json:"latency" form:"latency" gorm:"column:latency;comment:延迟" swaggertype:"string"` // 延迟 17 | Agent string `json:"agent" form:"agent" gorm:"column:agent;comment:代理"` // 代理 18 | ErrorMessage string `json:"error_message" form:"error_message" gorm:"column:error_message;comment:错误信息"` // 错误信息 19 | Body string `json:"body" form:"body" gorm:"type:longtext;column:body;comment:请求Body"` // 请求Body 20 | Resp string `json:"resp" form:"resp" gorm:"type:longtext;column:resp;comment:响应Body"` // 响应Body 21 | UserID int `json:"user_id" form:"user_id" gorm:"column:user_id;comment:用户id"` // 用户id 22 | User User `json:"user"` 23 | } 24 | -------------------------------------------------------------------------------- /model/system/request/api.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | import ( 4 | "FiberBoot/model/common/request" 5 | "FiberBoot/model/system" 6 | ) 7 | 8 | // SearchApiParams api分页条件查询及排序结构体 9 | type SearchApiParams struct { 10 | system.Api 11 | request.PageInfo 12 | OrderKey string `json:"orderKey"` // 排序 13 | Desc bool `json:"desc"` // 排序方式:升序false(默认)|降序true 14 | } 15 | -------------------------------------------------------------------------------- /model/system/request/casbin.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | // CasbinInfo Casbin info structure 4 | type CasbinInfo struct { 5 | Path string `json:"path"` // 路径 6 | Method string `json:"method"` // 方法 7 | } 8 | 9 | // CasbinInReceive Casbin structure for input parameters 10 | type CasbinInReceive struct { 11 | AuthorityId string `json:"authorityId"` // 权限id 12 | CasbinInfos []CasbinInfo `json:"casbinInfos"` 13 | } 14 | 15 | func DefaultCasbin() []CasbinInfo { 16 | return []CasbinInfo{ 17 | {Path: "/menu/getMenu", Method: "POST"}, 18 | {Path: "/jwt/jsonInBlacklist", Method: "POST"}, 19 | {Path: "/base/login", Method: "POST"}, 20 | {Path: "/user/register", Method: "POST"}, 21 | {Path: "/user/changePassword", Method: "POST"}, 22 | {Path: "/user/setUserAuthority", Method: "POST"}, 23 | {Path: "/user/setUserInfo", Method: "PUT"}, 24 | {Path: "/user/getUserInfo", Method: "GET"}, 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /model/system/request/init.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | type InitDB struct { 4 | Host string `json:"host"` // 服务器地址 5 | Port string `json:"port"` // 数据库连接端口 6 | UserName string `json:"userName" binding:"required"` // 数据库用户名 7 | Password string `json:"password"` // 数据库密码 8 | DBName string `json:"dbName" binding:"required"` // 数据库名 9 | } 10 | -------------------------------------------------------------------------------- /model/system/request/jwt.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | import ( 4 | "github.com/golang-jwt/jwt/v4" 5 | uuid "github.com/satori/go.uuid" 6 | ) 7 | 8 | // CustomClaims Custom claims structure 9 | type CustomClaims struct { 10 | UUID uuid.UUID 11 | ID uint 12 | Username string 13 | NickName string 14 | AuthorityId string 15 | BufferTime int64 16 | jwt.StandardClaims 17 | } 18 | -------------------------------------------------------------------------------- /model/system/request/menu.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/system" 6 | ) 7 | 8 | // AddMenuAuthorityInfo Add menu authority info structure 9 | type AddMenuAuthorityInfo struct { 10 | Menus []system.BaseMenu 11 | AuthorityId string // 角色ID 12 | } 13 | 14 | func DefaultMenu() []system.BaseMenu { 15 | return []system.BaseMenu{{ 16 | MODEL: global.MODEL{ID: 1}, 17 | ParentId: "0", 18 | Path: "dashboard", 19 | Name: "dashboard", 20 | Component: "view/dashboard/index.vue", 21 | Sort: 1, 22 | Meta: system.Meta{ 23 | Title: "仪表盘", 24 | Icon: "setting", 25 | }, 26 | }} 27 | } 28 | -------------------------------------------------------------------------------- /model/system/request/operations.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | import ( 4 | "FiberBoot/model/common/request" 5 | "FiberBoot/model/system" 6 | ) 7 | 8 | type OperationsSearch struct { 9 | system.Operations 10 | request.PageInfo 11 | } 12 | -------------------------------------------------------------------------------- /model/system/request/user.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | // Register User register structure 4 | type Register struct { 5 | Username string `json:"userName"` 6 | Password string `json:"passWord"` 7 | NickName string `json:"nickName" gorm:"default:'管理员'"` 8 | Avatar string `json:"avatar" gorm:""` 9 | AuthorityId string `json:"authorityId" gorm:"default:888"` 10 | AuthorityIds []string `json:"authorityIds"` 11 | } 12 | 13 | // Login User login structure 14 | type Login struct { 15 | Username string `json:"username"` // 用户名 16 | Password string `json:"password"` // 密码 17 | Captcha string `json:"captcha"` // 验证码 18 | CaptchaId string `json:"captchaId"` // 验证码ID 19 | } 20 | 21 | // ChangePasswordStruct Modify password structure 22 | type ChangePasswordStruct struct { 23 | Username string `json:"username"` // 用户名 24 | Password string `json:"password"` // 密码 25 | NewPassword string `json:"newPassword"` // 新密码 26 | } 27 | 28 | // SetUserAuth Modify user's auth structure 29 | type SetUserAuth struct { 30 | AuthorityId string `json:"authorityId"` // 角色ID 31 | } 32 | 33 | // SetUserAuthorities Modify user's auth structure 34 | type SetUserAuthorities struct { 35 | ID uint 36 | AuthorityIds []string `json:"authorityIds"` // 角色ID 37 | } 38 | -------------------------------------------------------------------------------- /model/system/response/api.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import "FiberBoot/model/system" 4 | 5 | type SysAPIResponse struct { 6 | Api system.Api `json:"api"` 7 | } 8 | 9 | type SysAPIListResponse struct { 10 | Apis []system.Api `json:"apis"` 11 | } 12 | -------------------------------------------------------------------------------- /model/system/response/authority.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import "FiberBoot/model/system" 4 | 5 | type SysAuthorityResponse struct { 6 | Authority system.Authority `json:"authority"` 7 | } 8 | 9 | type SysAuthorityCopyResponse struct { 10 | Authority system.Authority `json:"authority"` 11 | OldAuthorityId string `json:"oldAuthorityId"` // 旧角色ID 12 | } 13 | -------------------------------------------------------------------------------- /model/system/response/captcha.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | type SysCaptchaResponse struct { 4 | CaptchaId string `json:"captchaId"` 5 | PicPath string `json:"picPath"` 6 | } 7 | -------------------------------------------------------------------------------- /model/system/response/casbin.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import ( 4 | "FiberBoot/model/system/request" 5 | ) 6 | 7 | type PolicyPathResponse struct { 8 | Paths []request.CasbinInfo `json:"paths"` 9 | } 10 | -------------------------------------------------------------------------------- /model/system/response/menu.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import "FiberBoot/model/system" 4 | 5 | type SysMenusResponse struct { 6 | Menus []system.Menu `json:"menus"` 7 | } 8 | 9 | type SysBaseMenusResponse struct { 10 | Menus []system.BaseMenu `json:"menus"` 11 | } 12 | 13 | type SysBaseMenuResponse struct { 14 | Menu system.BaseMenu `json:"menu"` 15 | } 16 | -------------------------------------------------------------------------------- /model/system/response/system.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import "FiberBoot/config" 4 | 5 | type SysConfigResponse struct { 6 | Config config.Server `json:"config"` 7 | } 8 | -------------------------------------------------------------------------------- /model/system/response/user.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import ( 4 | "FiberBoot/model/system" 5 | ) 6 | 7 | type UserResponse struct { 8 | User system.User `json:"user"` 9 | } 10 | 11 | type LoginResponse struct { 12 | User system.User `json:"user"` 13 | Token string `json:"token"` 14 | ExpiresAt int64 `json:"expiresAt"` 15 | } 16 | -------------------------------------------------------------------------------- /model/system/system.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/config" 5 | ) 6 | 7 | // System 配置文件结构体 8 | type System struct { 9 | Config config.Server 10 | } 11 | -------------------------------------------------------------------------------- /model/system/user.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "github.com/satori/go.uuid" 6 | ) 7 | 8 | type User struct { 9 | global.MODEL 10 | UUID uuid.UUID `json:"uuid" gorm:"comment:用户UUID"` // 用户UUID 11 | Username string `json:"userName" gorm:"comment:用户登录名"` // 用户登录名 12 | Password string `json:"-" gorm:"comment:用户登录密码"` // 用户登录密码 13 | NickName string `json:"nickName" gorm:"default:系统用户;comment:用户昵称"` // 用户昵称 14 | Avatar string `json:"avatar" gorm:"comment:用户头像"` // 用户头像 15 | Authority Authority `json:"authority" gorm:"foreignKey:AuthorityId;references:AuthorityId;comment:用户角色"` 16 | AuthorityId string `json:"authorityId" gorm:"default:888;comment:用户角色ID"` // 用户角色ID 17 | SideMode string `json:"sideMode" gorm:"default:dark;comment:用户角色ID"` // 用户侧边主题 18 | ActiveColor string `json:"activeColor" gorm:"default:#1890ff;comment:用户角色ID"` // 活跃颜色 19 | BaseColor string `json:"baseColor" gorm:"default:#fff;comment:用户角色ID"` // 基础颜色 20 | Authorities []Authority `json:"authorities" gorm:"many2many:user_authority;"` 21 | } 22 | -------------------------------------------------------------------------------- /model/system/userAuthority.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | type UserAuthority struct { 4 | UserId uint `gorm:"column:user_id"` 5 | AuthorityAuthorityId string `gorm:"column:authority_authority_id"` 6 | } 7 | 8 | func (s *UserAuthority) TableName() string { 9 | return "user_authority" 10 | } 11 | -------------------------------------------------------------------------------- /packfile/notUsePackFile.go: -------------------------------------------------------------------------------- 1 | // +build !packfile 2 | 3 | package packfile 4 | -------------------------------------------------------------------------------- /packfile/usePackFile.go: -------------------------------------------------------------------------------- 1 | // +build packfile 2 | 3 | package packfile 4 | 5 | import ( 6 | "fmt" 7 | "io/ioutil" 8 | "os" 9 | "path/filepath" 10 | "strings" 11 | ) 12 | 13 | //go:generate go-bindata -o=staticFile.go -pkg=packfile -tags=packfile ../resource/... ../config.yaml 14 | 15 | func writeFile(path string, data []byte) { 16 | // 如果文件夹不存在,预先创建文件夹 17 | if lastSeparator := strings.LastIndex(path, "/"); lastSeparator != -1 { 18 | dirPath := path[:lastSeparator] 19 | if _, err := os.Stat(dirPath); err != nil && os.IsNotExist(err) { 20 | os.MkdirAll(dirPath, os.ModePerm) 21 | } 22 | } 23 | 24 | // 已存在的文件,不应该覆盖重写,可能在前端更改了配置文件等 25 | if _, err := os.Stat(path); os.IsNotExist(err) { 26 | if err2 := ioutil.WriteFile(path, data, os.ModePerm); err2 != nil { 27 | fmt.Printf("Write file failed: %s\n", path) 28 | } 29 | } else { 30 | fmt.Printf("File exist, skip: %s\n", path) 31 | } 32 | } 33 | 34 | func init() { 35 | for key := range _bindata { 36 | filePath, _ := filepath.Abs(strings.TrimPrefix(key, ".")) 37 | data, err := Asset(key) 38 | if err != nil { 39 | // Asset was not found. 40 | fmt.Printf("Fail to find: %s\n", filePath) 41 | } else { 42 | writeFile(filePath, data) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /resource/excel/ExcelExport.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlameMida/FiberBoot/9daeeb9630603efa01da7c37b4b62d9b4eacde3a/resource/excel/ExcelExport.xlsx -------------------------------------------------------------------------------- /resource/excel/ExcelImport.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlameMida/FiberBoot/9daeeb9630603efa01da7c37b4b62d9b4eacde3a/resource/excel/ExcelImport.xlsx -------------------------------------------------------------------------------- /resource/excel/ExcelTemplate.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlameMida/FiberBoot/9daeeb9630603efa01da7c37b4b62d9b4eacde3a/resource/excel/ExcelTemplate.xlsx -------------------------------------------------------------------------------- /resource/rbac_model.conf: -------------------------------------------------------------------------------- 1 | [request_definition] 2 | r = sub, obj, act 3 | 4 | [policy_definition] 5 | p = sub, obj, act 6 | 7 | [role_definition] 8 | g = _, _ 9 | 10 | [policy_effect] 11 | e = some(where (p.eft == allow)) 12 | 13 | [matchers] 14 | m = r.sub == p.sub && ParamsMatch(r.obj,p.obj) && r.act == p.act 15 | -------------------------------------------------------------------------------- /router/enter.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "FiberBoot/router/example" 5 | "FiberBoot/router/system" 6 | ) 7 | 8 | type Router struct { 9 | System system.RouterGroup 10 | Example example.RouterGroup 11 | } 12 | 13 | var AppRouter = new(Router) 14 | -------------------------------------------------------------------------------- /router/example/customer.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | v1 "FiberBoot/api/v1" 5 | "FiberBoot/middleware" 6 | "github.com/gofiber/fiber/v2" 7 | ) 8 | 9 | type CustomerRouter struct { 10 | } 11 | 12 | func (e *CustomerRouter) InitCustomerRouter(Router fiber.Router) { 13 | customerRouter := Router.Group("customer").Use(middleware.Operations()) 14 | var customerApi = v1.AppApi.ExampleApi.CustomerApi 15 | { 16 | customerRouter.Post("customer", customerApi.CreateCustomer) // 创建客户 17 | customerRouter.Put("customer", customerApi.UpdateCustomer) // 更新客户 18 | customerRouter.Delete("customer", customerApi.DeleteCustomer) // 删除客户 19 | customerRouter.Get("customer", customerApi.GetCustomer) // 获取单一客户信息 20 | customerRouter.Get("customerList", customerApi.GetCustomerList) // 获取客户列表 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /router/example/enter.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | type RouterGroup struct { 4 | CustomerRouter 5 | ExcelRouter 6 | FileTransferRouter 7 | SimpleUploaderRouter 8 | } 9 | -------------------------------------------------------------------------------- /router/example/excel.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "FiberBoot/api/v1" 5 | "github.com/gofiber/fiber/v2" 6 | ) 7 | 8 | type ExcelRouter struct { 9 | } 10 | 11 | func (e *ExcelRouter) InitExcelRouter(Router fiber.Router) { 12 | excelRouter := Router.Group("excel") 13 | var exaExcelApi = v1.AppApi.ExampleApi.ExcelApi 14 | { 15 | excelRouter.Post("/importExcel", exaExcelApi.ImportExcel) // 导入Excel 16 | excelRouter.Get("/loadExcel", exaExcelApi.LoadExcel) // 加载Excel数据 17 | excelRouter.Post("/exportExcel", exaExcelApi.ExportExcel) // 导出Excel 18 | excelRouter.Get("/downloadTemplate", exaExcelApi.DownloadTemplate) // 下载模板文件 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /router/example/fileTransfer.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "FiberBoot/api/v1" 5 | "github.com/gofiber/fiber/v2" 6 | ) 7 | 8 | type FileTransferRouter struct { 9 | } 10 | 11 | func (e *FileTransferRouter) InitFileTransferRouter(Router fiber.Router) { 12 | fileTransferRouter := Router.Group("fileTransfer") 13 | var exaFileTransferApi = v1.AppApi.ExampleApi.FileTransferApi 14 | { 15 | fileTransferRouter.Post("/upload", exaFileTransferApi.UploadFile) // 上传文件 16 | fileTransferRouter.Post("/getFileList", exaFileTransferApi.GetFileList) // 获取上传文件列表 17 | fileTransferRouter.Post("/deleteFile", exaFileTransferApi.DeleteFile) // 删除指定文件 18 | fileTransferRouter.Post("/breakpointContinue", exaFileTransferApi.BreakpointContinue) // 断点续传 19 | fileTransferRouter.Get("/findFile", exaFileTransferApi.FindFile) // 查询当前文件成功的切片 20 | fileTransferRouter.Post("/breakpointContinueFinish", exaFileTransferApi.BreakpointContinueFinish) // 查询当前文件成功的切片 21 | fileTransferRouter.Post("/removeChunk", exaFileTransferApi.RemoveChunk) // 查询当前文件成功的切片 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /router/example/simpleUpload.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "FiberBoot/api/v1" 5 | "github.com/gofiber/fiber/v2" 6 | ) 7 | 8 | type SimpleUploaderRouter struct{} 9 | 10 | func (e *SimpleUploaderRouter) InitSimpleUploaderRouter(Router fiber.Router) { 11 | simpleUploadRouter := Router.Group("simpleUpload") 12 | var simpleUploadApi = v1.AppApi.ExampleApi.SimpleUploadApi 13 | { 14 | simpleUploadRouter.Post("upload", simpleUploadApi.SimpleUpload) // 上传功能 15 | simpleUploadRouter.Get("checkFileMd5", simpleUploadApi.CheckFileMd5) // 文件完整度验证 16 | simpleUploadRouter.Get("mergeFileMd5", simpleUploadApi.MergeFileMd5) // 合并文件 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /router/system/api.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/api/v1" 5 | "FiberBoot/middleware" 6 | "github.com/gofiber/fiber/v2" 7 | ) 8 | 9 | type ApiRouter struct { 10 | } 11 | 12 | func (s *ApiRouter) InitApiRouter(Router fiber.Router) { 13 | apiRouter := Router.Group("api").Use(middleware.Operations()) 14 | var API = v1.AppApi.SystemApi.Api 15 | { 16 | apiRouter.Post("createApi", API.CreateApi) // 创建Api 17 | apiRouter.Post("deleteApi", API.DeleteApi) // 删除Api 18 | apiRouter.Post("getApiList", API.GetApiList) // 获取Api列表 19 | apiRouter.Post("getApiById", API.GetApiById) // 获取单条Api消息 20 | apiRouter.Post("updateApi", API.UpdateApi) // 更新api 21 | apiRouter.Post("getAllApis", API.GetAllApis) // 获取所有api 22 | apiRouter.Delete("deleteApisByIds", API.DeleteApisByIds) // 删除选中api 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /router/system/authority.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/api/v1" 5 | "FiberBoot/middleware" 6 | "github.com/gofiber/fiber/v2" 7 | ) 8 | 9 | type AuthorityRouter struct { 10 | } 11 | 12 | func (s *AuthorityRouter) InitAuthorityRouter(Router fiber.Router) { 13 | authorityRouter := Router.Group("authority").Use(middleware.Operations()) 14 | var authorityApi = v1.AppApi.SystemApi.Authority 15 | { 16 | authorityRouter.Post("createAuthority", authorityApi.CreateAuthority) // 创建角色 17 | authorityRouter.Post("deleteAuthority", authorityApi.DeleteAuthority) // 删除角色 18 | authorityRouter.Put("updateAuthority", authorityApi.UpdateAuthority) // 更新角色 19 | authorityRouter.Post("copyAuthority", authorityApi.CopyAuthority) // 更新角色 20 | authorityRouter.Post("getAuthorityList", authorityApi.GetAuthorityList) // 获取角色列表 21 | authorityRouter.Post("setDataAuthority", authorityApi.SetDataAuthority) // 设置角色资源权限 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /router/system/base.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/api/v1" 5 | "github.com/gofiber/fiber/v2" 6 | ) 7 | 8 | type BaseRouter struct { 9 | } 10 | 11 | func (s *BaseRouter) InitBaseRouter(Router fiber.Router) (R fiber.Router) { 12 | baseRouter := Router.Group("base") 13 | var baseApi = v1.AppApi.SystemApi.Base 14 | { 15 | baseRouter.Post("login", baseApi.Login) 16 | baseRouter.Post("captcha", baseApi.Captcha) 17 | } 18 | return baseRouter 19 | } 20 | -------------------------------------------------------------------------------- /router/system/casbin.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/api/v1" 5 | "FiberBoot/middleware" 6 | "github.com/gofiber/fiber/v2" 7 | ) 8 | 9 | type CasbinRouter struct { 10 | } 11 | 12 | func (s *CasbinRouter) InitCasbinRouter(Router fiber.Router) { 13 | casbinRouter := Router.Group("casbin").Use(middleware.Operations()) 14 | var casbinApi = v1.AppApi.SystemApi.Casbin 15 | { 16 | casbinRouter.Post("updateCasbin", casbinApi.UpdateCasbin) 17 | casbinRouter.Post("getPolicyPathByAuthorityId", casbinApi.GetPolicyPathByAuthorityId) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /router/system/email.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/api/v1" 5 | "FiberBoot/middleware" 6 | "github.com/gofiber/fiber/v2" 7 | ) 8 | 9 | type EmailRouter struct { 10 | } 11 | 12 | func (s *EmailRouter) InitEmailRouter(Router fiber.Router) { 13 | emailRouter := Router.Group("email").Use(middleware.Operations()) 14 | var systemApi = v1.AppApi.SystemApi.Systems 15 | { 16 | emailRouter.Post("emailTest", systemApi.EmailTest) // 发送测试邮件 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /router/system/enter.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | type RouterGroup struct { 4 | ApiRouter 5 | AuthorityRouter 6 | BaseRouter 7 | CasbinRouter 8 | EmailRouter 9 | InitRouter 10 | JwtRouter 11 | MenuRouter 12 | OperationsRouter 13 | SysRouter 14 | UserRouter 15 | } 16 | -------------------------------------------------------------------------------- /router/system/initdb.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/api/v1" 5 | "github.com/gofiber/fiber/v2" 6 | ) 7 | 8 | type InitRouter struct { 9 | } 10 | 11 | func (s *InitRouter) InitInitRouter(Router fiber.Router) { 12 | initRouter := Router.Group("init") 13 | var dbApi = v1.AppApi.SystemApi.DB 14 | { 15 | initRouter.Post("initDB", dbApi.InitDB) // 创建Api 16 | initRouter.Post("checkDB", dbApi.CheckDB) // 创建Api 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /router/system/jwt.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/api/v1" 5 | "FiberBoot/middleware" 6 | "github.com/gofiber/fiber/v2" 7 | ) 8 | 9 | type JwtRouter struct { 10 | } 11 | 12 | func (s *JwtRouter) InitJwtRouter(Router fiber.Router) { 13 | jwtRouter := Router.Group("jwt").Use(middleware.Operations()) 14 | var jwtApi = v1.AppApi.SystemApi.Jwt 15 | { 16 | jwtRouter.Post("jsonInBlacklist", jwtApi.JsonInBlacklist) // jwt加入黑名单 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /router/system/menu.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/api/v1" 5 | "FiberBoot/middleware" 6 | "github.com/gofiber/fiber/v2" 7 | ) 8 | 9 | type MenuRouter struct { 10 | } 11 | 12 | func (s *MenuRouter) InitMenuRouter(Router fiber.Router) (R fiber.Router) { 13 | menuRouter := Router.Group("menu").Use(middleware.Operations()) 14 | var authorityMenuApi = v1.AppApi.SystemApi.AuthorityMenu 15 | { 16 | menuRouter.Post("getMenu", authorityMenuApi.GetMenu) // 获取菜单树 17 | menuRouter.Post("getMenuList", authorityMenuApi.GetMenuList) // 分页获取基础menu列表 18 | menuRouter.Post("addBaseMenu", authorityMenuApi.AddBaseMenu) // 新增菜单 19 | menuRouter.Post("getBaseMenuTree", authorityMenuApi.GetBaseMenuTree) // 获取用户动态路由 20 | menuRouter.Post("addMenuAuthority", authorityMenuApi.AddMenuAuthority) // 增加menu和角色关联关系 21 | menuRouter.Post("getMenuAuthority", authorityMenuApi.GetMenuAuthority) // 获取指定角色menu 22 | menuRouter.Post("deleteBaseMenu", authorityMenuApi.DeleteBaseMenu) // 删除菜单 23 | menuRouter.Post("updateBaseMenu", authorityMenuApi.UpdateBaseMenu) // 更新菜单 24 | menuRouter.Post("getBaseMenuById", authorityMenuApi.GetBaseMenuById) // 根据id获取菜单 25 | } 26 | return menuRouter 27 | } 28 | -------------------------------------------------------------------------------- /router/system/operations.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/api/v1" 5 | "FiberBoot/middleware" 6 | "github.com/gofiber/fiber/v2" 7 | ) 8 | 9 | type OperationsRouter struct { 10 | } 11 | 12 | func (s *OperationsRouter) InitOperationsRouter(Router fiber.Router) { 13 | operationsRouter := Router.Group("operations").Use(middleware.Operations()) 14 | var operations = v1.AppApi.SystemApi.Operations 15 | { 16 | operationsRouter.Post("createOperations", operations.CreateOperations) // 新建Operations 17 | operationsRouter.Delete("deleteOperations", operations.DeleteOperations) // 删除Operations 18 | operationsRouter.Delete("deleteOperationsByIds", operations.DeleteOperationsByIds) // 批量删除Operations 19 | operationsRouter.Get("findOperations", operations.FindOperations) // 根据ID获取Operations 20 | operationsRouter.Get("getOperationsList", operations.GetOperationsList) // 获取Operations列表 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /router/system/system.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/api/v1" 5 | "FiberBoot/middleware" 6 | "github.com/gofiber/fiber/v2" 7 | ) 8 | 9 | type SysRouter struct { 10 | } 11 | 12 | func (s *SysRouter) InitSystemRouter(Router fiber.Router) { 13 | sysRouter := Router.Group("system").Use(middleware.Operations()) 14 | var systems = v1.AppApi.SystemApi.Systems 15 | { 16 | sysRouter.Post("getSystemConfig", systems.GetSystemConfig) // 获取配置文件内容 17 | sysRouter.Post("setSystemConfig", systems.SetSystemConfig) // 设置配置文件内容 18 | sysRouter.Post("getServerInfo", systems.GetServerInfo) // 获取服务器信息 19 | sysRouter.Post("reloadSystem", systems.ReloadSystem) // 重启服务 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /router/system/user.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/api/v1" 5 | "FiberBoot/middleware" 6 | "github.com/gofiber/fiber/v2" 7 | ) 8 | 9 | type UserRouter struct { 10 | } 11 | 12 | func (s *UserRouter) InitUserRouter(Router fiber.Router) { 13 | userRouter := Router.Group("user").Use(middleware.Operations()) 14 | var baseApi = v1.AppApi.SystemApi.Base 15 | { 16 | userRouter.Post("register", baseApi.Register) // 用户注册账号 17 | userRouter.Post("changePassword", baseApi.ChangePassword) // 用户修改密码 18 | userRouter.Post("getUserList", baseApi.GetUserList) // 分页获取用户列表 19 | userRouter.Post("setUserAuthority", baseApi.SetUserAuthority) // 设置用户权限 20 | userRouter.Delete("deleteUser", baseApi.DeleteUser) // 删除用户 21 | userRouter.Put("setUserInfo", baseApi.SetUserInfo) // 设置用户信息 22 | userRouter.Post("setUserAuthorities", baseApi.SetUserAuthorities) // 设置用户权限组 23 | userRouter.Get("getUserInfo", baseApi.GetUserInfo) // 获取自身信息 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /service/enter.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "FiberBoot/service/example" 5 | "FiberBoot/service/system" 6 | ) 7 | 8 | type Service struct { 9 | ExampleService example.ServiceGroup 10 | SystemService system.ServiceGroup 11 | } 12 | 13 | var AppService = new(Service) 14 | -------------------------------------------------------------------------------- /service/example/advanceUpload.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/example" 6 | "errors" 7 | "gorm.io/gorm" 8 | ) 9 | 10 | type FileTransferService struct { 11 | } 12 | 13 | //@author: Flame 14 | //@function: FindOrCreateFile 15 | //@description: 上传文件时检测当前文件属性,如果没有文件则创建,有则返回文件的当前切片 16 | //@param: fileMd5 string, fileName string, chunkTotal int 17 | //@return: err error, file model.File 18 | 19 | func (e *FileTransferService) FindOrCreateFile(fileMd5 string, fileName string, chunkTotal int) (err error, file example.File) { 20 | var cFile example.File 21 | cFile.FileMd5 = fileMd5 22 | cFile.FileName = fileName 23 | cFile.ChunkTotal = chunkTotal 24 | 25 | if errors.Is(global.DB.Where("file_md5 = ? AND is_finish = ?", fileMd5, true).First(&file).Error, gorm.ErrRecordNotFound) { 26 | err = global.DB.Where("file_md5 = ? AND file_name = ?", fileMd5, fileName).Preload("FileChunk").FirstOrCreate(&file, cFile).Error 27 | return err, file 28 | } 29 | cFile.IsFinish = true 30 | cFile.FilePath = file.FilePath 31 | err = global.DB.Create(&cFile).Error 32 | return err, cFile 33 | } 34 | 35 | //@author: Flame 36 | //@function: CreateFileChunk 37 | //@description: 创建文件切片记录 38 | //@param: id uint, fileChunkPath string, fileChunkNumber int 39 | //@return: error 40 | 41 | func (e *FileTransferService) CreateFileChunk(id uint, fileChunkPath string, fileChunkNumber int) error { 42 | var chunk example.FileChunk 43 | chunk.FileChunkPath = fileChunkPath 44 | chunk.FileID = id 45 | chunk.FileChunkNumber = fileChunkNumber 46 | err := global.DB.Create(&chunk).Error 47 | return err 48 | } 49 | 50 | //@author: Flame 51 | //@function: DeleteFileChunk 52 | //@description: 删除文件切片记录 53 | //@param: fileMd5 string, fileName string, filePath string 54 | //@return: error 55 | 56 | func (e *FileTransferService) DeleteFileChunk(fileMd5 string, fileName string, filePath string) error { 57 | var chunks []example.FileChunk 58 | var file example.File 59 | err := global.DB.Where("file_md5 = ? AND file_name = ?", fileMd5, fileName).First(&file).Update("IsFinish", true).Update("file_path", filePath).Error 60 | err = global.DB.Where("exa_file_id = ?", file.ID).Delete(&chunks).Unscoped().Error 61 | return err 62 | } 63 | -------------------------------------------------------------------------------- /service/example/customer.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/common/request" 6 | "FiberBoot/model/example" 7 | "FiberBoot/model/system" 8 | systemService "FiberBoot/service/system" 9 | ) 10 | 11 | type CustomerService struct { 12 | } 13 | 14 | //@author: Flame 15 | //@function: CreateCustomer 16 | //@description: 创建客户 17 | //@param: e model.Customer 18 | //@return: err error 19 | 20 | func (exa *CustomerService) CreateCustomer(e example.Customer) (err error) { 21 | err = global.DB.Create(&e).Error 22 | return err 23 | } 24 | 25 | //@author: Flame 26 | //@function: DeleteFileChunk 27 | //@description: 删除客户 28 | //@param: e model.Customer 29 | //@return: err error 30 | 31 | func (exa *CustomerService) DeleteCustomer(e example.Customer) (err error) { 32 | err = global.DB.Delete(&e).Error 33 | return err 34 | } 35 | 36 | //@author: Flame 37 | //@function: UpdateCustomer 38 | //@description: 更新客户 39 | //@param: e *model.Customer 40 | //@return: err error 41 | 42 | func (exa *CustomerService) UpdateCustomer(e *example.Customer) (err error) { 43 | err = global.DB.Save(e).Error 44 | return err 45 | } 46 | 47 | //@author: Flame 48 | //@function: GetCustomer 49 | //@description: 获取客户信息 50 | //@param: id uint 51 | //@return: err error, customer model.Customer 52 | 53 | func (exa *CustomerService) GetCustomer(id uint) (err error, customer example.Customer) { 54 | err = global.DB.Where("id = ?", id).First(&customer).Error 55 | return 56 | } 57 | 58 | //@author: Flame 59 | //@function: GetCustomerInfoList 60 | //@description: 分页获取客户列表 61 | //@param: UserAuthorityID string, info request.PageInfo 62 | //@return: err error, list interface{}, total int64 63 | 64 | func (exa *CustomerService) GetCustomerInfoList(UserAuthorityID string, info request.PageInfo) (err error, list interface{}, total int64) { 65 | limit := info.PageSize 66 | offset := info.PageSize * (info.Page - 1) 67 | db := global.DB.Model(&example.Customer{}) 68 | var a system.Authority 69 | a.AuthorityId = UserAuthorityID 70 | err, auth := systemService.AuthorityServiceApp.GetAuthorityInfo(a) 71 | var dataId []string 72 | for _, v := range auth.DataAuthorityId { 73 | dataId = append(dataId, v.AuthorityId) 74 | } 75 | var CustomerList []example.Customer 76 | err = db.Where("user_authority_id in ?", dataId).Count(&total).Error 77 | if err != nil { 78 | return err, CustomerList, total 79 | } else { 80 | err = db.Limit(limit).Offset(offset).Preload("User").Where("user_authority_id in ?", dataId).Find(&CustomerList).Error 81 | } 82 | return err, CustomerList, total 83 | } 84 | -------------------------------------------------------------------------------- /service/example/enter.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | type ServiceGroup struct { 4 | FileTransferService 5 | CustomerService 6 | ExcelService 7 | SimpleUploadService 8 | } 9 | -------------------------------------------------------------------------------- /service/example/excel.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/system" 6 | "errors" 7 | "fmt" 8 | "github.com/xuri/excelize/v2" 9 | "strconv" 10 | ) 11 | 12 | type ExcelService struct { 13 | } 14 | 15 | func (exa *ExcelService) ParseInfoList2Excel(infoList []system.BaseMenu, filePath string) error { 16 | excel := excelize.NewFile() 17 | err := excel.SetSheetRow("Sheet1", "A1", &[]string{"ID", "路由Name", "路由Path", "是否隐藏", "父节点", "排序", "文件名称"}) 18 | if err != nil { 19 | return err 20 | } 21 | for i, menu := range infoList { 22 | axis := fmt.Sprintf("A%d", i+2) 23 | err := excel.SetSheetRow("Sheet1", axis, &[]interface{}{ 24 | menu.ID, 25 | menu.Name, 26 | menu.Path, 27 | menu.Hidden, 28 | menu.ParentId, 29 | menu.Sort, 30 | menu.Component, 31 | }) 32 | if err != nil { 33 | return err 34 | } 35 | } 36 | err = excel.SaveAs(filePath) 37 | return err 38 | } 39 | 40 | func (exa *ExcelService) ParseExcel2InfoList() ([]system.BaseMenu, error) { 41 | skipHeader := true 42 | fixedHeader := []string{"ID", "路由Name", "路由Path", "是否隐藏", "父节点", "排序", "文件名称"} 43 | file, err := excelize.OpenFile(global.CONFIG.Excel.Dir + "ExcelImport.xlsx") 44 | if err != nil { 45 | return nil, err 46 | } 47 | menus := make([]system.BaseMenu, 0) 48 | rows, err := file.Rows("Sheet1") 49 | if err != nil { 50 | return nil, err 51 | } 52 | for rows.Next() { 53 | row, err := rows.Columns() 54 | if err != nil { 55 | return nil, err 56 | } 57 | if skipHeader { 58 | if exa.compareStrSlice(row, fixedHeader) { 59 | skipHeader = false 60 | continue 61 | } else { 62 | return nil, errors.New("Excel格式错误 ") 63 | } 64 | } 65 | if len(row) != len(fixedHeader) { 66 | continue 67 | } 68 | id, _ := strconv.Atoi(row[0]) 69 | hidden, _ := strconv.ParseBool(row[3]) 70 | sort, _ := strconv.Atoi(row[5]) 71 | menu := system.BaseMenu{ 72 | MODEL: global.MODEL{ 73 | ID: uint(id), 74 | }, 75 | Name: row[1], 76 | Path: row[2], 77 | Hidden: hidden, 78 | ParentId: row[4], 79 | Sort: sort, 80 | Component: row[6], 81 | } 82 | menus = append(menus, menu) 83 | } 84 | return menus, nil 85 | } 86 | 87 | func (exa *ExcelService) compareStrSlice(a, b []string) bool { 88 | if len(a) != len(b) { 89 | return false 90 | } 91 | if (b == nil) != (a == nil) { 92 | return false 93 | } 94 | for key, value := range a { 95 | if value != b[key] { 96 | return false 97 | } 98 | } 99 | return true 100 | } 101 | -------------------------------------------------------------------------------- /service/example/fileTransfer.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/common/request" 6 | "FiberBoot/model/example" 7 | "FiberBoot/utils/upload" 8 | "errors" 9 | "mime/multipart" 10 | "strings" 11 | ) 12 | 13 | //@author: Flame 14 | //@function: Upload 15 | //@description: 创建文件上传记录 16 | //@param: file model.FileTransfer 17 | //@return: error 18 | 19 | func (e *FileTransferService) Upload(file example.FileTransfer) error { 20 | return global.DB.Create(&file).Error 21 | } 22 | 23 | //@author: Flame 24 | //@function: FindFile 25 | //@description: 删除文件切片记录 26 | //@param: id uint 27 | //@return: error, model.FileTransfer 28 | 29 | func (e *FileTransferService) FindFile(id uint) (error, example.FileTransfer) { 30 | var file example.FileTransfer 31 | err := global.DB.Where("id = ?", id).First(&file).Error 32 | return err, file 33 | } 34 | 35 | //@author: Flame 36 | //@function: DeleteFile 37 | //@description: 删除文件记录 38 | //@param: file model.FileTransfer 39 | //@return: err error 40 | 41 | func (e *FileTransferService) DeleteFile(file example.FileTransfer) (err error) { 42 | var fileFromDb example.FileTransfer 43 | err, fileFromDb = e.FindFile(file.ID) 44 | oss := upload.NewOss() 45 | if err = oss.DeleteFile(fileFromDb.Key); err != nil { 46 | return errors.New("文件删除失败") 47 | } 48 | err = global.DB.Where("id = ?", file.ID).Unscoped().Delete(&file).Error 49 | return err 50 | } 51 | 52 | //@author: Flame 53 | //@function: GetFileRecordInfoList 54 | //@description: 分页获取数据 55 | //@param: info request.PageInfo 56 | //@return: err error, list interface{}, total int64 57 | 58 | func (e *FileTransferService) GetFileRecordInfoList(info request.PageInfo) (err error, list interface{}, total int64) { 59 | limit := info.PageSize 60 | offset := info.PageSize * (info.Page - 1) 61 | db := global.DB 62 | var fileLists []example.FileTransfer 63 | err = db.Find(&fileLists).Count(&total).Error 64 | err = db.Limit(limit).Offset(offset).Order("updated_at desc").Find(&fileLists).Error 65 | return err, fileLists, total 66 | } 67 | 68 | //@author: Flame 69 | //@function: UploadFile 70 | //@description: 根据配置文件判断是文件上传到本地或者七牛云 71 | //@param: header *multipart.FileHeader, noSave string 72 | //@return: err error, file model.FileTransfer 73 | 74 | func (e *FileTransferService) UploadFile(header *multipart.FileHeader, noSave string) (err error, file example.FileTransfer) { 75 | oss := upload.NewOss() 76 | filePath, key, uploadErr := oss.UploadFile(header) 77 | if uploadErr != nil { 78 | panic(err) 79 | } 80 | if noSave == "0" { 81 | s := strings.Split(header.Filename, ".") 82 | f := example.FileTransfer{ 83 | Url: filePath, 84 | Name: header.Filename, 85 | Tag: s[len(s)-1], 86 | Key: key, 87 | } 88 | return e.Upload(f), f 89 | } 90 | return 91 | } 92 | -------------------------------------------------------------------------------- /service/example/simpleUpload.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/example" 6 | "errors" 7 | "fmt" 8 | "gorm.io/gorm" 9 | "io/ioutil" 10 | "os" 11 | "strconv" 12 | ) 13 | 14 | type SimpleUploadService struct { 15 | } 16 | 17 | //@author: Flame 18 | //@function: SaveChunk 19 | //@description: 保存文件切片路径 20 | //@param: uploader model.SimpleUploader 21 | //@return: err error 22 | 23 | func (exa *SimpleUploadService) SaveChunk(uploader example.SimpleUploader) (err error) { 24 | return global.DB.Create(uploader).Error 25 | } 26 | 27 | //@author: Flame 28 | //@function: CheckFileMd5 29 | //@description: 检查文件是否已经上传过 30 | //@param: md5 string 31 | //@return: err error, uploads []model.SimpleUploader, isDone bool 32 | 33 | func (exa *SimpleUploadService) CheckFileMd5(md5 string) (err error, uploads []example.SimpleUploader, isDone bool) { 34 | err = global.DB.Find(&uploads, "identifier = ? AND is_done = ?", md5, false).Error 35 | isDone = errors.Is(global.DB.First(&example.SimpleUploader{}, "identifier = ? AND is_done = ?", md5, true).Error, gorm.ErrRecordNotFound) 36 | return err, uploads, !isDone 37 | } 38 | 39 | //@author: Flame 40 | //@function: MergeFileMd5 41 | //@description: 合并文件 42 | //@param: md5 string, fileName string 43 | //@return: err error 44 | 45 | func (exa *SimpleUploadService) MergeFileMd5(md5 string, fileName string) (err error) { 46 | finishDir := "./finish/" 47 | dir := "./chunk/" + md5 48 | // 如果文件上传成功 不做后续操作 通知成功即可 49 | if !errors.Is(global.DB.First(&example.SimpleUploader{}, "identifier = ? AND is_done = ?", md5, true).Error, gorm.ErrRecordNotFound) { 50 | return nil 51 | } 52 | 53 | // 打开切片文件夹 54 | rd, err := ioutil.ReadDir(dir) 55 | _ = os.MkdirAll(finishDir, os.ModePerm) 56 | // 创建目标文件 57 | fd, err := os.OpenFile(finishDir+fileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644) 58 | if err != nil { 59 | return 60 | } 61 | // 关闭文件 62 | defer func(fd *os.File) { 63 | _ = fd.Close() 64 | }(fd) 65 | // 将切片文件按照顺序写入 66 | for k := range rd { 67 | content, _ := ioutil.ReadFile(dir + "/" + fileName + strconv.Itoa(k+1)) 68 | _, err = fd.Write(content) 69 | if err != nil { 70 | _ = os.Remove(finishDir + fileName) 71 | } 72 | } 73 | 74 | if err != nil { 75 | return err 76 | } 77 | err = global.DB.Transaction(func(tx *gorm.DB) error { 78 | // 删除切片信息 79 | if err = tx.Delete(&example.SimpleUploader{}, "identifier = ? AND is_done = ?", md5, false).Error; err != nil { 80 | fmt.Println(err) 81 | return err 82 | } 83 | data := example.SimpleUploader{ 84 | Identifier: md5, 85 | IsDone: true, 86 | FilePath: finishDir + fileName, 87 | Filename: fileName, 88 | } 89 | // 添加文件信息 90 | if err = tx.Create(&data).Error; err != nil { 91 | fmt.Println(err) 92 | return err 93 | } 94 | return nil 95 | }) 96 | 97 | err = os.RemoveAll(dir) // 清除切片 98 | return err 99 | } 100 | -------------------------------------------------------------------------------- /service/system/api.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/common/request" 6 | "FiberBoot/model/system" 7 | "errors" 8 | 9 | "gorm.io/gorm" 10 | ) 11 | 12 | //@author: Flame 13 | //@function: CreateApi 14 | //@description: 新增基础api 15 | //@param: api model.Api 16 | //@return: err error 17 | 18 | type ApiService struct { 19 | } 20 | 21 | func (apiService *ApiService) CreateApi(api system.Api) (err error) { 22 | if !errors.Is(global.DB.Where("path = ? AND method = ?", api.Path, api.Method).First(&system.Api{}).Error, gorm.ErrRecordNotFound) { 23 | return errors.New("存在相同api") 24 | } 25 | return global.DB.Create(&api).Error 26 | } 27 | 28 | //@author: Flame 29 | //@function: DeleteApi 30 | //@description: 删除基础api 31 | //@param: api model.Api 32 | //@return: err error 33 | 34 | func (apiService *ApiService) DeleteApi(api system.Api) (err error) { 35 | err = global.DB.Delete(&api).Error 36 | CasbinServiceApp.ClearCasbin(1, api.Path, api.Method) 37 | return err 38 | } 39 | 40 | //@author: Flame 41 | //@function: GetAPIInfoList 42 | //@description: 分页获取数据, 43 | //@param: api model.Api, info request.PageInfo, order string, desc bool 44 | //@return: err error 45 | 46 | func (apiService *ApiService) GetAPIInfoList(api system.Api, info request.PageInfo, order string, desc bool) (err error, list interface{}, total int64) { 47 | limit := info.PageSize 48 | offset := info.PageSize * (info.Page - 1) 49 | db := global.DB.Model(&system.Api{}) 50 | var apiList []system.Api 51 | 52 | if api.Path != "" { 53 | db = db.Where("path LIKE ?", "%"+api.Path+"%") 54 | } 55 | 56 | if api.Description != "" { 57 | db = db.Where("description LIKE ?", "%"+api.Description+"%") 58 | } 59 | 60 | if api.Method != "" { 61 | db = db.Where("method = ?", api.Method) 62 | } 63 | 64 | if api.ApiGroup != "" { 65 | db = db.Where("api_group = ?", api.ApiGroup) 66 | } 67 | 68 | err = db.Count(&total).Error 69 | 70 | if err != nil { 71 | return err, apiList, total 72 | } else { 73 | db = db.Limit(limit).Offset(offset) 74 | if order != "" { 75 | var OrderStr string 76 | if desc { 77 | OrderStr = order + " desc" 78 | } else { 79 | OrderStr = order 80 | } 81 | err = db.Order(OrderStr).Find(&apiList).Error 82 | } else { 83 | err = db.Order("api_group").Find(&apiList).Error 84 | } 85 | } 86 | return err, apiList, total 87 | } 88 | 89 | //@author: Flame 90 | //@function: GetAllApis 91 | //@description: 获取所有的api 92 | //@return: err error, apis []model.Api 93 | 94 | func (apiService *ApiService) GetAllApis() (err error, apis []system.Api) { 95 | err = global.DB.Find(&apis).Error 96 | return 97 | } 98 | 99 | //@author: Flame 100 | //@function: GetApiById 101 | //@description: 根据id获取api 102 | //@param: id float64 103 | //@return: err error, api model.Api 104 | 105 | func (apiService *ApiService) GetApiById(id float64) (err error, api system.Api) { 106 | err = global.DB.Where("id = ?", id).First(&api).Error 107 | return 108 | } 109 | 110 | //@author: Flame 111 | //@function: UpdateApi 112 | //@description: 根据id更新api 113 | //@param: api model.Api 114 | //@return: err error 115 | 116 | func (apiService *ApiService) UpdateApi(api system.Api) (err error) { 117 | var oldA system.Api 118 | err = global.DB.Where("id = ?", api.ID).First(&oldA).Error 119 | if oldA.Path != api.Path || oldA.Method != api.Method { 120 | if !errors.Is(global.DB.Where("path = ? AND method = ?", api.Path, api.Method).First(&system.Api{}).Error, gorm.ErrRecordNotFound) { 121 | return errors.New("存在相同api路径") 122 | } 123 | } 124 | if err != nil { 125 | return err 126 | } else { 127 | err = CasbinServiceApp.UpdateCasbinApi(oldA.Path, api.Path, oldA.Method, api.Method) 128 | if err != nil { 129 | return err 130 | } else { 131 | err = global.DB.Save(&api).Error 132 | } 133 | } 134 | return err 135 | } 136 | 137 | //@author: Flame 138 | //@function: DeleteApis 139 | //@description: 删除选中API 140 | //@param: apis []model.Api 141 | //@return: err error 142 | 143 | func (apiService *ApiService) DeleteApisByIds(ids request.IdsReq) (err error) { 144 | err = global.DB.Delete(&[]system.Api{}, "id in ?", ids.Ids).Error 145 | return err 146 | } 147 | 148 | func (apiService *ApiService) DeleteApiByIds(ids []string) (err error) { 149 | return global.DB.Delete(system.Api{}, ids).Error 150 | } 151 | -------------------------------------------------------------------------------- /service/system/baseMenu.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/system" 6 | "errors" 7 | "gorm.io/gorm" 8 | ) 9 | 10 | type BaseMenuService struct { 11 | } 12 | 13 | //@author: Flame 14 | //@function: DeleteBaseMenu 15 | //@description: 删除基础路由 16 | //@param: id float64 17 | //@return: err error 18 | 19 | func (baseMenuService *BaseMenuService) DeleteBaseMenu(id float64) (err error) { 20 | err = global.DB.Preload("Parameters").Where("parent_id = ?", id).First(&system.BaseMenu{}).Error 21 | if err != nil { 22 | var menu system.BaseMenu 23 | db := global.DB.Preload("SysAuthorities").Where("id = ?", id).First(&menu).Delete(&menu) 24 | err = global.DB.Delete(&system.BaseMenuParameter{}, "base_menu_id = ?", id).Error 25 | if len(menu.SysAuthorities) > 0 { 26 | err = global.DB.Model(&menu).Association("SysAuthorities").Delete(&menu.SysAuthorities) 27 | } else { 28 | err = db.Error 29 | } 30 | } else { 31 | return errors.New("此菜单存在子菜单不可删除") 32 | } 33 | return err 34 | } 35 | 36 | //@author: Flame 37 | //@function: UpdateBaseMenu 38 | //@description: 更新路由 39 | //@param: menu model.BaseMenu 40 | //@return: err error 41 | 42 | func (baseMenuService *BaseMenuService) UpdateBaseMenu(menu system.BaseMenu) (err error) { 43 | var oldMenu system.BaseMenu 44 | upDateMap := make(map[string]interface{}) 45 | upDateMap["keep_alive"] = menu.KeepAlive 46 | upDateMap["close_tab"] = menu.CloseTab 47 | upDateMap["default_menu"] = menu.DefaultMenu 48 | upDateMap["parent_id"] = menu.ParentId 49 | upDateMap["path"] = menu.Path 50 | upDateMap["name"] = menu.Name 51 | upDateMap["hidden"] = menu.Hidden 52 | upDateMap["component"] = menu.Component 53 | upDateMap["title"] = menu.Title 54 | upDateMap["icon"] = menu.Icon 55 | upDateMap["sort"] = menu.Sort 56 | 57 | err = global.DB.Transaction(func(tx *gorm.DB) error { 58 | db := tx.Where("id = ?", menu.ID).Find(&oldMenu) 59 | if oldMenu.Name != menu.Name { 60 | if !errors.Is(tx.Where("id <> ? AND name = ?", menu.ID, menu.Name).First(&system.BaseMenu{}).Error, gorm.ErrRecordNotFound) { 61 | global.LOG.Debug("存在相同name修改失败") 62 | return errors.New("存在相同name修改失败") 63 | } 64 | } 65 | txErr := tx.Unscoped().Delete(&system.BaseMenuParameter{}, "base_menu_id = ?", menu.ID).Error 66 | if txErr != nil { 67 | global.LOG.Debug(txErr.Error()) 68 | return txErr 69 | } 70 | if len(menu.Parameters) > 0 { 71 | for k := range menu.Parameters { 72 | menu.Parameters[k].BaseMenuID = menu.ID 73 | } 74 | txErr = tx.Create(&menu.Parameters).Error 75 | if txErr != nil { 76 | global.LOG.Debug(txErr.Error()) 77 | return txErr 78 | } 79 | } 80 | 81 | txErr = db.Updates(upDateMap).Error 82 | if txErr != nil { 83 | global.LOG.Debug(txErr.Error()) 84 | return txErr 85 | } 86 | return nil 87 | }) 88 | return err 89 | } 90 | 91 | //@author: Flame 92 | //@function: GetBaseMenuById 93 | //@description: 返回当前选中menu 94 | //@param: id float64 95 | //@return: err error, menu model.BaseMenu 96 | 97 | func (baseMenuService *BaseMenuService) GetBaseMenuById(id float64) (err error, menu system.BaseMenu) { 98 | err = global.DB.Preload("Parameters").Where("id = ?", id).First(&menu).Error 99 | return 100 | } 101 | -------------------------------------------------------------------------------- /service/system/casbin.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/system" 6 | "FiberBoot/model/system/request" 7 | "errors" 8 | "github.com/casbin/casbin/v2" 9 | "github.com/casbin/casbin/v2/util" 10 | gormAdapter "github.com/casbin/gorm-adapter/v3" 11 | _ "github.com/go-sql-driver/mysql" 12 | "strings" 13 | "sync" 14 | ) 15 | 16 | //@author: Flame 17 | //@function: UpdateCasbin 18 | //@description: 更新casbin权限 19 | //@param: authorityId string, casbinInfos []request.CasbinInfo 20 | //@return: error 21 | 22 | type CasbinService struct { 23 | } 24 | 25 | var CasbinServiceApp = new(CasbinService) 26 | 27 | func (casbinService *CasbinService) UpdateCasbin(authorityId string, casbinInfos []request.CasbinInfo) error { 28 | casbinService.ClearCasbin(0, authorityId) 29 | var rules [][]string 30 | for _, v := range casbinInfos { 31 | cm := system.CasbinModel{ 32 | Ptype: "p", 33 | AuthorityId: authorityId, 34 | Path: v.Path, 35 | Method: v.Method, 36 | } 37 | rules = append(rules, []string{cm.AuthorityId, cm.Path, cm.Method}) 38 | } 39 | e := casbinService.Casbin() 40 | success, _ := e.AddPolicies(rules) 41 | if success == false { 42 | return errors.New("存在相同api,添加失败,请联系管理员") 43 | } 44 | return nil 45 | } 46 | 47 | //@author: Flame 48 | //@function: UpdateCasbinApi 49 | //@description: API更新随动 50 | //@param: oldPath string, newPath string, oldMethod string, newMethod string 51 | //@return: error 52 | 53 | func (casbinService *CasbinService) UpdateCasbinApi(oldPath string, newPath string, oldMethod string, newMethod string) error { 54 | err := global.DB.Table("casbin_rule").Model(&system.CasbinModel{}).Where("v1 = ? AND v2 = ?", oldPath, oldMethod).Updates(map[string]interface{}{ 55 | "v1": newPath, 56 | "v2": newMethod, 57 | }).Error 58 | return err 59 | } 60 | 61 | //@author: Flame 62 | //@function: GetPolicyPathByAuthorityId 63 | //@description: 获取权限列表 64 | //@param: authorityId string 65 | //@return: pathMaps []request.CasbinInfo 66 | 67 | func (casbinService *CasbinService) GetPolicyPathByAuthorityId(authorityId string) (pathMaps []request.CasbinInfo) { 68 | e := casbinService.Casbin() 69 | list := e.GetFilteredPolicy(0, authorityId) 70 | for _, v := range list { 71 | pathMaps = append(pathMaps, request.CasbinInfo{ 72 | Path: v[1], 73 | Method: v[2], 74 | }) 75 | } 76 | return pathMaps 77 | } 78 | 79 | //@author: Flame 80 | //@function: ClearCasbin 81 | //@description: 清除匹配的权限 82 | //@param: v int, p ...string 83 | //@return: bool 84 | 85 | func (casbinService *CasbinService) ClearCasbin(v int, p ...string) bool { 86 | e := casbinService.Casbin() 87 | success, _ := e.RemoveFilteredPolicy(v, p...) 88 | return success 89 | 90 | } 91 | 92 | //@author: Flame 93 | //@function: Casbin 94 | //@description: 持久化到数据库 引入自定义规则 95 | //@return: *casbin.Enforcer 96 | 97 | var ( 98 | syncedEnforcer *casbin.SyncedEnforcer 99 | once sync.Once 100 | ) 101 | 102 | func (casbinService *CasbinService) Casbin() *casbin.SyncedEnforcer { 103 | once.Do(func() { 104 | a, _ := gormAdapter.NewAdapterByDB(global.DB) 105 | syncedEnforcer, _ = casbin.NewSyncedEnforcer(global.CONFIG.Casbin.ModelPath, a) 106 | syncedEnforcer.AddFunction("ParamsMatch", casbinService.ParamsMatchFunc) 107 | }) 108 | _ = syncedEnforcer.LoadPolicy() 109 | return syncedEnforcer 110 | } 111 | 112 | //@author: Flame 113 | //@function: ParamsMatch 114 | //@description: 自定义规则函数 115 | //@param: fullNameKey1 string, key2 string 116 | //@return: bool 117 | 118 | func (casbinService *CasbinService) ParamsMatch(fullNameKey1 string, key2 string) bool { 119 | key1 := strings.Split(fullNameKey1, "?")[0] 120 | // 剥离路径后再使用casbin的keyMatch2 121 | return util.KeyMatch2(key1, key2) 122 | } 123 | 124 | //@author: Flame 125 | //@function: ParamsMatchFunc 126 | //@description: 自定义规则函数 127 | //@param: args ...interface{} 128 | //@return: interface{}, error 129 | 130 | func (casbinService *CasbinService) ParamsMatchFunc(args ...interface{}) (interface{}, error) { 131 | name1 := args[0].(string) 132 | name2 := args[1].(string) 133 | 134 | return casbinService.ParamsMatch(name1, name2), nil 135 | } 136 | -------------------------------------------------------------------------------- /service/system/email.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/utils" 5 | ) 6 | 7 | type EmailService struct { 8 | } 9 | 10 | //@author: Flame 11 | //@function: EmailTest 12 | //@description: 发送邮件测试 13 | //@return: err error 14 | 15 | func (e *EmailService) EmailTest() (err error) { 16 | subject := "test" 17 | body := "test" 18 | err = utils.EmailTest(subject, body) 19 | return err 20 | } 21 | -------------------------------------------------------------------------------- /service/system/enter.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | type ServiceGroup struct { 4 | JwtService 5 | ApiService 6 | AuthorityService 7 | BaseMenuService 8 | CasbinService 9 | EmailService 10 | InitDBService 11 | MenuService 12 | OperationsService 13 | ConfigService 14 | UserService 15 | } 16 | -------------------------------------------------------------------------------- /service/system/initDB.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/config" 5 | "FiberBoot/global" 6 | "FiberBoot/model/example" 7 | "FiberBoot/model/system" 8 | "FiberBoot/model/system/request" 9 | "FiberBoot/source" 10 | "FiberBoot/utils" 11 | "database/sql" 12 | "fmt" 13 | "github.com/spf13/viper" 14 | "gorm.io/driver/mysql" 15 | "gorm.io/gorm" 16 | ) 17 | 18 | //@author: Flame 19 | //@function: writeConfig 20 | //@description: 回写配置 21 | //@param: viper *viper.Viper, mysql config.Mysql 22 | //@return: error 23 | 24 | type InitDBService struct { 25 | } 26 | 27 | func (initDBService *InitDBService) writeConfig(viper *viper.Viper, mysql config.Mysql) error { 28 | global.CONFIG.Mysql = mysql 29 | cs := utils.StructToMap(global.CONFIG) 30 | for k, v := range cs { 31 | viper.Set(k, v) 32 | } 33 | return viper.WriteConfig() 34 | } 35 | 36 | //@author: Flame 37 | //@function: createTable 38 | //@description: 创建数据库(mysql) 39 | //@param: dsn string, driver string, createSql 40 | //@return: error 41 | 42 | func (initDBService *InitDBService) createTable(dsn string, driver string, createSql string) error { 43 | db, err := sql.Open(driver, dsn) 44 | if err != nil { 45 | return err 46 | } 47 | defer func(db *sql.DB) { 48 | err := db.Close() 49 | if err != nil { 50 | 51 | } 52 | }(db) 53 | if err = db.Ping(); err != nil { 54 | return err 55 | } 56 | _, err = db.Exec(createSql) 57 | return err 58 | } 59 | 60 | func (initDBService *InitDBService) initDB(InitDBFunctions ...system.InitDBFunc) (err error) { 61 | for _, v := range InitDBFunctions { 62 | err = v.Init() 63 | if err != nil { 64 | return err 65 | } 66 | } 67 | return nil 68 | } 69 | 70 | //@author: Flame 71 | //@function: InitDB 72 | //@description: 创建数据库并初始化 73 | //@param: conf request.InitDB 74 | //@return: error 75 | 76 | func (initDBService *InitDBService) InitDB(conf request.InitDB) error { 77 | 78 | if conf.Host == "" { 79 | conf.Host = "127.0.0.1" 80 | } 81 | 82 | if conf.Port == "" { 83 | conf.Port = "3306" 84 | } 85 | dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/", conf.UserName, conf.Password, conf.Host, conf.Port) 86 | createSql := fmt.Sprintf("CREATE DATABASE IF NOT EXISTS `%s` DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci;", conf.DBName) 87 | if err := initDBService.createTable(dsn, "mysql", createSql); err != nil { 88 | return err 89 | } 90 | 91 | MysqlConfig := config.Mysql{ 92 | Path: fmt.Sprintf("%s:%s", conf.Host, conf.Port), 93 | Dbname: conf.DBName, 94 | Username: conf.UserName, 95 | Password: conf.Password, 96 | Config: "charset=utf8mb4&parseTime=True&loc=Local", 97 | } 98 | 99 | if MysqlConfig.Dbname == "" { 100 | return nil 101 | } 102 | 103 | linkDns := MysqlConfig.Username + ":" + MysqlConfig.Password + "@tcp(" + MysqlConfig.Path + ")/" + MysqlConfig.Dbname + "?" + MysqlConfig.Config 104 | mysqlConfig := mysql.Config{ 105 | DSN: linkDns, // DSN data source name 106 | DefaultStringSize: 256, // string 类型字段的默认长度 107 | DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持 108 | DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引 109 | DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列 110 | SkipInitializeWithVersion: false, // 根据版本自动配置 111 | } 112 | if db, err := gorm.Open(mysql.New(mysqlConfig), &gorm.Config{DisableForeignKeyConstraintWhenMigrating: true}); err != nil { 113 | return nil 114 | } else { 115 | sqlDB, _ := db.DB() 116 | sqlDB.SetMaxIdleConns(MysqlConfig.MaxIdleConns) 117 | sqlDB.SetMaxOpenConns(MysqlConfig.MaxOpenConns) 118 | global.DB = db 119 | } 120 | 121 | err := global.DB.AutoMigrate( 122 | system.User{}, 123 | system.Authority{}, 124 | system.Api{}, 125 | system.BaseMenu{}, 126 | system.BaseMenuParameter{}, 127 | system.JwtBlacklist{}, 128 | example.FileTransfer{}, 129 | example.File{}, 130 | example.FileChunk{}, 131 | example.SimpleUploader{}, 132 | example.Customer{}, 133 | system.Operations{}, 134 | ) 135 | if err != nil { 136 | global.DB = nil 137 | return err 138 | } 139 | err = initDBService.initDB( 140 | source.Admin, 141 | source.Api, 142 | source.AuthorityMenu, 143 | source.Authority, 144 | source.AuthoritiesMenus, 145 | source.Casbin, 146 | source.DataAuthorities, 147 | source.BaseMenu, 148 | source.UserAuthority, 149 | ) 150 | if err != nil { 151 | global.DB = nil 152 | return err 153 | } 154 | if err = initDBService.writeConfig(global.VP, MysqlConfig); err != nil { 155 | return err 156 | } 157 | return nil 158 | } 159 | -------------------------------------------------------------------------------- /service/system/jwt.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/system" 6 | "context" 7 | "errors" 8 | "time" 9 | 10 | "gorm.io/gorm" 11 | ) 12 | 13 | type JwtService struct { 14 | } 15 | 16 | //@author: Flame 17 | //@function: JsonInBlacklist 18 | //@description: 拉黑jwt 19 | //@param: jwtList model.JwtBlacklist 20 | //@return: err error 21 | 22 | func (jwtService *JwtService) JsonInBlacklist(jwtList system.JwtBlacklist) (err error) { 23 | err = global.DB.Create(&jwtList).Error 24 | return 25 | } 26 | 27 | //@author: Flame 28 | //@function: IsBlacklist 29 | //@description: 判断JWT是否在黑名单内部 30 | //@param: jwt string 31 | //@return: bool 32 | 33 | func (jwtService *JwtService) IsBlacklist(jwt string) bool { 34 | err := global.DB.Where("jwt = ?", jwt).First(&system.JwtBlacklist{}).Error 35 | isNotFound := errors.Is(err, gorm.ErrRecordNotFound) 36 | return !isNotFound 37 | } 38 | 39 | //@author: Flame 40 | //@function: GetRedisJWT 41 | //@description: 从redis取jwt 42 | //@param: userName string 43 | //@return: err error, redisJWT string 44 | 45 | func (jwtService *JwtService) GetRedisJWT(userName string) (err error, redisJWT string) { 46 | redisJWT, err = global.REDIS.Get(context.Background(), userName).Result() 47 | return err, redisJWT 48 | } 49 | 50 | //@author: Flame 51 | //@function: SetRedisJWT 52 | //@description: jwt存入redis并设置过期时间 53 | //@param: jwt string, userName string 54 | //@return: err error 55 | 56 | func (jwtService *JwtService) SetRedisJWT(jwt string, userName string) (err error) { 57 | // 此处过期时间等于jwt过期时间 58 | timer := time.Duration(global.CONFIG.JWT.ExpiresTime) * time.Second 59 | err = global.REDIS.Set(context.Background(), userName, jwt, timer).Err() 60 | return err 61 | } 62 | -------------------------------------------------------------------------------- /service/system/menu.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/common/request" 6 | "FiberBoot/model/system" 7 | "errors" 8 | "gorm.io/gorm" 9 | "strconv" 10 | ) 11 | 12 | //@author: Flame 13 | //@function: getMenuTreeMap 14 | //@description: 获取路由总树map 15 | //@param: authorityId string 16 | //@return: err error, treeMap map[string][]model.Menu 17 | 18 | type MenuService struct { 19 | } 20 | 21 | var MenuServiceApp = new(MenuService) 22 | 23 | func (menuService *MenuService) getMenuTreeMap(authorityId string) (err error, treeMap map[string][]system.Menu) { 24 | var allMenus []system.Menu 25 | treeMap = make(map[string][]system.Menu) 26 | err = global.DB.Where("authority_id = ?", authorityId).Order("sort").Preload("Parameters").Find(&allMenus).Error 27 | for _, v := range allMenus { 28 | treeMap[v.ParentId] = append(treeMap[v.ParentId], v) 29 | } 30 | return err, treeMap 31 | } 32 | 33 | //@author: Flame 34 | //@function: GetMenuTree 35 | //@description: 获取动态菜单树 36 | //@param: authorityId string 37 | //@return: err error, menus []model.Menu 38 | 39 | func (menuService *MenuService) GetMenuTree(authorityId string) (err error, menus []system.Menu) { 40 | err, menuTree := menuService.getMenuTreeMap(authorityId) 41 | menus = menuTree["0"] 42 | for i := 0; i < len(menus); i++ { 43 | err = menuService.getChildrenList(&menus[i], menuTree) 44 | } 45 | return err, menus 46 | } 47 | 48 | //@author: Flame 49 | //@function: getChildrenList 50 | //@description: 获取子菜单 51 | //@param: menu *model.Menu, treeMap map[string][]model.Menu 52 | //@return: err error 53 | 54 | func (menuService *MenuService) getChildrenList(menu *system.Menu, treeMap map[string][]system.Menu) (err error) { 55 | menu.Children = treeMap[menu.MenuId] 56 | for i := 0; i < len(menu.Children); i++ { 57 | err = menuService.getChildrenList(&menu.Children[i], treeMap) 58 | } 59 | return err 60 | } 61 | 62 | //@author: Flame 63 | //@function: GetInfoList 64 | //@description: 获取路由分页 65 | //@return: err error, list interface{}, total int64 66 | 67 | func (menuService *MenuService) GetInfoList() (err error, list interface{}, total int64) { 68 | var menuList []system.BaseMenu 69 | err, treeMap := menuService.getBaseMenuTreeMap() 70 | menuList = treeMap["0"] 71 | for i := 0; i < len(menuList); i++ { 72 | err = menuService.getBaseChildrenList(&menuList[i], treeMap) 73 | } 74 | return err, menuList, total 75 | } 76 | 77 | //@author: Flame 78 | //@function: getBaseChildrenList 79 | //@description: 获取菜单的子菜单 80 | //@param: menu *model.BaseMenu, treeMap map[string][]model.BaseMenu 81 | //@return: err error 82 | 83 | func (menuService *MenuService) getBaseChildrenList(menu *system.BaseMenu, treeMap map[string][]system.BaseMenu) (err error) { 84 | menu.Children = treeMap[strconv.Itoa(int(menu.ID))] 85 | for i := 0; i < len(menu.Children); i++ { 86 | err = menuService.getBaseChildrenList(&menu.Children[i], treeMap) 87 | } 88 | return err 89 | } 90 | 91 | //@author: Flame 92 | //@function: AddBaseMenu 93 | //@description: 添加基础路由 94 | //@param: menu model.BaseMenu 95 | //@return: error 96 | 97 | func (menuService *MenuService) AddBaseMenu(menu system.BaseMenu) error { 98 | if !errors.Is(global.DB.Where("name = ?", menu.Name).First(&system.BaseMenu{}).Error, gorm.ErrRecordNotFound) { 99 | return errors.New("存在重复name,请修改name") 100 | } 101 | return global.DB.Create(&menu).Error 102 | } 103 | 104 | //@author: Flame 105 | //@function: getBaseMenuTreeMap 106 | //@description: 获取路由总树map 107 | //@return: err error, treeMap map[string][]model.BaseMenu 108 | 109 | func (menuService *MenuService) getBaseMenuTreeMap() (err error, treeMap map[string][]system.BaseMenu) { 110 | var allMenus []system.BaseMenu 111 | treeMap = make(map[string][]system.BaseMenu) 112 | err = global.DB.Order("sort").Preload("Parameters").Find(&allMenus).Error 113 | for _, v := range allMenus { 114 | treeMap[v.ParentId] = append(treeMap[v.ParentId], v) 115 | } 116 | return err, treeMap 117 | } 118 | 119 | //@author: Flame 120 | //@function: GetBaseMenuTree 121 | //@description: 获取基础路由树 122 | //@return: err error, menus []model.BaseMenu 123 | 124 | func (menuService *MenuService) GetBaseMenuTree() (err error, menus []system.BaseMenu) { 125 | err, treeMap := menuService.getBaseMenuTreeMap() 126 | menus = treeMap["0"] 127 | for i := 0; i < len(menus); i++ { 128 | err = menuService.getBaseChildrenList(&menus[i], treeMap) 129 | } 130 | return err, menus 131 | } 132 | 133 | //@author: Flame 134 | //@function: AddMenuAuthority 135 | //@description: 为角色增加menu树 136 | //@param: menus []model.BaseMenu, authorityId string 137 | //@return: err error 138 | 139 | func (menuService *MenuService) AddMenuAuthority(menus []system.BaseMenu, authorityId string) (err error) { 140 | var auth system.Authority 141 | auth.AuthorityId = authorityId 142 | auth.SysBaseMenus = menus 143 | err = AuthorityServiceApp.SetMenuAuthority(&auth) 144 | return err 145 | } 146 | 147 | //@author: Flame 148 | //@function: GetMenuAuthority 149 | //@description: 查看当前角色树 150 | //@param: info *request.GetAuthorityId 151 | //@return: err error, menus []model.Menu 152 | 153 | func (menuService *MenuService) GetMenuAuthority(info *request.GetAuthorityId) (err error, menus []system.Menu) { 154 | err = global.DB.Where("authority_id = ? ", info.AuthorityId).Order("sort").Find(&menus).Error 155 | //sql := "SELECT authority_menu.keep_alive,authority_menu.default_menu,authority_menu.created_at,authority_menu.updated_at,authority_menu.deleted_at,authority_menu.menu_level,authority_menu.parent_id,authority_menu.path,authority_menu.`name`,authority_menu.hidden,authority_menu.component,authority_menu.title,authority_menu.icon,authority_menu.sort,authority_menu.menu_id,authority_menu.authority_id FROM authority_menu WHERE authority_menu.authority_id = ? ORDER BY authority_menu.sort ASC" 156 | //err = global.DB.Raw(sql, authorityId).Scan(&menus).Error 157 | return err, menus 158 | } 159 | -------------------------------------------------------------------------------- /service/system/operations.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/common/request" 6 | "FiberBoot/model/system" 7 | systemReq "FiberBoot/model/system/request" 8 | ) 9 | 10 | //@author: Flame 11 | //@function: CreateOperations 12 | //@description: 创建记录 13 | //@param: operations model.Operations 14 | //@return: err error 15 | 16 | type OperationsService struct { 17 | } 18 | 19 | func (OperationsService *OperationsService) CreateOperations(operations system.Operations) (err error) { 20 | err = global.DB.Create(&operations).Error 21 | return err 22 | } 23 | 24 | //@author: Flame 25 | //@author: Flame 26 | //@function: DeleteOperationsByIds 27 | //@description: 批量删除记录 28 | //@param: ids request.IdsReq 29 | //@return: err error 30 | 31 | func (OperationsService *OperationsService) DeleteOperationsByIds(ids request.IdsReq) (err error) { 32 | err = global.DB.Delete(&[]system.Operations{}, "id in (?)", ids.Ids).Error 33 | return err 34 | } 35 | 36 | //@author: Flame 37 | //@function: DeleteOperations 38 | //@description: 删除操作记录 39 | //@param: operations model.Operations 40 | //@return: err error 41 | 42 | func (OperationsService *OperationsService) DeleteOperations(operations system.Operations) (err error) { 43 | err = global.DB.Delete(&operations).Error 44 | return err 45 | } 46 | 47 | //@author: Flame 48 | //@function: DeleteOperations 49 | //@description: 根据id获取单条操作记录 50 | //@param: id uint 51 | //@return: err error, operations model.Operations 52 | 53 | func (OperationsService *OperationsService) GetOperations(id uint) (err error, operations system.Operations) { 54 | err = global.DB.Where("id = ?", id).First(&operations).Error 55 | return 56 | } 57 | 58 | //@author: Flame 59 | //@author: Flame 60 | //@function: GetOperationsInfoList 61 | //@description: 分页获取操作记录列表 62 | //@param: info systemReq.OperationsSearch 63 | //@return: err error, list interface{}, total int64 64 | 65 | func (OperationsService *OperationsService) GetOperationsInfoList(info systemReq.OperationsSearch) (err error, list interface{}, total int64) { 66 | limit := info.PageSize 67 | offset := info.PageSize * (info.Page - 1) 68 | // 创建db 69 | db := global.DB.Model(&system.Operations{}) 70 | var operations []system.Operations 71 | // 如果有条件搜索 下方会自动创建搜索语句 72 | if info.Method != "" { 73 | db = db.Where("method = ?", info.Method) 74 | } 75 | if info.Path != "" { 76 | db = db.Where("path LIKE ?", "%"+info.Path+"%") 77 | } 78 | if info.Status != 0 { 79 | db = db.Where("status = ?", info.Status) 80 | } 81 | err = db.Count(&total).Error 82 | err = db.Order("id desc").Limit(limit).Offset(offset).Preload("User").Find(&operations).Error 83 | return err, operations, total 84 | } 85 | -------------------------------------------------------------------------------- /service/system/system.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "FiberBoot/config" 5 | "FiberBoot/global" 6 | "FiberBoot/model/system" 7 | "FiberBoot/utils" 8 | "go.uber.org/zap" 9 | ) 10 | 11 | //@author: Flame 12 | //@function: GetSystemConfig 13 | //@description: 读取配置文件 14 | //@return: err error, conf config.Server 15 | 16 | type ConfigService struct { 17 | } 18 | 19 | func (configService *ConfigService) GetSystemConfig() (err error, conf config.Server) { 20 | return nil, global.CONFIG 21 | } 22 | 23 | // @description set system config, 24 | //@author: Flame 25 | //@function: SetSystemConfig 26 | //@description: 设置配置文件 27 | //@param: system model.System 28 | //@return: err error 29 | 30 | func (configService *ConfigService) SetSystemConfig(system system.System) (err error) { 31 | cs := utils.StructToMap(system.Config) 32 | for k, v := range cs { 33 | global.VP.Set(k, v) 34 | } 35 | err = global.VP.WriteConfig() 36 | return err 37 | } 38 | 39 | //@author: Flame 40 | //@function: GetServerInfo 41 | //@description: 获取服务器信息 42 | //@return: server *utils.Server, err error 43 | 44 | func (configService *ConfigService) GetServerInfo() (server *utils.Server, err error) { 45 | var s utils.Server 46 | s.Os = utils.InitOS() 47 | if s.Cpu, err = utils.InitCPU(); err != nil { 48 | global.LOG.Error("func utils.InitCPU() Failed", zap.String("err", err.Error())) 49 | return &s, err 50 | } 51 | if s.Rrm, err = utils.InitRAM(); err != nil { 52 | global.LOG.Error("func utils.InitRAM() Failed", zap.String("err", err.Error())) 53 | return &s, err 54 | } 55 | if s.Disk, err = utils.InitDisk(); err != nil { 56 | global.LOG.Error("func utils.InitDisk() Failed", zap.String("err", err.Error())) 57 | return &s, err 58 | } 59 | 60 | return &s, nil 61 | } 62 | -------------------------------------------------------------------------------- /source/admin.go: -------------------------------------------------------------------------------- 1 | package source 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/system" 6 | "github.com/gookit/color" 7 | "time" 8 | 9 | uuid "github.com/satori/go.uuid" 10 | "gorm.io/gorm" 11 | ) 12 | 13 | var Admin = new(admin) 14 | 15 | type admin struct{} 16 | 17 | var admins = []system.User{ 18 | {MODEL: global.MODEL{ID: 1, CreatedAt: time.Now(), UpdatedAt: time.Now()}, UUID: uuid.NewV4(), Username: "admin", Password: "e10adc3949ba59abbe56e057f20f883e", NickName: "超级管理员", Avatar: "", AuthorityId: "888"}, 19 | {MODEL: global.MODEL{ID: 2, CreatedAt: time.Now(), UpdatedAt: time.Now()}, UUID: uuid.NewV4(), Username: "manager", Password: "3ec063004a6f31642261936a379fde3d", NickName: "管理员", Avatar: "", AuthorityId: "9528"}, 20 | } 21 | 22 | // Init 23 | //@author: Flame 24 | //@description: users 表数据初始化 25 | func (a *admin) Init() error { 26 | return global.DB.Transaction(func(tx *gorm.DB) error { 27 | if tx.Where("id IN ?", []int{1, 2}).Find(&[]system.User{}).RowsAffected == 2 { 28 | color.Danger.Println("\n[Mysql] --> users 表的初始数据已存在!") 29 | return nil 30 | } 31 | if err := tx.Create(&admins).Error; err != nil { // 遇到错误时回滚事务 32 | return err 33 | } 34 | color.Info.Println("\n[Mysql] --> users 表初始数据成功!") 35 | return nil 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /source/authorities_menus.go: -------------------------------------------------------------------------------- 1 | package source 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "github.com/gookit/color" 6 | "gorm.io/gorm" 7 | ) 8 | 9 | var AuthoritiesMenus = new(authoritiesMenus) 10 | 11 | type authoritiesMenus struct{} 12 | 13 | type AuthorityMenus struct { 14 | AuthorityId string `gorm:"column:authority_authority_id"` 15 | BaseMenuId uint `gorm:"column:base_menu_id"` 16 | } 17 | 18 | var authorityMenus = []AuthorityMenus{ 19 | {"888", 1}, 20 | {"888", 2}, 21 | {"888", 3}, 22 | {"888", 4}, 23 | {"888", 5}, 24 | {"888", 6}, 25 | {"888", 7}, 26 | {"888", 8}, 27 | {"888", 9}, 28 | {"888", 10}, 29 | {"888", 11}, 30 | {"888", 12}, 31 | {"888", 13}, 32 | {"888", 14}, 33 | {"888", 15}, 34 | {"888", 16}, 35 | {"888", 17}, 36 | {"888", 18}, 37 | {"888", 19}, 38 | {"888", 20}, 39 | {"888", 21}, 40 | {"888", 22}, 41 | {"888", 23}, 42 | {"888", 24}, 43 | {"888", 25}, 44 | {"8881", 1}, 45 | {"8881", 2}, 46 | {"8881", 8}, 47 | {"9528", 1}, 48 | {"9528", 2}, 49 | {"9528", 3}, 50 | {"9528", 4}, 51 | {"9528", 5}, 52 | {"9528", 6}, 53 | {"9528", 7}, 54 | {"9528", 8}, 55 | {"9528", 9}, 56 | {"9528", 10}, 57 | {"9528", 11}, 58 | {"9528", 12}, 59 | {"9528", 14}, 60 | {"9528", 15}, 61 | {"9528", 16}, 62 | {"9528", 17}, 63 | } 64 | 65 | // Init @author: Flame 66 | //@description: authority_menus 表数据初始化 67 | func (a *authoritiesMenus) Init() error { 68 | return global.DB.Table("authority_menus").Transaction(func(tx *gorm.DB) error { 69 | if tx.Where("authority_authority_id IN ('888', '8881', '9528')").Find(&[]AuthorityMenus{}).RowsAffected == 48 { 70 | color.Danger.Println("\n[Mysql] --> authority_menus 表的初始数据已存在!") 71 | return nil 72 | } 73 | if err := tx.Create(&authorityMenus).Error; err != nil { // 遇到错误时回滚事务 74 | return err 75 | } 76 | color.Info.Println("\n[Mysql] --> authority_menus 表初始数据成功!") 77 | return nil 78 | }) 79 | } 80 | -------------------------------------------------------------------------------- /source/authority.go: -------------------------------------------------------------------------------- 1 | package source 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/system" 6 | "github.com/gookit/color" 7 | "time" 8 | 9 | "gorm.io/gorm" 10 | ) 11 | 12 | var Authority = new(authority) 13 | 14 | type authority struct{} 15 | 16 | var authorities = []system.Authority{ 17 | {CreatedAt: time.Now(), UpdatedAt: time.Now(), AuthorityId: "888", AuthorityName: "普通用户", ParentId: "0", DefaultRouter: "dashboard"}, 18 | {CreatedAt: time.Now(), UpdatedAt: time.Now(), AuthorityId: "8881", AuthorityName: "普通用户子角色", ParentId: "888", DefaultRouter: "dashboard"}, 19 | {CreatedAt: time.Now(), UpdatedAt: time.Now(), AuthorityId: "9528", AuthorityName: "测试角色", ParentId: "0", DefaultRouter: "dashboard"}, 20 | } 21 | 22 | // Init @author: Flame 23 | //@description: authorities 表数据初始化 24 | func (a *authority) Init() error { 25 | return global.DB.Transaction(func(tx *gorm.DB) error { 26 | if tx.Where("authority_id IN ? ", []string{"888", "9528"}).Find(&[]system.Authority{}).RowsAffected == 2 { 27 | color.Danger.Println("\n[Mysql] --> authorities 表的初始数据已存在!") 28 | return nil 29 | } 30 | if err := tx.Create(&authorities).Error; err != nil { // 遇到错误时回滚事务 31 | return err 32 | } 33 | color.Info.Println("\n[Mysql] --> authorities 表初始数据成功!") 34 | return nil 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /source/authority_menu.go: -------------------------------------------------------------------------------- 1 | package source 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/system" 6 | "github.com/gookit/color" 7 | ) 8 | 9 | var AuthorityMenu = new(authorityMenu) 10 | 11 | type authorityMenu struct{} 12 | 13 | // Init @author: Flame 14 | //@description: authority_menu 视图数据初始化 15 | func (a *authorityMenu) Init() error { 16 | if global.DB.Find(&[]system.Menu{}).RowsAffected > 0 { 17 | color.Danger.Println("\n[Mysql] --> authority_menu 视图已存在!") 18 | return nil 19 | } 20 | if err := global.DB.Exec("CREATE ALGORITHM = UNDEFINED SQL SECURITY DEFINER VIEW `authority_menu` AS select `base_menus`.`id` AS `id`,`base_menus`.`created_at` AS `created_at`, `base_menus`.`updated_at` AS `updated_at`, `base_menus`.`deleted_at` AS `deleted_at`, `base_menus`.`menu_level` AS `menu_level`,`base_menus`.`parent_id` AS `parent_id`,`base_menus`.`path` AS `path`,`base_menus`.`name` AS `name`,`base_menus`.`hidden` AS `hidden`,`base_menus`.`component` AS `component`, `base_menus`.`title` AS `title`,`base_menus`.`icon` AS `icon`,`base_menus`.`sort` AS `sort`,`authority_menus`.`authority_authority_id` AS `authority_id`,`authority_menus`.`base_menu_id` AS `menu_id`,`base_menus`.`keep_alive` AS `keep_alive`,`base_menus`.`close_tab` AS `close_tab`,`base_menus`.`default_menu` AS `default_menu` from (`authority_menus` join `base_menus` on ((`authority_menus`.`base_menu_id` = `base_menus`.`id`)))").Error; err != nil { 21 | return err 22 | } 23 | color.Info.Println("\n[Mysql] --> authority_menu 视图创建成功!") 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /source/data_authorities.go: -------------------------------------------------------------------------------- 1 | package source 2 | 3 | import ( 4 | "FiberBoot/global" 5 | 6 | "github.com/gookit/color" 7 | "gorm.io/gorm" 8 | ) 9 | 10 | var DataAuthorities = new(dataAuthorities) 11 | 12 | type dataAuthorities struct{} 13 | 14 | type DataAuthority struct { 15 | AuthorityId string `gorm:"column:authority_authority_id"` 16 | DataAuthority string `gorm:"column:data_authority_id_authority_id"` 17 | } 18 | 19 | var infos = []DataAuthority{ 20 | {"888", "888"}, 21 | {"888", "8881"}, 22 | {"888", "9528"}, 23 | {"9528", "8881"}, 24 | {"9528", "9528"}, 25 | } 26 | 27 | // Init @author: Flame 28 | //@description: data_authority_id 表数据初始化 29 | func (d *dataAuthorities) Init() error { 30 | return global.DB.Table("data_authority_id").Transaction(func(tx *gorm.DB) error { 31 | if tx.Where("data_authority_id_authority_id IN ('888', '9528') ").Find(&[]DataAuthority{}).RowsAffected == 5 { 32 | color.Danger.Println("\n[Mysql] --> data_authority_id 表初始数据已存在!") 33 | return nil 34 | } 35 | if err := tx.Create(&infos).Error; err != nil { // 遇到错误时回滚事务 36 | return err 37 | } 38 | color.Info.Println("\n[Mysql] --> data_authority_id 表初始数据成功!") 39 | return nil 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /source/menu.go: -------------------------------------------------------------------------------- 1 | package source 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/system" 6 | "github.com/gookit/color" 7 | "time" 8 | 9 | "gorm.io/gorm" 10 | ) 11 | 12 | var BaseMenu = new(menu) 13 | 14 | type menu struct{} 15 | 16 | var menus = []system.BaseMenu{ 17 | {MODEL: global.MODEL{ID: 1, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, ParentId: "0", Path: "dashboard", Name: "dashboard", Hidden: false, Component: "view/dashboard/index.vue", Sort: 1, Meta: system.Meta{Title: "仪表盘", Icon: "setting"}}, 18 | {MODEL: global.MODEL{ID: 2, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, Hidden: false, ParentId: "0", Path: "about", Name: "about", Component: "view/about/index.vue", Sort: 7, Meta: system.Meta{Title: "关于我们", Icon: "info"}}, 19 | {MODEL: global.MODEL{ID: 3, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, Hidden: false, ParentId: "0", Path: "admin", Name: "superAdmin", Component: "view/superAdmin/index.vue", Sort: 3, Meta: system.Meta{Title: "超级管理员", Icon: "user-solid"}}, 20 | {MODEL: global.MODEL{ID: 4, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, Hidden: false, ParentId: "3", Path: "authority", Name: "authority", Component: "view/superAdmin/authority/authority.vue", Sort: 1, Meta: system.Meta{Title: "角色管理", Icon: "s-custom"}}, 21 | {MODEL: global.MODEL{ID: 5, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, Hidden: false, ParentId: "3", Path: "menu", Name: "menu", Component: "view/superAdmin/menu/menu.vue", Sort: 2, Meta: system.Meta{Title: "菜单管理", Icon: "s-order", KeepAlive: true}}, 22 | {MODEL: global.MODEL{ID: 6, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, Hidden: false, ParentId: "3", Path: "api", Name: "api", Component: "view/superAdmin/api/api.vue", Sort: 3, Meta: system.Meta{Title: "api管理", Icon: "s-platform", KeepAlive: true}}, 23 | {MODEL: global.MODEL{ID: 7, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, Hidden: false, ParentId: "3", Path: "user", Name: "user", Component: "view/superAdmin/user/user.vue", Sort: 4, Meta: system.Meta{Title: "用户管理", Icon: "coordinate"}}, 24 | {MODEL: global.MODEL{ID: 8, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, Hidden: true, ParentId: "0", Path: "person", Name: "person", Component: "view/person/person.vue", Sort: 4, Meta: system.Meta{Title: "个人信息", Icon: "message-solid"}}, 25 | {MODEL: global.MODEL{ID: 9, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, Hidden: false, ParentId: "0", Path: "example", Name: "example", Component: "view/example/index.vue", Sort: 6, Meta: system.Meta{Title: "示例文件", Icon: "s-management"}}, 26 | {MODEL: global.MODEL{ID: 10, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, Hidden: false, ParentId: "9", Path: "excel", Name: "excel", Component: "view/example/excel/excel.vue", Sort: 4, Meta: system.Meta{Title: "excel导入导出", Icon: "s-marketing"}}, 27 | {MODEL: global.MODEL{ID: 11, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, Hidden: false, ParentId: "9", Path: "upload", Name: "upload", Component: "view/example/upload/upload.vue", Sort: 5, Meta: system.Meta{Title: "媒体库(上传下载)", Icon: "upload"}}, 28 | {MODEL: global.MODEL{ID: 12, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, Hidden: false, ParentId: "9", Path: "breakpoint", Name: "breakpoint", Component: "view/example/breakpoint/breakpoint.vue", Sort: 6, Meta: system.Meta{Title: "断点续传", Icon: "upload"}}, 29 | {MODEL: global.MODEL{ID: 13, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, Hidden: false, ParentId: "9", Path: "customer", Name: "customer", Component: "view/example/customer/customer.vue", Sort: 7, Meta: system.Meta{Title: "客户列表(资源示例)", Icon: "s-custom"}}, 30 | {MODEL: global.MODEL{ID: 14, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, Hidden: false, ParentId: "0", Path: "systemTools", Name: "systemTools", Component: "view/systemTools/index.vue", Sort: 5, Meta: system.Meta{Title: "系统工具", Icon: "s-cooperation"}}, 31 | 32 | {MODEL: global.MODEL{ID: 16, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, Hidden: false, ParentId: "14", Path: "formCreate", Name: "formCreate", Component: "view/systemTools/formCreate/index.vue", Sort: 2, Meta: system.Meta{Title: "表单生成器", Icon: "magic-stick", KeepAlive: true}}, 33 | {MODEL: global.MODEL{ID: 17, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, Hidden: false, ParentId: "14", Path: "system", Name: "system", Component: "view/systemTools/system/system.vue", Sort: 3, Meta: system.Meta{Title: "系统配置", Icon: "s-operation"}}, 34 | 35 | {MODEL: global.MODEL{ID: 20, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, Hidden: false, ParentId: "3", Path: "operation", Name: "operation", Component: "view/superAdmin/operation/operations.vue", Sort: 6, Meta: system.Meta{Title: "操作历史", Icon: "time"}}, 36 | {MODEL: global.MODEL{ID: 21, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, Hidden: false, ParentId: "9", Path: "simpleUpload", Name: "simpleUpload", Component: "view/example/simpleUpload/simpleUpload", Sort: 6, Meta: system.Meta{Title: "断点续传(插件版)", Icon: "upload"}}, 37 | {MODEL: global.MODEL{ID: 23, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, ParentId: "0", Path: "state", Name: "state", Hidden: false, Component: "view/system/state.vue", Sort: 6, Meta: system.Meta{Title: "服务器状态", Icon: "cloudy"}}, 38 | } 39 | 40 | // Init @author: Flame 41 | //@description: base_menus 表数据初始化 42 | func (m *menu) Init() error { 43 | return global.DB.Transaction(func(tx *gorm.DB) error { 44 | if tx.Where("id IN ?", []int{1, 29}).Find(&[]system.BaseMenu{}).RowsAffected == 2 { 45 | color.Danger.Println("\n[Mysql] --> base_menus 表的初始数据已存在!") 46 | return nil 47 | } 48 | if err := tx.Create(&menus).Error; err != nil { // 遇到错误时回滚事务 49 | return err 50 | } 51 | color.Info.Println("\n[Mysql] --> base_menus 表初始数据成功!") 52 | return nil 53 | }) 54 | } 55 | -------------------------------------------------------------------------------- /source/user_authority.go.go: -------------------------------------------------------------------------------- 1 | package source 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/model/system" 6 | "github.com/gookit/color" 7 | "gorm.io/gorm" 8 | ) 9 | 10 | var UserAuthority = new(userAuthority) 11 | 12 | type userAuthority struct{} 13 | 14 | var userAuthorityModel = []system.UserAuthority{ 15 | {1, "888"}, 16 | {1, "8881"}, 17 | {1, "9528"}, 18 | {2, "888"}, 19 | } 20 | 21 | // Init @description: user_authority 数据初始化 22 | func (a *userAuthority) Init() error { 23 | return global.DB.Model(&system.UserAuthority{}).Transaction(func(tx *gorm.DB) error { 24 | if tx.Where("user_id IN (1, 2)").Find(&[]system.UserAuthority{}).RowsAffected == 4 { 25 | color.Danger.Println("\n[Mysql] --> user_authority 表的初始数据已存在!") 26 | return nil 27 | } 28 | if err := tx.Create(&userAuthorityModel).Error; err != nil { // 遇到错误时回滚事务 29 | return err 30 | } 31 | color.Info.Println("\n[Mysql] --> user_authority 表初始数据成功!") 32 | return nil 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /utils/breakpoint_continue.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "strconv" 7 | ) 8 | 9 | // 前端传来文件片与当前片为什么文件的第几片 10 | // 后端拿到以后比较次分片是否上传 或者是否为不完全片 11 | // 前端发送每片多大 12 | // 前端告知是否为最后一片且是否完成 13 | 14 | const breakpointDir = "./breakpointDir/" 15 | const finishDir = "./fileDir/" 16 | 17 | //@author: Flame 18 | //@function: BreakPointContinue 19 | //@description: 断点续传 20 | //@param: content []byte, fileName string, contentNumber int, contentTotal int, fileMd5 string 21 | //@return: error, string 22 | 23 | func BreakPointContinue(content []byte, fileName string, contentNumber int, fileMd5 string) (error, string) { 24 | path := breakpointDir + fileMd5 + "/" 25 | err := os.MkdirAll(path, os.ModePerm) 26 | if err != nil { 27 | return err, path 28 | } 29 | err, patch := makeFileContent(content, fileName, path, contentNumber) 30 | return err, patch 31 | 32 | } 33 | 34 | //@author: Flame 35 | //@function: CheckMd5 36 | //@description: 检查Md5 37 | //@param: content []byte, chunkMd5 string 38 | //@return: CanUpload bool 39 | 40 | func CheckMd5(content []byte, chunkMd5 string) (CanUpload bool) { 41 | fileMd5 := MD5V(content) 42 | if fileMd5 == chunkMd5 { 43 | return true // 可以继续上传 44 | } else { 45 | return false // 切片不完整,废弃 46 | } 47 | } 48 | 49 | //@author: Flame 50 | //@function: makeFileContent 51 | //@description: 创建切片内容 52 | //@param: content []byte, fileName string, FileDir string, contentNumber int 53 | //@return: error, string 54 | 55 | func makeFileContent(content []byte, fileName string, FileDir string, contentNumber int) (error, string) { 56 | path := FileDir + fileName + "_" + strconv.Itoa(contentNumber) 57 | f, err := os.Create(path) 58 | if err != nil { 59 | return err, path 60 | } else { 61 | _, err = f.Write(content) 62 | if err != nil { 63 | return err, path 64 | } 65 | } 66 | defer func(f *os.File) { 67 | _ = f.Close() 68 | }(f) 69 | return nil, path 70 | } 71 | 72 | //@author: Flame 73 | //@function: makeFileContent 74 | //@description: 创建切片文件 75 | //@param: fileName string, FileMd5 string 76 | //@return: error, string 77 | 78 | func MakeFile(fileName string, FileMd5 string) (error, string) { 79 | rd, err := ioutil.ReadDir(breakpointDir + FileMd5) 80 | if err != nil { 81 | return err, finishDir + fileName 82 | } 83 | _ = os.MkdirAll(finishDir, os.ModePerm) 84 | fd, err := os.OpenFile(finishDir+fileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644) 85 | if err != nil { 86 | return err, finishDir + fileName 87 | } 88 | defer func(fd *os.File) { 89 | _ = fd.Close() 90 | }(fd) 91 | for k := range rd { 92 | content, _ := ioutil.ReadFile(breakpointDir + FileMd5 + "/" + fileName + "_" + strconv.Itoa(k)) 93 | _, err = fd.Write(content) 94 | if err != nil { 95 | _ = os.Remove(finishDir + fileName) 96 | return err, finishDir + fileName 97 | } 98 | } 99 | return nil, finishDir + fileName 100 | } 101 | 102 | //@author: Flame 103 | //@function: RemoveChunk 104 | //@description: 移除切片 105 | //@param: FileMd5 string 106 | //@return: error 107 | 108 | func RemoveChunk(FileMd5 string) error { 109 | err := os.RemoveAll(breakpointDir + FileMd5) 110 | return err 111 | } 112 | -------------------------------------------------------------------------------- /utils/captcha/redis.go: -------------------------------------------------------------------------------- 1 | package captcha 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "context" 6 | "time" 7 | 8 | "github.com/mojocn/base64Captcha" 9 | "go.uber.org/zap" 10 | ) 11 | 12 | func NewDefaultRedisStore() base64Captcha.Store { 13 | return &RedisStore{ 14 | Expiration: time.Second * 180, 15 | PreKey: "CAPTCHA_", 16 | } 17 | } 18 | 19 | type RedisStore struct { 20 | Expiration time.Duration 21 | PreKey string 22 | Context context.Context 23 | } 24 | 25 | func (rs *RedisStore) UseWithCtx(ctx context.Context) base64Captcha.Store { 26 | rs.Context = ctx 27 | return rs 28 | } 29 | func (rs *RedisStore) Set(id string, value string) error { 30 | err := global.REDIS.Set(rs.Context, rs.PreKey+id, value, rs.Expiration).Err() 31 | if err != nil { 32 | global.LOG.Error("RedisStoreSetError!", zap.Error(err)) 33 | return err 34 | } 35 | return nil 36 | } 37 | 38 | func (rs *RedisStore) Get(key string, clear bool) string { 39 | val, err := global.REDIS.Get(rs.Context, key).Result() 40 | if err != nil { 41 | global.LOG.Error("RedisStoreGetError!", zap.Error(err)) 42 | return "" 43 | } 44 | if clear { 45 | err := global.REDIS.Del(rs.Context, key).Err() 46 | if err != nil { 47 | global.LOG.Error("RedisStoreClearError!", zap.Error(err)) 48 | return "" 49 | } 50 | } 51 | return val 52 | } 53 | 54 | func (rs *RedisStore) Verify(id, answer string, clear bool) bool { 55 | key := rs.PreKey + id 56 | v := rs.Get(key, clear) 57 | return v == answer 58 | } 59 | -------------------------------------------------------------------------------- /utils/clamis.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "FiberBoot/global" 5 | systemReq "FiberBoot/model/system/request" 6 | "github.com/gofiber/fiber/v2" 7 | uuid "github.com/satori/go.uuid" 8 | ) 9 | 10 | // GetUserID 从Gin的Context中获取从jwt解析出来的用户ID 11 | func GetUserID(c *fiber.Ctx) uint { 12 | if claims := c.Locals("claims"); claims == nil { 13 | global.LOG.Error("从Gin的Context中获取从jwt解析出来的用户ID失败, 请检查路由是否使用jwt中间件!") 14 | return 0 15 | } else { 16 | waitUse := claims.(*systemReq.CustomClaims) 17 | return waitUse.ID 18 | } 19 | } 20 | 21 | // GetUserUuid 从Gin的Context中获取从jwt解析出来的用户UUID 22 | func GetUserUuid(c *fiber.Ctx) uuid.UUID { 23 | if claims := c.Locals("claims"); claims == nil { 24 | global.LOG.Error("从Gin的Context中获取从jwt解析出来的用户UUID失败, 请检查路由是否使用jwt中间件!") 25 | return uuid.UUID{} 26 | } else { 27 | waitUse := claims.(*systemReq.CustomClaims) 28 | return waitUse.UUID 29 | } 30 | } 31 | 32 | // GetUserAuthorityId 从Gin的Context中获取从jwt解析出来的用户角色id 33 | func GetUserAuthorityId(c *fiber.Ctx) string { 34 | if claims := c.Locals("claims"); claims == nil { 35 | global.LOG.Error("从Gin的Context中获取从jwt解析出来的用户UUID失败, 请检查路由是否使用jwt中间件!") 36 | return "" 37 | } else { 38 | waitUse := claims.(*systemReq.CustomClaims) 39 | return waitUse.AuthorityId 40 | } 41 | } 42 | 43 | // GetUserInfo 从Gin的Context中获取从jwt解析出来的用户角色id 44 | func GetUserInfo(c *fiber.Ctx) *systemReq.CustomClaims { 45 | if claims := c.Locals("claims"); claims == nil { 46 | global.LOG.Error("从Gin的Context中获取从jwt解析出来的用户UUID失败, 请检查路由是否使用jwt中间件!") 47 | return nil 48 | } else { 49 | waitUse := claims.(*systemReq.CustomClaims) 50 | return waitUse 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /utils/constant.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | const ( 4 | ConfigEnv = "CONFIG" 5 | ConfigFile = "config.yaml" 6 | ) 7 | -------------------------------------------------------------------------------- /utils/dbOperations.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "time" 7 | 8 | "gorm.io/gorm" 9 | ) 10 | 11 | //@author: Flame 12 | //@function: ClearTable 13 | //@description: 清理数据库表数据 14 | //@param: db(数据库对象) *gorm.DB, tableName(表名) string, compareField(比较字段) string, interval(间隔) string 15 | //@return: error 16 | 17 | func ClearTable(db *gorm.DB, tableName string, compareField string, interval string) error { 18 | if db == nil { 19 | return errors.New("db Cannot be empty") 20 | } 21 | duration, err := time.ParseDuration(interval) 22 | if err != nil { 23 | return err 24 | } 25 | if duration < 0 { 26 | return errors.New("parse duration < 0") 27 | } 28 | return db.Debug().Exec(fmt.Sprintf("DELETE FROM %s WHERE %s < ?", tableName, compareField), time.Now().Add(-duration)).Error 29 | } 30 | -------------------------------------------------------------------------------- /utils/directory.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "go.uber.org/zap" 6 | "os" 7 | ) 8 | 9 | //@author: Flame 10 | //@function: PathExists 11 | //@description: 文件目录是否存在 12 | //@param: path string 13 | //@return: bool, error 14 | 15 | func PathExists(path string) (bool, error) { 16 | _, err := os.Stat(path) 17 | if err == nil { 18 | return true, nil 19 | } 20 | if os.IsNotExist(err) { 21 | return false, nil 22 | } 23 | return false, err 24 | } 25 | 26 | //@author: Flame 27 | //@function: CreateDir 28 | //@description: 批量创建文件夹 29 | //@param: dirs ...string 30 | //@return: err error 31 | 32 | func CreateDir(dirs ...string) (err error) { 33 | for _, v := range dirs { 34 | exist, err := PathExists(v) 35 | if err != nil { 36 | return err 37 | } 38 | if !exist { 39 | global.LOG.Debug("create directory" + v) 40 | if err := os.MkdirAll(v, os.ModePerm); err != nil { 41 | global.LOG.Error("create directory"+v, zap.Any(" error:", err)) 42 | return err 43 | } 44 | } 45 | } 46 | return err 47 | } 48 | -------------------------------------------------------------------------------- /utils/email.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "crypto/tls" 5 | "fmt" 6 | "net/smtp" 7 | "strings" 8 | 9 | "FiberBoot/global" 10 | 11 | "github.com/jordan-wright/email" 12 | ) 13 | 14 | //@author: Flame 15 | //@function: Email 16 | //@description: Email发送方法 17 | //@param: subject string, body string 18 | //@return: error 19 | 20 | func Email(subject string, body string) error { 21 | to := strings.Split(global.CONFIG.Email.To, ",") 22 | return send(to, subject, body) 23 | } 24 | 25 | //@author: Flame 26 | //@function: ErrorToEmail 27 | //@description: 给email中间件错误发送邮件到指定邮箱 28 | //@param: subject string, body string 29 | //@return: error 30 | 31 | func ErrorToEmail(subject string, body string) error { 32 | to := strings.Split(global.CONFIG.Email.To, ",") 33 | if to[len(to)-1] == "" { // 判断切片的最后一个元素是否为空,为空则移除 34 | to = to[:len(to)-1] 35 | } 36 | return send(to, subject, body) 37 | } 38 | 39 | //@author: Flame 40 | //@function: EmailTest 41 | //@description: Email测试方法 42 | //@param: subject string, body string 43 | //@return: error 44 | 45 | func EmailTest(subject string, body string) error { 46 | to := []string{global.CONFIG.Email.From} 47 | return send(to, subject, body) 48 | } 49 | 50 | //@author: Flame 51 | //@function: send 52 | //@description: Email发送方法 53 | //@param: subject string, body string 54 | //@return: error 55 | 56 | func send(to []string, subject string, body string) error { 57 | from := global.CONFIG.Email.From 58 | nickname := global.CONFIG.Email.Nickname 59 | secret := global.CONFIG.Email.Secret 60 | host := global.CONFIG.Email.Host 61 | port := global.CONFIG.Email.Port 62 | isSSL := global.CONFIG.Email.IsSSL 63 | 64 | auth := smtp.PlainAuth("", from, secret, host) 65 | e := email.NewEmail() 66 | if nickname != "" { 67 | e.From = fmt.Sprintf("%s <%s>", nickname, from) 68 | } else { 69 | e.From = from 70 | } 71 | e.To = to 72 | e.Subject = subject 73 | e.HTML = []byte(body) 74 | var err error 75 | hostAddr := fmt.Sprintf("%s:%d", host, port) 76 | if isSSL { 77 | err = e.SendWithTLS(hostAddr, auth, &tls.Config{ServerName: host}) 78 | } else { 79 | err = e.Send(hostAddr, auth) 80 | } 81 | return err 82 | } 83 | -------------------------------------------------------------------------------- /utils/files.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "reflect" 7 | "strings" 8 | ) 9 | 10 | //@author: Flame 11 | //@function: FileMove 12 | //@description: 文件移动供外部调用 13 | //@param: src string, dst string(src: 源位置,绝对路径or相对路径, dst: 目标位置,绝对路径or相对路径,必须为文件夹) 14 | //@return: err error 15 | 16 | func FileMove(src string, dst string) (err error) { 17 | if dst == "" { 18 | return nil 19 | } 20 | src, err = filepath.Abs(src) 21 | if err != nil { 22 | return err 23 | } 24 | dst, err = filepath.Abs(dst) 25 | if err != nil { 26 | return err 27 | } 28 | var revoke = false 29 | dir := filepath.Dir(dst) 30 | Redirect: 31 | _, err = os.Stat(dir) 32 | if err != nil { 33 | err = os.MkdirAll(dir, 0755) 34 | if err != nil { 35 | return err 36 | } 37 | if !revoke { 38 | revoke = true 39 | goto Redirect 40 | } 41 | } 42 | return os.Rename(src, dst) 43 | } 44 | 45 | func DeLFile(filePath string) error { 46 | return os.RemoveAll(filePath) 47 | } 48 | 49 | //@author: Flame 50 | //@function: TrimSpace 51 | //@description: 去除结构体空格 52 | //@param: target interface (target: 目标结构体,传入必须是指针类型) 53 | //@return: null 54 | 55 | func TrimSpace(target interface{}) { 56 | t := reflect.TypeOf(target) 57 | if t.Kind() != reflect.Ptr { 58 | return 59 | } 60 | t = t.Elem() 61 | v := reflect.ValueOf(target).Elem() 62 | for i := 0; i < t.NumField(); i++ { 63 | switch v.Field(i).Kind() { 64 | case reflect.String: 65 | v.Field(i).SetString(strings.TrimSpace(v.Field(i).String())) 66 | } 67 | } 68 | return 69 | } 70 | -------------------------------------------------------------------------------- /utils/formate.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "strings" 7 | ) 8 | 9 | //@author: Flame 10 | //@function: StructToMap 11 | //@description: 利用反射将结构体转化为map 12 | //@param: obj interface{} 13 | //@return: map[string]interface{} 14 | 15 | func StructToMap(obj interface{}) map[string]interface{} { 16 | obj1 := reflect.TypeOf(obj) 17 | obj2 := reflect.ValueOf(obj) 18 | 19 | var data = make(map[string]interface{}) 20 | for i := 0; i < obj1.NumField(); i++ { 21 | if obj1.Field(i).Tag.Get("mapstructure") != "" { 22 | data[obj1.Field(i).Tag.Get("mapstructure")] = obj2.Field(i).Interface() 23 | } else { 24 | data[obj1.Field(i).Name] = obj2.Field(i).Interface() 25 | } 26 | } 27 | return data 28 | } 29 | 30 | //@author: Flame 31 | //@function: ArrayToString 32 | //@description: 将数组格式化为字符串 33 | //@param: array []interface{} 34 | //@return: string 35 | 36 | func ArrayToString(array []interface{}) string { 37 | return strings.Replace(strings.Trim(fmt.Sprint(array), "[]"), " ", ",", -1) 38 | } 39 | -------------------------------------------------------------------------------- /utils/md5.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | ) 7 | 8 | //@author: Flame 9 | //@function: MD5V 10 | //@description: md5加密 11 | //@param: str []byte 12 | //@return: string 13 | 14 | func MD5V(str []byte) string { 15 | h := md5.New() 16 | h.Write(str) 17 | return hex.EncodeToString(h.Sum(nil)) 18 | } 19 | -------------------------------------------------------------------------------- /utils/reload.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | "os/exec" 7 | "runtime" 8 | "strconv" 9 | ) 10 | 11 | func Reload() error { 12 | if runtime.GOOS == "windows" { 13 | return errors.New("系统不支持") 14 | } 15 | pid := os.Getpid() 16 | cmd := exec.Command("kill", "-1", strconv.Itoa(pid)) 17 | return cmd.Run() 18 | } 19 | -------------------------------------------------------------------------------- /utils/rotatelogs_unix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package utils 4 | 5 | import ( 6 | "FiberBoot/global" 7 | zapRotateLogs "github.com/lestrrat-go/file-rotatelogs" 8 | "go.uber.org/zap/zapcore" 9 | "os" 10 | "path" 11 | "time" 12 | ) 13 | 14 | //@author: Flame 15 | //@function: GetWriteSyncer 16 | //@description: zap logger中加入file-RotateLogs 17 | //@return: zapcore.WriteSyncer, error 18 | 19 | func GetWriteSyncer() (zapcore.WriteSyncer, error) { 20 | fileWriter, err := zapRotateLogs.New( 21 | path.Join(global.CONFIG.Zap.Director, "%Y-%m-%d.log"), 22 | zapRotateLogs.WithLinkName(global.CONFIG.Zap.LinkName), 23 | zapRotateLogs.WithMaxAge(7*24*time.Hour), 24 | zapRotateLogs.WithRotationTime(24*time.Hour), 25 | ) 26 | if global.CONFIG.Zap.LogInConsole { 27 | return zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(fileWriter)), err 28 | } 29 | return zapcore.AddSync(fileWriter), err 30 | } 31 | -------------------------------------------------------------------------------- /utils/rotatelogs_windows.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "FiberBoot/global" 5 | zapRotateLogs "github.com/lestrrat-go/file-rotatelogs" 6 | "go.uber.org/zap/zapcore" 7 | "os" 8 | "path" 9 | "time" 10 | ) 11 | 12 | //@author: Flame 13 | //@function: GetWriteSyncer 14 | //@description: zap logger中加入file-RotateLogs 15 | //@return: zapcore.WriteSyncer, error 16 | 17 | func GetWriteSyncer() (zapcore.WriteSyncer, error) { 18 | fileWriter, err := zapRotateLogs.New( 19 | path.Join(global.CONFIG.Zap.Director, "%Y-%m-%d.log"), 20 | zapRotateLogs.WithMaxAge(7*24*time.Hour), 21 | zapRotateLogs.WithRotationTime(24*time.Hour), 22 | ) 23 | if global.CONFIG.Zap.LogInConsole { 24 | return zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(fileWriter)), err 25 | } 26 | return zapcore.AddSync(fileWriter), err 27 | } 28 | -------------------------------------------------------------------------------- /utils/server.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/shirou/gopsutil/v3/cpu" 5 | "github.com/shirou/gopsutil/v3/disk" 6 | "github.com/shirou/gopsutil/v3/mem" 7 | "runtime" 8 | "time" 9 | ) 10 | 11 | const ( 12 | B = 1 13 | KB = 1024 * B 14 | MB = 1024 * KB 15 | GB = 1024 * MB 16 | ) 17 | 18 | type Server struct { 19 | Os Os `json:"os"` 20 | Cpu Cpu `json:"cpu"` 21 | Rrm Rrm `json:"ram"` 22 | Disk Disk `json:"disk"` 23 | } 24 | 25 | type Os struct { 26 | GOOS string `json:"goos"` 27 | NumCPU int `json:"numCpu"` 28 | Compiler string `json:"compiler"` 29 | GoVersion string `json:"goVersion"` 30 | NumGoroutine int `json:"numGoroutine"` 31 | } 32 | 33 | type Cpu struct { 34 | Cpus []float64 `json:"cpus"` 35 | Cores int `json:"cores"` 36 | } 37 | 38 | type Rrm struct { 39 | UsedMB int `json:"usedMb"` 40 | TotalMB int `json:"totalMb"` 41 | UsedPercent int `json:"usedPercent"` 42 | } 43 | 44 | type Disk struct { 45 | UsedMB int `json:"usedMb"` 46 | UsedGB int `json:"usedGb"` 47 | TotalMB int `json:"totalMb"` 48 | TotalGB int `json:"totalGb"` 49 | UsedPercent int `json:"usedPercent"` 50 | } 51 | 52 | //@author: Flame 53 | //@function: InitCPU 54 | //@description: OS信息 55 | //@return: o Os, err error 56 | 57 | func InitOS() (o Os) { 58 | o.GOOS = runtime.GOOS 59 | o.NumCPU = runtime.NumCPU() 60 | o.Compiler = runtime.Compiler 61 | o.GoVersion = runtime.Version() 62 | o.NumGoroutine = runtime.NumGoroutine() 63 | return o 64 | } 65 | 66 | //@author: Flame 67 | //@function: InitCPU 68 | //@description: CPU信息 69 | //@return: c Cpu, err error 70 | 71 | func InitCPU() (c Cpu, err error) { 72 | if cores, err := cpu.Counts(false); err != nil { 73 | return c, err 74 | } else { 75 | c.Cores = cores 76 | } 77 | if cpus, err := cpu.Percent(time.Duration(200)*time.Millisecond, true); err != nil { 78 | return c, err 79 | } else { 80 | c.Cpus = cpus 81 | } 82 | return c, nil 83 | } 84 | 85 | //@author: Flame 86 | //@function: InitRAM 87 | //@description: ARM信息 88 | //@return: r Rrm, err error 89 | 90 | func InitRAM() (r Rrm, err error) { 91 | if u, err := mem.VirtualMemory(); err != nil { 92 | return r, err 93 | } else { 94 | r.UsedMB = int(u.Used) / MB 95 | r.TotalMB = int(u.Total) / MB 96 | r.UsedPercent = int(u.UsedPercent) 97 | } 98 | return r, nil 99 | } 100 | 101 | //@author: Flame 102 | //@function: InitDisk 103 | //@description: 硬盘信息 104 | //@return: d Disk, err error 105 | 106 | func InitDisk() (d Disk, err error) { 107 | if u, err := disk.Usage("/"); err != nil { 108 | return d, err 109 | } else { 110 | d.UsedMB = int(u.Used) / MB 111 | d.UsedGB = int(u.Used) / GB 112 | d.TotalMB = int(u.Total) / MB 113 | d.TotalGB = int(u.Total) / GB 114 | d.UsedPercent = int(u.UsedPercent) 115 | } 116 | return d, nil 117 | } 118 | -------------------------------------------------------------------------------- /utils/timer/timed_task.go: -------------------------------------------------------------------------------- 1 | package timer 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/robfig/cron/v3" 7 | ) 8 | 9 | type Timer interface { 10 | AddTaskByFunc(taskName string, spec string, task func()) (cron.EntryID, error) 11 | AddTaskByJob(taskName string, spec string, job interface{ Run() }) (cron.EntryID, error) 12 | FindCron(taskName string) (*cron.Cron, bool) 13 | StartTask(taskName string) 14 | StopTask(taskName string) 15 | Remove(taskName string, id int) 16 | Clear(taskName string) 17 | Close() 18 | } 19 | 20 | // timer 定时任务管理 21 | type timer struct { 22 | taskList map[string]*cron.Cron 23 | sync.Mutex 24 | } 25 | 26 | // AddTaskByFunc 通过函数的方法添加任务 27 | func (t *timer) AddTaskByFunc(taskName string, spec string, task func()) (cron.EntryID, error) { 28 | t.Lock() 29 | defer t.Unlock() 30 | if _, ok := t.taskList[taskName]; !ok { 31 | t.taskList[taskName] = cron.New() 32 | } 33 | id, err := t.taskList[taskName].AddFunc(spec, task) 34 | t.taskList[taskName].Start() 35 | return id, err 36 | } 37 | 38 | // AddTaskByJob 通过接口的方法添加任务 39 | func (t *timer) AddTaskByJob(taskName string, spec string, job interface{ Run() }) (cron.EntryID, error) { 40 | t.Lock() 41 | defer t.Unlock() 42 | if _, ok := t.taskList[taskName]; !ok { 43 | t.taskList[taskName] = cron.New() 44 | } 45 | id, err := t.taskList[taskName].AddJob(spec, job) 46 | t.taskList[taskName].Start() 47 | return id, err 48 | } 49 | 50 | // FindCron 获取对应taskName的cron 可能会为空 51 | func (t *timer) FindCron(taskName string) (*cron.Cron, bool) { 52 | t.Lock() 53 | defer t.Unlock() 54 | v, ok := t.taskList[taskName] 55 | return v, ok 56 | } 57 | 58 | // StartTask 开始任务 59 | func (t *timer) StartTask(taskName string) { 60 | t.Lock() 61 | defer t.Unlock() 62 | if v, ok := t.taskList[taskName]; ok { 63 | v.Start() 64 | } 65 | return 66 | } 67 | 68 | // StopTask 停止任务 69 | func (t *timer) StopTask(taskName string) { 70 | t.Lock() 71 | defer t.Unlock() 72 | if v, ok := t.taskList[taskName]; ok { 73 | v.Stop() 74 | } 75 | return 76 | } 77 | 78 | // Remove 从taskName 删除指定任务 79 | func (t *timer) Remove(taskName string, id int) { 80 | t.Lock() 81 | defer t.Unlock() 82 | if v, ok := t.taskList[taskName]; ok { 83 | v.Remove(cron.EntryID(id)) 84 | } 85 | return 86 | } 87 | 88 | // Clear 清除任务 89 | func (t *timer) Clear(taskName string) { 90 | t.Lock() 91 | defer t.Unlock() 92 | if v, ok := t.taskList[taskName]; ok { 93 | v.Stop() 94 | delete(t.taskList, taskName) 95 | } 96 | } 97 | 98 | // Close 释放资源 99 | func (t *timer) Close() { 100 | t.Lock() 101 | defer t.Unlock() 102 | for _, v := range t.taskList { 103 | v.Stop() 104 | } 105 | } 106 | 107 | func NewTimerTask() Timer { 108 | return &timer{taskList: make(map[string]*cron.Cron)} 109 | } 110 | -------------------------------------------------------------------------------- /utils/upload/aliyun_oss.go: -------------------------------------------------------------------------------- 1 | package upload 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "errors" 6 | "github.com/aliyun/aliyun-oss-go-sdk/oss" 7 | "go.uber.org/zap" 8 | "mime/multipart" 9 | "time" 10 | ) 11 | 12 | type AliyunOSS struct{} 13 | 14 | func (*AliyunOSS) UploadFile(file *multipart.FileHeader) (string, string, error) { 15 | bucket, err := NewBucket() 16 | if err != nil { 17 | global.LOG.Error("function AliyunOSS.NewBucket() Failed", zap.Any("err", err.Error())) 18 | return "", "", errors.New("function AliyunOSS.NewBucket() Failed, err:" + err.Error()) 19 | } 20 | 21 | // 读取本地文件。 22 | f, openError := file.Open() 23 | if openError != nil { 24 | global.LOG.Error("function file.Open() Failed", zap.Any("err", openError.Error())) 25 | return "", "", errors.New("function file.Open() Failed, err:" + openError.Error()) 26 | } 27 | defer func(f multipart.File) { 28 | _ = f.Close() 29 | }(f) // 创建文件 defer 关闭 30 | // 上传阿里云路径 文件名格式 自己可以改 建议保证唯一性 31 | //yunFileTmpPath := filepath.Join("uploads", time.Now().Format("2006-01-02")) + "/" + file.Filename 32 | yunFileTmpPath := global.CONFIG.AliyunOSS.BasePath + "/" + "uploads" + "/" + time.Now().Format("2006-01-02") + "/" + file.Filename 33 | 34 | // 上传文件流。 35 | err = bucket.PutObject(yunFileTmpPath, f) 36 | if err != nil { 37 | global.LOG.Error("function formUploader.Put() Failed", zap.Any("err", err.Error())) 38 | return "", "", errors.New("function formUploader.Put() Failed, err:" + err.Error()) 39 | } 40 | 41 | return global.CONFIG.AliyunOSS.BucketUrl + "/" + yunFileTmpPath, yunFileTmpPath, nil 42 | } 43 | 44 | func (*AliyunOSS) DeleteFile(key string) error { 45 | bucket, err := NewBucket() 46 | if err != nil { 47 | global.LOG.Error("function AliyunOSS.NewBucket() Failed", zap.Any("err", err.Error())) 48 | return errors.New("function AliyunOSS.NewBucket() Failed, err:" + err.Error()) 49 | } 50 | 51 | // 删除单个文件。objectName表示删除OSS文件时需要指定包含文件后缀在内的完整路径,例如abc/efg/123.jpg。 52 | // 如需删除文件夹,请将objectName设置为对应的文件夹名称。如果文件夹非空,则需要将文件夹下的所有object删除后才能删除该文件夹。 53 | err = bucket.DeleteObject(key) 54 | if err != nil { 55 | global.LOG.Error("function bucketManager.Delete() Filed", zap.Any("err", err.Error())) 56 | return errors.New("function bucketManager.Delete() Filed, err:" + err.Error()) 57 | } 58 | 59 | return nil 60 | } 61 | 62 | func NewBucket() (*oss.Bucket, error) { 63 | // 创建OSSClient实例。 64 | client, err := oss.New(global.CONFIG.AliyunOSS.Endpoint, global.CONFIG.AliyunOSS.AccessKeyId, global.CONFIG.AliyunOSS.AccessKeySecret) 65 | if err != nil { 66 | return nil, err 67 | } 68 | 69 | // 获取存储空间。 70 | bucket, err := client.Bucket(global.CONFIG.AliyunOSS.BucketName) 71 | if err != nil { 72 | return nil, err 73 | } 74 | 75 | return bucket, nil 76 | } 77 | -------------------------------------------------------------------------------- /utils/upload/local.go: -------------------------------------------------------------------------------- 1 | package upload 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "FiberBoot/utils" 6 | "errors" 7 | "go.uber.org/zap" 8 | "io" 9 | "mime/multipart" 10 | "os" 11 | "path" 12 | "strings" 13 | "time" 14 | ) 15 | 16 | type Local struct{} 17 | 18 | //@author: Flame 19 | //@object: *Local 20 | //@function: UploadFile 21 | //@description: 上传文件 22 | //@param: file *multipart.FileHeader 23 | //@return: string, string, error 24 | 25 | func (*Local) UploadFile(file *multipart.FileHeader) (string, string, error) { 26 | // 读取文件后缀 27 | ext := path.Ext(file.Filename) 28 | // 读取文件名并加密 29 | name := strings.TrimSuffix(file.Filename, ext) 30 | name = utils.MD5V([]byte(name)) 31 | // 拼接新文件名 32 | filename := name + "_" + time.Now().Format("20060102150405") + ext 33 | // 尝试创建此路径 34 | mkdirErr := os.MkdirAll(global.CONFIG.Local.Path, os.ModePerm) 35 | if mkdirErr != nil { 36 | global.LOG.Error("function os.MkdirAll() Filed", zap.Any("err", mkdirErr.Error())) 37 | return "", "", errors.New("function os.MkdirAll() Filed, err:" + mkdirErr.Error()) 38 | } 39 | // 拼接路径和文件名 40 | p := global.CONFIG.Local.Path + "/" + filename 41 | 42 | f, openError := file.Open() // 读取文件 43 | if openError != nil { 44 | global.LOG.Error("function file.Open() Filed", zap.Any("err", openError.Error())) 45 | return "", "", errors.New("function file.Open() Filed, err:" + openError.Error()) 46 | } 47 | defer func(f multipart.File) { 48 | _ = f.Close() 49 | }(f) // 创建文件 defer 关闭 50 | 51 | out, createErr := os.Create(p) 52 | if createErr != nil { 53 | global.LOG.Error("function os.Create() Filed", zap.Any("err", createErr.Error())) 54 | 55 | return "", "", errors.New("function os.Create() Filed, err:" + createErr.Error()) 56 | } 57 | defer func(out *os.File) { 58 | _ = out.Close() 59 | }(out) // 创建文件 defer 关闭 60 | 61 | _, copyErr := io.Copy(out, f) // 传输(拷贝)文件 62 | if copyErr != nil { 63 | global.LOG.Error("function io.Copy() Filed", zap.Any("err", copyErr.Error())) 64 | return "", "", errors.New("function io.Copy() Filed, err:" + copyErr.Error()) 65 | } 66 | return p, filename, nil 67 | } 68 | 69 | // DeleteFile 70 | //@author: Flame 71 | //@object: *Local 72 | //@function: DeleteFile 73 | //@description: 删除文件 74 | //@param: key string 75 | //@return: error 76 | func (*Local) DeleteFile(key string) error { 77 | p := global.CONFIG.Local.Path + "/" + key 78 | if strings.Contains(p, global.CONFIG.Local.Path) { 79 | if err := os.Remove(p); err != nil { 80 | return errors.New("本地文件删除失败, err:" + err.Error()) 81 | } 82 | } 83 | return nil 84 | } 85 | -------------------------------------------------------------------------------- /utils/upload/qiniu.go: -------------------------------------------------------------------------------- 1 | package upload 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "context" 6 | "errors" 7 | "fmt" 8 | "github.com/qiniu/go-sdk/v7/auth/qbox" 9 | "github.com/qiniu/go-sdk/v7/storage" 10 | "go.uber.org/zap" 11 | "mime/multipart" 12 | "time" 13 | ) 14 | 15 | type Qiniu struct{} 16 | 17 | //@author: Flame 18 | //@object: *Qiniu 19 | //@function: UploadFile 20 | //@description: 上传文件 21 | //@param: file *multipart.FileHeader 22 | //@return: string, string, error 23 | 24 | func (*Qiniu) UploadFile(file *multipart.FileHeader) (string, string, error) { 25 | putPolicy := storage.PutPolicy{Scope: global.CONFIG.Qiniu.Bucket} 26 | mac := qbox.NewMac(global.CONFIG.Qiniu.AccessKey, global.CONFIG.Qiniu.SecretKey) 27 | upToken := putPolicy.UploadToken(mac) 28 | cfg := qiniuConfig() 29 | formUploader := storage.NewFormUploader(cfg) 30 | ret := storage.PutRet{} 31 | putExtra := storage.PutExtra{Params: map[string]string{"x:name": "github logo"}} 32 | 33 | f, openError := file.Open() 34 | if openError != nil { 35 | global.LOG.Error("function file.Open() Filed", zap.Any("err", openError.Error())) 36 | 37 | return "", "", errors.New("function file.Open() Filed, err:" + openError.Error()) 38 | } 39 | defer func(f multipart.File) { 40 | _ = f.Close() 41 | }(f) // 创建文件 defer 关闭 42 | fileKey := fmt.Sprintf("%d%s", time.Now().Unix(), file.Filename) // 文件名格式 自己可以改 建议保证唯一性 43 | putErr := formUploader.Put(context.Background(), &ret, upToken, fileKey, f, file.Size, &putExtra) 44 | if putErr != nil { 45 | global.LOG.Error("function formUploader.Put() Filed", zap.Any("err", putErr.Error())) 46 | return "", "", errors.New("function formUploader.Put() Filed, err:" + putErr.Error()) 47 | } 48 | return global.CONFIG.Qiniu.ImgPath + "/" + ret.Key, ret.Key, nil 49 | } 50 | 51 | //@author: Flame 52 | //@object: *Qiniu 53 | //@function: DeleteFile 54 | //@description: 删除文件 55 | //@param: key string 56 | //@return: error 57 | 58 | func (*Qiniu) DeleteFile(key string) error { 59 | mac := qbox.NewMac(global.CONFIG.Qiniu.AccessKey, global.CONFIG.Qiniu.SecretKey) 60 | cfg := qiniuConfig() 61 | bucketManager := storage.NewBucketManager(mac, cfg) 62 | if err := bucketManager.Delete(global.CONFIG.Qiniu.Bucket, key); err != nil { 63 | global.LOG.Error("function bucketManager.Delete() Filed", zap.Any("err", err.Error())) 64 | return errors.New("function bucketManager.Delete() Filed, err:" + err.Error()) 65 | } 66 | return nil 67 | } 68 | 69 | //@author: Flame 70 | //@object: *Qiniu 71 | //@function: qiniuConfig 72 | //@description: 根据配置文件进行返回七牛云的配置 73 | //@return: *storage.Config 74 | 75 | func qiniuConfig() *storage.Config { 76 | cfg := storage.Config{ 77 | UseHTTPS: global.CONFIG.Qiniu.UseHTTPS, 78 | UseCdnDomains: global.CONFIG.Qiniu.UseCdnDomains, 79 | } 80 | switch global.CONFIG.Qiniu.Zone { // 根据配置文件进行初始化空间对应的机房 81 | case "ZoneHuadong": 82 | cfg.Zone = &storage.ZoneHuadong 83 | case "ZoneHuabei": 84 | cfg.Zone = &storage.ZoneHuabei 85 | case "ZoneHuanan": 86 | cfg.Zone = &storage.ZoneHuanan 87 | case "ZoneBeimei": 88 | cfg.Zone = &storage.ZoneBeimei 89 | case "ZoneXinjiapo": 90 | cfg.Zone = &storage.ZoneXinjiapo 91 | } 92 | return &cfg 93 | } 94 | -------------------------------------------------------------------------------- /utils/upload/tencent_cos.go: -------------------------------------------------------------------------------- 1 | package upload 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "context" 6 | "errors" 7 | "fmt" 8 | "mime/multipart" 9 | "net/http" 10 | "net/url" 11 | "time" 12 | 13 | "github.com/tencentyun/cos-go-sdk-v5" 14 | "go.uber.org/zap" 15 | ) 16 | 17 | type TencentCOS struct{} 18 | 19 | // UploadFile upload file to COS 20 | func (*TencentCOS) UploadFile(file *multipart.FileHeader) (string, string, error) { 21 | client := NewClient() 22 | f, openError := file.Open() 23 | if openError != nil { 24 | global.LOG.Error("function file.Open() Filed", zap.Any("err", openError.Error())) 25 | return "", "", errors.New("function file.Open() Filed, err:" + openError.Error()) 26 | } 27 | defer func(f multipart.File) { 28 | _ = f.Close() 29 | }(f) // 创建文件 defer 关闭 30 | fileKey := fmt.Sprintf("%d%s", time.Now().Unix(), file.Filename) 31 | 32 | _, err := client.Object.Put(context.Background(), global.CONFIG.TencentCOS.PathPrefix+"/"+fileKey, f, nil) 33 | if err != nil { 34 | panic(err) 35 | } 36 | return global.CONFIG.TencentCOS.BaseURL + "/" + global.CONFIG.TencentCOS.PathPrefix + "/" + fileKey, fileKey, nil 37 | } 38 | 39 | // DeleteFile delete file form COS 40 | func (*TencentCOS) DeleteFile(key string) error { 41 | client := NewClient() 42 | name := global.CONFIG.TencentCOS.PathPrefix + "/" + key 43 | _, err := client.Object.Delete(context.Background(), name) 44 | if err != nil { 45 | global.LOG.Error("function bucketManager.Delete() Filed", zap.Any("err", err.Error())) 46 | return errors.New("function bucketManager.Delete() Filed, err:" + err.Error()) 47 | } 48 | return nil 49 | } 50 | 51 | // NewClient init COS client 52 | func NewClient() *cos.Client { 53 | urlStr, _ := url.Parse("https://" + global.CONFIG.TencentCOS.Bucket + ".cos." + global.CONFIG.TencentCOS.Region + ".myqcloud.com") 54 | baseURL := &cos.BaseURL{BucketURL: urlStr} 55 | client := cos.NewClient(baseURL, &http.Client{ 56 | Transport: &cos.AuthorizationTransport{ 57 | SecretID: global.CONFIG.TencentCOS.SecretID, 58 | SecretKey: global.CONFIG.TencentCOS.SecretKey, 59 | }, 60 | }) 61 | return client 62 | } 63 | -------------------------------------------------------------------------------- /utils/upload/upload.go: -------------------------------------------------------------------------------- 1 | package upload 2 | 3 | import ( 4 | "FiberBoot/global" 5 | "mime/multipart" 6 | ) 7 | 8 | //@author: Flame 9 | //@interface_name: OSS 10 | //@description: OSS接口 11 | 12 | type OSS interface { 13 | UploadFile(file *multipart.FileHeader) (string, string, error) 14 | DeleteFile(key string) error 15 | } 16 | 17 | //@author: Flame 18 | //@function: NewOss 19 | //@description: OSS接口 20 | //@description: OSS的实例化方法 21 | //@return: OSS 22 | 23 | func NewOss() OSS { 24 | switch global.CONFIG.System.OssType { 25 | case "local": 26 | return &Local{} 27 | case "qiniu": 28 | return &Qiniu{} 29 | case "tencent-cos": 30 | return &TencentCOS{} 31 | case "aliyun-oss": 32 | return &AliyunOSS{} 33 | default: 34 | return &Local{} 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /utils/verify.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | var ( 4 | IdVerify = Rules{"ID": {NotEmpty()}} 5 | ApiVerify = Rules{"Path": {NotEmpty()}, "Description": {NotEmpty()}, "ApiGroup": {NotEmpty()}, "Method": {NotEmpty()}} 6 | MenuVerify = Rules{"Path": {NotEmpty()}, "ParentId": {NotEmpty()}, "Name": {NotEmpty()}, "Component": {NotEmpty()}, "Sort": {Ge("0")}} 7 | MenuMetaVerify = Rules{"Title": {NotEmpty()}} 8 | LoginVerify = Rules{"CaptchaId": {NotEmpty()}, "Captcha": {NotEmpty()}, "Username": {NotEmpty()}, "Password": {NotEmpty()}} 9 | RegisterVerify = Rules{"Username": {NotEmpty()}, "NickName": {NotEmpty()}, "Password": {NotEmpty()}, "AuthorityId": {NotEmpty()}} 10 | PageInfoVerify = Rules{"Page": {NotEmpty()}, "PageSize": {NotEmpty()}} 11 | CustomerVerify = Rules{"CustomerName": {NotEmpty()}, "CustomerPhoneData": {NotEmpty()}} 12 | AuthorityVerify = Rules{"AuthorityId": {NotEmpty()}, "AuthorityName": {NotEmpty()}, "ParentId": {NotEmpty()}} 13 | AuthorityIdVerify = Rules{"AuthorityId": {NotEmpty()}} 14 | OldAuthorityVerify = Rules{"OldAuthorityId": {NotEmpty()}} 15 | ChangePasswordVerify = Rules{"Username": {NotEmpty()}, "Password": {NotEmpty()}, "NewPassword": {NotEmpty()}} 16 | SetUserAuthorityVerify = Rules{"AuthorityId": {NotEmpty()}} 17 | ) 18 | -------------------------------------------------------------------------------- /utils/zipfiles.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "archive/zip" 5 | "io" 6 | "os" 7 | "strings" 8 | ) 9 | 10 | //@author: Flame 11 | //@function: ZipFiles 12 | //@description: 压缩文件 13 | //@param: filename string, files []string, oldForm, newForm string 14 | //@return: error 15 | 16 | func ZipFiles(filename string, files []string, oldForm, newForm string) error { 17 | 18 | newZipFile, err := os.Create(filename) 19 | if err != nil { 20 | return err 21 | } 22 | defer func() { 23 | _ = newZipFile.Close() 24 | }() 25 | 26 | zipWriter := zip.NewWriter(newZipFile) 27 | defer func() { 28 | _ = zipWriter.Close() 29 | }() 30 | 31 | // 把files添加到zip中 32 | for _, file := range files { 33 | 34 | err = func(file string) error { 35 | zipFile, err := os.Open(file) 36 | if err != nil { 37 | return err 38 | } 39 | defer func(zipFile *os.File) { 40 | _ = zipFile.Close() 41 | }(zipFile) 42 | // 获取file的基础信息 43 | info, err := zipFile.Stat() 44 | if err != nil { 45 | return err 46 | } 47 | 48 | header, err := zip.FileInfoHeader(info) 49 | if err != nil { 50 | return err 51 | } 52 | 53 | // 使用上面的FileInfoHeader() 就可以把文件保存的路径替换成我们自己想要的了,如下面 54 | header.Name = strings.Replace(file, oldForm, newForm, -1) 55 | 56 | // 优化压缩 57 | // 更多参考see http://golang.org/pkg/archive/zip/#pkg-constants 58 | header.Method = zip.Deflate 59 | 60 | writer, err := zipWriter.CreateHeader(header) 61 | if err != nil { 62 | return err 63 | } 64 | if _, err = io.Copy(writer, zipFile); err != nil { 65 | return err 66 | } 67 | return nil 68 | }(file) 69 | if err != nil { 70 | return err 71 | } 72 | } 73 | return nil 74 | } 75 | --------------------------------------------------------------------------------