diff --git a/go.mod b/go.mod index 195c57e0c75153a0af9baa693d0c9cffb1af21bd..ada7d6a97bc357e33e8c264624497c6a2776ba1b 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.12 require ( github.com/dgrijalva/jwt-go v3.2.0+incompatible + github.com/go-sql-driver/mysql v1.6.0 github.com/julienschmidt/httprouter v1.3.0 github.com/natefinch/lumberjack v2.0.0+incompatible github.com/satori/go.uuid v1.2.0 @@ -13,4 +14,5 @@ require ( google.golang.org/grpc v1.40.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/yaml.v2 v2.3.0 + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b ) diff --git a/go.sum b/go.sum index 76086e8011694a356c66bec73ec5cbbe5a70ce65..7526e4bf9ceaa9a373ce74ba976d041d365d4308 100644 --- a/go.sum +++ b/go.sum @@ -43,6 +43,8 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= diff --git a/src/utils/other.go b/src/utils/other.go index 7a31738183f3b3f779adcf210317564e3b2c2aba..03b2cb12efa53d190d68ece7f614f46ff09caba0 100644 --- a/src/utils/other.go +++ b/src/utils/other.go @@ -2,13 +2,24 @@ package utils import ( "crypto/md5" + "encoding/base64" "fmt" - "gopkg.in/yaml.v2" + "gopkg.in/yaml.v3" "io/ioutil" "strconv" "strings" ) +func Base64Encode(s string) string { + return base64.StdEncoding.EncodeToString([]byte(s)) + +} + +func Base64Decode(s string) string { + decoded, _ := base64.StdEncoding.DecodeString(s) + return string(decoded) +} + func InStringArray(s string, arr []string) bool { for _, s1 := range arr { if s == s1 { diff --git a/src/v1/handler/db_version_mag_v2.go b/src/v1/handler/db_version_mag_v2.go index 54f550ad8a9fdd14d6dd7a2f90203db01a18ff7b..0d1493b2d292023a59116c500cf5f88fbec6706a 100644 --- a/src/v1/handler/db_version_mag_v2.go +++ b/src/v1/handler/db_version_mag_v2.go @@ -4,27 +4,50 @@ import ( "database/sql" "fmt" "gitee.com/scottq/go-framework/src/utils" + "io/ioutil" + "strings" ) +type DbVersionInfo struct { + Version string + SqlContent string + Comment string +} + +type IVersionMagV2 interface { + AppendVersionDir(sqlDir string) error + AppendVersion(v DbVersionInfo) + AppendSqlContent(version string, content string) + + Upgrade(version string) error + UpgradeOne(vInfo DbVersionInfo) error + Version() (string, error) + + SetLabel(label string) + Label() string + + ILogger +} + type VersionMagV2 struct { - sqlArr []string - db *sql.DB - label string + versions []DbVersionInfo + db *sql.DB + label string optLock IOptimisticLock _logger logFunc } -func NewDBVersionMagV2(sqlArr []string, db *sql.DB) (IVersionMag, error) { +func NewDBVersionMagV2(db *sql.DB) (IVersionMagV2, error) { lock, err := NewOptimisticLock(db, 60) if err != nil { return nil, err } mag := &VersionMagV2{ - db: db, - sqlArr: sqlArr, - optLock: lock, + db: db, + optLock: lock, + versions: []DbVersionInfo{}, } if err := mag.init(); err != nil { return nil, err @@ -48,10 +71,52 @@ func (this *VersionMagV2) AddLogger(logger logFunc) { this._logger = logger } +func (this *VersionMagV2) AppendVersion(v DbVersionInfo) { + this.versions = append(this.versions, v) +} + +func (this *VersionMagV2) AppendSqlContent(version string, content string) { + this.versions = append(this.versions, DbVersionInfo{ + Version: version, + SqlContent: content, + }) +} + +func (this *VersionMagV2) AppendVersionDir(sqlDir string) error { + + files, err := ioutil.ReadDir(sqlDir) + if err != nil { + return err + } + + // + for _, x := range files { + if x.IsDir() { + continue + } + sqlFile := x.Name() + sqlFileFull := sqlDir + "/" + sqlFile + + upVersion := strings.ReplaceAll(sqlFile, ".sql", "") + upVersion = strings.TrimLeft(upVersion, "v") + + //执行升级文件 + bytes, err := ioutil.ReadFile(sqlFileFull) + if err != nil { + return err + } + + execSql := string(bytes) + this.AppendSqlContent(upVersion, execSql) + } + + return nil +} + //升级版本 func (this *VersionMagV2) Upgrade(version string) error { if version == "" { - version, _ = this.Version() + version="v99.99.99" } this.logInfo("version upgrade to " + version) @@ -63,16 +128,16 @@ func (this *VersionMagV2) Upgrade(version string) error { defer this.optLock.UnLock(lockName) // - for index, x := range this.sqlArr { + for index, vInfo := range this.versions { upVersion := fmt.Sprintf("v%d", index+1) if utils.CompareVersion(upVersion, version) > 0 { continue } - err := this.UpgradeOne(upVersion, x) + + err := this.UpgradeOne(vInfo) if err != nil { - panic(err.Error()) return err } } @@ -109,21 +174,22 @@ func (this *VersionMagV2) logError(err error) { } //升级一次版本 -func (this *VersionMagV2) UpgradeOne(upVersion string, sql string) error { +func (this *VersionMagV2) UpgradeOne(vInfo DbVersionInfo) error { var err error version, err := this.Version() if err != nil { return err } + upVersion := vInfo.Version //无需处理 - if utils.CompareVersion(upVersion, version) <= 0 { + if utils.CompareVersion(vInfo.Version, version) <= 0 { this.logInfo("no need upgrade: v" + upVersion) return nil } - err = this.upgradeSql(upVersion, sql, "upgrade to "+upVersion) + err = this.upgrade(vInfo) if err != nil { return err } @@ -133,9 +199,14 @@ func (this *VersionMagV2) UpgradeOne(upVersion string, sql string) error { return nil } -func (this *VersionMagV2) upgradeSql(upVersion string, sqlContent string, comment string) error { +func (this *VersionMagV2) upgrade(vInfo DbVersionInfo) error { var err error + sqlContent := vInfo.SqlContent + + if sqlContent == "" { + return fmt.Errorf("exec upgrade [%s] content is empty", vInfo.Version) + } _, err = this.db.Exec(sqlContent) if err != nil { this.logError(err) @@ -143,7 +214,7 @@ func (this *VersionMagV2) upgradeSql(upVersion string, sqlContent string, commen } //保存version信息 - err = this.addVersionRecord(this.label, upVersion, comment) + err = this.addVersionRecord(this.label, vInfo.Version, vInfo.Comment) if err != nil { this.logError(err) return err diff --git a/src/v1/handler/db_version_mag_v2_test.go b/src/v1/handler/db_version_mag_v2_test.go new file mode 100644 index 0000000000000000000000000000000000000000..7e83f9aa8f4e204e95eaa6d4a5d5f8d9e4b42e7a --- /dev/null +++ b/src/v1/handler/db_version_mag_v2_test.go @@ -0,0 +1,54 @@ +package handler + +import ( + "database/sql" + "fmt" + "gitee.com/scottq/go-framework/src/utils" + "github.com/go-sql-driver/mysql" + "net" + "testing" +) + +func TestNewDBVersionMagV2(t *testing.T) { + db, err := newDb() + if err != nil { + t.Fatalf("new db err:%s", err) + return + } + magV2, err := NewDBVersionMagV2(db) + if err != nil { + t.Fatalf("new mag err:%s", err) + return + } + + magV2.AddLogger(func(info string, err error) { + fmt.Printf("%s,%s\n", info, err) + }) + + magV2.AppendSqlContent("1.0.1", utils.Base64Decode("CgoJQ1JFQVRFIFRBQkxFIElGIE5PVCBFWElTVFMgYGFkbWluYAoJKAoJCWBpZGAgICAgICAgICBiaWdpbnQgdW5zaWduZWQgICAgICAgICAgICAgICAgICAgICAgICAgTk9UIE5VTEwgQVVUT19JTkNSRU1FTlQsCgkJYGNyZWF0ZWRfYXRgIHRpbWVzdGFtcCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOT1QgTlVMTCBERUZBVUxUIENVUlJFTlRfVElNRVNUQU1QLAoJCWB1cGRhdGVkX2F0YCBkYXRldGltZSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgREVGQVVMVCBOVUxMLAoJCWBkZWxldGVkX2F0YCBkYXRldGltZSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgREVGQVVMVCBOVUxMLAoKCQlgbmlja25hbWVgICAgdmFyY2hhcig1NikgQ09MTEFURSB1dGY4bWI0X3VuaWNvZGVfY2kgIE5PVCBOVUxMIERFRkFVTFQgJycgQ09NTUVOVCAn5pi156ewJywKCWBhdmF0YXJgICAgICB2YXJjaGFyKDUxMikgQ09MTEFURSB1dGY4bWI0X3VuaWNvZGVfY2kgTk9UIE5VTEwgREVGQVVMVCAnJyBDT01NRU5UICflpLTlg4/lnLDlnYAnLAoJYGVtYWlsYCAgICAgIHZhcmNoYXIoNjQpIENPTExBVEUgdXRmOG1iNF91bmljb2RlX2NpICBOT1QgTlVMTCBERUZBVUxUICcnIENPTU1FTlQgJ2VtYWls5Zyw5Z2AJywKCWBhY2NvdW50YCAgICB2YXJjaGFyKDMyKSBDT0xMQVRFIHV0ZjhtYjRfdW5pY29kZV9jaSAgTk9UIE5VTEwgREVGQVVMVCAnJyBDT01NRU5UICfotKblj7cnLAoJYHBhc3N3b3JkYCAgIHZhcmNoYXIoNjQpIENPTExBVEUgdXRmOG1iNF91bmljb2RlX2NpICBOT1QgTlVMTCBERUZBVUxUICcnIENPTU1FTlQgJ+WvhueggScsCglgaXNfcm9vdGAgICAgdGlueWludCB1bnNpZ25lZCAgICAgICAgICAgICAgICAgICAgICAgIE5PVCBOVUxMIERFRkFVTFQgJzAnIENPTU1FTlQgJ+aYr+WQpuacgOmrmOadg+mZkCcsCglgc3RhdHVzYCAgICAgdGlueWludCB1bnNpZ25lZCAgICAgICAgICAgICAgICAgICAgICAgIE5PVCBOVUxMIERFRkFVTFQgJzAnIENPTU1FTlQgJ+eKtuaAge+8jDDvvJrml6DvvIwx77ya5q2j5bi4LDLvvJrooqvnpoHnlKgnLAoJUFJJTUFSWSBLRVkgKGBpZGApLAoJCUtFWSBgaWR4X25hbWVgIChgbmlja25hbWVgLCBgc3RhdHVzYCksCgkJS0VZIGBpZHhfYWNjb3VudGAgKGBhY2NvdW50YCwgYHN0YXR1c2ApLAoJCUtFWSBgaWR4X2RlbGV0ZWAgKGBkZWxldGVkX2F0YCwgYHN0YXR1c2ApCgkpIEVOR0lORSA9IElubm9EQgoJQVVUT19JTkNSRU1FTlQgPSAxCglERUZBVUxUIENIQVJTRVQgPSB1dGY4bWI0CglDT0xMQVRFID0gdXRmOG1iNF91bmljb2RlX2NpIENPTU1FTlQgPSflkI7lj7DnrqHnkIblkZjooagnOwoKCUlOU0VSVCBJTlRPIGBhZG1pbmAgKGBuaWNrbmFtZWAsIGBlbWFpbGAsIGBhY2NvdW50YCwgYHBhc3N3b3JkYCwgYGlzX3Jvb3RgLCBgc3RhdHVzYCkKCVZBTFVFUyAoJ+i2hee6p+euoeeQhuWRmCcsICdhZG1pbkBhZG1pbi5jb20nLCAnYWRtaW5pc3RyYXRvcicsICcxNGUxYjYwMGIxZmQ1NzlmNDc0MzNiODhlOGQ4NTI5MScsICcxJywgJzEnKTsKCglDUkVBVEUgVEFCTEUgSUYgTk9UIEVYSVNUUyBgYWRtaW5fbG9nYAoJKAoJCWBpZGAgICAgICAgICBiaWdpbnQgdW5zaWduZWQgICAgICAgICAgICAgICAgICAgICAgICAgTk9UIE5VTEwgQVVUT19JTkNSRU1FTlQsCgkJYGNyZWF0ZWRfYXRgIHRpbWVzdGFtcCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOT1QgTlVMTCBERUZBVUxUIENVUlJFTlRfVElNRVNUQU1QLAoJCWB1cGRhdGVkX2F0YCBkYXRldGltZSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgREVGQVVMVCBOVUxMLAoKCQlgYWRtaW5faWRgICAgYmlnaW50IHVuc2lnbmVkICAgICAgICAgICAgICAgICAgICAgICAgIE5PVCBOVUxMIERFRkFVTFQgJzAnIENPTU1FTlQgJ+WQjuWPsOeUqOaItycsCglgbmlja25hbWVgICAgdmFyY2hhcig1NikgQ09MTEFURSB1dGY4bWI0X3VuaWNvZGVfY2kgIE5PVCBOVUxMIERFRkFVTFQgJycgQ09NTUVOVCAn5pi156ewJywKCWBhY2NvdW50YCAgICB2YXJjaGFyKDU2KSBDT0xMQVRFIHV0ZjhtYjRfdW5pY29kZV9jaSAgTk9UIE5VTEwgREVGQVVMVCAnJyBDT01NRU5UICfotKblj7cnLAoJYGxvZ2AgICAgICAgIHZhcmNoYXIoMjU2KSBDT0xMQVRFIHV0ZjhtYjRfdW5pY29kZV9jaSBOT1QgTlVMTCBERUZBVUxUICcnIENPTU1FTlQgJ+aXpeW/l+WGheWuuScsCglgaXBgICAgICAgICAgdmFyY2hhcigxNSkgQ09MTEFURSB1dGY4bWI0X3VuaWNvZGVfY2kgIE5PVCBOVUxMIERFRkFVTFQgJycgQ09NTUVOVCAnaXDlnLDlnYAnLAoJUFJJTUFSWSBLRVkgKGBpZGApLAoJCUtFWSBgaWR4X2FkbWluYCAoYGFkbWluX2lkYCkKCSkgRU5HSU5FID0gSW5ub0RCCglBVVRPX0lOQ1JFTUVOVCA9IDEKCURFRkFVTFQgQ0hBUlNFVCA9IHV0ZjhtYjQKCUNPTExBVEUgPSB1dGY4bWI0X3VuaWNvZGVfY2kgQ09NTUVOVCA9J+euoeeQhuWRmOaXpeW/l+ihqCc7")) + + err = magV2.Upgrade("") + if err != nil { + t.Fatalf("upgrade err:%s", err) + } +} + +func newDb() (*sql.DB, error) { + dbConfig := mysql.NewConfig() + dbConfig.User = "root" + dbConfig.Passwd = "root" + dbConfig.Net = "tcp" + dbConfig.Addr = net.JoinHostPort("127.0.0.1", "3306") + dbConfig.DBName = "db_test" + dbConfig.MultiStatements = true + dbConfig.RejectReadOnly = false + extParam := make(map[string]string) + extParam["group_concat_max_len"] = "1000" + dbConfig.Params = extParam + + newDb, err := sql.Open("mysql", dbConfig.FormatDSN()) + if err != nil { + return nil, err + } + return newDb, nil +} diff --git a/src/v1/httpserver/vars.go b/src/v1/httpserver/vars.go index b4da7e514f009e5d901002eb117aababc5e34252..53262007665ca855f601283b06b0b6aab18b0cad 100644 --- a/src/v1/httpserver/vars.go +++ b/src/v1/httpserver/vars.go @@ -2,6 +2,7 @@ package httpserver import ( "context" + "fmt" "gitee.com/scottq/go-framework/src/utils" "golang.org/x/time/rate" "net/http" @@ -32,7 +33,9 @@ var GlobalMiddlewarePanic Middleware = func(ctx *Ctx, next func(*Ctx)) { defer func() { if err := recover(); err != nil { debug.PrintStack() + fmt.Printf("panic:%s\n",err) ctx.WriteHeader(http.StatusInternalServerError) + ctx.SetMsg(fmt.Sprintf("panic:%s",err)) ctx.Response() return }