sys_initdb.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. package system
  2. import (
  3. "context"
  4. "database/sql"
  5. "errors"
  6. "fmt"
  7. "github.com/flipped-aurora/gin-vue-admin/server/global"
  8. "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
  9. "gorm.io/gorm"
  10. "sort"
  11. )
  12. const (
  13. Mysql = "mysql"
  14. Pgsql = "pgsql"
  15. Sqlite = "sqlite"
  16. Mssql = "mssql"
  17. InitSuccess = "\n[%v] --> 初始数据成功!\n"
  18. InitDataExist = "\n[%v] --> %v 的初始数据已存在!\n"
  19. InitDataFailed = "\n[%v] --> %v 初始数据失败! \nerr: %+v\n"
  20. InitDataSuccess = "\n[%v] --> %v 初始数据成功!\n"
  21. )
  22. const (
  23. InitOrderSystem = 10
  24. InitOrderInternal = 1000
  25. InitOrderExternal = 100000
  26. )
  27. var (
  28. ErrMissingDBContext = errors.New("missing db in context")
  29. ErrMissingDependentContext = errors.New("missing dependent value in context")
  30. ErrDBTypeMismatch = errors.New("db type mismatch")
  31. )
  32. // SubInitializer 提供 source/*/init() 使用的接口,每个 initializer 完成一个初始化过程
  33. type SubInitializer interface {
  34. InitializerName() string // 不一定代表单独一个表,所以改成了更宽泛的语义
  35. MigrateTable(ctx context.Context) (next context.Context, err error)
  36. InitializeData(ctx context.Context) (next context.Context, err error)
  37. TableCreated(ctx context.Context) bool
  38. DataInserted(ctx context.Context) bool
  39. }
  40. // TypedDBInitHandler 执行传入的 initializer
  41. type TypedDBInitHandler interface {
  42. EnsureDB(ctx context.Context, conf *request.InitDB) (context.Context, error) // 建库,失败属于 fatal error,因此让它 panic
  43. WriteConfig(ctx context.Context) error // 回写配置
  44. InitTables(ctx context.Context, inits initSlice) error // 建表 handler
  45. InitData(ctx context.Context, inits initSlice) error // 建数据 handler
  46. }
  47. // orderedInitializer 组合一个顺序字段,以供排序
  48. type orderedInitializer struct {
  49. order int
  50. SubInitializer
  51. }
  52. // initSlice 供 initializer 排序依赖时使用
  53. type initSlice []*orderedInitializer
  54. var (
  55. initializers initSlice
  56. cache map[string]*orderedInitializer
  57. )
  58. // RegisterInit 注册要执行的初始化过程,会在 InitDB() 时调用
  59. func RegisterInit(order int, i SubInitializer) {
  60. if initializers == nil {
  61. initializers = initSlice{}
  62. }
  63. if cache == nil {
  64. cache = map[string]*orderedInitializer{}
  65. }
  66. name := i.InitializerName()
  67. if _, existed := cache[name]; existed {
  68. panic(fmt.Sprintf("Name conflict on %s", name))
  69. }
  70. ni := orderedInitializer{order, i}
  71. initializers = append(initializers, &ni)
  72. cache[name] = &ni
  73. }
  74. /* ---- * service * ---- */
  75. type InitDBService struct{}
  76. // InitDB 创建数据库并初始化 总入口
  77. func (initDBService *InitDBService) InitDB(conf request.InitDB) (err error) {
  78. ctx := context.TODO()
  79. ctx = context.WithValue(ctx, "adminPassword", conf.AdminPassword)
  80. if len(initializers) == 0 {
  81. return errors.New("无可用初始化过程,请检查初始化是否已执行完成")
  82. }
  83. sort.Sort(&initializers) // 保证有依赖的 initializer 排在后面执行
  84. // Note: 若 initializer 只有单一依赖,可以写为 B=A+1, C=A+1; 由于 BC 之间没有依赖关系,所以谁先谁后并不影响初始化
  85. // 若存在多个依赖,可以写为 C=A+B, D=A+B+C, E=A+1;
  86. // C必然>A|B,因此在AB之后执行,D必然>A|B|C,因此在ABC后执行,而E只依赖A,顺序与CD无关,因此E与CD哪个先执行并不影响
  87. var initHandler TypedDBInitHandler
  88. switch conf.DBType {
  89. case "mysql":
  90. initHandler = NewMysqlInitHandler()
  91. ctx = context.WithValue(ctx, "dbtype", "mysql")
  92. case "pgsql":
  93. initHandler = NewPgsqlInitHandler()
  94. ctx = context.WithValue(ctx, "dbtype", "pgsql")
  95. case "sqlite":
  96. initHandler = NewSqliteInitHandler()
  97. ctx = context.WithValue(ctx, "dbtype", "sqlite")
  98. case "mssql":
  99. initHandler = NewMssqlInitHandler()
  100. ctx = context.WithValue(ctx, "dbtype", "mssql")
  101. default:
  102. initHandler = NewMysqlInitHandler()
  103. ctx = context.WithValue(ctx, "dbtype", "mysql")
  104. }
  105. ctx, err = initHandler.EnsureDB(ctx, &conf)
  106. if err != nil {
  107. return err
  108. }
  109. db := ctx.Value("db").(*gorm.DB)
  110. global.GVA_DB = db
  111. if err = initHandler.InitTables(ctx, initializers); err != nil {
  112. return err
  113. }
  114. if err = initHandler.InitData(ctx, initializers); err != nil {
  115. return err
  116. }
  117. if err = initHandler.WriteConfig(ctx); err != nil {
  118. return err
  119. }
  120. initializers = initSlice{}
  121. cache = map[string]*orderedInitializer{}
  122. return nil
  123. }
  124. // createDatabase 创建数据库( EnsureDB() 中调用 )
  125. func createDatabase(dsn string, driver string, createSql string) error {
  126. db, err := sql.Open(driver, dsn)
  127. if err != nil {
  128. return err
  129. }
  130. defer func(db *sql.DB) {
  131. err = db.Close()
  132. if err != nil {
  133. fmt.Println(err)
  134. }
  135. }(db)
  136. if err = db.Ping(); err != nil {
  137. return err
  138. }
  139. _, err = db.Exec(createSql)
  140. return err
  141. }
  142. // createTables 创建表(默认 dbInitHandler.initTables 行为)
  143. func createTables(ctx context.Context, inits initSlice) error {
  144. next, cancel := context.WithCancel(ctx)
  145. defer func(c func()) { c() }(cancel)
  146. for _, init := range inits {
  147. if init.TableCreated(next) {
  148. continue
  149. }
  150. if n, err := init.MigrateTable(next); err != nil {
  151. return err
  152. } else {
  153. next = n
  154. }
  155. }
  156. return nil
  157. }
  158. /* -- sortable interface -- */
  159. func (a initSlice) Len() int {
  160. return len(a)
  161. }
  162. func (a initSlice) Less(i, j int) bool {
  163. return a[i].order < a[j].order
  164. }
  165. func (a initSlice) Swap(i, j int) {
  166. a[i], a[j] = a[j], a[i]
  167. }