diff --git a/cmd/mysql-kit/core/funcs.go b/cmd/mysql-kit/core/funcs.go new file mode 100644 index 0000000..9a8bc95 --- /dev/null +++ b/cmd/mysql-kit/core/funcs.go @@ -0,0 +1 @@ +package core diff --git a/cmd/mysql-kit/core/template.go b/cmd/mysql-kit/core/template.go new file mode 100644 index 0000000..ae8f5fc --- /dev/null +++ b/cmd/mysql-kit/core/template.go @@ -0,0 +1,35 @@ +package core + +import ( + "fmt" + "git.diulo.com/mogfee/kit/mysql/ddl" + "git.diulo.com/mogfee/kit/stringx" + "html/template" + "io" + "strings" +) + +func SaveFile(w io.Writer, templateContent string, table *ddl.Table) error { + tmp, err := template.New("").Funcs(map[string]any{ + "UpperType": func(str string) string { + return stringx.Ucfirst(ddl.GoName(str)) + }, + "LowerType": func(str string) string { + if str == "type" { + str = "vtype" + } + return stringx.Lcfirst(ddl.GoName(str)) + }, + "UpdateColumn": func(columns []*ddl.TableColumn) string { + arr := []string{} + for _, v := range columns { + arr = append(arr, fmt.Sprintf("%s=?", v.Name)) + } + return strings.Join(arr, " and ") + }, + }).Parse(templateContent) + if err != nil { + return err + } + return tmp.Execute(w, table) +} diff --git a/cmd/mysql-kit/core/vars.go b/cmd/mysql-kit/core/vars.go new file mode 100644 index 0000000..af04722 --- /dev/null +++ b/cmd/mysql-kit/core/vars.go @@ -0,0 +1,48 @@ +package core + +import ( + "fmt" + "os" + "strings" +) + +func VarsFile(savePath string) error { + saveFileName := fmt.Sprintf("%s/vars.go", strings.TrimRight(savePath, "/")) + return os.WriteFile(saveFileName, []byte(` +package model + +import ( + "git.diulo.com/mogfee/kit/errors" + "gorm.io/gorm" +) + +var ErrNotFound = errors.BadRequest("DB_NOT_FOUND", "数据不存在") +var ErrIdRequest = errors.BadRequest("DB_ID_REQUIRED", "id不能为空") + +func IsErrNotFound(err error) bool { + return errors.Reason(err) == "DB_NOT_FOUND" +} + +func IsErrIdRequest(err error) bool { + return errors.Reason(err) == "DB_ID_REQUIRED" +} +func findResultWithError[T any](data T, err error) (T, error) { + switch err { + case nil: + return data, nil + case gorm.ErrRecordNotFound: + return data, ErrNotFound + default: + return data, err + } +} +func GetPage(page int32, size int32) (offset int, pageSize int) { + if page <= 1 { + page = 1 + } + offset = int((page - 1) * size) + return offset, int(size) +} + +`), os.ModePerm) +} diff --git a/cmd/mysql-kit/custom.tpl b/cmd/mysql-kit/custom.tpl new file mode 100644 index 0000000..60a5c31 --- /dev/null +++ b/cmd/mysql-kit/custom.tpl @@ -0,0 +1,18 @@ +package model + +import "gorm.io/gorm" + +type ( + {{$.Name|UpperType}}Model interface { + {{$.Name|LowerType}}Model + } + custom{{$.Name|UpperType}}Model struct { + *default{{$.Name|UpperType}}Model + } +) + +func New{{$.Name|UpperType}}Model(db *gorm.DB) {{$.Name|UpperType}}Model { + return &custom{{$.Name|UpperType}}Model{ + default{{$.Name|UpperType}}Model: new{{$.Name|UpperType}}DAO(db), + } +} diff --git a/cmd/mysql-kit/main.go b/cmd/mysql-kit/main.go index 6be0485..7dc64ce 100644 --- a/cmd/mysql-kit/main.go +++ b/cmd/mysql-kit/main.go @@ -4,20 +4,23 @@ import ( _ "embed" "flag" "fmt" + "git.diulo.com/mogfee/kit/cmd/mysql-kit/core" "git.diulo.com/mogfee/kit/mysql/ddl" - "git.diulo.com/mogfee/kit/stringx" - "html/template" + "git.diulo.com/mogfee/kit/pathx" "os" "os/exec" "strings" ) //go:embed template.tpl -var modelTemplate string +var templateContent string + +//go:embed custom.tpl +var customContent string func main() { - sqlFile := flag.String("f", "", "数据库创建mysqldump文件") - savePath := flag.String("s", "./model/", "数据库存储路径") + sqlFile := flag.String("f", "/Users/mogfee/web/sendmail_server_new/test_gozero.sql", "数据库创建mysqldump文件") + savePath := flag.String("s", "./model", "数据库存储路径") flag.Parse() fmt.Println("sqlfile:", *sqlFile) fmt.Println("savePath:", *savePath) @@ -25,46 +28,58 @@ func main() { return } os.MkdirAll(*savePath, os.ModePerm) + + if err := core.VarsFile(*savePath); err != nil { + panic(err) + } err := ddl.Parser(*sqlFile, func(table *ddl.Table) error { + //table.Imports = append(table.Imports, "git.diulo.com/mogfee/kit/errors") + //modelFileName := fmt.Sprintf("%s/%s_model_gen.go", strings.TrimRight(*savePath, "/"), table.Name) + modelGenFile := fmt.Sprintf("%s/%smodel_gen.go", strings.TrimRight(*savePath, "/"), filename(table.Name)) + modelFile := fmt.Sprintf("%s/%smodel.go", strings.TrimRight(*savePath, "/"), filename(table.Name)) table.Imports = append(table.Imports, "gorm.io/gorm") table.Imports = append(table.Imports, "context") - //table.Imports = append(table.Imports, "git.diulo.com/mogfee/kit/errors") - saveFileName := fmt.Sprintf("%s/%s_gen.go", strings.TrimRight(*savePath, "/"), table.Name) - tmp, err := template.New("").Funcs(map[string]any{ - "UpperType": func(str string) string { - return stringx.Ucfirst(ddl.GoName(str)) - }, - "LowerType": func(str string) string { - if str == "type" { - str = "vtype" - } - return stringx.Lcfirst(ddl.GoName(str)) - }, - "UpdateColumn": func(columns []*ddl.TableColumn) string { - arr := []string{} - for _, v := range columns { - arr = append(arr, fmt.Sprintf("%s=?", v.Name)) - } - return strings.Join(arr, " and ") - }, - }).Parse(modelTemplate) - if err != nil { + + table.Indexes = filterIndex(table.Indexes) + + if err := saveFile(modelGenFile, templateContent, table, false); err != nil { return err } - //ddl.PrintJson(table) - //table.Primary.GoType - //fmt.Println(table.Primary) - f, err := os.Create(saveFileName) - if err != nil { + + if err := saveFile(modelFile, customContent, table, true); err != nil { return err } - defer f.Close() - return tmp.Execute(f, table) + return nil }) if err != nil { panic(err) } exec.Command("gofmt", "-l", "-w", *savePath+"/..").Run() +} +func filename(tableName string) string { + return strings.ToLower(strings.Join(strings.Split(tableName, "_"), "")) +} +func filterIndex(index []*ddl.TableIndex) []*ddl.TableIndex { + idxs := make([]*ddl.TableIndex, 0) + for _, v := range index { + if v.IsUnique { + idxs = append(idxs, v) + } + } + return idxs +} +func saveFile(fileName string, templateContent string, table *ddl.Table, isOnce bool) error { + if isOnce { + if pathx.Exists(fileName) { + return nil + } + } + f, err := os.Create(fileName) + if err != nil { + return err + } + defer f.Close() + return core.SaveFile(f, templateContent, table) } diff --git a/cmd/mysql-kit/template.tpl b/cmd/mysql-kit/template.tpl index 0c112df..460938b 100644 --- a/cmd/mysql-kit/template.tpl +++ b/cmd/mysql-kit/template.tpl @@ -5,14 +5,27 @@ import ( "{{$val}}"{{end}} ) {{end}} -type {{.Name|UpperType}} struct { +type ( + {{.Name|LowerType}}Model interface { + Insert(ctx context.Context, data *{{.Name|UpperType}}) error + {{ if .Primary}}Update(ctx context.Context, {{.Primary.Name|LowerType}} {{.Primary.GoType}}, updates map[string]any) error + Delete(ctx context.Context, {{.Primary.Name|LowerType}} {{.Primary.GoType}}) error + FindOne(ctx context.Context, {{.Primary.Name|LowerType}} {{.Primary.GoType}}) (*{{.Name|UpperType}}, error){{end}} + {{range $val:= .Indexes}}Find{{$val.Name|UpperType}}(ctx context.Context {{range $column:=$val.Columns}},{{$column.Name|LowerType}} {{$column.GoType}}{{end}}) (*{{$.Name|UpperType}}, error){{end}} + } + {{.Name|UpperType}} struct { updates map[string]any {{range $val := .Columns}}{{$val.Name|UpperType}} {{$val.GoType}} `db:"{{$val.Name}}"`{{if $val.Comment}}//{{$val.Comment}}{{end}} {{end}} -} + } + default{{.Name|UpperType}}Model struct { + db *gorm.DB + } +) func (s *{{.Name|UpperType}}) TableName() string { return "{{.Name}}" } + func New{{.Name|UpperType}}() *{{.Name|UpperType}} { return &{{.Name|UpperType}}{ updates: make(map[string]any), @@ -26,7 +39,6 @@ func (s *{{$.Name|UpperType}}) Set{{$val.Name|UpperType}}({{$val.Name|LowerType} s.set("{{$val.Name}}", {{$val.Name|LowerType}}) } {{end}} - func (s *{{.Name|UpperType}}) set(key string, val any) { s.updates[key] = val } @@ -37,41 +49,37 @@ func (s *{{.Name|UpperType}}) UpdateColumn() map[string]any { -type default{{.Name|UpperType}}DAO struct { - db *gorm.DB -} -func New{{.Name|UpperType}}DAO(db *gorm.DB) *default{{.Name|UpperType}}DAO { - return &default{{.Name|UpperType}}DAO{ +func new{{.Name|UpperType}}DAO(db *gorm.DB) *default{{.Name|UpperType}}Model { + return &default{{.Name|UpperType}}Model{ db: db, } } -func (s *default{{.Name|UpperType}}DAO) Insert(ctx context.Context, data *{{.Name|UpperType}}) error { +func (s *default{{.Name|UpperType}}Model) Insert(ctx context.Context, data *{{.Name|UpperType}}) error { return s.db.Create(data).Error } {{ if .Primary}} -func (s *default{{.Name|UpperType}}DAO) Update(ctx context.Context, {{.Primary.Name|LowerType}} {{.Primary.GoType}}, updates map[string]any) error { +func (s *default{{.Name|UpperType}}Model) Update(ctx context.Context, {{.Primary.Name|LowerType}} {{.Primary.GoType}}, updates map[string]any) error { return s.db.Model({{.Name|UpperType}}{}).Where("{{.Primary.Name}}=?", {{.Primary.Name|LowerType}}).Updates(updates).Error } -func (s *default{{.Name|UpperType}}DAO) Delete(ctx context.Context, {{.Primary.Name|LowerType}} {{.Primary.GoType}}) error { +func (s *default{{.Name|UpperType}}Model) Delete(ctx context.Context, {{.Primary.Name|LowerType}} {{.Primary.GoType}}) error { return s.db.Where("{{.Primary.Name}}=?", {{.Primary.Name|LowerType}}).Delete(&{{.Name|UpperType}}{}).Error } -func (s *default{{.Name|UpperType}}DAO) FindOne(ctx context.Context, {{.Primary.Name|LowerType}} {{.Primary.GoType}}) (*{{.Name|UpperType}}, error) { +func (s *default{{.Name|UpperType}}Model) FindOne(ctx context.Context, {{.Primary.Name|LowerType}} {{.Primary.GoType}}) (*{{.Name|UpperType}}, error) { row := {{.Name|UpperType}}{} err := s.db.Where("{{.Primary.Name}}=?", {{.Primary.Name|LowerType}}).Find(&row).Error - if err!=nil{ - return nil,err - } - return &row, nil + return findResultWithError(&row,err) } {{end}} {{range $val:= .Indexes}} -func (s *default{{$.Name|UpperType}}DAO) Find{{$val.Name|UpperType}}(ctx context.Context {{range $column:=$val.Columns}},{{$column.Name|LowerType}} {{$column.GoType}}{{end}}) error { - return s.db.Where("{{$val.Columns|UpdateColumn}}" {{range $column:=$val.Columns}},{{$column.Name|LowerType}}{{end}}).Delete(&{{$.Name|UpperType}}{}).Error +func (s *default{{$.Name|UpperType}}Model) Find{{$val.Name|UpperType}}(ctx context.Context {{range $column:=$val.Columns}},{{$column.Name|LowerType}} {{$column.GoType}}{{end}}) (*{{$.Name|UpperType}}, error) { + row := {{$.Name|UpperType}}{} + err := s.db.Where("{{$val.Columns|UpdateColumn}}" {{range $column:=$val.Columns}},{{$column.Name|LowerType}}{{end}}).Find(&row).Error + return findResultWithError(&row,err) } {{end}} diff --git a/errors/sqlerror.go b/errors/sqlerror.go deleted file mode 100644 index d064bed..0000000 --- a/errors/sqlerror.go +++ /dev/null @@ -1,7 +0,0 @@ -package errors - -import ( - "gorm.io/gorm" -) - -var ErrNotFound = gorm.ErrRecordNotFound diff --git a/log/filter_test.go b/log/filter_test.go index a61c5bc..f7d8bea 100644 --- a/log/filter_test.go +++ b/log/filter_test.go @@ -5,7 +5,7 @@ import ( ) func TestNewFilter(t *testing.T) { - logger := With(DefaultLogger)//"ts", DefaultTimestamp, "caller", DefaultCaller + logger := With(DefaultLogger) //"ts", DefaultTimestamp, "caller", DefaultCaller log := NewHelper(NewFilter(logger, FilterKey("username"), diff --git a/middleware/logging/logging.go b/middleware/logging/logging.go index 8a21eb5..3586d13 100644 --- a/middleware/logging/logging.go +++ b/middleware/logging/logging.go @@ -8,7 +8,7 @@ import ( func Server() middleware.Middleware { return func(handler middleware.Handler) middleware.Handler { return func(ctx context.Context, a any) (any, error) { - + return handler(ctx, a) } } diff --git a/mysql/builder/builder.go b/mysql/builder/builder.go new file mode 100644 index 0000000..37beeb4 --- /dev/null +++ b/mysql/builder/builder.go @@ -0,0 +1,47 @@ +package builder + +import ( + "fmt" + "git.diulo.com/mogfee/kit/stringx" + "reflect" + "strings" +) + +const dbTag = "db" + +func RawFieldNames(in any) []string { + out := make([]string, 0) + v := reflect.ValueOf(in) + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + if v.Kind() != reflect.Struct { + panic(fmt.Errorf("ToMap only accepts structs; got %T", v)) + } + typ := v.Type() + for i := 0; i < v.NumField(); i++ { + fi := typ.Field(i) + if !stringx.IsFirstUpper(fi.Name) { + continue + } + tagv := fi.Tag.Get(dbTag) + switch tagv { + case "-": + continue + case "": + out = append(out, fmt.Sprintf("`%s`", fi.Name)) + default: + if strings.Contains(tagv, ",") { + tagv = strings.TrimSpace(strings.Split(tagv, ",")[0]) + } + if tagv == "-" { + continue + } + if len(tagv) == 0 { + tagv = fi.Name + } + out = append(out, fmt.Sprintf("`%s`", tagv)) + } + } + return out +} diff --git a/mysql/builder/builder_test.go b/mysql/builder/builder_test.go new file mode 100644 index 0000000..530ad9f --- /dev/null +++ b/mysql/builder/builder_test.go @@ -0,0 +1,23 @@ +package builder + +import ( + "fmt" + "testing" + "time" +) + +type AdminUser struct { + updates map[string]any + Id int64 `db:"id"` + CreatedAt time.Time `db:"created_at"` + UpdatedAt time.Time `db:"updated_at"` + Status int32 `db:"status"` + Username string `db:"username"` + Password string `db:"password"` + RoleId int64 `db:"role_id"` + AliasId int32 `db:"alias_id"` +} + +func TestBuild(t *testing.T) { + fmt.Println(RawFieldNames(AdminUser{})) +} diff --git a/pathx/a.txt b/pathx/a.txt new file mode 100644 index 0000000..e69de29 diff --git a/pathx/file.go b/pathx/file.go new file mode 100644 index 0000000..9844a6d --- /dev/null +++ b/pathx/file.go @@ -0,0 +1,13 @@ +package pathx + +import ( + "os" +) + +func Exists(file string) bool { + _, err := os.Stat(file) + if os.IsNotExist(err) { + return false + } + return true +} diff --git a/pathx/file_test.go b/pathx/file_test.go new file mode 100644 index 0000000..14bd7c7 --- /dev/null +++ b/pathx/file_test.go @@ -0,0 +1,11 @@ +package pathx + +import ( + "fmt" + "testing" +) + +func TestExists(t *testing.T) { + fmt.Println(Exists("./a.txt")) + fmt.Println(Exists("./a1.txt")) +} diff --git a/stringx/stringx.go b/stringx/stringx.go index 7ec4d5f..814b1ad 100644 --- a/stringx/stringx.go +++ b/stringx/stringx.go @@ -1,6 +1,9 @@ package stringx -import "unicode" +import ( + "strings" + "unicode" +) func Ucfirst(str string) string { for i, v := range str { @@ -15,3 +18,12 @@ func Lcfirst(str string) string { } return "" } +func IsFirstUpper(name string) bool { + for a, b := range name { + if a == 0 && string(b) == strings.ToUpper(string(b)) { + return true + } + break + } + return false +} diff --git a/stringx/stringx_test.go b/stringx/stringx_test.go new file mode 100644 index 0000000..3a0f7bd --- /dev/null +++ b/stringx/stringx_test.go @@ -0,0 +1,11 @@ +package stringx + +import ( + "fmt" + "testing" +) + +func TestIsFirstUpper(t *testing.T) { + fmt.Println(IsFirstUpper("abc")) + fmt.Println(IsFirstUpper("Abc")) +} diff --git a/tools/model/cmd.go b/tools/model/cmd.go new file mode 100644 index 0000000..7324632 --- /dev/null +++ b/tools/model/cmd.go @@ -0,0 +1,22 @@ +package main + +import ( + "fmt" + "github.com/spf13/cobra" +) + +func main() { + Cmd := &cobra.Command{ + Use: "model", + } + mysqlCmd := &cobra.Command{ + Use: "mysql", + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Println("aaa") + return nil + }, + } + + Cmd.AddCommand(mysqlCmd) + Cmd.Execute() +} diff --git a/tools/model/sql/command/command.go b/tools/model/sql/command/command.go new file mode 100644 index 0000000..d47dcf0 --- /dev/null +++ b/tools/model/sql/command/command.go @@ -0,0 +1 @@ +package command diff --git a/tools/model/sql/gen/d/main.go b/tools/model/sql/gen/d/main.go new file mode 100644 index 0000000..0b78cb7 --- /dev/null +++ b/tools/model/sql/gen/d/main.go @@ -0,0 +1,11 @@ +package main + +import "git.diulo.com/mogfee/kit/tools/model/sql/gen" + +func main() { + filenmae := "/Users/mogfee/web/sendmail_server_new/sendmail.sch.sql" + err := gen.NewGenerator().StartFromDDL(filenmae, false, true, "sendmail") + if err != nil { + panic(err) + } +} diff --git a/tools/model/sql/gen/gen.go b/tools/model/sql/gen/gen.go new file mode 100644 index 0000000..accac0c --- /dev/null +++ b/tools/model/sql/gen/gen.go @@ -0,0 +1,52 @@ +package gen + +import "git.diulo.com/mogfee/kit/mysql/ddl" + +type ( + defaultGenerator struct { + dir string + pkg string + ignoreColumns []string + } + code struct { + importsCode string + varsCode string + typesCode string + newCode string + insertCode string + findCode string + updateCode string + deleteCode string + cacheExtra string + tableName string + } + codeTuple struct { + modelCode string + modelCustomCode string + } +) + +func NewGenerator() *defaultGenerator { + return &defaultGenerator{} +} + +func (g *defaultGenerator) StartFromDDL(filename string, withCache, strict bool, database string) error { + modelList, err := g.genFromDDL(filename, withCache, strict, database) + if err != nil { + return err + } + return g.createFile(modelList) +} + +func (g *defaultGenerator) genFromDDL(filename string, cache bool, strict bool, database string) ( + map[string]*codeTuple, error) { + m := make(map[string]*codeTuple) + err := ddl.Parser(filename, func(table *ddl.Table) error { + code, err := g.genModel(e, cache) + if err != nil { + return err + } + + }) + return m, err +} diff --git a/tools/model/sql/gen/parser/parser.go b/tools/model/sql/gen/parser/parser.go new file mode 100644 index 0000000..12a5def --- /dev/null +++ b/tools/model/sql/gen/parser/parser.go @@ -0,0 +1,71 @@ +package parser + +import ( + "git.diulo.com/mogfee/kit/mysql/ddl" +) + +const timeImport = "time.Time" + +type ( + Table struct { + Name string + Db string + PrimaryKey Primary + UniqueIndex map[string][]*Field + Fields []*Field + } + Primary struct { + Field + AutoIncrement bool + } + Field struct { + NameOriginal string + Name string + DateType string + Comment string + SeqInIndex int + OrdinalPosition int + ContainsPQ bool + } + KeyType int +) + +func parseNameOriginal(ts []*ddl.Table) (nameOriginals [][]string) { + var columns []string + for _, t := range ts { + columns = []string{} + for _, c := range t.Columns { + columns = append(columns, c.Name) + } + nameOriginals = append(nameOriginals, columns) + } + return nameOriginals +} + +func Parse(filename, database string, strict bool) ([]*Table, error) { + list := make([]*Table, 0) + err := ddl.Parser(filename, func(table *ddl.Table) error { + + list = append(list, &Table{ + Name: table.Name, + Db: "", + PrimaryKey: Primary{ + Field: Field{ + NameOriginal: "", + Name: "", + DateType: "", + Comment: "", + SeqInIndex: 0, + OrdinalPosition: 0, + ContainsPQ: false, + }, + AutoIncrement: false, + }, + UniqueIndex: nil, + Fields: nil, + }) + + return nil + }) + return list, err +}