diff --git a/cmd/README.md b/cmd/README.md new file mode 100644 index 0000000..ddf7c63 --- /dev/null +++ b/cmd/README.md @@ -0,0 +1,69 @@ +# GF-Source 自维护代码 + +这个目录包含了从 GoFrame (GF) 框架复制并修改的 DAO 生成工具源码,已将所有 `gdb` 相关引用替换为项目自定义的 `database` 包。 + +## 📁 目录结构 + +``` +gf-source/ +├── internal/ # 内部工具包 +│ ├── consts/ # 常量定义(从 GF internal/consts 复制) +│ └── utility/ # 工具函数 +│ ├── mlog/ # 日志工具 +│ └── utils/ # 通用工具 +├── templates/ # 代码生成模板 +│ ├── consts_gen_dao_template_dao.go +│ ├── consts_gen_dao_template_do.go +│ ├── consts_gen_dao_template_entity.go +│ └── consts_gen_dao_template_table.go +├── gendao*.go # DAO 生成核心逻辑 +├── go.mod # 子模块依赖 +└── go.sum +``` + +## 🔧 主要修改 + +### 1. 数据库接口替换 +- ✅ `github.com/gogf/gf/v2/database/gdb` → `git.magicany.cc/black1552/gin-base/database` +- ✅ `gdb.DB` → `database.DB` +- ✅ `gdb.ConfigNode` → `database.ConfigNode` +- ✅ `gdb.AddConfigNode()` → `database.AddConfigNode()` +- ✅ `gdb.Instance()` → `database.Instance()` +- ✅ `g.DB()` → `database.Database()` + +### 2. 内部包路径修改 +- ✅ `github.com/gogf/gf/cmd/gf/v2/internal/consts` → `git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/consts` +- ✅ `github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog` → `git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/mlog` +- ✅ `github.com/gogf/gf/cmd/gf/v2/internal/utility/utils` → `git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/utils` + +### 3. 包名统一 +- ✅ 所有 gendao 相关文件统一为 `package gendao` +- ✅ 模板文件移至 `templates/` 子目录(避免包名冲突) + +## 📦 编译 + +```bash +cd cmd/gf-source +go build . +``` + +## ⚠️ 注意事项 + +1. **这是参考代码**:这个目录主要用于参考 GF 的 DAO 生成逻辑,实际使用的是 `cmd/gin-dao-gen/main.go` +2. **不要直接导入**:这个子模块有独立的 go.mod,不应该被主项目直接导入 +3. **保持同步**:如果 GF 更新了 DAO 生成逻辑,需要手动同步这些文件并重新应用修改 + +## 🔄 更新流程 + +如果需要从 GF 更新代码: + +1. 从 `D:\web-object\gf\cmd\gf\internal\cmd\gendao` 复制最新文件 +2. 运行批量替换脚本将 `gdb` 改为 `database` +3. 运行批量替换脚本将 internal 路径改为本项目路径 +4. 测试编译确保没有错误 + +## 📝 相关文件 + +- 主程序:`cmd/gin-dao-gen/main.go` +- 数据库包:`database/` +- 配置文件:`config/config.toml` diff --git a/cmd/gf-source/gendao.go b/cmd/gf-source/gendao.go new file mode 100644 index 0000000..f79e062 --- /dev/null +++ b/cmd/gf-source/gendao.go @@ -0,0 +1,491 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gendao + +import ( + "context" + "fmt" + "sort" + "strings" + + "github.com/olekukonko/tablewriter" + "github.com/olekukonko/tablewriter/renderer" + "github.com/olekukonko/tablewriter/tw" + "golang.org/x/mod/modfile" + + "git.magicany.cc/black1552/gin-base/database" + "github.com/gogf/gf/v2/container/garray" + "github.com/gogf/gf/v2/container/gset" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gfile" + "github.com/gogf/gf/v2/os/gproc" + "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/os/gview" + "github.com/gogf/gf/v2/text/gregex" + "github.com/gogf/gf/v2/text/gstr" + + "git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/mlog" + "git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/utils" +) + +type ( + CGenDao struct{} + CGenDaoInput struct { + g.Meta `name:"dao" config:"{CGenDaoConfig}" usage:"{CGenDaoUsage}" brief:"{CGenDaoBrief}" eg:"{CGenDaoEg}" ad:"{CGenDaoAd}"` + Path string `name:"path" short:"p" brief:"{CGenDaoBriefPath}" d:"internal"` + Link string `name:"link" short:"l" brief:"{CGenDaoBriefLink}"` + Tables string `name:"tables" short:"t" brief:"{CGenDaoBriefTables}"` + TablesEx string `name:"tablesEx" short:"x" brief:"{CGenDaoBriefTablesEx}"` + ShardingPattern []string `name:"shardingPattern" short:"sp" brief:"{CGenDaoBriefShardingPattern}"` + Group string `name:"group" short:"g" brief:"{CGenDaoBriefGroup}" d:"default"` + Prefix string `name:"prefix" short:"f" brief:"{CGenDaoBriefPrefix}"` + RemovePrefix string `name:"removePrefix" short:"r" brief:"{CGenDaoBriefRemovePrefix}"` + RemoveFieldPrefix string `name:"removeFieldPrefix" short:"rf" brief:"{CGenDaoBriefRemoveFieldPrefix}"` + JsonCase string `name:"jsonCase" short:"j" brief:"{CGenDaoBriefJsonCase}" d:"CamelLower"` + ImportPrefix string `name:"importPrefix" short:"i" brief:"{CGenDaoBriefImportPrefix}"` + DaoPath string `name:"daoPath" short:"d" brief:"{CGenDaoBriefDaoPath}" d:"dao"` + TablePath string `name:"tablePath" short:"tp" brief:"{CGenDaoBriefTablePath}" d:"table"` + DoPath string `name:"doPath" short:"o" brief:"{CGenDaoBriefDoPath}" d:"model/do"` + EntityPath string `name:"entityPath" short:"e" brief:"{CGenDaoBriefEntityPath}" d:"model/entity"` + TplDaoTablePath string `name:"tplDaoTablePath" short:"t0" brief:"{CGenDaoBriefTplDaoTablePath}"` + TplDaoIndexPath string `name:"tplDaoIndexPath" short:"t1" brief:"{CGenDaoBriefTplDaoIndexPath}"` + TplDaoInternalPath string `name:"tplDaoInternalPath" short:"t2" brief:"{CGenDaoBriefTplDaoInternalPath}"` + TplDaoDoPath string `name:"tplDaoDoPath" short:"t3" brief:"{CGenDaoBriefTplDaoDoPathPath}"` + TplDaoEntityPath string `name:"tplDaoEntityPath" short:"t4" brief:"{CGenDaoBriefTplDaoEntityPath}"` + StdTime bool `name:"stdTime" short:"s" brief:"{CGenDaoBriefStdTime}" orphan:"true"` + WithTime bool `name:"withTime" short:"w" brief:"{CGenDaoBriefWithTime}" orphan:"true"` + GJsonSupport bool `name:"gJsonSupport" short:"n" brief:"{CGenDaoBriefGJsonSupport}" orphan:"true"` + OverwriteDao bool `name:"overwriteDao" short:"v" brief:"{CGenDaoBriefOverwriteDao}" orphan:"true"` + DescriptionTag bool `name:"descriptionTag" short:"c" brief:"{CGenDaoBriefDescriptionTag}" orphan:"true"` + NoJsonTag bool `name:"noJsonTag" short:"k" brief:"{CGenDaoBriefNoJsonTag}" orphan:"true"` + NoModelComment bool `name:"noModelComment" short:"m" brief:"{CGenDaoBriefNoModelComment}" orphan:"true"` + Clear bool `name:"clear" short:"a" brief:"{CGenDaoBriefClear}" orphan:"true"` + GenTable bool `name:"genTable" short:"gt" brief:"{CGenDaoBriefGenTable}" orphan:"true"` + + TypeMapping map[DBFieldTypeName]CustomAttributeType `name:"typeMapping" short:"y" brief:"{CGenDaoBriefTypeMapping}" orphan:"true"` + FieldMapping map[DBTableFieldName]CustomAttributeType `name:"fieldMapping" short:"fm" brief:"{CGenDaoBriefFieldMapping}" orphan:"true"` + + // internal usage purpose. + genItems *CGenDaoInternalGenItems + } + CGenDaoOutput struct{} + + CGenDaoInternalInput struct { + CGenDaoInput + DB database.DB + TableNames []string + NewTableNames []string + ShardingTableSet *gset.StrSet + } + DBTableFieldName = string + DBFieldTypeName = string + CustomAttributeType struct { + Type string `brief:"custom attribute type name"` + Import string `brief:"custom import for this type"` + } +) + +var ( + createdAt = gtime.Now() + tplView = gview.New() + defaultTypeMapping = map[DBFieldTypeName]CustomAttributeType{ + "decimal": { + Type: "float64", + }, + "money": { + Type: "float64", + }, + "numeric": { + Type: "float64", + }, + "smallmoney": { + Type: "float64", + }, + "uuid": { + Type: "uuid.UUID", + Import: "github.com/google/uuid", + }, + } + + // tablewriter Options + twRenderer = tablewriter.WithRenderer(renderer.NewBlueprint(tw.Rendition{ + Borders: tw.Border{Top: tw.Off, Bottom: tw.Off, Left: tw.Off, Right: tw.Off}, + Settings: tw.Settings{ + Separators: tw.Separators{BetweenRows: tw.Off, BetweenColumns: tw.Off}, + }, + Symbols: tw.NewSymbols(tw.StyleASCII), + })) + twConfig = tablewriter.WithConfig(tablewriter.Config{ + Row: tw.CellConfig{ + Formatting: tw.CellFormatting{AutoWrap: tw.WrapNone}, + }, + }) +) + +func (c CGenDao) Dao(ctx context.Context, in CGenDaoInput) (out *CGenDaoOutput, err error) { + in.genItems = newCGenDaoInternalGenItems() + if in.Link != "" { + doGenDaoForArray(ctx, -1, in) + } else if g.Cfg().Available(ctx) { + v := g.Cfg().MustGet(ctx, CGenDaoConfig) + if v.IsSlice() { + for i := 0; i < len(v.Interfaces()); i++ { + doGenDaoForArray(ctx, i, in) + } + } else { + doGenDaoForArray(ctx, -1, in) + } + } else { + doGenDaoForArray(ctx, -1, in) + } + doClear(in.genItems) + mlog.Print("done!") + return +} + +// doGenDaoForArray implements the "gen dao" command for configuration array. +func doGenDaoForArray(ctx context.Context, index int, in CGenDaoInput) { + var ( + err error + db database.DB + ) + if index >= 0 { + err = g.Cfg().MustGet( + ctx, + fmt.Sprintf(`%s.%d`, CGenDaoConfig, index), + ).Scan(&in) + if err != nil { + mlog.Fatalf(`invalid configuration of "%s": %+v`, CGenDaoConfig, err) + } + } + if dirRealPath := gfile.RealPath(in.Path); dirRealPath == "" { + mlog.Fatalf(`path "%s" does not exist`, in.Path) + } + removePrefixArray := gstr.SplitAndTrim(in.RemovePrefix, ",") + + // It uses user passed database configuration. + if in.Link != "" { + var tempGroup = gtime.TimestampNanoStr() + err = database.AddConfigNode(tempGroup, database.ConfigNode{ + Link: in.Link, + }) + if err != nil { + mlog.Fatalf(`database configuration failed: %+v`, err) + } + if db, err = database.Instance(tempGroup); err != nil { + mlog.Fatalf(`database initialization failed: %+v`, err) + } + } else { + db = database.Database(in.Group) + } + if db == nil { + mlog.Fatal(`database initialization failed, may be invalid database configuration`) + } + + var tableNames []string + if in.Tables != "" { + inputTables := gstr.SplitAndTrim(in.Tables, ",") + // Check if any table pattern contains wildcard characters. + // https://github.com/gogf/gf/issues/4629 + var hasPattern bool + for _, t := range inputTables { + if containsWildcard(t) { + hasPattern = true + break + } + } + if hasPattern { + // Fetch all tables first, then filter by patterns. + allTables, err := db.Tables(context.TODO()) + if err != nil { + mlog.Fatalf("fetching tables failed: %+v", err) + } + tableNames = filterTablesByPatterns(allTables, inputTables) + } else { + // Use exact table names as before. + tableNames = inputTables + } + } else { + tableNames, err = db.Tables(context.TODO()) + if err != nil { + mlog.Fatalf("fetching tables failed: %+v", err) + } + } + // Table excluding. + if in.TablesEx != "" { + array := garray.NewStrArrayFrom(tableNames) + for _, p := range gstr.SplitAndTrim(in.TablesEx, ",") { + if containsWildcard(p) { + // Use exact match with ^ and $ anchors for consistency with tables pattern. + regPattern := "^" + patternToRegex(p) + "$" + for _, v := range array.Clone().Slice() { + if gregex.IsMatchString(regPattern, v) { + array.RemoveValue(v) + } + } + } else { + array.RemoveValue(p) + } + } + tableNames = array.Slice() + } + + // merge default typeMapping to input typeMapping. + if in.TypeMapping == nil { + in.TypeMapping = defaultTypeMapping + } else { + for key, typeMapping := range defaultTypeMapping { + if _, ok := in.TypeMapping[key]; !ok { + in.TypeMapping[key] = typeMapping + } + } + } + + // Generating dao & model go files one by one according to given table name. + var ( + newTableNames = make([]string, len(tableNames)) + shardingNewTableSet = gset.NewStrSet() + ) + // Sort sharding patterns by length descending, so that longer (more specific) patterns + // are matched first. This prevents shorter patterns like "a_?" from incorrectly matching + // tables that should match longer patterns like "a_b_?" or "a_c_?". + // https://github.com/gogf/gf/issues/4603 + sortedShardingPatterns := make([]string, len(in.ShardingPattern)) + copy(sortedShardingPatterns, in.ShardingPattern) + sort.Slice(sortedShardingPatterns, func(i, j int) bool { + return len(sortedShardingPatterns[i]) > len(sortedShardingPatterns[j]) + }) + for i, tableName := range tableNames { + newTableName := tableName + for _, v := range removePrefixArray { + newTableName = gstr.TrimLeftStr(newTableName, v, 1) + } + if len(sortedShardingPatterns) > 0 { + for _, pattern := range sortedShardingPatterns { + var ( + match []string + regPattern = gstr.Replace(pattern, "?", `(.+)`) + ) + match, err = gregex.MatchString(regPattern, newTableName) + if err != nil { + mlog.Fatalf(`invalid sharding pattern "%s": %+v`, pattern, err) + } + if len(match) < 2 { + continue + } + newTableName = gstr.Replace(pattern, "?", "") + newTableName = gstr.Trim(newTableName, `_.-`) + if shardingNewTableSet.Contains(newTableName) { + tableNames[i] = "" + break + } + // Add prefix to sharding table name, if not, the isSharding check would not match. + shardingNewTableSet.Add(in.Prefix + newTableName) + break + } + } + newTableName = in.Prefix + newTableName + if tableNames[i] != "" { + // If shardingNewTableSet contains newTableName (tableName is empty), it should not be added to tableNames, make it empty and filter later. + newTableNames[i] = newTableName + } + } + tableNames = garray.NewStrArrayFrom(tableNames).FilterEmpty().Slice() + newTableNames = garray.NewStrArrayFrom(newTableNames).FilterEmpty().Slice() // Filter empty table names. make sure that newTableNames and tableNames have the same length. + in.genItems.Scale() + + // Dao: index and internal. + generateDao(ctx, CGenDaoInternalInput{ + CGenDaoInput: in, + DB: db, + TableNames: tableNames, + NewTableNames: newTableNames, + ShardingTableSet: shardingNewTableSet, + }) + // Table: table fields. + generateTable(ctx, CGenDaoInternalInput{ + CGenDaoInput: in, + DB: db, + TableNames: tableNames, + NewTableNames: newTableNames, + ShardingTableSet: shardingNewTableSet, + }) + // Do. + generateDo(ctx, CGenDaoInternalInput{ + CGenDaoInput: in, + DB: db, + TableNames: tableNames, + NewTableNames: newTableNames, + }) + // Entity. + generateEntity(ctx, CGenDaoInternalInput{ + CGenDaoInput: in, + DB: db, + TableNames: tableNames, + NewTableNames: newTableNames, + }) + + in.genItems.SetClear(in.Clear) +} + +func getImportPartContent(ctx context.Context, source string, isDo bool, appendImports []string) string { + var packageImportsArray = garray.NewStrArray() + if isDo { + packageImportsArray.Append(`"github.com/gogf/gf/v2/frame/g"`) + } + + // Time package recognition. + if strings.Contains(source, "gtime.Time") { + packageImportsArray.Append(`"github.com/gogf/gf/v2/os/gtime"`) + } else if strings.Contains(source, "time.Time") { + packageImportsArray.Append(`"time"`) + } + + // Json type. + if strings.Contains(source, "gjson.Json") { + packageImportsArray.Append(`"github.com/gogf/gf/v2/encoding/gjson"`) + } + + // Check and update imports in go.mod + if len(appendImports) > 0 { + goModPath := utils.GetModPath() + if goModPath == "" { + mlog.Fatal("go.mod not found in current project") + } + mod, err := modfile.Parse(goModPath, gfile.GetBytes(goModPath), nil) + if err != nil { + mlog.Fatalf("parse go.mod failed: %+v", err) + } + for _, appendImport := range appendImports { + found := false + for _, require := range mod.Require { + if gstr.Contains(appendImport, require.Mod.Path) { + found = true + break + } + } + if !found { + if err = gproc.ShellRun(ctx, `go get `+appendImport); err != nil { + mlog.Fatalf(`%+v`, err) + } + } + packageImportsArray.Append(fmt.Sprintf(`"%s"`, appendImport)) + } + } + + // Generate and write content to golang file. + packageImportsStr := "" + if packageImportsArray.Len() > 0 { + packageImportsStr = fmt.Sprintf("import(\n%s\n)", packageImportsArray.Join("\n")) + } + return packageImportsStr +} + +func assignDefaultVar(view *gview.View, in CGenDaoInternalInput) { + var ( + tplCreatedAtDatetimeStr string + tplDatetimeStr = createdAt.String() + ) + if in.WithTime { + tplCreatedAtDatetimeStr = fmt.Sprintf(`Created at %s`, tplDatetimeStr) + } + view.Assigns(g.Map{ + tplVarDatetimeStr: tplDatetimeStr, + tplVarCreatedAtDatetimeStr: tplCreatedAtDatetimeStr, + }) +} + +func sortFieldKeyForDao(fieldMap map[string]*database.TableField) []string { + names := make(map[int]string) + for _, field := range fieldMap { + names[field.Index] = field.Name + } + var ( + i = 0 + j = 0 + result = make([]string, len(names)) + ) + for { + if len(names) == 0 { + break + } + if val, ok := names[i]; ok { + result[j] = val + j++ + delete(names, i) + } + i++ + } + return result +} + +func getTemplateFromPathOrDefault(filePath string, def string) string { + if filePath != "" { + if contents := gfile.GetContents(filePath); contents != "" { + return contents + } + } + return def +} + +// containsWildcard checks if the pattern contains wildcard characters (* or ?). +func containsWildcard(pattern string) bool { + return gstr.Contains(pattern, "*") || gstr.Contains(pattern, "?") +} + +// patternToRegex converts a wildcard pattern to a regex pattern. +// Wildcard characters: * matches any characters, ? matches single character. +func patternToRegex(pattern string) string { + pattern = gstr.ReplaceByMap(pattern, map[string]string{ + "\r": "", + "\n": "", + }) + pattern = gstr.ReplaceByMap(pattern, map[string]string{ + "*": "\r", + "?": "\n", + }) + pattern = gregex.Quote(pattern) + pattern = gstr.ReplaceByMap(pattern, map[string]string{ + "\r": ".*", + "\n": ".", + }) + return pattern +} + +// filterTablesByPatterns filters tables by given patterns. +// Patterns support wildcard characters: * matches any characters, ? matches single character. +// https://github.com/gogf/gf/issues/4629 +func filterTablesByPatterns(allTables []string, patterns []string) []string { + var result []string + matched := make(map[string]bool) + allTablesSet := make(map[string]bool) + for _, t := range allTables { + allTablesSet[t] = true + } + for _, p := range patterns { + if containsWildcard(p) { + regPattern := "^" + patternToRegex(p) + "$" + for _, table := range allTables { + if !matched[table] && gregex.IsMatchString(regPattern, table) { + result = append(result, table) + matched[table] = true + } + } + } else { + // Exact table name, use direct string comparison. + if !allTablesSet[p] { + mlog.Printf(`table "%s" does not exist, skipped`, p) + continue + } + if !matched[p] { + result = append(result, p) + matched[p] = true + } + } + } + return result +} diff --git a/cmd/gf-source/gendao_clear.go b/cmd/gf-source/gendao_clear.go new file mode 100644 index 0000000..c3a2d7a --- /dev/null +++ b/cmd/gf-source/gendao_clear.go @@ -0,0 +1,48 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gendao + +import ( + "github.com/gogf/gf/v2/os/gfile" + "github.com/gogf/gf/v2/text/gstr" + + "git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/mlog" +) + +func doClear(items *CGenDaoInternalGenItems) { + var allGeneratedFilePaths = make([]string, 0) + for _, item := range items.Items { + allGeneratedFilePaths = append(allGeneratedFilePaths, item.GeneratedFilePaths...) + } + for i, v := range allGeneratedFilePaths { + allGeneratedFilePaths[i] = gfile.RealPath(v) + } + for _, item := range items.Items { + if !item.Clear { + continue + } + doClearItem(item, allGeneratedFilePaths) + } +} + +func doClearItem(item CGenDaoInternalGenItem, allGeneratedFilePaths []string) { + var generatedFilePaths = make([]string, 0) + for _, dirPath := range item.StorageDirPaths { + filePaths, err := gfile.ScanDirFile(dirPath, "*.go", true) + if err != nil { + mlog.Fatal(err) + } + generatedFilePaths = append(generatedFilePaths, filePaths...) + } + for _, filePath := range generatedFilePaths { + if !gstr.InArray(allGeneratedFilePaths, filePath) { + if err := gfile.RemoveFile(filePath); err != nil { + mlog.Print(err) + } + } + } +} diff --git a/cmd/gf-source/gendao_dao.go b/cmd/gf-source/gendao_dao.go new file mode 100644 index 0000000..af90508 --- /dev/null +++ b/cmd/gf-source/gendao_dao.go @@ -0,0 +1,260 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gendao + +import ( + "bytes" + "context" + "fmt" + "path/filepath" + "strings" + + "github.com/olekukonko/tablewriter" + + "git.magicany.cc/black1552/gin-base/database" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gfile" + "github.com/gogf/gf/v2/os/gview" + "github.com/gogf/gf/v2/text/gstr" + + "git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/consts" + "git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/mlog" + "git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/utils" +) + +func generateDao(ctx context.Context, in CGenDaoInternalInput) { + var ( + dirPathDao = gfile.Join(in.Path, in.DaoPath) + dirPathDaoInternal = gfile.Join(dirPathDao, "internal") + ) + in.genItems.AppendDirPath(dirPathDao) + for i := 0; i < len(in.TableNames); i++ { + var ( + realTableName = in.TableNames[i] + newTableName = in.NewTableNames[i] + ) + generateDaoSingle(ctx, generateDaoSingleInput{ + CGenDaoInternalInput: in, + TableName: realTableName, + NewTableName: newTableName, + DirPathDao: dirPathDao, + DirPathDaoInternal: dirPathDaoInternal, + IsSharding: in.ShardingTableSet.Contains(newTableName), + }) + } +} + +type generateDaoSingleInput struct { + CGenDaoInternalInput + // TableName specifies the table name of the table. + TableName string + // NewTableName specifies the prefix-stripped or custom edited name of the table. + NewTableName string + DirPathDao string + DirPathDaoInternal string + IsSharding bool +} + +// generateDaoSingle generates the dao and model content of given table. +func generateDaoSingle(ctx context.Context, in generateDaoSingleInput) { + // Generating table data preparing. + fieldMap, err := in.DB.TableFields(ctx, in.TableName) + if err != nil { + mlog.Fatalf(`fetching tables fields failed for table "%s": %+v`, in.TableName, err) + } + var ( + tableNameCamelCase = formatFieldName(in.NewTableName, FieldNameCaseCamel) + tableNameCamelLowerCase = formatFieldName(in.NewTableName, FieldNameCaseCamelLower) + tableNameSnakeCase = gstr.CaseSnake(in.NewTableName) + importPrefix = in.ImportPrefix + ) + if importPrefix == "" { + importPrefix = utils.GetImportPath(gfile.Join(in.Path, in.DaoPath)) + } else { + importPrefix = gstr.Join(g.SliceStr{importPrefix, in.DaoPath}, "/") + } + + fileName := gstr.Trim(tableNameSnakeCase, "-_.") + if len(fileName) > 5 && fileName[len(fileName)-5:] == "_test" { + // Add suffix to avoid the table name which contains "_test", + // which would make the go file a testing file. + fileName += "_table" + } + + // dao - index + generateDaoIndex(generateDaoIndexInput{ + generateDaoSingleInput: in, + TableNameCamelCase: tableNameCamelCase, + TableNameCamelLowerCase: tableNameCamelLowerCase, + ImportPrefix: importPrefix, + FileName: fileName, + }) + + // dao - internal + generateDaoInternal(generateDaoInternalInput{ + generateDaoSingleInput: in, + TableNameCamelCase: tableNameCamelCase, + TableNameCamelLowerCase: tableNameCamelLowerCase, + ImportPrefix: importPrefix, + FileName: fileName, + FieldMap: fieldMap, + }) +} + +type generateDaoIndexInput struct { + generateDaoSingleInput + TableNameCamelCase string + TableNameCamelLowerCase string + ImportPrefix string + FileName string +} + +func generateDaoIndex(in generateDaoIndexInput) { + path := filepath.FromSlash(gfile.Join(in.DirPathDao, in.FileName+".go")) + // It should add path to result slice whenever it would generate the path file or not. + in.genItems.AppendGeneratedFilePath(path) + if in.OverwriteDao || !gfile.Exists(path) { + var ( + ctx = context.Background() + tplContent = getTemplateFromPathOrDefault( + in.TplDaoIndexPath, consts.TemplateGenDaoIndexContent, + ) + ) + tplView.ClearAssigns() + tplView.Assigns(gview.Params{ + tplVarTableSharding: in.IsSharding, + tplVarTableShardingPrefix: in.NewTableName + "_", + tplVarImportPrefix: in.ImportPrefix, + tplVarTableName: in.TableName, + tplVarTableNameCamelCase: in.TableNameCamelCase, + tplVarTableNameCamelLowerCase: in.TableNameCamelLowerCase, + tplVarPackageName: filepath.Base(in.DaoPath), + }) + indexContent, err := tplView.ParseContent(ctx, tplContent) + if err != nil { + mlog.Fatalf("parsing template content failed: %v", err) + } + if err = gfile.PutContents(path, strings.TrimSpace(indexContent)); err != nil { + mlog.Fatalf("writing content to '%s' failed: %v", path, err) + } else { + utils.GoFmt(path) + mlog.Print("generated:", gfile.RealPath(path)) + } + } +} + +type generateDaoInternalInput struct { + generateDaoSingleInput + TableNameCamelCase string + TableNameCamelLowerCase string + ImportPrefix string + FileName string + FieldMap map[string]*database.TableField +} + +func generateDaoInternal(in generateDaoInternalInput) { + var ( + ctx = context.Background() + removeFieldPrefixArray = gstr.SplitAndTrim(in.RemoveFieldPrefix, ",") + tplContent = getTemplateFromPathOrDefault( + in.TplDaoInternalPath, consts.TemplateGenDaoInternalContent, + ) + ) + tplView.ClearAssigns() + tplView.Assigns(gview.Params{ + tplVarImportPrefix: in.ImportPrefix, + tplVarTableName: in.TableName, + tplVarGroupName: in.Group, + tplVarTableNameCamelCase: in.TableNameCamelCase, + tplVarTableNameCamelLowerCase: in.TableNameCamelLowerCase, + tplVarColumnDefine: gstr.Trim(generateColumnDefinitionForDao(in.FieldMap, removeFieldPrefixArray)), + tplVarColumnNames: gstr.Trim(generateColumnNamesForDao(in.FieldMap, removeFieldPrefixArray)), + }) + assignDefaultVar(tplView, in.CGenDaoInternalInput) + modelContent, err := tplView.ParseContent(ctx, tplContent) + if err != nil { + mlog.Fatalf("parsing template content failed: %v", err) + } + path := filepath.FromSlash(gfile.Join(in.DirPathDaoInternal, in.FileName+".go")) + in.genItems.AppendGeneratedFilePath(path) + if err := gfile.PutContents(path, strings.TrimSpace(modelContent)); err != nil { + mlog.Fatalf("writing content to '%s' failed: %v", path, err) + } else { + utils.GoFmt(path) + mlog.Print("generated:", gfile.RealPath(path)) + } +} + +// generateColumnNamesForDao generates and returns the column names assignment content of column struct +// for specified table. +func generateColumnNamesForDao(fieldMap map[string]*database.TableField, removeFieldPrefixArray []string) string { + var ( + buffer = bytes.NewBuffer(nil) + array = make([][]string, len(fieldMap)) + names = sortFieldKeyForDao(fieldMap) + ) + + for index, name := range names { + field := fieldMap[name] + + newFiledName := field.Name + for _, v := range removeFieldPrefixArray { + newFiledName = gstr.TrimLeftStr(newFiledName, v, 1) + } + + array[index] = []string{ + " #" + formatFieldName(newFiledName, FieldNameCaseCamel) + ":", + fmt.Sprintf(` #"%s",`, field.Name), + } + } + table := tablewriter.NewTable(buffer, twRenderer, twConfig) + table.Bulk(array) + table.Render() + namesContent := buffer.String() + // Let's do this hack of table writer for indent! + namesContent = gstr.Replace(namesContent, " #", "") + buffer.Reset() + buffer.WriteString(namesContent) + return buffer.String() +} + +// generateColumnDefinitionForDao generates and returns the column names definition for specified table. +func generateColumnDefinitionForDao(fieldMap map[string]*database.TableField, removeFieldPrefixArray []string) string { + var ( + buffer = bytes.NewBuffer(nil) + array = make([][]string, len(fieldMap)) + names = sortFieldKeyForDao(fieldMap) + ) + + for index, name := range names { + var ( + field = fieldMap[name] + comment = gstr.Trim(gstr.ReplaceByArray(field.Comment, g.SliceStr{ + "\n", " ", + "\r", " ", + })) + ) + newFiledName := field.Name + for _, v := range removeFieldPrefixArray { + newFiledName = gstr.TrimLeftStr(newFiledName, v, 1) + } + array[index] = []string{ + " #" + formatFieldName(newFiledName, FieldNameCaseCamel), + " # " + "string", + " #" + fmt.Sprintf(`// %s`, comment), + } + } + table := tablewriter.NewTable(buffer, twRenderer, twConfig) + table.Bulk(array) + table.Render() + defineContent := buffer.String() + // Let's do this hack of table writer for indent! + defineContent = gstr.Replace(defineContent, " #", "") + buffer.Reset() + buffer.WriteString(defineContent) + return buffer.String() +} diff --git a/cmd/gf-source/gendao_do.go b/cmd/gf-source/gendao_do.go new file mode 100644 index 0000000..6668391 --- /dev/null +++ b/cmd/gf-source/gendao_do.go @@ -0,0 +1,100 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gendao + +import ( + "context" + "fmt" + "path/filepath" + "strings" + + "github.com/gogf/gf/v2/os/gfile" + "github.com/gogf/gf/v2/os/gview" + "github.com/gogf/gf/v2/text/gregex" + "github.com/gogf/gf/v2/text/gstr" + + "git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/consts" + "git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/mlog" + "git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/utils" +) + +func generateDo(ctx context.Context, in CGenDaoInternalInput) { + var dirPathDo = filepath.FromSlash(gfile.Join(in.Path, in.DoPath)) + in.genItems.AppendDirPath(dirPathDo) + in.NoJsonTag = true + in.DescriptionTag = false + in.NoModelComment = false + // Model content. + for i, tableName := range in.TableNames { + fieldMap, err := in.DB.TableFields(ctx, tableName) + if err != nil { + mlog.Fatalf("fetching tables fields failed for table '%s':\n%v", tableName, err) + } + var ( + newTableName = in.NewTableNames[i] + doFilePath = gfile.Join(dirPathDo, gstr.CaseSnake(newTableName)+".go") + structDefinition, _ = generateStructDefinition(ctx, generateStructDefinitionInput{ + CGenDaoInternalInput: in, + TableName: tableName, + StructName: formatFieldName(newTableName, FieldNameCaseCamel), + FieldMap: fieldMap, + IsDo: true, + }) + ) + // replace all types to any. + structDefinition, _ = gregex.ReplaceStringFuncMatch( + "([A-Z]\\w*?)\\s+([\\w\\*\\.]+?)\\s+(//)", + structDefinition, + func(match []string) string { + // If the type is already a pointer/slice/map, it does nothing. + if !gstr.HasPrefix(match[2], "*") && !gstr.HasPrefix(match[2], "[]") && !gstr.HasPrefix(match[2], "map") { + return fmt.Sprintf(`%s any %s`, match[1], match[3]) + } + return match[0] + }, + ) + modelContent := generateDoContent( + ctx, + in, + tableName, + formatFieldName(newTableName, FieldNameCaseCamel), + structDefinition, + ) + in.genItems.AppendGeneratedFilePath(doFilePath) + err = gfile.PutContents(doFilePath, strings.TrimSpace(modelContent)) + if err != nil { + mlog.Fatalf(`writing content to "%s" failed: %v`, doFilePath, err) + } else { + utils.GoFmt(doFilePath) + mlog.Print("generated:", gfile.RealPath(doFilePath)) + } + } +} + +func generateDoContent( + ctx context.Context, in CGenDaoInternalInput, tableName, tableNameCamelCase, structDefine string, +) string { + var ( + tplContent = getTemplateFromPathOrDefault( + in.TplDaoDoPath, consts.TemplateGenDaoDoContent, + ) + ) + tplView.ClearAssigns() + tplView.Assigns(gview.Params{ + tplVarTableName: tableName, + tplVarPackageImports: getImportPartContent(ctx, structDefine, true, nil), + tplVarTableNameCamelCase: tableNameCamelCase, + tplVarStructDefine: structDefine, + tplVarPackageName: filepath.Base(in.DoPath), + }) + assignDefaultVar(tplView, in) + doContent, err := tplView.ParseContent(ctx, tplContent) + if err != nil { + mlog.Fatalf("parsing template content failed: %v", err) + } + return doContent +} diff --git a/cmd/gf-source/gendao_entity.go b/cmd/gf-source/gendao_entity.go new file mode 100644 index 0000000..4f8bece --- /dev/null +++ b/cmd/gf-source/gendao_entity.go @@ -0,0 +1,85 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gendao + +import ( + "context" + "path/filepath" + "strings" + + "github.com/gogf/gf/v2/os/gfile" + "github.com/gogf/gf/v2/os/gview" + "github.com/gogf/gf/v2/text/gstr" + + "git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/consts" + "git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/mlog" + "git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/utils" +) + +func generateEntity(ctx context.Context, in CGenDaoInternalInput) { + var dirPathEntity = gfile.Join(in.Path, in.EntityPath) + in.genItems.AppendDirPath(dirPathEntity) + // Model content. + for i, tableName := range in.TableNames { + fieldMap, err := in.DB.TableFields(ctx, tableName) + if err != nil { + mlog.Fatalf("fetching tables fields failed for table '%s':\n%v", tableName, err) + } + + var ( + newTableName = in.NewTableNames[i] + entityFilePath = filepath.FromSlash(gfile.Join(dirPathEntity, gstr.CaseSnake(newTableName)+".go")) + structDefinition, appendImports = generateStructDefinition(ctx, generateStructDefinitionInput{ + CGenDaoInternalInput: in, + TableName: tableName, + StructName: formatFieldName(newTableName, FieldNameCaseCamel), + FieldMap: fieldMap, + IsDo: false, + }) + entityContent = generateEntityContent( + ctx, + in, + newTableName, + formatFieldName(newTableName, FieldNameCaseCamel), + structDefinition, + appendImports, + ) + ) + in.genItems.AppendGeneratedFilePath(entityFilePath) + err = gfile.PutContents(entityFilePath, strings.TrimSpace(entityContent)) + if err != nil { + mlog.Fatalf("writing content to '%s' failed: %v", entityFilePath, err) + } else { + utils.GoFmt(entityFilePath) + mlog.Print("generated:", gfile.RealPath(entityFilePath)) + } + } +} + +func generateEntityContent( + ctx context.Context, in CGenDaoInternalInput, tableName, tableNameCamelCase, structDefine string, appendImports []string, +) string { + var ( + tplContent = getTemplateFromPathOrDefault( + in.TplDaoEntityPath, consts.TemplateGenDaoEntityContent, + ) + ) + tplView.ClearAssigns() + tplView.Assigns(gview.Params{ + tplVarTableName: tableName, + tplVarPackageImports: getImportPartContent(ctx, structDefine, false, appendImports), + tplVarTableNameCamelCase: tableNameCamelCase, + tplVarStructDefine: structDefine, + tplVarPackageName: filepath.Base(in.EntityPath), + }) + assignDefaultVar(tplView, in) + entityContent, err := tplView.ParseContent(ctx, tplContent) + if err != nil { + mlog.Fatalf("parsing template content failed: %v", err) + } + return entityContent +} diff --git a/cmd/gf-source/gendao_gen_item.go b/cmd/gf-source/gendao_gen_item.go new file mode 100644 index 0000000..fcb7bf9 --- /dev/null +++ b/cmd/gf-source/gendao_gen_item.go @@ -0,0 +1,53 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gendao + +type ( + CGenDaoInternalGenItems struct { + index int + Items []CGenDaoInternalGenItem + } + CGenDaoInternalGenItem struct { + Clear bool + StorageDirPaths []string + GeneratedFilePaths []string + } +) + +func newCGenDaoInternalGenItems() *CGenDaoInternalGenItems { + return &CGenDaoInternalGenItems{ + index: -1, + Items: make([]CGenDaoInternalGenItem, 0), + } +} + +func (i *CGenDaoInternalGenItems) Scale() { + i.Items = append(i.Items, CGenDaoInternalGenItem{ + StorageDirPaths: make([]string, 0), + GeneratedFilePaths: make([]string, 0), + Clear: false, + }) + i.index++ +} + +func (i *CGenDaoInternalGenItems) SetClear(clear bool) { + i.Items[i.index].Clear = clear +} + +func (i *CGenDaoInternalGenItems) AppendDirPath(storageDirPath string) { + i.Items[i.index].StorageDirPaths = append( + i.Items[i.index].StorageDirPaths, + storageDirPath, + ) +} + +func (i *CGenDaoInternalGenItems) AppendGeneratedFilePath(generatedFilePath string) { + i.Items[i.index].GeneratedFilePaths = append( + i.Items[i.index].GeneratedFilePaths, + generatedFilePath, + ) +} diff --git a/cmd/gf-source/gendao_structure.go b/cmd/gf-source/gendao_structure.go new file mode 100644 index 0000000..fbaa1a7 --- /dev/null +++ b/cmd/gf-source/gendao_structure.go @@ -0,0 +1,230 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gendao + +import ( + "bytes" + "context" + "fmt" + "strings" + + "github.com/olekukonko/tablewriter" + + "git.magicany.cc/black1552/gin-base/database" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/text/gregex" + "github.com/gogf/gf/v2/text/gstr" +) + +type generateStructDefinitionInput struct { + CGenDaoInternalInput + TableName string // Table name. + StructName string // Struct name. + FieldMap map[string]*database.TableField // Table field map. + IsDo bool // Is generating DTO struct. +} + +func generateStructDefinition(ctx context.Context, in generateStructDefinitionInput) (string, []string) { + var appendImports []string + buffer := bytes.NewBuffer(nil) + array := make([][]string, len(in.FieldMap)) + names := sortFieldKeyForDao(in.FieldMap) + for index, name := range names { + var imports string + field := in.FieldMap[name] + array[index], imports = generateStructFieldDefinition(ctx, field, in) + if imports != "" { + appendImports = append(appendImports, imports) + } + } + table := tablewriter.NewTable(buffer, twRenderer, twConfig) + table.Bulk(array) + table.Render() + stContent := buffer.String() + // Let's do this hack of table writer for indent! + stContent = gstr.Replace(stContent, " #", "") + stContent = gstr.Replace(stContent, "` ", "`") + stContent = gstr.Replace(stContent, "``", "") + buffer.Reset() + fmt.Fprintf(buffer, "type %s struct {\n", in.StructName) + if in.IsDo { + fmt.Fprintf(buffer, "g.Meta `orm:\"table:%s, do:true\"`\n", in.TableName) + } + buffer.WriteString(stContent) + buffer.WriteString("}") + return buffer.String(), appendImports +} + +func getTypeMappingInfo( + ctx context.Context, fieldType string, inTypeMapping map[DBFieldTypeName]CustomAttributeType, +) (typeNameStr, importStr string) { + if typeMapping, ok := inTypeMapping[strings.ToLower(fieldType)]; ok { + typeNameStr = typeMapping.Type + importStr = typeMapping.Import + return + } + tryTypeMatch, _ := gregex.MatchString(`(.+?)\(([^\(\)]+)\)([\s\)]*)`, fieldType) + var ( + tryTypeName string + moreTry bool + ) + if len(tryTypeMatch) == 4 { + tryTypeMatch3, _ := gregex.ReplaceString(`\s+`, "", tryTypeMatch[3]) + tryTypeName = gstr.Trim(tryTypeMatch[1]) + tryTypeMatch3 + moreTry = tryTypeMatch3 != "" + } else { + tryTypeName = gstr.Split(fieldType, " ")[0] + } + if tryTypeName != "" { + if typeMapping, ok := inTypeMapping[strings.ToLower(tryTypeName)]; ok { + typeNameStr = typeMapping.Type + importStr = typeMapping.Import + } else if moreTry { + typeNameStr, importStr = getTypeMappingInfo(ctx, tryTypeName, inTypeMapping) + } + } + return +} + +// generateStructFieldDefinition generates and returns the attribute definition for specified field. +func generateStructFieldDefinition( + ctx context.Context, field *database.TableField, in generateStructDefinitionInput, +) (attrLines []string, appendImport string) { + var ( + err error + localTypeName database.LocalType + localTypeNameStr string + ) + + if in.TypeMapping != nil && len(in.TypeMapping) > 0 { + localTypeNameStr, appendImport = getTypeMappingInfo(ctx, field.Type, in.TypeMapping) + } + + if localTypeNameStr == "" { + localTypeName, err = in.DB.CheckLocalTypeForField(ctx, field.Type, nil) + if err != nil { + panic(err) + } + localTypeNameStr = string(localTypeName) + switch localTypeName { + case database.LocalTypeDate, database.LocalTypeTime, database.LocalTypeDatetime: + if in.StdTime { + localTypeNameStr = "time.Time" + } else { + localTypeNameStr = "*gtime.Time" + } + + case database.LocalTypeInt64Bytes: + localTypeNameStr = "int64" + + case database.LocalTypeUint64Bytes: + localTypeNameStr = "uint64" + + // Special type handle. + case database.LocalTypeJson, database.LocalTypeJsonb: + if in.GJsonSupport { + localTypeNameStr = "*gjson.Json" + } else { + localTypeNameStr = "string" + } + } + } + + var ( + tagKey = "`" + descriptionTag = gstr.Replace(formatComment(field.Comment), `"`, `\"`) + ) + removeFieldPrefixArray := gstr.SplitAndTrim(in.RemoveFieldPrefix, ",") + newFiledName := field.Name + for _, v := range removeFieldPrefixArray { + newFiledName = gstr.TrimLeftStr(newFiledName, v, 1) + } + + if in.FieldMapping != nil && len(in.FieldMapping) > 0 { + if typeMapping, ok := in.FieldMapping[fmt.Sprintf("%s.%s", in.TableName, newFiledName)]; ok { + localTypeNameStr = typeMapping.Type + appendImport = typeMapping.Import + } + } + + attrLines = []string{ + " #" + formatFieldName(newFiledName, FieldNameCaseCamel), + " #" + localTypeNameStr, + } + + jsonTag := gstr.CaseConvert(newFiledName, gstr.CaseTypeMatch(in.JsonCase)) + attrLines = append(attrLines, fmt.Sprintf(` #%sjson:"%s"`, tagKey, jsonTag)) + // orm tag + if !in.IsDo { + // entity + attrLines = append(attrLines, fmt.Sprintf(` #orm:"%s"`, field.Name)) + } + attrLines = append(attrLines, fmt.Sprintf(` #description:"%s"%s`, descriptionTag, tagKey)) + attrLines = append(attrLines, fmt.Sprintf(` #// %s`, formatComment(field.Comment))) + + for k, v := range attrLines { + if in.NoJsonTag { + v, _ = gregex.ReplaceString(`json:".+"`, ``, v) + } + if !in.DescriptionTag { + v, _ = gregex.ReplaceString(`description:".*"`, ``, v) + } + if in.NoModelComment { + v, _ = gregex.ReplaceString(`//.+`, ``, v) + } + attrLines[k] = v + } + return attrLines, appendImport +} + +type FieldNameCase string + +const ( + FieldNameCaseCamel FieldNameCase = "CaseCamel" + FieldNameCaseCamelLower FieldNameCase = "CaseCamelLower" +) + +// formatFieldName formats and returns a new field name that is used for golang codes generating. +func formatFieldName(fieldName string, nameCase FieldNameCase) string { + // For normal databases like mysql, pgsql, sqlite, + // field/table names of that are in normal case. + var newFieldName = fieldName + if isAllUpper(fieldName) { + // For special databases like dm, oracle, + // field/table names of that are in upper case. + newFieldName = strings.ToLower(fieldName) + } + switch nameCase { + case FieldNameCaseCamel: + return gstr.CaseCamel(newFieldName) + case FieldNameCaseCamelLower: + return gstr.CaseCamelLower(newFieldName) + default: + return "" + } +} + +// isAllUpper checks and returns whether given `fieldName` all letters are upper case. +func isAllUpper(fieldName string) bool { + for _, b := range fieldName { + if b >= 'a' && b <= 'z' { + return false + } + } + return true +} + +// formatComment formats the comment string to fit the golang code without any lines. +func formatComment(comment string) string { + comment = gstr.ReplaceByArray(comment, g.SliceStr{ + "\n", " ", + "\r", " ", + }) + comment = gstr.Replace(comment, `\n`, " ") + comment = gstr.Trim(comment) + return comment +} diff --git a/cmd/gf-source/gendao_table.go b/cmd/gf-source/gendao_table.go new file mode 100644 index 0000000..b0a34cd --- /dev/null +++ b/cmd/gf-source/gendao_table.go @@ -0,0 +1,147 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gendao + +import ( + "bytes" + "context" + "path/filepath" + "sort" + "strconv" + "strings" + + "git.magicany.cc/black1552/gin-base/database" + "github.com/gogf/gf/v2/os/gfile" + "github.com/gogf/gf/v2/os/gview" + "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/gconv" + + "git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/consts" + "git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/mlog" + "git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/utils" +) + +// generateTable generates dao files for given tables. +func generateTable(ctx context.Context, in CGenDaoInternalInput) { + dirPathTable := gfile.Join(in.Path, in.TablePath) + if !in.GenTable { + if gfile.Exists(dirPathTable) { + in.genItems.AppendDirPath(dirPathTable) + } + return + } + in.genItems.AppendDirPath(dirPathTable) + for i := 0; i < len(in.TableNames); i++ { + var ( + realTableName = in.TableNames[i] + newTableName = in.NewTableNames[i] + ) + generateTableSingle(ctx, generateTableSingleInput{ + CGenDaoInternalInput: in, + TableName: realTableName, + NewTableName: newTableName, + DirPathTable: dirPathTable, + }) + } +} + +// generateTableSingleInput is the input parameter for generateTableSingle. +type generateTableSingleInput struct { + CGenDaoInternalInput + // TableName specifies the table name of the table. + TableName string + // NewTableName specifies the prefix-stripped or custom edited name of the table. + NewTableName string + DirPathTable string +} + +// generateTableSingle generates dao files for a single table. +func generateTableSingle(ctx context.Context, in generateTableSingleInput) { + // Generating table data preparing. + fieldMap, err := in.DB.TableFields(ctx, in.TableName) + if err != nil { + mlog.Fatalf(`fetching tables fields failed for table "%s": %+v`, in.TableName, err) + } + + tableNameSnakeCase := gstr.CaseSnake(in.NewTableName) + fileName := gstr.Trim(tableNameSnakeCase, "-_.") + if len(fileName) > 5 && fileName[len(fileName)-5:] == "_test" { + // Add suffix to avoid the table name which contains "_test", + // which would make the go file a testing file. + fileName += "_table" + } + path := filepath.FromSlash(gfile.Join(in.DirPathTable, fileName+".go")) + in.genItems.AppendGeneratedFilePath(path) + if in.OverwriteDao || !gfile.Exists(path) { + var ( + ctx = context.Background() + tplContent = getTemplateFromPathOrDefault( + in.TplDaoTablePath, consts.TemplateGenTableContent, + ) + ) + tplView.ClearAssigns() + tplView.Assigns(gview.Params{ + tplVarGroupName: in.Group, + tplVarTableName: in.TableName, + tplVarTableNameCamelCase: formatFieldName(in.NewTableName, FieldNameCaseCamel), + tplVarPackageName: filepath.Base(in.TablePath), + tplVarTableFields: generateTableFields(fieldMap), + }) + indexContent, err := tplView.ParseContent(ctx, tplContent) + if err != nil { + mlog.Fatalf("parsing template content failed: %v", err) + } + if err = gfile.PutContents(path, strings.TrimSpace(indexContent)); err != nil { + mlog.Fatalf("writing content to '%s' failed: %v", path, err) + } else { + utils.GoFmt(path) + mlog.Print("generated:", gfile.RealPath(path)) + } + } +} + +// generateTableFields generates and returns the field definition content for specified table. +func generateTableFields(fields map[string]*database.TableField) string { + var buf bytes.Buffer + fieldNames := make([]string, 0, len(fields)) + for fieldName := range fields { + fieldNames = append(fieldNames, fieldName) + } + sort.Slice(fieldNames, func(i, j int) bool { + return fields[fieldNames[i]].Index < fields[fieldNames[j]].Index // asc + }) + for index, fieldName := range fieldNames { + field := fields[fieldName] + buf.WriteString(" " + strconv.Quote(field.Name) + ": {\n") + buf.WriteString(" Index: " + gconv.String(field.Index) + ",\n") + buf.WriteString(" Name: " + strconv.Quote(field.Name) + ",\n") + buf.WriteString(" Type: " + strconv.Quote(field.Type) + ",\n") + buf.WriteString(" Null: " + gconv.String(field.Null) + ",\n") + buf.WriteString(" Key: " + strconv.Quote(field.Key) + ",\n") + buf.WriteString(" Default: " + generateDefaultValue(field.Default) + ",\n") + buf.WriteString(" Extra: " + strconv.Quote(field.Extra) + ",\n") + buf.WriteString(" Comment: " + strconv.Quote(field.Comment) + ",\n") + buf.WriteString(" },") + if index != len(fieldNames)-1 { + buf.WriteString("\n") + } + } + return buf.String() +} + +// generateDefaultValue generates and returns the default value definition for specified field. +func generateDefaultValue(value interface{}) string { + if value == nil { + return "nil" + } + switch v := value.(type) { + case string: + return strconv.Quote(v) + default: + return gconv.String(v) + } +} diff --git a/cmd/gf-source/gendao_tag.go b/cmd/gf-source/gendao_tag.go new file mode 100644 index 0000000..25bc842 --- /dev/null +++ b/cmd/gf-source/gendao_tag.go @@ -0,0 +1,155 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gendao + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/util/gtag" +) + +const ( + CGenDaoConfig = `gfcli.gen.dao` + CGenDaoUsage = `gf gen dao [OPTION]` + CGenDaoBrief = `automatically generate go files for dao/do/entity` + CGenDaoEg = ` +gf gen dao +gf gen dao -l "mysql:root:12345678@tcp(127.0.0.1:3306)/test" +gf gen dao -p ./model -g user-center -t user,user_detail,user_login +gf gen dao -r user_ +` + + CGenDaoAd = ` +CONFIGURATION SUPPORT + Options are also supported by configuration file. + It's suggested using configuration file instead of command line arguments making producing. + The configuration node name is "gfcli.gen.dao", which also supports multiple databases, for example(config.yaml): + gfcli: + gen: + dao: + - link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test" + tables: "order,products" + jsonCase: "CamelLower" + - link: "mysql:root:12345678@tcp(127.0.0.1:3306)/primary" + path: "./my-app" + prefix: "primary_" + tables: "user, userDetail" + typeMapping: + decimal: + type: decimal.Decimal + import: github.com/shopspring/decimal + numeric: + type: string + fieldMapping: + table_name.field_name: + type: decimal.Decimal + import: github.com/shopspring/decimal +` + CGenDaoBriefPath = `directory path for generated files` + CGenDaoBriefLink = `database configuration, the same as the ORM configuration of GoFrame` + CGenDaoBriefTables = `generate models only for given tables, multiple table names separated with ','` + CGenDaoBriefTablesEx = `generate models excluding given tables, multiple table names separated with ','` + CGenDaoBriefPrefix = `add prefix for all table of specified link/database tables` + CGenDaoBriefRemovePrefix = `remove specified prefix of the table, multiple prefix separated with ','` + CGenDaoBriefRemoveFieldPrefix = `remove specified prefix of the field, multiple prefix separated with ','` + CGenDaoBriefStdTime = `use time.Time from stdlib instead of gtime.Time for generated time/date fields of tables` + CGenDaoBriefWithTime = `add created time for auto produced go files` + CGenDaoBriefGJsonSupport = `use gJsonSupport to use *gjson.Json instead of string for generated json fields of tables` + CGenDaoBriefImportPrefix = `custom import prefix for generated go files` + CGenDaoBriefDaoPath = `directory path for storing generated dao files under path` + CGenDaoBriefTablePath = `directory path for storing generated table files under path` + CGenDaoBriefDoPath = `directory path for storing generated do files under path` + CGenDaoBriefEntityPath = `directory path for storing generated entity files under path` + CGenDaoBriefOverwriteDao = `overwrite all dao files both inside/outside internal folder` + CGenDaoBriefModelFile = `custom file name for storing generated model content` + CGenDaoBriefModelFileForDao = `custom file name generating model for DAO operations like Where/Data. It's empty in default` + CGenDaoBriefDescriptionTag = `add comment to description tag for each field` + CGenDaoBriefNoJsonTag = `no json tag will be added for each field` + CGenDaoBriefNoModelComment = `no model comment will be added for each field` + CGenDaoBriefClear = `delete all generated go files that do not exist in database` + CGenDaoBriefGenTable = `generate table files` + CGenDaoBriefTypeMapping = `custom local type mapping for generated struct attributes relevant to fields of table` + CGenDaoBriefFieldMapping = `custom local type mapping for generated struct attributes relevant to specific fields of table` + CGenDaoBriefShardingPattern = `sharding pattern for table name, e.g. "users_?" will be replace tables "users_001,users_002,..." to "users" dao` + CGenDaoBriefGroup = ` +specifying the configuration group name of database for generated ORM instance, +it's not necessary and the default value is "default" +` + CGenDaoBriefJsonCase = ` +generated json tag case for model struct, cases are as follows: +| Case | Example | +|---------------- |--------------------| +| Camel | AnyKindOfString | +| CamelLower | anyKindOfString | default +| Snake | any_kind_of_string | +| SnakeScreaming | ANY_KIND_OF_STRING | +| SnakeFirstUpper | rgb_code_md5 | +| Kebab | any-kind-of-string | +| KebabScreaming | ANY-KIND-OF-STRING | +` + CGenDaoBriefTplDaoIndexPath = `template file path for dao index file` + CGenDaoBriefTplDaoInternalPath = `template file path for dao internal file` + CGenDaoBriefTplDaoDoPathPath = `template file path for dao do file` + CGenDaoBriefTplDaoEntityPath = `template file path for dao entity file` + + tplVarTableName = `TplTableName` + tplVarTableNameCamelCase = `TplTableNameCamelCase` + tplVarTableNameCamelLowerCase = `TplTableNameCamelLowerCase` + tplVarTableSharding = `TplTableSharding` + tplVarTableShardingPrefix = `TplTableShardingPrefix` + tplVarTableFields = `TplTableFields` + tplVarPackageImports = `TplPackageImports` + tplVarImportPrefix = `TplImportPrefix` + tplVarStructDefine = `TplStructDefine` + tplVarColumnDefine = `TplColumnDefine` + tplVarColumnNames = `TplColumnNames` + tplVarGroupName = `TplGroupName` + tplVarDatetimeStr = `TplDatetimeStr` + tplVarCreatedAtDatetimeStr = `TplCreatedAtDatetimeStr` + tplVarPackageName = `TplPackageName` +) + +func init() { + gtag.Sets(g.MapStrStr{ + `CGenDaoConfig`: CGenDaoConfig, + `CGenDaoUsage`: CGenDaoUsage, + `CGenDaoBrief`: CGenDaoBrief, + `CGenDaoEg`: CGenDaoEg, + `CGenDaoAd`: CGenDaoAd, + `CGenDaoBriefPath`: CGenDaoBriefPath, + `CGenDaoBriefLink`: CGenDaoBriefLink, + `CGenDaoBriefTables`: CGenDaoBriefTables, + `CGenDaoBriefTablesEx`: CGenDaoBriefTablesEx, + `CGenDaoBriefPrefix`: CGenDaoBriefPrefix, + `CGenDaoBriefRemovePrefix`: CGenDaoBriefRemovePrefix, + `CGenDaoBriefRemoveFieldPrefix`: CGenDaoBriefRemoveFieldPrefix, + `CGenDaoBriefStdTime`: CGenDaoBriefStdTime, + `CGenDaoBriefWithTime`: CGenDaoBriefWithTime, + `CGenDaoBriefDaoPath`: CGenDaoBriefDaoPath, + `CGenDaoBriefTablePath`: CGenDaoBriefTablePath, + `CGenDaoBriefDoPath`: CGenDaoBriefDoPath, + `CGenDaoBriefEntityPath`: CGenDaoBriefEntityPath, + `CGenDaoBriefGJsonSupport`: CGenDaoBriefGJsonSupport, + `CGenDaoBriefImportPrefix`: CGenDaoBriefImportPrefix, + `CGenDaoBriefOverwriteDao`: CGenDaoBriefOverwriteDao, + `CGenDaoBriefModelFile`: CGenDaoBriefModelFile, + `CGenDaoBriefModelFileForDao`: CGenDaoBriefModelFileForDao, + `CGenDaoBriefDescriptionTag`: CGenDaoBriefDescriptionTag, + `CGenDaoBriefNoJsonTag`: CGenDaoBriefNoJsonTag, + `CGenDaoBriefNoModelComment`: CGenDaoBriefNoModelComment, + `CGenDaoBriefClear`: CGenDaoBriefClear, + `CGenDaoBriefGenTable`: CGenDaoBriefGenTable, + `CGenDaoBriefTypeMapping`: CGenDaoBriefTypeMapping, + `CGenDaoBriefFieldMapping`: CGenDaoBriefFieldMapping, + `CGenDaoBriefShardingPattern`: CGenDaoBriefShardingPattern, + `CGenDaoBriefGroup`: CGenDaoBriefGroup, + `CGenDaoBriefJsonCase`: CGenDaoBriefJsonCase, + `CGenDaoBriefTplDaoIndexPath`: CGenDaoBriefTplDaoIndexPath, + `CGenDaoBriefTplDaoInternalPath`: CGenDaoBriefTplDaoInternalPath, + `CGenDaoBriefTplDaoDoPathPath`: CGenDaoBriefTplDaoDoPathPath, + `CGenDaoBriefTplDaoEntityPath`: CGenDaoBriefTplDaoEntityPath, + }) +} diff --git a/cmd/gf-source/internal/consts/consts.go b/cmd/gf-source/internal/consts/consts.go new file mode 100644 index 0000000..cece25d --- /dev/null +++ b/cmd/gf-source/internal/consts/consts.go @@ -0,0 +1,13 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package consts + +const ( + // DoNotEditKey is used in generated files, + // which marks the files will be overwritten by CLI tool. + DoNotEditKey = `DO NOT EDIT` +) diff --git a/cmd/gf-source/internal/consts/consts_gen_ctrl_template.go b/cmd/gf-source/internal/consts/consts_gen_ctrl_template.go new file mode 100644 index 0000000..ac6ee7d --- /dev/null +++ b/cmd/gf-source/internal/consts/consts_gen_ctrl_template.go @@ -0,0 +1,87 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package consts + +const TemplateGenCtrlControllerEmpty = ` +// ================================================================================= +// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish. +// ================================================================================= + +package {Module} +` + +const TemplateGenCtrlControllerNewEmpty = ` +// ================================================================================= +// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish. +// ================================================================================= + +package {Module} + +import ( + {ImportPath} +) +` + +const TemplateGenCtrlControllerNewFunc = ` +type {CtrlName} struct{} + +func {NewFuncName}() {InterfaceName} { + return &{CtrlName}{} +} +` + +const TemplateGenCtrlControllerMethodFunc = ` +package {Module} + +import ( + "context" + + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" + + "{ImportPath}" +) +{MethodComment} +func (c *{CtrlName}) {MethodName}(ctx context.Context, req *{Version}.{MethodName}Req) (res *{Version}.{MethodName}Res, err error) { + return nil, gerror.NewCode(gcode.CodeNotImplemented) +} +` + +const TemplateGenCtrlControllerHeader = ` +package {Module} + +import ( + "context" + + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" + + "{ImportPath}" +) + +` + +const TemplateGenCtrlControllerMethodFuncMerge = ` +{MethodComment} +func (c *{CtrlName}) {MethodName}(ctx context.Context, req *{Version}.{MethodName}Req) (res *{Version}.{MethodName}Res, err error) { + return nil, gerror.NewCode(gcode.CodeNotImplemented) +} +` + +const TemplateGenCtrlApiInterface = ` +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package {Module} + +import ( +{ImportPaths} +) + +{Interfaces} +` diff --git a/cmd/gf-source/internal/consts/consts_gen_ctrl_template_sdk.go b/cmd/gf-source/internal/consts/consts_gen_ctrl_template_sdk.go new file mode 100644 index 0000000..f2c4670 --- /dev/null +++ b/cmd/gf-source/internal/consts/consts_gen_ctrl_template_sdk.go @@ -0,0 +1,89 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package consts + +const TemplateGenCtrlSdkPkgNew = ` +// ================================================================================= +// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish. +// ================================================================================= + +package {PkgName} + +import ( + "fmt" + + "github.com/gogf/gf/contrib/sdk/httpclient/v2" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/text/gstr" +) + +type implementer struct { + config httpclient.Config +} + +func New(config httpclient.Config) IClient { + return &implementer{ + config: config, + } +} + +` + +const TemplateGenCtrlSdkIClient = ` +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package {PkgName} + +import ( +) + +type IClient interface { +} +` + +const TemplateGenCtrlSdkImplementer = ` +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package {PkgName} + +import ( + "context" + + "github.com/gogf/gf/contrib/sdk/httpclient/v2" + "github.com/gogf/gf/v2/text/gstr" + +{ImportPaths} +) + +type implementer{ImplementerName} struct { + *httpclient.Client +} + +` + +const TemplateGenCtrlSdkImplementerNew = ` +func (i *implementer) {ImplementerName}() {Module}.I{ImplementerName} { + var ( + client = httpclient.New(i.config) + prefix = gstr.TrimRight(i.config.URL, "/") + "{VersionPrefix}" + ) + client.Client = client.Prefix(prefix) + return &implementer{ImplementerName}{client} +} + +` + +const TemplateGenCtrlSdkImplementerFunc = `{MethodComment} +func (i *implementer{ImplementerName}) {MethodName}(ctx context.Context, req *{Version}.{MethodName}Req) (res *{Version}.{MethodName}Res, err error) { + err = i.Request(ctx, req, &res) + return +} +` diff --git a/cmd/gf-source/internal/consts/consts_gen_dao_template_dao.go b/cmd/gf-source/internal/consts/consts_gen_dao_template_dao.go new file mode 100644 index 0000000..cffa30e --- /dev/null +++ b/cmd/gf-source/internal/consts/consts_gen_dao_template_dao.go @@ -0,0 +1,139 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package consts + +const TemplateGenDaoIndexContent = ` +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package {{.TplPackageName}} + +import ( + "{{.TplImportPrefix}}/internal" +) + +// {{.TplTableNameCamelLowerCase}}Dao is the data access object for the table {{.TplTableName}}. +// You can define custom methods on it to extend its functionality as needed. +type {{.TplTableNameCamelLowerCase}}Dao struct { + *internal.{{.TplTableNameCamelCase}}Dao +} + +var ( + // {{.TplTableNameCamelCase}} is a globally accessible object for table {{.TplTableName}} operations. + {{.TplTableNameCamelCase}} = {{.TplTableNameCamelLowerCase}}Dao{ +{{- if .TplTableSharding -}} + internal.New{{.TplTableNameCamelCase}}Dao({{.TplTableNameCamelLowerCase}}ShardingHandler), +{{- else -}} + internal.New{{.TplTableNameCamelCase}}Dao(), +{{- end -}} + } +) + +{{if .TplTableSharding -}} +// {{.TplTableNameCamelLowerCase}}ShardingHandler is the handler for sharding operations. +// You can fill this sharding handler with your custom implementation. +func {{.TplTableNameCamelLowerCase}}ShardingHandler(m *gdb.Model) *gdb.Model { + m = m.Sharding(gdb.ShardingConfig{ + Table: gdb.ShardingTableConfig{ + Enable: true, + Prefix: "{{.TplTableShardingPrefix}}", + // Replace Rule field with your custom sharding rule. + // Or you can use "&gdb.DefaultShardingRule{}" for default sharding rule. + Rule: nil, + }, + Schema: gdb.ShardingSchemaConfig{}, + }) + return m +} +{{- end}} + +// Add your custom methods and functionality below. + +` + +const TemplateGenDaoInternalContent = ` +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. {{.TplCreatedAtDatetimeStr}} +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// {{.TplTableNameCamelCase}}Dao is the data access object for the table {{.TplTableName}}. +type {{.TplTableNameCamelCase}}Dao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns {{.TplTableNameCamelCase}}Columns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// {{.TplTableNameCamelCase}}Columns defines and stores column names for the table {{.TplTableName}}. +type {{.TplTableNameCamelCase}}Columns struct { + {{.TplColumnDefine}} +} + +// {{.TplTableNameCamelLowerCase}}Columns holds the columns for the table {{.TplTableName}}. +var {{.TplTableNameCamelLowerCase}}Columns = {{.TplTableNameCamelCase}}Columns{ + {{.TplColumnNames}} +} + +// New{{.TplTableNameCamelCase}}Dao creates and returns a new DAO object for table data access. +func New{{.TplTableNameCamelCase}}Dao(handlers ...gdb.ModelHandler) *{{.TplTableNameCamelCase}}Dao { + return &{{.TplTableNameCamelCase}}Dao{ + group: "{{.TplGroupName}}", + table: "{{.TplTableName}}", + columns: {{.TplTableNameCamelLowerCase}}Columns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *{{.TplTableNameCamelCase}}Dao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *{{.TplTableNameCamelCase}}Dao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *{{.TplTableNameCamelCase}}Dao) Columns() {{.TplTableNameCamelCase}}Columns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *{{.TplTableNameCamelCase}}Dao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *{{.TplTableNameCamelCase}}Dao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *{{.TplTableNameCamelCase}}Dao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} +` diff --git a/cmd/gf-source/internal/consts/consts_gen_dao_template_do.go b/cmd/gf-source/internal/consts/consts_gen_dao_template_do.go new file mode 100644 index 0000000..97a404e --- /dev/null +++ b/cmd/gf-source/internal/consts/consts_gen_dao_template_do.go @@ -0,0 +1,20 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package consts + +const TemplateGenDaoDoContent = ` +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. {{.TplCreatedAtDatetimeStr}} +// ================================================================================= + +package {{.TplPackageName}} + +{{.TplPackageImports}} + +// {{.TplTableNameCamelCase}} is the golang structure of table {{.TplTableName}} for DAO operations like Where/Data. +{{.TplStructDefine}} +` diff --git a/cmd/gf-source/internal/consts/consts_gen_dao_template_entity.go b/cmd/gf-source/internal/consts/consts_gen_dao_template_entity.go new file mode 100644 index 0000000..c54713a --- /dev/null +++ b/cmd/gf-source/internal/consts/consts_gen_dao_template_entity.go @@ -0,0 +1,20 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package consts + +const TemplateGenDaoEntityContent = ` +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. {{.TplCreatedAtDatetimeStr}} +// ================================================================================= + +package {{.TplPackageName}} + +{{.TplPackageImports}} + +// {{.TplTableNameCamelCase}} is the golang structure for table {{.TplTableName}}. +{{.TplStructDefine}} +` diff --git a/cmd/gf-source/internal/consts/consts_gen_dao_template_table.go b/cmd/gf-source/internal/consts/consts_gen_dao_template_table.go new file mode 100644 index 0000000..a60c92d --- /dev/null +++ b/cmd/gf-source/internal/consts/consts_gen_dao_template_table.go @@ -0,0 +1,35 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package consts + +const TemplateGenTableContent = ` +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package {{.TplPackageName}} + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" +) + +// {{.TplTableNameCamelCase}} defines the fields of table "{{.TplTableName}}" with their properties. +// This map is used internally by GoFrame ORM to understand table structure. +var {{.TplTableNameCamelCase}} = map[string]*gdb.TableField{ +{{.TplTableFields}} +} + +// Set{{.TplTableNameCamelCase}}TableFields registers the table fields definition to the database instance. +// db: database instance that implements gdb.DB interface. +// schema: optional schema/namespace name, especially for databases that support schemas. +func Set{{.TplTableNameCamelCase}}TableFields(ctx context.Context, db gdb.DB, schema ...string) error { + return db.GetCore().SetTableFields(ctx, "{{.TplTableName}}", {{.TplTableNameCamelCase}}, schema...) +} + +` diff --git a/cmd/gf-source/internal/consts/consts_gen_enums_template.go b/cmd/gf-source/internal/consts/consts_gen_enums_template.go new file mode 100644 index 0000000..9477787 --- /dev/null +++ b/cmd/gf-source/internal/consts/consts_gen_enums_template.go @@ -0,0 +1,23 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package consts + +const TemplateGenEnums = ` +// ================================================================================ +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================ + +package {PackageName} + +import ( + "github.com/gogf/gf/v2/util/gtag" +) + +func init() { + gtag.SetGlobalEnums({EnumsJson}) +} +` diff --git a/cmd/gf-source/internal/consts/consts_gen_pbentity_template.go b/cmd/gf-source/internal/consts/consts_gen_pbentity_template.go new file mode 100644 index 0000000..73b693e --- /dev/null +++ b/cmd/gf-source/internal/consts/consts_gen_pbentity_template.go @@ -0,0 +1,23 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package consts + +const TemplatePbEntityMessageContent = ` +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +syntax = "proto3"; + +package {PackageName}; + +option go_package = "{GoPackage}"; +{OptionContent} +{Imports} + +{EntityMessage} +` diff --git a/cmd/gf-source/internal/consts/consts_gen_service_template.go b/cmd/gf-source/internal/consts/consts_gen_service_template.go new file mode 100644 index 0000000..f5c4f5a --- /dev/null +++ b/cmd/gf-source/internal/consts/consts_gen_service_template.go @@ -0,0 +1,41 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package consts + +const TemplateGenServiceContentHead = ` +// ================================================================================ +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// You can delete these comments if you wish manually maintain this interface file. +// ================================================================================ + +package {PackageName} + +{Imports} +` + +const TemplateGenServiceContentInterface = ` +{InterfaceName} interface { + {FuncDefinition} +} +` + +const TemplateGenServiceContentVariable = ` +local{StructName} {InterfaceName} +` + +const TemplateGenServiceContentRegister = ` +func {StructName}() {InterfaceName} { + if local{StructName} == nil { + panic("implement not found for interface {InterfaceName}, forgot register?") + } + return local{StructName} +} + +func Register{StructName}(i {InterfaceName}) { + local{StructName} = i +} +` diff --git a/cmd/gf-source/internal/consts/consts_gen_service_template_logic.go b/cmd/gf-source/internal/consts/consts_gen_service_template_logic.go new file mode 100644 index 0000000..eb43a03 --- /dev/null +++ b/cmd/gf-source/internal/consts/consts_gen_service_template_logic.go @@ -0,0 +1,19 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package consts + +const TemplateGenServiceLogicContent = ` +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package {PackageName} + +import( + {Imports} +) +` diff --git a/cmd/gf-source/internal/utility/mlog/mlog.go b/cmd/gf-source/internal/utility/mlog/mlog.go new file mode 100644 index 0000000..2181501 --- /dev/null +++ b/cmd/gf-source/internal/utility/mlog/mlog.go @@ -0,0 +1,76 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package mlog + +import ( + "context" + + "github.com/gogf/gf/v2/os/gcmd" + "github.com/gogf/gf/v2/os/genv" + "github.com/gogf/gf/v2/os/glog" +) + +const ( + headerPrintEnvName = "GF_CLI_MLOG_HEADER" +) + +var ( + ctx = context.TODO() + logger = glog.New() +) + +func init() { + if genv.Get(headerPrintEnvName).String() == "1" { + logger.SetHeaderPrint(true) + } else { + logger.SetHeaderPrint(false) + } + + if gcmd.GetOpt("debug") != nil || gcmd.GetOpt("gf.debug") != nil { + logger.SetHeaderPrint(true) + logger.SetStackSkip(4) + logger.SetFlags(logger.GetFlags() | glog.F_FILE_LONG) + logger.SetDebug(true) + } else { + logger.SetStack(false) + logger.SetDebug(false) + } +} + +// SetHeaderPrint enables/disables header printing to stdout. +func SetHeaderPrint(enabled bool) { + logger.SetHeaderPrint(enabled) + if enabled { + _ = genv.Set(headerPrintEnvName, "1") + } else { + _ = genv.Set(headerPrintEnvName, "0") + } +} + +func Print(v ...any) { + logger.Print(ctx, v...) +} + +func Printf(format string, v ...any) { + logger.Printf(ctx, format, v...) +} + +func Fatal(v ...any) { + logger.Fatal(ctx, v...) +} + +func Fatalf(format string, v ...any) { + logger.Fatalf(ctx, format, v...) +} + +func Debug(v ...any) { + logger.Debug(ctx, v...) +} + +func Debugf(format string, v ...any) { + logger.Debugf(ctx, format, v...) +} diff --git a/cmd/gf-source/internal/utility/utils/utils.go b/cmd/gf-source/internal/utility/utils/utils.go new file mode 100644 index 0000000..e4d4787 --- /dev/null +++ b/cmd/gf-source/internal/utility/utils/utils.go @@ -0,0 +1,135 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package utils + +import ( + "context" + "fmt" + + "golang.org/x/tools/imports" + + "github.com/gogf/gf/v2/os/gfile" + "github.com/gogf/gf/v2/os/gproc" + "github.com/gogf/gf/v2/text/gregex" + "github.com/gogf/gf/v2/text/gstr" + + "git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/consts" + "git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/mlog" +) + +// GoFmt formats the source file and adds or removes import statements as necessary. +func GoFmt(path string) { + replaceFunc := func(path, content string) string { + res, err := imports.Process(path, []byte(content), nil) + if err != nil { + mlog.Printf(`error format "%s" go files: %v`, path, err) + return content + } + return string(res) + } + + var err error + if gfile.IsFile(path) { + // File format. + if gfile.ExtName(path) != "go" { + return + } + err = gfile.ReplaceFileFunc(replaceFunc, path) + } else { + // Folder format. + err = gfile.ReplaceDirFunc(replaceFunc, path, "*.go", true) + } + if err != nil { + mlog.Printf(`error format "%s" go files: %v`, path, err) + } +} + +// GoModTidy executes `go mod tidy` at specified directory `dirPath`. +func GoModTidy(ctx context.Context, dirPath string) error { + command := fmt.Sprintf(`cd %s && go mod tidy`, dirPath) + err := gproc.ShellRun(ctx, command) + return err +} + +// IsFileDoNotEdit checks and returns whether file contains `do not edit` key. +func IsFileDoNotEdit(filePath string) bool { + if !gfile.Exists(filePath) { + return true + } + return gstr.Contains(gfile.GetContents(filePath), consts.DoNotEditKey) +} + +// ReplaceGeneratedContentGFV2 replaces generated go content from goframe v1 to v2. +func ReplaceGeneratedContentGFV2(folderPath string) (err error) { + return gfile.ReplaceDirFunc(func(path, content string) string { + if gstr.Contains(content, `"github.com/gogf/gf`) && !gstr.Contains(content, `"github.com/gogf/gf/v2`) { + content = gstr.Replace(content, `"github.com/gogf/gf"`, `"github.com/gogf/gf/v2"`) + content = gstr.Replace(content, `"github.com/gogf/gf/`, `"github.com/gogf/gf/v2/`) + content = gstr.Replace(content, `"github.com/gogf/gf/v2/contrib/`, `"github.com/gogf/gf/contrib/`) + return content + } + return content + }, folderPath, "*.go", true) +} + +// GetImportPath calculates and returns the golang import path for given `dirPath`. +// Note that it needs a `go.mod` in current working directory or parent directories to detect the path. +func GetImportPath(dirPath string) string { + // If `filePath` does not exist, create it firstly to find the import path. + var realPath = gfile.RealPath(dirPath) + if realPath == "" { + _ = gfile.Mkdir(dirPath) + realPath = gfile.RealPath(dirPath) + } + + var ( + newDir = gfile.Dir(realPath) + oldDir string + suffix = gfile.Basename(dirPath) + goModName = "go.mod" + goModPath string + importPath string + ) + for { + goModPath = gfile.Join(newDir, goModName) + if gfile.Exists(goModPath) { + match, _ := gregex.MatchString(`^module\s+(.+)\s*`, gfile.GetContents(goModPath)) + importPath = gstr.Trim(match[1]) + "/" + suffix + importPath = gstr.Replace(importPath, `\`, `/`) + importPath = gstr.TrimRight(importPath, `/`) + return importPath + } + oldDir = newDir + newDir = gfile.Dir(oldDir) + if newDir == oldDir { + return "" + } + suffix = gfile.Basename(oldDir) + "/" + suffix + } +} + +// GetModPath retrieves and returns the file path of go.mod for current project. +func GetModPath() string { + var ( + oldDir = gfile.Pwd() + newDir = oldDir + goModName = "go.mod" + goModPath string + ) + for { + goModPath = gfile.Join(newDir, goModName) + if gfile.Exists(goModPath) { + return goModPath + } + newDir = gfile.Dir(oldDir) + if newDir == oldDir { + break + } + oldDir = newDir + } + return "" +} diff --git a/cmd/gf-source/internal/utility/utils/utils_http_download.go b/cmd/gf-source/internal/utility/utils/utils_http_download.go new file mode 100644 index 0000000..40d74fa --- /dev/null +++ b/cmd/gf-source/internal/utility/utils/utils_http_download.go @@ -0,0 +1,55 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package utils + +import ( + "io" + "net/http" + "os" + "time" + + "github.com/schollz/progressbar/v3" + + "github.com/gogf/gf/v2/errors/gerror" + + "git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/mlog" +) + +// HTTPDownloadFileWithPercent downloads target url file to local path with percent process printing. +func HTTPDownloadFileWithPercent(url string, localSaveFilePath string) error { + start := time.Now() + out, err := os.Create(localSaveFilePath) + if err != nil { + return gerror.Wrapf(err, `download "%s" to "%s" failed`, url, localSaveFilePath) + } + defer out.Close() + + headResp, err := http.Head(url) + if err != nil { + return gerror.Wrapf(err, `download "%s" to "%s" failed`, url, localSaveFilePath) + } + defer headResp.Body.Close() + + resp, err := http.Get(url) + if err != nil { + return gerror.Wrapf(err, `download "%s" to "%s" failed`, url, localSaveFilePath) + } + defer resp.Body.Close() + + bar := progressbar.NewOptions(int(resp.ContentLength), progressbar.OptionShowBytes(true), progressbar.OptionShowCount()) + writer := io.MultiWriter(out, bar) + _, err = io.Copy(writer, resp.Body) + + elapsed := time.Since(start) + if elapsed > time.Minute { + mlog.Printf(`download completed in %.0fm`, float64(elapsed)/float64(time.Minute)) + } else { + mlog.Printf(`download completed in %.0fs`, elapsed.Seconds()) + } + + return nil +} diff --git a/cmd/gf-source/internal/utility/utils/utils_test.go b/cmd/gf-source/internal/utility/utils/utils_test.go new file mode 100644 index 0000000..edf01d6 --- /dev/null +++ b/cmd/gf-source/internal/utility/utils/utils_test.go @@ -0,0 +1,23 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package utils_test + +import ( + "fmt" + "testing" + + "github.com/gogf/gf/v2/test/gtest" + + "git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/utils" +) + +func Test_GetModPath(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + goModPath := utils.GetModPath() + fmt.Println(goModPath) + }) +} diff --git a/cmd/gf-source/templates/consts_gen_dao_template_dao.go b/cmd/gf-source/templates/consts_gen_dao_template_dao.go new file mode 100644 index 0000000..3904f00 --- /dev/null +++ b/cmd/gf-source/templates/consts_gen_dao_template_dao.go @@ -0,0 +1,139 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package consts + +const TemplateGenDaoIndexContent = ` +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package {{.TplPackageName}} + +import ( + "{{.TplImportPrefix}}/internal" +) + +// {{.TplTableNameCamelLowerCase}}Dao is the data access object for the table {{.TplTableName}}. +// You can define custom methods on it to extend its functionality as needed. +type {{.TplTableNameCamelLowerCase}}Dao struct { + *internal.{{.TplTableNameCamelCase}}Dao +} + +var ( + // {{.TplTableNameCamelCase}} is a globally accessible object for table {{.TplTableName}} operations. + {{.TplTableNameCamelCase}} = {{.TplTableNameCamelLowerCase}}Dao{ +{{- if .TplTableSharding -}} + internal.New{{.TplTableNameCamelCase}}Dao({{.TplTableNameCamelLowerCase}}ShardingHandler), +{{- else -}} + internal.New{{.TplTableNameCamelCase}}Dao(), +{{- end -}} + } +) + +{{if .TplTableSharding -}} +// {{.TplTableNameCamelLowerCase}}ShardingHandler is the handler for sharding operations. +// You can fill this sharding handler with your custom implementation. +func {{.TplTableNameCamelLowerCase}}ShardingHandler(m *database.Model) *database.Model { + m = m.Sharding(database.ShardingConfig{ + Table: database.ShardingTableConfig{ + Enable: true, + Prefix: "{{.TplTableShardingPrefix}}", + // Replace Rule field with your custom sharding rule. + // Or you can use "&database.DefaultShardingRule{}" for default sharding rule. + Rule: nil, + }, + Schema: database.ShardingSchemaConfig{}, + }) + return m +} +{{- end}} + +// Add your custom methods and functionality below. + +` + +const TemplateGenDaoInternalContent = ` +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. {{.TplCreatedAtDatetimeStr}} +// ========================================================================== + +package internal + +import ( + "context" + + "git.magicany.cc/black1552/gin-base/database" + "github.com/gogf/gf/v2/frame/g" +) + +// {{.TplTableNameCamelCase}}Dao is the data access object for the table {{.TplTableName}}. +type {{.TplTableNameCamelCase}}Dao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns {{.TplTableNameCamelCase}}Columns // columns contains all the column names of Table for convenient usage. + handlers []database.ModelHandler // handlers for customized model modification. +} + +// {{.TplTableNameCamelCase}}Columns defines and stores column names for the table {{.TplTableName}}. +type {{.TplTableNameCamelCase}}Columns struct { + {{.TplColumnDefine}} +} + +// {{.TplTableNameCamelLowerCase}}Columns holds the columns for the table {{.TplTableName}}. +var {{.TplTableNameCamelLowerCase}}Columns = {{.TplTableNameCamelCase}}Columns{ + {{.TplColumnNames}} +} + +// New{{.TplTableNameCamelCase}}Dao creates and returns a new DAO object for table data access. +func New{{.TplTableNameCamelCase}}Dao(handlers ...database.ModelHandler) *{{.TplTableNameCamelCase}}Dao { + return &{{.TplTableNameCamelCase}}Dao{ + group: "{{.TplGroupName}}", + table: "{{.TplTableName}}", + columns: {{.TplTableNameCamelLowerCase}}Columns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *{{.TplTableNameCamelCase}}Dao) DB() database.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *{{.TplTableNameCamelCase}}Dao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *{{.TplTableNameCamelCase}}Dao) Columns() {{.TplTableNameCamelCase}}Columns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *{{.TplTableNameCamelCase}}Dao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *{{.TplTableNameCamelCase}}Dao) Ctx(ctx context.Context) *database.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *{{.TplTableNameCamelCase}}Dao) Transaction(ctx context.Context, f func(ctx context.Context, tx database.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} +` diff --git a/cmd/gf-source/templates/consts_gen_dao_template_do.go b/cmd/gf-source/templates/consts_gen_dao_template_do.go new file mode 100644 index 0000000..97a404e --- /dev/null +++ b/cmd/gf-source/templates/consts_gen_dao_template_do.go @@ -0,0 +1,20 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package consts + +const TemplateGenDaoDoContent = ` +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. {{.TplCreatedAtDatetimeStr}} +// ================================================================================= + +package {{.TplPackageName}} + +{{.TplPackageImports}} + +// {{.TplTableNameCamelCase}} is the golang structure of table {{.TplTableName}} for DAO operations like Where/Data. +{{.TplStructDefine}} +` diff --git a/cmd/gf-source/templates/consts_gen_dao_template_entity.go b/cmd/gf-source/templates/consts_gen_dao_template_entity.go new file mode 100644 index 0000000..c54713a --- /dev/null +++ b/cmd/gf-source/templates/consts_gen_dao_template_entity.go @@ -0,0 +1,20 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package consts + +const TemplateGenDaoEntityContent = ` +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. {{.TplCreatedAtDatetimeStr}} +// ================================================================================= + +package {{.TplPackageName}} + +{{.TplPackageImports}} + +// {{.TplTableNameCamelCase}} is the golang structure for table {{.TplTableName}}. +{{.TplStructDefine}} +` diff --git a/cmd/gf-source/templates/consts_gen_dao_template_table.go b/cmd/gf-source/templates/consts_gen_dao_template_table.go new file mode 100644 index 0000000..e930f30 --- /dev/null +++ b/cmd/gf-source/templates/consts_gen_dao_template_table.go @@ -0,0 +1,35 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package consts + +const TemplateGenTableContent = ` +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package {{.TplPackageName}} + +import ( + "context" + + "git.magicany.cc/black1552/gin-base/database" +) + +// {{.TplTableNameCamelCase}} defines the fields of table "{{.TplTableName}}" with their properties. +// This map is used internally by GoFrame ORM to understand table structure. +var {{.TplTableNameCamelCase}} = map[string]*database.TableField{ +{{.TplTableFields}} +} + +// Set{{.TplTableNameCamelCase}}TableFields registers the table fields definition to the database instance. +// db: database instance that implements database.DB interface. +// schema: optional schema/namespace name, especially for databases that support schemas. +func Set{{.TplTableNameCamelCase}}TableFields(ctx context.Context, db database.DB, schema ...string) error { + return db.GetCore().SetTableFields(ctx, "{{.TplTableName}}", {{.TplTableNameCamelCase}}, schema...) +} + +` diff --git a/cmd/gin-dao-gen/main.go b/cmd/gin-dao-gen/main.go new file mode 100644 index 0000000..aaadf5f --- /dev/null +++ b/cmd/gin-dao-gen/main.go @@ -0,0 +1,376 @@ +package main + +import ( + "context" + "fmt" + "os" + "strings" + + "git.magicany.cc/black1552/gin-base/config" + "git.magicany.cc/black1552/gin-base/database" + _ "git.magicany.cc/black1552/gin-base/database/drivers" +) + +func main() { + ctx := context.Background() + + // 加载配置 + cfg := config.GetAllConfig() + if cfg == nil { + fmt.Println("❌ 错误: 配置为空") + os.Exit(1) + } + + // 检查数据库配置 + dbConfigMap, ok := cfg["database"].(map[string]any) + if !ok || len(dbConfigMap) == 0 { + fmt.Println("❌ 错误: 未找到数据库配置") + os.Exit(1) + } + + // 获取默认数据库配置 + defaultDbConfig, ok := dbConfigMap["default"].(map[string]any) + if !ok { + fmt.Println("❌ 错误: 未找到 default 数据库配置") + os.Exit(1) + } + + // 提取配置值 + host := getStringValue(defaultDbConfig, "host", "127.0.0.1") + port := getStringValue(defaultDbConfig, "port", "3306") + name := getStringValue(defaultDbConfig, "name", "test") + dbType := getStringValue(defaultDbConfig, "type", "mysql") + + fmt.Println("=== Gin-Base DAO 代码生成工具 ===") + fmt.Printf("📊 数据库: %s\n", name) + fmt.Printf("🔧 类型: %s\n", dbType) + fmt.Printf("🌐 主机: %s:%s\n\n", host, port) + + // 初始化数据库连接 + err := initDatabaseFromMap(dbConfigMap) + if err != nil { + fmt.Printf("❌ 数据库初始化失败: %v\n", err) + os.Exit(1) + } + + // 获取数据库实例 + db := database.Database() + + // 获取所有表 + tables, err := db.Tables(ctx) + if err != nil { + fmt.Printf("❌ 获取表列表失败: %v\n", err) + os.Exit(1) + } + + fmt.Printf("📋 找到 %d 个表:\n", len(tables)) + for i, table := range tables { + fmt.Printf(" %d. %s\n", i+1, table) + } + fmt.Println() + + // 询问用户要生成的表 + var selectedTables []string + if len(os.Args) > 1 { + // 从命令行参数获取表名 + selectedTables = os.Args[1:] + } else { + // 默认生成所有表 + selectedTables = tables + fmt.Println("💡 提示: 可以通过命令行参数指定要生成的表") + fmt.Println(" 例如: gin-dao-gen users orders") + } + + // 创建输出目录 + dirs := []string{ + "./internal/dao", + "./internal/model/do", + "./internal/model/entity", + "./internal/model/table", + } + + for _, dir := range dirs { + if err := os.MkdirAll(dir, 0755); err != nil { + fmt.Printf("❌ 创建目录失败 %s: %v\n", dir, err) + os.Exit(1) + } + } + + // 为每个表生成代码 + for _, tableName := range selectedTables { + fmt.Printf("\n🔨 正在生成表 [%s] 的代码...\n", tableName) + + // 获取表字段信息 + fields, err := db.TableFields(ctx, tableName) + if err != nil { + fmt.Printf(" ⚠️ 获取表字段失败: %v\n", err) + continue + } + + // 生成 Entity + entityName := tableNameToStructName(tableName) + generateEntity(tableName, entityName, fields) + + // 生成 DO + generateDO(tableName, entityName, fields) + + // 生成 DAO + generateDAO(tableName, entityName) + + // 生成 Table + generateTable(tableName, entityName, fields) + + fmt.Printf(" ✅ 完成\n") + } + + fmt.Println("\n🎉 代码生成完成!") + fmt.Println("📁 生成的文件位于:") + fmt.Println(" - ./internal/dao/") + fmt.Println(" - ./internal/model/do/") + fmt.Println(" - ./internal/model/entity/") + fmt.Println(" - ./internal/model/table/") +} + +// 从 Map 初始化数据库 +func initDatabaseFromMap(dbConfigMap map[string]any) error { + for name, nodeConfig := range dbConfigMap { + nodeMap, ok := nodeConfig.(map[string]any) + if !ok { + continue + } + + configNode := database.ConfigNode{ + Host: getStringValue(nodeMap, "host", "127.0.0.1"), + Port: getStringValue(nodeMap, "port", "3306"), + User: getStringValue(nodeMap, "user", "root"), + Pass: getStringValue(nodeMap, "pass", ""), + Name: getStringValue(nodeMap, "name", ""), + Type: getStringValue(nodeMap, "type", "mysql"), + Role: database.Role(getStringValue(nodeMap, "role", "master")), + Debug: getBoolValue(nodeMap, "debug", false), + Prefix: getStringValue(nodeMap, "prefix", ""), + Charset: getStringValue(nodeMap, "charset", "utf8"), + } + + if err := database.AddConfigNode(name, configNode); err != nil { + return fmt.Errorf("add config node %s failed: %w", name, err) + } + } + + return nil +} + +// 辅助函数:从 map 中获取字符串值 +func getStringValue(m map[string]any, key string, defaultValue string) string { + if val, ok := m[key]; ok { + if str, ok := val.(string); ok { + return str + } + } + return defaultValue +} + +// 辅助函数:从 map 中获取布尔值 +func getBoolValue(m map[string]any, key string, defaultValue bool) bool { + if val, ok := m[key]; ok { + if b, ok := val.(bool); ok { + return b + } + } + return defaultValue +} + +// 表名转结构体名 +func tableNameToStructName(tableName string) string { + parts := strings.Split(tableName, "_") + var result strings.Builder + for _, part := range parts { + if len(part) > 0 { + result.WriteString(strings.ToUpper(part[:1])) + result.WriteString(part[1:]) + } + } + return result.String() +} + +// 生成 Entity 文件 +func generateEntity(tableName, entityName string, fields map[string]*database.TableField) { + filename := fmt.Sprintf("./internal/model/entity/%s.go", tableName) + + var content strings.Builder + content.WriteString("package entity\n\n") + content.WriteString("// Auto-generated by gin-base gen dao tool\n\n") + content.WriteString(fmt.Sprintf("// %s represents the entity for table %s\n", entityName, tableName)) + content.WriteString(fmt.Sprintf("type %s struct {\n", entityName)) + + for _, field := range fields { + fieldName := fieldNameToStructName(field.Name) + goType := dbTypeToGoType(field.Type) + jsonTag := field.Name + + content.WriteString(fmt.Sprintf("\t%s %s `json:\"%s\" description:\"%s\"`\n", + fieldName, goType, jsonTag, field.Comment)) + } + + content.WriteString("}\n") + + if err := os.WriteFile(filename, []byte(content.String()), 0644); err != nil { + fmt.Printf(" ❌ 写入文件失败: %v\n", err) + } else { + fmt.Printf(" 📄 生成 Entity: %s\n", filename) + } +} + +// 生成 DO 文件 +func generateDO(tableName, entityName string, fields map[string]*database.TableField) { + filename := fmt.Sprintf("./internal/model/do/%s.go", tableName) + + var content strings.Builder + content.WriteString("package do\n\n") + content.WriteString("import \"github.com/gogf/gf/v2/frame/g\"\n\n") + content.WriteString("// Auto-generated by gin-base gen dao tool\n\n") + content.WriteString(fmt.Sprintf("// %s represents the data object for table %s\n", entityName, tableName)) + content.WriteString(fmt.Sprintf("type %s struct {\n\tg.Meta `orm:\"table:%s, do:true\"`\n\n", entityName, tableName)) + + for _, field := range fields { + fieldName := fieldNameToStructName(field.Name) + goType := dbTypeToGoType(field.Type) + + content.WriteString(fmt.Sprintf("\t%s *%s `json:\"%s,omitempty\"`\n", + fieldName, goType, field.Name)) + } + + content.WriteString("}\n") + + if err := os.WriteFile(filename, []byte(content.String()), 0644); err != nil { + fmt.Printf(" ❌ 写入文件失败: %v\n", err) + } else { + fmt.Printf(" 📄 生成 DO: %s\n", filename) + } +} + +// 生成 DAO 文件 +func generateDAO(tableName, entityName string) { + filename := fmt.Sprintf("./internal/dao/%s.go", tableName) + lowerName := strings.ToLower(entityName[:1]) + entityName[1:] + + var content strings.Builder + content.WriteString("package dao\n\n") + content.WriteString("import (\n") + content.WriteString("\t\"git.magicany.cc/black1552/gin-base/database\"\n") + content.WriteString(fmt.Sprintf("\t\"git.magicany.cc/black1552/gin-base/internal/model/entity\"\n")) + content.WriteString(")\n\n") + content.WriteString("// Auto-generated by gin-base gen dao tool\n\n") + content.WriteString(fmt.Sprintf("// %s is the DAO for table %s\n", entityName, tableName)) + content.WriteString(fmt.Sprintf("var %s = New%s()\n\n", lowerName, entityName)) + content.WriteString(fmt.Sprintf("// %s creates and returns a new DAO instance\n", entityName)) + content.WriteString(fmt.Sprintf("func New%s() *%sDao {\n", entityName, entityName)) + content.WriteString(fmt.Sprintf("\treturn &%sDao{\n", entityName)) + content.WriteString("\t\ttable: \"" + tableName + "\",\n") + content.WriteString("\t}\n") + content.WriteString("}\n\n") + content.WriteString(fmt.Sprintf("// %sDao is the data access object for %s\n", entityName, tableName)) + content.WriteString(fmt.Sprintf("type %sDao struct {\n", entityName)) + content.WriteString("\ttable string\n") + content.WriteString("}\n\n") + content.WriteString("// Table returns the table name\n") + content.WriteString(fmt.Sprintf("func (d *%sDao) Table() string {\n", entityName)) + content.WriteString("\treturn d.table\n") + content.WriteString("}\n\n") + content.WriteString("// DB returns the database instance\n") + content.WriteString("func (d *DB) DB() database.DB {\n") + content.WriteString("\treturn database.Database()\n") + content.WriteString("}\n") + + if err := os.WriteFile(filename, []byte(content.String()), 0644); err != nil { + fmt.Printf(" ❌ 写入文件失败: %v\n", err) + } else { + fmt.Printf(" 📄 生成 DAO: %s\n", filename) + } +} + +// 生成 Table 文件 +func generateTable(tableName, entityName string, fields map[string]*database.TableField) { + filename := fmt.Sprintf("./internal/model/table/%s.go", tableName) + + var content strings.Builder + content.WriteString("package table\n\n") + content.WriteString("// Auto-generated by gin-base gen dao tool\n\n") + content.WriteString("const (\n") + content.WriteString(fmt.Sprintf("\t// %s is the table name\n", entityName)) + content.WriteString(fmt.Sprintf("\t%s = \"%s\"\n", entityName, tableName)) + content.WriteString(")\n\n") + content.WriteString("// Columns defines all columns of the table\n") + content.WriteString("var Columns = struct {\n") + + for _, field := range fields { + fieldName := fieldNameToConstName(field.Name) + content.WriteString(fmt.Sprintf("\t%s string\n", fieldName)) + } + content.WriteString("}{\n") + + for _, field := range fields { + fieldName := fieldNameToConstName(field.Name) + content.WriteString(fmt.Sprintf("\t%s: \"%s\",\n", fieldName, field.Name)) + } + content.WriteString("}\n") + + if err := os.WriteFile(filename, []byte(content.String()), 0644); err != nil { + fmt.Printf(" ❌ 写入文件失败: %v\n", err) + } else { + fmt.Printf(" 📄 生成 Table: %s\n", filename) + } +} + +// 字段名转结构体字段名 +func fieldNameToStructName(fieldName string) string { + parts := strings.Split(fieldName, "_") + var result strings.Builder + for _, part := range parts { + if len(part) > 0 { + result.WriteString(strings.ToUpper(part[:1])) + result.WriteString(part[1:]) + } + } + return result.String() +} + +// 字段名转常量名 +func fieldNameToConstName(fieldName string) string { + parts := strings.Split(fieldName, "_") + var result strings.Builder + for i, part := range parts { + if i > 0 { + result.WriteString("_") + } + result.WriteString(strings.ToUpper(part)) + } + return result.String() +} + +// 数据库类型转 Go 类型 +func dbTypeToGoType(dbType string) string { + dbType = strings.ToLower(dbType) + + switch { + case strings.Contains(dbType, "int"): + if strings.Contains(dbType, "bigint") { + return "int64" + } + return "int" + case strings.Contains(dbType, "float"), strings.Contains(dbType, "double"), strings.Contains(dbType, "decimal"): + return "float64" + case strings.Contains(dbType, "bool"): + return "bool" + case strings.Contains(dbType, "datetime"), strings.Contains(dbType, "timestamp"): + return "*gtime.Time" + case strings.Contains(dbType, "date"): + return "*gtime.Time" + case strings.Contains(dbType, "text"), strings.Contains(dbType, "char"), strings.Contains(dbType, "varchar"): + return "string" + case strings.Contains(dbType, "blob"), strings.Contains(dbType, "binary"): + return "[]byte" + default: + return "string" + } +} diff --git a/cmd/go.mod b/cmd/go.mod new file mode 100644 index 0000000..618bf0c --- /dev/null +++ b/cmd/go.mod @@ -0,0 +1,81 @@ +module git.magicany.cc/black1552/gin-base/cmd + +go 1.25.0 + +require ( + git.magicany.cc/black1552/gin-base v1.0.2009 + github.com/gogf/gf/v2 v2.10.0 + github.com/olekukonko/tablewriter v1.1.4 + github.com/schollz/progressbar/v3 v3.19.0 + golang.org/x/mod v0.34.0 + golang.org/x/tools v0.43.0 +) + +require ( + filippo.io/edwards25519 v1.2.0 // indirect + github.com/BurntSushi/toml v1.6.0 // indirect + github.com/ClickHouse/clickhouse-go/v2 v2.0.15 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/clbanning/mxj/v2 v2.7.0 // indirect + github.com/clipperhouse/displaywidth v0.10.0 // indirect + github.com/clipperhouse/uax29/v2 v2.6.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/emirpasic/gods/v2 v2.0.0-alpha // indirect + github.com/fatih/color v1.19.0 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-sql-driver/mysql v1.9.3 // indirect + github.com/go-viper/mapstructure/v2 v2.5.0 // indirect + github.com/goccy/go-json v0.10.6 // indirect + github.com/golang-jwt/jwt/v5 v5.3.1 // indirect + github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect + github.com/golang-sql/sqlexp v0.1.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/websocket v1.5.3 // indirect + github.com/grokify/html-strip-tags-go v0.1.0 // indirect + github.com/lib/pq v1.12.0 // indirect + github.com/magiconair/properties v1.8.10 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.21 // indirect + github.com/microsoft/go-mssqldb v1.7.1 // indirect + github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect + github.com/ncruces/go-strftime v1.0.0 // indirect + github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect + github.com/olekukonko/errors v1.2.0 // indirect + github.com/olekukonko/ll v0.1.8 // indirect + github.com/paulmach/orb v0.7.1 // indirect + github.com/pelletier/go-toml/v2 v2.3.0 // indirect + github.com/pierrec/lz4/v4 v4.1.14 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/sagikazarmark/locafero v0.12.0 // indirect + github.com/shopspring/decimal v1.3.1 // indirect + github.com/sijms/go-ora/v2 v2.7.10 // indirect + github.com/spf13/afero v1.15.0 // indirect + github.com/spf13/cast v1.10.0 // indirect + github.com/spf13/pflag v1.0.10 // indirect + github.com/spf13/viper v1.21.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/otel v1.42.0 // indirect + go.opentelemetry.io/otel/metric v1.42.0 // indirect + go.opentelemetry.io/otel/sdk v1.42.0 // indirect + go.opentelemetry.io/otel/trace v1.42.0 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/crypto v0.49.0 // indirect + golang.org/x/net v0.52.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.42.0 // indirect + golang.org/x/term v0.41.0 // indirect + golang.org/x/text v0.35.0 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + modernc.org/libc v1.70.0 // indirect + modernc.org/mathutil v1.7.1 // indirect + modernc.org/memory v1.11.0 // indirect + modernc.org/sqlite v1.48.0 // indirect +) + +replace git.magicany.cc/black1552/gin-base => ./.. diff --git a/cmd/go.sum b/cmd/go.sum new file mode 100644 index 0000000..69f5ffa --- /dev/null +++ b/cmd/go.sum @@ -0,0 +1,277 @@ +filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= +filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 h1:lGlwhPtrX6EVml1hO0ivjkUxsSyl4dsiw9qcA1k/3IQ= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1/go.mod h1:RKUqNu35KJYcVG/fqTRqmuXJZYNhYkBrnC/hX7yGbTA= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 h1:sO0/P7g68FrryJzljemN+6GTssUXdANk6aJ7T1ZxnsQ= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1/go.mod h1:h8hyGFDsU5HMivxiS2iYFZsgDbU9OnnJ163x5UGVKYo= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 h1:6oNBlSdi1QqM1PNW7FPA6xOGA5UNsXnkaYZz9vdPGhA= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1 h1:MyVTgWR8qd/Jw1Le0NZebGBUCLbtak3bJ3z1OlqZBpw= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1/go.mod h1:GpPjLhVR9dnUoJMyHWSPy71xY9/lcmpzIPZXmF0FCVY= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 h1:D3occbWoio4EBLkbkevetNMAVX197GkzbUMtqjGWn80= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0/go.mod h1:bTSOgj05NGRuHHhQwAdPnYr9TOdNmKlZTgGLL6nyAdI= +github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 h1:DzHpqpoJVaCgOUdVHxE8QB52S6NiVdDQvGlny1qvPqA= +github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= +github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/ClickHouse/clickhouse-go v1.5.4/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI= +github.com/ClickHouse/clickhouse-go/v2 v2.0.15 h1:lLAZliqrZEygkxosLaW1qHyeTb4Ho7fVCZ0WKCpLocU= +github.com/ClickHouse/clickhouse-go/v2 v2.0.15/go.mod h1:Z21o82zD8FFqefOQDg93c0XITlxGbTsWQuRm588Azkk= +github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chengxilo/virtualterm v1.0.4 h1:Z6IpERbRVlfB8WkOmtbHiDbBANU7cimRIof7mk9/PwM= +github.com/chengxilo/virtualterm v1.0.4/go.mod h1:DyxxBZz/x1iqJjFxTFcr6/x+jSpqN0iwWCOK1q10rlY= +github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= +github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= +github.com/clipperhouse/displaywidth v0.10.0 h1:GhBG8WuerxjFQQYeuZAeVTuyxuX+UraiZGD4HJQ3Y8g= +github.com/clipperhouse/displaywidth v0.10.0/go.mod h1:XqJajYsaiEwkxOj4bowCTMcT1SgvHo9flfF3jQasdbs= +github.com/clipperhouse/uax29/v2 v2.6.0 h1:z0cDbUV+aPASdFb2/ndFnS9ts/WNXgTNNGFoKXuhpos= +github.com/clipperhouse/uax29/v2 v2.6.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= +github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/emirpasic/gods/v2 v2.0.0-alpha h1:dwFlh8pBg1VMOXWGipNMRt8v96dKAIvBehtCt6OtunU= +github.com/emirpasic/gods/v2 v2.0.0-alpha/go.mod h1:W0y4M2dtBB9U5z3YlghmpuUhiaZT2h6yoeE+C1sCp6A= +github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w= +github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= +github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= +github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= +github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU= +github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/gogf/gf/v2 v2.10.0 h1:rzDROlyqGMe/eM6dCalSR8dZOuMIdLhmxKSH1DGhbFs= +github.com/gogf/gf/v2 v2.10.0/go.mod h1:Svl1N+E8G/QshU2DUbh/3J/AJauqCgUnxHurXWR4Qx0= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= +github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= +github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grokify/html-strip-tags-go v0.1.0 h1:03UrQLjAny8xci+R+qjCce/MYnpNXCtgzltlQbOBae4= +github.com/grokify/html-strip-tags-go v0.1.0/go.mod h1:ZdzgfHEzAfz9X6Xe5eBLVblWIxXfYSQ40S/VKrAOGpc= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.12.0 h1:mC1zeiNamwKBecjHarAr26c/+d8V5w/u4J0I/yASbJo= +github.com/lib/pq v1.12.0/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.21 h1:jJKAZiQH+2mIinzCJIaIG9Be1+0NR+5sz/lYEEjdM8w= +github.com/mattn/go-runewidth v0.0.21/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= +github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/microsoft/go-mssqldb v1.7.1 h1:KU/g8aWeM3Hx7IMOFpiwYiUkU+9zeISb4+tx3ScVfsM= +github.com/microsoft/go-mssqldb v1.7.1/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= +github.com/mkevac/debugcharts v0.0.0-20191222103121-ae1c48aa8615/go.mod h1:Ad7oeElCZqA1Ufj0U9/liOF4BtVepxRcTvr2ey7zTvM= +github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= +github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc= +github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0= +github.com/olekukonko/errors v1.2.0 h1:10Zcn4GeV59t/EGqJc8fUjtFT/FuUh5bTMzZ1XwmCRo= +github.com/olekukonko/errors v1.2.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y= +github.com/olekukonko/ll v0.1.8 h1:ysHCJRGHYKzmBSdz9w5AySztx7lG8SQY+naTGYUbsz8= +github.com/olekukonko/ll v0.1.8/go.mod h1:RPRC6UcscfFZgjo1nulkfMH5IM0QAYim0LfnMvUuozw= +github.com/olekukonko/tablewriter v1.1.4 h1:ORUMI3dXbMnRlRggJX3+q7OzQFDdvgbN9nVWj1drm6I= +github.com/olekukonko/tablewriter v1.1.4/go.mod h1:+kedxuyTtgoZLwif3P1Em4hARJs+mVnzKxmsCL/C5RY= +github.com/paulmach/orb v0.7.1 h1:Zha++Z5OX/l168sqHK3k4z18LDvr+YAO/VjK0ReQ9rU= +github.com/paulmach/orb v0.7.1/go.mod h1:FWRlTgl88VI1RBx/MkrwWDRhQ96ctqMCh8boXhmqB/A= +github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY= +github.com/pelletier/go-toml/v2 v2.3.0 h1:k59bC/lIZREW0/iVaQR8nDHxVq8OVlIzYCOJf421CaM= +github.com/pelletier/go-toml/v2 v2.3.0/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE= +github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4= +github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI= +github.com/schollz/progressbar/v3 v3.19.0 h1:Ea18xuIRQXLAUidVDox3AbwfUhD0/1IvohyTutOIFoc= +github.com/schollz/progressbar/v3 v3.19.0/go.mod h1:IsO3lpbaGuzh8zIMzgY3+J8l4C8GjO0Y9S69eFvNsec= +github.com/shirou/gopsutil v2.19.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= +github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/sijms/go-ora/v2 v2.7.10 h1:GSLdj0PYYgSndhsnm7b6p32OqgnwnUZSkFb3j+htfhI= +github.com/sijms/go-ora/v2 v2.7.10/go.mod h1:EHxlY6x7y9HAsdfumurRfTd+v8NrEOTR3Xl4FWlH6xk= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= +github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= +github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk= +go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho= +go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc= +go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4= +go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI= +go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo= +go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts= +go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA= +go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc= +go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU= +go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY= +go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= +golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= +golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= +golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191220220014-0732a990476f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220429233432-b5fbb4746d32/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= +golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= +golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis= +modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= +modernc.org/ccgo/v4 v4.32.0 h1:hjG66bI/kqIPX1b2yT6fr/jt+QedtP2fqojG2VrFuVw= +modernc.org/ccgo/v4 v4.32.0/go.mod h1:6F08EBCx5uQc38kMGl+0Nm0oWczoo1c7cgpzEry7Uc0= +modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM= +modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU= +modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= +modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= +modernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo= +modernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY= +modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks= +modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI= +modernc.org/libc v1.70.0 h1:U58NawXqXbgpZ/dcdS9kMshu08aiA6b7gusEusqzNkw= +modernc.org/libc v1.70.0/go.mod h1:OVmxFGP1CI/Z4L3E0Q3Mf1PDE0BucwMkcXjjLntvHJo= +modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= +modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= +modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= +modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= +modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= +modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= +modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= +modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= +modernc.org/sqlite v1.48.0 h1:ElZyLop3Q2mHYk5IFPPXADejZrlHu7APbpB0sF78bq4= +modernc.org/sqlite v1.48.0/go.mod h1:hWjRO6Tj/5Ik8ieqxQybiEOUXy0NJFNp2tpvVpKlvig= +modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= +modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= diff --git a/database/gdb_core.go b/database/gdb_core.go index 83d35ea..a242436 100644 --- a/database/gdb_core.go +++ b/database/gdb_core.go @@ -717,12 +717,10 @@ func (c *Core) MarshalJSON() ([]byte, error) { // writeSqlToLogger outputs the Sql object to logger. // It is enabled only if configuration "debug" is true. -func (c *Core) writeSqlToLogger(ctx context.Context, sql *Sql) { +func (c *Core) writeSqlToLogger(sql *Sql) { var transactionIdStr string if sql.IsTransaction { - if v := ctx.Value(transactionIdForLoggerCtx); v != nil { - transactionIdStr = fmt.Sprintf(`[txid:%d] `, v.(uint64)) - } + transactionIdStr = fmt.Sprintf(`[tx] `) } s := fmt.Sprintf( "[%3d ms] [%s] [%s] [rows:%-3d] %s%s", @@ -730,9 +728,9 @@ func (c *Core) writeSqlToLogger(ctx context.Context, sql *Sql) { ) if sql.Error != nil { s += "\nError: " + sql.Error.Error() - c.logger.Error(ctx, s) + c.logger.Error(s) } else { - c.logger.Debug(ctx, s) + c.logger.Debug(s) } } diff --git a/database/gdb_core_underlying.go b/database/gdb_core_underlying.go index 1753fbe..9db3087 100644 --- a/database/gdb_core_underlying.go +++ b/database/gdb_core_underlying.go @@ -307,7 +307,7 @@ func (c *Core) DoCommit(ctx context.Context, in DoCommitInput) (out DoCommitOutp // Logging. if c.db.GetDebug() { - c.writeSqlToLogger(ctx, sqlObj) + c.writeSqlToLogger(sqlObj) } if err != nil && err != sql.ErrNoRows { err = gerror.WrapCode(