This commit is contained in:
孟帅
2025-03-22 20:23:27 +08:00
parent 5301bedff2
commit 62af998991
98 changed files with 1925 additions and 2860 deletions

View File

@@ -7,5 +7,5 @@ package consts
// VersionApp HotGo版本
const (
VersionApp = "2.15.11"
VersionApp = "2.16.10"
)

View File

@@ -49,7 +49,7 @@ func Dao(ctx context.Context) (err error) {
inp.ImportPrefix = utils.GetImportPath(inp.Path)
inp.Path = tempPathPrefix + "/" + inp.Path
if err = gfile.Remove(inp.Path); err != nil {
if err = gfile.RemoveAll(inp.Path); err != nil {
err = gerror.Newf("清理临时生成目录失败:%v", err)
return err
}

View File

@@ -4,6 +4,7 @@
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// Package cmd provides the management of CLI commands for `gf` tool.
package cmd
import (
@@ -19,9 +20,8 @@ import (
"hotgo/internal/library/hggen/internal/utility/mlog"
)
var (
GF = cGF{}
)
// GF is the management object for `gf` command line tool.
var GF = cGF{}
type cGF struct {
g.Meta `name:"gf" ad:"{cGFAd}"`

View File

@@ -19,6 +19,7 @@ import (
"github.com/gogf/gf/v2/encoding/gcompress"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gfile"
"hotgo/internal/library/hggen/internal/utility/mlog"
)

View File

@@ -9,13 +9,13 @@ package cmd
import (
"context"
"github.com/gogf/gf/v2/os/gproc"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/errors/gerror"
"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/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
"hotgo/internal/library/hggen/internal/utility/mlog"
)

View File

@@ -8,13 +8,15 @@ package cmd
import (
//_ "github.com/gogf/gf/contrib/drivers/clickhouse/v2"
// _ "github.com/gogf/gf/contrib/drivers/dm/v2" // precompilation does not support certain target platforms.
//_ "github.com/gogf/gf/contrib/drivers/mssql/v2"
_ "github.com/gogf/gf/contrib/drivers/mysql/v2"
//_ "github.com/gogf/gf/contrib/drivers/oracle/v2"
//_ "github.com/gogf/gf/contrib/drivers/pgsql/v2"
//_ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
// do not add dm in cli pre-compilation,
// the dm driver does not support certain target platforms.
// _ "github.com/gogf/gf/contrib/drivers/dm/v2"
"hotgo/internal/library/hggen/internal/cmd/gendao"
)

View File

@@ -19,6 +19,7 @@ import (
"github.com/gogf/gf/v2/os/gres"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gtag"
"hotgo/internal/library/hggen/internal/utility/allyes"
"hotgo/internal/library/hggen/internal/utility/mlog"
"hotgo/internal/library/hggen/internal/utility/utils"

View File

@@ -19,6 +19,7 @@ import (
"github.com/gogf/gf/v2/os/gproc"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gtag"
"hotgo/internal/library/hggen/internal/utility/mlog"
"hotgo/internal/library/hggen/internal/utility/utils"
)

View File

@@ -1,38 +0,0 @@
// 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 cmd
import (
"context"
"fmt"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/test/gtest"
)
var (
ctx = context.Background()
testDB gdb.DB
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test?loc=Local&parseTime=true"
)
func init() {
var err error
testDB, err = gdb.New(gdb.ConfigNode{
Link: link,
})
if err != nil {
panic(err)
}
}
func dropTableWithDb(db gdb.DB, table string) {
dropTableStmt := fmt.Sprintf("DROP TABLE IF EXISTS `%s`", table)
if _, err := db.Exec(ctx, dropTableStmt); err != nil {
gtest.Error(err)
}
}

View File

@@ -1,151 +0,0 @@
// 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 cmd
import (
"testing"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gproc"
"github.com/gogf/gf/v2/test/gtest"
"github.com/gogf/gf/v2/text/gstr"
)
func Test_Build_Single(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
buildPath = gtest.DataPath(`build`, `single`)
pwd = gfile.Pwd()
binaryName = `t.test`
binaryPath = gtest.DataPath(`build`, `single`, binaryName)
f = cBuild{}
)
defer gfile.Chdir(pwd)
defer gfile.Remove(binaryPath)
err := gfile.Chdir(buildPath)
t.AssertNil(err)
t.Assert(gfile.Exists(binaryPath), false)
_, err = f.Index(ctx, cBuildInput{
File: cBuildDefaultFile,
Name: binaryName,
})
t.AssertNil(err)
t.Assert(gfile.Exists(binaryPath), true)
})
}
func Test_Build_Single_Output(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
buildPath = gtest.DataPath(`build`, `single`)
pwd = gfile.Pwd()
binaryName = `tt`
binaryDirPath = gtest.DataPath(`build`, `single`, `tt`)
binaryPath = gtest.DataPath(`build`, `single`, `tt`, binaryName)
f = cBuild{}
)
defer gfile.Chdir(pwd)
defer gfile.Remove(binaryDirPath)
err := gfile.Chdir(buildPath)
t.AssertNil(err)
t.Assert(gfile.Exists(binaryPath), false)
_, err = f.Index(ctx, cBuildInput{
Output: "./tt/tt",
})
t.AssertNil(err)
t.Assert(gfile.Exists(binaryPath), true)
})
}
func Test_Build_Single_Path(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
buildPath = gtest.DataPath(`build`, `single`)
pwd = gfile.Pwd()
dirName = "ttt"
binaryName = `main`
binaryDirPath = gtest.DataPath(`build`, `single`, dirName)
binaryPath = gtest.DataPath(`build`, `single`, dirName, binaryName)
f = cBuild{}
)
defer gfile.Chdir(pwd)
defer gfile.Remove(binaryDirPath)
err := gfile.Chdir(buildPath)
t.AssertNil(err)
t.Assert(gfile.Exists(binaryPath), false)
_, err = f.Index(ctx, cBuildInput{
Path: "ttt",
})
t.AssertNil(err)
t.Assert(gfile.Exists(binaryPath), true)
})
}
func Test_Build_Single_VarMap(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
buildPath = gtest.DataPath(`build`, `varmap`)
pwd = gfile.Pwd()
binaryName = `main`
binaryPath = gtest.DataPath(`build`, `varmap`, binaryName)
f = cBuild{}
)
defer gfile.Chdir(pwd)
defer gfile.Remove(binaryPath)
err := gfile.Chdir(buildPath)
t.AssertNil(err)
t.Assert(gfile.Exists(binaryPath), false)
_, err = f.Index(ctx, cBuildInput{
VarMap: map[string]interface{}{
"a": "1",
"b": "2",
},
})
t.AssertNil(err)
t.Assert(gfile.Exists(binaryPath), true)
result, err := gproc.ShellExec(ctx, binaryPath)
t.AssertNil(err)
t.Assert(gstr.Contains(result, `a: 1`), true)
t.Assert(gstr.Contains(result, `b: 2`), true)
})
}
func Test_Build_Multiple(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
buildPath = gtest.DataPath(`build`, `multiple`)
pwd = gfile.Pwd()
binaryDirPath = gtest.DataPath(`build`, `multiple`, `temp`)
binaryPathLinux = gtest.DataPath(`build`, `multiple`, `temp`, `v1.1`, `linux_amd64`, `ttt`)
binaryPathWindows = gtest.DataPath(`build`, `multiple`, `temp`, `v1.1`, `windows_amd64`, `ttt.exe`)
f = cBuild{}
)
defer gfile.Chdir(pwd)
defer gfile.Remove(binaryDirPath)
err := gfile.Chdir(buildPath)
t.AssertNil(err)
t.Assert(gfile.Exists(binaryPathLinux), false)
t.Assert(gfile.Exists(binaryPathWindows), false)
_, err = f.Index(ctx, cBuildInput{
File: "multiple.go",
Name: "ttt",
Version: "v1.1",
Arch: "amd64",
System: "linux, windows",
Path: "temp",
})
t.AssertNil(err)
t.Assert(gfile.Exists(binaryPathLinux), true)
t.Assert(gfile.Exists(binaryPathWindows), true)
})
}

View File

@@ -1,24 +0,0 @@
// 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 cmd
import (
"testing"
"github.com/gogf/gf/v2/test/gtest"
)
func Test_Fix_doFixV25Content(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
content = gtest.DataContent(`fix`, `fix25_content.go`)
f = cFix{}
)
_, err := f.doFixV25Content(content)
t.AssertNil(err)
})
}

View File

@@ -1,309 +0,0 @@
// 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 cmd
import (
"path/filepath"
"testing"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/test/gtest"
"github.com/gogf/gf/v2/util/guid"
"github.com/gogf/gf/v2/util/gutil"
"hotgo/internal/library/hggen/internal/cmd/genctrl"
)
func Test_Gen_Ctrl_Default(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
path = gfile.Temp(guid.S())
apiFolder = gtest.DataPath("genctrl", "api")
in = genctrl.CGenCtrlInput{
SrcFolder: apiFolder,
DstFolder: path,
WatchFile: "",
SdkPath: "",
SdkStdVersion: false,
SdkNoV1: false,
Clear: false,
Merge: false,
}
)
err := gutil.FillStructWithDefault(&in)
t.AssertNil(err)
err = gfile.Mkdir(path)
t.AssertNil(err)
defer gfile.Remove(path)
_, err = genctrl.CGenCtrl{}.Ctrl(ctx, in)
t.AssertNil(err)
// apiInterface file
var (
genApi = apiFolder + filepath.FromSlash("/article/article.go")
genApiExpect = apiFolder + filepath.FromSlash("/article/article_expect.go")
)
defer gfile.Remove(genApi)
t.Assert(gfile.GetContents(genApi), gfile.GetContents(genApiExpect))
// files
files, err := gfile.ScanDir(path, "*.go", true)
t.AssertNil(err)
t.Assert(files, []string{
path + filepath.FromSlash("/article/article.go"),
path + filepath.FromSlash("/article/article_new.go"),
path + filepath.FromSlash("/article/article_v1_create.go"),
path + filepath.FromSlash("/article/article_v1_get_list.go"),
path + filepath.FromSlash("/article/article_v1_get_one.go"),
path + filepath.FromSlash("/article/article_v1_update.go"),
path + filepath.FromSlash("/article/article_v2_create.go"),
path + filepath.FromSlash("/article/article_v2_update.go"),
})
// content
testPath := gtest.DataPath("genctrl", "controller")
expectFiles := []string{
testPath + filepath.FromSlash("/article/article.go"),
testPath + filepath.FromSlash("/article/article_new.go"),
testPath + filepath.FromSlash("/article/article_v1_create.go"),
testPath + filepath.FromSlash("/article/article_v1_get_list.go"),
testPath + filepath.FromSlash("/article/article_v1_get_one.go"),
testPath + filepath.FromSlash("/article/article_v1_update.go"),
testPath + filepath.FromSlash("/article/article_v2_create.go"),
testPath + filepath.FromSlash("/article/article_v2_update.go"),
}
for i := range files {
t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i]))
}
})
}
func expectFilesContent(t *gtest.T, paths []string, expectPaths []string) {
for i, expectFile := range expectPaths {
val := gfile.GetContents(paths[i])
expect := gfile.GetContents(expectFile)
t.Assert(val, expect)
}
}
// gf gen ctrl -m
// In the same module, different API files are added
func Test_Gen_Ctrl_UseMerge_AddNewFile(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
ctrlPath = gfile.Temp(guid.S())
//ctrlPath = gtest.DataPath("issue", "3460", "controller")
apiFolder = gtest.DataPath("genctrl-merge", "add_new_file", "api")
in = genctrl.CGenCtrlInput{
SrcFolder: apiFolder,
DstFolder: ctrlPath,
Merge: true,
}
)
const testNewApiFile = `
package v1
import "github.com/gogf/gf/v2/frame/g"
type DictTypeAddReq struct {
g.Meta
}
type DictTypeAddRes struct {
}
`
err := gfile.Mkdir(ctrlPath)
t.AssertNil(err)
defer gfile.Remove(ctrlPath)
_, err = genctrl.CGenCtrl{}.Ctrl(ctx, in)
t.AssertNil(err)
var (
genApi = filepath.Join(apiFolder, "/dict/dict.go")
genApiExpect = filepath.Join(apiFolder, "/dict/dict_expect.go")
)
defer gfile.Remove(genApi)
t.Assert(gfile.GetContents(genApi), gfile.GetContents(genApiExpect))
genCtrlFiles, err := gfile.ScanDir(ctrlPath, "*.go", true)
t.AssertNil(err)
t.Assert(genCtrlFiles, []string{
filepath.Join(ctrlPath, "/dict/dict.go"),
filepath.Join(ctrlPath, "/dict/dict_new.go"),
filepath.Join(ctrlPath, "/dict/dict_v1_dict_type.go"),
})
expectCtrlPath := gtest.DataPath("genctrl-merge", "add_new_file", "controller")
expectFiles := []string{
filepath.Join(expectCtrlPath, "/dict/dict.go"),
filepath.Join(expectCtrlPath, "/dict/dict_new.go"),
filepath.Join(expectCtrlPath, "/dict/dict_v1_dict_type.go"),
}
// Line Feed maybe \r\n or \n
expectFilesContent(t, genCtrlFiles, expectFiles)
// Add a new API file
newApiFilePath := filepath.Join(apiFolder, "/dict/v1/test_new.go")
err = gfile.PutContents(newApiFilePath, testNewApiFile)
t.AssertNil(err)
defer gfile.Remove(newApiFilePath)
// Then execute the command
_, err = genctrl.CGenCtrl{}.Ctrl(ctx, in)
t.AssertNil(err)
genApi = filepath.Join(apiFolder, "/dict.go")
genApiExpect = filepath.Join(apiFolder, "/dict_add_new_ctrl_expect.gotest")
t.Assert(gfile.GetContents(genApi), gfile.GetContents(genApiExpect))
genCtrlFiles = append(genCtrlFiles, filepath.Join(ctrlPath, "/dict/dict_v1_test_new.go"))
// Use the gotest suffix, otherwise the IDE will delete the import
expectFiles = append(expectFiles, filepath.Join(expectCtrlPath, "/dict/dict_v1_test_new.gotest"))
// Line Feed maybe \r\n or \n
expectFilesContent(t, genCtrlFiles, expectFiles)
})
}
// gf gen ctrl -m
// In the same module, Add the same file to the API
func Test_Gen_Ctrl_UseMerge_AddNewCtrl(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
ctrlPath = gfile.Temp(guid.S())
//ctrlPath = gtest.DataPath("issue", "3460", "controller")
apiFolder = gtest.DataPath("genctrl-merge", "add_new_ctrl", "api")
in = genctrl.CGenCtrlInput{
SrcFolder: apiFolder,
DstFolder: ctrlPath,
Merge: true,
}
)
err := gfile.Mkdir(ctrlPath)
t.AssertNil(err)
defer gfile.Remove(ctrlPath)
_, err = genctrl.CGenCtrl{}.Ctrl(ctx, in)
t.AssertNil(err)
var (
genApi = filepath.Join(apiFolder, "/dict/dict.go")
genApiExpect = filepath.Join(apiFolder, "/dict/dict_expect.go")
)
defer gfile.Remove(genApi)
t.Assert(gfile.GetContents(genApi), gfile.GetContents(genApiExpect))
genCtrlFiles, err := gfile.ScanDir(ctrlPath, "*.go", true)
t.AssertNil(err)
t.Assert(genCtrlFiles, []string{
filepath.Join(ctrlPath, "/dict/dict.go"),
filepath.Join(ctrlPath, "/dict/dict_new.go"),
filepath.Join(ctrlPath, "/dict/dict_v1_dict_type.go"),
})
expectCtrlPath := gtest.DataPath("genctrl-merge", "add_new_ctrl", "controller")
expectFiles := []string{
filepath.Join(expectCtrlPath, "/dict/dict.go"),
filepath.Join(expectCtrlPath, "/dict/dict_new.go"),
filepath.Join(expectCtrlPath, "/dict/dict_v1_dict_type.go"),
}
// Line Feed maybe \r\n or \n
expectFilesContent(t, genCtrlFiles, expectFiles)
const testNewApiFile = `
type DictTypeAddReq struct {
g.Meta
}
type DictTypeAddRes struct {
}
`
dictModuleFileName := filepath.Join(apiFolder, "/dict/v1/dict_type.go")
// Save the contents of the file before the changes
apiFileContents := gfile.GetContents(dictModuleFileName)
// Add a new API file
err = gfile.PutContentsAppend(dictModuleFileName, testNewApiFile)
t.AssertNil(err)
//==================================
// Then execute the command
_, err = genctrl.CGenCtrl{}.Ctrl(ctx, in)
t.AssertNil(err)
genApi = filepath.Join(apiFolder, "/dict.go")
genApiExpect = filepath.Join(apiFolder, "/dict_add_new_ctrl_expect.gotest")
t.Assert(gfile.GetContents(genApi), gfile.GetContents(genApiExpect))
// Use the gotest suffix, otherwise the IDE will delete the import
expectFiles[2] = filepath.Join(expectCtrlPath, "/dict/dict_v1_test_new.gotest")
// Line Feed maybe \r\n or \n
expectFilesContent(t, genCtrlFiles, expectFiles)
// Restore the contents of the original API file
err = gfile.PutContents(dictModuleFileName, apiFileContents)
t.AssertNil(err)
})
}
// https://github.com/gogf/gf/issues/3460
func Test_Gen_Ctrl_UseMerge_Issue3460(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
ctrlPath = gfile.Temp(guid.S())
//ctrlPath = gtest.DataPath("issue", "3460", "controller")
apiFolder = gtest.DataPath("issue", "3460", "api")
in = genctrl.CGenCtrlInput{
SrcFolder: apiFolder,
DstFolder: ctrlPath,
WatchFile: "",
SdkPath: "",
SdkStdVersion: false,
SdkNoV1: false,
Clear: false,
Merge: true,
}
)
err := gfile.Mkdir(ctrlPath)
t.AssertNil(err)
defer gfile.Remove(ctrlPath)
_, err = genctrl.CGenCtrl{}.Ctrl(ctx, in)
t.AssertNil(err)
files, err := gfile.ScanDir(ctrlPath, "*.go", true)
t.AssertNil(err)
t.Assert(files, []string{
filepath.Join(ctrlPath, "/hello/hello.go"),
filepath.Join(ctrlPath, "/hello/hello_new.go"),
filepath.Join(ctrlPath, "/hello/hello_v1_req.go"),
filepath.Join(ctrlPath, "/hello/hello_v2_req.go"),
})
expectCtrlPath := gtest.DataPath("issue", "3460", "controller")
expectFiles := []string{
filepath.Join(expectCtrlPath, "/hello/hello.go"),
filepath.Join(expectCtrlPath, "/hello/hello_new.go"),
filepath.Join(expectCtrlPath, "/hello/hello_v1_req.go"),
filepath.Join(expectCtrlPath, "/hello/hello_v2_req.go"),
}
// Line Feed maybe \r\n or \n
for i, expectFile := range expectFiles {
val := gfile.GetContents(files[i])
expect := gfile.GetContents(expectFile)
t.Assert(val, expect)
}
})
}

View File

@@ -1,841 +0,0 @@
// 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 cmd
import (
"fmt"
"path/filepath"
"testing"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcfg"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/test/gtest"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/guid"
"github.com/gogf/gf/v2/util/gutil"
"hotgo/internal/library/hggen/internal/cmd/gendao"
)
func Test_Gen_Dao_Default(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
err error
db = testDB
table = "table_user"
sqlContent = fmt.Sprintf(
gtest.DataContent(`gendao`, `user.tpl.sql`),
table,
)
)
dropTableWithDb(db, table)
array := gstr.SplitAndTrim(sqlContent, ";")
for _, v := range array {
if _, err = db.Exec(ctx, v); err != nil {
t.AssertNil(err)
}
}
defer dropTableWithDb(db, table)
var (
path = gfile.Temp(guid.S())
group = "test"
in = gendao.CGenDaoInput{
Path: path,
Link: link,
Tables: "",
TablesEx: "",
Group: group,
Prefix: "",
RemovePrefix: "",
JsonCase: "SnakeScreaming",
ImportPrefix: "",
DaoPath: "",
DoPath: "",
EntityPath: "",
TplDaoIndexPath: "",
TplDaoInternalPath: "",
TplDaoDoPath: "",
TplDaoEntityPath: "",
StdTime: false,
WithTime: false,
GJsonSupport: false,
OverwriteDao: false,
DescriptionTag: false,
NoJsonTag: false,
NoModelComment: false,
Clear: false,
TypeMapping: nil,
FieldMapping: nil,
}
)
err = gutil.FillStructWithDefault(&in)
t.AssertNil(err)
err = gfile.Mkdir(path)
t.AssertNil(err)
// for go mod import path auto retrieve.
err = gfile.Copy(
gtest.DataPath("gendao", "go.mod.txt"),
gfile.Join(path, "go.mod"),
)
t.AssertNil(err)
_, err = gendao.CGenDao{}.Dao(ctx, in)
t.AssertNil(err)
defer gfile.Remove(path)
// files
files, err := gfile.ScanDir(path, "*.go", true)
t.AssertNil(err)
t.Assert(files, []string{
filepath.FromSlash(path + "/dao/internal/table_user.go"),
filepath.FromSlash(path + "/dao/table_user.go"),
filepath.FromSlash(path + "/model/do/table_user.go"),
filepath.FromSlash(path + "/model/entity/table_user.go"),
})
// content
testPath := gtest.DataPath("gendao", "generated_user")
expectFiles := []string{
filepath.FromSlash(testPath + "/dao/internal/table_user.go"),
filepath.FromSlash(testPath + "/dao/table_user.go"),
filepath.FromSlash(testPath + "/model/do/table_user.go"),
filepath.FromSlash(testPath + "/model/entity/table_user.go"),
}
for i, _ := range files {
t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i]))
}
})
}
func Test_Gen_Dao_TypeMapping(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
err error
db = testDB
table = "table_user"
sqlContent = fmt.Sprintf(
gtest.DataContent(`gendao`, `user.tpl.sql`),
table,
)
)
defer dropTableWithDb(db, table)
array := gstr.SplitAndTrim(sqlContent, ";")
for _, v := range array {
if _, err = db.Exec(ctx, v); err != nil {
t.AssertNil(err)
}
}
defer dropTableWithDb(db, table)
var (
path = gfile.Temp(guid.S())
group = "test"
in = gendao.CGenDaoInput{
Path: path,
Link: link,
Tables: "",
TablesEx: "",
Group: group,
Prefix: "",
RemovePrefix: "",
JsonCase: "",
ImportPrefix: "",
DaoPath: "",
DoPath: "",
EntityPath: "",
TplDaoIndexPath: "",
TplDaoInternalPath: "",
TplDaoDoPath: "",
TplDaoEntityPath: "",
StdTime: false,
WithTime: false,
GJsonSupport: false,
OverwriteDao: false,
DescriptionTag: false,
NoJsonTag: false,
NoModelComment: false,
Clear: false,
TypeMapping: map[gendao.DBFieldTypeName]gendao.CustomAttributeType{
"int": {
Type: "int64",
Import: "",
},
"decimal": {
Type: "decimal.Decimal",
Import: "github.com/shopspring/decimal",
},
},
FieldMapping: nil,
}
)
err = gutil.FillStructWithDefault(&in)
t.AssertNil(err)
err = gfile.Mkdir(path)
t.AssertNil(err)
// for go mod import path auto retrieve.
err = gfile.Copy(
gtest.DataPath("gendao", "go.mod.txt"),
gfile.Join(path, "go.mod"),
)
t.AssertNil(err)
_, err = gendao.CGenDao{}.Dao(ctx, in)
t.AssertNil(err)
defer gfile.Remove(path)
// files
files, err := gfile.ScanDir(path, "*.go", true)
t.AssertNil(err)
t.Assert(files, []string{
filepath.FromSlash(path + "/dao/internal/table_user.go"),
filepath.FromSlash(path + "/dao/table_user.go"),
filepath.FromSlash(path + "/model/do/table_user.go"),
filepath.FromSlash(path + "/model/entity/table_user.go"),
})
// content
testPath := gtest.DataPath("gendao", "generated_user_type_mapping")
expectFiles := []string{
filepath.FromSlash(testPath + "/dao/internal/table_user.go"),
filepath.FromSlash(testPath + "/dao/table_user.go"),
filepath.FromSlash(testPath + "/model/do/table_user.go"),
filepath.FromSlash(testPath + "/model/entity/table_user.go"),
}
for i, _ := range files {
t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i]))
}
})
}
func Test_Gen_Dao_FieldMapping(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
err error
db = testDB
table = "table_user"
sqlContent = fmt.Sprintf(
gtest.DataContent(`gendao`, `user.tpl.sql`),
table,
)
)
defer dropTableWithDb(db, table)
array := gstr.SplitAndTrim(sqlContent, ";")
for _, v := range array {
if _, err = db.Exec(ctx, v); err != nil {
t.AssertNil(err)
}
}
defer dropTableWithDb(db, table)
var (
path = gfile.Temp(guid.S())
group = "test"
in = gendao.CGenDaoInput{
Path: path,
Link: link,
Tables: "",
TablesEx: "",
Group: group,
Prefix: "",
RemovePrefix: "",
JsonCase: "",
ImportPrefix: "",
DaoPath: "",
DoPath: "",
EntityPath: "",
TplDaoIndexPath: "",
TplDaoInternalPath: "",
TplDaoDoPath: "",
TplDaoEntityPath: "",
StdTime: false,
WithTime: false,
GJsonSupport: false,
OverwriteDao: false,
DescriptionTag: false,
NoJsonTag: false,
NoModelComment: false,
Clear: false,
TypeMapping: map[gendao.DBFieldTypeName]gendao.CustomAttributeType{
"int": {
Type: "int64",
Import: "",
},
},
FieldMapping: map[gendao.DBTableFieldName]gendao.CustomAttributeType{
"table_user.score": {
Type: "decimal.Decimal",
Import: "github.com/shopspring/decimal",
},
},
}
)
err = gutil.FillStructWithDefault(&in)
t.AssertNil(err)
err = gfile.Mkdir(path)
t.AssertNil(err)
// for go mod import path auto retrieve.
err = gfile.Copy(
gtest.DataPath("gendao", "go.mod.txt"),
gfile.Join(path, "go.mod"),
)
t.AssertNil(err)
_, err = gendao.CGenDao{}.Dao(ctx, in)
t.AssertNil(err)
defer gfile.Remove(path)
// files
files, err := gfile.ScanDir(path, "*.go", true)
t.AssertNil(err)
t.Assert(files, []string{
filepath.FromSlash(path + "/dao/internal/table_user.go"),
filepath.FromSlash(path + "/dao/table_user.go"),
filepath.FromSlash(path + "/model/do/table_user.go"),
filepath.FromSlash(path + "/model/entity/table_user.go"),
})
// content
testPath := gtest.DataPath("gendao", "generated_user_field_mapping")
expectFiles := []string{
filepath.FromSlash(testPath + "/dao/internal/table_user.go"),
filepath.FromSlash(testPath + "/dao/table_user.go"),
filepath.FromSlash(testPath + "/model/do/table_user.go"),
filepath.FromSlash(testPath + "/model/entity/table_user.go"),
}
for i, _ := range files {
t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i]))
}
})
}
func execSqlFile(db gdb.DB, filePath string, args ...any) error {
sqlContent := fmt.Sprintf(
gfile.GetContents(filePath),
args...,
)
array := gstr.SplitAndTrim(sqlContent, ";")
for _, v := range array {
if _, err := db.Exec(ctx, v); err != nil {
return err
}
}
return nil
}
// https://github.com/gogf/gf/issues/2572
func Test_Gen_Dao_Issue2572(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
err error
db = testDB
table1 = "user1"
table2 = "user2"
issueDirPath = gtest.DataPath(`issue`, `2572`)
)
t.AssertNil(execSqlFile(db, gtest.DataPath(`issue`, `2572`, `sql1.sql`)))
t.AssertNil(execSqlFile(db, gtest.DataPath(`issue`, `2572`, `sql2.sql`)))
defer dropTableWithDb(db, table1)
defer dropTableWithDb(db, table2)
var (
path = gfile.Temp(guid.S())
group = "test"
in = gendao.CGenDaoInput{
Path: path,
Link: "",
Tables: "",
TablesEx: "",
Group: group,
Prefix: "",
RemovePrefix: "",
JsonCase: "SnakeScreaming",
ImportPrefix: "",
DaoPath: "",
DoPath: "",
EntityPath: "",
TplDaoIndexPath: "",
TplDaoInternalPath: "",
TplDaoDoPath: "",
TplDaoEntityPath: "",
StdTime: false,
WithTime: false,
GJsonSupport: false,
OverwriteDao: false,
DescriptionTag: false,
NoJsonTag: false,
NoModelComment: false,
Clear: false,
TypeMapping: nil,
FieldMapping: nil,
}
)
err = gutil.FillStructWithDefault(&in)
t.AssertNil(err)
err = gfile.Copy(issueDirPath, path)
t.AssertNil(err)
defer gfile.Remove(path)
pwd := gfile.Pwd()
err = gfile.Chdir(path)
t.AssertNil(err)
defer gfile.Chdir(pwd)
_, err = gendao.CGenDao{}.Dao(ctx, in)
t.AssertNil(err)
generatedFiles, err := gfile.ScanDir(path, "*.go", true)
t.AssertNil(err)
t.Assert(len(generatedFiles), 8)
for i, generatedFile := range generatedFiles {
generatedFiles[i] = gstr.TrimLeftStr(generatedFile, path)
}
t.Assert(gstr.InArray(generatedFiles,
filepath.FromSlash("/dao/internal/user_1.go")), true)
t.Assert(gstr.InArray(generatedFiles,
filepath.FromSlash("/dao/internal/user_2.go")), true)
t.Assert(gstr.InArray(generatedFiles,
filepath.FromSlash("/dao/user_1.go")), true)
t.Assert(gstr.InArray(generatedFiles,
filepath.FromSlash("/dao/user_2.go")), true)
t.Assert(gstr.InArray(generatedFiles,
filepath.FromSlash("/model/do/user_1.go")), true)
t.Assert(gstr.InArray(generatedFiles,
filepath.FromSlash("/model/do/user_2.go")), true)
t.Assert(gstr.InArray(generatedFiles,
filepath.FromSlash("/model/entity/user_1.go")), true)
t.Assert(gstr.InArray(generatedFiles,
filepath.FromSlash("/model/entity/user_2.go")), true)
})
}
// https://github.com/gogf/gf/issues/2616
func Test_Gen_Dao_Issue2616(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
err error
db = testDB
table1 = "user1"
table2 = "user2"
issueDirPath = gtest.DataPath(`issue`, `2616`)
)
t.AssertNil(execSqlFile(db, gtest.DataPath(`issue`, `2616`, `sql1.sql`)))
t.AssertNil(execSqlFile(db, gtest.DataPath(`issue`, `2616`, `sql2.sql`)))
defer dropTableWithDb(db, table1)
defer dropTableWithDb(db, table2)
var (
path = gfile.Temp(guid.S())
group = "test"
in = gendao.CGenDaoInput{
Path: path,
Link: "",
Tables: "",
TablesEx: "",
Group: group,
Prefix: "",
RemovePrefix: "",
JsonCase: "SnakeScreaming",
ImportPrefix: "",
DaoPath: "",
DoPath: "",
EntityPath: "",
TplDaoIndexPath: "",
TplDaoInternalPath: "",
TplDaoDoPath: "",
TplDaoEntityPath: "",
StdTime: false,
WithTime: false,
GJsonSupport: false,
OverwriteDao: false,
DescriptionTag: false,
NoJsonTag: false,
NoModelComment: false,
Clear: false,
TypeMapping: nil,
FieldMapping: nil,
}
)
err = gutil.FillStructWithDefault(&in)
t.AssertNil(err)
err = gfile.Copy(issueDirPath, path)
t.AssertNil(err)
defer gfile.Remove(path)
pwd := gfile.Pwd()
err = gfile.Chdir(path)
t.AssertNil(err)
defer gfile.Chdir(pwd)
_, err = gendao.CGenDao{}.Dao(ctx, in)
t.AssertNil(err)
generatedFiles, err := gfile.ScanDir(path, "*.go", true)
t.AssertNil(err)
t.Assert(len(generatedFiles), 8)
for i, generatedFile := range generatedFiles {
generatedFiles[i] = gstr.TrimLeftStr(generatedFile, path)
}
t.Assert(gstr.InArray(generatedFiles,
filepath.FromSlash("/dao/internal/user_1.go")), true)
t.Assert(gstr.InArray(generatedFiles,
filepath.FromSlash("/dao/internal/user_2.go")), true)
t.Assert(gstr.InArray(generatedFiles,
filepath.FromSlash("/dao/user_1.go")), true)
t.Assert(gstr.InArray(generatedFiles,
filepath.FromSlash("/dao/user_2.go")), true)
t.Assert(gstr.InArray(generatedFiles,
filepath.FromSlash("/model/do/user_1.go")), true)
t.Assert(gstr.InArray(generatedFiles,
filepath.FromSlash("/model/do/user_2.go")), true)
t.Assert(gstr.InArray(generatedFiles,
filepath.FromSlash("/model/entity/user_1.go")), true)
t.Assert(gstr.InArray(generatedFiles,
filepath.FromSlash("/model/entity/user_2.go")), true)
// Key string to check if overwrite the dao files.
// dao user1 is not be overwritten as configured in config.yaml.
// dao user2 is to be overwritten as configured in config.yaml.
var (
keyStr = `// I am not overwritten.`
daoUser1Content = gfile.GetContents(path + "/dao/user_1.go")
daoUser2Content = gfile.GetContents(path + "/dao/user_2.go")
)
t.Assert(gstr.Contains(daoUser1Content, keyStr), true)
t.Assert(gstr.Contains(daoUser2Content, keyStr), false)
})
}
// https://github.com/gogf/gf/issues/2746
func Test_Gen_Dao_Issue2746(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
err error
mdb gdb.DB
link2746 = "mariadb:root:12345678@tcp(127.0.0.1:3307)/test?loc=Local&parseTime=true"
table = "issue2746"
sqlContent = fmt.Sprintf(
gtest.DataContent(`issue`, `2746`, `sql.sql`),
table,
)
)
mdb, err = gdb.New(gdb.ConfigNode{
Link: link2746,
})
t.AssertNil(err)
array := gstr.SplitAndTrim(sqlContent, ";")
for _, v := range array {
if _, err = mdb.Exec(ctx, v); err != nil {
t.AssertNil(err)
}
}
defer dropTableWithDb(mdb, table)
var (
path = gfile.Temp(guid.S())
group = "test"
in = gendao.CGenDaoInput{
Path: path,
Link: link2746,
Tables: "",
TablesEx: "",
Group: group,
Prefix: "",
RemovePrefix: "",
JsonCase: "SnakeScreaming",
ImportPrefix: "",
DaoPath: "",
DoPath: "",
EntityPath: "",
TplDaoIndexPath: "",
TplDaoInternalPath: "",
TplDaoDoPath: "",
TplDaoEntityPath: "",
StdTime: false,
WithTime: false,
GJsonSupport: true,
OverwriteDao: false,
DescriptionTag: false,
NoJsonTag: false,
NoModelComment: false,
Clear: false,
TypeMapping: nil,
FieldMapping: nil,
}
)
err = gutil.FillStructWithDefault(&in)
t.AssertNil(err)
err = gfile.Mkdir(path)
t.AssertNil(err)
_, err = gendao.CGenDao{}.Dao(ctx, in)
t.AssertNil(err)
defer gfile.Remove(path)
var (
file = filepath.FromSlash(path + "/model/entity/issue_2746.go")
expectContent = gtest.DataContent(`issue`, `2746`, `issue_2746.go`)
)
t.Assert(expectContent, gfile.GetContents(file))
})
}
// https://github.com/gogf/gf/issues/3459
func Test_Gen_Dao_Issue3459(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
err error
db = testDB
table = "table_user"
sqlContent = fmt.Sprintf(
gtest.DataContent(`gendao`, `user.tpl.sql`),
table,
)
)
dropTableWithDb(db, table)
array := gstr.SplitAndTrim(sqlContent, ";")
for _, v := range array {
if _, err = db.Exec(ctx, v); err != nil {
t.AssertNil(err)
}
}
defer dropTableWithDb(db, table)
var (
confDir = gtest.DataPath("issue", "3459")
path = gfile.Temp(guid.S())
group = "test"
in = gendao.CGenDaoInput{
Path: path,
Link: link,
Tables: "",
TablesEx: "",
Group: group,
Prefix: "",
RemovePrefix: "",
JsonCase: "SnakeScreaming",
ImportPrefix: "",
DaoPath: "",
DoPath: "",
EntityPath: "",
TplDaoIndexPath: "",
TplDaoInternalPath: "",
TplDaoDoPath: "",
TplDaoEntityPath: "",
StdTime: false,
WithTime: false,
GJsonSupport: false,
OverwriteDao: false,
DescriptionTag: false,
NoJsonTag: false,
NoModelComment: false,
Clear: false,
TypeMapping: nil,
}
)
err = g.Cfg().GetAdapter().(*gcfg.AdapterFile).SetPath(confDir)
t.AssertNil(err)
err = gutil.FillStructWithDefault(&in)
t.AssertNil(err)
err = gfile.Mkdir(path)
t.AssertNil(err)
// for go mod import path auto retrieve.
err = gfile.Copy(
gtest.DataPath("gendao", "go.mod.txt"),
gfile.Join(path, "go.mod"),
)
t.AssertNil(err)
_, err = gendao.CGenDao{}.Dao(ctx, in)
t.AssertNil(err)
defer gfile.Remove(path)
// files
files, err := gfile.ScanDir(path, "*.go", true)
t.AssertNil(err)
t.Assert(files, []string{
filepath.FromSlash(path + "/dao/internal/table_user.go"),
filepath.FromSlash(path + "/dao/table_user.go"),
filepath.FromSlash(path + "/model/do/table_user.go"),
filepath.FromSlash(path + "/model/entity/table_user.go"),
})
// content
testPath := gtest.DataPath("gendao", "generated_user")
expectFiles := []string{
filepath.FromSlash(testPath + "/dao/internal/table_user.go"),
filepath.FromSlash(testPath + "/dao/table_user.go"),
filepath.FromSlash(testPath + "/model/do/table_user.go"),
filepath.FromSlash(testPath + "/model/entity/table_user.go"),
}
for i, _ := range files {
t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i]))
}
})
}
// https://github.com/gogf/gf/issues/3749
func Test_Gen_Dao_Issue3749(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
err error
db = testDB
table = "table_user"
sqlContent = fmt.Sprintf(
gtest.DataContent(`issue`, `3749`, `user.tpl.sql`),
table,
)
)
dropTableWithDb(db, table)
array := gstr.SplitAndTrim(sqlContent, ";")
for _, v := range array {
if _, err = db.Exec(ctx, v); err != nil {
t.AssertNil(err)
}
}
defer dropTableWithDb(db, table)
var (
path = gfile.Temp(guid.S())
group = "test"
in = gendao.CGenDaoInput{
Path: path,
Link: link,
Group: group,
}
)
err = gutil.FillStructWithDefault(&in)
t.AssertNil(err)
err = gfile.Mkdir(path)
t.AssertNil(err)
// for go mod import path auto retrieve.
err = gfile.Copy(
gtest.DataPath("gendao", "go.mod.txt"),
gfile.Join(path, "go.mod"),
)
t.AssertNil(err)
_, err = gendao.CGenDao{}.Dao(ctx, in)
t.AssertNil(err)
defer gfile.Remove(path)
// files
files, err := gfile.ScanDir(path, "*.go", true)
t.AssertNil(err)
t.Assert(files, []string{
filepath.FromSlash(path + "/dao/internal/table_user.go"),
filepath.FromSlash(path + "/dao/table_user.go"),
filepath.FromSlash(path + "/model/do/table_user.go"),
filepath.FromSlash(path + "/model/entity/table_user.go"),
})
// content
testPath := gtest.DataPath(`issue`, `3749`)
expectFiles := []string{
filepath.FromSlash(testPath + "/dao/internal/table_user.go"),
filepath.FromSlash(testPath + "/dao/table_user.go"),
filepath.FromSlash(testPath + "/model/do/table_user.go"),
filepath.FromSlash(testPath + "/model/entity/table_user.go"),
}
for i, _ := range files {
t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i]))
}
})
}
func Test_Gen_Dao_Sqlite3(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
err error
table = "table_user"
path = gfile.Temp(guid.S())
linkSqlite3 = fmt.Sprintf("sqlite::@file(%s/db.sqlite3)", path)
sqlContent = fmt.Sprintf(
gtest.DataContent(`gendao`, `sqlite3`, `user.sqlite3.sql`),
table,
)
)
err = gfile.Mkdir(path)
t.AssertNil(err)
defer gfile.Remove(path)
dbSqlite3, err := gdb.New(gdb.ConfigNode{
Link: linkSqlite3,
})
t.AssertNil(err)
array := gstr.SplitAndTrim(sqlContent, ";")
for _, v := range array {
if v == "" {
continue
}
if _, err = dbSqlite3.Exec(ctx, v); err != nil {
t.AssertNil(err)
}
}
var (
group = "test"
in = gendao.CGenDaoInput{
Path: path,
Link: linkSqlite3,
Group: group,
Tables: table,
}
)
err = gutil.FillStructWithDefault(&in)
t.AssertNil(err)
// for go mod import path auto retrieve.
err = gfile.Copy(
gtest.DataPath("gendao", "go.mod.txt"),
gfile.Join(path, "go.mod"),
)
t.AssertNil(err)
_, err = gendao.CGenDao{}.Dao(ctx, in)
t.AssertNil(err)
defer gfile.Remove(path)
// files
files, err := gfile.ScanDir(path, "*.go", true)
t.AssertNil(err)
t.Assert(files, []string{
filepath.FromSlash(path + "/dao/internal/table_user.go"),
filepath.FromSlash(path + "/dao/table_user.go"),
filepath.FromSlash(path + "/model/do/table_user.go"),
filepath.FromSlash(path + "/model/entity/table_user.go"),
})
// content
testPath := gtest.DataPath("gendao", "generated_user_sqlite3")
expectFiles := []string{
filepath.FromSlash(testPath + "/dao/internal/table_user.go"),
filepath.FromSlash(testPath + "/dao/table_user.go"),
filepath.FromSlash(testPath + "/model/do/table_user.go"),
filepath.FromSlash(testPath + "/model/entity/table_user.go"),
}
for i, _ := range files {
t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i]))
}
})
}

View File

@@ -1,50 +0,0 @@
// 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 cmd
import (
"path/filepath"
"testing"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/test/gtest"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/guid"
"hotgo/internal/library/hggen/internal/cmd/genpb"
)
func TestGenPbIssue3882(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
outputPath = gfile.Temp(guid.S())
outputApiPath = filepath.Join(outputPath, "api")
outputCtrlPath = filepath.Join(outputPath, "controller")
protobufFolder = gtest.DataPath("issue", "3882")
in = genpb.CGenPbInput{
Path: protobufFolder,
OutputApi: outputApiPath,
OutputCtrl: outputCtrlPath,
}
err error
)
err = gfile.Mkdir(outputApiPath)
t.AssertNil(err)
err = gfile.Mkdir(outputCtrlPath)
t.AssertNil(err)
defer gfile.Remove(outputPath)
_, err = genpb.CGenPb{}.Pb(ctx, in)
t.AssertNil(err)
var (
genContent = gfile.GetContents(filepath.Join(outputApiPath, "issue3882.pb.go"))
exceptText = `dc:"Some comment on field with 'one' 'two' 'three' in the comment."`
)
t.Assert(gstr.Contains(genContent, exceptText), true)
})
}

View File

@@ -1,210 +0,0 @@
// 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 cmd
import (
"fmt"
"path/filepath"
"testing"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/test/gtest"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/guid"
"github.com/gogf/gf/v2/util/gutil"
"hotgo/internal/library/hggen/internal/cmd/genpbentity"
)
func Test_Gen_Pbentity_Default(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
err error
db = testDB
table = "table_user"
sqlContent = fmt.Sprintf(
gtest.DataContent(`genpbentity`, `user.tpl.sql`),
table,
)
)
dropTableWithDb(db, table)
array := gstr.SplitAndTrim(sqlContent, ";")
for _, v := range array {
if _, err = db.Exec(ctx, v); err != nil {
t.AssertNil(err)
}
}
defer dropTableWithDb(db, table)
var (
path = gfile.Temp(guid.S())
in = genpbentity.CGenPbEntityInput{
Path: path,
Package: "unittest",
Link: link,
Tables: "",
Prefix: "",
RemovePrefix: "",
RemoveFieldPrefix: "",
NameCase: "",
JsonCase: "",
Option: "",
}
)
err = gutil.FillStructWithDefault(&in)
t.AssertNil(err)
err = gfile.Mkdir(path)
t.AssertNil(err)
defer gfile.Remove(path)
_, err = genpbentity.CGenPbEntity{}.PbEntity(ctx, in)
t.AssertNil(err)
// files
files, err := gfile.ScanDir(path, "*.proto", false)
t.AssertNil(err)
t.Assert(files, []string{
path + filepath.FromSlash("/table_user.proto"),
})
// contents
testPath := gtest.DataPath("genpbentity", "generated")
expectFiles := []string{
testPath + filepath.FromSlash("/table_user.proto"),
}
for i := range files {
t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i]))
}
})
}
func Test_Gen_Pbentity_NameCase_SnakeScreaming(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
err error
db = testDB
table = "table_user"
sqlContent = fmt.Sprintf(
gtest.DataContent(`genpbentity`, `user.tpl.sql`),
table,
)
)
dropTableWithDb(db, table)
array := gstr.SplitAndTrim(sqlContent, ";")
for _, v := range array {
if _, err = db.Exec(ctx, v); err != nil {
t.AssertNil(err)
}
}
defer dropTableWithDb(db, table)
var (
path = gfile.Temp(guid.S())
in = genpbentity.CGenPbEntityInput{
Path: path,
Package: "unittest",
Link: link,
Tables: "",
Prefix: "",
RemovePrefix: "",
RemoveFieldPrefix: "",
NameCase: "SnakeScreaming",
JsonCase: "",
Option: "",
}
)
err = gutil.FillStructWithDefault(&in)
t.AssertNil(err)
err = gfile.Mkdir(path)
t.AssertNil(err)
defer gfile.Remove(path)
_, err = genpbentity.CGenPbEntity{}.PbEntity(ctx, in)
t.AssertNil(err)
// files
files, err := gfile.ScanDir(path, "*.proto", false)
t.AssertNil(err)
t.Assert(files, []string{
path + filepath.FromSlash("/table_user.proto"),
})
// contents
testPath := gtest.DataPath("genpbentity", "generated")
expectFiles := []string{
testPath + filepath.FromSlash("/table_user_snake_screaming.proto"),
}
for i := range files {
t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i]))
}
})
}
// https://github.com/gogf/gf/issues/3545
func Test_Issue_3545(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
err error
db = testDB
table = "table_user"
sqlContent = fmt.Sprintf(
gtest.DataContent(`genpbentity`, `user.tpl.sql`),
table,
)
)
dropTableWithDb(db, table)
array := gstr.SplitAndTrim(sqlContent, ";")
for _, v := range array {
if _, err = db.Exec(ctx, v); err != nil {
t.AssertNil(err)
}
}
defer dropTableWithDb(db, table)
var (
path = gfile.Temp(guid.S())
in = genpbentity.CGenPbEntityInput{
Path: path,
Package: "",
Link: link,
Tables: "",
Prefix: "",
RemovePrefix: "",
RemoveFieldPrefix: "",
NameCase: "",
JsonCase: "",
Option: "",
}
)
err = gutil.FillStructWithDefault(&in)
t.AssertNil(err)
err = gfile.Mkdir(path)
t.AssertNil(err)
defer gfile.Remove(path)
_, err = genpbentity.CGenPbEntity{}.PbEntity(ctx, in)
t.AssertNil(err)
// files
files, err := gfile.ScanDir(path, "*.proto", false)
t.AssertNil(err)
t.Assert(files, []string{
path + filepath.FromSlash("/table_user.proto"),
})
// contents
testPath := gtest.DataPath("issue", "3545")
expectFiles := []string{
testPath + filepath.FromSlash("/table_user.proto"),
}
for i := range files {
t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i]))
}
})
}

View File

@@ -1,155 +0,0 @@
// 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 cmd
import (
"path/filepath"
"testing"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/test/gtest"
"github.com/gogf/gf/v2/util/guid"
"github.com/gogf/gf/v2/util/gutil"
"hotgo/internal/library/hggen/internal/cmd/genservice"
)
func Test_Gen_Service_Default(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
path = gfile.Temp(guid.S())
dstFolder = path + filepath.FromSlash("/service")
srvFolder = gtest.DataPath("genservice", "logic")
in = genservice.CGenServiceInput{
SrcFolder: srvFolder,
DstFolder: dstFolder,
DstFileNameCase: "Snake",
WatchFile: "",
StPattern: "",
Packages: nil,
ImportPrefix: "",
Clear: false,
}
)
err := gutil.FillStructWithDefault(&in)
t.AssertNil(err)
err = gfile.Mkdir(path)
t.AssertNil(err)
defer gfile.Remove(path)
_, err = genservice.CGenService{}.Service(ctx, in)
t.AssertNil(err)
// logic file
var (
genSrv = srvFolder + filepath.FromSlash("/logic.go")
genSrvExpect = srvFolder + filepath.FromSlash("/logic_expect.go")
)
defer gfile.Remove(genSrv)
t.Assert(gfile.GetContents(genSrv), gfile.GetContents(genSrvExpect))
// files
files, err := gfile.ScanDir(dstFolder, "*.go", true)
t.AssertNil(err)
t.Assert(files, []string{
dstFolder + filepath.FromSlash("/article.go"),
dstFolder + filepath.FromSlash("/delivery.go"),
dstFolder + filepath.FromSlash("/user.go"),
})
// contents
testPath := gtest.DataPath("genservice", "service")
expectFiles := []string{
testPath + filepath.FromSlash("/article.go"),
testPath + filepath.FromSlash("/delivery.go"),
testPath + filepath.FromSlash("/user.go"),
}
for i := range files {
t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i]))
}
})
}
// https://github.com/gogf/gf/issues/3328
func Test_Issue3328(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
path = gfile.Temp(guid.S())
dstFolder = path + filepath.FromSlash("/service")
srvFolder = gtest.DataPath("issue", "3328", "logic")
logicGoPath = srvFolder + filepath.FromSlash("/logic.go")
in = genservice.CGenServiceInput{
SrcFolder: srvFolder,
DstFolder: dstFolder,
DstFileNameCase: "Snake",
WatchFile: "",
StPattern: "",
Packages: nil,
ImportPrefix: "",
Clear: false,
}
)
gfile.Remove(logicGoPath)
defer gfile.Remove(logicGoPath)
err := gutil.FillStructWithDefault(&in)
t.AssertNil(err)
err = gfile.Mkdir(path)
t.AssertNil(err)
defer gfile.Remove(path)
_, err = genservice.CGenService{}.Service(ctx, in)
t.AssertNil(err)
files, err := gfile.ScanDir(srvFolder, "*", true)
for _, file := range files {
if file == logicGoPath {
if gfile.IsDir(logicGoPath) {
t.Fatalf("%s should not is folder", logicGoPath)
}
}
}
})
}
// https://github.com/gogf/gf/issues/3835
func Test_Issue3835(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
path = gfile.Temp(guid.S())
dstFolder = path + filepath.FromSlash("/service")
srvFolder = gtest.DataPath("issue", "3835", "logic")
in = genservice.CGenServiceInput{
SrcFolder: srvFolder,
DstFolder: dstFolder,
DstFileNameCase: "Snake",
WatchFile: "",
StPattern: "",
Packages: nil,
ImportPrefix: "",
Clear: false,
}
)
err := gutil.FillStructWithDefault(&in)
t.AssertNil(err)
err = gfile.Mkdir(path)
t.AssertNil(err)
defer gfile.Remove(path)
_, err = genservice.CGenService{}.Service(ctx, in)
t.AssertNil(err)
// contents
var (
genFile = dstFolder + filepath.FromSlash("/issue_3835.go")
expectFile = gtest.DataPath("issue", "3835", "service", "issue_3835.go")
)
t.Assert(gfile.GetContents(genFile), gfile.GetContents(expectFile))
})
}

View File

@@ -6,7 +6,11 @@
package genctrl
import "github.com/gogf/gf/v2/text/gstr"
import (
"fmt"
"github.com/gogf/gf/v2/text/gstr"
)
type apiItem struct {
Import string `eg:"demo.com/api/user/v1"`
@@ -14,6 +18,7 @@ type apiItem struct {
Module string `eg:"user"`
Version string `eg:"v1"`
MethodName string `eg:"GetList"`
Comment string `eg:"GetList get list"`
}
func (a apiItem) String() string {
@@ -21,3 +26,12 @@ func (a apiItem) String() string {
a.Import, a.Module, a.Version, a.MethodName,
}, ",")
}
// GetComment returns the comment of apiItem.
func (a apiItem) GetComment() string {
if a.Comment == "" {
return ""
}
// format for handling comments
return fmt.Sprintf("\n// %s %s", a.MethodName, a.Comment)
}

View File

@@ -17,9 +17,14 @@ import (
"github.com/gogf/gf/v2/text/gstr"
)
// getStructsNameInSrc retrieves all struct names
type structInfo struct {
structName string
comment string
}
// getStructsNameInSrc retrieves all struct names and comment
// that end in "Req" and have "g.Meta" in their body.
func (c CGenCtrl) getStructsNameInSrc(filePath string) (structsName []string, err error) {
func (c CGenCtrl) getStructsNameInSrc(filePath string) (structInfos []*structInfo, err error) {
var (
fileContent = gfile.GetContents(filePath)
fileSet = token.NewFileSet()
@@ -32,8 +37,8 @@ func (c CGenCtrl) getStructsNameInSrc(filePath string) (structsName []string, er
ast.Inspect(node, func(n ast.Node) bool {
if typeSpec, ok := n.(*ast.TypeSpec); ok {
methodName := typeSpec.Name.Name
if !gstr.HasSuffix(methodName, "Req") {
structName := typeSpec.Name.Name
if !gstr.HasSuffix(structName, "Req") {
// ignore struct name that do not end in "Req"
return true
}
@@ -46,7 +51,19 @@ func (c CGenCtrl) getStructsNameInSrc(filePath string) (structsName []string, er
if !gstr.Contains(buf.String(), `g.Meta`) {
return true
}
structsName = append(structsName, methodName)
comment := typeSpec.Doc.Text()
// remove the struct name from the comment
if gstr.HasPrefix(comment, structName) {
comment = gstr.TrimLeftStr(comment, structName, 1)
}
// remove the comment \n or space
comment = gstr.Trim(comment)
structInfos = append(structInfos, &structInfo{
structName: structName,
comment: comment,
})
}
}
return true

View File

@@ -10,6 +10,7 @@ import (
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
"hotgo/internal/library/hggen/internal/utility/utils"
)
@@ -38,15 +39,16 @@ func (c CGenCtrl) getApiItemsInSrc(apiModuleFolderPath string) (items []apiItem,
if err != nil {
return nil, err
}
for _, methodName := range structsInfo {
for _, s := range structsInfo {
// remove end "Req"
methodName = gstr.TrimRightStr(methodName, "Req", 1)
methodName := gstr.TrimRightStr(s.structName, "Req", 1)
item := apiItem{
Import: gstr.Trim(importPath, `"`),
FileName: gfile.Name(apiFileFolderPath),
Module: gfile.Basename(apiModuleFolderPath),
Version: gfile.Basename(apiVersionFolderPath),
MethodName: methodName,
Comment: s.comment,
}
items = append(items, item)
}

View File

@@ -15,6 +15,7 @@ import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gstr"
"hotgo/internal/library/hggen/internal/consts"
"hotgo/internal/library/hggen/internal/utility/mlog"
)
@@ -137,10 +138,11 @@ func (c *controllerGenerator) doGenerateCtrlItem(dstModuleFolderPath string, ite
if gfile.Exists(methodFilePath) {
content = gstr.ReplaceByMap(consts.TemplateGenCtrlControllerMethodFuncMerge, g.MapStrStr{
"{Module}": item.Module,
"{CtrlName}": ctrlName,
"{Version}": item.Version,
"{MethodName}": item.MethodName,
"{Module}": item.Module,
"{CtrlName}": ctrlName,
"{Version}": item.Version,
"{MethodName}": item.MethodName,
"{MethodComment}": item.GetComment(),
})
if gstr.Contains(gfile.GetContents(methodFilePath), fmt.Sprintf(`func (c *%v) %v(`, ctrlName, item.MethodName)) {
@@ -151,11 +153,12 @@ func (c *controllerGenerator) doGenerateCtrlItem(dstModuleFolderPath string, ite
}
} else {
content = gstr.ReplaceByMap(consts.TemplateGenCtrlControllerMethodFunc, g.MapStrStr{
"{Module}": item.Module,
"{ImportPath}": item.Import,
"{CtrlName}": ctrlName,
"{Version}": item.Version,
"{MethodName}": item.MethodName,
"{Module}": item.Module,
"{ImportPath}": item.Import,
"{CtrlName}": ctrlName,
"{Version}": item.Version,
"{MethodName}": item.MethodName,
"{MethodComment}": item.GetComment(),
})
if err = gfile.PutContents(methodFilePath, gstr.TrimLeft(content)); err != nil {
return err
@@ -191,10 +194,11 @@ func (c *controllerGenerator) doGenerateCtrlMergeItem(dstModuleFolderPath string
}
ctrl := gstr.TrimLeft(gstr.ReplaceByMap(consts.TemplateGenCtrlControllerMethodFuncMerge, g.MapStrStr{
"{Module}": api.Module,
"{CtrlName}": fmt.Sprintf(`Controller%s`, gstr.UcFirst(api.Version)),
"{Version}": api.Version,
"{MethodName}": api.MethodName,
"{Module}": api.Module,
"{CtrlName}": fmt.Sprintf(`Controller%s`, gstr.UcFirst(api.Version)),
"{Version}": api.Version,
"{MethodName}": api.MethodName,
"{MethodComment}": api.GetComment(),
}))
ctrlFileItem.controllers.WriteString(ctrl)
doneApiSet.Add(api.String())

View File

@@ -11,6 +11,7 @@ import (
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gstr"
"hotgo/internal/library/hggen/internal/utility/mlog"
)

View File

@@ -16,6 +16,7 @@ import (
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"hotgo/internal/library/hggen/internal/consts"
"hotgo/internal/library/hggen/internal/utility/mlog"
"hotgo/internal/library/hggen/internal/utility/utils"

View File

@@ -15,6 +15,7 @@ import (
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
"hotgo/internal/library/hggen/internal/consts"
"hotgo/internal/library/hggen/internal/utility/mlog"
)
@@ -179,6 +180,7 @@ func (c *apiSdkGenerator) doGenerateSdkImplementer(
"{Version}": item.Version,
"{MethodName}": item.MethodName,
"{ImplementerName}": implementerName,
"{MethodComment}": item.GetComment(),
}))
implementerFileContent += "\n"
}

View File

@@ -14,115 +14,77 @@ import (
"golang.org/x/mod/modfile"
"github.com/gogf/gf/v2/container/garray"
"github.com/gogf/gf/v2/container/gset"
"github.com/gogf/gf/v2/database/gdb"
"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"
"github.com/gogf/gf/v2/util/gtag"
"hotgo/internal/library/hggen/internal/utility/utils"
"hotgo/internal/library/hggen/internal/utility/mlog"
"hotgo/internal/library/hggen/internal/utility/utils"
)
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_
`
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"`
DoPath string `name:"doPath" short:"o" brief:"{CGenDaoBriefDoPath}" d:"model/do"`
EntityPath string `name:"entityPath" short:"e" brief:"{CGenDaoBriefEntityPath}" d:"model/entity"`
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"`
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`
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`
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`
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`
TypeMapping map[DBFieldTypeName]CustomAttributeType `name:"typeMapping" short:"y" brief:"{CGenDaoBriefTypeMapping}" orphan:"true"`
FieldMapping map[DBTableFieldName]CustomAttributeType `name:"fieldMapping" short:"fm" brief:"{CGenDaoBriefFieldMapping}" orphan:"true"`
tplVarTableName = `{TplTableName}`
tplVarTableNameCamelCase = `{TplTableNameCamelCase}`
tplVarTableNameCamelLowerCase = `{TplTableNameCamelLowerCase}`
tplVarPackageImports = `{TplPackageImports}`
tplVarImportPrefix = `{TplImportPrefix}`
tplVarStructDefine = `{TplStructDefine}`
tplVarColumnDefine = `{TplColumnDefine}`
tplVarColumnNames = `{TplColumnNames}`
tplVarGroupName = `{TplGroupName}`
tplVarDatetimeStr = `{TplDatetimeStr}`
tplVarCreatedAtDatetimeStr = `{TplCreatedAtDatetimeStr}`
tplVarPackageName = `{TplPackageName}`
// internal usage purpose.
genItems *CGenDaoInternalGenItems
}
CGenDaoOutput struct{}
CGenDaoInternalInput struct {
CGenDaoInput
DB gdb.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",
@@ -139,97 +101,6 @@ var (
}
)
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,
`CGenDaoBriefDoPath`: CGenDaoBriefDoPath,
`CGenDaoBriefEntityPath`: CGenDaoBriefEntityPath,
`CGenDaoBriefGJsonSupport`: CGenDaoBriefGJsonSupport,
`CGenDaoBriefImportPrefix`: CGenDaoBriefImportPrefix,
`CGenDaoBriefOverwriteDao`: CGenDaoBriefOverwriteDao,
`CGenDaoBriefModelFile`: CGenDaoBriefModelFile,
`CGenDaoBriefModelFileForDao`: CGenDaoBriefModelFileForDao,
`CGenDaoBriefDescriptionTag`: CGenDaoBriefDescriptionTag,
`CGenDaoBriefNoJsonTag`: CGenDaoBriefNoJsonTag,
`CGenDaoBriefNoModelComment`: CGenDaoBriefNoModelComment,
`CGenDaoBriefClear`: CGenDaoBriefClear,
`CGenDaoBriefTypeMapping`: CGenDaoBriefTypeMapping,
`CGenDaoBriefFieldMapping`: CGenDaoBriefFieldMapping,
`CGenDaoBriefGroup`: CGenDaoBriefGroup,
`CGenDaoBriefJsonCase`: CGenDaoBriefJsonCase,
`CGenDaoBriefTplDaoIndexPath`: CGenDaoBriefTplDaoIndexPath,
`CGenDaoBriefTplDaoInternalPath`: CGenDaoBriefTplDaoInternalPath,
`CGenDaoBriefTplDaoDoPathPath`: CGenDaoBriefTplDaoDoPathPath,
`CGenDaoBriefTplDaoEntityPath`: CGenDaoBriefTplDaoEntityPath,
})
}
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}"`
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"`
DoPath string `name:"doPath" short:"o" brief:"{CGenDaoBriefDoPath}" d:"model/do"`
EntityPath string `name:"entityPath" short:"e" brief:"{CGenDaoBriefEntityPath}" d:"model/entity"`
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"`
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 gdb.DB
TableNames []string
NewTableNames []string
}
DBTableFieldName = string
DBFieldTypeName = string
CustomAttributeType struct {
Type string `brief:"custom attribute type name"`
Import string `brief:"custom import for this type"`
}
)
func (c CGenDao) Dao(ctx context.Context, in CGenDaoInput) (out *CGenDaoOutput, err error) {
in.genItems = newCGenDaoInternalGenItems()
if in.Link != "" {
@@ -278,9 +149,12 @@ func doGenDaoForArray(ctx context.Context, index int, in CGenDaoInput) {
// It uses user passed database configuration.
if in.Link != "" {
var tempGroup = gtime.TimestampNanoStr()
gdb.AddConfigNode(tempGroup, gdb.ConfigNode{
err = gdb.AddConfigNode(tempGroup, gdb.ConfigNode{
Link: in.Link,
})
if err != nil {
mlog.Fatalf(`database configuration failed: %+v`, err)
}
if db, err = gdb.Instance(tempGroup); err != nil {
mlog.Fatalf(`database initialization failed: %+v`, err)
}
@@ -321,24 +195,51 @@ func doGenDaoForArray(ctx context.Context, index int, in CGenDaoInput) {
}
// Generating dao & model go files one by one according to given table name.
newTableNames := make([]string, len(tableNames))
var (
newTableNames = make([]string, len(tableNames))
shardingNewTableSet = gset.NewStrSet()
)
for i, tableName := range tableNames {
newTableName := tableName
for _, v := range removePrefixArray {
newTableName = gstr.TrimLeftStr(newTableName, v, 1)
}
if len(in.ShardingPattern) > 0 {
for _, pattern := range in.ShardingPattern {
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] = ""
continue
}
shardingNewTableSet.Add(newTableName)
}
}
newTableName = in.Prefix + newTableName
newTableNames[i] = newTableName
}
tableNames = garray.NewStrArrayFrom(tableNames).FilterEmpty().Slice()
in.genItems.Scale()
// Dao: index and internal.
generateDao(ctx, CGenDaoInternalInput{
CGenDaoInput: in,
DB: db,
TableNames: tableNames,
NewTableNames: newTableNames,
CGenDaoInput: in,
DB: db,
TableNames: tableNames,
NewTableNames: newTableNames,
ShardingTableSet: shardingNewTableSet,
})
// Do.
generateDo(ctx, CGenDaoInternalInput{
@@ -411,13 +312,15 @@ func getImportPartContent(ctx context.Context, source string, isDo bool, appendI
return packageImportsStr
}
func replaceDefaultVar(in CGenDaoInternalInput, origin string) string {
var tplCreatedAtDatetimeStr string
var tplDatetimeStr string = createdAt.String()
func assignDefaultVar(view *gview.View, in CGenDaoInternalInput) {
var (
tplCreatedAtDatetimeStr string
tplDatetimeStr = createdAt.String()
)
if in.WithTime {
tplCreatedAtDatetimeStr = fmt.Sprintf(`Created at %s`, tplDatetimeStr)
}
return gstr.ReplaceByMap(origin, g.MapStrStr{
view.Assigns(g.Map{
tplVarDatetimeStr: tplDatetimeStr,
tplVarCreatedAtDatetimeStr: tplCreatedAtDatetimeStr,
})

View File

@@ -18,6 +18,7 @@ import (
"github.com/gogf/gf/v2/database/gdb"
"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"
"hotgo/internal/library/hggen/internal/consts"
@@ -32,22 +33,30 @@ func generateDao(ctx context.Context, in CGenDaoInternalInput) {
)
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: in.TableNames[i],
NewTableName: in.NewTableNames[i],
TableName: realTableName,
NewTableName: newTableName,
DirPathDao: dirPathDao,
DirPathDaoInternal: dirPathDaoInternal,
IsSharding: in.ShardingTableSet.Contains(newTableName),
})
}
}
type generateDaoSingleInput struct {
CGenDaoInternalInput
TableName string // TableName specifies the table name of the table.
NewTableName string // NewTableName specifies the prefix-stripped name of the table.
// 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.
@@ -109,17 +118,26 @@ func generateDaoIndex(in generateDaoIndexInput) {
// 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) {
indexContent := gstr.ReplaceByMap(
getTemplateFromPathOrDefault(in.TplDaoIndexPath, consts.TemplateGenDaoIndexContent),
g.MapStrStr{
tplVarImportPrefix: in.ImportPrefix,
tplVarTableName: in.TableName,
tplVarTableNameCamelCase: in.TableNameCamelCase,
tplVarTableNameCamelLowerCase: in.TableNameCamelLowerCase,
tplVarPackageName: filepath.Base(in.DaoPath),
})
indexContent = replaceDefaultVar(in.CGenDaoInternalInput, indexContent)
if err := gfile.PutContents(path, strings.TrimSpace(indexContent)); err != nil {
var (
ctx = context.Background()
tplContent = getTemplateFromPathOrDefault(
in.TplDaoIndexPath, consts.TemplateGenDaoIndexContent,
)
)
tplView.ClearAssigns()
tplView.Assigns(gview.Params{
tplVarTableSharding: in.IsSharding,
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)
@@ -138,20 +156,29 @@ type generateDaoInternalInput struct {
}
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"))
removeFieldPrefixArray := gstr.SplitAndTrim(in.RemoveFieldPrefix, ",")
modelContent := gstr.ReplaceByMap(
getTemplateFromPathOrDefault(in.TplDaoInternalPath, consts.TemplateGenDaoInternalContent),
g.MapStrStr{
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)),
})
modelContent = replaceDefaultVar(in.CGenDaoInternalInput, modelContent)
in.genItems.AppendGeneratedFilePath(path)
if err := gfile.PutContents(path, strings.TrimSpace(modelContent)); err != nil {
mlog.Fatalf("writing content to '%s' failed: %v", path, err)

View File

@@ -12,8 +12,8 @@ import (
"path/filepath"
"strings"
"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/gregex"
"github.com/gogf/gf/v2/text/gstr"
@@ -78,16 +78,23 @@ func generateDo(ctx context.Context, in CGenDaoInternalInput) {
func generateDoContent(
ctx context.Context, in CGenDaoInternalInput, tableName, tableNameCamelCase, structDefine string,
) string {
doContent := gstr.ReplaceByMap(
getTemplateFromPathOrDefault(in.TplDaoDoPath, consts.TemplateGenDaoDoContent),
g.MapStrStr{
tplVarTableName: tableName,
tplVarPackageImports: getImportPartContent(ctx, structDefine, true, nil),
tplVarTableNameCamelCase: tableNameCamelCase,
tplVarStructDefine: structDefine,
tplVarPackageName: filepath.Base(in.DoPath),
},
var (
tplContent = getTemplateFromPathOrDefault(
in.TplDaoDoPath, consts.TemplateGenDaoDoContent,
)
)
doContent = replaceDefaultVar(in, doContent)
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
}

View File

@@ -11,8 +11,8 @@ import (
"path/filepath"
"strings"
"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"
"hotgo/internal/library/hggen/internal/consts"
@@ -63,16 +63,23 @@ func generateEntity(ctx context.Context, in CGenDaoInternalInput) {
func generateEntityContent(
ctx context.Context, in CGenDaoInternalInput, tableName, tableNameCamelCase, structDefine string, appendImports []string,
) string {
entityContent := gstr.ReplaceByMap(
getTemplateFromPathOrDefault(in.TplDaoEntityPath, consts.TemplateGenDaoEntityContent),
g.MapStrStr{
tplVarTableName: tableName,
tplVarPackageImports: getImportPartContent(ctx, structDefine, false, appendImports),
tplVarTableNameCamelCase: tableNameCamelCase,
tplVarStructDefine: structDefine,
tplVarPackageName: filepath.Base(in.EntityPath),
},
var (
tplContent = getTemplateFromPathOrDefault(
in.TplDaoEntityPath, consts.TemplateGenDaoEntityContent,
)
)
entityContent = replaceDefaultVar(in, entityContent)
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
}

View File

@@ -38,14 +38,14 @@ func (i *CGenDaoInternalGenItems) SetClear(clear bool) {
i.Items[i.index].Clear = clear
}
func (i CGenDaoInternalGenItems) AppendDirPath(storageDirPath string) {
func (i *CGenDaoInternalGenItems) AppendDirPath(storageDirPath string) {
i.Items[i.index].StorageDirPaths = append(
i.Items[i.index].StorageDirPaths,
storageDirPath,
)
}
func (i CGenDaoInternalGenItems) AppendGeneratedFilePath(generatedFilePath string) {
func (i *CGenDaoInternalGenItems) AppendGeneratedFilePath(generatedFilePath string) {
i.Items[i.index].GeneratedFilePaths = append(
i.Items[i.index].GeneratedFilePaths,
generatedFilePath,

View File

@@ -0,0 +1,149 @@
// 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`
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`
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`
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,
`CGenDaoBriefDoPath`: CGenDaoBriefDoPath,
`CGenDaoBriefEntityPath`: CGenDaoBriefEntityPath,
`CGenDaoBriefGJsonSupport`: CGenDaoBriefGJsonSupport,
`CGenDaoBriefImportPrefix`: CGenDaoBriefImportPrefix,
`CGenDaoBriefOverwriteDao`: CGenDaoBriefOverwriteDao,
`CGenDaoBriefModelFile`: CGenDaoBriefModelFile,
`CGenDaoBriefModelFileForDao`: CGenDaoBriefModelFileForDao,
`CGenDaoBriefDescriptionTag`: CGenDaoBriefDescriptionTag,
`CGenDaoBriefNoJsonTag`: CGenDaoBriefNoJsonTag,
`CGenDaoBriefNoModelComment`: CGenDaoBriefNoModelComment,
`CGenDaoBriefClear`: CGenDaoBriefClear,
`CGenDaoBriefTypeMapping`: CGenDaoBriefTypeMapping,
`CGenDaoBriefFieldMapping`: CGenDaoBriefFieldMapping,
`CGenDaoBriefShardingPattern`: CGenDaoBriefShardingPattern,
`CGenDaoBriefGroup`: CGenDaoBriefGroup,
`CGenDaoBriefJsonCase`: CGenDaoBriefJsonCase,
`CGenDaoBriefTplDaoIndexPath`: CGenDaoBriefTplDaoIndexPath,
`CGenDaoBriefTplDaoInternalPath`: CGenDaoBriefTplDaoInternalPath,
`CGenDaoBriefTplDaoDoPathPath`: CGenDaoBriefTplDaoDoPathPath,
`CGenDaoBriefTplDaoEntityPath`: CGenDaoBriefTplDaoEntityPath,
})
}

View File

@@ -8,12 +8,14 @@ package genenums
import (
"context"
"golang.org/x/tools/go/packages"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gtag"
"hotgo/internal/library/hggen/internal/consts"
"hotgo/internal/library/hggen/internal/utility/mlog"
)
@@ -22,8 +24,8 @@ type (
CGenEnums struct{}
CGenEnumsInput struct {
g.Meta `name:"enums" config:"{CGenEnumsConfig}" brief:"{CGenEnumsBrief}" eg:"{CGenEnumsEg}"`
Src string `name:"src" short:"s" dc:"source folder path to be parsed" d:"."`
Path string `name:"path" short:"p" dc:"output go file path storing enums content" d:"internal/boot/boot_enums.go"`
Src string `name:"src" short:"s" dc:"source folder path to be parsed" d:"api"`
Path string `name:"path" short:"p" dc:"output go file path storing enums content" d:"internal/packed/packed_enums.go"`
Prefixes []string `name:"prefixes" short:"x" dc:"only exports packages that starts with specified prefixes"`
}
CGenEnumsOutput struct{}
@@ -34,8 +36,8 @@ const (
CGenEnumsBrief = `parse go files in current project and generate enums go file`
CGenEnumsEg = `
gf gen enums
gf gen enums -p internal/boot/boot_enums.go
gf gen enums -p internal/boot/boot_enums.go -s .
gf gen enums -p internal/packed/packed_enums.go
gf gen enums -p internal/packed/packed_enums.go -s .
gf gen enums -x github.com/gogf
`
)

View File

@@ -9,6 +9,7 @@ package genenums
import (
"go/constant"
"go/types"
"golang.org/x/tools/go/packages"
"github.com/gogf/gf/v2/encoding/gjson"

View File

@@ -13,6 +13,7 @@ import (
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gproc"
"github.com/gogf/gf/v2/util/gtag"
"hotgo/internal/library/hggen/internal/utility/mlog"
)

View File

@@ -15,6 +15,7 @@ import (
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
"hotgo/internal/library/hggen/internal/utility/utils"
)

View File

@@ -16,6 +16,7 @@ import (
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"hotgo/internal/library/hggen/internal/utility/utils"
)
@@ -71,6 +72,10 @@ func (c CGenPb) doTagReplacement(ctx context.Context, content string) (string, e
if !lineTagMap.IsEmpty() {
tagContent := c.listMapToStructTag(lineTagMap)
lineTagMap.Clear()
// If already have it, don't add it anymore
if gstr.Contains(gstr.StrTill(line, "` //"), tagContent) {
continue
}
line, _ = gregex.ReplaceString("`(.+)`", fmt.Sprintf("`$1 %s`", tagContent), line)
}
lines[index] = line

View File

@@ -11,11 +11,13 @@ import (
"context"
"fmt"
"path/filepath"
"regexp"
"strings"
"github.com/olekukonko/tablewriter"
"hotgo/internal/library/hggen/internal/utility/utils"
"github.com/gogf/gf/v2/container/garray"
"github.com/gogf/gf/v2/container/gset"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gctx"
@@ -25,8 +27,10 @@ import (
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/gtag"
"hotgo/internal/library/hggen/internal/consts"
"hotgo/internal/library/hggen/internal/utility/mlog"
"hotgo/internal/library/hggen/internal/utility/utils"
)
type (
@@ -35,14 +39,19 @@ type (
g.Meta `name:"pbentity" config:"{CGenPbEntityConfig}" brief:"{CGenPbEntityBrief}" eg:"{CGenPbEntityEg}" ad:"{CGenPbEntityAd}"`
Path string `name:"path" short:"p" brief:"{CGenPbEntityBriefPath}" d:"manifest/protobuf/pbentity"`
Package string `name:"package" short:"k" brief:"{CGenPbEntityBriefPackage}"`
GoPackage string `name:"goPackage" short:"g" brief:"{CGenPbEntityBriefGoPackage}"`
Link string `name:"link" short:"l" brief:"{CGenPbEntityBriefLink}"`
Tables string `name:"tables" short:"t" brief:"{CGenPbEntityBriefTables}"`
Prefix string `name:"prefix" short:"f" brief:"{CGenPbEntityBriefPrefix}"`
RemovePrefix string `name:"removePrefix" short:"r" brief:"{CGenPbEntityBriefRemovePrefix}"`
RemoveFieldPrefix string `name:"removeFieldPrefix" short:"rf" brief:"{CGenPbEntityBriefRemoveFieldPrefix}"`
TablesEx string `name:"tablesEx" short:"x" brief:"{CGenDaoBriefTablesEx}"`
NameCase string `name:"nameCase" short:"n" brief:"{CGenPbEntityBriefNameCase}" d:"Camel"`
JsonCase string `name:"jsonCase" short:"j" brief:"{CGenPbEntityBriefJsonCase}" d:"none"`
Option string `name:"option" short:"o" brief:"{CGenPbEntityBriefOption}"`
TypeMapping map[DBFieldTypeName]CustomAttributeType `name:"typeMapping" short:"y" brief:"{CGenPbEntityBriefTypeMapping}" orphan:"true"`
FieldMapping map[DBTableFieldName]CustomAttributeType `name:"fieldMapping" short:"fm" brief:"{CGenPbEntityBriefFieldMapping}" orphan:"true"`
}
CGenPbEntityOutput struct{}
@@ -52,6 +61,13 @@ type (
TableName string // TableName specifies the table name of the table.
NewTableName string // NewTableName specifies the prefix-stripped name of the table.
}
DBTableFieldName = string
DBFieldTypeName = string
CustomAttributeType struct {
Type string `brief:"custom attribute type name"`
Import string `brief:"custom import for this type"`
}
)
const (
@@ -69,7 +85,7 @@ gf gen pbentity -r user_
CGenPbEntityAd = `
CONFIGURATION SUPPORT
Options are also supported by configuration file.
It's suggested using configuration file instead of command line arguments making producing.
It's suggested using configuration file instead of command line arguments making producing.
The configuration node name is "gf.gen.pbentity", which also supports multiple databases, for example(config.yaml):
gfcli:
gen:
@@ -88,13 +104,22 @@ CONFIGURATION SUPPORT
option go_package = "protobuf/demos";
option java_package = "protobuf/demos";
option php_namespace = "protobuf/demos";
typeMapping:
json:
type: google.protobuf.Value
import: google/protobuf/struct.proto
jsonb:
type: google.protobuf.Value
import: google/protobuf/struct.proto
`
CGenPbEntityBriefPath = `directory path for generated files storing`
CGenPbEntityBriefPackage = `package path for all entity proto files`
CGenPbEntityBriefGoPackage = `go package path for all entity proto files`
CGenPbEntityBriefLink = `database configuration, the same as the ORM configuration of GoFrame`
CGenPbEntityBriefTables = `generate models only for given tables, multiple table names separated with ','`
CGenPbEntityBriefPrefix = `add specified prefix for all entity names and entity proto files`
CGenPbEntityBriefRemovePrefix = `remove specified prefix of the table, multiple prefix separated with ','`
CGenPbEntityBriefTablesEx = `generate all models exclude the specified tables, multiple prefix separated with ','`
CGenPbEntityBriefRemoveFieldPrefix = `remove specified prefix of the field, multiple prefix separated with ','`
CGenPbEntityBriefOption = `extra protobuf options`
CGenPbEntityBriefGroup = `
@@ -106,7 +131,7 @@ it's not necessary and the default value is "default"
case for message attribute names, default is "Camel":
| Case | Example |
|---------------- |--------------------|
| Camel | AnyKindOfString |
| Camel | AnyKindOfString |
| CamelLower | anyKindOfString | default
| Snake | any_kind_of_string |
| SnakeScreaming | ANY_KIND_OF_STRING |
@@ -119,8 +144,95 @@ case for message attribute names, default is "Camel":
case for message json tag, cases are the same as "nameCase", default "CamelLower".
set it to "none" to ignore json tag generating.
`
CGenPbEntityBriefTypeMapping = `custom local type mapping for generated struct attributes relevant to fields of table`
CGenPbEntityBriefFieldMapping = `custom local type mapping for generated struct attributes relevant to specific fields of table`
)
var defaultTypeMapping = map[DBFieldTypeName]CustomAttributeType{
// gdb.LocalTypeString
"string": {
Type: "string",
},
// gdb.LocalTypeTime
// "time": {
// Type: "google.protobuf.Duration",
// Import: "google/protobuf/duration.proto",
// },
// gdb.LocalTypeDate
"date": {
Type: "google.protobuf.Timestamp",
Import: "google/protobuf/timestamp.proto",
},
// gdb.LocalTypeDatetime
"datetime": {
Type: "google.protobuf.Timestamp",
Import: "google/protobuf/timestamp.proto",
},
// gdb.LocalTypeInt
"int": {
Type: "int32",
},
// gdb.LocalTypeUint
"uint": {
Type: "uint32",
},
// gdb.LocalTypeInt64
"int64": {
Type: "int64",
},
// gdb.LocalTypeUint64
"uint64": {
Type: "uint64",
},
// gdb.LocalTypeIntSlice
"[]int": {
Type: "repeated int32",
},
// gdb.LocalTypeInt64Slice
"[]int64": {
Type: "repeated int64",
},
// gdb.LocalTypeUint64Slice
"[]uint64": {
Type: "repeated uint64",
},
// gdb.LocalTypeInt64Bytes
"int64-bytes": {
Type: "repeated int64",
},
// gdb.LocalTypeUint64Bytes
"uint64-bytes": {
Type: "repeated uint64",
},
// gdb.LocalTypeFloat32
"float32": {
Type: "float",
},
// gdb.LocalTypeFloat64
"float64": {
Type: "double",
},
// gdb.LocalTypeBytes
"[]byte": {
Type: "bytes",
},
// gdb.LocalTypeBool
"bool": {
Type: "bool",
},
// gdb.LocalTypeJson
// "json": {
// Type: "google.protobuf.Value",
// Import: "google/protobuf/struct.proto",
// },
// gdb.LocalTypeJsonb
// "jsonb": {
// Type: "google.protobuf.Value",
// Import: "google/protobuf/struct.proto",
// },
}
func init() {
gtag.Sets(g.MapStrStr{
`CGenPbEntityConfig`: CGenPbEntityConfig,
@@ -129,15 +241,19 @@ func init() {
`CGenPbEntityAd`: CGenPbEntityAd,
`CGenPbEntityBriefPath`: CGenPbEntityBriefPath,
`CGenPbEntityBriefPackage`: CGenPbEntityBriefPackage,
`CGenPbEntityBriefGoPackage`: CGenPbEntityBriefGoPackage,
`CGenPbEntityBriefLink`: CGenPbEntityBriefLink,
`CGenPbEntityBriefTables`: CGenPbEntityBriefTables,
`CGenPbEntityBriefPrefix`: CGenPbEntityBriefPrefix,
`CGenPbEntityBriefRemovePrefix`: CGenPbEntityBriefRemovePrefix,
`CGenPbEntityBriefTablesEx`: CGenPbEntityBriefTablesEx,
`CGenPbEntityBriefRemoveFieldPrefix`: CGenPbEntityBriefRemoveFieldPrefix,
`CGenPbEntityBriefGroup`: CGenPbEntityBriefGroup,
`CGenPbEntityBriefNameCase`: CGenPbEntityBriefNameCase,
`CGenPbEntityBriefJsonCase`: CGenPbEntityBriefJsonCase,
`CGenPbEntityBriefOption`: CGenPbEntityBriefOption,
`CGenPbEntityBriefTypeMapping`: CGenPbEntityBriefTypeMapping,
`CGenPbEntityBriefFieldMapping`: CGenPbEntityBriefFieldMapping,
})
}
@@ -181,6 +297,9 @@ func doGenPbEntityForArray(ctx context.Context, index int, in CGenPbEntityInput)
in.Package = modName + "/" + defaultPackageSuffix
}
removePrefixArray := gstr.SplitAndTrim(in.RemovePrefix, ",")
excludeTables := gset.NewStrSetFrom(gstr.SplitAndTrim(in.TablesEx, ","))
// It uses user passed database configuration.
if in.Link != "" {
var (
@@ -210,8 +329,21 @@ func doGenPbEntityForArray(ctx context.Context, index int, in CGenPbEntityInput)
mlog.Fatalf("fetching tables failed: \n %v", err)
}
}
// 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
}
}
}
for _, tableName := range tableNames {
if excludeTables.Contains(tableName) {
continue
}
newTableName := tableName
for _, v := range removePrefixArray {
newTableName = gstr.TrimLeftStr(newTableName, v, 1)
@@ -234,20 +366,29 @@ func generatePbEntityContentFile(ctx context.Context, in CGenPbEntityInternalInp
// Change the `newTableName` if `Prefix` is given.
newTableName := in.Prefix + in.NewTableName
var (
imports string
tableNameCamelCase = gstr.CaseCamel(newTableName)
tableNameSnakeCase = gstr.CaseSnake(newTableName)
entityMessageDefine = generateEntityMessageDefinition(tableNameCamelCase, fieldMap, in)
fileName = gstr.Trim(tableNameSnakeCase, "-_.")
path = filepath.FromSlash(gfile.Join(in.Path, fileName+".proto"))
tableNameCamelCase = gstr.CaseCamel(newTableName)
tableNameSnakeCase = gstr.CaseSnake(newTableName)
entityMessageDefine, appendImports = generateEntityMessageDefinition(tableNameCamelCase, fieldMap, in)
fileName = gstr.Trim(tableNameSnakeCase, "-_.")
path = filepath.FromSlash(gfile.Join(in.Path, fileName+".proto"))
)
if gstr.Contains(entityMessageDefine, "google.protobuf.Timestamp") {
imports = `import "google/protobuf/timestamp.proto";`
packageImportStr := ""
var packageImportsArray = garray.NewStrArray()
if len(appendImports) > 0 {
for _, appendImport := range appendImports {
packageImportStr = fmt.Sprintf(`import "%s";`, appendImport)
if packageImportsArray.Search(packageImportStr) == -1 {
packageImportsArray.Append(packageImportStr)
}
}
}
if in.GoPackage == "" {
in.GoPackage = in.Package
}
entityContent := gstr.ReplaceByMap(getTplPbEntityContent(""), g.MapStrStr{
"{Imports}": imports,
"{Imports}": packageImportsArray.Join("\n"),
"{PackageName}": gfile.Basename(in.Package),
"{GoPackage}": in.Package,
"{GoPackage}": in.GoPackage,
"{OptionContent}": in.Option,
"{EntityMessage}": entityMessageDefine,
})
@@ -259,14 +400,19 @@ func generatePbEntityContentFile(ctx context.Context, in CGenPbEntityInternalInp
}
// generateEntityMessageDefinition generates and returns the message definition for specified table.
func generateEntityMessageDefinition(entityName string, fieldMap map[string]*gdb.TableField, in CGenPbEntityInternalInput) string {
func generateEntityMessageDefinition(entityName string, fieldMap map[string]*gdb.TableField, in CGenPbEntityInternalInput) (string, []string) {
var (
buffer = bytes.NewBuffer(nil)
array = make([][]string, len(fieldMap))
names = sortFieldKeyForPbEntity(fieldMap)
appendImports []string
buffer = bytes.NewBuffer(nil)
array = make([][]string, len(fieldMap))
names = sortFieldKeyForPbEntity(fieldMap)
)
for index, name := range names {
array[index] = generateMessageFieldForPbEntity(index+1, fieldMap[name], in)
var imports string
array[index], imports = generateMessageFieldForPbEntity(index+1, fieldMap[name], in)
if imports != "" {
appendImports = append(appendImports, imports)
}
}
tw := tablewriter.NewWriter(buffer)
tw.SetBorder(false)
@@ -277,48 +423,38 @@ func generateEntityMessageDefinition(entityName string, fieldMap map[string]*gdb
tw.Render()
stContent := buffer.String()
// Let's do this hack of table writer for indent!
stContent = gstr.Replace(stContent, " #", "")
stContent = regexp.MustCompile(`\s+\n`).ReplaceAllString(gstr.Replace(stContent, " #", ""), "\n")
buffer.Reset()
buffer.WriteString(fmt.Sprintf("message %s {\n", entityName))
buffer.WriteString(stContent)
buffer.WriteString("}")
return buffer.String()
return buffer.String(), appendImports
}
// generateMessageFieldForPbEntity generates and returns the message definition for specified field.
func generateMessageFieldForPbEntity(index int, field *gdb.TableField, in CGenPbEntityInternalInput) []string {
func generateMessageFieldForPbEntity(index int, field *gdb.TableField, in CGenPbEntityInternalInput) (attrLines []string, appendImport string) {
var (
localTypeName gdb.LocalType
comment string
jsonTagStr string
err error
ctx = gctx.GetInitCtx()
localTypeNameStr string
localTypeName gdb.LocalType
comment string
jsonTagStr string
err error
ctx = gctx.GetInitCtx()
)
localTypeName, err = in.DB.CheckLocalTypeForField(ctx, field.Type, nil)
if err != nil {
panic(err)
if in.TypeMapping != nil && len(in.TypeMapping) > 0 {
localTypeName, err = in.DB.CheckLocalTypeForField(ctx, field.Type, nil)
if err != nil {
panic(err)
}
if localTypeName != "" {
if typeMapping, ok := in.TypeMapping[strings.ToLower(string(localTypeName))]; ok {
localTypeNameStr = typeMapping.Type
appendImport = typeMapping.Import
}
}
}
var typeMapping = map[gdb.LocalType]string{
gdb.LocalTypeString: "string",
gdb.LocalTypeDate: "google.protobuf.Timestamp",
gdb.LocalTypeDatetime: "google.protobuf.Timestamp",
gdb.LocalTypeInt: "int32",
gdb.LocalTypeUint: "uint32",
gdb.LocalTypeInt64: "int64",
gdb.LocalTypeUint64: "uint64",
gdb.LocalTypeIntSlice: "repeated int32",
gdb.LocalTypeInt64Slice: "repeated int64",
gdb.LocalTypeUint64Slice: "repeated uint64",
gdb.LocalTypeInt64Bytes: "repeated int64",
gdb.LocalTypeUint64Bytes: "repeated uint64",
gdb.LocalTypeFloat32: "float",
gdb.LocalTypeFloat64: "double",
gdb.LocalTypeBytes: "bytes",
gdb.LocalTypeBool: "bool",
gdb.LocalTypeJson: "string",
gdb.LocalTypeJsonb: "string",
}
localTypeNameStr := typeMapping[localTypeName]
if localTypeNameStr == "" {
localTypeNameStr = "string"
}
@@ -351,12 +487,19 @@ func generateMessageFieldForPbEntity(index int, field *gdb.TableField, in CGenPb
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
}
}
return []string{
" #" + localTypeNameStr,
" #" + formatCase(newFiledName, in.NameCase),
" #= " + gconv.String(index) + jsonTagStr + ";",
" #" + fmt.Sprintf(`// %s`, comment),
}
}, appendImport
}
func getTplPbEntityContent(tplEntityPath string) string {

View File

@@ -94,6 +94,20 @@ const (
genServiceFileLockSeconds = 10
)
type fileInfo struct {
PkgItems []pkgItem
FuncItems []funcItem
}
type folderInfo struct {
SrcPackageName string
SrcImportedPackages *garray.SortedStrArray
SrcStructFunctions *gmap.ListMap
DstFilePath string
FileInfos []*fileInfo
}
func (c CGenService) Service(ctx context.Context, in CGenServiceInput) (out *CGenServiceOutput, err error) {
in.SrcFolder = filepath.ToSlash(in.SrcFolder)
in.SrcFolder = gstr.TrimRight(in.SrcFolder, `/`)
@@ -163,7 +177,12 @@ func (c CGenService) Service(ctx context.Context, in CGenServiceInput) (out *CGe
return nil, err
}
// it will use goroutine to generate service files for each package.
var wg = sync.WaitGroup{}
var (
folderInfos []folderInfo
wg = sync.WaitGroup{}
allStructItems = make(map[string][]string)
)
for _, srcFolderPath := range srcFolderPaths {
if !gfile.IsDir(srcFolderPath) {
continue
@@ -175,7 +194,7 @@ func (c CGenService) Service(ctx context.Context, in CGenServiceInput) (out *CGe
if len(files) == 0 {
continue
}
// Parse single logic package folder.
var (
srcPackageName = gfile.Basename(srcFolderPath)
srcImportedPackages = garray.NewSortedStrArray().SetUnique(true)
@@ -184,14 +203,46 @@ func (c CGenService) Service(ctx context.Context, in CGenServiceInput) (out *CGe
c.getDstFileNameCase(srcPackageName, in.DstFileNameCase)+".go",
)
)
generatedDstFilePathSet.Add(dstFilePath)
// if it were to use goroutine,
// it would cause the order of the generated functions in the file to be disordered.
folder := folderInfo{
SrcPackageName: srcPackageName,
SrcImportedPackages: srcImportedPackages,
SrcStructFunctions: srcStructFunctions,
DstFilePath: dstFilePath,
}
for _, file := range files {
pkgItems, funcItems, err := c.parseItemsInSrc(file)
pkgItems, structItems, funcItems, err := c.parseItemsInSrc(file)
if err != nil {
return nil, err
}
for k, v := range structItems {
allStructItems[k] = v
}
folder.FileInfos = append(folder.FileInfos, &fileInfo{
PkgItems: pkgItems,
FuncItems: funcItems,
})
}
folderInfos = append(folderInfos, folder)
}
folderInfos = c.calculateStructEmbeddedFuncInfos(folderInfos, allStructItems)
for _, folder := range folderInfos {
// Parse single logic package folder.
var (
srcPackageName = folder.SrcPackageName
srcImportedPackages = folder.SrcImportedPackages
srcStructFunctions = folder.SrcStructFunctions
dstFilePath = folder.DstFilePath
)
generatedDstFilePathSet.Add(dstFilePath)
// if it were to use goroutine,
// it would cause the order of the generated functions in the file to be disordered.
for _, file := range folder.FileInfos {
pkgItems, funcItems := file.PkgItems, file.FuncItems
// Calculate imported packages for service generating.
err = c.calculateImportedItems(in, pkgItems, funcItems, srcImportedPackages)

View File

@@ -10,8 +10,10 @@ import (
"go/ast"
"go/parser"
"go/token"
"strings"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gstructs"
"github.com/gogf/gf/v2/text/gstr"
)
@@ -32,7 +34,7 @@ type funcItem struct {
// parseItemsInSrc parses the pkgItem and funcItem from the specified file.
// It can't skip the private methods.
// It can't skip the imported packages of import alias equal to `_`.
func (c CGenService) parseItemsInSrc(filePath string) (pkgItems []pkgItem, funcItems []funcItem, err error) {
func (c CGenService) parseItemsInSrc(filePath string) (pkgItems []pkgItem, structItems map[string][]string, funcItems []funcItem, err error) {
var (
fileContent = gfile.GetContents(filePath)
fileSet = token.NewFileSet()
@@ -43,11 +45,107 @@ func (c CGenService) parseItemsInSrc(filePath string) (pkgItems []pkgItem, funcI
return
}
structItems = make(map[string][]string)
pkg := node.Name.Name
pkgAliasMap := make(map[string]string)
ast.Inspect(node, func(n ast.Node) bool {
switch x := n.(type) {
case *ast.ImportSpec:
// parse the imported packages.
pkgItems = append(pkgItems, c.parseImportPackages(x))
pkgItem := c.parseImportPackages(x)
pkgItems = append(pkgItems, pkgItem)
pkgPath := strings.Trim(pkgItem.Path, "\"")
pkgPath = strings.ReplaceAll(pkgPath, "\\", "/")
tmp := strings.Split(pkgPath, "/")
srcPkg := tmp[len(tmp)-1]
if srcPkg != pkgItem.Alias {
pkgAliasMap[pkgItem.Alias] = srcPkg
}
case *ast.TypeSpec: // type define
switch xType := x.Type.(type) {
case *ast.StructType: // define struct
// parse the struct declaration.
var structName = pkg + "." + x.Name.Name
var structEmbeddedStruct []string
for _, field := range xType.Fields.List {
if len(field.Names) > 0 || field.Tag == nil { // not anonymous field
continue
}
tagValue := strings.Trim(field.Tag.Value, "`")
tagValue = strings.TrimSpace(tagValue)
if len(tagValue) == 0 { // not set tag
continue
}
tags := gstructs.ParseTag(tagValue)
if v, ok := tags["gen"]; !ok || v != "extend" {
continue
}
var embeddedStruct string
switch v := field.Type.(type) {
case *ast.Ident:
if embeddedStruct, err = c.astExprToString(v); err != nil {
embeddedStruct = ""
break
}
embeddedStruct = pkg + "." + embeddedStruct
case *ast.StarExpr:
if embeddedStruct, err = c.astExprToString(v.X); err != nil {
embeddedStruct = ""
break
}
embeddedStruct = pkg + "." + embeddedStruct
case *ast.SelectorExpr:
var pkg string
if pkg, err = c.astExprToString(v.X); err != nil {
embeddedStruct = ""
break
}
if v, ok := pkgAliasMap[pkg]; ok {
pkg = v
}
if embeddedStruct, err = c.astExprToString(v.Sel); err != nil {
embeddedStruct = ""
break
}
embeddedStruct = pkg + "." + embeddedStruct
}
if embeddedStruct == "" {
continue
}
structEmbeddedStruct = append(structEmbeddedStruct, embeddedStruct)
}
if len(structEmbeddedStruct) > 0 {
structItems[structName] = structEmbeddedStruct
}
case *ast.Ident: // define ident
var (
structName = pkg + "." + x.Name.Name
typeName = pkg + "." + xType.Name
)
structItems[structName] = []string{typeName}
case *ast.SelectorExpr: // define selector
var (
structName = pkg + "." + x.Name.Name
selecotrPkg string
typeName string
)
if selecotrPkg, err = c.astExprToString(xType.X); err != nil {
break
}
if v, ok := pkgAliasMap[selecotrPkg]; ok {
selecotrPkg = v
}
if typeName, err = c.astExprToString(xType.Sel); err != nil {
break
}
typeName = selecotrPkg + "." + typeName
structItems[structName] = []string{typeName}
}
case *ast.FuncDecl:
// parse the function items.

View File

@@ -15,6 +15,7 @@ import (
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
"hotgo/internal/library/hggen/internal/utility/mlog"
)
@@ -150,3 +151,78 @@ func (c CGenService) tidyResult(resultSlice []map[string]string) (resultStr stri
}
return
}
func (c CGenService) getStructFuncItems(structName string, allStructItems map[string][]string, funcItemsWithoutEmbed map[string][]*funcItem) (funcItems []*funcItem) {
funcItemNameSet := map[string]struct{}{}
if items, ok := funcItemsWithoutEmbed[structName]; ok {
funcItems = append(funcItems, items...)
for _, item := range items {
funcItemNameSet[item.MethodName] = struct{}{}
}
}
embeddedStructNames, ok := allStructItems[structName]
if !ok {
return
}
for _, embeddedStructName := range embeddedStructNames {
items := c.getStructFuncItems(embeddedStructName, allStructItems, funcItemsWithoutEmbed)
for _, item := range items {
if _, ok := funcItemNameSet[item.MethodName]; ok {
continue
}
funcItemNameSet[item.MethodName] = struct{}{}
funcItems = append(funcItems, item)
}
}
return
}
func (c CGenService) calculateStructEmbeddedFuncInfos(folderInfos []folderInfo, allStructItems map[string][]string) (newFolerInfos []folderInfo) {
funcItemsWithoutEmbed := make(map[string][]*funcItem)
funcItemMap := make(map[string]*([]funcItem))
funcItemsWithoutEmbedMap := make(map[string]*funcItem)
newFolerInfos = append(newFolerInfos, folderInfos...)
for _, folder := range newFolerInfos {
for k := range folder.FileInfos {
fi := folder.FileInfos[k]
for k := range fi.FuncItems {
item := &fi.FuncItems[k]
receiver := folder.SrcPackageName + "." + strings.ReplaceAll(item.Receiver, "*", "")
funcItemMap[receiver] = &fi.FuncItems
funcItemsWithoutEmbed[receiver] = append(funcItemsWithoutEmbed[receiver], item)
funcItemsWithoutEmbedMap[fmt.Sprintf("%s:%s", receiver, item.MethodName)] = item
}
}
}
for receiver, structItems := range allStructItems {
receiverName := strings.ReplaceAll(receiver, "*", "")
for _, structName := range structItems {
// Get the list of methods for the corresponding structName.
for _, funcItem := range c.getStructFuncItems(structName, allStructItems, funcItemsWithoutEmbed) {
if _, ok := funcItemsWithoutEmbedMap[fmt.Sprintf("%s:%s", receiverName, funcItem.MethodName)]; ok {
continue
}
if funcItemsPtr, ok := funcItemMap[receiverName]; ok {
newFuncItem := *funcItem
newFuncItem.Receiver = getReceiverName(receiver)
(*funcItemsPtr) = append((*funcItemsPtr), newFuncItem)
}
}
}
}
return
}
func getReceiverName(receiver string) string {
ss := strings.Split(receiver, ".")
return ss[len(ss)-1]
}

View File

@@ -14,6 +14,7 @@ import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gstr"
"hotgo/internal/library/hggen/internal/consts"
"hotgo/internal/library/hggen/internal/utility/mlog"
"hotgo/internal/library/hggen/internal/utility/utils"

View File

@@ -14,6 +14,7 @@ import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
"hotgo/internal/library/hggen/internal/consts"
)

View File

@@ -45,7 +45,7 @@ import (
"{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)
}
@@ -66,7 +66,7 @@ import (
`
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)
}

View File

@@ -81,7 +81,7 @@ func (i *implementer) {ImplementerName}() {Module}.I{ImplementerName} {
`
const TemplateGenCtrlSdkImplementerFunc = `
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

View File

@@ -8,38 +8,57 @@ package consts
const TemplateGenDaoIndexContent = `
// =================================================================================
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
// =================================================================================
package {TplPackageName}
package {{.TplPackageName}}
import (
"{TplImportPrefix}/internal"
"{{.TplImportPrefix}}/internal"
)
// internal{TplTableNameCamelCase}Dao is internal type for wrapping internal DAO implements.
type internal{TplTableNameCamelCase}Dao = *internal.{TplTableNameCamelCase}Dao
// {TplTableNameCamelLowerCase}Dao is the data access object for table {TplTableName}.
// You can define custom methods on it to extend its functionality as you wish.
type {TplTableNameCamelLowerCase}Dao struct {
internal{TplTableNameCamelCase}Dao
// {{.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 globally public accessible object for table {TplTableName} operations.
{TplTableNameCamelCase} = {TplTableNameCamelLowerCase}Dao{
internal.New{TplTableNameCamelCase}Dao(),
// {{.TplTableNameCamelCase}} is a globally accessible object for table {{.TplTableName}} operations.
{{.TplTableNameCamelCase}} = {{.TplTableNameCamelLowerCase}}Dao{
{{- if .TplTableSharding -}}
internal.New{{.TplTableNameCamelCase}}Dao(userShardingHandler),
{{- else -}}
internal.New{{.TplTableNameCamelCase}}Dao(),
{{- end -}}
}
)
// Fill with you ideas below.
{{if .TplTableSharding -}}
// userShardingHandler is the handler for sharding operations.
// You can fill this sharding handler with your custom implementation.
func userShardingHandler(m *gdb.Model) *gdb.Model {
m = m.Sharding(gdb.ShardingConfig{
Table: gdb.ShardingTableConfig{
Enable: true,
Prefix: "",
// 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}
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. {{.TplCreatedAtDatetimeStr}}
// ==========================================================================
package internal
@@ -51,64 +70,70 @@ import (
"github.com/gogf/gf/v2/frame/g"
)
// {TplTableNameCamelCase}Dao is the data access object for table {TplTableName}.
type {TplTableNameCamelCase}Dao struct {
// {{.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 current DAO.
columns {TplTableNameCamelCase}Columns // columns contains all the column names of Table for convenient usage.
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 table {TplTableName}.
type {TplTableNameCamelCase}Columns struct {
{TplColumnDefine}
// {{.TplTableNameCamelCase}}Columns defines and stores column names for the table {{.TplTableName}}.
type {{.TplTableNameCamelCase}}Columns struct {
{{.TplColumnDefine}}
}
// {TplTableNameCamelLowerCase}Columns holds the columns for table {TplTableName}.
var {TplTableNameCamelLowerCase}Columns = {TplTableNameCamelCase}Columns{
{TplColumnNames}
// {{.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() *{TplTableNameCamelCase}Dao {
return &{TplTableNameCamelCase}Dao{
group: "{TplGroupName}",
table: "{TplTableName}",
columns: {TplTableNameCamelLowerCase}Columns,
// 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 current DAO.
func (dao *{TplTableNameCamelCase}Dao) DB() gdb.DB {
// 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 current dao.
func (dao *{TplTableNameCamelCase}Dao) Table() string {
// Table returns the table name of the current DAO.
func (dao *{{.TplTableNameCamelCase}}Dao) Table() string {
return dao.table
}
// Columns returns all column names of current dao.
func (dao *{TplTableNameCamelCase}Dao) Columns() {TplTableNameCamelCase}Columns {
// Columns returns all column names of the current DAO.
func (dao *{{.TplTableNameCamelCase}}Dao) Columns() {{.TplTableNameCamelCase}}Columns {
return dao.columns
}
// Group returns the configuration group name of database of current dao.
func (dao *{TplTableNameCamelCase}Dao) Group() string {
// Group returns the database configuration group name of the current DAO.
func (dao *{{.TplTableNameCamelCase}}Dao) Group() string {
return dao.group
}
// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
func (dao *{TplTableNameCamelCase}Dao) Ctx(ctx context.Context) *gdb.Model {
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
// 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 rollbacks the transaction and returns the error from function f if it returns non-nil error.
// 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 that, you should not Commit or Rollback the transaction in function f
// 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) {
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)
}
`

View File

@@ -8,13 +8,13 @@ package consts
const TemplateGenDaoDoContent = `
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. {TplCreatedAtDatetimeStr}
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. {{.TplCreatedAtDatetimeStr}}
// =================================================================================
package {TplPackageName}
package {{.TplPackageName}}
{TplPackageImports}
{{.TplPackageImports}}
// {TplTableNameCamelCase} is the golang structure of table {TplTableName} for DAO operations like Where/Data.
{TplStructDefine}
// {{.TplTableNameCamelCase}} is the golang structure of table {{.TplTableName}} for DAO operations like Where/Data.
{{.TplStructDefine}}
`

View File

@@ -8,13 +8,13 @@ package consts
const TemplateGenDaoEntityContent = `
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. {TplCreatedAtDatetimeStr}
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. {{.TplCreatedAtDatetimeStr}}
// =================================================================================
package {TplPackageName}
package {{.TplPackageName}}
{TplPackageImports}
{{.TplPackageImports}}
// {TplTableNameCamelCase} is the golang structure for table {TplTableName}.
{TplStructDefine}
// {{.TplTableNameCamelCase}} is the golang structure for table {{.TplTableName}}.
{{.TplStructDefine}}
`

View File

@@ -162,8 +162,14 @@ func (s serviceInstall) getGoPathBin() string {
func (s serviceInstall) getAvailablePaths() []serviceInstallAvailablePath {
var (
folderPaths []serviceInstallAvailablePath
binaryFileName = "gf" + gfile.Ext(gfile.SelfPath())
binaryFileName = "gf"
)
// Windows binary file name suffix.
if runtime.GOOS == "windows" {
binaryFileName += ".exe"
}
// $GOPATH/bin
if goPathBin := s.getGoPathBin(); goPathBin != "" {
folderPaths = s.checkAndAppendToAvailablePath(

View File

@@ -7,13 +7,13 @@
package utils
import (
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
"github.com/schollz/progressbar/v3"
"github.com/gogf/gf/v2/errors/gerror"
"hotgo/internal/library/hggen/internal/utility/mlog"
@@ -34,26 +34,16 @@ func HTTPDownloadFileWithPercent(url string, localSaveFilePath string) error {
}
defer headResp.Body.Close()
size, err := strconv.Atoi(headResp.Header.Get("Content-Length"))
if err != nil {
return gerror.Wrap(err, "retrieve Content-Length failed")
}
doneCh := make(chan int64)
go doPrintDownloadPercent(doneCh, localSaveFilePath, int64(size))
resp, err := http.Get(url)
if err != nil {
return gerror.Wrapf(err, `download "%s" to "%s" failed`, url, localSaveFilePath)
}
defer resp.Body.Close()
wroteBytesCount, err := io.Copy(out, resp.Body)
if err != nil {
return gerror.Wrapf(err, `download "%s" to "%s" failed`, url, localSaveFilePath)
}
bar := progressbar.NewOptions(int(resp.ContentLength), progressbar.OptionShowBytes(true), progressbar.OptionShowCount())
writer := io.MultiWriter(out, bar)
_, err = io.Copy(writer, resp.Body)
doneCh <- wroteBytesCount
elapsed := time.Since(start)
if elapsed > time.Minute {
mlog.Printf(`download completed in %.0fm`, float64(elapsed)/float64(time.Minute))
@@ -63,45 +53,3 @@ func HTTPDownloadFileWithPercent(url string, localSaveFilePath string) error {
return nil
}
func doPrintDownloadPercent(doneCh chan int64, localSaveFilePath string, total int64) {
var (
stop = false
lastPercentFmt string
)
file, err := os.Open(localSaveFilePath)
if err != nil {
mlog.Fatal(err)
}
defer file.Close()
for {
select {
case <-doneCh:
stop = true
default:
fi, err := file.Stat()
if err != nil {
mlog.Fatal(err)
}
size := fi.Size()
if size == 0 {
size = 1
}
var (
percent = float64(size) / float64(total) * 100
percentFmt = fmt.Sprintf(`%.0f`, percent) + "%"
)
if lastPercentFmt != percentFmt {
lastPercentFmt = percentFmt
mlog.Print(percentFmt)
}
}
if stop {
break
}
time.Sleep(time.Second)
}
}

View File

@@ -11,6 +11,7 @@ import (
"testing"
"github.com/gogf/gf/v2/test/gtest"
"hotgo/internal/library/hggen/internal/utility/utils"
)

View File

@@ -403,7 +403,7 @@ func (l *gCurd) loadView(ctx context.Context, in *CurdPreviewInput) (err error)
"importWebApi": in.options.ImportWebApi, // 导入webApi
"apiPrefix": in.options.ApiPrefix, // api前缀
"componentPrefix": componentPrefix, // vue子组件前缀
"in": in.In, // 在模版中使用`in`参数,如:插件目录名称
"in": in.In, // 在模版中使用`in`参数,如:插件目录名称
})
in.view = view
@@ -453,7 +453,7 @@ func (l *gCurd) DoBuild(ctx context.Context, in *CurdBuildInput) (err error) {
// 导入失败将sql文件删除
if err = ImportSql(ctx, vi.Path); err != nil {
_ = gfile.Remove(vi.Path)
_ = gfile.RemoveAll(vi.Path)
}
return
}

View File

@@ -109,7 +109,7 @@ var MemberSummary = gdb.HookHandler{
}
var members []*MemberSumma
if err = g.Model("admin_member").Ctx(ctx).WhereIn("id", memberIds).Scan(&members); err != nil {
if err = g.Model("admin_member").Ctx(ctx).Fields(MemberSumma{}).WhereIn("id", memberIds).Scan(&members); err != nil {
return nil, err
}

View File

@@ -158,26 +158,26 @@ func IsZipType(ext string) bool {
// 如果文件类型没有加入系统映射类型,默认认为不是合法的文件类型。建议将常用的上传文件类型加入映射关系。
// 当然你也可以不做限制,可以上传任意文件。但需要谨慎处理和设置相应的安全措施。
// 获取任意扩展名的扩展类型mime.TypeByExtension(".xls")
func GetFileMimeType(ext string) (string, error) {
func GetFileMimeType(ext string) string {
if mime, ok := imgType[ext]; ok {
return mime, nil
return mime
}
if mime, ok := docType[ext]; ok {
return mime, nil
return mime
}
if mime, ok := audioType[ext]; ok {
return mime, nil
return mime
}
if mime, ok := videoType[ext]; ok {
return mime, nil
return mime
}
if mime, ok := zipType[ext]; ok {
return mime, nil
return mime
}
if mime, ok := otherType[ext]; ok {
return mime, nil
return mime
}
return "", gerror.Newf("Invalid file type:%v", ext)
return "application/octet-stream"
}
// GetFileKind 获取文件上传类型

View File

@@ -103,10 +103,6 @@ func DoUpload(ctx context.Context, typ string, file *ghttp.UploadFile) (result *
// ValidateFileMeta 验证文件元数据
func ValidateFileMeta(typ string, meta *FileMeta) (err error) {
if _, err = GetFileMimeType(meta.Ext); err != nil {
return
}
switch typ {
case KindImg:
if !IsImgType(meta.Ext) {
@@ -152,7 +148,7 @@ func ValidateFileMeta(typ string, meta *FileMeta) (err error) {
}
if len(config.FileType) > 0 && !validate.InSlice(strings.Split(config.FileType, `,`), meta.Ext) {
err = gerror.New("上传文件类型未经允许")
err = gerror.Newf("上传文件类型未经允许:%v", meta.Ext)
return
}
}
@@ -190,10 +186,7 @@ func GetFileMeta(file *ghttp.UploadFile) (meta *FileMeta, err error) {
meta.Size = file.Size
meta.Ext = Ext(file.Filename)
meta.Kind = GetFileKind(meta.Ext)
meta.MimeType, err = GetFileMimeType(meta.Ext)
if err != nil {
return
}
meta.MimeType = GetFileMimeType(meta.Ext)
// 兼容naiveUI
naiveType := meta.MimeType
@@ -272,10 +265,7 @@ func CheckMultipart(ctx context.Context, in *CheckMultipartParams) (res *CheckMu
meta.Size = in.Size
meta.Ext = Ext(in.FileName)
meta.Kind = GetFileKind(meta.Ext)
meta.MimeType, err = GetFileMimeType(meta.Ext)
if err != nil {
return
}
meta.MimeType = GetFileMimeType(meta.Ext)
// 兼容naiveUI
naiveType := "text/plain"

View File

@@ -17,6 +17,7 @@ import (
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/library/contexts"
"hotgo/internal/library/hgorm/hook"
"hotgo/internal/library/location"
"hotgo/internal/model"
"hotgo/internal/model/entity"
@@ -80,6 +81,18 @@ func (s *sAdminCash) List(ctx context.Context, in *adminin.CashListInp) (list []
mod = mod.Where("member_id", in.MemberId)
}
// 用户筛选
if len(in.ComplexMemberId) == 2 && len(in.ComplexMemberId[0]) > 0 {
memberIds, err := service.AdminMember().GetComplexMemberIds(ctx, in.ComplexMemberId[0], in.ComplexMemberId[1])
if err != nil {
return nil, 0, err
}
if len(memberIds) == 0 {
return nil, 0, nil
}
mod = mod.WhereIn(dao.AdminOrder.Columns().MemberId, memberIds)
}
if len(in.CreatedAt) == 2 {
mod = mod.WhereBetween("created_at", gtime.New(in.CreatedAt[0]), gtime.New(in.CreatedAt[1]))
}
@@ -93,6 +106,9 @@ func (s *sAdminCash) List(ctx context.Context, in *adminin.CashListInp) (list []
mod = mod.Where("member_id", opMemberId)
}
// 申请人摘要信息
mod = mod.Hook(hook.MemberSummary)
totalCount, err = mod.Count()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
@@ -107,19 +123,6 @@ func (s *sAdminCash) List(ctx context.Context, in *adminin.CashListInp) (list []
err = gerror.Wrap(err, consts.ErrorORM)
return
}
for _, v := range list {
var member *entity.AdminMember
if err = dao.AdminMember.Ctx(ctx).Fields("real_name", "username").Where("id", v.MemberId).Scan(&member); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, totalCount, err
}
if member != nil {
v.MemberName = member.RealName
v.MemberUser = member.Username
}
}
return
}

View File

@@ -14,6 +14,7 @@ import (
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/library/hgorm/handler"
"hotgo/internal/library/hgorm/hook"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/adminin"
"hotgo/internal/model/input/form"
@@ -174,6 +175,18 @@ func (s *sAdminCreditsLog) List(ctx context.Context, in *adminin.CreditsLogListI
mod = mod.Where(dao.AdminCreditsLog.Columns().MemberId, in.MemberId)
}
// 用户筛选
if len(in.ComplexMemberId) == 2 && len(in.ComplexMemberId[0]) > 0 {
memberIds, err := service.AdminMember().GetComplexMemberIds(ctx, in.ComplexMemberId[0], in.ComplexMemberId[1])
if err != nil {
return nil, 0, err
}
if len(memberIds) == 0 {
return nil, 0, nil
}
mod = mod.WhereIn(dao.AdminOrder.Columns().MemberId, memberIds)
}
// 查询应用id
if in.AppId != "" {
mod = mod.WhereLike(dao.AdminCreditsLog.Columns().AppId, in.AppId)
@@ -209,7 +222,10 @@ func (s *sAdminCreditsLog) List(ctx context.Context, in *adminin.CreditsLogListI
mod = mod.WhereBetween(dao.AdminCreditsLog.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
}
totalCount, err = mod.Clone().Count(1)
// 操作人摘要信息
mod = mod.Hook(hook.MemberSummary)
totalCount, err = mod.Clone().Count()
if err != nil {
return
}

View File

@@ -16,6 +16,7 @@ import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/grand"
"hotgo/internal/consts"
"hotgo/internal/dao"
@@ -28,6 +29,7 @@ import (
"hotgo/internal/model/input/adminin"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
"hotgo/utility/convert"
"hotgo/utility/tree"
"hotgo/utility/validate"
"sync"
@@ -764,6 +766,52 @@ func (s *sAdminMember) Select(ctx context.Context, in *adminin.MemberSelectInp)
return
}
// GetLowerIds 获取指定用户的所有下级ID集合
func (s *sAdminMember) GetLowerIds(ctx context.Context, memberId int64) (ids []int64, err error) {
array, err := dao.AdminMember.Ctx(ctx).
Fields("id").
WhereLike("tree", "%"+tree.GenLabel("", memberId)+"%").
Array()
if err != nil {
return nil, err
}
for _, v := range array {
ids = append(ids, v.Int64())
}
return
}
// GetComplexMemberIds 组合查找符合条件的用户ID
func (s *sAdminMember) GetComplexMemberIds(ctx context.Context, memberIdx, opt string) (ids []int64, err error) {
memberId := gconv.Int64(memberIdx)
count, err := s.FilterAuthModel(ctx, contexts.GetUserId(ctx)).WherePri(memberId).Count()
if err != nil {
return nil, err
}
if count == 0 {
return
}
switch opt {
case "1": // 仅查自己
ids = append(ids, memberId)
case "2": // 仅查下级
ids, err = s.GetLowerIds(ctx, memberId)
if err != nil {
return nil, err
}
default: // 查全部
ids, err = s.GetLowerIds(ctx, memberId)
if err != nil {
return nil, err
}
ids = append(ids, memberId)
}
ids = convert.UniqueSlice(ids)
return
}
// GetIdsByKeyword 根据关键词查找符合条件的用户ID
func (s *sAdminMember) GetIdsByKeyword(ctx context.Context, ks string) (res []int64, err error) {
ks = gstr.Trim(ks)

View File

@@ -13,6 +13,7 @@ import (
"hotgo/internal/library/contexts"
"hotgo/internal/library/hgorm"
"hotgo/internal/library/hgorm/handler"
"hotgo/internal/library/hgorm/hook"
"hotgo/internal/library/payment"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/adminin"
@@ -276,11 +277,26 @@ func (s *sAdminOrder) List(ctx context.Context, in *adminin.OrderListInp) (list
mod = mod.Where(dao.AdminOrder.Columns().MemberId, in.MemberId)
}
// 下单用户筛选
if len(in.ComplexMemberId) == 2 && len(in.ComplexMemberId[0]) > 0 {
memberIds, err := service.AdminMember().GetComplexMemberIds(ctx, in.ComplexMemberId[0], in.ComplexMemberId[1])
if err != nil {
return nil, 0, err
}
if len(memberIds) == 0 {
return nil, 0, nil
}
mod = mod.WhereIn(dao.AdminOrder.Columns().MemberId, memberIds)
}
mod = mod.LeftJoin(hgorm.GenJoinOnRelation(
dao.AdminOrder.Table(), dao.AdminOrder.Columns().OrderSn, // 主表表名,关联条件
dao.PayLog.Table(), "payLog", dao.PayLog.Columns().OrderSn, // 关联表表名,别名,关联条件
)...)
// 操作人摘要信息
mod = mod.Hook(hook.MemberSummary)
totalCount, err = mod.Clone().Count(1)
if err != nil {
return

View File

@@ -357,6 +357,18 @@ func (s *sSysLog) List(ctx context.Context, in *sysin.LogListInp) (list []*sysin
mod = mod.Where("member_id", in.MemberId)
}
// 操作人筛选
if len(in.ComplexMemberId) == 2 && len(in.ComplexMemberId[0]) > 0 {
memberIds, err := service.AdminMember().GetComplexMemberIds(ctx, in.ComplexMemberId[0], in.ComplexMemberId[1])
if err != nil {
return nil, 0, err
}
if len(memberIds) == 0 {
return nil, 0, nil
}
mod = mod.WhereIn("member_id", memberIds)
}
// 访问IP
if in.Ip != "" {
mod = mod.Where("ip", in.Ip)

View File

@@ -6,6 +6,7 @@
package adminin
import (
"hotgo/internal/library/hgorm/hook"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/form"
)
@@ -24,13 +25,13 @@ type CashViewModel struct {
type CashListInp struct {
form.PageReq
form.StatusReq
MemberId int64 `json:"memberId"`
CreatedAt []int64 `json:"created_at"`
MemberId int64 `json:"memberId"`
CreatedAt []int64 `json:"created_at"`
ComplexMemberId []string `json:"complexMemberId" dc:"申请人"`
}
type CashListModel struct {
MemberUser string `json:"memberUser"`
MemberName string `json:"memberName"`
MemberBySumma *hook.MemberSumma `json:"memberBySumma" dc:"申请人信息"`
entity.AdminCash
}

View File

@@ -13,6 +13,7 @@ import (
"github.com/gogf/gf/v2/net/ghttp"
"hotgo/internal/consts"
"hotgo/internal/library/contexts"
"hotgo/internal/library/hgorm/hook"
"hotgo/internal/library/location"
"hotgo/internal/model/input/form"
@@ -98,15 +99,16 @@ type CreditsLogSaveIntegralModel struct {
// CreditsLogListInp 获取资产变动列表
type CreditsLogListInp struct {
form.PageReq
Id int64 `json:"id" dc:"变动ID"`
MemberId int64 `json:"memberId" dc:"管理员ID"`
AppId string `json:"appId" dc:"应用id"`
CreditType string `json:"creditType" dc:"变动类型"`
CreditGroup string `json:"creditGroup" dc:"变动的组别"`
Remark string `json:"remark" dc:"备注"`
Ip string `json:"ip" dc:"操作人IP"`
Status int `json:"status" dc:"状态"`
CreatedAt []*gtime.Time `json:"createdAt" dc:"创建时间"`
Id int64 `json:"id" dc:"变动ID"`
MemberId int64 `json:"memberId" dc:"管理员ID"`
AppId string `json:"appId" dc:"应用id"`
CreditType string `json:"creditType" dc:"变动类型"`
CreditGroup string `json:"creditGroup" dc:"变动的组别"`
Remark string `json:"remark" dc:"备注"`
Ip string `json:"ip" dc:"操作人IP"`
Status int `json:"status" dc:"状态"`
CreatedAt []*gtime.Time `json:"createdAt" dc:"创建时间"`
ComplexMemberId []string `json:"complexMemberId" dc:"用户"`
}
func (in *CreditsLogListInp) Filter(ctx context.Context) (err error) {
@@ -114,21 +116,22 @@ func (in *CreditsLogListInp) Filter(ctx context.Context) (err error) {
}
type CreditsLogListModel struct {
Id int64 `json:"id" dc:"变动ID"`
MemberId int64 `json:"memberId" dc:"管理员ID"`
AppId string `json:"appId" dc:"应用id"`
AddonsName string `json:"addonsName" dc:"插件名称"`
CreditType string `json:"creditType" dc:"变动类型"`
CreditGroup string `json:"creditGroup" dc:"变动的组别"`
BeforeNum float64 `json:"beforeNum" dc:"变动前"`
Num float64 `json:"num" dc:"变动数据"`
AfterNum float64 `json:"afterNum" dc:"变动后"`
Remark string `json:"remark" dc:"备注"`
Ip string `json:"ip" dc:"操作人IP"`
MapId int64 `json:"mapId" dc:"关联ID"`
Status int `json:"status" dc:"状态"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
Id int64 `json:"id" dc:"变动ID"`
MemberId int64 `json:"memberId" dc:"管理员ID"`
AppId string `json:"appId" dc:"应用id"`
AddonsName string `json:"addonsName" dc:"插件名称"`
CreditType string `json:"creditType" dc:"变动类型"`
CreditGroup string `json:"creditGroup" dc:"变动的组别"`
BeforeNum float64 `json:"beforeNum" dc:"变动前"`
Num float64 `json:"num" dc:"变动数据"`
AfterNum float64 `json:"afterNum" dc:"变动后"`
Remark string `json:"remark" dc:"备注"`
Ip string `json:"ip" dc:"操作人IP"`
MapId int64 `json:"mapId" dc:"关联ID"`
Status int `json:"status" dc:"状态"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
MemberBySumma *hook.MemberSumma `json:"memberBySumma" dc:"用户信息"`
}
// CreditsLogExportModel 导出资产变动

View File

@@ -11,6 +11,7 @@ import (
"github.com/gogf/gf/v2/os/gtime"
"hotgo/internal/consts"
"hotgo/internal/library/dict"
"hotgo/internal/library/hgorm/hook"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/form"
"hotgo/internal/model/input/payin"
@@ -114,6 +115,7 @@ type OrderListInp struct {
Status int `json:"status" dc:"状态"`
CreatedAt []*gtime.Time `json:"createdAt" dc:"创建时间"`
PayLogOutTradeNo string `json:"payLogOutTradeNo" dc:"商户订单号"`
ComplexMemberId []string `json:"complexMemberId" dc:"下单用户"`
}
func (in *OrderListInp) Filter(ctx context.Context) (err error) {
@@ -122,8 +124,9 @@ func (in *OrderListInp) Filter(ctx context.Context) (err error) {
type OrderListModel struct {
entity.AdminOrder
OutTradeNo string `json:"payLogOutTradeNo" dc:"商户订单号"`
PayType string `json:"payLogPayType" dc:"支付类型"`
OutTradeNo string `json:"payLogOutTradeNo" dc:"商户订单号"`
PayType string `json:"payLogPayType" dc:"支付类型"`
MemberBySumma *hook.MemberSumma `json:"memberBySumma" dc:"下单用户信息"`
}
// OrderExportModel 导出充值订单

View File

@@ -14,16 +14,17 @@ import (
type LogListInp struct {
form.PageReq
form.StatusReq
ReqId string `json:"reqId" dc:"对外ID"`
Module string `json:"module" dc:"应用端口"`
MemberId int `json:"memberId" dc:"用户ID"`
TakeUpTime string `json:"takeUpTime" dc:"请求耗时"`
Method string `json:"method" dc:"请求方式"`
Url string `json:"url" dc:"请求路径"`
Ip string `json:"ip" dc:"访问IP"`
ErrorCode string `json:"errorCode" dc:"状态码"`
CreatedAt []int64 `json:"createdAt" dc:"创建时间"`
Keyword string `json:"keyword" dc:"关键词"`
ReqId string `json:"reqId" dc:"对外ID"`
Module string `json:"module" dc:"应用端口"`
MemberId int `json:"memberId" dc:"用户ID"`
TakeUpTime string `json:"takeUpTime" dc:"请求耗时"`
Method string `json:"method" dc:"请求方式"`
Url string `json:"url" dc:"请求路径"`
Ip string `json:"ip" dc:"访问IP"`
ErrorCode string `json:"errorCode" dc:"状态码"`
CreatedAt []int64 `json:"createdAt" dc:"创建时间"`
Keyword string `json:"keyword" dc:"关键词"`
ComplexMemberId []string `json:"complexMemberId" dc:"操作人筛选"`
}
type LogListModel struct {

View File

@@ -106,6 +106,10 @@ type (
GetIdByCode(ctx context.Context, in *adminin.GetIdByCodeInp) (res *adminin.GetIdByCodeModel, err error)
// Select 获取可选的用户选项
Select(ctx context.Context, in *adminin.MemberSelectInp) (res []*adminin.MemberSelectModel, err error)
// GetLowerIds 获取指定用户的所有下级ID集合
GetLowerIds(ctx context.Context, memberId int64) (ids []int64, err error)
// GetComplexMemberIds 组合查找符合条件的用户ID
GetComplexMemberIds(ctx context.Context, memberIdx string, opt string) (ids []int64, err error)
// GetIdsByKeyword 根据关键词查找符合条件的用户ID
GetIdsByKeyword(ctx context.Context, ks string) (res []int64, err error)
// VerifySuperId 验证是否为超管