diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..adf8f72 --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +# ---> Go +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work + diff --git a/model.go b/model.go new file mode 100644 index 0000000..3f674b3 --- /dev/null +++ b/model.go @@ -0,0 +1,202 @@ +package model + +import ( + "strconv" + "strings" + "time" + + "gorm.io/driver/postgres" + "gorm.io/gorm" + "qoobing.com/gomod/log" + "qoobing.com/gomod/redis" + "qoobing.com/gomod/database" +) + + +type Model struct { + DB *gorm.DB + DbTxStatus DbTxStatus + Redis redis.Conn + RedisPool *redis.Pool +} + +type DbTxStatus int +const ( + DBTX_STATUS_TX_NONE DbTxStatus = 0 + DBTX_STATUS_TX_DOING DbTxStatus = 1 + DBTX_STATUS_TX_SUCCESS DbTxStatus = 2 + DBTX_STATUS_TX_FAILED DbTxStatus = 3 +) + +var ( + defaultDb *database.Config + defaultDsn string + defaultDbDebug bool + defaultGormDB *gorm.DB + defaultRds redis.Config + defaultRedis *redis.Pool + defaultRedisDebug bool + defaultOptions []Option +) + +func NewModel() *Model { + if len(defaultOptions) == 0 { + panic("No defualt Database&Redis been configed") + } + return NewModelWithOption(defaultOptions...) +} + +func NewModelDefault() *Model { + return mdb.NewModelWithOption(OptOpenDefaultDatabase, OptOpenDefaultRedis) +} + +func NewModelDefaultRedis() *Model { + return mdb.NewModelWithOption(OptOpenDefaultRedis) +} + +func NewModelDefaultDatabase() *Model { + return mdb.NewModelWithOption(OptOpenDefaultDatabase) +} + +func NewModelWithOption(options ...Option) *Model { + n := 0 + m := Model{} + log.Debugf("Start NewModelWithOption...") + for _, option := range options { + n++ + option(&m) + } + log.Debugf("Finish NewModelWithOption(with %d options)", n) + return &m +} + +func (m *Model) Close() { + if m.Redis != nil { + m.Redis.Close() + } + if m.DB == nil { + m.DbTxStatus = DBTX_STATUS_TX_NONE + } else if m.DbTxStatus == DBTX_STATUS_TX_DOING { + m.DB.Rollback() + m.DbTxStatus = DBTX_STATUS_TX_FAILED + } +} + +func (m *Model) Begin() { + if m.DB == nil { + panic("unreachable code, m.DB is uninitialized") + } else if m.DbTxStatus != DBTX_STATUS_TX_NONE { + panic("unreachable code, begin transaction towice???") + } else { + m.DbTxStatus = DBTX_STATUS_TX_DOING + m.DB = m.DB.Begin() + } +} + +func (m *Model) Commit() error { + if m.DB == nil { + panic("unreachable code, m.DB is uninitialized") + } else if m.DbTxStatus != DBTX_STATUS_TX_DOING { + return nil + } else if err := m.DB.Commit().Error; err != nil { + m.DbTxStatus = DBTX_STATUS_TX_FAILED + return err + } else { + m.DbTxStatus = DBTX_STATUS_TX_SUCCESS + return nil + } +} + +// Option is model's option +type Option func(*Model) + +// OptOpenDefaultDatabase option function for open default database +func OptOpenDefaultDatabase(m *Model) { + if defaultGormDB != nil { + m.DB = defaultGormDB + } else if db, err := gorm.Open( + postgres.New(postgres.Config{ + DSN: defaultDsn, + PreferSimpleProtocol: true, + }), &gorm.Config{}); err != nil { + panic("DATABASE_OPEN_ERROR") + } else { + if defaultDbDebug { + m.DB = db.Debug() + } else { + m.DB = db + } + if sqlDB, err := m.DB.DB(); err != nil { + panic("DATABASE_OPEN_ERROR") + } else { + sqlDB.SetMaxIdleConns(3) + sqlDB.SetMaxOpenConns(10) + sqlDB.SetConnMaxLifetime(time.Minute) + } + defaultGormDB = m.DB + } + //TODO: check cocurrent + m.DB = m.DB.Session(&gorm.Session{QueryFields: true}) + log.Debugf("Opt for open default database done") + return +} + +// OptOpenDefaultRedisSentinelPool option function for open default redis sentinel +func OptOpenDefaultRedis(m *Model) { + if defaultRedis != nil { + m.Redis = defaultRedis.Get() + m.RedisPool = defaultRedis + } else if pool := sentinel.NewPool(defaultRds); pool == nil { + panic("REDIS_ERROR") + } else { + defaultRedis = pool + m.Redis = defaultRedis.Get() + m.RedisPool = defaultRedis + } + + log.Debugf("Opt for open default redis done") + return +} + +// Init init default database config & redis config +func Init(defaultDb *database.Config, defaultRds *redis.Config){ + defaultOptions = []Options{} + + if defaultDb { + // database init + arrConfStr := []string{ + "host=" + defaultDb.Host, + "port=" + strconv.Itoa(defaultDb.Port), + "user=" + defaultDb.Username, + "password=" + defaultDb.Password, + "dbname=" + defaultDb.Dbname, + defaultDb.ExtraParameters, + } + defaultDsn = strings.Join(arrConfStr, " ") + defaultDbDebug = defaultDb.Debug + // database debug + if defaultDbDebug && len(defaultDb.Password) > 5 { + p := defaultDb.Password + l := len(p) + secDefaultDsn := strings.Replace(defaultDsn, p[2:l-3], "*****", 1) + log.Debugf("defaultDsn='%s'", secDefaultDsn) + } + + defaultOptions = append(defaultOptions, OptOpenDefaultDatabase) + } + + if defaultRds != nil { + // redis init + defaultRds = coreRedis + defaultRedisDebug = coreRedis.Debug + // redis debug + if defaultRedisDebug && len(coreRedis.Password) > 5 { + p := coreRedis.Password + l := len(p) + secDefaultRds := defaultRds + secDefaultRds.Password = strings.Replace(p, p[2:l-3], "*****", 1) + log.PrintPretty("defaultRds=", secDefaultRds) + } + defaultOptions = append(defaultOptions, OptOpenDefaultRedis) + } +}