给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的就可以。注意连接数据库的代码有所改变,下面有展示。

image.png

 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的两个建表文件删掉。

      image.png

      这个文件的关键代码,是如下展示,建立权限与目录关联的一张表。
 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,发送请求启动数据初始化。如下所示。

image.png

一切顺利的话,数据库就建立完毕,并初始化了一些基础数据了。可以看到在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中的数据!研究了两周,看到改了以后的代码成功运行,还是很有成就感的。

image.png

后续工作

后续就是继续研究前端的vue代码,实现我自己这个系统的特定功能了。首先把账号管理模块搞定。