给Gin-Vue-Admin框架增加Sqlite数据库支持
缘起
博主老潘我目前代码的水平是会一点Vue,曾经用Vue和Python做过一个小web系统。下面准备把Go作为第二主力语言,最近刚开始入门。为了在实践中学习,于是我制定了一个小目标,准备边学习边研究,做一个工作中团队使用的数据治理系统,取名为DMFlow,即如水一样顺畅的数据管理。目标不算小,系统需要有常规的账号管理,权限登录。也有一些特定需求,比如适合工作流程的特定的数据处理业务,系统只能在公司的封闭远程桌面云上运行,数据库只能选取本地的Sqlite。系统的架构准备前端用Vue,后端用Go,不从头做起,借鉴选取一个现成的系统,经过搜索,找到了国人开发的一个优秀的管理平台:Gin-Vue-Admin。引用官网的介绍:==GIN-VUE-ADMIN(GVA)是一个基于vue和gin开发的全栈前后端分离的开发基础平台,拥有jwt鉴权,动态路由,动态菜单,casbin鉴权,表单生成器,代码生成器等功能,提供了多种示例文件,让大家把更多时间专注在业务开发上。==于是我决定用这个项目为基础,学习Vue和Go语言开发,希望在这个平台的基础上最终做成我的DMFlow系统。
GVA目前只支持Mysql和PostgreSQL数据库,而我必须使用本地不需要部署的Sqlite数据库。所以首先要解决的一个大问题就是给GVA项目框架增加Sqlite数据库支持。
目标效果
实现GVA生成Sqlite数据库,并自动初始数据。
读取Sqlite中的初始数据,实现用初始的管理员账号正常登录,展示首页仪表盘。
具体步骤
为实现支持sqlite,博主半小白还是研究了好久go和GVA的逻辑,最后还是初步成功了。把主要步骤记录如下。
1. 给config.yaml增加Sqlite相关内容
GVA的config.yaml是整个系统的配置文件。首先要增加sqlite的相关内容。
-
- 把系统数据库的类型改为Sqlite
1 # ./config.yaml 后端server根目录下的config.yaml文件
2 system:
3 env: develop
4 addr: 8888
5 db-type: sqlite # 此处修改为sqlite
6 oss-type: local
7 use-multipoint: false
8 use-redis: false
9 iplimit-count: 15000
10 iplimit-time: 3600
- 增加Sqlite的具体配置,这里我就直接Copy了mysql的相关参数条目。开始时db-name参数留空,初始系统后会把请求的数据库名自动填上。
1 sqlite:
2 path: "./sqlite_db"
3 port: ""
4 config: sslmode=disable TimeZone=Asia/Shanghai
5 db-name: # db-name 在开始留空,后面会自动填上
6 username: pp
7 password: ""
8 max-idle-conns: 10
9 max-open-conns: 100
10 log-mode: error
11 log-zap: false
2. 在初始数据模块中增加Sqlite相关组件
- 修改service/system/init_db.go,增加Sqlite分支,修改InitDB函数。
1 // 仅展示部分函数代码
2 func (initDBService *InitDBService) InitDB(conf request.InitDB) (err error) {
3 ctx := context.TODO()
4 if len(initializers) == 0 {
5 return errors.New("无可用初始化过程,请检查初始化是否已执行完成")
6 }
7 sort.Sort(&initializers) // 保证有依赖的 initializer 排在后面执行
8 // Note: 若 initializer 只有单一依赖,可以写为 B=A+1, C=A+1; 由于 BC 之间没有依赖关系,所以谁先谁后并不影响初始化
9 // 若存在多个依赖,可以写为 C=A+B, D=A+B+C, E=A+1;
10 // C必然>A|B,因此在AB之后执行,D必然>A|B|C,因此在ABC后执行,而E只依赖A,顺序与CD无关,因此E与CD哪个先执行并不影响
11 var initHandler TypedDBInitHandler
12 switch conf.DBType {
13 case "mysql":
14 initHandler = NewMysqlInitHandler()
15 ctx = context.WithValue(ctx, "dbtype", "mysql")
16 case "pgsql":
17 initHandler = NewPgsqlInitHandler()
18 ctx = context.WithValue(ctx, "dbtype", "pgsql")
19 case "sqlite": //增加sqlite分支
20 initHandler = NewSqliteInitHandler()
21 ctx = context.WithValue(ctx, "dbtype", "sqlite")
22 default:
23 initHandler = NewMysqlInitHandler()
24 ctx = context.WithValue(ctx, "dbtype", "mysql")
- 增加sqlite建立数据库、初始表及初始数据的单独代码文件,service/system/sys_initdb_sqlite.go 。内容基本上照搬mysql的就可以。注意连接数据库的代码有所改变,下面有展示。
1 // service/system/sys_initdb_sqlite.go
2 func (h SqliteInitHandler) EnsureDB(ctx context.Context, conf *request.InitDB) (next context.Context, err error) {
3 if s, ok := ctx.Value("dbtype").(string); !ok || s != "sqlite" {
4 return ctx, ErrDBTypeMismatch
5 }
6 db_path := "./sqlite_db/dmflow_std.db"
7 var db *gorm.DB
8 if db, err = gorm.Open(sqlite.Open(db_path), &gorm.Config{}); err != nil {
9 return ctx, nil
10 }
11 global.GVA_CONFIG.AutoCode.Root, _ = filepath.Abs("..")
12 c := conf.ToSqliteConfig()
13 next = context.WithValue(ctx, "config", c)
14 next = context.WithValue(next, "db", db)
15 return next, err
16 }
-
- 增加source文件夹中,sqlite单独建立关联表的文件 后台有一些表是通过mysql view的功能建立的,不适合sqlite,所以还得单独改写成适合sqlite的。而且建议先把mysql和postg的两个建表文件删掉。 这个文件的关键代码,是如下展示,建立权限与目录关联的一张表。
1 // source/system/view_authority_menu_sqlite.go
2 func (v *initMenuViewSqlite) InitializeData(ctx context.Context) (context.Context, error) {
3 db, ok := ctx.Value("db").(*gorm.DB)
4 if !ok {
5 return ctx, system.ErrMissingDBContext
6 }
7 if s, ok := ctx.Value("dbtype").(string); !ok || s != "sqlite" {
8 return ctx, nil // ignore
9 }
10 db.AutoMigrate(&sysModel.SysMenu{})
11 rawAuthMenusSlice := []sysModel.SysBaseMenu{}
12 db.Preload("SysAuthoritys").Find(&rawAuthMenusSlice) //先获得所有权限与菜单管理的结果
13 authMenusSlice := []sysModel.SysMenu{}
14 for _, rawAuthMenu := range rawAuthMenusSlice { //构造SysMenu的slice用于插入数据
15 fmt.Println((rawAuthMenu))
16 for _, authority := range rawAuthMenu.SysAuthoritys {
17 authMenu := sysModel.SysMenu{
18 SysBaseMenu: sysModel.SysBaseMenu{
19 MenuLevel: rawAuthMenu.MenuLevel,
20 ParentId: rawAuthMenu.ParentId,
21 Path: rawAuthMenu.Path,
22 Name: rawAuthMenu.Name,
23 Hidden: rawAuthMenu.Hidden,
24 Component: rawAuthMenu.Component,
25 Sort: rawAuthMenu.Sort,
26 Meta: rawAuthMenu.Meta,
27 },
28 MenuId: strconv.Itoa(int(rawAuthMenu.ID)),
29 AuthorityId: authority.AuthorityId,
30 }
31 authMenusSlice = append(authMenusSlice, authMenu)
32 }
33
34 }
35 err := db.Create(&authMenusSlice).Error
3. 建立Sqlite数据库,初始数据
上述工作做完,应该可以启动服务器了。
找一个发送请求的API测试工具,例如postman,发送请求启动数据初始化。如下所示。
一切顺利的话,数据库就建立完毕,并初始化了一些基础数据了。可以看到在sqlite_db文件夹里新生成的sqlite数据库文件。
4. 在启动数据库连接模块中增加Sqlite连接组件
增加连接已有sqlite数据库的代码,在initialize/gorm.go的Gorm函数中增加sqlite分支。
1 func Gorm() *gorm.DB {
2 fmt.Println(global.GVA_CONFIG.System.DbType)
3 switch global.GVA_CONFIG.System.DbType {
4 case "mysql":
5 return GormMysql()
6 case "pgsql":
7 return GormPgSql()
8 case "sqlite":
9 return GormSqlite()
10 default:
11 return GormSqlite()
12 }
13 }
增加initialize/gorm_sqlite.go,添加具体连接的代码。
1 func GormSqlite() *gorm.DB {
2 m := global.GVA_CONFIG.Sqlite
3 if m.Dbname == "" {
4 return nil
5 }
6 db_path := fmt.Sprintf("%s/%s.db", m.Path, m.Dbname)
7 if db, err := gorm.Open(sqlite.Open(db_path), internal.Gorm.Config()); err != nil {
8 return nil
9 } else {
10 sqlDB, _ := db.DB()
11 sqlDB.SetMaxIdleConns(m.MaxIdleConns)
12 sqlDB.SetMaxOpenConns(m.MaxOpenConns)
13 return db
14 }
15 }
在重新启动后台服务器。
然后启动前端,输入默认管理员账号和密码,admin,123456。成功连接了sqlite中的数据!研究了两周,看到改了以后的代码成功运行,还是很有成就感的。
后续工作
后续就是继续研究前端的vue代码,实现我自己这个系统的特定功能了。首先把账号管理模块搞定。