sys_initdb.go 5.0 KB

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