├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
├── app
├── admin
│ ├── controllers
│ │ ├── common.go
│ │ ├── dept.go
│ │ ├── dict.go
│ │ ├── log.go
│ │ ├── menu.go
│ │ ├── role.go
│ │ ├── server.go
│ │ └── user.go
│ ├── entity
│ │ ├── admin_sys_dept.go
│ │ ├── admin_sys_dict_data.go
│ │ ├── admin_sys_dict_type.go
│ │ ├── admin_sys_log.go
│ │ ├── admin_sys_menu.go
│ │ ├── admin_sys_role.go
│ │ ├── admin_sys_role_dept.go
│ │ ├── admin_sys_role_menu.go
│ │ ├── admin_sys_user.go
│ │ ├── admin_sys_user_role.go
│ │ ├── admin_uploads.go
│ │ └── admin_uploads_type.go
│ ├── request
│ │ ├── dept.go
│ │ ├── dict.go
│ │ ├── log.go
│ │ ├── menu.go
│ │ ├── role.go
│ │ └── user.go
│ ├── response
│ │ ├── dept.go
│ │ ├── log.go
│ │ ├── menu.go
│ │ ├── role.go
│ │ ├── server.go
│ │ └── user.go
│ ├── routers
│ │ ├── common.go
│ │ ├── dept.go
│ │ ├── dict.go
│ │ ├── index.go
│ │ ├── log.go
│ │ ├── menu.go
│ │ ├── role.go
│ │ ├── server.go
│ │ └── user.go
│ └── services
│ │ ├── common.go
│ │ ├── dept.go
│ │ ├── dict.go
│ │ ├── log.go
│ │ ├── menu.go
│ │ ├── role.go
│ │ ├── server.go
│ │ └── user.go
├── api
│ ├── controllers
│ │ └── user.go
│ └── routers
│ │ ├── index.go
│ │ └── user.go
└── index.go
├── common
├── auth
│ └── auth.go
├── common.go
├── middlewares
│ ├── jwt.go
│ └── record.go
└── response.go
├── config.toml
├── core
├── config
│ └── config.go
├── index.go
├── middlewares
│ ├── cors.go
│ ├── logger.go
│ ├── middlewares.go
│ └── recovery.go
├── redis
│ └── redis.go
├── xorm
│ └── xorm.go
└── zap
│ ├── lumberjack
│ ├── chown.go
│ ├── chown_linux.go
│ └── lumberjack.go
│ └── zap.go
├── go.mod
├── go.sum
├── main.go
├── sql
└── seed-admin.sql
└── utils
├── cache.go
├── captcha.go
├── claims.go
├── file.go
├── jwt.go
├── md5.go
├── slice.go
└── validator.go
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .idea
3 | .vscode
4 | *.local
5 | /log/*
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # 编译阶段
2 | FROM golang:alpine AS go-builder
3 |
4 | # 进入工作目录
5 | WORKDIR /www
6 | COPY . .
7 | # 配置模块代理 国外服务器可以不配
8 | ENV GO111MODULE=on
9 | ENV GOPROXY=https://goproxy.cn,direct
10 | # 打包 linux/AMD64 架构
11 | RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o server
12 |
13 | # 运行阶段
14 | FROM alpine AS admin-runner
15 |
16 | # 进入工作目录
17 | WORKDIR /www
18 |
19 | # 复制打包的Go文件到系统用户可执行程序目录下
20 | COPY --from=go-builder /www/server /www
21 | # 复制配置文件到系统用户可执行程序目录下
22 | COPY --from=go-builder /www/config.toml /www
23 | # 复制sql到系统用户可执行程序目录下
24 | COPY --from=go-builder /www/sql /www
25 | # 复制静态目录到系统用户可执行程序目录下
26 | COPY --from=go-builder /www/wwwroot /www
27 | # 将时区设置为东八区
28 | RUN echo "https://mirrors.aliyun.com/alpine/v3.8/main/" > /etc/apk/repositories \
29 | && echo "https://mirrors.aliyun.com/alpine/v3.8/community/" >> /etc/apk/repositories \
30 | && apk add --no-cache tzdata \
31 | && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
32 | && echo Asia/Shanghai > /etc/timezone \
33 | && apk del tzdata
34 |
35 | # 暴露服务端口
36 | EXPOSE 8080
37 | # 启动服务
38 | ENTRYPOINT ["/www/server"]
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2022 pya789
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Seed - Admin
5 | 基于Go/C#(开发中)/Vue的前后端分离后台权限管理系统
6 |
12 |
13 | ## 项目简介
14 | seed-admin是一个纯粹的、完全开源的高可维护权限管理系统,减少了依赖,设计简单化,减少二次开发难度,提高可定制性。支持docker/常规上传部署。
15 | - 前端采用vue3、naive-ui... 支持template和jsx两种方式开发亦可混合使用。
16 | - 后端提供Go/C#两种解决方案可根据自身对语言的熟悉程度择其一使用
17 | - Go方案采用gin、xorm、mysql、redis、jwt
18 | - C#方案正在开发中...
19 | - 即时性动态权限菜单 权限修改后用户无需刷新即可生效
20 | - 高效率开发 前端封装CURD组件仅需填写json即可生成CURD页面
21 |
22 | ## 默认功能
23 | - 仪表盘:好像并没有没什么用...有数据展示需求请自由发挥
24 | - 用户管理:提供部门配置和用户权限与信息设置。
25 | - 部门系统(内嵌在用户管理中):配置组织机构(公司,部门,小组等)。
26 | - 角色管理:控制角色菜单权限、按钮权限、部门数据权限等。
27 | - 菜单管理:配置系统菜单、操作权限标识、按钮权限标识等。
28 | - 操作日志:负责记录系统登录日志、操作日志和查询。
29 | - 系统监控:负责监控系统CPU、内存、硬盘、服务等相关信息。
30 | - 字典管理:负责对数据库字段值做字典解析。
31 | - 个人管理:编辑修改个人信息。
32 |
33 | 为了维持精简性前端echarts等均无引入,后端工作流等业务组件均无开发计划有需求请自行fork开发
34 |
35 | ## 技术栈
36 | - 前端:**`vue3` `vuex` `vue-Router` `naive-ui` `...`**
37 | - Go后端:**`gin` `xorm` `mysql` `redis` `...`**
38 | - C#后端:**`开发中...`**
39 | ## 项目演示
40 | 演示地址:
41 | [https://admin.seed-app.com](https://admin.seed-app.com)
42 |
43 | - 账户:admin
44 | - 密码:123123
45 |
46 | > 提示:演示系统数据库每10分钟会自动还原一次
47 |
48 | 文档地址:
49 | [https://www.seed-app.com/admin](https://www.seed-app.com/admin/)
50 |
51 |
52 |
53 | ## 项目关联仓库(GitHub)
54 | Vue3前端:[https://github.com/seed-app/seed-admin-vue](https://github.com/seed-app/seed-admin-vue)
55 | Go服务端:[https://github.com/seed-app/seed-admin-go](https://github.com/seed-app/seed-admin-go)
56 | C#服务端:[https://github.com/seed-app/seed-admin-dotnet](https://github.com/seed-app/seed-admin-dotnet) (开发中...)
57 | ## 项目关联仓库(Gitee)
58 | Vue3前端:[https://gitee.com/seed-app/seed-admin-vue](https://gitee.com/seed-app/seed-admin-vue)
59 | Go服务端:[https://gitee.com/seed-app/seed-admin-go](https://gitee.com/seed-app/seed-admin-go)
60 | C#服务端:[https://gitee.com/seed-app/seed-admin-dotnet](https://gitee.com/seed-app/seed-admin-dotnet) (开发中...)
61 |
62 | ## 学(kai)习(che)交(mo)流(yu)群
63 | 企鹅1群:8455822
--------------------------------------------------------------------------------
/app/admin/controllers/common.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "seed-admin/app/admin/services"
5 | "seed-admin/common"
6 |
7 | "github.com/gin-gonic/gin"
8 | )
9 |
10 | type Common struct{}
11 |
12 | var commonService services.CommonService
13 |
14 | // 上传图片
15 | func (*Common) UploadImages(ctx *gin.Context) {
16 | res, err := commonService.Uploads(ctx)
17 | if err != nil {
18 | common.LOG.Error(err.Error())
19 | common.FailMsg(ctx, err.Error())
20 | return
21 | }
22 | common.OkMsgData(ctx, "上传成功", res)
23 | }
24 |
--------------------------------------------------------------------------------
/app/admin/controllers/dept.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "seed-admin/app/admin/request"
5 | "seed-admin/app/admin/services"
6 | "seed-admin/common"
7 | "seed-admin/utils"
8 |
9 | "github.com/gin-gonic/gin"
10 | )
11 |
12 | type Dept struct{}
13 |
14 | var deptService services.DeptService
15 |
16 | func (*Dept) List(ctx *gin.Context) {
17 | res, err := deptService.GetAllDept()
18 | if err != nil {
19 | common.LOG.Error(err.Error())
20 | }
21 | common.OkData(ctx, res)
22 | }
23 |
24 | // 增加部门
25 | func (*Dept) Add(ctx *gin.Context) {
26 | var params request.DeptAdd
27 | _ = ctx.ShouldBindJSON(¶ms)
28 | // 验证参数合法性
29 | if err := utils.ParamsVerify(¶ms); err != nil {
30 | common.FailMsg(ctx, err.Error())
31 | return
32 | }
33 | if err := deptService.AddDept(¶ms); err != nil {
34 | common.LOG.Error(err.Error())
35 | common.FailMsg(ctx, err.Error())
36 | return
37 | }
38 | common.OkMsg(ctx, "增加部门成功")
39 | }
40 |
41 | // 更新部门
42 | func (*Dept) Update(ctx *gin.Context) {
43 | var params request.DeptUpdate
44 | _ = ctx.ShouldBindJSON(¶ms)
45 | // 验证参数合法性
46 | if err := utils.ParamsVerify(¶ms); err != nil {
47 | common.FailMsg(ctx, err.Error())
48 | return
49 | }
50 | if err := deptService.UpdateDept(¶ms); err != nil {
51 | common.LOG.Error(err.Error())
52 | common.FailMsg(ctx, err.Error())
53 | return
54 | }
55 | common.OkMsg(ctx, "更新部门成功")
56 | }
57 |
58 | // 删除部门
59 | func (*Dept) Del(ctx *gin.Context) {
60 | var params request.DeptDel
61 | _ = ctx.ShouldBindJSON(¶ms)
62 | // 验证参数合法性
63 | if err := utils.ParamsVerify(¶ms); err != nil {
64 | common.FailMsg(ctx, err.Error())
65 | return
66 | }
67 | if err := deptService.DelDept(¶ms); err != nil {
68 | common.LOG.Error(err.Error())
69 | common.FailMsg(ctx, err.Error())
70 | return
71 | }
72 | common.OkMsg(ctx, "删除部门成功")
73 | }
74 |
75 | // 部门信息
76 | func (*Dept) Info(ctx *gin.Context) {
77 | var params request.DeptInfo
78 | _ = ctx.ShouldBindQuery(¶ms)
79 | // 验证参数合法性
80 | if err := utils.ParamsVerify(¶ms); err != nil {
81 | common.FailMsg(ctx, err.Error())
82 | return
83 | }
84 | res, err := deptService.GetInfo(params.Id)
85 | if err != nil {
86 | common.LOG.Error(err.Error())
87 | common.FailMsg(ctx, err.Error())
88 | return
89 | }
90 | common.OkData(ctx, res)
91 | }
92 |
--------------------------------------------------------------------------------
/app/admin/controllers/dict.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "seed-admin/app/admin/request"
5 | "seed-admin/app/admin/services"
6 | "seed-admin/common"
7 | "seed-admin/utils"
8 |
9 | "github.com/gin-gonic/gin"
10 | )
11 |
12 | type Dict struct{}
13 |
14 | var dictService services.DictService
15 |
16 | // 字典列表
17 | func (*Dict) List(ctx *gin.Context) {
18 | var params request.DictList
19 | _ = ctx.ShouldBindQuery(¶ms)
20 | // 验证参数合法性
21 | if err := utils.ParamsVerify(¶ms); err != nil {
22 | common.FailMsg(ctx, err.Error())
23 | return
24 | }
25 | res, count, err := dictService.GetAllDictType(¶ms)
26 | if err != nil {
27 | common.LOG.Error(err.Error())
28 | common.FailMsg(ctx, err.Error())
29 | return
30 | }
31 | common.OkData(ctx, map[string]any{
32 | "list": res,
33 | "count": count,
34 | })
35 | }
36 |
37 | // 增加字典
38 | func (*Dict) Add(ctx *gin.Context) {
39 | var params request.DictAdd
40 | _ = ctx.ShouldBindJSON(¶ms)
41 | // 验证参数合法性
42 | if err := utils.ParamsVerify(¶ms); err != nil {
43 | common.FailMsg(ctx, err.Error())
44 | return
45 | }
46 | if err := dictService.AddDictType(¶ms); err != nil {
47 | common.LOG.Error(err.Error())
48 | common.FailMsg(ctx, err.Error())
49 | }
50 | common.OkMsg(ctx, "增加字典成功")
51 | }
52 | func (*Dict) Update(ctx *gin.Context) {
53 | var params request.DictUpdate
54 | _ = ctx.ShouldBindJSON(¶ms)
55 | // 验证参数合法性
56 | if err := utils.ParamsVerify(¶ms); err != nil {
57 | common.FailMsg(ctx, err.Error())
58 | return
59 | }
60 | if err := dictService.UpdateDictType(¶ms); err != nil {
61 | common.LOG.Error(err.Error())
62 | common.FailMsg(ctx, err.Error())
63 | }
64 | common.OkMsg(ctx, "更新字典成功")
65 | }
66 |
67 | // 删除字典
68 | func (*Dict) Del(ctx *gin.Context) {
69 | var params request.DictDel
70 | _ = ctx.ShouldBindJSON(¶ms)
71 | // 验证参数合法性
72 | if err := utils.ParamsVerify(¶ms); err != nil {
73 | common.FailMsg(ctx, err.Error())
74 | return
75 | }
76 | if err := dictService.DelDictType(¶ms); err != nil {
77 | common.LOG.Error(err.Error())
78 | common.FailMsg(ctx, err.Error())
79 | }
80 | common.OkMsg(ctx, "删除字典成功")
81 | }
82 |
83 | // 获取字典信息
84 | func (*Dict) Info(ctx *gin.Context) {
85 | var params request.DictInfo
86 | _ = ctx.ShouldBindQuery(¶ms)
87 | // 验证参数合法性
88 | if err := utils.ParamsVerify(¶ms); err != nil {
89 | common.FailMsg(ctx, err.Error())
90 | return
91 | }
92 | res, err := dictService.GetDictTypeInfo(params.Id)
93 | if err != nil {
94 | common.LOG.Error(err.Error())
95 | common.FailMsg(ctx, err.Error())
96 | return
97 | }
98 | common.OkData(ctx, res)
99 | }
100 |
101 | // 字典数据列表
102 | func (*Dict) DataList(ctx *gin.Context) {
103 | var params request.DictDataList
104 | _ = ctx.ShouldBindQuery(¶ms)
105 | // 验证参数合法性
106 | if err := utils.ParamsVerify(¶ms); err != nil {
107 | common.FailMsg(ctx, err.Error())
108 | return
109 | }
110 | res, count, err := dictService.GetAllDictData(¶ms)
111 | if err != nil {
112 | common.LOG.Error(err.Error())
113 | common.FailMsg(ctx, err.Error())
114 | return
115 | }
116 | common.OkData(ctx, map[string]any{
117 | "list": res,
118 | "count": count,
119 | })
120 | }
121 |
122 | // 增加字典数据
123 | func (*Dict) DataAdd(ctx *gin.Context) {
124 | var params request.DictDataAdd
125 | _ = ctx.ShouldBindJSON(¶ms)
126 | // 验证参数合法性
127 | if err := utils.ParamsVerify(¶ms); err != nil {
128 | common.FailMsg(ctx, err.Error())
129 | return
130 | }
131 | if err := dictService.AddDictData(¶ms); err != nil {
132 | common.LOG.Error(err.Error())
133 | common.FailMsg(ctx, err.Error())
134 | }
135 | common.OkMsg(ctx, "增加字典数据成功")
136 | }
137 |
138 | // 字典数据更新
139 | func (*Dict) DataUpdate(ctx *gin.Context) {
140 | var params request.DictDataUpdate
141 | _ = ctx.ShouldBindJSON(¶ms)
142 | // 验证参数合法性
143 | if err := utils.ParamsVerify(¶ms); err != nil {
144 | common.FailMsg(ctx, err.Error())
145 | return
146 | }
147 | if err := dictService.UpdateDictData(¶ms); err != nil {
148 | common.LOG.Error(err.Error())
149 | common.FailMsg(ctx, err.Error())
150 | }
151 | common.OkMsg(ctx, "更新字典数据成功")
152 | }
153 |
154 | // 删除字典数据
155 | func (*Dict) DataDel(ctx *gin.Context) {
156 | var params request.DictDataDel
157 | _ = ctx.ShouldBindJSON(¶ms)
158 | // 验证参数合法性
159 | if err := utils.ParamsVerify(¶ms); err != nil {
160 | common.FailMsg(ctx, err.Error())
161 | return
162 | }
163 | if err := dictService.DelDictData(¶ms); err != nil {
164 | common.LOG.Error(err.Error())
165 | common.FailMsg(ctx, err.Error())
166 | }
167 | common.OkMsg(ctx, "删除字典数据成功")
168 | }
169 |
170 | // 获取字典数据信息
171 | func (*Dict) DataInfo(ctx *gin.Context) {
172 | var params request.DictDataInfo
173 | _ = ctx.ShouldBindQuery(¶ms)
174 | // 验证参数合法性
175 | if err := utils.ParamsVerify(¶ms); err != nil {
176 | common.FailMsg(ctx, err.Error())
177 | return
178 | }
179 | res, err := dictService.GetDictDataInfo(params.Id)
180 | if err != nil {
181 | common.LOG.Error(err.Error())
182 | common.FailMsg(ctx, err.Error())
183 | return
184 | }
185 | common.OkData(ctx, res)
186 | }
187 |
188 | // 根据类型获取字典
189 | func (*Dict) TypeData(ctx *gin.Context) {
190 | dictType := ctx.Query("type")
191 | res, err := dictService.GetTypeData(dictType)
192 | if err != nil {
193 | common.LOG.Error(err.Error())
194 | common.FailMsg(ctx, err.Error())
195 | return
196 | }
197 | common.OkData(ctx, res)
198 | }
199 |
--------------------------------------------------------------------------------
/app/admin/controllers/log.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "seed-admin/app/admin/request"
5 | "seed-admin/app/admin/services"
6 | "seed-admin/common"
7 | "seed-admin/utils"
8 |
9 | "github.com/gin-gonic/gin"
10 | )
11 |
12 | type Log struct{}
13 |
14 | var logService services.LogService
15 |
16 | func (*Log) List(ctx *gin.Context) {
17 | var params request.LogList
18 | _ = ctx.ShouldBindQuery(¶ms)
19 | // 验证参数合法性
20 | if err := utils.ParamsVerify(¶ms); err != nil {
21 | common.FailMsg(ctx, err.Error())
22 | return
23 | }
24 | res, count, err := logService.GetAllLog(¶ms)
25 | if err != nil {
26 | common.LOG.Error(err.Error())
27 | }
28 | common.OkData(ctx, map[string]any{
29 | "list": res,
30 | "count": count,
31 | })
32 | }
33 | func (*Log) Del(ctx *gin.Context) {
34 | var params request.LogDel
35 | _ = ctx.ShouldBindJSON(¶ms)
36 | // 验证参数合法性
37 | if err := utils.ParamsVerify(¶ms); err != nil {
38 | common.FailMsg(ctx, err.Error())
39 | return
40 | }
41 | if err := logService.DelLog(¶ms); err != nil {
42 | common.LOG.Error(err.Error())
43 | common.FailMsg(ctx, err.Error())
44 | return
45 | }
46 | common.OkMsg(ctx, "删除日志成功")
47 | }
48 |
--------------------------------------------------------------------------------
/app/admin/controllers/menu.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "seed-admin/app/admin/request"
5 | "seed-admin/app/admin/services"
6 | "seed-admin/common"
7 | "seed-admin/utils"
8 | "strconv"
9 |
10 | "github.com/gin-gonic/gin"
11 | )
12 |
13 | type Menu struct{}
14 |
15 | var menuService services.MenuService
16 |
17 | // 菜单和权限
18 | func (*Menu) PermMenu(ctx *gin.Context) {
19 | menus, err := menuService.GetMenu(utils.GetUserId(ctx))
20 | if err != nil {
21 | common.LOG.Error(err.Error())
22 | }
23 | perms, err := menuService.GetPerms(utils.GetUserId(ctx))
24 | if err != nil {
25 | common.LOG.Error(err.Error())
26 | }
27 | common.OkData(ctx, map[string]any{
28 | "menus": menus,
29 | "perms": perms,
30 | })
31 | }
32 |
33 | // 菜单列表
34 | func (*Menu) List(ctx *gin.Context) {
35 | name := ctx.Query("name")
36 | status := ctx.Query("status")
37 | res, err := menuService.GetAllMenu(name, status)
38 | if err != nil {
39 | common.LOG.Error(err.Error())
40 | }
41 | common.OkData(ctx, res)
42 | }
43 |
44 | // 获取菜单信息
45 | func (*Menu) Info(ctx *gin.Context) {
46 | id, _ := strconv.Atoi(ctx.Query("id"))
47 | res, err := menuService.GetInfo(id)
48 | if err != nil {
49 | common.LOG.Error(err.Error())
50 | }
51 | common.OkData(ctx, res)
52 | }
53 |
54 | // 增加菜单
55 | func (*Menu) Add(ctx *gin.Context) {
56 | var params request.Menu
57 | _ = ctx.ShouldBindJSON(¶ms)
58 | // 验证参数合法性
59 | if err := utils.ParamsVerify(¶ms); err != nil {
60 | common.FailMsg(ctx, err.Error())
61 | return
62 | }
63 | if err := menuService.AddMenu(¶ms); err != nil {
64 | common.LOG.Error(err.Error())
65 | common.FailMsg(ctx, err.Error())
66 | return
67 | }
68 | common.OkMsg(ctx, "增加菜单成功")
69 | }
70 |
71 | // 编辑菜单
72 | func (*Menu) Update(ctx *gin.Context) {
73 | var params request.Menu
74 | _ = ctx.ShouldBindJSON(¶ms)
75 | // 验证参数合法性
76 | if err := utils.ParamsVerify(¶ms); err != nil {
77 | common.FailMsg(ctx, err.Error())
78 | return
79 | }
80 | err := menuService.UpdateMenu(¶ms)
81 | if err != nil {
82 | common.LOG.Error(err.Error())
83 | common.FailMsg(ctx, err.Error())
84 | return
85 | }
86 | common.OkMsg(ctx, "更新菜单成功")
87 | }
88 |
89 | // 删除菜单
90 | func (*Menu) Del(ctx *gin.Context) {
91 | var params request.MenuDel
92 | _ = ctx.ShouldBindJSON(¶ms)
93 | // 验证参数合法性
94 | if err := utils.ParamsVerify(¶ms); err != nil {
95 | common.FailMsg(ctx, err.Error())
96 | return
97 | }
98 | if err := menuService.DelMenu(¶ms); err != nil {
99 | common.LOG.Error(err.Error())
100 | common.FailMsg(ctx, err.Error())
101 | return
102 | }
103 | common.OkMsg(ctx, "删除菜单成功")
104 | }
105 |
--------------------------------------------------------------------------------
/app/admin/controllers/role.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "seed-admin/app/admin/request"
5 | "seed-admin/app/admin/services"
6 | "seed-admin/common"
7 | "seed-admin/utils"
8 | "strconv"
9 |
10 | "github.com/gin-gonic/gin"
11 | )
12 |
13 | type Role struct{}
14 |
15 | var roleService services.RoleService
16 |
17 | // 获取角色列表
18 | func (*Role) List(ctx *gin.Context) {
19 | var params request.RoleList
20 | _ = ctx.ShouldBindQuery(¶ms)
21 | // 验证参数合法性
22 | if err := utils.ParamsVerify(¶ms); err != nil {
23 | common.FailMsg(ctx, err.Error())
24 | return
25 | }
26 | res, count, err := roleService.GetAllRole(¶ms)
27 | if err != nil {
28 | common.LOG.Error(err.Error())
29 | }
30 | common.OkData(ctx, map[string]any{
31 | "list": res,
32 | "count": count,
33 | })
34 | }
35 |
36 | // 获取角色信息
37 | func (*Role) Info(ctx *gin.Context) {
38 | id, _ := strconv.Atoi(ctx.Query("id"))
39 | res, err := roleService.GetInfo(id)
40 | if err != nil {
41 | common.LOG.Error(err.Error())
42 | }
43 | common.OkData(ctx, res)
44 | }
45 |
46 | // 新增角色
47 | func (*Role) Add(ctx *gin.Context) {
48 | var params request.Role
49 | _ = ctx.ShouldBindJSON(¶ms)
50 | // 验证参数合法性
51 | if err := utils.ParamsVerify(¶ms); err != nil {
52 | common.FailMsg(ctx, err.Error())
53 | return
54 | }
55 | if err := roleService.AddRole(¶ms); err != nil {
56 | common.LOG.Error(err.Error())
57 | common.FailMsg(ctx, err.Error())
58 | return
59 | }
60 | common.Ok(ctx)
61 | }
62 |
63 | // 编辑角色
64 | func (*Role) Update(ctx *gin.Context) {
65 | var params request.RoleUpdate
66 | _ = ctx.ShouldBindJSON(¶ms)
67 | // 验证参数合法性
68 | if err := utils.ParamsVerify(¶ms); err != nil {
69 | common.FailMsg(ctx, err.Error())
70 | return
71 | }
72 | if err := roleService.UpdateRole(¶ms); err != nil {
73 | common.LOG.Error(err.Error())
74 | common.FailMsg(ctx, err.Error())
75 | return
76 | }
77 | common.OkMsg(ctx, "更新角色成功")
78 | }
79 |
80 | // 删除角色
81 | func (*Role) Del(ctx *gin.Context) {
82 | var params request.RoleDel
83 | _ = ctx.ShouldBindJSON(¶ms)
84 | // 验证参数合法性
85 | if err := utils.ParamsVerify(¶ms); err != nil {
86 | common.FailMsg(ctx, err.Error())
87 | return
88 | }
89 | if err := roleService.DelRole(¶ms); err != nil {
90 | common.LOG.Error(err.Error())
91 | common.FailMsg(ctx, err.Error())
92 | return
93 | }
94 | common.OkMsg(ctx, "删除角色成功")
95 | }
96 |
--------------------------------------------------------------------------------
/app/admin/controllers/server.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "seed-admin/app/admin/services"
5 | "seed-admin/common"
6 |
7 | "github.com/gin-gonic/gin"
8 | )
9 |
10 | type Server struct{}
11 |
12 | var serverService services.ServerService
13 |
14 | // 服务器信息
15 | func (*Server) Info(ctx *gin.Context) {
16 | res, err := serverService.GetSystemInfo()
17 | if err != nil {
18 | common.FailMsg(ctx, "获取服务器信息失败")
19 | return
20 | }
21 | common.OkData(ctx, res)
22 | }
23 |
--------------------------------------------------------------------------------
/app/admin/controllers/user.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "seed-admin/app/admin/request"
5 | "seed-admin/app/admin/services"
6 | "seed-admin/common"
7 | "seed-admin/utils"
8 | "strconv"
9 |
10 | "github.com/gin-gonic/gin"
11 | )
12 |
13 | type User struct{}
14 |
15 | var userService services.UserService
16 |
17 | // 登录
18 | func (*User) Login(ctx *gin.Context) {
19 | // 绑定body参数到结构
20 | var params request.Login
21 | _ = ctx.ShouldBindJSON(¶ms)
22 | // 验证参数合法性
23 | if err := utils.ParamsVerify(¶ms); err != nil {
24 | common.FailMsg(ctx, err.Error())
25 | return
26 | }
27 | // 验证验证码合法性 注意CaptchaVerify调用后验证码已销毁 所以需要让前端再次获取新的验证码
28 | if err := utils.CaptchaVerify(params.CaptchaId, params.Captcha); err != nil {
29 | common.Message(ctx, common.REFRESH_CAPTCHA, err.Error())
30 | return
31 | }
32 | // 调用服务登录
33 | user, roleIds, err := userService.Login(¶ms)
34 | if err != nil {
35 | common.Message(ctx, common.REFRESH_CAPTCHA, err.Error())
36 | return
37 | }
38 | if len(roleIds) == 0 {
39 | common.Message(ctx, common.REFRESH_CAPTCHA, "您没有任何角色权限,无法登录")
40 | return
41 | }
42 | // 获取token
43 | token, err := userService.GetToken(user)
44 | if err != nil {
45 | common.Message(ctx, common.REFRESH_CAPTCHA, err.Error())
46 | return
47 | }
48 | common.OkMsgData(ctx, "登录成功", map[string]string{
49 | "token": token,
50 | })
51 | }
52 |
53 | // 获取登录人的用户信息
54 | func (*User) Person(ctx *gin.Context) {
55 | userInfo, err := userService.GetPerson(utils.GetUserId(ctx))
56 | if err != nil {
57 | common.Message(ctx, common.AUTHORIZATION_FAIL, err.Error())
58 | return
59 | }
60 | common.OkData(ctx, userInfo)
61 | }
62 |
63 | // 生成验证码
64 | func (*User) Captcha(ctx *gin.Context) {
65 | captchaId := ctx.Query("captchaId")
66 | cap := utils.NewCaptcha()
67 | var image []byte
68 | var err error
69 | // 是否进入重载
70 | if captchaId != "" {
71 | _ = cap.Reload(captchaId)
72 | image, err = cap.ImageByte(captchaId)
73 | if err != nil {
74 | common.FailMsg(ctx, err.Error())
75 | return
76 | }
77 | } else {
78 | captchaId = cap.CreateImage()
79 | image, err = cap.ImageByte(captchaId)
80 | if err != nil {
81 | common.FailMsg(ctx, err.Error())
82 | return
83 | }
84 | }
85 | data := map[string]any{
86 | "id": captchaId,
87 | "image": image,
88 | }
89 | common.OkData(ctx, data)
90 | }
91 |
92 | // 获取用户列表
93 | func (*User) List(ctx *gin.Context) {
94 | var params request.UserList
95 | _ = ctx.ShouldBindJSON(¶ms)
96 | // 验证参数合法性
97 | if err := utils.ParamsVerify(¶ms); err != nil {
98 | common.FailMsg(ctx, err.Error())
99 | return
100 | }
101 | res, count, err := userService.GetAllUser(¶ms, utils.GetUserId(ctx))
102 | if err != nil {
103 | common.LOG.Error(err.Error())
104 | common.FailMsg(ctx, err.Error())
105 | return
106 | }
107 | common.OkData(ctx, map[string]any{
108 | "list": res,
109 | "count": count,
110 | })
111 | }
112 |
113 | // 修改用户角色
114 | func (*User) UpdateUserRole(ctx *gin.Context) {
115 | var params request.UserRoleUpdate
116 | _ = ctx.ShouldBindJSON(¶ms)
117 | // 验证参数合法性
118 | if err := utils.ParamsVerify(¶ms); err != nil {
119 | common.FailMsg(ctx, err.Error())
120 | return
121 | }
122 | if err := userService.UpdateUserRole(¶ms); err != nil {
123 | common.LOG.Error(err.Error())
124 | common.FailMsg(ctx, err.Error())
125 | return
126 | }
127 | common.OkMsg(ctx, "更新用户角色成功")
128 | }
129 |
130 | // 修改用户头像
131 | func (*User) UpdateAvatar(ctx *gin.Context) {
132 | var params request.UserAvatarUpdate
133 | _ = ctx.ShouldBindJSON(¶ms)
134 | // 验证参数合法性
135 | if err := utils.ParamsVerify(¶ms); err != nil {
136 | common.FailMsg(ctx, err.Error())
137 | return
138 | }
139 | if err := userService.UpdateAvatar(¶ms, utils.GetUserId(ctx)); err != nil {
140 | common.LOG.Error(err.Error())
141 | common.FailMsg(ctx, err.Error())
142 | return
143 | }
144 | common.OkMsg(ctx, "修改用户头像成功")
145 | }
146 |
147 | // 更新用户基础信息
148 | func (*User) UpdateBaseInfo(ctx *gin.Context) {
149 | var params request.UserBaseInfoUpdate
150 | _ = ctx.ShouldBindJSON(¶ms)
151 | // 验证参数合法性
152 | if err := utils.ParamsVerify(¶ms); err != nil {
153 | common.FailMsg(ctx, err.Error())
154 | return
155 | }
156 | if err := userService.UpdateBaseInfo(¶ms, utils.GetUserId(ctx)); err != nil {
157 | common.LOG.Error(err.Error())
158 | common.FailMsg(ctx, err.Error())
159 | return
160 | }
161 | common.OkMsg(ctx, "修改基础信息成功")
162 | }
163 |
164 | // 更新用户密码
165 | func (*User) UpdatePassword(ctx *gin.Context) {
166 | var params request.UserPasswordUpdate
167 | _ = ctx.ShouldBindJSON(¶ms)
168 | // 验证参数合法性
169 | if err := utils.ParamsVerify(¶ms); err != nil {
170 | common.FailMsg(ctx, err.Error())
171 | return
172 | }
173 | if err := userService.UpdatePassword(¶ms, utils.GetUserId(ctx)); err != nil {
174 | common.LOG.Error(err.Error())
175 | common.FailMsg(ctx, err.Error())
176 | return
177 | }
178 | common.OkMsg(ctx, "修改基础信息成功")
179 | }
180 |
181 | // 获取用户信息
182 | func (*User) Info(ctx *gin.Context) {
183 | id, _ := strconv.Atoi(ctx.Query("id"))
184 | userInfo, err := userService.GetInfo(id)
185 | if err != nil {
186 | common.FailMsg(ctx, err.Error())
187 | return
188 | }
189 | common.OkData(ctx, userInfo)
190 | }
191 |
192 | // 新增用户
193 | func (*User) Add(ctx *gin.Context) {
194 | var params request.User
195 | _ = ctx.ShouldBindJSON(¶ms)
196 | // 验证参数合法性
197 | if err := utils.ParamsVerify(¶ms); err != nil {
198 | common.FailMsg(ctx, err.Error())
199 | return
200 | }
201 | if err := userService.AddUser(¶ms); err != nil {
202 | common.LOG.Error(err.Error())
203 | common.FailMsg(ctx, err.Error())
204 | return
205 | }
206 | common.OkMsg(ctx, "新增用户成功")
207 | }
208 |
209 | // 修改用户
210 | func (*User) Update(ctx *gin.Context) {
211 | var params request.UserUpdate
212 | _ = ctx.ShouldBindJSON(¶ms)
213 | // 验证参数合法性
214 | if err := utils.ParamsVerify(¶ms); err != nil {
215 | common.FailMsg(ctx, err.Error())
216 | return
217 | }
218 | if err := userService.UpdateUser(¶ms); err != nil {
219 | common.LOG.Error(err.Error())
220 | common.FailMsg(ctx, err.Error())
221 | return
222 | }
223 | common.OkMsg(ctx, "用户信息修改成功")
224 | }
225 |
226 | // 删除用户
227 | func (*User) Del(ctx *gin.Context) {
228 | var params request.UserDel
229 | _ = ctx.ShouldBindJSON(¶ms)
230 | // 验证参数合法性
231 | if err := utils.ParamsVerify(¶ms); err != nil {
232 | common.FailMsg(ctx, err.Error())
233 | return
234 | }
235 | if err := userService.DelUser(¶ms); err != nil {
236 | common.LOG.Error(err.Error())
237 | common.FailMsg(ctx, err.Error())
238 | return
239 | }
240 | common.OkMsg(ctx, "删除用户成功")
241 | }
242 |
243 | // 转移部门
244 | func (*User) Move(ctx *gin.Context) {
245 | var params request.UserMove
246 | _ = ctx.ShouldBindJSON(¶ms)
247 | // 验证参数合法性
248 | if err := utils.ParamsVerify(¶ms); err != nil {
249 | common.FailMsg(ctx, err.Error())
250 | return
251 | }
252 | if err := userService.MoveDept(¶ms); err != nil {
253 | common.LOG.Error(err.Error())
254 | common.FailMsg(ctx, err.Error())
255 | return
256 | }
257 | common.OkMsg(ctx, "转移部门成功")
258 | }
259 |
--------------------------------------------------------------------------------
/app/admin/entity/admin_sys_dept.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import "time"
4 |
5 | type AdminSysDept struct {
6 | Id int `json:"id" xorm:"pk autoincr comment('主键')"`
7 | Name string `json:"name" xorm:"notnull comment('部门名称')"`
8 | ParentId int `json:"parentId" xorm:"comment('父级ID')"`
9 | Sort int `json:"sort" xorm:"comment('排序')"`
10 | CreatedTime time.Time `json:"createdTime" xorm:"created comment('创建时间')"`
11 | UpdatedTime time.Time `json:"updatedTime" xorm:"updated comment('更新时间')"`
12 | }
13 |
--------------------------------------------------------------------------------
/app/admin/entity/admin_sys_dict_data.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import "time"
4 |
5 | type AdminSysDictData struct {
6 | Id int `json:"id" xorm:"pk autoincr comment('主键')"`
7 | Pid int `json:"pid" xorm:"notnull comment('主键')"`
8 | Label string `json:"label" xorm:"comment('字典标签')"`
9 | Value string `json:"value" xorm:"comment('字典值')"`
10 | Status int `json:"status" xorm:"bool notnull default(0) comment('状态0.正常 1.禁用')"`
11 | CreatedTime time.Time `json:"createdTime" xorm:"created comment('创建时间')"`
12 | UpdatedTime time.Time `json:"updatedTime" xorm:"updated comment('更新时间')"`
13 | }
14 |
--------------------------------------------------------------------------------
/app/admin/entity/admin_sys_dict_type.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import "time"
4 |
5 | type AdminSysDictType struct {
6 | Id int `json:"id" xorm:"pk autoincr comment('主键')"`
7 | Name string `json:"name" xorm:"comment('字典名称')"`
8 | Type string `json:"type" xorm:"comment('字典类型')"`
9 | Status int `json:"status" xorm:"bool notnull default(0) comment('状态0.正常 1.禁用')"`
10 | CreatedTime time.Time `json:"createdTime" xorm:"created comment('创建时间')"`
11 | UpdatedTime time.Time `json:"updatedTime" xorm:"updated comment('更新时间')"`
12 | }
13 |
--------------------------------------------------------------------------------
/app/admin/entity/admin_sys_log.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import "time"
4 |
5 | type AdminSysLog struct {
6 | Id int `json:"id" xorm:"pk autoincr comment('主键')"`
7 | UserId int `json:"userId" xorm:"notnull comment('角色ID')"`
8 | Method string `json:"method" xorm:"notnull comment('请求方式')"`
9 | Action string `json:"action" xorm:"notnull comment('行为')"`
10 | Ip string `json:"ip" xorm:"comment('IP')"`
11 | StatusCode int `json:"statusCode" xorm:"comment('响应状态')"`
12 | Params string `json:"params" xorm:"longtext comment('参数')"`
13 | Results string `json:"results" xorm:"longtext comment('响应结果')"`
14 | CreatedTime time.Time `json:"createdTime" xorm:"created comment('创建时间')"`
15 | UpdatedTime time.Time `json:"updatedTime" xorm:"updated comment('更新时间')"`
16 | }
17 |
--------------------------------------------------------------------------------
/app/admin/entity/admin_sys_menu.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import "time"
4 |
5 | type AdminSysMenu struct {
6 | Id int `json:"id" xorm:"pk autoincr comment('主键')"`
7 | ParentId int `json:"parentId" xorm:"notnull default(0) comment('父级ID 0为根项目 主键不要设置为0')"`
8 | Name string `json:"name" xorm:"notnull comment('菜单名称')"`
9 | RouterName string `json:"routerName" xorm:"comment('路由名称')"`
10 | RouterPath string `json:"routerPath" xorm:"comment('路由地址')"`
11 | PagePath string `json:"pagePath" xorm:"comment('页面路径')"`
12 | Perms string `json:"perms" xorm:"comment('权限标识')"`
13 | Type int `json:"type" xorm:"bool notnull default(0) comment('类型0.目录 1.菜单 2.按钮')"`
14 | Icon string `json:"icon" xorm:"comment('图标')"`
15 | Sort int `json:"sort" xorm:"comment('排序')"`
16 | Visible int `json:"visible" xorm:"bool notnull default(0) comment('隐藏0.显示 1.隐藏')"`
17 | KeepAlive int `json:"keepAlive" xorm:"bool notnull default(0) comment('页面缓存0.否 1.是')"`
18 | Status int `json:"status" xorm:"bool notnull default(0) comment('状态0.正常 1.禁用')"`
19 | CreatedTime time.Time `json:"createdTime" xorm:"created comment('创建时间')"`
20 | UpdatedTime time.Time `json:"updatedTime" xorm:"updated comment('更新时间')"`
21 | }
22 |
--------------------------------------------------------------------------------
/app/admin/entity/admin_sys_role.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import "time"
4 |
5 | type AdminSysRole struct {
6 | Id int `json:"id" xorm:"pk autoincr comment('主键')"` // 主键
7 | Name string `json:"name" xorm:"notnull comment('角色名称')"`
8 | Label string `json:"label" xorm:"comment('角色标签')"`
9 | Remark string `json:"remark" xorm:"comment('备注')"`
10 | Relevance int `json:"relevance" xorm:"bool notnull default(1) comment('上下级数据权限是否关联0.是 1.否')"`
11 | CreatedTime time.Time `json:"createdTime" xorm:"created comment('创建时间')"`
12 | UpdatedTime time.Time `json:"updatedTime" xorm:"updated comment('更新时间')"`
13 | }
14 |
--------------------------------------------------------------------------------
/app/admin/entity/admin_sys_role_dept.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | type AdminSysRoleDept struct {
4 | Id int `xorm:"pk autoincr comment('主键')"`
5 | RoleId int `xorm:"notnull comment('角色ID')"`
6 | DeptId int `xorm:"notnull comment('部门ID')"`
7 | }
8 |
--------------------------------------------------------------------------------
/app/admin/entity/admin_sys_role_menu.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | type AdminSysRoleMenu struct {
4 | Id int `xorm:"pk autoincr comment('主键')"`
5 | RoleId int `xorm:"notnull comment('角色ID')"`
6 | MenuId int `xorm:"notnull comment('菜单ID')"`
7 | }
8 |
--------------------------------------------------------------------------------
/app/admin/entity/admin_sys_user.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import "time"
4 |
5 | type AdminSysUser struct {
6 | Id int `json:"id" xorm:"pk autoincr comment('主键')"`
7 | DeptId int `json:"deptId" xorm:"comment('部门ID')"`
8 | Username string `json:"username" xorm:"comment('用户名')"`
9 | Password string `json:"password" xorm:"comment('密码')"`
10 | NickName string `json:"nickName" xorm:"default('未命名') comment('用户名称')"`
11 | Phone string `json:"phone" xorm:"comment('手机')"`
12 | Email string `json:"email" xorm:"comment('邮箱')"`
13 | Avatar string `json:"avatar" xorm:"comment('头像')"`
14 | Status int `json:"status" xorm:"bool notnull default(0) comment('状态0.正常 1.禁用')"`
15 | IsDeleted int `json:"isDeleted" xorm:"bool notnull default(0) comment('软删除0.正常 1.删除')"`
16 | CreatedTime time.Time `json:"createdTime" xorm:"created comment('创建时间')"`
17 | UpdatedTime time.Time `json:"updatedTime" xorm:"updated comment('更新时间')"`
18 | }
19 |
--------------------------------------------------------------------------------
/app/admin/entity/admin_sys_user_role.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | type AdminSysUserRole struct {
4 | Id int `xorm:"pk autoincr comment('主键')"`
5 | UserId int `xorm:"notnull comment('用户ID')"`
6 | RoleId int `xorm:"notnull comment('角色ID')"`
7 | }
8 |
--------------------------------------------------------------------------------
/app/admin/entity/admin_uploads.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import "time"
4 |
5 | type AdminUploads struct {
6 | Id int `json:"id" xorm:"pk autoincr comment('主键')"`
7 | Name string `json:"name" xorm:"comment('文件名')"`
8 | Url string `json:"url" xorm:"comment('地址')"`
9 | Type int `json:"type" xorm:"comment('分类')"`
10 | CreatedTime time.Time `json:"createdTime" xorm:"created comment('创建时间')"`
11 | UpdatedTime time.Time `json:"updatedTime" xorm:"updated comment('更新时间')"`
12 | }
13 |
--------------------------------------------------------------------------------
/app/admin/entity/admin_uploads_type.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import "time"
4 |
5 | type AdminUploadsType struct {
6 | Id int `json:"id" xorm:"pk autoincr comment('主键')"`
7 | Name string `json:"name" xorm:"comment('分类名称')"`
8 | Label string `json:"label" xorm:"comment('分类标识')"`
9 | CreatedTime time.Time `json:"createdTime" xorm:"created comment('创建时间')"`
10 | UpdatedTime time.Time `json:"updatedTime" xorm:"updated comment('更新时间')"`
11 | }
12 |
--------------------------------------------------------------------------------
/app/admin/request/dept.go:
--------------------------------------------------------------------------------
1 | package request
2 |
3 | type DeptInfo struct {
4 | Id *int `form:"id" validate:"required"`
5 | }
6 | type DeptAdd struct {
7 | Name *string `json:"name" validate:"required"`
8 | ParentId *int `json:"parentId" validate:"required"`
9 | Sort int `json:"sort" validate:"-"`
10 | }
11 | type DeptUpdate struct {
12 | Id *int `form:"id" validate:"required"`
13 | Name *string `json:"name" validate:"required"`
14 | ParentId *int `json:"parentId" validate:"required"`
15 | Sort int `json:"sort" validate:"-"`
16 | }
17 | type DeptDel struct {
18 | Pid *int `json:"pid" validate:"required"`
19 | Ids []int `json:"ids" validate:"required"`
20 | UserDel bool `json:"userDel" validate:"-"`
21 | }
22 |
--------------------------------------------------------------------------------
/app/admin/request/dict.go:
--------------------------------------------------------------------------------
1 | package request
2 |
3 | type DictList struct {
4 | Name string `form:"name" validate:"-"`
5 | Status string `form:"status" validate:"-"`
6 | PageNum *int `form:"pageNum" validate:"required"`
7 | PageSize *int `form:"pageSize" validate:"required"`
8 | }
9 | type DictAdd struct {
10 | Name *string `json:"name" validate:"required"`
11 | Type *string `json:"type" validate:"required"`
12 | Status int `json:"status" validate:"-"`
13 | }
14 | type DictUpdate struct {
15 | Id *int `json:"id" validate:"required"`
16 | Name *string `json:"name" validate:"required"`
17 | Type *string `json:"type" validate:"required"`
18 | Status int `json:"status" validate:"-"`
19 | }
20 | type DictDel struct {
21 | Ids []int `json:"ids" validate:"required"`
22 | }
23 | type DictInfo struct {
24 | Id *int `form:"id" validate:"required"`
25 | }
26 | type DictDataList struct {
27 | Pid *int `form:"pid" validate:"required"`
28 | Label string `form:"label" validate:"-"`
29 | Status string `form:"status" validate:"-"`
30 | PageNum *int `form:"pageNum" validate:"required"`
31 | PageSize *int `form:"pageSize" validate:"required"`
32 | }
33 | type DictDataAdd struct {
34 | Pid *int `json:"pid" validate:"required"`
35 | Label *string `json:"label" validate:"required"`
36 | Value *string `json:"value" validate:"required"`
37 | Status int `json:"status" validate:"-"`
38 | }
39 | type DictDataInfo struct {
40 | Id *int `form:"id" validate:"required"`
41 | }
42 | type DictDataUpdate struct {
43 | Id *int `json:"id" validate:"required"`
44 | Pid *int `json:"pid" validate:"required"`
45 | Label *string `json:"label" validate:"required"`
46 | Value *string `json:"value" validate:"required"`
47 | Status int `json:"status" validate:"-"`
48 | }
49 | type DictDataDel struct {
50 | Ids []int `json:"ids" validate:"required"`
51 | }
52 |
--------------------------------------------------------------------------------
/app/admin/request/log.go:
--------------------------------------------------------------------------------
1 | package request
2 |
3 | type LogList struct {
4 | UserName string `form:"username" validate:"-"`
5 | Method string `form:"method" validate:"-"`
6 | Action string `form:"action" validate:"-"`
7 | Ip string `form:"ip" validate:"-"`
8 | PageNum *int `form:"pageNum" validate:"required"`
9 | PageSize *int `form:"pageSize" validate:"required"`
10 | }
11 | type LogDel struct {
12 | Ids []int `json:"ids" validate:"required"`
13 | }
14 |
--------------------------------------------------------------------------------
/app/admin/request/menu.go:
--------------------------------------------------------------------------------
1 | package request
2 |
3 | type Menu struct {
4 | Id int `json:"id" validate:"-"`
5 | Type *int `json:"type" validate:"required"`
6 | ParentId *int `json:"parentId" validate:"required"`
7 | Name *string `json:"name" validate:"required"`
8 | RouterName string `json:"routerName" validate:"-"`
9 | RouterPath string `json:"routerPath" validate:"-"`
10 | PagePath string `json:"pagePath" validate:"-"`
11 | Perms string `json:"perms" validate:"-"`
12 | Icon string `json:"icon" validate:"-"`
13 | Sort int `json:"sort" validate:"-"`
14 | KeepAlive int `json:"keepAlive" validate:"-"`
15 | Status int `json:"status" validate:"-"`
16 | Visible int `json:"visible" validate:"-"`
17 | }
18 |
19 | type MenuDel struct {
20 | Ids []int `json:"ids" validate:"required"`
21 | }
22 |
--------------------------------------------------------------------------------
/app/admin/request/role.go:
--------------------------------------------------------------------------------
1 | package request
2 |
3 | type Role struct {
4 | Name *string `json:"name" validate:"required"`
5 | Label *string `json:"label" validate:"required"`
6 | Remark string `json:"remark" validate:"-"`
7 | Relevance int `json:"relevance" validate:"-"`
8 | MenuIds []int `json:"menuIds" validate:"-"`
9 | DeptIds []int `json:"deptIds" validate:"-"`
10 | }
11 | type RoleUpdate struct {
12 | Id *int `json:"id" validate:"required"`
13 | Name *string `json:"name" validate:"required"`
14 | Label *string `json:"label" validate:"required"`
15 | Remark string `json:"remark" validate:"-"`
16 | Relevance int `json:"relevance" validate:"-"`
17 | MenuIds []int `json:"menuIds" validate:"-"`
18 | DeptIds []int `json:"deptIds" validate:"-"`
19 | }
20 | type RoleList struct {
21 | Name string `form:"name" validate:"-"`
22 | PageNum *int `form:"pageNum" validate:"required"`
23 | PageSize *int `form:"pageSize" validate:"required"`
24 | }
25 | type RoleDel struct {
26 | Ids []int `json:"ids" validate:"required"`
27 | }
28 |
--------------------------------------------------------------------------------
/app/admin/request/user.go:
--------------------------------------------------------------------------------
1 | package request
2 |
3 | type Login struct {
4 | Username string `json:"username" validate:"required|alphaNum|minLen:1|maxLen:18"`
5 | Password string `json:"password" validate:"required|alphaDash|minLen:6|maxLen:18"`
6 | CaptchaId string `json:"captchaId" validata:"required"`
7 | Captcha string `json:"captcha" validate:"required|minLen:4|maxLen:4"`
8 | }
9 | type User struct {
10 | Username string `json:"username" validate:"required|alphaNum|minLen:1|maxLen:18"`
11 | NickName *string `json:"nickName" validate:"required|minLen:1|maxLen:12"`
12 | Password string `json:"password" validate:"-"`
13 | Avatar string `json:"avatar" validate:"-"`
14 | Phone string `json:"phone" validate:"-"`
15 | Email string `json:"email" validate:"-"`
16 | Status int `json:"status" validate:"-"`
17 | DeptId int `json:"deptId" validate:"-"`
18 | RoleIds []int `json:"roleIds" validate:"ints"`
19 | }
20 | type UserList struct {
21 | Username string `json:"username" validate:"-"`
22 | NickName string `json:"nickName" validata:"-"`
23 | Phone string `json:"phone" validate:"-"`
24 | Status *int `json:"status" validate:"-"`
25 | DeptId []int `json:"deptId" validate:"-"`
26 | PageNum *int `json:"pageNum" validate:"required"`
27 | PageSize *int `json:"pageSize" validate:"required"`
28 | }
29 |
30 | type UserRoleUpdate struct {
31 | Id *int `json:"id" validate:"required"`
32 | RoleIds []int `json:"roleIds" validate:"ints"`
33 | }
34 |
35 | type UserUpdate struct {
36 | Id *int `json:"id" validate:"required"`
37 | Username string `json:"username" validate:"required|alphaNum|minLen:1|maxLen:18"`
38 | NickName *string `json:"nickName" validate:"required|minLen:1|maxLen:12"`
39 | Password string `json:"password" validate:"-"`
40 | Avatar string `json:"avatar" validate:"-"`
41 | Phone string `json:"phone" validate:"-"`
42 | Email string `json:"email" validate:"-"`
43 | Status int `json:"status" validate:"-"`
44 | DeptId int `json:"deptId" validate:"-"`
45 | RoleIds []int `json:"roleIds" validate:"ints"`
46 | }
47 | type UserDel struct {
48 | Ids []int `json:"ids" validate:"required"`
49 | }
50 | type UserMove struct {
51 | DeptId *int `json:"deptId" validate:"required"`
52 | Ids []int `json:"ids" validate:"required"`
53 | }
54 | type UserAvatarUpdate struct {
55 | Url string `json:"url"`
56 | }
57 | type UserBaseInfoUpdate struct {
58 | NickName *string `json:"nickName" validate:"required|minLen:1|maxLen:12"`
59 | Phone string `json:"phone" validate:"-"`
60 | Email string `json:"email" validate:"-"`
61 | }
62 | type UserPasswordUpdate struct {
63 | OldPassword string `json:"oldPassword" validate:"required|alphaDash|minLen:6|maxLen:18"`
64 | NewPassword string `json:"newPassword" validate:"required|alphaDash|minLen:6|maxLen:18"`
65 | }
66 |
--------------------------------------------------------------------------------
/app/admin/response/dept.go:
--------------------------------------------------------------------------------
1 | package response
2 |
3 | type DeptTree struct {
4 | Id int `json:"id"`
5 | Name string `json:"name"`
6 | ParentId int `json:"parentId"`
7 | Sort int `json:"sort"`
8 | Children []DeptTree `json:"children"`
9 | }
10 |
--------------------------------------------------------------------------------
/app/admin/response/log.go:
--------------------------------------------------------------------------------
1 | package response
2 |
3 | import "time"
4 |
5 | type LogList struct {
6 | Id int `json:"id"`
7 | Username string `json:"username" xorm:"'username'"`
8 | Method string `json:"method"`
9 | Action string `json:"action"`
10 | Ip string `json:"ip"`
11 | StatusCode int `json:"statusCode"`
12 | Params string `json:"params"`
13 | Results string `json:"results"`
14 | CreatedTime time.Time `json:"createdTime"`
15 | }
16 |
--------------------------------------------------------------------------------
/app/admin/response/menu.go:
--------------------------------------------------------------------------------
1 | package response
2 |
3 | type Meta struct {
4 | Icon string `json:"icon"`
5 | Sort int `json:"sort"`
6 | IsRoot bool `json:"isRoot"`
7 | Title string `json:"title"`
8 | Type int `json:"type"`
9 | Perms string `json:"perms"`
10 | KeepAlive bool `json:"keepAlive"`
11 | Status int `json:"status"`
12 | Visible bool `json:"visible"`
13 | }
14 | type MenuTree struct {
15 | Path string `json:"path"`
16 | Name string `json:"name"`
17 | Meta Meta `json:"meta"`
18 | Component string `json:"component"`
19 | Id int `json:"id"`
20 | ParentId int `json:"parentId"`
21 | Children []MenuTree `json:"children"`
22 | }
23 |
--------------------------------------------------------------------------------
/app/admin/response/role.go:
--------------------------------------------------------------------------------
1 | package response
2 |
3 | type Role struct {
4 | Id int `json:"id"`
5 | Name string `json:"name"`
6 | Label string `json:"label"`
7 | Remark string `json:"remark"`
8 | Relevance int `json:"relevance"`
9 | Status int `json:"status"`
10 | MenuIds []int `json:"menuIds"`
11 | DeptIds []int `json:"deptIds"`
12 | }
13 |
--------------------------------------------------------------------------------
/app/admin/response/server.go:
--------------------------------------------------------------------------------
1 | package response
2 |
3 | type ServerInfo struct {
4 | CPU CPU `json:"cpu"`
5 | Host Host `json:"host"`
6 | RAM RAM `json:"ram"`
7 | Disk Disk `json:"disk"`
8 | Runtime Runtime `json:"runtime"`
9 | }
10 | type CPU struct {
11 | Name string `json:"name"`
12 | Cores int32 `json:"cores"`
13 | Percent int `json:"percent"`
14 | }
15 | type Host struct {
16 | OS string `json:"os"`
17 | Kernel string `json:"kernel"`
18 | Runtime string `json:"runtime"`
19 | }
20 | type RAM struct {
21 | Total float64 `json:"total"`
22 | Available float64 `json:"available"`
23 | Used float64 `json:"used"`
24 | Percent int `json:"percent"`
25 | }
26 | type Disk struct {
27 | Total float64 `json:"total"`
28 | Available float64 `json:"available"`
29 | Used float64 `json:"used"`
30 | Percent int `json:"percent"`
31 | }
32 | type Runtime struct {
33 | Version string `json:"version"`
34 | Language string `json:"language"`
35 | StartTime string `json:"startTime"`
36 | Runtime string `json:"runtime"`
37 | }
38 |
--------------------------------------------------------------------------------
/app/admin/response/user.go:
--------------------------------------------------------------------------------
1 | package response
2 |
3 | import "time"
4 |
5 | type UserList struct {
6 | Id int `json:"id"`
7 | DeptId int `json:"deptId"`
8 | Username string `json:"username"`
9 | NickName string `json:"nickName"`
10 | Phone string `json:"phone"`
11 | Email string `json:"email"`
12 | Avatar string `json:"avatar"`
13 | Status int `json:"status"`
14 | IsDeleted int `json:"isDeleted"`
15 | CreatedTime time.Time `json:"createdTime"`
16 | UpdatedTime time.Time `json:"updatedTime"`
17 | RoleIds string `json:"roleIds"`
18 | DeptName string `json:"deptName"`
19 | }
20 | type UserInfo struct {
21 | Id int `json:"id"`
22 | DeptId int `json:"deptId"`
23 | Username string `json:"username"`
24 | NickName string `json:"nickName"`
25 | Phone string `json:"phone"`
26 | Email string `json:"email"`
27 | Avatar string `json:"avatar"`
28 | Status int `json:"status"`
29 | RoleIds []int `json:"roleIds"`
30 | }
31 | type UserProfile struct {
32 | Id int `json:"id"`
33 | DeptId int `json:"deptId"`
34 | Username string `json:"username"`
35 | NickName string `json:"nickName"`
36 | Phone string `json:"phone"`
37 | Email string `json:"email"`
38 | Avatar string `json:"avatar"`
39 | Status int `json:"status"`
40 | IsDeleted int `json:"isDeleted"`
41 | Roles []string `json:"roles"`
42 | DeptName string `json:"deptName" xorm:"'name'"`
43 | RoleNames []string `json:"roleNames"`
44 | CreatedTime time.Time `json:"createdTime"`
45 | UpdatedTime time.Time `json:"updatedTime"`
46 | }
47 |
--------------------------------------------------------------------------------
/app/admin/routers/common.go:
--------------------------------------------------------------------------------
1 | package routers
2 |
3 | import (
4 | "seed-admin/common/middlewares"
5 | )
6 |
7 | func (admin *Admin) useCommon() {
8 | router := admin.router.Group("common").Use(middlewares.JwtAuth())
9 | {
10 | router.POST("/uploadImages", admin.Common.UploadImages)
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/admin/routers/dept.go:
--------------------------------------------------------------------------------
1 | package routers
2 |
3 | import (
4 | "seed-admin/common/auth"
5 | "seed-admin/common/middlewares"
6 | )
7 |
8 | func (admin *Admin) useDept() {
9 | router := admin.router.Group("dept").Use(middlewares.JwtAuth()).Use(middlewares.OperationRecorder())
10 | {
11 | router.GET("/list", auth.Perms([]string{"sys:dept:list"}), admin.Dept.List)
12 | router.GET("/info", auth.Perms([]string{"sys:dept:info"}), admin.Dept.Info)
13 | router.POST("/add", auth.Perms([]string{"sys:dept:add"}), admin.Dept.Add)
14 | router.POST("/update", auth.Perms([]string{"sys:dept:update"}), admin.Dept.Update)
15 | router.POST("/del", auth.Perms([]string{"sys:dept:del"}), admin.Dept.Del)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/admin/routers/dict.go:
--------------------------------------------------------------------------------
1 | package routers
2 |
3 | import (
4 | "seed-admin/common/auth"
5 | "seed-admin/common/middlewares"
6 | )
7 |
8 | func (admin *Admin) useDict() {
9 | router := admin.router.Group("dict").Use(middlewares.JwtAuth()).Use(middlewares.OperationRecorder())
10 | {
11 | router.GET("/list", auth.Perms([]string{"sys:dict:list"}), admin.Dict.List)
12 | router.GET("/info", auth.Perms([]string{"sys:dict:info"}), admin.Dict.Info)
13 | router.POST("/add", auth.Perms([]string{"sys:dict:add"}), admin.Dict.Add)
14 | router.POST("/update", auth.Perms([]string{"sys:dict:update"}), admin.Dict.Update)
15 | router.POST("/del", auth.Perms([]string{"sys:dict:del"}), admin.Dict.Del)
16 | router.GET("/dataList", auth.Perms([]string{"sys:dict:details:list"}), admin.Dict.DataList)
17 | router.GET("/dataInfo", auth.Perms([]string{"sys:dict:details:info"}), admin.Dict.DataInfo)
18 | router.POST("/dataAdd", auth.Perms([]string{"sys:dict:details:add"}), admin.Dict.DataAdd)
19 | router.POST("/dataUpdate", auth.Perms([]string{"sys:dict:details:update"}), admin.Dict.DataUpdate)
20 | router.POST("/dataDel", auth.Perms([]string{"sys:dict:details:del"}), admin.Dict.DataDel)
21 | router.GET("/typeData", admin.Dict.TypeData)
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/admin/routers/index.go:
--------------------------------------------------------------------------------
1 | package routers
2 |
3 | import (
4 | "seed-admin/app/admin/controllers"
5 |
6 | "github.com/gin-gonic/gin"
7 | )
8 |
9 | type Admin struct {
10 | router *gin.RouterGroup
11 | User *controllers.User
12 | Menu *controllers.Menu
13 | Role *controllers.Role
14 | Dept *controllers.Dept
15 | Dict *controllers.Dict
16 | Log *controllers.Log
17 | Server *controllers.Server
18 | Common *controllers.Common
19 | }
20 |
21 | func New(router *gin.RouterGroup) {
22 | controllers := new(Admin)
23 | controllers.router = router.Group("admin")
24 | controllers.useUser()
25 | controllers.useMenu()
26 | controllers.useRole()
27 | controllers.useDept()
28 | controllers.useDict()
29 | controllers.useLog()
30 | controllers.useServer()
31 | controllers.useCommon()
32 | }
33 |
--------------------------------------------------------------------------------
/app/admin/routers/log.go:
--------------------------------------------------------------------------------
1 | package routers
2 |
3 | import (
4 | "seed-admin/common/auth"
5 | "seed-admin/common/middlewares"
6 | )
7 |
8 | func (admin *Admin) useLog() {
9 | router := admin.router.Group("log").Use(middlewares.JwtAuth())
10 | {
11 | router.GET("/list", auth.Perms([]string{"sys:log:list"}), admin.Log.List)
12 | router.POST("/del", auth.Perms([]string{"sys:log:del"}), admin.Log.Del)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/admin/routers/menu.go:
--------------------------------------------------------------------------------
1 | package routers
2 |
3 | import (
4 | "seed-admin/common/auth"
5 | "seed-admin/common/middlewares"
6 | )
7 |
8 | func (admin *Admin) useMenu() {
9 | router := admin.router.Group("menu").Use(middlewares.JwtAuth()).Use(middlewares.OperationRecorder())
10 | {
11 | router.GET("/permMenu", admin.Menu.PermMenu)
12 | router.GET("/list", auth.Perms([]string{"sys:menu:list"}), admin.Menu.List)
13 | router.GET("/info", auth.Perms([]string{"sys:menu:info"}), admin.Menu.Info)
14 | router.POST("/add", auth.Perms([]string{"sys:menu:add"}), admin.Menu.Add)
15 | router.POST("/update", auth.Perms([]string{"sys:menu:update"}), admin.Menu.Update)
16 | router.POST("/del", auth.Perms([]string{"sys:menu:del"}), admin.Menu.Del)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/admin/routers/role.go:
--------------------------------------------------------------------------------
1 | package routers
2 |
3 | import (
4 | "seed-admin/common/auth"
5 | "seed-admin/common/middlewares"
6 | )
7 |
8 | func (admin *Admin) useRole() {
9 | router := admin.router.Group("role").Use(middlewares.JwtAuth()).Use(middlewares.OperationRecorder())
10 | {
11 | router.GET("/list", auth.Perms([]string{"sys:role:list"}), admin.Role.List)
12 | router.GET("/info", auth.Perms([]string{"sys:role:info"}), admin.Role.Info)
13 | router.POST("/add", auth.Perms([]string{"sys:role:add"}), admin.Role.Add)
14 | router.POST("/update", auth.Perms([]string{"sys:role:update"}), admin.Role.Update)
15 | router.POST("/del", auth.Perms([]string{"sys:role:del"}), admin.Role.Del)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/admin/routers/server.go:
--------------------------------------------------------------------------------
1 | package routers
2 |
3 | import (
4 | "seed-admin/common/auth"
5 | "seed-admin/common/middlewares"
6 | )
7 |
8 | func (admin *Admin) useServer() {
9 | router := admin.router.Group("server").Use(middlewares.JwtAuth())
10 | {
11 | router.GET("/info", auth.Perms([]string{"sys:server:info"}), admin.Server.Info)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/admin/routers/user.go:
--------------------------------------------------------------------------------
1 | package routers
2 |
3 | import (
4 | "seed-admin/common/auth"
5 | "seed-admin/common/middlewares"
6 | )
7 |
8 | func (admin *Admin) useUser() {
9 | router := admin.router.Group("user")
10 | {
11 | router.POST("/login", middlewares.OperationRecorder(), admin.User.Login)
12 | router.GET("/captcha", admin.User.Captcha)
13 | jwtAuth := router.Group("").Use(middlewares.JwtAuth(), middlewares.OperationRecorder())
14 | {
15 | jwtAuth.GET("/person", admin.User.Person)
16 | jwtAuth.POST("/list", auth.Perms([]string{"sys:user:list"}), admin.User.List)
17 | jwtAuth.POST("/add", auth.Perms([]string{"sys:user:add"}), admin.User.Add)
18 | jwtAuth.POST("/del", auth.Perms([]string{"sys:user:del"}), admin.User.Del)
19 | jwtAuth.GET("/info", auth.Perms([]string{"sys:user:info"}), admin.User.Info)
20 | jwtAuth.POST("/update", auth.Perms([]string{"sys:user:update"}), admin.User.Update)
21 | jwtAuth.POST("/move", auth.Perms([]string{"sys:user:move"}), admin.User.Move)
22 | jwtAuth.POST("/updateUserRole", auth.Perms([]string{"sys:user:updateUserRole"}), admin.User.UpdateUserRole)
23 | jwtAuth.POST("/updateAvatar", admin.User.UpdateAvatar)
24 | jwtAuth.POST("/updateBaseInfo", admin.User.UpdateBaseInfo)
25 | jwtAuth.POST("/updatePassword", admin.User.UpdatePassword)
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/admin/services/common.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "io/ioutil"
7 | "mime/multipart"
8 | "os"
9 | "seed-admin/app/admin/entity"
10 | "seed-admin/common"
11 | "seed-admin/utils"
12 | "strings"
13 | "time"
14 |
15 | "github.com/gin-gonic/gin"
16 | "github.com/gofrs/uuid"
17 | )
18 |
19 | type CommonService struct{}
20 |
21 | // 上传文件
22 | func (*CommonService) Uploads(ctx *gin.Context) (map[string]any, error) {
23 | fileType := ctx.Request.FormValue("type")
24 | file, header, err := ctx.Request.FormFile("file")
25 | if err != nil {
26 | return nil, err
27 | }
28 | // 允许上传的后缀列表
29 | allowList := []string{"image/png", "image/jpeg", "image/jpg", "image/gif"}
30 | // 判断大小
31 | if header.Size > (1024*1024)*common.CONFIG.Int64("upload.fileSize") {
32 | return nil, errors.New("上传的文件大小超过限制")
33 | }
34 | // 判断格式
35 | if !utils.SliceIncludes(allowList, header.Header.Get("Content-Type")) {
36 | return nil, errors.New("禁止上传此格式")
37 | }
38 | url, id, err := local(file, header, fileType, ctx.Request.Host, ctx.Request.Header.Get("X-Forwarded-Proto"))
39 | if err != nil {
40 | return nil, err
41 | }
42 | return map[string]any{
43 | "url": url,
44 | "id": id,
45 | }, nil
46 | }
47 |
48 | // 本地上传
49 | func local(file multipart.File, header *multipart.FileHeader, fileType string, host string, scheme string) (string, int, error) {
50 | // 读取byte
51 | b, err := ioutil.ReadAll(file)
52 | if err != nil {
53 | return "", 0, err
54 | }
55 | // 如果uploads目录不存在则创建
56 | uploadsPath := fmt.Sprintf(".%v%v", common.CONFIG.String("app.staticPath"), common.CONFIG.String("upload.path"))
57 | if ok, _ := utils.PathExists(uploadsPath); !ok {
58 | if err := os.Mkdir(uploadsPath, 0777); err != nil {
59 | return "", 0, err
60 | }
61 | }
62 | // 当日的目录不存在则创建
63 | toDayPath := fmt.Sprintf("%v/%v", uploadsPath, time.Now().Format("2006-01-02"))
64 | if ok, _ := utils.PathExists(toDayPath); !ok {
65 | if err := os.Mkdir(toDayPath, 0777); err != nil {
66 | return "", 0, err
67 | }
68 | }
69 | // 生成UUID文件名
70 | fileName, err := uuid.NewV4()
71 | if err != nil {
72 | return "", 0, err
73 | }
74 | // 获取格式
75 | suffix := strings.Split(header.Filename, ".")[1]
76 | // 组装路径输出到文件
77 | filePath := toDayPath + "/" + fileName.String() + "." + suffix
78 | if err := ioutil.WriteFile(filePath, b, 0777); err != nil {
79 | return "", 0, err
80 | }
81 | // 判断是否有证书
82 | schemeStr := ""
83 | if scheme == "" || scheme == "http" {
84 | schemeStr = "http://"
85 | } else {
86 | schemeStr = "https://"
87 | }
88 | // 组装url
89 | url := fmt.Sprintf("%v%v%v", schemeStr, host, strings.TrimPrefix(filePath, "."))
90 | typeId := 1
91 | if ok, err := common.DB.Table("admin_uploads_type").Where("label = ?", fileType).Cols("id").Get(&typeId); !ok {
92 | if err != nil {
93 | return "", 0, err
94 | }
95 | return "", 0, errors.New("图片分类不存在")
96 | }
97 | // 入库
98 | upload := &entity.AdminUploads{
99 | Name: header.Filename,
100 | Url: url,
101 | Type: typeId,
102 | }
103 | if _, err := common.DB.Insert(upload); err != nil {
104 | return "", 0, err
105 | }
106 | return upload.Url, upload.Id, nil
107 | }
108 |
--------------------------------------------------------------------------------
/app/admin/services/dept.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "errors"
5 | "seed-admin/app/admin/entity"
6 | "seed-admin/app/admin/request"
7 | "seed-admin/app/admin/response"
8 | "seed-admin/common"
9 | "seed-admin/utils"
10 | )
11 |
12 | type DeptService struct {
13 | authCache utils.AuthCache
14 | }
15 |
16 | // 获取所有部门
17 | func (*DeptService) GetAllDept() ([]response.DeptTree, error) {
18 | depts := make([]response.DeptTree, 0)
19 | sql := `
20 | SELECT
21 | *
22 | FROM
23 | admin_sys_dept
24 | ORDER BY
25 | sort
26 | `
27 | if err := common.DB.SQL(sql).Find(&depts); err != nil {
28 | return nil, err
29 | }
30 | res := deptTree(depts, 0)
31 | return res, nil
32 | }
33 |
34 | // 增加部门
35 | func (*DeptService) AddDept(params *request.DeptAdd) error {
36 | dept := &entity.AdminSysDept{
37 | Name: *params.Name,
38 | ParentId: *params.ParentId,
39 | Sort: params.Sort,
40 | }
41 | if _, err := common.DB.Insert(dept); err != nil {
42 | return err
43 | }
44 | return nil
45 | }
46 |
47 | // 更新部门
48 | func (*DeptService) UpdateDept(params *request.DeptUpdate) error {
49 | if *params.Id == 1 {
50 | if *params.ParentId != 0 {
51 | return errors.New("顶级部门的上级不可更改")
52 | }
53 | }
54 | if *params.Id == *params.ParentId {
55 | return errors.New("不能自己成为自己的上级")
56 | }
57 | dept := &entity.AdminSysDept{
58 | Name: *params.Name,
59 | ParentId: *params.ParentId,
60 | Sort: params.Sort,
61 | }
62 | if _, err := common.DB.Where("id = ?", params.Id).AllCols().Update(dept); err != nil {
63 | return err
64 | }
65 | return nil
66 | }
67 |
68 | // 删除部门
69 | func (*DeptService) DelDept(params *request.DeptDel) error {
70 | if *params.Pid == 1 {
71 | return errors.New("顶级部门不可删除")
72 | }
73 | session := common.DB.NewSession()
74 | defer session.Close()
75 | // 事务开启
76 | if err := session.Begin(); err != nil {
77 | return err
78 | }
79 | pid := 0
80 | if ok, err := session.Table("admin_sys_dept").Where("id = ?", params.Pid).Cols("parent_id").Get(&pid); !ok {
81 | if err != nil {
82 | return err
83 | }
84 | return errors.New("获取父级的上级ID失败")
85 | }
86 | // 删除部门及子部门
87 | if _, err := session.Table("admin_sys_dept").In("id", params.Ids).Delete(); err != nil {
88 | return err
89 | }
90 | // 处理用户
91 | if params.UserDel {
92 | if _, err := session.Table("admin_sys_user").In("dept_id", params.Ids).Delete(); err != nil {
93 | return err
94 | }
95 | } else {
96 | user := new(entity.AdminSysUser)
97 | user.DeptId = pid
98 | if _, err := session.In("dept_id", params.Ids).Cols("dept_id").Update(user); err != nil {
99 | return err
100 | }
101 | }
102 | return session.Commit()
103 | }
104 |
105 | // 获取部门信息
106 | func (*DeptService) GetInfo(id *int) (*entity.AdminSysDept, error) {
107 | dept := new(entity.AdminSysDept)
108 | if ok, err := common.DB.Where("id = ?", id).Get(dept); !ok {
109 | if err != nil {
110 | return nil, err
111 | }
112 | return nil, errors.New("获取部门信息失败")
113 | }
114 | return dept, nil
115 | }
116 |
117 | // 根据用户ID获取部门权限
118 | func (deptService *DeptService) GetIds(userId int) ([]int, error) {
119 | deptIds := make([]int, 0)
120 | roleIds, err := deptService.authCache.GetRoleIds(userId)
121 | if err != nil {
122 | return nil, err
123 | }
124 | if err := common.DB.Table("admin_sys_role_dept").In("role_id", roleIds).Cols("dept_id").Find(&deptIds); err != nil {
125 | if err != nil {
126 | return nil, err
127 | }
128 | }
129 | return deptIds, nil
130 | }
131 |
132 | // 递归获取部门树
133 | func deptTree(depts []response.DeptTree, pid int) []response.DeptTree {
134 | var nodes = make([]response.DeptTree, 0, len(depts))
135 | for _, item := range depts {
136 | if item.ParentId == pid {
137 | item.Children = append(item.Children, deptTree(depts, item.Id)...)
138 | nodes = append(nodes, item)
139 | }
140 | }
141 | return nodes
142 | }
143 |
--------------------------------------------------------------------------------
/app/admin/services/dict.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "errors"
5 | "seed-admin/app/admin/entity"
6 | "seed-admin/app/admin/request"
7 | "seed-admin/common"
8 | )
9 |
10 | type DictService struct{}
11 |
12 | // 获取所有字典
13 | func (*DictService) GetAllDictType(params *request.DictList) ([]entity.AdminSysDictType, int64, error) {
14 | dict := new(entity.AdminSysDictType)
15 | dicts := make([]entity.AdminSysDictType, 0)
16 | count, err := common.DB.
17 | Where("name LIKE ? AND status LIKE ?", "%"+params.Name+"%", "%"+params.Status+"%").
18 | Count(dict)
19 | if err != nil {
20 | return nil, 0, err
21 | }
22 | if err := common.DB.
23 | Where("name LIKE ? AND status LIKE ?", "%"+params.Name+"%", "%"+params.Status+"%").
24 | Limit(*params.PageSize, (*params.PageNum-1)*(*params.PageSize)).
25 | Find(&dicts); err != nil {
26 | return nil, 0, err
27 | }
28 | return dicts, count, nil
29 | }
30 |
31 | // 增加字典
32 | func (*DictService) AddDictType(params *request.DictAdd) error {
33 | dictType := &entity.AdminSysDictType{
34 | Name: *params.Name,
35 | Type: *params.Type,
36 | Status: params.Status,
37 | }
38 | // 插入角色表
39 | if _, err := common.DB.Insert(dictType); err != nil {
40 | return err
41 | }
42 | return nil
43 | }
44 |
45 | // 获取字典类型信息
46 | func (*DictService) GetDictTypeInfo(id *int) (*entity.AdminSysDictType, error) {
47 | dictType := new(entity.AdminSysDictType)
48 | if ok, err := common.DB.Where("id = ?", id).Get(dictType); !ok {
49 | if err != nil {
50 | return nil, err
51 | }
52 | return nil, errors.New("获取字典类型信息失败")
53 | }
54 | return dictType, nil
55 | }
56 |
57 | // 更新字典
58 | func (*DictService) UpdateDictType(params *request.DictUpdate) error {
59 | dictType := &entity.AdminSysDictType{
60 | Name: *params.Name,
61 | Type: *params.Type,
62 | Status: params.Status,
63 | }
64 | if _, err := common.DB.Where("id = ?", params.Id).AllCols().Update(dictType); err != nil {
65 | return err
66 | }
67 | return nil
68 | }
69 |
70 | // 删除字典
71 | func (*DictService) DelDictType(params *request.DictDel) error {
72 | session := common.DB.NewSession()
73 | defer session.Close()
74 | if err := session.Begin(); err != nil {
75 | return err
76 | }
77 | // 删除类型
78 | dictType := new(entity.AdminSysDictType)
79 | if _, err := common.DB.In("id", params.Ids).Delete(dictType); err != nil {
80 | return err
81 | }
82 | dictData := new(entity.AdminSysDictData)
83 | // 删除类型下的数据
84 | if _, err := session.In("pid", params.Ids).Delete(dictData); err != nil {
85 | return err
86 | }
87 | return session.Commit()
88 | }
89 |
90 | // 获取词典的全部数据
91 | func (*DictService) GetAllDictData(params *request.DictDataList) ([]entity.AdminSysDictData, int64, error) {
92 | dictData := new(entity.AdminSysDictData)
93 | dictDatas := make([]entity.AdminSysDictData, 0)
94 | count, err := common.DB.
95 | Where("label LIKE ? AND status LIKE ?", "%"+params.Label+"%", "%"+params.Status+"%").
96 | Where("pid = ?", params.Pid).Count(dictData)
97 | if err != nil {
98 | return nil, 0, err
99 | }
100 | if err := common.DB.
101 | Where("label LIKE ? AND status LIKE ?", "%"+params.Label+"%", "%"+params.Status+"%").
102 | Where("pid = ?", params.Pid).
103 | Limit(*params.PageSize, (*params.PageNum-1)*(*params.PageSize)).
104 | Find(&dictDatas); err != nil {
105 | return nil, 0, err
106 | }
107 | return dictDatas, count, nil
108 | }
109 |
110 | // 增加字典数据
111 | func (*DictService) AddDictData(params *request.DictDataAdd) error {
112 | dictData := &entity.AdminSysDictData{
113 | Pid: *params.Pid,
114 | Label: *params.Label,
115 | Value: *params.Value,
116 | Status: params.Status,
117 | }
118 | // 插入角色表
119 | if _, err := common.DB.Insert(dictData); err != nil {
120 | return err
121 | }
122 | return nil
123 | }
124 |
125 | // 获取字典数据信息
126 | func (*DictService) GetDictDataInfo(id *int) (*entity.AdminSysDictData, error) {
127 | dictData := new(entity.AdminSysDictData)
128 | if ok, err := common.DB.Where("id = ?", id).Get(dictData); !ok {
129 | if err != nil {
130 | return nil, err
131 | }
132 | return nil, errors.New("获取字典数据信息失败")
133 | }
134 | return dictData, nil
135 | }
136 |
137 | // 更新字典数据
138 | func (*DictService) UpdateDictData(params *request.DictDataUpdate) error {
139 | dictData := &entity.AdminSysDictData{
140 | Pid: *params.Pid,
141 | Label: *params.Label,
142 | Value: *params.Value,
143 | Status: params.Status,
144 | }
145 | if _, err := common.DB.Where("id = ?", params.Id).AllCols().Update(dictData); err != nil {
146 | return err
147 | }
148 | return nil
149 | }
150 |
151 | // 删除字典数据
152 | func (*DictService) DelDictData(params *request.DictDataDel) error {
153 | dictData := new(entity.AdminSysDictData)
154 | // 删除类型下的数据
155 | if _, err := common.DB.In("id", params.Ids).Delete(dictData); err != nil {
156 | return err
157 | }
158 | return nil
159 | }
160 |
161 | // 根据类型获取数据
162 | func (*DictService) GetTypeData(dictType string) ([]entity.AdminSysDictData, error) {
163 | dictData := make([]entity.AdminSysDictData, 0)
164 | if err := common.DB.Table("admin_sys_dict_data").
165 | Alias("data").
166 | Join("INNER", []string{"admin_sys_dict_type", "type"}, "data.pid = type.id").
167 | Where("type = ?", dictType).
168 | Find(&dictData); err != nil {
169 | return nil, err
170 | }
171 | return dictData, nil
172 | }
173 |
--------------------------------------------------------------------------------
/app/admin/services/log.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "seed-admin/app/admin/entity"
5 | "seed-admin/app/admin/request"
6 | "seed-admin/app/admin/response"
7 | "seed-admin/common"
8 | )
9 |
10 | type LogService struct{}
11 |
12 | // 增加日志
13 | func (*LogService) AddLog(log *entity.AdminSysLog) error {
14 | if _, err := common.DB.Insert(log); err != nil {
15 | return err
16 | }
17 | return nil
18 | }
19 |
20 | // 删除日志
21 | func (*LogService) DelLog(params *request.LogDel) error {
22 | // 删除用户
23 | if _, err := common.DB.Table("admin_sys_log").In("id", params.Ids).Delete(); err != nil {
24 | return err
25 | }
26 | return nil
27 | }
28 |
29 | // 获取全部日志
30 | func (*LogService) GetAllLog(params *request.LogList) ([]response.LogList, int64, error) {
31 | logs := make([]response.LogList, 0)
32 | count, err := common.DB.SQL(`
33 | SELECT count(l.id) FROM
34 | admin_sys_log AS l
35 | LEFT JOIN
36 | admin_sys_user AS u
37 | ON
38 | l.user_id = u.id
39 | WHERE
40 | l.method LIKE ?
41 | AND
42 | l.action LIKE ?
43 | AND
44 | l.ip LIKE ?`, "%"+params.Method+"%", "%"+params.Action+"%", "%"+params.Ip+"%").
45 | Count()
46 | if err != nil {
47 | return nil, 0, err
48 | }
49 | if err := common.DB.SQL(`
50 | SELECT * FROM
51 | admin_sys_log AS l
52 | LEFT JOIN
53 | admin_sys_user AS u
54 | ON
55 | l.user_id = u.id
56 | WHERE
57 | l.method LIKE ?
58 | AND
59 | l.action LIKE ?
60 | AND
61 | l.ip LIKE ?
62 | ORDER BY l.id DESC
63 | LIMIT ?,?`, "%"+params.Method+"%", "%"+params.Action+"%", "%"+params.Ip+"%", (*params.PageNum-1)*(*params.PageSize), *params.PageSize).
64 | Find(&logs); err != nil {
65 | return nil, 0, err
66 | }
67 | return logs, count, nil
68 | }
69 |
--------------------------------------------------------------------------------
/app/admin/services/menu.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "fmt"
5 | "seed-admin/app/admin/entity"
6 | "seed-admin/app/admin/request"
7 | "seed-admin/app/admin/response"
8 | "seed-admin/common"
9 | "seed-admin/utils"
10 | "strconv"
11 | )
12 |
13 | type MenuService struct {
14 | authCache utils.AuthCache
15 | }
16 |
17 | // 获取用户菜单
18 | func (menuService *MenuService) GetMenu(userId int) ([]response.MenuTree, error) {
19 | roleIds, err := menuService.authCache.GetRoleIds(userId)
20 | if err != nil {
21 | return nil, err
22 | }
23 | // 利用工具库函数把权限ID数组转成字符串
24 | roleIdsIn := utils.SliceToInStr(roleIds)
25 | // 组装sql
26 | sql := fmt.Sprintf(`
27 | SELECT m.* FROM admin_sys_menu m
28 | JOIN admin_sys_role_menu rm
29 | on m.id = rm.menu_id AND rm.role_id in (%v)
30 | WHERE status = 0 GROUP BY m.id ORDER BY m.sort`, roleIdsIn)
31 | res, err := common.DB.QueryString(sql)
32 | if err != nil {
33 | return nil, err
34 | }
35 | menus := assemblyMenu(res)
36 | return menus, nil
37 | }
38 |
39 | // 获取用户权限
40 | func (menuService *MenuService) GetPerms(userId int) ([]string, error) {
41 | roleIds, err := menuService.authCache.GetRoleIds(userId)
42 | perms := make([]string, 0)
43 | if err != nil {
44 | return nil, err
45 | }
46 | if len(roleIds) == 0 {
47 | return perms, nil
48 | }
49 | // 利用工具库函数把权限ID数组转成字符串
50 | roleIdsIn := utils.SliceToInStr(roleIds)
51 | // 组装SQL
52 | sql := fmt.Sprintf(`
53 | SELECT DISTINCT m.perms FROM admin_sys_menu m
54 | JOIN admin_sys_role_menu rm
55 | on m.id = rm.menu_id AND rm.role_id in(%v)
56 | WHERE m.perms IS NOT NULL AND m.perms != "" AND m.status = 0
57 | `, roleIdsIn)
58 | if err := common.DB.SQL(sql).Find(&perms); err != nil {
59 | return nil, err
60 | }
61 | menuService.authCache.SetMenuPerms(userId, perms)
62 | return perms, nil
63 | }
64 |
65 | // 获取全部菜单
66 | func (*MenuService) GetAllMenu(name string, status string) ([]response.MenuTree, error) {
67 | // 组装sql
68 | sql := fmt.Sprintf(`
69 | SELECT * FROM admin_sys_menu
70 | WHERE name LIKE '%s' AND status LIKE '%s'
71 | ORDER BY sort`, "%"+name+"%", "%"+status+"%")
72 | res, err := common.DB.QueryString(sql)
73 | if err != nil {
74 | return nil, err
75 | }
76 | menus := assemblyMenu(res)
77 | return menus, nil
78 | }
79 |
80 | // 添加菜单
81 | func (*MenuService) AddMenu(params *request.Menu) error {
82 | menu := entity.AdminSysMenu{
83 | ParentId: *params.ParentId,
84 | Name: *params.Name,
85 | RouterName: params.RouterName,
86 | RouterPath: params.RouterPath,
87 | PagePath: params.PagePath,
88 | Perms: params.Perms,
89 | Type: *params.Type,
90 | Icon: params.Icon,
91 | Sort: params.Sort,
92 | KeepAlive: params.KeepAlive,
93 | Status: params.Status,
94 | }
95 | if _, err := common.DB.Insert(menu); err != nil {
96 | return err
97 | }
98 | return nil
99 | }
100 |
101 | // 编辑菜单
102 | func (menuService *MenuService) UpdateMenu(params *request.Menu) error {
103 | menu := entity.AdminSysMenu{
104 | ParentId: *params.ParentId,
105 | Name: *params.Name,
106 | RouterName: params.RouterName,
107 | RouterPath: params.RouterPath,
108 | PagePath: params.PagePath,
109 | Perms: params.Perms,
110 | Type: *params.Type,
111 | Icon: params.Icon,
112 | Sort: params.Sort,
113 | KeepAlive: params.KeepAlive,
114 | Status: params.Status,
115 | Visible: params.Visible,
116 | }
117 | if _, err := common.DB.Where("id = ?", params.Id).AllCols().Update(menu); err != nil {
118 | return err
119 | }
120 | if err := menuService.authCache.UpdateAllPerm(); err != nil {
121 | return err
122 | }
123 | return nil
124 | }
125 |
126 | // 删除菜单
127 | func (menuService *MenuService) DelMenu(params *request.MenuDel) error {
128 | if _, err := common.DB.Table("admin_sys_menu").In("id", params.Ids).Delete(); err != nil {
129 | return err
130 | }
131 | if err := menuService.authCache.UpdateAllPerm(); err != nil {
132 | return err
133 | }
134 | return nil
135 | }
136 |
137 | // 获取菜单信息
138 | func (*MenuService) GetInfo(id int) (*entity.AdminSysMenu, error) {
139 | menu := &entity.AdminSysMenu{
140 | Id: id,
141 | }
142 | if ok, err := common.DB.Get(menu); ok {
143 | return menu, nil
144 | } else {
145 | if err != nil {
146 | return nil, err
147 | }
148 | return nil, fmt.Errorf("获取菜单信息失败")
149 | }
150 | }
151 |
152 | // 组装菜单
153 | func assemblyMenu(res []map[string]string) []response.MenuTree {
154 | menus := make([]response.MenuTree, 0, len(res))
155 | if len(res) == 0 {
156 | return menus
157 | }
158 | pid, _ := strconv.Atoi(res[0]["parent_id"])
159 | for _, item := range res {
160 | parentId, _ := strconv.Atoi(item["parent_id"])
161 | if pid > parentId {
162 | pid = parentId
163 | }
164 | isRoot := false
165 | if parentId == 0 {
166 | isRoot = true
167 | }
168 | visible := false
169 | if item["visible"] == "0" {
170 | visible = true
171 | }
172 | id, _ := strconv.Atoi(item["id"])
173 | t, _ := strconv.Atoi(item["type"])
174 | sort, _ := strconv.Atoi(item["sort"])
175 | keepAlive, _ := strconv.ParseBool(item["keep_alive"])
176 | status, _ := strconv.Atoi(item["status"])
177 | menus = append(menus, response.MenuTree{
178 | Path: item["router_path"],
179 | Name: item["router_name"],
180 | Component: item["page_path"],
181 | Id: id,
182 | ParentId: parentId,
183 | Meta: response.Meta{
184 | Icon: item["icon"],
185 | Sort: sort,
186 | IsRoot: isRoot,
187 | Title: item["name"],
188 | Type: t,
189 | Perms: item["perms"],
190 | KeepAlive: keepAlive,
191 | Status: status,
192 | Visible: visible,
193 | },
194 | Children: []response.MenuTree{},
195 | })
196 | }
197 | m := menuTree(menus, pid)
198 | return m
199 | }
200 |
201 | // 递归获取菜单树
202 | func menuTree(menus []response.MenuTree, pid int) []response.MenuTree {
203 | var nodes = make([]response.MenuTree, 0, len(menus))
204 | for _, item := range menus {
205 | if item.ParentId == pid {
206 | item.Children = append(item.Children, menuTree(menus, item.Id)...)
207 | nodes = append(nodes, item)
208 | }
209 | }
210 | return nodes
211 | }
212 |
--------------------------------------------------------------------------------
/app/admin/services/role.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "seed-admin/app/admin/entity"
7 | "seed-admin/app/admin/request"
8 | "seed-admin/app/admin/response"
9 | "seed-admin/common"
10 | "seed-admin/utils"
11 | )
12 |
13 | type RoleService struct {
14 | authCache utils.AuthCache
15 | }
16 |
17 | // 获取全部角色
18 | func (*RoleService) GetAllRole(params *request.RoleList) ([]entity.AdminSysRole, int64, error) {
19 | role := new(entity.AdminSysRole)
20 | roles := make([]entity.AdminSysRole, 0)
21 | count, err := common.DB.Where("name LIKE ?", "%"+params.Name+"%").Count(role)
22 | if err != nil {
23 | return nil, 0, err
24 | }
25 | if err := common.DB.Where("name LIKE ?", "%"+params.Name+"%").
26 | Limit(*params.PageSize, (*params.PageNum-1)*(*params.PageSize)).
27 | Find(&roles); err != nil {
28 | return nil, 0, err
29 | }
30 | return roles, count, nil
31 | }
32 |
33 | // 获取角色信息
34 | func (*RoleService) GetInfo(id int) (*response.Role, error) {
35 | role := &entity.AdminSysRole{
36 | Id: id,
37 | }
38 | menuIds := make([]int, 0)
39 | deptIds := make([]int, 0)
40 | if ok, err := common.DB.Get(role); ok {
41 | if err := common.DB.Table("admin_sys_role_menu").Where("role_id = ?", role.Id).Cols("menu_id").Find(&menuIds); err != nil {
42 | return nil, err
43 | }
44 | if err := common.DB.Table("admin_sys_role_dept").Where("role_id = ?", role.Id).Cols("dept_id").Find(&deptIds); err != nil {
45 | return nil, err
46 | }
47 | res := &response.Role{
48 | Id: role.Id,
49 | Name: role.Name,
50 | Label: role.Label,
51 | Remark: role.Remark,
52 | Relevance: role.Relevance,
53 | MenuIds: menuIds,
54 | DeptIds: deptIds,
55 | }
56 | return res, nil
57 | } else {
58 | if err != nil {
59 | return nil, err
60 | }
61 | return nil, fmt.Errorf("获取角色信息失败")
62 | }
63 | }
64 |
65 | // 增加一个角色
66 | func (*RoleService) AddRole(params *request.Role) error {
67 | session := common.DB.NewSession()
68 | defer session.Close()
69 | if err := session.Begin(); err != nil {
70 | return err
71 | }
72 | role := &entity.AdminSysRole{
73 | Name: *params.Name,
74 | Label: *params.Label,
75 | Remark: params.Remark,
76 | Relevance: params.Relevance,
77 | }
78 | // 插入角色表
79 | if _, err := session.Insert(role); err != nil {
80 | return err
81 | }
82 | if len(params.MenuIds) == 0 {
83 | return errors.New("至少需要选择一个菜单")
84 | }
85 | // 插入角色菜单关联表
86 | roleMenu := make([]entity.AdminSysRoleMenu, 0, len(params.MenuIds))
87 | for _, menuId := range params.MenuIds {
88 | roleMenu = append(roleMenu, entity.AdminSysRoleMenu{
89 | RoleId: role.Id,
90 | MenuId: menuId,
91 | })
92 | }
93 | if _, err := session.Insert(roleMenu); err != nil {
94 | return err
95 | }
96 | if len(params.DeptIds) == 0 {
97 | return session.Commit()
98 | }
99 | // 插入角色部门关联表
100 | roleDept := make([]entity.AdminSysRoleDept, 0, len(params.DeptIds))
101 | for _, deptId := range params.DeptIds {
102 | roleDept = append(roleDept, entity.AdminSysRoleDept{
103 | RoleId: role.Id,
104 | DeptId: deptId,
105 | })
106 | }
107 | if _, err := session.Insert(roleDept); err != nil {
108 | return err
109 | }
110 | return session.Commit()
111 | }
112 |
113 | // 更新角色
114 | func (roleService *RoleService) UpdateRole(params *request.RoleUpdate) error {
115 | session := common.DB.NewSession()
116 | defer session.Close()
117 | if err := session.Begin(); err != nil {
118 | return err
119 | }
120 | role := entity.AdminSysRole{
121 | Name: *params.Name,
122 | Label: *params.Label,
123 | Remark: params.Remark,
124 | Relevance: params.Relevance,
125 | }
126 | // 更新角色表
127 | if _, err := session.Where("id = ?", params.Id).AllCols().Update(role); err != nil {
128 | return err
129 | }
130 | // 删除角色原有菜单权限
131 | if _, err := session.Table("admin_sys_role_menu").Where("role_id = ?", params.Id).Delete(); err != nil {
132 | return err
133 | }
134 | // 删除角色原有部门权限
135 | if _, err := session.Table("admin_sys_role_dept").Where("role_id = ?", params.Id).Delete(); err != nil {
136 | return err
137 | }
138 | if len(params.MenuIds) != 0 {
139 | // 增加新的角色权限
140 | roleMenu := make([]entity.AdminSysRoleMenu, 0, len(params.MenuIds))
141 | for _, menuId := range params.MenuIds {
142 | roleMenu = append(roleMenu, entity.AdminSysRoleMenu{
143 | RoleId: *params.Id,
144 | MenuId: menuId,
145 | })
146 | }
147 | if _, err := session.Insert(roleMenu); err != nil {
148 | return err
149 | }
150 | }
151 | if len(params.DeptIds) != 0 {
152 | // 增加新的角色部门
153 | roleDept := make([]entity.AdminSysRoleDept, 0, len(params.DeptIds))
154 | for _, deptId := range params.DeptIds {
155 | roleDept = append(roleDept, entity.AdminSysRoleDept{
156 | RoleId: *params.Id,
157 | DeptId: deptId,
158 | })
159 | }
160 | if _, err := session.Insert(roleDept); err != nil {
161 | return err
162 | }
163 | }
164 | if err := session.Commit(); err != nil {
165 | return err
166 | }
167 | if err := roleService.authCache.UpdateAllRole(); err != nil {
168 | return err
169 | }
170 | if err := roleService.authCache.UpdateAllPerm(); err != nil {
171 | return err
172 | }
173 | return nil
174 | }
175 |
176 | // 删除角色
177 | func (roleService *RoleService) DelRole(params *request.RoleDel) error {
178 | session := common.DB.NewSession()
179 | defer session.Close()
180 | if err := session.Begin(); err != nil {
181 | return err
182 | }
183 | // 删除角色所属菜单权限
184 | if _, err := session.Table("admin_sys_role_menu").In("role_id", params.Ids).Delete(); err != nil {
185 | return err
186 | }
187 | // 删除角色原有部门权限
188 | if _, err := session.Table("admin_sys_role_dept").In("role_id", params.Ids).Delete(); err != nil {
189 | return err
190 | }
191 | // 删除用户此角色
192 | if _, err := session.Table("admin_sys_user_role").In("role_id", params.Ids).Delete(); err != nil {
193 | return err
194 | }
195 | // 删除角色
196 | if _, err := session.Table("admin_sys_role").In("id", params.Ids).Delete(); err != nil {
197 | return err
198 | }
199 | if err := session.Commit(); err != nil {
200 | return err
201 | }
202 | if err := roleService.authCache.UpdateAllRole(); err != nil {
203 | return err
204 | }
205 | if err := roleService.authCache.UpdateAllPerm(); err != nil {
206 | return err
207 | }
208 | return nil
209 | }
210 |
--------------------------------------------------------------------------------
/app/admin/services/server.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "fmt"
5 | "runtime"
6 | "seed-admin/app/admin/response"
7 | "seed-admin/common"
8 | "strconv"
9 | "time"
10 |
11 | "github.com/shirou/gopsutil/v3/cpu"
12 | "github.com/shirou/gopsutil/v3/disk"
13 | "github.com/shirou/gopsutil/v3/host"
14 | "github.com/shirou/gopsutil/v3/mem"
15 | )
16 |
17 | type ServerService struct{}
18 |
19 | func (*ServerService) GetSystemInfo() (*response.ServerInfo, error) {
20 | cpu, err := GetCPUInfo()
21 | if err != nil {
22 | return nil, err
23 | }
24 | host, err := GetHostInfo()
25 | if err != nil {
26 | return nil, err
27 | }
28 | ram, err := GetRAMInfo()
29 | if err != nil {
30 | return nil, err
31 | }
32 | disk, err := GetDiskInfo()
33 | if err != nil {
34 | return nil, err
35 | }
36 | runtime, err := GetRuntimeInfo()
37 | if err != nil {
38 | return nil, err
39 | }
40 | res := &response.ServerInfo{
41 | CPU: *cpu,
42 | RAM: *ram,
43 | Disk: *disk,
44 | Host: *host,
45 | Runtime: *runtime,
46 | }
47 | return res, nil
48 | }
49 |
50 | // 获取CPU信息
51 | func GetCPUInfo() (*response.CPU, error) {
52 | info, err := cpu.Info()
53 | if err != nil {
54 | common.LOG.Error("获取CPU信息出错:" + err.Error())
55 | return nil, err
56 | }
57 | cpuPercent, err := cpu.Percent(time.Second, false)
58 | if err != nil {
59 | common.LOG.Error("获取CPU百分比出错:" + err.Error())
60 | return nil, err
61 | }
62 | res := &response.CPU{
63 | Name: info[0].ModelName,
64 | Cores: info[0].Cores,
65 | Percent: int(cpuPercent[0]),
66 | }
67 | return res, nil
68 | }
69 |
70 | // 获取主机信息
71 | func GetHostInfo() (*response.Host, error) {
72 | info, err := host.Info()
73 | if err != nil {
74 | common.LOG.Error("获取内存信息出错:" + err.Error())
75 | return nil, err
76 | }
77 | res := &response.Host{
78 | OS: info.OS,
79 | Kernel: info.KernelArch,
80 | Runtime: resolveTime(info.Uptime),
81 | }
82 | return res, nil
83 | }
84 |
85 | // 获取内存信息
86 | func GetRAMInfo() (*response.RAM, error) {
87 | info, err := mem.VirtualMemory()
88 | if err != nil {
89 | common.LOG.Error("获取内存信息出错:" + err.Error())
90 | return nil, err
91 | }
92 | res := &response.RAM{
93 | Total: BtoGb(info.Total),
94 | Available: BtoGb(info.Available),
95 | Used: BtoGb(info.Used),
96 | Percent: int(info.UsedPercent),
97 | }
98 | return res, nil
99 | }
100 |
101 | // 获取硬盘信息
102 | func GetDiskInfo() (*response.Disk, error) {
103 | parts, err := disk.Partitions(true)
104 | if err != nil {
105 | common.LOG.Error("获取硬盘盘符出错:" + err.Error())
106 | return nil, err
107 | }
108 | var total uint64
109 | var free uint64
110 | var used uint64
111 | var percent float64
112 | for _, part := range parts {
113 | info, err := disk.Usage(part.Mountpoint)
114 | if err != nil {
115 | common.LOG.Error("获取硬盘信息出错:" + err.Error())
116 | return nil, err
117 | }
118 | total += info.Total
119 | free += info.Free
120 | used += info.Used
121 | percent += info.UsedPercent
122 | }
123 | res := &response.Disk{
124 | Total: BtoGb(total),
125 | Available: BtoGb(free),
126 | Used: BtoGb(used),
127 | Percent: int(percent),
128 | }
129 | return res, nil
130 | }
131 |
132 | // 获取运行信息
133 | func GetRuntimeInfo() (*response.Runtime, error) {
134 | res := &response.Runtime{
135 | Version: runtime.Version(),
136 | Language: "Go",
137 | StartTime: common.StartTime.Format("2006-01-02 15:04:05"),
138 | Runtime: resolveTime(uint64(time.Since(common.StartTime).Seconds())),
139 | }
140 | return res, nil
141 | }
142 |
143 | // B转Gb
144 | func BtoGb(b uint64) float64 {
145 | str := fmt.Sprintf("%.2f", float64(b)/1024/1024/1024)
146 | res, err := strconv.ParseFloat(str, 64)
147 | if err != nil {
148 | common.LOG.Error("B转GB出错:" + err.Error())
149 | return 0
150 | }
151 | return res
152 | }
153 |
154 | // 秒转换时间
155 | func resolveTime(seconds uint64) string {
156 | days := seconds / (24 * 3600)
157 | hours := seconds % (24 * 3600) / 3600
158 | minutes := seconds % 3600 / 60
159 | return fmt.Sprintf("%v天%v小时%v分钟", days, hours, minutes)
160 | }
161 |
--------------------------------------------------------------------------------
/app/admin/services/user.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "seed-admin/app/admin/entity"
7 | "seed-admin/app/admin/request"
8 | "seed-admin/app/admin/response"
9 | "seed-admin/common"
10 | "seed-admin/utils"
11 | "strconv"
12 | "time"
13 |
14 | "github.com/golang-jwt/jwt"
15 | )
16 |
17 | type UserService struct {
18 | authCache utils.AuthCache
19 | menuService MenuService
20 | deptService DeptService
21 | }
22 |
23 | // 登录
24 | func (*UserService) Login(params *request.Login) (*entity.AdminSysUser, []int, error) {
25 | // MD5加盐 盐值一旦设定并在生产模式使用后切勿更改 随意更改后会造成之前盐值不同的用户无法登录
26 | params.Password = utils.Md5Salt(params.Password, common.CONFIG.String("app.md5Salt"))
27 | // 使用帐密做查询
28 | user := &entity.AdminSysUser{
29 | Username: params.Username,
30 | Password: params.Password,
31 | }
32 | if ok, err := common.DB.Get(user); ok {
33 | if user.Status == 1 {
34 | return nil, nil, errors.New("该用户已被禁用")
35 | }
36 | // 查询用户拥有的角色ID
37 | roleIds := make([]int, 0)
38 | if err = common.DB.Table("admin_sys_user_role").
39 | Where("user_id = ?", user.Id).Cols("role_id").
40 | Find(&roleIds); err != nil {
41 | return nil, nil, err
42 | }
43 | return user, roleIds, nil
44 | } else {
45 | if err != nil {
46 | return nil, nil, err
47 | }
48 | return nil, nil, fmt.Errorf("帐号或密码错误")
49 | }
50 | }
51 |
52 | // 生成一个token
53 | func (*UserService) GetToken(user *entity.AdminSysUser) (string, error) {
54 | j := utils.NewJwt()
55 | token, err := j.CreateToken(utils.CustomerClaims{
56 | UserId: user.Id,
57 | StandardClaims: &jwt.StandardClaims{
58 | Issuer: common.CONFIG.String("jwt.Issuer"),
59 | IssuedAt: time.Now().Unix(),
60 | NotBefore: time.Now().Unix(),
61 | ExpiresAt: time.Now().Unix() + common.CONFIG.Int64("jwt.ExpireSeconds"),
62 | },
63 | })
64 | return token, err
65 | }
66 |
67 | // 获取个人用户信息
68 | func (userService *UserService) GetPerson(userId int) (*response.UserProfile, error) {
69 | user := new(response.UserProfile)
70 | if ok, err := common.DB.
71 | Table("admin_sys_user").
72 | Alias("u").
73 | Join("INNER", []string{"admin_sys_dept", "d"}, "u.dept_id = d.id").
74 | Where("u.status = ? AND u.id = ?", 0, userId).
75 | Get(user); ok {
76 | // 查询用户拥有的角色ID
77 | roleMap := make([]map[string]any, 0)
78 | if err = common.DB.Table("admin_sys_user_role").Alias("ur").
79 | Join("INNER", []string{"admin_sys_role", "r"}, "ur.role_id = r.id").
80 | Where("user_id = ?", user.Id).Cols("r.id", "r.label", "r.name").
81 | Find(&roleMap); err != nil {
82 | return nil, err
83 | }
84 | if len(roleMap) <= 0 {
85 | return nil, errors.New("该用户没有权限")
86 | }
87 | roleIds := make([]int, 0, len(roleMap))
88 | roleLabels := make([]string, 0, len(roleMap))
89 | roleNames := make([]string, 0, len(roleMap))
90 | for _, item := range roleMap {
91 | roleIds = append(roleIds, int(item["id"].(int32)))
92 | roleLabels = append(roleLabels, item["label"].(string))
93 | roleNames = append(roleNames, item["name"].(string))
94 | }
95 | userService.authCache.SetRoleIds(userId, roleIds)
96 | userService.authCache.SetRoleLabels(userId, roleLabels)
97 | user.Roles = roleLabels
98 | user.RoleNames = roleNames
99 | return user, nil
100 | } else {
101 | if err != nil {
102 | return nil, err
103 | }
104 | return nil, fmt.Errorf("获取用户信息失败/用户被禁止使用")
105 | }
106 | }
107 |
108 | // 获取用户信息
109 | func (*UserService) GetInfo(userId int) (*response.UserInfo, error) {
110 | user := &entity.AdminSysUser{
111 | Id: userId,
112 | }
113 | if ok, err := common.DB.Omit("password").Get(user); ok {
114 | // 查询用户拥有的角色ID
115 | roleIds := make([]int, 0)
116 | if err = common.DB.Table("admin_sys_user_role").
117 | Where("user_id = ?", user.Id).Cols("role_id").
118 | Find(&roleIds); err != nil {
119 | return nil, err
120 | }
121 | res := &response.UserInfo{
122 | Id: user.Id,
123 | DeptId: user.DeptId,
124 | Username: user.Username,
125 | NickName: user.NickName,
126 | Phone: user.Phone,
127 | Email: user.Email,
128 | Avatar: user.Avatar,
129 | Status: user.Status,
130 | RoleIds: roleIds,
131 | }
132 | return res, nil
133 | } else {
134 | if err != nil {
135 | return nil, err
136 | }
137 | return nil, fmt.Errorf("获取用户信息失败")
138 | }
139 | }
140 |
141 | // 更新头像
142 | func (*UserService) UpdateAvatar(params *request.UserAvatarUpdate, userId int) error {
143 | user := &entity.AdminSysUser{
144 | Avatar: params.Url,
145 | }
146 | if _, err := common.DB.Where("id = ?", userId).Cols("avatar").Update(user); err != nil {
147 | return err
148 | }
149 | return nil
150 | }
151 |
152 | // 获取全部用户
153 | func (userService *UserService) GetAllUser(params *request.UserList, userId int) ([]response.UserList, int64, error) {
154 | user := new(entity.AdminSysUser)
155 | users := make([]response.UserList, 0)
156 | // 获取当前操作用户的部门权限ID组
157 | userDeptIds, err := userService.deptService.GetIds(userId)
158 | if err != nil {
159 | return nil, 0, err
160 | }
161 | // 处理用户部门权限条件
162 | userDeptInStr := ""
163 | userDeptCountInStr := ""
164 |
165 | if len(userDeptIds) != 0 {
166 | userDeptIdsIn := utils.SliceToInStr(userDeptIds)
167 | userDeptInStr = fmt.Sprintf("AND user.dept_id in(%v)", userDeptIdsIn)
168 | userDeptCountInStr = fmt.Sprintf("dept_id in(%v)", userDeptIdsIn)
169 | }
170 | deptInStr := ""
171 | deptCountInStr := ""
172 | if len(params.DeptId) != 0 {
173 | deptIdsIn := utils.SliceToInStr(params.DeptId)
174 | deptInStr = fmt.Sprintf("AND user.dept_id in(%v)", deptIdsIn)
175 | deptCountInStr = fmt.Sprintf("dept_id in(%v)", deptIdsIn)
176 | }
177 | status := ""
178 | if params.Status != nil {
179 | status = strconv.Itoa(*params.Status)
180 | }
181 | count, err := common.DB.
182 | Where("username LIKE ?", "%"+params.Username+"%").
183 | Where("nick_name LIKE ?", "%"+params.NickName+"%").
184 | Where("phone LIKE ?", "%"+params.Phone+"%").
185 | Where("status LIKE ?", "%"+status+"%").
186 | Where(deptCountInStr).
187 | Where(userDeptCountInStr).
188 | Count(user)
189 | if err != nil {
190 | return nil, 0, err
191 | }
192 | sql := fmt.Sprintf(`
193 | SELECT
194 | user.*,
195 | GROUP_CONCAT(role.id) AS role_ids,
196 | dept.name AS dept_name
197 | FROM
198 | admin_sys_user user
199 | LEFT JOIN admin_sys_user_role user_role ON user.id = user_role.user_id
200 | LEFT JOIN admin_sys_role role ON user_role.role_id = role.id
201 | LEFT JOIN admin_sys_dept dept ON user.dept_id = dept.id
202 | WHERE
203 | user.username LIKE '%s' AND
204 | user.nick_name LIKE '%s' AND
205 | user.phone LIKE '%s' AND
206 | user.status LIKE '%s'
207 | %v %v
208 | GROUP BY user.id
209 | LIMIT %v,%v
210 | `, "%"+params.Username+"%", "%"+params.NickName+"%", "%"+params.Phone+"%", "%"+status+"%", userDeptInStr, deptInStr, (*params.PageNum-1)*(*params.PageSize), *params.PageSize)
211 | if err := common.DB.SQL(sql).Find(&users); err != nil {
212 | return nil, 0, err
213 | }
214 | return users, count, nil
215 | }
216 |
217 | // 更新用户角色
218 | func (userService *UserService) UpdateUserRole(params *request.UserRoleUpdate) error {
219 | session := common.DB.NewSession()
220 | defer session.Close()
221 | if err := session.Begin(); err != nil {
222 | return err
223 | }
224 | // 删除用户原有角色
225 | if _, err := session.Table("admin_sys_user_role").Where("user_id = ?", params.Id).Delete(); err != nil {
226 | return err
227 | }
228 | if len(params.RoleIds) != 0 {
229 | // 给用户增加新的角色
230 | userRole := make([]entity.AdminSysUserRole, 0, len(params.RoleIds))
231 | for _, roleId := range params.RoleIds {
232 | userRole = append(userRole, entity.AdminSysUserRole{
233 | UserId: *params.Id,
234 | RoleId: roleId,
235 | })
236 | }
237 | if _, err := session.Insert(userRole); err != nil {
238 | return err
239 | }
240 | }
241 | if err := session.Commit(); err != nil {
242 | return err
243 | }
244 | // 处理缓存权限
245 | role := make([]entity.AdminSysRole, 0, len(params.RoleIds))
246 | if err := common.DB.In("id", params.RoleIds).Find(&role); err != nil {
247 | return err
248 | }
249 | userService.authCache.SetRoleIds(*params.Id, params.RoleIds)
250 | roleLabels := make([]string, 0, len(role))
251 | for _, item := range role {
252 | roleLabels = append(roleLabels, item.Label)
253 | }
254 | userService.authCache.SetRoleLabels(*params.Id, roleLabels)
255 | perms, err := userService.menuService.GetPerms(*params.Id)
256 | if err != nil {
257 | return err
258 | }
259 | userService.authCache.SetMenuPerms(*params.Id, perms)
260 | return nil
261 | }
262 |
263 | // 新增用户
264 | func (*UserService) AddUser(params *request.User) error {
265 | session := common.DB.NewSession()
266 | defer session.Close()
267 | if err := session.Begin(); err != nil {
268 | return err
269 | }
270 | user := &entity.AdminSysUser{
271 | Username: params.Username,
272 | NickName: *params.NickName,
273 | Password: utils.Md5Salt(params.Password, common.CONFIG.String("app.md5Salt")),
274 | DeptId: params.DeptId,
275 | Phone: params.Phone,
276 | Email: params.Email,
277 | Avatar: params.Avatar,
278 | Status: params.Status,
279 | }
280 | if _, err := session.Insert(user); err != nil {
281 | return err
282 | }
283 | if len(params.RoleIds) == 0 {
284 | return session.Commit()
285 | }
286 | // 增加新的角色权限
287 | userRole := make([]entity.AdminSysUserRole, 0, len(params.RoleIds))
288 | for _, roleId := range params.RoleIds {
289 | userRole = append(userRole, entity.AdminSysUserRole{
290 | UserId: user.Id,
291 | RoleId: roleId,
292 | })
293 | }
294 | if _, err := session.Insert(userRole); err != nil {
295 | return err
296 | }
297 | return session.Commit()
298 | }
299 |
300 | // 更新用户基础信息
301 | func (*UserService) UpdateBaseInfo(params *request.UserBaseInfoUpdate, userId int) error {
302 | // 更新用户表
303 | user := &entity.AdminSysUser{
304 | NickName: *params.NickName,
305 | Phone: params.Phone,
306 | Email: params.Email,
307 | }
308 | if _, err := common.DB.Where("id = ?", userId).Cols("nick_name", "phone", "email").Update(user); err != nil {
309 | return err
310 | }
311 | return nil
312 | }
313 |
314 | // 更新用户密码
315 | func (*UserService) UpdatePassword(params *request.UserPasswordUpdate, userId int) error {
316 | user := new(entity.AdminSysUser)
317 | if ok, err := common.DB.Where("id = ?", userId).Cols("password").Get(user); !ok {
318 | if err != nil {
319 | return err
320 | }
321 | return errors.New("获取旧密码失败")
322 | }
323 | if user.Password != utils.Md5Salt(params.OldPassword, common.CONFIG.String("app.md5Salt")) {
324 | return errors.New("旧密码错误,请检查旧密码是否输入正确")
325 | }
326 | user.Password = utils.Md5Salt(params.NewPassword, common.CONFIG.String("app.md5Salt"))
327 | // 更新用户表
328 | if _, err := common.DB.Where("id = ?", userId).Cols("password").Update(user); err != nil {
329 | return err
330 | }
331 | return nil
332 | }
333 |
334 | // 更新用户
335 | func (userService *UserService) UpdateUser(params *request.UserUpdate) error {
336 | session := common.DB.NewSession()
337 | defer session.Close()
338 | if err := session.Begin(); err != nil {
339 | return err
340 | }
341 | // 更新用户表
342 | user := &entity.AdminSysUser{
343 | Username: params.Username,
344 | NickName: *params.NickName,
345 | Password: utils.Md5Salt(params.Password, common.CONFIG.String("app.md5Salt")),
346 | DeptId: params.DeptId,
347 | Phone: params.Phone,
348 | Email: params.Email,
349 | Avatar: params.Avatar,
350 | Status: params.Status,
351 | }
352 | if _, err := session.Where("id = ?", params.Id).AllCols().Update(user); err != nil {
353 | return err
354 | }
355 | userRoleUpdate := &request.UserRoleUpdate{
356 | Id: params.Id,
357 | RoleIds: params.RoleIds,
358 | }
359 | if err := userService.UpdateUserRole(userRoleUpdate); err != nil {
360 | return err
361 | }
362 | if params.Status != 0 {
363 | userService.authCache.Del([]int{*params.Id})
364 | }
365 | return session.Commit()
366 | }
367 |
368 | // 删除用户
369 | func (userService *UserService) DelUser(params *request.UserDel) error {
370 | session := common.DB.NewSession()
371 | defer session.Close()
372 | if err := session.Begin(); err != nil {
373 | return err
374 | }
375 | // 删除用户所有角色
376 | if _, err := session.Table("admin_sys_user_role").In("user_id", params.Ids).Delete(); err != nil {
377 | return err
378 | }
379 | // 删除用户
380 | if _, err := session.Table("admin_sys_user").In("id", params.Ids).Delete(); err != nil {
381 | return err
382 | }
383 | if err := session.Commit(); err != nil {
384 | return err
385 | }
386 | if err := userService.authCache.Del(params.Ids); err != nil {
387 | return err
388 | }
389 | return nil
390 | }
391 |
392 | // 移动部门
393 | func (*UserService) MoveDept(params *request.UserMove) error {
394 | user := new(entity.AdminSysUser)
395 | user.DeptId = *params.DeptId
396 | if _, err := common.DB.In("id", params.Ids).Update(user); err != nil {
397 | return err
398 | }
399 | return nil
400 | }
401 |
--------------------------------------------------------------------------------
/app/api/controllers/user.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "seed-admin/common"
5 |
6 | "github.com/gin-gonic/gin"
7 | )
8 |
9 | type User struct{}
10 |
11 | func (*User) Demo(ctx *gin.Context) {
12 | common.OkMsg(ctx, "我只是个demo啊")
13 | }
14 |
--------------------------------------------------------------------------------
/app/api/routers/index.go:
--------------------------------------------------------------------------------
1 | package routers
2 |
3 | import (
4 | "seed-admin/app/api/controllers"
5 |
6 | "github.com/gin-gonic/gin"
7 | )
8 |
9 | type Api struct {
10 | router *gin.RouterGroup
11 | User *controllers.User
12 | }
13 |
14 | func New(router *gin.RouterGroup) {
15 | controllers := new(Api)
16 | controllers.router = router.Group("api")
17 | controllers.useUser()
18 | }
19 |
--------------------------------------------------------------------------------
/app/api/routers/user.go:
--------------------------------------------------------------------------------
1 | package routers
2 |
3 | func (api *Api) useUser() {
4 | router := api.router.Group("user")
5 | {
6 | router.GET("/demo", api.User.Demo)
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/app/index.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "seed-admin/app/admin/entity"
5 | admin "seed-admin/app/admin/routers"
6 | api "seed-admin/app/api/routers"
7 | "seed-admin/common"
8 |
9 | "github.com/gin-gonic/gin"
10 | )
11 |
12 | func Load(r *gin.Engine) {
13 | admin.New(&r.RouterGroup)
14 | api.New(&r.RouterGroup)
15 | dataTableSync()
16 | }
17 |
18 | // 需要同步到数据库的实体
19 | // 只允许增量同步,只允许在开发模式模式下使用
20 | // 如有需要自行添加所需同步的表...
21 | func dataTableSync() {
22 | if gin.Mode() == gin.DebugMode {
23 | err := common.DB.Sync(
24 | new(entity.AdminSysUser),
25 | new(entity.AdminSysUserRole),
26 | new(entity.AdminSysRole),
27 | new(entity.AdminSysMenu),
28 | new(entity.AdminSysRoleMenu),
29 | new(entity.AdminSysDept),
30 | new(entity.AdminSysRoleDept),
31 | new(entity.AdminSysLog),
32 | new(entity.AdminSysDictType),
33 | new(entity.AdminSysDictData),
34 | new(entity.AdminUploads),
35 | new(entity.AdminUploadsType),
36 | )
37 | if err != nil {
38 | panic(err.Error())
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/common/auth/auth.go:
--------------------------------------------------------------------------------
1 | package auth
2 |
3 | import (
4 | "errors"
5 | "seed-admin/common"
6 | "seed-admin/utils"
7 |
8 | "github.com/gin-gonic/gin"
9 | )
10 |
11 | const (
12 | // OR逻辑常量
13 | OR = "or"
14 | // AND逻辑常量
15 | AND = "and"
16 | )
17 |
18 | // 验证角色
19 | // roleLabels:要验证的角色label
20 | // logical:auth.OR || auth.AND 默认OR
21 | func Roles(roleLabels []string, logical ...string) gin.HandlerFunc {
22 | return func(ctx *gin.Context) {
23 | userId := utils.GetUserId(ctx)
24 | AuthCache := new(utils.AuthCache)
25 | labelsCache, err := AuthCache.GetRoleLabels(userId)
26 | if err != nil {
27 | common.FailMsg(ctx, err.Error())
28 | ctx.Abort()
29 | return
30 | }
31 | if len(logical) > 0 {
32 | switch logical[0] {
33 | case "or":
34 | if err := orVerify(labelsCache, roleLabels); err != nil {
35 | common.FailMsg(ctx, "未满足接口所需角色")
36 | ctx.Abort()
37 | return
38 | }
39 | case "and":
40 | if err := andVerify(labelsCache, roleLabels); err != nil {
41 | common.FailMsg(ctx, "未满足接口所需角色")
42 | ctx.Abort()
43 | return
44 | }
45 | default:
46 | common.FailMsg(ctx, "角色验证参数错误")
47 | ctx.Abort()
48 | return
49 | }
50 | } else {
51 | // 默认的验证条件 需要默认OR可以把函数改成orVerify
52 | if err := andVerify(labelsCache, roleLabels); err != nil {
53 | common.FailMsg(ctx, "未满足接口所需角色")
54 | ctx.Abort()
55 | return
56 | }
57 | }
58 |
59 | ctx.Next()
60 | }
61 | }
62 |
63 | // 验证权限
64 | // menuPerms:要验证的菜单权限
65 | // logical:auth.OR || auth.AND (默认AND)
66 | func Perms(menuPerms []string, logical ...string) gin.HandlerFunc {
67 | return func(ctx *gin.Context) {
68 | userId := utils.GetUserId(ctx)
69 | AuthCache := new(utils.AuthCache)
70 | permsCache, err := AuthCache.GetMenuPerms(userId)
71 | if err != nil {
72 | common.FailMsg(ctx, err.Error())
73 | ctx.Abort()
74 | return
75 | }
76 | if len(logical) > 0 {
77 | switch logical[0] {
78 | case "or":
79 | if err := orVerify(permsCache, menuPerms); err != nil {
80 | common.FailMsg(ctx, "权限验证失败")
81 | ctx.Abort()
82 | return
83 | }
84 | case "and":
85 | if err := andVerify(permsCache, menuPerms); err != nil {
86 | common.FailMsg(ctx, "权限验证失败")
87 | ctx.Abort()
88 | return
89 | }
90 | default:
91 | common.FailMsg(ctx, "权限验证参数错误")
92 | ctx.Abort()
93 | return
94 | }
95 |
96 | } else {
97 | // 默认的验证条件 需要默认OR可以把函数改成orVerify
98 | if err := andVerify(permsCache, menuPerms); err != nil {
99 | common.FailMsg(ctx, "权限验证失败")
100 | ctx.Abort()
101 | return
102 | }
103 | }
104 | ctx.Next()
105 | }
106 | }
107 |
108 | // OR验证
109 | func orVerify(cacheData []string, verifyData []string) error {
110 | is := false
111 | for _, perm := range verifyData {
112 | if utils.SliceIncludes(cacheData, perm) {
113 | is = true
114 | }
115 | }
116 | if !is {
117 | return errors.New("验证失败")
118 | }
119 | return nil
120 | }
121 |
122 | // AND验证
123 | func andVerify(cacheData []string, verifyData []string) error {
124 | count := 0
125 | for _, perm := range cacheData {
126 | if utils.SliceIncludes(verifyData, perm) {
127 | count++
128 | }
129 | }
130 | if count != len(verifyData) {
131 | return errors.New("验证失败")
132 | }
133 | return nil
134 | }
135 |
--------------------------------------------------------------------------------
/common/common.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/go-redis/redis"
7 | "github.com/gookit/config/v2"
8 | "go.uber.org/zap"
9 | "xorm.io/xorm"
10 | )
11 |
12 | var (
13 | DB *xorm.Engine
14 | CONFIG *config.Config
15 | LOG *zap.Logger
16 | Redis *redis.Client
17 | StartTime time.Time
18 | )
19 |
--------------------------------------------------------------------------------
/common/middlewares/jwt.go:
--------------------------------------------------------------------------------
1 | package middlewares
2 |
3 | import (
4 | "seed-admin/common"
5 | "seed-admin/utils"
6 | "strings"
7 |
8 | "github.com/gin-gonic/gin"
9 | )
10 |
11 | // 鉴权中间件
12 | func JwtAuth() gin.HandlerFunc {
13 | return func(ctx *gin.Context) {
14 | tokenString := ctx.Request.Header.Get("Authorization")
15 | if tokenString == "" || tokenString == "Bearer " {
16 | common.Message(ctx, common.AUTHORIZATION_FAIL, "未携带token,认证失败")
17 | ctx.Abort()
18 | return
19 | }
20 | jwt := utils.NewJwt()
21 | tokenString = strings.TrimPrefix(tokenString, "Bearer ")
22 | claims, err := jwt.ParseToken(tokenString)
23 | if err != nil {
24 | common.Message(ctx, common.AUTHORIZATION_FAIL, "非法token或token已经过期,请重新登录")
25 | ctx.Abort()
26 | return
27 | }
28 | ctx.Set("claims", claims)
29 | ctx.Next()
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/common/middlewares/record.go:
--------------------------------------------------------------------------------
1 | package middlewares
2 |
3 | import (
4 | "bytes"
5 | "io"
6 | "net/http"
7 | "seed-admin/app/admin/entity"
8 | "seed-admin/app/admin/services"
9 | "seed-admin/common"
10 | "seed-admin/utils"
11 | "strconv"
12 | "time"
13 |
14 | "github.com/gin-gonic/gin"
15 | )
16 |
17 | var logService services.LogService
18 |
19 | // 操作记录器
20 | func OperationRecorder() gin.HandlerFunc {
21 | return func(ctx *gin.Context) {
22 | body := make([]byte, 0)
23 | // 只考虑常规的浏览器取参方式 get携带body post携带url等不再进行取参处理 有需要自己可以添加
24 | // 本项目不遵守Restful规范 delete,put等请求方法如有需要请自行添加case分支 这里只处理GETQuery和POSTBody的请求方式
25 | switch ctx.Request.Method {
26 | case http.MethodGet:
27 | body = []byte(ctx.Request.URL.Query().Encode())
28 | case http.MethodPost:
29 | var err error
30 | body, err = io.ReadAll(ctx.Request.Body)
31 | if err != nil {
32 | common.LOG.Error(err.Error())
33 | } else {
34 | ctx.Request.Body = io.NopCloser(bytes.NewBuffer(body))
35 | }
36 | }
37 | userId := 0
38 | if claims, ok := ctx.Get("claims"); ok {
39 | userId = claims.(*utils.CustomerClaims).UserId
40 | }
41 | // 5分钟内重复操作直接next不再重复记录
42 | if userId != 0 {
43 | ok, err := common.Redis.SIsMember("record"+strconv.Itoa(userId), ctx.Request.URL.Path).Result()
44 | if err != nil {
45 | common.LOG.Error(err.Error())
46 | }
47 | if ok {
48 | ctx.Next()
49 | return
50 | }
51 | }
52 | log := &entity.AdminSysLog{
53 | UserId: userId,
54 | Method: ctx.Request.Method,
55 | Action: ctx.Request.URL.Path,
56 | Ip: ctx.ClientIP(),
57 | Params: string(body),
58 | }
59 | writer := &responseBodyWriter{body: bytes.NewBufferString(""), ResponseWriter: ctx.Writer}
60 | ctx.Writer = writer
61 | ctx.Next()
62 | log.StatusCode = ctx.Writer.Status()
63 | log.Results = writer.body.String()
64 | if err := logService.AddLog(log); err != nil {
65 | common.LOG.Error(err.Error())
66 | }
67 | // 把操作用redis记录下来
68 | if userId != 0 {
69 | common.Redis.SAdd("record"+strconv.Itoa(userId), ctx.Request.URL.Path).Result()
70 | common.Redis.Expire("record"+strconv.Itoa(userId), time.Second*300).Result()
71 | }
72 | }
73 | }
74 |
75 | type responseBodyWriter struct {
76 | gin.ResponseWriter
77 | body *bytes.Buffer
78 | }
79 |
80 | func (r responseBodyWriter) Write(b []byte) (int, error) {
81 | r.body.Write(b)
82 | return r.ResponseWriter.Write(b)
83 | }
84 |
--------------------------------------------------------------------------------
/common/response.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "net/http"
5 |
6 | "github.com/gin-gonic/gin"
7 | )
8 |
9 | const (
10 | SUCCESS = 1000 // 请求成功 进入前端处理逻辑
11 | FAIL = 1001 // 请求错误 前端会自动抛出异常
12 | REFRESH_CAPTCHA = 1002 // 需要前端手动判断code == 1002处理的失败
13 | AUTHORIZATION_FAIL = 1004 // 鉴权失败 前端会自动抛出异常并退出登录
14 | )
15 |
16 | // 自定义通用消息
17 | func Message(ctx *gin.Context, status int, message string, data ...any) {
18 | var obj gin.H
19 | if len(data) == 0 {
20 | obj = gin.H{
21 | "code": status,
22 | "message": message,
23 | }
24 | } else {
25 | obj = gin.H{
26 | "code": status,
27 | "message": message,
28 | "data": data[0],
29 | }
30 | }
31 | ctx.JSON(http.StatusOK, obj)
32 | }
33 |
34 | // 默认的成功响应
35 | func Ok(ctx *gin.Context) {
36 | obj := gin.H{
37 | "code": SUCCESS,
38 | "message": "操作成功",
39 | }
40 | ctx.JSON(http.StatusOK, obj)
41 | }
42 |
43 | // 携带消息的成功响应
44 | func OkMsg(ctx *gin.Context, message string) {
45 | obj := gin.H{
46 | "code": SUCCESS,
47 | "message": message,
48 | }
49 | ctx.JSON(http.StatusOK, obj)
50 | }
51 |
52 | // 携带数据的成功响应
53 | func OkData(ctx *gin.Context, data any) {
54 | obj := gin.H{
55 | "code": SUCCESS,
56 | "message": "操作成功",
57 | "data": data,
58 | }
59 | ctx.JSON(http.StatusOK, obj)
60 | }
61 |
62 | // 携带消息和数据的成功响应
63 | func OkMsgData(ctx *gin.Context, message string, data any) {
64 | obj := gin.H{
65 | "code": SUCCESS,
66 | "message": message,
67 | "data": data,
68 | }
69 | ctx.JSON(http.StatusOK, obj)
70 | }
71 |
72 | // 默认的失败响应
73 | func Fail(ctx *gin.Context) {
74 | obj := gin.H{
75 | "code": FAIL,
76 | "message": "操作失败",
77 | }
78 | ctx.JSON(http.StatusOK, obj)
79 | }
80 |
81 | // 携带消息的失败响应
82 | func FailMsg(ctx *gin.Context, message string) {
83 | obj := gin.H{
84 | "code": FAIL,
85 | "message": message,
86 | }
87 | ctx.JSON(http.StatusOK, obj)
88 | }
89 |
90 | // 携带数据的失败响应
91 | func FailData(ctx *gin.Context, data any) {
92 | obj := gin.H{
93 | "code": FAIL,
94 | "message": "操作失败",
95 | "data": data,
96 | }
97 | ctx.JSON(http.StatusOK, obj)
98 | }
99 |
100 | // 携带消息和数据的失败响应
101 | func FailMsgData(ctx *gin.Context, message string, data any) {
102 | obj := gin.H{
103 | "code": FAIL,
104 | "message": message,
105 | "data": data,
106 | }
107 | ctx.JSON(http.StatusOK, obj)
108 | }
109 |
--------------------------------------------------------------------------------
/config.toml:
--------------------------------------------------------------------------------
1 | [app]
2 | mode = "debug" #运行环境 开发-debug 线上-release 测试-test
3 | name = "Seed-Admin" #应用名称
4 | port = 8080 #运行端口
5 | staticPath = "/wwwroot" #静态目录
6 | #盐值在生产模式使用后切勿随意更改 随意更改后会造成前后盐值不同算出的MD5值也不同导致用户无法登录
7 | md5Salt = "seed" #MD5盐值
8 |
9 | [mysql]
10 | server = "127.0.0.1" #数据库地址
11 | port = 3306 #端口
12 | user = "root" #帐号
13 | password = "123123asd" #密码
14 | database = "seed-admin" #库
15 | config = "" #杂项配置
16 | maxIdleConns = 2 #连接池的空闲数大小
17 | maxOpenConns = 0 #最大打开连接数 0为无限制
18 |
19 | [redis]
20 | server = "127.0.0.1" #地址
21 | port = 6379 #端口
22 | password = "123123" #密码
23 | database = 0 #库
24 |
25 | [jwt]
26 | signingKey = "seed"
27 | Issuer = "seed-admin"
28 | ExpireSeconds = 604800 #token过期时间(second)
29 |
30 | [captcha]
31 | len = 4 #验证码位数 修改后记得修改login参数的tag位数验证
32 | height = 80 #图片高度
33 | width = 200 #图片宽度
34 |
35 | [upload]
36 | fileSize = 5 #上传文件的大小(MB)
37 | path = "/uploads"
38 |
39 | [log]
40 | level = "info" #日志等级 debug || info || warn || error || dpanic || panic || fatal
41 | showLine = true #是否显示行
42 | outType = "all" #输出位置 console || file || all (console:输出到控制台 file:输出到日志文件 all:我全都要.jpg)
43 | console_format = "console" #输出到控制台时的输出格式 json || console
44 | file_format = "json" #输出到文件时的输出格式 json || console
45 | director = "log/runtime" #日志输出目录
46 | maxSize = 1 #切割大小(单位:mb)
47 | maxBackups = 10 #保留旧日志文件的最大数目(个)
48 | maxAge = 7 #保留旧日志文件的最大天数
49 | compress = false #是否压缩
50 |
51 | [gin_log]
52 | outType = "all" #输出位置 console || file || all (console:输出到控制台 file:输出到日志文件 all:我全都要.jpg)
53 | director = "log/http" #日志输出目录
54 | maxSize = 1 #切割大小(单位:mb)
55 | maxBackups = 10 #保留旧日志文件的最大数目(个)
56 | maxAge = 7 #保留旧日志文件的最大天数
57 | compress = false #是否压缩
58 |
59 | [xorm_log]
60 | level = "warn" #日志等级 debug || info || warn || error
61 | outType = "console" #输出位置 console || file (console:输出到控制台 file:输出到日志文件)
62 | showSql = true #在日志记录器上显示SQL语句(debug级别大于info时生效)
63 | director = "log/db" #日志输出目录
64 | maxSize = 1 #切割大小(单位:mb)
65 | maxBackups = 10 #保留旧日志文件的最大数目(个)
66 | maxAge = 7 #保留旧日志文件的最大天数
67 | compress = false #是否压缩
68 |
--------------------------------------------------------------------------------
/core/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "flag"
5 | "sync"
6 |
7 | "github.com/fsnotify/fsnotify"
8 | "github.com/gookit/config/v2"
9 | "github.com/gookit/config/v2/toml"
10 | "github.com/gookit/goutil/cliutil"
11 | )
12 |
13 | // 添加配置文件
14 | func Add(path ...string) *config.Config {
15 | filePath := ""
16 | // 判断是否使用函数入参设置配置文件
17 | if len(path) == 0 {
18 | // 判断启动时是否使用-c设置配置文件
19 | flag.StringVar(&filePath, "c", "", "您的配置文件.")
20 | flag.Parse()
21 | if filePath == "" {
22 | // 没有的话使用默认
23 | filePath = "config.toml"
24 | }
25 | } else {
26 | filePath = path[0]
27 | }
28 | // 创建新的配置实例
29 | c := config.NewWithOptions("appConfig", func(opts *config.Options) {
30 | opts.ParseEnv = true
31 | opts.HookFunc = hookFunc
32 | })
33 | // 加载驱动
34 | c.AddDriver(toml.Driver)
35 | // 加载配置文件
36 | if err := c.LoadFiles(filePath); err != nil {
37 | panic(err.Error())
38 | }
39 | // 监听配置文件热修改
40 | watchConfigFiles(c)
41 | return c
42 | }
43 |
44 | // 监听配置文件热修改
45 | func watchConfigFiles(cfg *config.Config) {
46 | // 开一个新线程防止主线程被卡住
47 | readyTask := new(sync.WaitGroup)
48 | readyTask.Add(1)
49 | go func() {
50 | watcher, err := fsnotify.NewWatcher()
51 | if err != nil {
52 | cliutil.Errorln(err.Error())
53 | return
54 | }
55 | defer watcher.Close()
56 | // 获取加载的配置文件
57 | files := cfg.LoadedFiles()
58 | if len(files) == 0 {
59 | cliutil.Errorln("未读取到配置文件")
60 | return
61 | }
62 | // 处理出错或通道关闭时的退出问题
63 | eventsTask := new(sync.WaitGroup)
64 | eventsTask.Add(1)
65 | go func() {
66 | for {
67 | select {
68 | case event, ok := <-watcher.Events:
69 | if !ok {
70 | eventsTask.Done()
71 | return
72 | }
73 | // 只有写入时才重新创建数据
74 | switch event.Op.String() {
75 | case "WRITE":
76 | // 重载数据
77 | if err := cfg.ReloadFiles(); err != nil {
78 | eventsTask.Done()
79 | cliutil.Errorf("重载%s数据出错,err:%s\n", event.Name, err.Error())
80 | return
81 | }
82 | cliutil.Infof("监听到%s变动\n", event.Name)
83 | case "REMOVE":
84 | eventsTask.Done()
85 | cliutil.Errorf("重载%s数据出错,err:文件被删除,请不要删除配置文件\n", event.Name)
86 | return
87 | default:
88 | cliutil.Infof("监听到%s变动 Op->%s\n", event.Name, event.Op.String())
89 | }
90 | case err, ok := <-watcher.Errors:
91 | if ok {
92 | cliutil.Errorln(err.Error())
93 | }
94 | if err != nil {
95 | cliutil.Errorln(err.Error())
96 | }
97 | eventsTask.Done()
98 | return
99 | }
100 | }
101 | }()
102 | // 加载文件的监听
103 | for _, path := range files {
104 | if err := watcher.Add(path); err != nil {
105 | cliutil.Errorln(err.Error())
106 | }
107 | }
108 | // 加载文件监听成功后释放创建监听的线程
109 | readyTask.Done()
110 | // 等待事件释放
111 | eventsTask.Wait()
112 | }()
113 | // 等待监听成功
114 | readyTask.Wait()
115 | }
116 |
117 | // 监听配置修改钩子
118 | func hookFunc(event string, c *config.Config) {
119 | // if event == "set.value" || event == "set.data" {
120 | // buf := new(buffer.Buffer)
121 | // // 第二个参数是导出格式,有config.JSON | config.Toml | config.Yaml等等等 根据你到导出的格式选用
122 | // _, err := c.DumpTo(buf, config.Toml)
123 | // if err != nil {
124 | // common.LOG.Error(err.Error())
125 | // return
126 | // }
127 | // if err = ioutil.WriteFile("需要导出的文件地址", buf.Bytes(), 0755); err != nil {
128 | // common.LOG.Error(err.Error())
129 | // return
130 | // }
131 | // }
132 | }
133 |
--------------------------------------------------------------------------------
/core/index.go:
--------------------------------------------------------------------------------
1 | package core
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | routers "seed-admin/app"
7 | common "seed-admin/common"
8 | config "seed-admin/core/config"
9 | middlewares "seed-admin/core/middlewares"
10 | redis "seed-admin/core/redis"
11 | xorm "seed-admin/core/xorm"
12 | log "seed-admin/core/zap"
13 | "time"
14 |
15 | "github.com/gin-gonic/gin"
16 | )
17 |
18 | // 初始化
19 | func Run() {
20 | common.CONFIG = config.Add()
21 | common.LOG = log.AddZap()
22 | common.DB = xorm.AddXorm()
23 | common.Redis = redis.AddGoRedis()
24 | // 应用启动时间
25 | common.StartTime = time.Now()
26 | // 运行环境
27 | gin.SetMode(common.CONFIG.String("app.mode"))
28 | app := gin.New()
29 | // 加载全局中间件
30 | middlewares.Load(app)
31 | // 加载路由
32 | routers.Load(app)
33 | // 静态目录
34 | app.StaticFS(common.CONFIG.String("app.staticPath"), http.Dir("."+common.CONFIG.String("app.staticPath")))
35 | // 广告
36 | fmt.Println(`
37 | 欢迎使用 Seed-Admin
38 | 当前版本:V1.0.0
39 | QQ交流1群:8455822
40 | `)
41 | common.LOG.Info("滑稽")
42 | common.LOG.Error("错误的滑稽")
43 | // 冲
44 | app.Run(":" + common.CONFIG.String("app.port"))
45 | }
46 |
--------------------------------------------------------------------------------
/core/middlewares/cors.go:
--------------------------------------------------------------------------------
1 | package middlewares
2 |
3 | import (
4 | "net/http"
5 |
6 | "github.com/gin-gonic/gin"
7 | )
8 |
9 | func UseCors(r *gin.Engine) {
10 | r.Use(func(c *gin.Context) {
11 | method := c.Request.Method
12 | origin := c.Request.Header.Get("Origin")
13 | // 允许来源
14 | c.Header("Access-Control-Allow-Origin", origin)
15 | // 请求方式
16 | c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
17 | // 允许的标头
18 | c.Header("Access-Control-Allow-Headers", "Content-Type,Authorization")
19 | // 暴露标头
20 | c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
21 | // 是否允许携带cookie
22 | c.Header("Access-Control-Allow-Credentials", "true")
23 | // 预检时间(秒)
24 | c.Header("Access-Control-Max-Age", "3600")
25 | //放行所有OPTIONS方法
26 | if method == "OPTIONS" {
27 | c.AbortWithStatus(http.StatusNoContent)
28 | }
29 | c.Next()
30 | })
31 | }
32 |
--------------------------------------------------------------------------------
/core/middlewares/logger.go:
--------------------------------------------------------------------------------
1 | package middlewares
2 |
3 | import (
4 | "seed-admin/common"
5 |
6 | "seed-admin/core/zap/lumberjack"
7 |
8 | "github.com/gin-gonic/gin"
9 | )
10 |
11 | // // zap接管gin日志(有需要自行加载)
12 | // func UseZapLogger(r *gin.Engine) {
13 | // logger := common.LOG
14 | // r.Use(func(ctx *gin.Context) {
15 | // start := time.Now()
16 | // ctx.Next()
17 | // cost := time.Since(start)
18 | // logger.Info(ctx.Request.URL.Path,
19 | // zap.Int("status", ctx.Writer.Status()),
20 | // zap.String("method", ctx.Request.Method),
21 | // zap.String("path", ctx.Request.URL.Path),
22 | // zap.String("query", ctx.Request.URL.RawQuery),
23 | // zap.String("ip", ctx.ClientIP()),
24 | // zap.String("user-agent", ctx.Request.UserAgent()),
25 | // zap.String("errors", ctx.Errors.ByType(gin.ErrorTypePrivate).String()),
26 | // zap.Duration("cost", cost),
27 | // )
28 | // })
29 | // }
30 | func UseLogger(r *gin.Engine) {
31 | var loggerConfig = gin.LoggerConfig{
32 | Output: &lumberjack.Logger{
33 | Filename: "./" + common.CONFIG.String("gin_log.director") + "/http.log",
34 | MaxSize: common.CONFIG.Int("gin_log.maxSize"),
35 | MaxBackups: common.CONFIG.Int("gin_log.maxBackups"),
36 | MaxAge: common.CONFIG.Int("gin_log.maxAge"),
37 | Compress: common.CONFIG.Bool("gin_log.compress"),
38 | },
39 | }
40 | switch common.CONFIG.String("gin_log.outType") {
41 | case "console":
42 | r.Use(gin.Logger())
43 | case "file":
44 | r.Use(gin.LoggerWithConfig(loggerConfig))
45 | default:
46 | r.Use(gin.Logger()).Use(gin.LoggerWithConfig(loggerConfig))
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/core/middlewares/middlewares.go:
--------------------------------------------------------------------------------
1 | package middlewares
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | )
6 |
7 | func Load(r *gin.Engine) {
8 | UseRecovery(r)
9 | UseLogger(r)
10 | // useCors(r)
11 | }
12 |
--------------------------------------------------------------------------------
/core/middlewares/recovery.go:
--------------------------------------------------------------------------------
1 | package middlewares
2 |
3 | import "github.com/gin-gonic/gin"
4 |
5 | func UseRecovery(r *gin.Engine) {
6 | // 加载官方的恢复 如有必要自己写
7 | r.Use(gin.Recovery())
8 | }
9 |
--------------------------------------------------------------------------------
/core/redis/redis.go:
--------------------------------------------------------------------------------
1 | package redis
2 |
3 | import (
4 | "seed-admin/common"
5 |
6 | "github.com/go-redis/redis"
7 | )
8 |
9 | func AddGoRedis() *redis.Client {
10 | address := common.CONFIG.String("redis.server") + ":" + common.CONFIG.String("redis.port")
11 | password := common.CONFIG.String("redis.password")
12 | db := common.CONFIG.Int("redis.database")
13 | client := redis.NewClient(&redis.Options{
14 | Addr: address,
15 | Password: password,
16 | DB: db,
17 | })
18 | if _, err := client.Ping().Result(); err != nil {
19 | common.LOG.Error(err.Error())
20 | }
21 | return client
22 | }
23 |
--------------------------------------------------------------------------------
/core/xorm/xorm.go:
--------------------------------------------------------------------------------
1 | package xorm
2 |
3 | import (
4 | "database/sql"
5 | "fmt"
6 | goLog "log"
7 | "os"
8 | "seed-admin/common"
9 | "seed-admin/core/zap/lumberjack"
10 | "strings"
11 | "sync"
12 |
13 | _ "github.com/go-sql-driver/mysql"
14 | "xorm.io/xorm"
15 | "xorm.io/xorm/log"
16 | )
17 |
18 | var wirteSqlTask sync.WaitGroup
19 |
20 | func AddXorm() *xorm.Engine {
21 | if common.CONFIG.String("mysql.database") == "" {
22 | panic("配置mysql.database为空,请检查config.toml配置文件")
23 | }
24 | db, err := newXorm()
25 | if err != nil {
26 | panic(err.Error())
27 | }
28 | if err = db.Ping(); err != nil {
29 | if strings.Contains(err.Error(), "Unknown database") {
30 | return initDb()
31 | }
32 | panic(err.Error())
33 | }
34 | return db
35 | }
36 | func newXorm() (*xorm.Engine, error) {
37 | var config = common.CONFIG.StringMap("mysql")
38 | // 组装连接字符串
39 | dsn := fmt.Sprintf("%v:%v@tcp(%v:%v)/%v?%v", config["user"], config["password"], config["server"], config["port"], config["database"], config["config"])
40 | db, err := xorm.NewEngine("mysql", dsn)
41 | if common.CONFIG.String("xorm_log.outType") == "file" {
42 | db.SetLogger(log.NewSimpleLogger(&lumberjack.Logger{
43 | Filename: "./" + common.CONFIG.String("xorm_log.director") + "/db.log",
44 | MaxSize: common.CONFIG.Int("xorm_log.maxSize"),
45 | MaxBackups: common.CONFIG.Int("xorm_log.maxBackups"),
46 | MaxAge: common.CONFIG.Int("xorm_log.maxAge"),
47 | Compress: common.CONFIG.Bool("xorm_log.compress"),
48 | }))
49 | } else {
50 | db.SetLogger(log.NewSimpleLogger(os.Stdout))
51 | }
52 | db.ShowSQL(common.CONFIG.Bool("xorm_log.showSql"))
53 | db.SetLogLevel(newLogger())
54 | db.SetMaxIdleConns(common.CONFIG.Int("mysql.maxIdleConns"))
55 | db.SetMaxOpenConns(common.CONFIG.Int("mysql.maxOpenConns"))
56 | return db, err
57 | }
58 |
59 | func initDb() *xorm.Engine {
60 | var config = common.CONFIG.StringMap("mysql")
61 | goLog.Println("未发现数据库,将自动创建...")
62 | dsn := fmt.Sprintf("%v:%v@tcp(%v:%v)/", config["user"], config["password"], config["server"], config["port"])
63 | db, err := sql.Open("mysql", dsn)
64 | if err != nil {
65 | panic(err.Error())
66 | }
67 | defer func(db *sql.DB) {
68 | if err := db.Close(); err != nil {
69 | common.LOG.Error(err.Error())
70 | }
71 | }(db)
72 | if err = db.Ping(); err != nil {
73 | panic(err.Error())
74 | }
75 | createSql := fmt.Sprintf("CREATE DATABASE `%s` DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci;", config["database"])
76 | _, err = db.Exec(createSql)
77 | if err != nil {
78 | panic(err.Error())
79 | }
80 | goLog.Printf("数据库 %v 创建完毕...\n", config["database"])
81 | xormDb, err := newXorm()
82 | if err != nil {
83 | panic(err.Error())
84 | }
85 | wirteSql(xormDb)
86 | return xormDb
87 | }
88 |
89 | // 加载文件
90 | func loadFiles(path string) []string {
91 | file, err := os.OpenFile(path, os.O_RDONLY, os.ModeDir)
92 | if err != nil {
93 | panic(err.Error())
94 | }
95 | defer file.Close()
96 | fileInfo, _ := file.ReadDir(-1)
97 | files := []string{}
98 | for _, item := range fileInfo {
99 | files = append(files, item.Name())
100 | }
101 | return files
102 | }
103 |
104 | // 导入数据表和数据
105 | func wirteSql(db *xorm.Engine) {
106 | db.ShowSQL(false)
107 | goLog.Println("开始导入数据表与数据...")
108 | path := "sql/"
109 | files := loadFiles(path)
110 | for _, file := range files {
111 | wirteSqlTask.Add(1)
112 | go importFile(db, path+file)
113 | }
114 | wirteSqlTask.Wait()
115 | goLog.Println("数据表与数据导入完成...")
116 | db.ShowSQL(common.CONFIG.Bool("xorm_log.showSql"))
117 | }
118 |
119 | func importFile(db *xorm.Engine, path string) {
120 | _, err := db.ImportFile(path)
121 | if err != nil {
122 | goLog.Fatalln(path + "导入失败 error:" + err.Error())
123 | }
124 | wirteSqlTask.Done()
125 | }
126 |
127 | // 日志配置
128 | func newLogger() log.LogLevel {
129 | var level log.LogLevel
130 | switch common.CONFIG.String("xorm_log.level") {
131 | case "debug":
132 | level = log.LOG_DEBUG
133 | case "info":
134 | level = log.LOG_INFO
135 | case "warn":
136 | level = log.LOG_WARNING
137 | case "error":
138 | level = log.LOG_ERR
139 | default:
140 | level = log.LOG_INFO
141 | }
142 | return level
143 | }
144 |
--------------------------------------------------------------------------------
/core/zap/lumberjack/chown.go:
--------------------------------------------------------------------------------
1 | //go:build !linux
2 | // +build !linux
3 |
4 | package lumberjack
5 |
6 | import (
7 | "os"
8 | )
9 |
10 | func chown(_ string, _ os.FileInfo) error {
11 | return nil
12 | }
13 |
--------------------------------------------------------------------------------
/core/zap/lumberjack/chown_linux.go:
--------------------------------------------------------------------------------
1 | package lumberjack
2 |
3 | import (
4 | "os"
5 | "syscall"
6 | )
7 |
8 | // os_Chown is a var so we can mock it out during tests.
9 | var os_Chown = os.Chown
10 |
11 | func chown(name string, info os.FileInfo) error {
12 | f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, info.Mode())
13 | if err != nil {
14 | return err
15 | }
16 | f.Close()
17 | stat := info.Sys().(*syscall.Stat_t)
18 | return os_Chown(name, int(stat.Uid), int(stat.Gid))
19 | }
20 |
--------------------------------------------------------------------------------
/core/zap/lumberjack/lumberjack.go:
--------------------------------------------------------------------------------
1 | // Package lumberjack provides a rolling logger.
2 | //
3 | // Note that this is v2.0 of lumberjack, and should be imported using gopkg.in
4 | // thusly:
5 | //
6 | // import "gopkg.in/natefinch/lumberjack.v2"
7 | //
8 | // The package name remains simply lumberjack, and the code resides at
9 | // https://github.com/natefinch/lumberjack under the v2.0 branch.
10 | //
11 | // Lumberjack is intended to be one part of a logging infrastructure.
12 | // It is not an all-in-one solution, but instead is a pluggable
13 | // component at the bottom of the logging stack that simply controls the files
14 | // to which logs are written.
15 | //
16 | // Lumberjack plays well with any logging package that can write to an
17 | // io.Writer, including the standard library's log package.
18 | //
19 | // Lumberjack assumes that only one process is writing to the output files.
20 | // Using the same lumberjack configuration from multiple processes on the same
21 | // machine will result in improper behavior.
22 | package lumberjack
23 |
24 | import (
25 | "compress/gzip"
26 | "errors"
27 | "fmt"
28 | "io"
29 | "io/ioutil"
30 | "os"
31 | "path/filepath"
32 | "sort"
33 | "strings"
34 | "sync"
35 | "time"
36 | )
37 |
38 | const (
39 | compressSuffix = ".gz"
40 | defaultMaxSize = 100
41 | )
42 |
43 | // ensure we always implement io.WriteCloser
44 | var _ io.WriteCloser = (*Logger)(nil)
45 |
46 | // Logger is an io.WriteCloser that writes to the specified filename.
47 | //
48 | // Logger opens or creates the logfile on first Write. If the file exists and
49 | // is less than MaxSize megabytes, lumberjack will open and append to that file.
50 | // If the file exists and its size is >= MaxSize megabytes, the file is renamed
51 | // by putting the current time in a timestamp in the name immediately before the
52 | // file's extension (or the end of the filename if there's no extension). A new
53 | // log file is then created using original filename.
54 | //
55 | // Whenever a write would cause the current log file exceed MaxSize megabytes,
56 | // the current file is closed, renamed, and a new log file created with the
57 | // original name. Thus, the filename you give Logger is always the "current" log
58 | // file.
59 | //
60 | // Backups use the log file name given to Logger, in the form
61 | // `name-timestamp.ext` where name is the filename without the extension,
62 | // timestamp is the time at which the log was rotated formatted with the
63 | // time.Time format of `2006-01-02T15-04-05.000` and the extension is the
64 | // original extension. For example, if your Logger.Filename is
65 | // `/var/log/foo/server.log`, a backup created at 6:30pm on Nov 11 2016 would
66 | // use the filename `/var/log/foo/server-2016-11-04T18-30-00.000.log`
67 | //
68 | // Cleaning Up Old Log Files
69 | //
70 | // Whenever a new logfile gets created, old log files may be deleted. The most
71 | // recent files according to the encoded timestamp will be retained, up to a
72 | // number equal to MaxBackups (or all of them if MaxBackups is 0). Any files
73 | // with an encoded timestamp older than MaxAge days are deleted, regardless of
74 | // MaxBackups. Note that the time encoded in the timestamp is the rotation
75 | // time, which may differ from the last time that file was written to.
76 | //
77 | // If MaxBackups and MaxAge are both 0, no old log files will be deleted.
78 | type Logger struct {
79 | // Filename is the file to write logs to. Backup log files will be retained
80 | // in the same directory. It uses -lumberjack.log in
81 | // os.TempDir() if empty.
82 | Filename string `json:"filename" yaml:"filename"`
83 |
84 | // MaxSize is the maximum size in megabytes of the log file before it gets
85 | // rotated. It defaults to 100 megabytes.
86 | MaxSize int `json:"maxsize" yaml:"maxsize"`
87 |
88 | // MaxAge is the maximum number of days to retain old log files based on the
89 | // timestamp encoded in their filename. Note that a day is defined as 24
90 | // hours and may not exactly correspond to calendar days due to daylight
91 | // savings, leap seconds, etc. The default is not to remove old log files
92 | // based on age.
93 | MaxAge int `json:"maxage" yaml:"maxage"`
94 |
95 | // MaxBackups is the maximum number of old log files to retain. The default
96 | // is to retain all old log files (though MaxAge may still cause them to get
97 | // deleted.)
98 | MaxBackups int `json:"maxbackups" yaml:"maxbackups"`
99 |
100 | // LocalTime determines if the time used for formatting the timestamps in
101 | // backup files is the computer's local time. The default is to use UTC
102 | // time.
103 | LocalTime bool `json:"localtime" yaml:"localtime"`
104 |
105 | // Compress determines if the rotated log files should be compressed
106 | // using gzip. The default is not to perform compression.
107 | Compress bool `json:"compress" yaml:"compress"`
108 | BackupTimeFormat string `json:"backupTimeFormat" yaml:"backupTimeFormat"`
109 | size int64
110 | file *os.File
111 | mu sync.Mutex
112 |
113 | millCh chan bool
114 | startMill sync.Once
115 | }
116 |
117 | var (
118 | // currentTime exists so it can be mocked out by tests.
119 | currentTime = time.Now
120 |
121 | // os_Stat exists so it can be mocked out by tests.
122 | os_Stat = os.Stat
123 |
124 | // megabyte is the conversion factor between MaxSize and bytes. It is a
125 | // variable so tests can mock it out and not need to write megabytes of data
126 | // to disk.
127 | megabyte = 1024 * 1024
128 | )
129 |
130 | // Write implements io.Writer. If a write would cause the log file to be larger
131 | // than MaxSize, the file is closed, renamed to include a timestamp of the
132 | // current time, and a new log file is created using the original log file name.
133 | // If the length of the write is greater than MaxSize, an error is returned.
134 | func (l *Logger) Write(p []byte) (n int, err error) {
135 | l.mu.Lock()
136 | defer l.mu.Unlock()
137 |
138 | writeLen := int64(len(p))
139 | if writeLen > l.max() {
140 | return 0, fmt.Errorf(
141 | "write length %d exceeds maximum file size %d", writeLen, l.max(),
142 | )
143 | }
144 |
145 | if l.file == nil {
146 | if err = l.openExistingOrNew(len(p)); err != nil {
147 | return 0, err
148 | }
149 | }
150 |
151 | if l.size+writeLen > l.max() {
152 | if err := l.rotate(); err != nil {
153 | return 0, err
154 | }
155 | }
156 |
157 | n, err = l.file.Write(p)
158 | l.size += int64(n)
159 |
160 | return n, err
161 | }
162 |
163 | // Close implements io.Closer, and closes the current logfile.
164 | func (l *Logger) Close() error {
165 | l.mu.Lock()
166 | defer l.mu.Unlock()
167 | return l.close()
168 | }
169 |
170 | // close closes the file if it is open.
171 | func (l *Logger) close() error {
172 | if l.file == nil {
173 | return nil
174 | }
175 | err := l.file.Close()
176 | l.file = nil
177 | return err
178 | }
179 |
180 | // Rotate causes Logger to close the existing log file and immediately create a
181 | // new one. This is a helper function for applications that want to initiate
182 | // rotations outside of the normal rotation rules, such as in response to
183 | // SIGHUP. After rotating, this initiates compression and removal of old log
184 | // files according to the configuration.
185 | func (l *Logger) Rotate() error {
186 | l.mu.Lock()
187 | defer l.mu.Unlock()
188 | return l.rotate()
189 | }
190 |
191 | // rotate closes the current file, moves it aside with a timestamp in the name,
192 | // (if it exists), opens a new file with the original filename, and then runs
193 | // post-rotation processing and removal.
194 | func (l *Logger) rotate() error {
195 | if err := l.close(); err != nil {
196 | return err
197 | }
198 | if err := l.openNew(); err != nil {
199 | return err
200 | }
201 | l.mill()
202 | return nil
203 | }
204 |
205 | // openNew opens a new log file for writing, moving any old log file out of the
206 | // way. This methods assumes the file has already been closed.
207 | func (l *Logger) openNew() error {
208 | err := os.MkdirAll(l.dir(), 0744)
209 | if err != nil {
210 | return fmt.Errorf("can't make directories for new logfile: %s", err)
211 | }
212 |
213 | name := l.filename()
214 | mode := os.FileMode(0644)
215 | info, err := os_Stat(name)
216 | if err == nil {
217 | // Copy the mode off the old logfile.
218 | mode = info.Mode()
219 | // move the existing file
220 | newname := l.backupName(name, l.LocalTime)
221 | if err := os.Rename(name, newname); err != nil {
222 | return fmt.Errorf("can't rename log file: %s", err)
223 | }
224 |
225 | // this is a no-op anywhere but linux
226 | if err := chown(name, info); err != nil {
227 | return err
228 | }
229 | }
230 |
231 | // we use truncate here because this should only get called when we've moved
232 | // the file ourselves. if someone else creates the file in the meantime,
233 | // just wipe out the contents.
234 | f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode)
235 | if err != nil {
236 | return fmt.Errorf("can't open new logfile: %s", err)
237 | }
238 | l.file = f
239 | l.size = 0
240 | return nil
241 | }
242 |
243 | // backupName creates a new filename from the given name, inserting a timestamp
244 | // between the filename and the extension, using the local time if requested
245 | // (otherwise UTC).
246 | func (l *Logger) backupName(name string, local bool) string {
247 | dir := filepath.Dir(name)
248 | filename := filepath.Base(name)
249 | ext := filepath.Ext(filename)
250 | prefix := filename[:len(filename)-len(ext)]
251 | t := currentTime()
252 | if !local {
253 | t = t.Local()
254 | }
255 |
256 | timestamp := t.Format(l.BackupTimeFormat)
257 | return filepath.Join(dir, fmt.Sprintf("%s-%s%s", prefix, timestamp, ext))
258 | }
259 |
260 | // openExistingOrNew opens the logfile if it exists and if the current write
261 | // would not put it over MaxSize. If there is no such file or the write would
262 | // put it over the MaxSize, a new file is created.
263 | func (l *Logger) openExistingOrNew(writeLen int) error {
264 | l.mill()
265 |
266 | filename := l.filename()
267 | info, err := os_Stat(filename)
268 | if os.IsNotExist(err) {
269 | return l.openNew()
270 | }
271 | if err != nil {
272 | return fmt.Errorf("error getting log file info: %s", err)
273 | }
274 |
275 | if info.Size()+int64(writeLen) >= l.max() {
276 | return l.rotate()
277 | }
278 |
279 | file, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0644)
280 | if err != nil {
281 | // if we fail to open the old log file for some reason, just ignore
282 | // it and open a new log file.
283 | return l.openNew()
284 | }
285 | l.file = file
286 | l.size = info.Size()
287 | return nil
288 | }
289 |
290 | // genFilename generates the name of the logfile from the current time.
291 | func (l *Logger) filename() string {
292 | if l.Filename != "" {
293 | return l.Filename
294 | }
295 | name := filepath.Base(os.Args[0]) + "-lumberjack.log"
296 | return filepath.Join(os.TempDir(), name)
297 | }
298 |
299 | // millRunOnce performs compression and removal of stale log files.
300 | // Log files are compressed if enabled via configuration and old log
301 | // files are removed, keeping at most l.MaxBackups files, as long as
302 | // none of them are older than MaxAge.
303 | func (l *Logger) millRunOnce() error {
304 | if l.MaxBackups == 0 && l.MaxAge == 0 && !l.Compress {
305 | return nil
306 | }
307 |
308 | files, err := l.oldLogFiles()
309 | if err != nil {
310 | return err
311 | }
312 |
313 | var compress, remove []logInfo
314 |
315 | if l.MaxBackups > 0 && l.MaxBackups < len(files) {
316 | preserved := make(map[string]bool)
317 | var remaining []logInfo
318 | for _, f := range files {
319 | // Only count the uncompressed log file or the
320 | // compressed log file, not both.
321 | fn := f.Name()
322 | if strings.HasSuffix(fn, compressSuffix) {
323 | fn = fn[:len(fn)-len(compressSuffix)]
324 | }
325 | preserved[fn] = true
326 |
327 | if len(preserved) > l.MaxBackups {
328 | remove = append(remove, f)
329 | } else {
330 | remaining = append(remaining, f)
331 | }
332 | }
333 | files = remaining
334 | }
335 | if l.MaxAge > 0 {
336 | diff := time.Duration(int64(24*time.Hour) * int64(l.MaxAge))
337 | cutoff := currentTime().Add(-1 * diff)
338 | var remaining []logInfo
339 | for _, f := range files {
340 | if f.timestamp.Before(cutoff) {
341 | remove = append(remove, f)
342 | } else {
343 | remaining = append(remaining, f)
344 | }
345 | }
346 | files = remaining
347 | }
348 |
349 | if l.Compress {
350 | for _, f := range files {
351 | if !strings.HasSuffix(f.Name(), compressSuffix) {
352 | compress = append(compress, f)
353 | }
354 | }
355 | }
356 |
357 | for _, f := range remove {
358 | errRemove := os.Remove(filepath.Join(l.dir(), f.Name()))
359 | if err == nil && errRemove != nil {
360 | err = errRemove
361 | }
362 | }
363 | for _, f := range compress {
364 | fn := filepath.Join(l.dir(), f.Name())
365 | errCompress := compressLogFile(fn, fn+compressSuffix)
366 | if err == nil && errCompress != nil {
367 | err = errCompress
368 | }
369 | }
370 |
371 | return err
372 | }
373 |
374 | // millRun runs in a goroutine to manage post-rotation compression and removal
375 | // of old log files.
376 | func (l *Logger) millRun() {
377 | for _ = range l.millCh {
378 | // what am I going to do, log this?
379 | _ = l.millRunOnce()
380 | }
381 | }
382 |
383 | // mill performs post-rotation compression and removal of stale log files,
384 | // starting the mill goroutine if necessary.
385 | func (l *Logger) mill() {
386 | l.startMill.Do(func() {
387 | l.millCh = make(chan bool, 1)
388 | go l.millRun()
389 | })
390 | select {
391 | case l.millCh <- true:
392 | default:
393 | }
394 | }
395 |
396 | // oldLogFiles returns the list of backup log files stored in the same
397 | // directory as the current log file, sorted by ModTime
398 | func (l *Logger) oldLogFiles() ([]logInfo, error) {
399 | files, err := ioutil.ReadDir(l.dir())
400 | if err != nil {
401 | return nil, fmt.Errorf("can't read log file directory: %s", err)
402 | }
403 | logFiles := []logInfo{}
404 |
405 | prefix, ext := l.prefixAndExt()
406 |
407 | for _, f := range files {
408 | if f.IsDir() {
409 | continue
410 | }
411 | if t, err := l.timeFromName(f.Name(), prefix, ext); err == nil {
412 | logFiles = append(logFiles, logInfo{t, f})
413 | continue
414 | }
415 | if t, err := l.timeFromName(f.Name(), prefix, ext+compressSuffix); err == nil {
416 | logFiles = append(logFiles, logInfo{t, f})
417 | continue
418 | }
419 | // error parsing means that the suffix at the end was not generated
420 | // by lumberjack, and therefore it's not a backup file.
421 | }
422 |
423 | sort.Sort(byFormatTime(logFiles))
424 |
425 | return logFiles, nil
426 | }
427 |
428 | // timeFromName extracts the formatted time from the filename by stripping off
429 | // the filename's prefix and extension. This prevents someone's filename from
430 | // confusing time.parse.
431 | func (l *Logger) timeFromName(filename, prefix, ext string) (time.Time, error) {
432 | if !strings.HasPrefix(filename, prefix) {
433 | return time.Time{}, errors.New("mismatched prefix")
434 | }
435 | if !strings.HasSuffix(filename, ext) {
436 | return time.Time{}, errors.New("mismatched extension")
437 | }
438 | ts := filename[len(prefix) : len(filename)-len(ext)]
439 | return time.Parse(l.BackupTimeFormat, ts)
440 | }
441 |
442 | // max returns the maximum size in bytes of log files before rolling.
443 | func (l *Logger) max() int64 {
444 | if l.MaxSize == 0 {
445 | return int64(defaultMaxSize * megabyte)
446 | }
447 | return int64(l.MaxSize) * int64(megabyte)
448 | }
449 |
450 | // dir returns the directory for the current filename.
451 | func (l *Logger) dir() string {
452 | return filepath.Dir(l.filename())
453 | }
454 |
455 | // prefixAndExt returns the filename part and extension part from the Logger's
456 | // filename.
457 | func (l *Logger) prefixAndExt() (prefix, ext string) {
458 | filename := filepath.Base(l.filename())
459 | ext = filepath.Ext(filename)
460 | prefix = filename[:len(filename)-len(ext)] + "-"
461 | return prefix, ext
462 | }
463 |
464 | // compressLogFile compresses the given log file, removing the
465 | // uncompressed log file if successful.
466 | func compressLogFile(src, dst string) (err error) {
467 | f, err := os.Open(src)
468 | if err != nil {
469 | return fmt.Errorf("failed to open log file: %v", err)
470 | }
471 | defer f.Close()
472 |
473 | fi, err := os_Stat(src)
474 | if err != nil {
475 | return fmt.Errorf("failed to stat log file: %v", err)
476 | }
477 |
478 | if err := chown(dst, fi); err != nil {
479 | return fmt.Errorf("failed to chown compressed log file: %v", err)
480 | }
481 |
482 | // If this file already exists, we presume it was created by
483 | // a previous attempt to compress the log file.
484 | gzf, err := os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, fi.Mode())
485 | if err != nil {
486 | return fmt.Errorf("failed to open compressed log file: %v", err)
487 | }
488 | defer gzf.Close()
489 |
490 | gz := gzip.NewWriter(gzf)
491 |
492 | defer func() {
493 | if err != nil {
494 | os.Remove(dst)
495 | err = fmt.Errorf("failed to compress log file: %v", err)
496 | }
497 | }()
498 |
499 | if _, err := io.Copy(gz, f); err != nil {
500 | return err
501 | }
502 | if err := gz.Close(); err != nil {
503 | return err
504 | }
505 | if err := gzf.Close(); err != nil {
506 | return err
507 | }
508 |
509 | if err := f.Close(); err != nil {
510 | return err
511 | }
512 | if err := os.Remove(src); err != nil {
513 | return err
514 | }
515 |
516 | return nil
517 | }
518 |
519 | // logInfo is a convenience struct to return the filename and its embedded
520 | // timestamp.
521 | type logInfo struct {
522 | timestamp time.Time
523 | os.FileInfo
524 | }
525 |
526 | // byFormatTime sorts by newest time formatted in the name.
527 | type byFormatTime []logInfo
528 |
529 | func (b byFormatTime) Less(i, j int) bool {
530 | return b[i].timestamp.After(b[j].timestamp)
531 | }
532 |
533 | func (b byFormatTime) Swap(i, j int) {
534 | b[i], b[j] = b[j], b[i]
535 | }
536 |
537 | func (b byFormatTime) Len() int {
538 | return len(b)
539 | }
540 |
--------------------------------------------------------------------------------
/core/zap/zap.go:
--------------------------------------------------------------------------------
1 | package zap
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "seed-admin/common"
7 | "time"
8 |
9 | "seed-admin/core/zap/lumberjack"
10 |
11 | "github.com/gookit/color"
12 | "go.uber.org/zap"
13 | "go.uber.org/zap/zapcore"
14 | )
15 |
16 | func AddZap() *zap.Logger {
17 | options := []zap.Option{}
18 | if common.CONFIG.Bool("log.showLine") {
19 | options = append(options, zap.AddCaller())
20 | }
21 | // 如果日志等级为Debug/Error等级时显示堆栈信息
22 | if level() == zap.DebugLevel || level() == zap.ErrorLevel {
23 | options = append(options, zap.AddStacktrace(level()))
24 | }
25 | return zap.New(core(), options...)
26 | }
27 |
28 | func core() zapcore.Core {
29 | var teeCore []zapcore.Core
30 | consoleCore := zapcore.NewCore(encoder(true), zapcore.Lock(os.Stdout), level())
31 | fileCore := zapcore.NewCore(encoder(false), writerSyncerConfig(), level())
32 |
33 | switch common.CONFIG.Get("log.outType") {
34 | case "console":
35 | teeCore = append(teeCore, consoleCore)
36 | case "file":
37 | teeCore = append(teeCore, fileCore)
38 | default:
39 | teeCore = append(teeCore, consoleCore, fileCore)
40 | }
41 | return zapcore.NewTee(teeCore...)
42 | }
43 | func encoder(isConsole bool) zapcore.Encoder {
44 | var format string
45 | if isConsole {
46 | format = "log.console_format"
47 | } else {
48 | format = "log.file_format"
49 | }
50 | if common.CONFIG.Get(format) == "json" {
51 | return zapcore.NewJSONEncoder(encoderConfig(isConsole))
52 | }
53 | return zapcore.NewConsoleEncoder(encoderConfig(isConsole))
54 | }
55 |
56 | func encoderConfig(isConsole bool) zapcore.EncoderConfig {
57 | var encoderTime zapcore.TimeEncoder
58 | var encodeLevel zapcore.LevelEncoder
59 | if isConsole {
60 | encoderTime = consoleEncoderTime
61 | encodeLevel = consoleEncodeLevel
62 | } else {
63 | encoderTime = fileEncoderTime
64 | encodeLevel = fileEncodeLevel
65 | }
66 | encoderConfig := zapcore.EncoderConfig{
67 | TimeKey: "time",
68 | MessageKey: "message",
69 | LevelKey: "level",
70 | CallerKey: "caller",
71 | StacktraceKey: "stacktrace",
72 | EncodeTime: encoderTime,
73 | EncodeLevel: encodeLevel,
74 | EncodeCaller: zapcore.ShortCallerEncoder, // FullCallerEncoder || ShortCallerEncoder
75 | EncodeDuration: zapcore.SecondsDurationEncoder,
76 | ConsoleSeparator: " ",
77 | }
78 | return encoderConfig
79 | }
80 | func writerSyncerConfig() zapcore.WriteSyncer {
81 | lumberJackLogger := &lumberjack.Logger{
82 | Filename: "./" + common.CONFIG.String("log.director") + "/runtime.log",
83 | MaxSize: common.CONFIG.Int("log.maxSize"),
84 | MaxBackups: common.CONFIG.Int("log.maxBackups"),
85 | MaxAge: common.CONFIG.Int("log.maxAge"),
86 | Compress: common.CONFIG.Bool("log.compress"),
87 | BackupTimeFormat: "2006-01-02T15-04-05",
88 | }
89 | return zapcore.AddSync(lumberJackLogger)
90 | }
91 |
92 | // 获取配置里的日志等级
93 | func level() zapcore.Level {
94 | l := new(zapcore.Level)
95 | err := l.UnmarshalText([]byte(common.CONFIG.String("log.level")))
96 | if err != nil {
97 | return zap.InfoLevel
98 | }
99 | return *l
100 | }
101 | func consoleEncodeLevel(level zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
102 | if common.CONFIG.Get("log.console_format") == "json" {
103 | enc.AppendString(level.CapitalString())
104 | return
105 | }
106 | enc.AppendString(colorEnc(string(fmt.Sprint(level)), "["+level.CapitalString()+"]"))
107 | }
108 | func consoleEncoderTime(time time.Time, enc zapcore.PrimitiveArrayEncoder) {
109 | if common.CONFIG.Get("log.console_format") == "json" {
110 | enc.AppendString(time.Format("2006/01/02 15:04:05"))
111 | return
112 | }
113 | enc.AppendString("[" + common.CONFIG.String("app.name") + "]")
114 | enc.AppendString("[" + time.Format("2006/01/02 15:04:05") + "]")
115 | }
116 | func fileEncodeLevel(level zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
117 | if common.CONFIG.Get("log.file_format") == "json" {
118 | enc.AppendString(level.CapitalString())
119 | return
120 | }
121 | enc.AppendString("[" + level.CapitalString() + "]")
122 | }
123 | func fileEncoderTime(time time.Time, enc zapcore.PrimitiveArrayEncoder) {
124 | if common.CONFIG.Get("log.file_format") == "json" {
125 | enc.AppendString(time.Format("2006/01/02 15:04:05"))
126 | return
127 | }
128 | enc.AppendString("[" + common.CONFIG.String("app.name") + "]")
129 | enc.AppendString("[" + time.Format("2006/01/02 15:04:05") + "]")
130 | }
131 |
132 | // 给标签上个色
133 | func colorEnc(level string, value string) string {
134 | switch level {
135 | case "debug":
136 | return color.Debug.Sprintf(value)
137 | case "info":
138 | return color.Info.Sprintf(value)
139 | case "warn":
140 | return color.Warn.Sprintf(value)
141 | case "error":
142 | return color.Error.Sprintf(value)
143 | default:
144 | return color.Light.Sprintf(value)
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module seed-admin
2 |
3 | go 1.18
4 |
5 | require (
6 | github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
7 | github.com/fsnotify/fsnotify v1.5.4
8 | github.com/gin-gonic/gin v1.8.1
9 | github.com/go-redis/redis v6.15.9+incompatible
10 | github.com/go-sql-driver/mysql v1.6.0
11 | github.com/gofrs/uuid v4.2.0+incompatible
12 | github.com/golang-jwt/jwt v3.2.2+incompatible
13 | github.com/gookit/color v1.5.2
14 | github.com/gookit/config/v2 v2.1.8
15 | github.com/gookit/validate v1.4.2
16 | go.uber.org/zap v1.21.0
17 | xorm.io/xorm v1.3.1
18 | )
19 |
20 | require (
21 | github.com/BurntSushi/toml v1.2.1 // indirect
22 | github.com/gin-contrib/sse v0.1.0 // indirect
23 | github.com/go-ole/go-ole v1.2.6 // indirect
24 | github.com/go-playground/locales v0.14.0 // indirect
25 | github.com/go-playground/universal-translator v0.18.0 // indirect
26 | github.com/go-playground/validator/v10 v10.11.0 // indirect
27 | github.com/goccy/go-json v0.9.8 // indirect
28 | github.com/golang/snappy v0.0.4 // indirect
29 | github.com/gookit/filter v1.1.2 // indirect
30 | github.com/gookit/goutil v0.5.15
31 | github.com/imdario/mergo v0.3.13 // indirect
32 | github.com/json-iterator/go v1.1.12 // indirect
33 | github.com/leodido/go-urn v1.2.1 // indirect
34 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
35 | github.com/mattn/go-isatty v0.0.16 // indirect
36 | github.com/mitchellh/mapstructure v1.5.0 // indirect
37 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
38 | github.com/modern-go/reflect2 v1.0.2 // indirect
39 | github.com/pelletier/go-toml/v2 v2.0.2 // indirect
40 | github.com/pkg/errors v0.9.1 // indirect
41 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
42 | github.com/shirou/gopsutil/v3 v3.22.7
43 | github.com/syndtr/goleveldb v1.0.0 // indirect
44 | github.com/tklauser/go-sysconf v0.3.10 // indirect
45 | github.com/tklauser/numcpus v0.4.0 // indirect
46 | github.com/ugorji/go/codec v1.2.7 // indirect
47 | github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
48 | github.com/yusufpapurcu/wmi v1.2.2 // indirect
49 | go.uber.org/atomic v1.9.0 // indirect
50 | go.uber.org/multierr v1.8.0 // indirect; inr5t30936793adb5e
51 | golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect
52 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
53 | golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 // indirect
54 | golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 // indirect
55 | golang.org/x/text v0.3.8 // indirect
56 | google.golang.org/protobuf v1.28.0 // indirect
57 | gopkg.in/yaml.v2 v2.4.0 // indirect
58 | xorm.io/builder v0.3.11 // indirect
59 | )
60 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "seed-admin/core"
4 |
5 | func main() {
6 | core.Run()
7 | }
8 |
--------------------------------------------------------------------------------
/sql/seed-admin.sql:
--------------------------------------------------------------------------------
1 | /*
2 | Navicat MySQL Data Transfer
3 |
4 | Source Server : seed-admin
5 | Source Server Type : MySQL
6 | Source Server Version : 50739
7 | Source Host : 119.8.52.66:3306
8 | Source Schema : seed-admin
9 |
10 | Target Server Type : MySQL
11 | Target Server Version : 50739
12 | File Encoding : 65001
13 |
14 | Date: 12/08/2022 08:54:45
15 | */
16 |
17 | SET NAMES utf8mb4;
18 | SET FOREIGN_KEY_CHECKS = 0;
19 |
20 | -- ----------------------------
21 | -- Table structure for admin_sys_dept
22 | -- ----------------------------
23 | DROP TABLE IF EXISTS `admin_sys_dept`;
24 | CREATE TABLE `admin_sys_dept` (
25 | `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
26 | `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '部门名称',
27 | `parent_id` int(11) NULL DEFAULT NULL COMMENT '父级ID',
28 | `sort` int(11) NULL DEFAULT NULL COMMENT '排序',
29 | `created_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
30 | `updated_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
31 | PRIMARY KEY (`id`) USING BTREE
32 | ) ENGINE = InnoDB AUTO_INCREMENT = 32 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;
33 |
34 | -- ----------------------------
35 | -- Records of admin_sys_dept
36 | -- ----------------------------
37 | INSERT INTO `admin_sys_dept` VALUES (1, '心脏跳动科技', 0, 0, '2022-07-02 14:48:55', '2022-07-21 17:34:35');
38 | INSERT INTO `admin_sys_dept` VALUES (2, '北京总公司', 1, 0, '2022-07-02 14:51:09', '2022-07-08 18:52:48');
39 | INSERT INTO `admin_sys_dept` VALUES (5, '后勤部', 2, 0, '2022-07-02 14:52:48', '2022-07-08 19:31:37');
40 | INSERT INTO `admin_sys_dept` VALUES (6, '西藏分公司', 1, 0, '2022-07-02 14:52:26', '2022-07-08 19:35:38');
41 | INSERT INTO `admin_sys_dept` VALUES (21, '财务部', 6, 0, '2022-07-08 17:46:46', '2022-07-08 19:35:49');
42 | INSERT INTO `admin_sys_dept` VALUES (23, '工程部', 6, 0, '2022-07-08 19:53:15', '2022-07-08 19:53:15');
43 | INSERT INTO `admin_sys_dept` VALUES (25, '江浙沪分公司', 1, 0, '2022-07-10 10:50:02', '2022-07-10 10:50:02');
44 | INSERT INTO `admin_sys_dept` VALUES (26, '开发部', 25, 0, '2022-07-10 10:50:08', '2022-07-10 10:50:08');
45 | INSERT INTO `admin_sys_dept` VALUES (27, '财务部', 25, 0, '2022-07-10 10:50:18', '2022-07-10 10:50:18');
46 | INSERT INTO `admin_sys_dept` VALUES (28, '工程部', 25, 0, '2022-07-10 10:50:23', '2022-07-10 10:50:23');
47 | INSERT INTO `admin_sys_dept` VALUES (29, '东北分公司', 1, 0, '2022-07-10 10:50:32', '2022-07-10 10:50:32');
48 | INSERT INTO `admin_sys_dept` VALUES (30, '销售部', 29, 0, '2022-07-10 10:50:39', '2022-07-10 10:50:39');
49 | INSERT INTO `admin_sys_dept` VALUES (31, '后勤2部', 2, 0, '2022-07-19 18:30:04', '2022-07-22 23:04:24');
50 |
51 | -- ----------------------------
52 | -- Table structure for admin_sys_dict_data
53 | -- ----------------------------
54 | DROP TABLE IF EXISTS `admin_sys_dict_data`;
55 | CREATE TABLE `admin_sys_dict_data` (
56 | `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
57 | `pid` int(11) NOT NULL COMMENT '主键',
58 | `label` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '字典标签',
59 | `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '字典值',
60 | `status` tinyint(1) NOT NULL DEFAULT 0 COMMENT '状态0.正常 1.禁用',
61 | `created_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
62 | `updated_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
63 | PRIMARY KEY (`id`) USING BTREE
64 | ) ENGINE = InnoDB AUTO_INCREMENT = 28 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;
65 |
66 | -- ----------------------------
67 | -- Records of admin_sys_dict_data
68 | -- ----------------------------
69 | INSERT INTO `admin_sys_dict_data` VALUES (18, 7, '正常', '0', 0, '2022-07-16 15:49:08', '2022-07-16 19:09:14');
70 | INSERT INTO `admin_sys_dict_data` VALUES (19, 7, '禁用', '1', 0, '2022-07-16 15:49:14', '2022-07-17 14:22:40');
71 | INSERT INTO `admin_sys_dict_data` VALUES (20, 8, '目录', '0', 0, '2022-07-17 17:04:45', '2022-07-17 17:04:45');
72 | INSERT INTO `admin_sys_dict_data` VALUES (21, 8, '菜单', '1', 0, '2022-07-17 17:04:51', '2022-07-17 17:04:51');
73 | INSERT INTO `admin_sys_dict_data` VALUES (22, 8, '按钮', '2', 0, '2022-07-17 17:04:59', '2022-07-17 17:04:59');
74 | INSERT INTO `admin_sys_dict_data` VALUES (23, 9, '否', '0', 0, '2022-07-17 17:17:26', '2022-07-17 17:17:26');
75 | INSERT INTO `admin_sys_dict_data` VALUES (24, 9, '是', '1', 0, '2022-07-17 17:17:31', '2022-07-17 17:17:31');
76 | INSERT INTO `admin_sys_dict_data` VALUES (26, 19, '显示', '0', 0, '2022-07-27 15:10:11', '2022-07-27 15:10:11');
77 | INSERT INTO `admin_sys_dict_data` VALUES (27, 19, '隐藏', '1', 0, '2022-07-27 15:10:16', '2022-07-27 15:10:16');
78 |
79 | -- ----------------------------
80 | -- Table structure for admin_sys_dict_type
81 | -- ----------------------------
82 | DROP TABLE IF EXISTS `admin_sys_dict_type`;
83 | CREATE TABLE `admin_sys_dict_type` (
84 | `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
85 | `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '字典名称',
86 | `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '字典类型',
87 | `status` tinyint(1) NOT NULL DEFAULT 0 COMMENT '状态0.正常 1.禁用',
88 | `created_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
89 | `updated_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
90 | PRIMARY KEY (`id`) USING BTREE
91 | ) ENGINE = InnoDB AUTO_INCREMENT = 20 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;
92 |
93 | -- ----------------------------
94 | -- Records of admin_sys_dict_type
95 | -- ----------------------------
96 | INSERT INTO `admin_sys_dict_type` VALUES (7, '状态', 'sys_common_status', 0, '2022-07-16 15:48:41', '2022-07-16 19:14:34');
97 | INSERT INTO `admin_sys_dict_type` VALUES (8, '菜单类型', 'sys_menu_type', 0, '2022-07-17 17:04:25', '2022-07-17 17:04:31');
98 | INSERT INTO `admin_sys_dict_type` VALUES (9, '页面缓存', 'sys_page_keepAlive', 0, '2022-07-17 17:17:00', '2022-07-17 17:17:00');
99 | INSERT INTO `admin_sys_dict_type` VALUES (19, '菜单显示', 'sys_menu_visible', 0, '2022-07-27 15:09:48', '2022-07-27 15:10:25');
100 |
101 | -- ----------------------------
102 | -- Table structure for admin_sys_log
103 | -- ----------------------------
104 | DROP TABLE IF EXISTS `admin_sys_log`;
105 | CREATE TABLE `admin_sys_log` (
106 | `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
107 | `user_id` int(11) NOT NULL COMMENT '角色ID',
108 | `method` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '请求方式',
109 | `action` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '行为',
110 | `ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'IP',
111 | `status_code` int(11) NULL DEFAULT NULL COMMENT '响应状态',
112 | `params` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '参数',
113 | `results` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL,
114 | `created_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
115 | `updated_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
116 | PRIMARY KEY (`id`) USING BTREE
117 | ) ENGINE = InnoDB AUTO_INCREMENT = 1587 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;
118 |
119 | -- ----------------------------
120 | -- Records of admin_sys_log
121 | -- ----------------------------
122 |
123 | -- ----------------------------
124 | -- Table structure for admin_sys_menu
125 | -- ----------------------------
126 | DROP TABLE IF EXISTS `admin_sys_menu`;
127 | CREATE TABLE `admin_sys_menu` (
128 | `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
129 | `parent_id` int(11) NOT NULL DEFAULT 0,
130 | `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '菜单名称',
131 | `router_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '路由名称',
132 | `router_path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '路由地址',
133 | `page_path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '页面路径',
134 | `perms` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '权限标识',
135 | `type` tinyint(1) NOT NULL DEFAULT 0 COMMENT '类型0.目录 1.菜单 2.按钮',
136 | `icon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '图标',
137 | `sort` int(11) NULL DEFAULT NULL COMMENT '排序',
138 | `visible` tinyint(1) NOT NULL DEFAULT 0 COMMENT '隐藏0.显示 1.隐藏',
139 | `keep_alive` tinyint(1) NOT NULL DEFAULT 0 COMMENT '页面缓存0.否 1.是',
140 | `status` tinyint(1) NOT NULL DEFAULT 0 COMMENT '状态0.正常 1.禁用',
141 | `created_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
142 | `updated_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
143 | PRIMARY KEY (`id`) USING BTREE
144 | ) ENGINE = InnoDB AUTO_INCREMENT = 80 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;
145 |
146 | -- ----------------------------
147 | -- Records of admin_sys_menu
148 | -- ----------------------------
149 | INSERT INTO `admin_sys_menu` VALUES (1, 0, '仪表盘', 'dashboard', '/dashboard/index', '/dashboard/index.vue', 'sys:dashboard', 1, 'dashboardOutlined', 0, 0, 0, 0, '2022-06-18 04:46:24', '2022-07-12 17:12:33');
150 | INSERT INTO `admin_sys_menu` VALUES (2, 0, '系统管理', 'system', '/system', '', '', 0, 'settingOutlined', 1, 0, 0, 0, '2022-06-18 10:28:30', '2022-06-28 10:17:08');
151 | INSERT INTO `admin_sys_menu` VALUES (3, 2, '用户管理', 'user', '/system/user', '/system/user/index.vue', 'sys:user:list', 1, 'userOutlined', 0, 0, 0, 0, '2022-06-18 10:34:04', '2022-06-29 12:36:52');
152 | INSERT INTO `admin_sys_menu` VALUES (4, 2, '菜单管理', 'menu', '/system/menu', '/system/menu/index.vue', 'sys:menu:list', 1, 'menuOutlined', 2, 0, 0, 0, '2022-06-18 10:37:23', '2022-07-24 14:19:08');
153 | INSERT INTO `admin_sys_menu` VALUES (5, 2, '角色管理', 'role', '/system/role', '/system/role/index.vue', 'sys:role:list', 1, 'robotOutlined', 1, 0, 0, 0, '2022-06-18 10:37:26', '2022-07-24 14:19:05');
154 | INSERT INTO `admin_sys_menu` VALUES (6, 2, '操作日志', 'log', '/system/log', '/system/log/index.vue', 'sys:log:list', 1, 'minusSquareOutlined', 3, 0, 0, 0, '2022-06-18 10:38:48', '2022-07-29 08:13:25');
155 | INSERT INTO `admin_sys_menu` VALUES (7, 2, '系统监控', 'server', '/system/server', '/system/server/index.vue', 'sys:server:info', 1, 'fundProjectionScreenOutlined', 4, 0, 0, 0, '2022-06-20 13:13:36', '2022-08-01 16:38:20');
156 | INSERT INTO `admin_sys_menu` VALUES (8, 2, '字典管理', 'dict', '/system/dict', '/system/dict/index.vue', 'sys:dict:list', 1, 'bookOutlined', 5, 0, 0, 0, '2022-06-21 09:02:15', '2022-07-23 18:32:51');
157 | INSERT INTO `admin_sys_menu` VALUES (46, 5, '新增', NULL, NULL, NULL, 'sys:role:add', 2, '', 0, 0, 0, 0, '2022-06-26 19:27:46', '2022-06-26 19:27:46');
158 | INSERT INTO `admin_sys_menu` VALUES (50, 2, '字典数据', 'dictDetails', '/system/dict/details/:id', '/system/dict/details/index.vue', 'sys:dict:details:list', 1, '', 10, 1, 0, 0, '2022-07-11 17:20:33', '2022-07-22 22:12:06');
159 | INSERT INTO `admin_sys_menu` VALUES (51, 8, '信息', '', '', '', 'sys:dict:info', 2, '', 0, 0, 0, 0, '2022-07-22 18:44:20', '2022-07-22 18:44:20');
160 | INSERT INTO `admin_sys_menu` VALUES (52, 8, '新增', '', '', '', 'sys:dict:add', 2, '', 0, 0, 0, 0, '2022-07-22 22:04:53', '2022-07-22 22:04:53');
161 | INSERT INTO `admin_sys_menu` VALUES (53, 8, '编辑', '', '', '', 'sys:dict:update', 2, '', 0, 0, 0, 0, '2022-07-22 22:05:12', '2022-07-22 22:06:24');
162 | INSERT INTO `admin_sys_menu` VALUES (54, 8, '删除', '', '', '', 'sys:dict:del', 2, '', 0, 0, 0, 0, '2022-07-22 22:06:18', '2022-07-22 22:06:18');
163 | INSERT INTO `admin_sys_menu` VALUES (55, 50, '信息', '', '', '', 'sys:dict:details:info', 2, '', 0, 0, 0, 0, '2022-07-22 22:13:23', '2022-07-22 22:13:23');
164 | INSERT INTO `admin_sys_menu` VALUES (56, 50, '新增', '', '', '', 'sys:dict:details:add', 2, '', 0, 0, 0, 0, '2022-07-22 22:14:40', '2022-07-22 22:14:40');
165 | INSERT INTO `admin_sys_menu` VALUES (57, 50, '编辑', '', '', '', 'sys:dict:details:update', 2, '', 0, 0, 0, 0, '2022-07-22 22:15:00', '2022-07-22 22:15:00');
166 | INSERT INTO `admin_sys_menu` VALUES (58, 50, '删除', '', '', '', 'sys:dict:details:del', 2, '', 0, 0, 0, 0, '2022-07-22 22:15:12', '2022-07-22 22:15:12');
167 | INSERT INTO `admin_sys_menu` VALUES (59, 3, '新增', '', '', '', 'sys:user:add', 2, '', 0, 0, 0, 0, '2022-07-22 22:29:04', '2022-07-22 22:29:04');
168 | INSERT INTO `admin_sys_menu` VALUES (60, 3, '删除', '', '', '', 'sys:user:del', 2, '', 0, 0, 0, 0, '2022-07-22 22:29:15', '2022-07-22 22:29:15');
169 | INSERT INTO `admin_sys_menu` VALUES (61, 3, '信息', '', '', '', 'sys:user:info', 2, '', 0, 0, 0, 0, '2022-07-22 22:29:27', '2022-07-22 22:29:27');
170 | INSERT INTO `admin_sys_menu` VALUES (62, 3, '编辑', '', '', '', 'sys:user:update', 2, '', 0, 0, 0, 0, '2022-07-22 22:29:38', '2022-07-22 22:29:38');
171 | INSERT INTO `admin_sys_menu` VALUES (63, 3, '移动用户部门', '', '', '', 'sys:user:move', 2, '', 0, 0, 0, 0, '2022-07-22 22:29:58', '2022-07-22 22:29:58');
172 | INSERT INTO `admin_sys_menu` VALUES (64, 3, '更新用户角色', '', '', '', 'sys:user:updateUserRole', 2, '', 0, 0, 0, 0, '2022-07-22 22:30:45', '2022-07-22 22:30:45');
173 | INSERT INTO `admin_sys_menu` VALUES (65, 5, '信息', '', '', '', 'sys:role:info', 2, '', 0, 0, 0, 0, '2022-07-22 22:35:42', '2022-07-22 22:35:42');
174 | INSERT INTO `admin_sys_menu` VALUES (66, 5, '编辑', '', '', '', 'sys:role:update', 2, '', 0, 0, 0, 0, '2022-07-22 22:35:53', '2022-07-22 22:35:53');
175 | INSERT INTO `admin_sys_menu` VALUES (67, 5, '删除', '', '', '', 'sys:role:del', 2, '', 0, 0, 0, 0, '2022-07-22 22:36:03', '2022-07-22 22:36:03');
176 | INSERT INTO `admin_sys_menu` VALUES (68, 4, '信息', '', '', '', 'sys:menu:info', 2, '', 0, 0, 0, 0, '2022-07-22 22:42:14', '2022-07-22 22:42:14');
177 | INSERT INTO `admin_sys_menu` VALUES (69, 4, '新增', '', '', '', 'sys:menu:add', 2, '', 0, 0, 0, 0, '2022-07-22 22:42:33', '2022-07-22 22:42:33');
178 | INSERT INTO `admin_sys_menu` VALUES (70, 4, '编辑', '', '', '', 'sys:menu:update', 2, '', 0, 0, 0, 0, '2022-07-22 22:42:43', '2022-07-22 22:42:43');
179 | INSERT INTO `admin_sys_menu` VALUES (71, 4, '删除', '', '', '', 'sys:menu:del', 2, '', 0, 0, 0, 0, '2022-07-22 22:42:55', '2022-07-22 22:42:55');
180 | INSERT INTO `admin_sys_menu` VALUES (72, 3, '部门列表', '', '', '', 'sys:dept:list', 2, '', 0, 0, 0, 0, '2022-07-22 22:52:04', '2022-07-22 22:52:04');
181 | INSERT INTO `admin_sys_menu` VALUES (73, 3, '部门信息', '', '', '', 'sys:dept:info', 2, '', 0, 0, 0, 0, '2022-07-22 22:53:14', '2022-07-22 22:53:14');
182 | INSERT INTO `admin_sys_menu` VALUES (74, 3, '部门新增', '', '', '', 'sys:dept:add', 2, '', 0, 0, 0, 0, '2022-07-22 22:53:31', '2022-07-22 22:53:31');
183 | INSERT INTO `admin_sys_menu` VALUES (75, 3, '部门编辑', '', '', '', 'sys:dept:update', 2, '', 0, 0, 0, 0, '2022-07-22 22:53:44', '2022-07-22 22:53:44');
184 | INSERT INTO `admin_sys_menu` VALUES (76, 3, '部门删除', '', '', '', 'sys:dept:del', 2, '', 0, 0, 0, 0, '2022-07-22 22:53:56', '2022-07-22 22:53:56');
185 | INSERT INTO `admin_sys_menu` VALUES (79, 6, '删除', '', '', '', 'sys:log:del', 2, '', 0, 0, 0, 0, '2022-07-29 11:59:19', '2022-07-29 11:59:19');
186 |
187 | -- ----------------------------
188 | -- Table structure for admin_sys_role
189 | -- ----------------------------
190 | DROP TABLE IF EXISTS `admin_sys_role`;
191 | CREATE TABLE `admin_sys_role` (
192 | `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
193 | `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '角色名称',
194 | `label` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '角色标签',
195 | `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注',
196 | `relevance` tinyint(1) NOT NULL DEFAULT 1 COMMENT '上下级数据权限是否关联0.是 1.否',
197 | `created_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
198 | `updated_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
199 | PRIMARY KEY (`id`) USING BTREE
200 | ) ENGINE = InnoDB AUTO_INCREMENT = 36 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;
201 |
202 | -- ----------------------------
203 | -- Records of admin_sys_role
204 | -- ----------------------------
205 | INSERT INTO `admin_sys_role` VALUES (1, '超级管理员', 'admin', '超级管理员', 0, '2022-06-18 10:49:48', '2022-08-09 18:31:37');
206 | INSERT INTO `admin_sys_role` VALUES (35, '普通角色', 'test', '我只是一个普通的角色', 0, '2022-08-09 18:04:04', '2022-08-09 18:04:04');
207 |
208 | -- ----------------------------
209 | -- Table structure for admin_sys_role_dept
210 | -- ----------------------------
211 | DROP TABLE IF EXISTS `admin_sys_role_dept`;
212 | CREATE TABLE `admin_sys_role_dept` (
213 | `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
214 | `role_id` int(11) NOT NULL COMMENT '角色ID',
215 | `dept_id` int(11) NOT NULL COMMENT '部门ID',
216 | PRIMARY KEY (`id`) USING BTREE
217 | ) ENGINE = InnoDB AUTO_INCREMENT = 119 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;
218 |
219 | -- ----------------------------
220 | -- Records of admin_sys_role_dept
221 | -- ----------------------------
222 | INSERT INTO `admin_sys_role_dept` VALUES (92, 35, 1);
223 | INSERT INTO `admin_sys_role_dept` VALUES (93, 35, 2);
224 | INSERT INTO `admin_sys_role_dept` VALUES (94, 35, 5);
225 | INSERT INTO `admin_sys_role_dept` VALUES (95, 35, 31);
226 | INSERT INTO `admin_sys_role_dept` VALUES (96, 35, 6);
227 | INSERT INTO `admin_sys_role_dept` VALUES (97, 35, 21);
228 | INSERT INTO `admin_sys_role_dept` VALUES (98, 35, 23);
229 | INSERT INTO `admin_sys_role_dept` VALUES (99, 35, 25);
230 | INSERT INTO `admin_sys_role_dept` VALUES (100, 35, 26);
231 | INSERT INTO `admin_sys_role_dept` VALUES (101, 35, 27);
232 | INSERT INTO `admin_sys_role_dept` VALUES (102, 35, 28);
233 | INSERT INTO `admin_sys_role_dept` VALUES (103, 35, 29);
234 | INSERT INTO `admin_sys_role_dept` VALUES (104, 35, 30);
235 | INSERT INTO `admin_sys_role_dept` VALUES (105, 1, 24);
236 | INSERT INTO `admin_sys_role_dept` VALUES (106, 1, 5);
237 | INSERT INTO `admin_sys_role_dept` VALUES (107, 1, 31);
238 | INSERT INTO `admin_sys_role_dept` VALUES (108, 1, 21);
239 | INSERT INTO `admin_sys_role_dept` VALUES (109, 1, 23);
240 | INSERT INTO `admin_sys_role_dept` VALUES (110, 1, 26);
241 | INSERT INTO `admin_sys_role_dept` VALUES (111, 1, 27);
242 | INSERT INTO `admin_sys_role_dept` VALUES (112, 1, 28);
243 | INSERT INTO `admin_sys_role_dept` VALUES (113, 1, 30);
244 | INSERT INTO `admin_sys_role_dept` VALUES (114, 1, 1);
245 | INSERT INTO `admin_sys_role_dept` VALUES (115, 1, 2);
246 | INSERT INTO `admin_sys_role_dept` VALUES (116, 1, 6);
247 | INSERT INTO `admin_sys_role_dept` VALUES (117, 1, 25);
248 | INSERT INTO `admin_sys_role_dept` VALUES (118, 1, 29);
249 |
250 | -- ----------------------------
251 | -- Table structure for admin_sys_role_menu
252 | -- ----------------------------
253 | DROP TABLE IF EXISTS `admin_sys_role_menu`;
254 | CREATE TABLE `admin_sys_role_menu` (
255 | `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
256 | `role_id` int(11) NOT NULL COMMENT '角色ID',
257 | `menu_id` int(11) NOT NULL COMMENT '菜单ID',
258 | PRIMARY KEY (`id`) USING BTREE
259 | ) ENGINE = InnoDB AUTO_INCREMENT = 3198 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;
260 |
261 | -- ----------------------------
262 | -- Records of admin_sys_role_menu
263 | -- ----------------------------
264 | INSERT INTO `admin_sys_role_menu` VALUES (3152, 35, 2);
265 | INSERT INTO `admin_sys_role_menu` VALUES (3153, 35, 7);
266 | INSERT INTO `admin_sys_role_menu` VALUES (3154, 35, 1);
267 | INSERT INTO `admin_sys_role_menu` VALUES (3155, 35, 2);
268 | INSERT INTO `admin_sys_role_menu` VALUES (3156, 35, 7);
269 | INSERT INTO `admin_sys_role_menu` VALUES (3157, 35, 1);
270 | INSERT INTO `admin_sys_role_menu` VALUES (3158, 35, 2);
271 | INSERT INTO `admin_sys_role_menu` VALUES (3159, 35, 7);
272 | INSERT INTO `admin_sys_role_menu` VALUES (3160, 35, 1);
273 | INSERT INTO `admin_sys_role_menu` VALUES (3161, 1, 1);
274 | INSERT INTO `admin_sys_role_menu` VALUES (3162, 1, 59);
275 | INSERT INTO `admin_sys_role_menu` VALUES (3163, 1, 60);
276 | INSERT INTO `admin_sys_role_menu` VALUES (3164, 1, 61);
277 | INSERT INTO `admin_sys_role_menu` VALUES (3165, 1, 62);
278 | INSERT INTO `admin_sys_role_menu` VALUES (3166, 1, 63);
279 | INSERT INTO `admin_sys_role_menu` VALUES (3167, 1, 64);
280 | INSERT INTO `admin_sys_role_menu` VALUES (3168, 1, 72);
281 | INSERT INTO `admin_sys_role_menu` VALUES (3169, 1, 73);
282 | INSERT INTO `admin_sys_role_menu` VALUES (3170, 1, 74);
283 | INSERT INTO `admin_sys_role_menu` VALUES (3171, 1, 75);
284 | INSERT INTO `admin_sys_role_menu` VALUES (3172, 1, 76);
285 | INSERT INTO `admin_sys_role_menu` VALUES (3173, 1, 46);
286 | INSERT INTO `admin_sys_role_menu` VALUES (3174, 1, 65);
287 | INSERT INTO `admin_sys_role_menu` VALUES (3175, 1, 66);
288 | INSERT INTO `admin_sys_role_menu` VALUES (3176, 1, 67);
289 | INSERT INTO `admin_sys_role_menu` VALUES (3177, 1, 68);
290 | INSERT INTO `admin_sys_role_menu` VALUES (3178, 1, 69);
291 | INSERT INTO `admin_sys_role_menu` VALUES (3179, 1, 70);
292 | INSERT INTO `admin_sys_role_menu` VALUES (3180, 1, 71);
293 | INSERT INTO `admin_sys_role_menu` VALUES (3181, 1, 79);
294 | INSERT INTO `admin_sys_role_menu` VALUES (3182, 1, 7);
295 | INSERT INTO `admin_sys_role_menu` VALUES (3183, 1, 51);
296 | INSERT INTO `admin_sys_role_menu` VALUES (3184, 1, 52);
297 | INSERT INTO `admin_sys_role_menu` VALUES (3185, 1, 53);
298 | INSERT INTO `admin_sys_role_menu` VALUES (3186, 1, 54);
299 | INSERT INTO `admin_sys_role_menu` VALUES (3187, 1, 55);
300 | INSERT INTO `admin_sys_role_menu` VALUES (3188, 1, 56);
301 | INSERT INTO `admin_sys_role_menu` VALUES (3189, 1, 57);
302 | INSERT INTO `admin_sys_role_menu` VALUES (3190, 1, 58);
303 | INSERT INTO `admin_sys_role_menu` VALUES (3191, 1, 2);
304 | INSERT INTO `admin_sys_role_menu` VALUES (3192, 1, 3);
305 | INSERT INTO `admin_sys_role_menu` VALUES (3193, 1, 5);
306 | INSERT INTO `admin_sys_role_menu` VALUES (3194, 1, 4);
307 | INSERT INTO `admin_sys_role_menu` VALUES (3195, 1, 6);
308 | INSERT INTO `admin_sys_role_menu` VALUES (3196, 1, 8);
309 | INSERT INTO `admin_sys_role_menu` VALUES (3197, 1, 50);
310 |
311 | -- ----------------------------
312 | -- Table structure for admin_sys_user
313 | -- ----------------------------
314 | DROP TABLE IF EXISTS `admin_sys_user`;
315 | CREATE TABLE `admin_sys_user` (
316 | `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
317 | `dept_id` int(11) NULL DEFAULT NULL COMMENT '部门ID',
318 | `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户名',
319 | `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '密码',
320 | `nick_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '未命名' COMMENT '用户名称',
321 | `phone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
322 | `email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '邮箱',
323 | `avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '头像',
324 | `status` tinyint(1) NOT NULL DEFAULT 0 COMMENT '状态0.正常 1.禁用',
325 | `is_deleted` tinyint(1) NOT NULL DEFAULT 0 COMMENT '软删除0.正常 1.删除',
326 | `created_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
327 | `updated_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
328 | PRIMARY KEY (`id`) USING BTREE
329 | ) ENGINE = InnoDB AUTO_INCREMENT = 31 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;
330 |
331 | -- ----------------------------
332 | -- Records of admin_sys_user
333 | -- ----------------------------
334 | INSERT INTO `admin_sys_user` VALUES (1, 1, 'admin', '73d77a08df7a0026706cd4be8317e3cd', '超级管理员', '13938749240', '120547546@qq.com', '', 0, 0, '2022-06-18 10:04:52', '2022-08-09 18:29:21');
335 | INSERT INTO `admin_sys_user` VALUES (2, 2, 'test', '73D77A08DF7A0026706CD4BE8317E3CD', '普通用户', '13503635596', '', '', 0, 0, '2022-06-18 10:40:02', '2022-07-17 14:39:13');
336 |
337 | -- ----------------------------
338 | -- Table structure for admin_sys_user_role
339 | -- ----------------------------
340 | DROP TABLE IF EXISTS `admin_sys_user_role`;
341 | CREATE TABLE `admin_sys_user_role` (
342 | `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
343 | `user_id` int(11) NOT NULL COMMENT '用户ID',
344 | `role_id` int(11) NOT NULL COMMENT '角色ID',
345 | PRIMARY KEY (`id`) USING BTREE
346 | ) ENGINE = InnoDB AUTO_INCREMENT = 747 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;
347 |
348 | -- ----------------------------
349 | -- Records of admin_sys_user_role
350 | -- ----------------------------
351 | INSERT INTO `admin_sys_user_role` VALUES (743, 1, 1);
352 | INSERT INTO `admin_sys_user_role` VALUES (746, 2, 35);
353 |
354 | -- ----------------------------
355 | -- Table structure for admin_uploads
356 | -- ----------------------------
357 | DROP TABLE IF EXISTS `admin_uploads`;
358 | CREATE TABLE `admin_uploads` (
359 | `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
360 | `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '文件名',
361 | `url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '地址',
362 | `type` int(11) NULL DEFAULT NULL COMMENT '分类',
363 | `created_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
364 | `updated_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
365 | PRIMARY KEY (`id`) USING BTREE
366 | ) ENGINE = InnoDB AUTO_INCREMENT = 93 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;
367 |
368 | -- ----------------------------
369 | -- Records of admin_uploads
370 | -- ----------------------------
371 | INSERT INTO `admin_uploads` VALUES (91, 'logo.png', 'https://admin.seed-app.com/wwwroot/uploads/2022-08-09/db41cb1e-47d0-481c-b928-17165ef9ea6d.png', 2, '2022-08-09 18:29:00', '2022-08-09 18:29:00');
372 | INSERT INTO `admin_uploads` VALUES (92, '微信截图_20220702182931.png', 'https://admin.seed-app.com/wwwroot/uploads/2022-08-09/5e963439-eb00-4250-8251-a029fb6eb2ea.png', 2, '2022-08-09 18:29:21', '2022-08-09 18:29:21');
373 |
374 | -- ----------------------------
375 | -- Table structure for admin_uploads_type
376 | -- ----------------------------
377 | DROP TABLE IF EXISTS `admin_uploads_type`;
378 | CREATE TABLE `admin_uploads_type` (
379 | `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
380 | `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '分类名称',
381 | `label` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '分类标识',
382 | `created_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
383 | `updated_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
384 | PRIMARY KEY (`id`) USING BTREE
385 | ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;
386 |
387 | -- ----------------------------
388 | -- Records of admin_uploads_type
389 | -- ----------------------------
390 | INSERT INTO `admin_uploads_type` VALUES (1, 'admin端通用上传', 'admin', '2022-07-04 15:17:13', '2022-07-04 15:17:16');
391 | INSERT INTO `admin_uploads_type` VALUES (2, 'admin头像上传', 'admin_avatar', '2022-08-03 17:09:46', '2022-08-03 17:09:48');
392 |
393 | SET FOREIGN_KEY_CHECKS = 1;
394 |
--------------------------------------------------------------------------------
/utils/cache.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "seed-admin/common"
5 | "strconv"
6 | "strings"
7 | "time"
8 |
9 | "github.com/gookit/goutil/jsonutil"
10 | )
11 |
12 | // 过期时间
13 | const TTL = time.Hour * 24
14 |
15 | // 前缀
16 | const PATTERN = "auth"
17 |
18 | type AuthCache struct{}
19 |
20 | func (*AuthCache) SetRoleIds(userId int, roleIds []int) {
21 | key := PATTERN + strconv.Itoa(userId)
22 | value, _ := jsonutil.Encode(roleIds)
23 | common.Redis.HMSet(key, map[string]any{
24 | "roleIds": value,
25 | }).Result()
26 | common.Redis.Expire(key, TTL).Result()
27 | }
28 | func (*AuthCache) GetRoleIds(userId int) ([]int, error) {
29 | key := PATTERN + strconv.Itoa(userId)
30 | // redis里取出权限并解码
31 | roleIds := make([]int, 0)
32 | roleIdsStr, err := common.Redis.HGet(key, "roleIds").Result()
33 | if err != nil {
34 | return nil, err
35 | }
36 | jsonutil.DecodeString(roleIdsStr, &roleIds)
37 | return roleIds, nil
38 | }
39 |
40 | // 设置角色权限
41 | func (*AuthCache) SetRoleLabels(userId int, roleLabels []string) {
42 | key := PATTERN + strconv.Itoa(userId)
43 | value, _ := jsonutil.Encode(roleLabels)
44 | common.Redis.HMSet(key, map[string]any{
45 | "roleLabels": value,
46 | }).Result()
47 | common.Redis.Expire(key, TTL).Result()
48 | }
49 | func (*AuthCache) GetRoleLabels(userId int) ([]string, error) {
50 | key := PATTERN + strconv.Itoa(userId)
51 | // redis里取出权限并解码
52 | roleLabels := make([]string, 0)
53 | roleIdsStr, err := common.Redis.HGet(key, "roleLabels").Result()
54 | if err != nil {
55 | return nil, err
56 | }
57 | jsonutil.DecodeString(roleIdsStr, &roleLabels)
58 | return roleLabels, nil
59 | }
60 |
61 | // 设置菜单权限
62 | func (*AuthCache) SetMenuPerms(userId int, menuPerms []string) {
63 | key := PATTERN + strconv.Itoa(userId)
64 | value, _ := jsonutil.Encode(menuPerms)
65 | common.Redis.HMSet(key, map[string]any{
66 | "menuPerms": value,
67 | }).Result()
68 | common.Redis.Expire(key, TTL).Result()
69 | }
70 | func (*AuthCache) GetMenuPerms(userId int) ([]string, error) {
71 | key := PATTERN + strconv.Itoa(userId)
72 | // redis里取出权限并解码
73 | menuPerms := make([]string, 0)
74 | roleIdsStr, err := common.Redis.HGet(key, "menuPerms").Result()
75 | if err != nil {
76 | return nil, err
77 | }
78 | jsonutil.DecodeString(roleIdsStr, &menuPerms)
79 | return menuPerms, nil
80 | }
81 |
82 | func (*AuthCache) Del(userIds []int) error {
83 | keyStrs := make([]string, 0, len(userIds))
84 | for _, item := range userIds {
85 | keyStrs = append(keyStrs, PATTERN+strconv.Itoa(item))
86 | }
87 | if _, err := common.Redis.Del(keyStrs...).Result(); err != nil {
88 | return err
89 | }
90 | return nil
91 | }
92 |
93 | // 获取缓存ID
94 | func getIds() ([]int, error) {
95 | // 拿到缓存所有ID
96 | data, err := common.Redis.Keys(PATTERN + "*").Result()
97 | if err != nil {
98 | return nil, err
99 | }
100 | // 去除PATTERN转换成int切片
101 | ids := make([]int, 0, len(data))
102 | for _, idstr := range data {
103 | idNum, _ := strconv.Atoi(strings.TrimPrefix(idstr, PATTERN))
104 | ids = append(ids, idNum)
105 | }
106 | return ids, nil
107 | }
108 |
109 | // 更新所有角色权限
110 | func (authCache *AuthCache) UpdateAllRole() error {
111 | ids, err := getIds()
112 | if err != nil {
113 | return err
114 | }
115 | // 查询用户拥有的角色ID和角色label
116 | roleMap := make([]map[string]any, 0)
117 | if err = common.DB.Table("admin_sys_user_role").Alias("ur").
118 | Join("INNER", []string{"admin_sys_role", "r"}, "ur.role_id = r.id").
119 | In("user_id", ids).Cols("ur.user_id", "r.id", "r.label").
120 | Find(&roleMap); err != nil {
121 | return err
122 | }
123 | // 通过userId进行分组
124 | roleIdsMap := map[int][]int{}
125 | roleLabelsMap := map[int][]string{}
126 | for _, role := range roleMap {
127 | key := int(role["user_id"].(int32))
128 | roleIdsMap[key] = append(roleIdsMap[key], int(role["id"].(int32)))
129 | roleLabelsMap[key] = append(roleLabelsMap[key], role["label"].(string))
130 | }
131 | // 修改缓存的角色id
132 | delIds := make([]int, 0)
133 | for userId, roleIds := range roleIdsMap {
134 | authCache.SetRoleIds(userId, roleIds)
135 | delIds = SliceDelete(ids, userId)
136 | ids = ids[:len(ids)-1]
137 | }
138 |
139 | authCache.Del(delIds)
140 | // 修改缓存的角色label
141 | for userId, labels := range roleLabelsMap {
142 | authCache.SetRoleLabels(userId, labels)
143 | }
144 | return nil
145 | }
146 |
147 | // 更新所有菜单接口权限
148 | func (authCache *AuthCache) UpdateAllPerm() error {
149 | ids, err := getIds()
150 | if err != nil {
151 | return err
152 | }
153 | // 查询用户拥有的菜单权限
154 | menusMap := make([]map[string]any, 0)
155 | if err = common.DB.Table("admin_sys_menu").Alias("m").
156 | Join("INNER", []string{"admin_sys_role_menu", "rm"}, "rm.menu_id = m.id").
157 | Join("INNER", []string{"admin_sys_user_role", "ur"}, "ur.role_id = rm.role_id").
158 | Where(`m.perms IS NOT NULL AND m.perms != "" AND m.status = 0`).
159 | In("ur.user_id", ids).
160 | Distinct("ur.user_id", "m.perms").
161 | Find(&menusMap); err != nil {
162 | return err
163 | }
164 | // 通过userId进行分组
165 | permsMap := map[int][]string{}
166 | for _, menu := range menusMap {
167 | key := int(menu["user_id"].(int32))
168 | permsMap[key] = append(permsMap[key], menu["perms"].(string))
169 | }
170 |
171 | // 修改缓存的菜单权限
172 | delIds := make([]int, 0)
173 | for userId, perms := range permsMap {
174 | authCache.SetMenuPerms(userId, perms)
175 | delIds = SliceDelete(ids, userId)
176 | ids = ids[:len(ids)-1]
177 | }
178 | authCache.Del(delIds)
179 | return nil
180 | }
181 |
--------------------------------------------------------------------------------
/utils/captcha.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "seed-admin/common"
7 | "sync"
8 |
9 | "github.com/dchest/captcha"
10 | "go.uber.org/zap"
11 | )
12 |
13 | type Captcha struct {
14 | }
15 |
16 | var once sync.Once
17 | var cap *Captcha
18 |
19 | // 单例
20 | func NewCaptcha() *Captcha {
21 | once.Do(func() {
22 | cap = &Captcha{}
23 | })
24 | return cap
25 | }
26 |
27 | // 验证
28 | func CaptchaVerify(captchaId string, value string) error {
29 | if captcha.VerifyString(captchaId, value) {
30 | return nil
31 | }
32 | return fmt.Errorf("验证码错误")
33 | }
34 |
35 | // 创建一个图片验证码
36 | func (c *Captcha) CreateImage() string {
37 | captchaId := captcha.NewLen(common.CONFIG.Int("captcha.len"))
38 | return captchaId
39 | }
40 |
41 | // 重载验证码
42 | func (c *Captcha) Reload(captchaId string) bool {
43 | return captcha.Reload(captchaId)
44 | }
45 |
46 | // 获取二进制图片
47 | func (c *Captcha) ImageByte(captchaId string) ([]byte, error) {
48 | var content bytes.Buffer
49 | if err := captcha.WriteImage(&content, captchaId, common.CONFIG.Int("captcha.width"), common.CONFIG.Int("captcha.height")); err != nil {
50 | common.LOG.Error("验证码获取失败", zap.String("err", err.Error()))
51 | return nil, err
52 | }
53 | return content.Bytes(), nil
54 | }
55 |
--------------------------------------------------------------------------------
/utils/claims.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "seed-admin/common"
5 |
6 | "github.com/gin-gonic/gin"
7 | )
8 |
9 | // 从token里解析userId
10 | func GetUserId(ctx *gin.Context) int {
11 | if claims, ok := ctx.Get("claims"); !ok {
12 | common.LOG.Error("解析userId出错,")
13 | return 0
14 | } else {
15 | return claims.(*CustomerClaims).UserId
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/utils/file.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "os"
5 | "path/filepath"
6 | "strings"
7 | )
8 |
9 | // 获取当前运行路径
10 | func CurrentDirectory() (string, error) {
11 | dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
12 | if err != nil {
13 | return "", err
14 | }
15 | return strings.Replace(dir, "\\", "/", -1), nil
16 | }
17 |
18 | // 判断目录是否存在
19 | func PathExists(path string) (bool, error) {
20 | _, err := os.Stat(path)
21 | if err == nil {
22 | return true, nil
23 | }
24 | if os.IsNotExist(err) {
25 | return false, nil
26 | }
27 | return false, err
28 | }
29 |
30 | // 检查拓展名
31 | func CheckExtension(path string) string {
32 | for i := len(path) - 1; i > 0; i-- {
33 | if path[i] == '.' {
34 | return path[i+1:]
35 | }
36 | }
37 | return ""
38 | }
39 |
--------------------------------------------------------------------------------
/utils/jwt.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "seed-admin/common"
5 |
6 | "github.com/golang-jwt/jwt"
7 | )
8 |
9 | type Jwt struct {
10 | SigningKey []byte
11 | }
12 | type CustomerClaims struct {
13 | *jwt.StandardClaims //标准字段
14 | UserId int `json:"user_id"`
15 | }
16 |
17 | func NewJwt(key ...[]byte) *Jwt {
18 | if len(key) != 0 {
19 | return &Jwt{key[0]}
20 | }
21 | return &Jwt{[]byte(common.CONFIG.String("jwt.signingKey"))}
22 | }
23 |
24 | // 创建Token
25 | func (j *Jwt) CreateToken(claims jwt.Claims) (string, error) {
26 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
27 | return token.SignedString(j.SigningKey)
28 | }
29 |
30 | // 解析Token
31 | func (j *Jwt) ParseToken(tokenString string) (*CustomerClaims, error) {
32 | token, err := jwt.ParseWithClaims(tokenString, &CustomerClaims{}, func(t *jwt.Token) (any, error) { return j.SigningKey, nil })
33 | if err != nil {
34 | return nil, err
35 | }
36 | return token.Claims.(*CustomerClaims), nil
37 | }
38 |
--------------------------------------------------------------------------------
/utils/md5.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "crypto/md5"
5 | "encoding/hex"
6 | )
7 |
8 | // 未加盐的MD5
9 | func Md5(v string) string {
10 | data := md5.Sum([]byte(v))
11 | return hex.EncodeToString(data[:])
12 | }
13 |
14 | // 加盐的MD5
15 | func Md5Salt(v, salt string) string {
16 | hash := md5.New()
17 | hash.Write([]byte(v))
18 | hash.Write([]byte(salt))
19 | return hex.EncodeToString(hash.Sum(nil))
20 | }
21 |
--------------------------------------------------------------------------------
/utils/slice.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import "fmt"
4 |
5 | type cumsum interface {
6 | int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | string
7 | }
8 |
9 | // 切片转in()参数字符
10 | func SliceToInStr[T cumsum](arr []T) string {
11 | str := ""
12 | for _, item := range arr {
13 | if len(str) == 0 {
14 | str += fmt.Sprint(item)
15 | } else {
16 | str += "," + fmt.Sprint(item)
17 | }
18 | }
19 | return str
20 | }
21 |
22 | // 查询切片内是否包含某个值
23 | func SliceIncludes[T cumsum](arr []T, formIndex T) bool {
24 | is := false
25 | for _, item := range arr {
26 | if item == formIndex {
27 | is = true
28 | }
29 | }
30 | return is
31 | }
32 |
33 | // 切片删除
34 | func SliceDelete[T cumsum](arr []T, elem T) []T {
35 | j := 0
36 | for _, item := range arr {
37 | if item != elem {
38 | arr[j] = item
39 | j++
40 | }
41 | }
42 | return arr[:j]
43 | }
44 |
--------------------------------------------------------------------------------
/utils/validator.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "github.com/gookit/validate"
5 | "github.com/gookit/validate/locales/zhcn"
6 | )
7 |
8 | // 验证数据
9 | func ParamsVerify(st any) error {
10 | // 中文输出注册到全局 如有需要可按照RegisterGlobal自定义msg信息
11 | zhcn.RegisterGlobal()
12 | v := validate.New(st)
13 | if !v.Validate() {
14 | return v.Errors.OneError()
15 | }
16 | return nil
17 | }
18 |
--------------------------------------------------------------------------------