sys_initdb.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. package system
  2. import (
  3. "context"
  4. "database/sql"
  5. "errors"
  6. "fmt"
  7. "gorm.io/gorm"
  8. "server/global"
  9. "server/model/system/request"
  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. if len(initializers) == 0 {
  80. return errors.New("无可用初始化过程,请检查初始化是否已执行完成")
  81. }
  82. sort.Sort(&initializers) // 保证有依赖的 initializer 排在后面执行
  83. // Note: 若 initializer 只有单一依赖,可以写为 B=A+1, C=A+1; 由于 BC 之间没有依赖关系,所以谁先谁后并不影响初始化
  84. // 若存在多个依赖,可以写为 C=A+B, D=A+B+C, E=A+1;
  85. // C必然>A|B,因此在AB之后执行,D必然>A|B|C,因此在ABC后执行,而E只依赖A,顺序与CD无关,因此E与CD哪个先执行并不影响
  86. var initHandler TypedDBInitHandler
  87. switch conf.DBType {
  88. case "mysql":
  89. initHandler = NewMysqlInitHandler()
  90. ctx = context.WithValue(ctx, "dbtype", "mysql")
  91. case "pgsql":
  92. initHandler = NewPgsqlInitHandler()
  93. ctx = context.WithValue(ctx, "dbtype", "pgsql")
  94. case "sqlite":
  95. initHandler = NewSqliteInitHandler()
  96. ctx = context.WithValue(ctx, "dbtype", "sqlite")
  97. case "mssql":
  98. initHandler = NewMssqlInitHandler()
  99. ctx = context.WithValue(ctx, "dbtype", "mssql")
  100. default:
  101. initHandler = NewMysqlInitHandler()
  102. ctx = context.WithValue(ctx, "dbtype", "mysql")
  103. }
  104. ctx, err = initHandler.EnsureDB(ctx, &conf)
  105. if err != nil {
  106. return err
  107. }
  108. db := ctx.Value("db").(*gorm.DB)
  109. global.GVA_DB = db
  110. if err = initHandler.InitTables(ctx, initializers); err != nil {
  111. return err
  112. }
  113. if err = initHandler.InitData(ctx, initializers); err != nil {
  114. return err
  115. }
  116. if err = initHandler.WriteConfig(ctx); err != nil {
  117. return err
  118. }
  119. initializers = initSlice{}
  120. cache = map[string]*orderedInitializer{}
  121. return nil
  122. }
  123. // createDatabase 创建数据库( EnsureDB() 中调用 )
  124. func createDatabase(dsn string, driver string, createSql string) error {
  125. db, err := sql.Open(driver, dsn)
  126. if err != nil {
  127. return err
  128. }
  129. defer func(db *sql.DB) {
  130. err = db.Close()
  131. if err != nil {
  132. fmt.Println(err)
  133. }
  134. }(db)
  135. if err = db.Ping(); err != nil {
  136. return err
  137. }
  138. _, err = db.Exec(createSql)
  139. return err
  140. }
  141. // createTables 创建表(默认 dbInitHandler.initTables 行为)
  142. func createTables(ctx context.Context, inits initSlice) error {
  143. next, cancel := context.WithCancel(ctx)
  144. defer func(c func()) { c() }(cancel)
  145. for _, init := range inits {
  146. if init.TableCreated(next) {
  147. continue
  148. }
  149. if n, err := init.MigrateTable(next); err != nil {
  150. return err
  151. } else {
  152. next = n
  153. }
  154. }
  155. return nil
  156. }
  157. /* -- sortable interface -- */
  158. func (a initSlice) Len() int {
  159. return len(a)
  160. }
  161. func (a initSlice) Less(i, j int) bool {
  162. return a[i].order < a[j].order
  163. }
  164. func (a initSlice) Swap(i, j int) {
  165. a[i], a[j] = a[j], a[i]
  166. }