package system import ( "context" "errors" "fmt" "path/filepath" "github.com/go-kratos/kratos/v2/log" "gorm.io/driver/mysql" "gorm.io/gorm" ) // MysqlConfig MySQL配置 type MysqlConfig struct { Path string Port string Config string Dbname string Username string Password string MaxIdleConns int MaxOpenConns int LogMode string } // Dsn 生成MySQL DSN func (c *MysqlConfig) Dsn() string { return c.Username + ":" + c.Password + "@tcp(" + c.Path + ":" + c.Port + ")/" + c.Dbname + "?" + c.Config } // MysqlInitHandler MySQL初始化处理器 type MysqlInitHandler struct { log *log.Helper configWriter func(ctx context.Context, dbType string, config interface{}) error } // NewMysqlInitHandler 创建MySQL初始化处理器 func NewMysqlInitHandler(logger *log.Helper, configWriter func(ctx context.Context, dbType string, config interface{}) error) *MysqlInitHandler { return &MysqlInitHandler{ log: logger, configWriter: configWriter, } } // WriteConfig mysql回写配置 func (h *MysqlInitHandler) WriteConfig(ctx context.Context) error { c, ok := ctx.Value("config").(MysqlConfig) if !ok { return errors.New("mysql config invalid") } if h.configWriter != nil { return h.configWriter(ctx, "mysql", c) } return nil } // EnsureDB 创建数据库并初始化 mysql func (h *MysqlInitHandler) EnsureDB(ctx context.Context, conf *InitDBRequest) (next context.Context, err error) { if s, ok := ctx.Value("dbtype").(string); !ok || s != "mysql" { return ctx, ErrDBTypeMismatch } c := h.toMysqlConfig(conf) next = context.WithValue(ctx, "config", c) if c.Dbname == "" { return ctx, nil } // 如果没有数据库名, 则跳出初始化数据 dsn := h.mysqlEmptyDsn(conf) createSql := fmt.Sprintf("CREATE DATABASE IF NOT EXISTS `%s` DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci;", c.Dbname) if err = createDatabase(dsn, "mysql", createSql); err != nil { return nil, err } // 创建数据库 var db *gorm.DB if db, err = gorm.Open(mysql.New(mysql.Config{ DSN: c.Dsn(), // DSN data source name DefaultStringSize: 191, // string 类型字段的默认长度 SkipInitializeWithVersion: true, // 根据版本自动配置 }), &gorm.Config{DisableForeignKeyConstraintWhenMigrating: true}); err != nil { return ctx, err } // 设置AutoCode根目录 autoCodeRoot, _ := filepath.Abs("..") next = context.WithValue(next, "autoCodeRoot", autoCodeRoot) next = context.WithValue(next, "db", db) return next, err } // InitTables 初始化表 func (h *MysqlInitHandler) InitTables(ctx context.Context, inits InitSlice) error { return createTables(ctx, inits) } // InitData 初始化数据 func (h *MysqlInitHandler) InitData(ctx context.Context, inits InitSlice) error { next, cancel := context.WithCancel(ctx) defer cancel() for _, init := range inits { if init.DataInserted(next) { h.log.Infof(InitDataExist, Mysql, init.InitializerName()) continue } if n, err := init.InitializeData(next); err != nil { h.log.Errorf(InitDataFailed, Mysql, init.InitializerName(), err) return err } else { next = n h.log.Infof(InitDataSuccess, Mysql, init.InitializerName()) } } h.log.Infof(InitSuccess, Mysql) return nil } // mysqlEmptyDsn mysql 空数据库 建库链接 func (h *MysqlInitHandler) mysqlEmptyDsn(i *InitDBRequest) string { host := i.Host port := i.Port if host == "" { host = "127.0.0.1" } if port == "" { port = "3306" } return fmt.Sprintf("%s:%s@tcp(%s:%s)/", i.UserName, i.Password, host, port) } // toMysqlConfig 转换为MySQL配置 func (h *MysqlInitHandler) toMysqlConfig(i *InitDBRequest) MysqlConfig { return MysqlConfig{ Path: i.Host, Port: i.Port, Dbname: i.DBName, Username: i.UserName, Password: i.Password, MaxIdleConns: 10, MaxOpenConns: 100, LogMode: "error", Config: "charset=utf8mb4&parseTime=True&loc=Local", } }