mirror of https://github.com/zhufuyi/sponge
support mongodb generate code
This commit is contained in:
parent
0ca5eaf5b1
commit
e10968ae32
|
@ -136,7 +136,7 @@ import (
|
|||
|
||||
{{- range .PbServices}}
|
||||
|
||||
// Test each method of {{.LowerName}} via the rpc client
|
||||
// Test each method of {{.LowerName}} via the grpc client
|
||||
func Test_service_{{.LowerName}}_methods(t *testing.T) {
|
||||
conn := getRPCClientConnForTest()
|
||||
cli := serverNameExampleV1.New{{.Name}}Client(conn)
|
||||
|
|
|
@ -120,6 +120,7 @@ func (g *stringCacheGenerator) generateCode() (string, error) {
|
|||
ignoreFiles := []string{ // specify the files in the subdirectory to be ignored for processing
|
||||
"doc.go",
|
||||
"userExample.go",
|
||||
"userExample.go.mgo",
|
||||
"userExample_test.go",
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,8 @@ const (
|
|||
DBDriverTidb = "tidb"
|
||||
// DBDriverSqlite sqlite driver
|
||||
DBDriverSqlite = "sqlite"
|
||||
// DBDriverMongodb mongodb driver
|
||||
DBDriverMongodb = "mongodb"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -41,10 +43,12 @@ var (
|
|||
cacheFile = "cache/cacheNameExample.go"
|
||||
|
||||
daoFile = "dao/userExample.go"
|
||||
daoMgoFile = "dao/userExample.go.mgo"
|
||||
daoFileMark = "// todo generate the update fields code to here"
|
||||
daoTestFile = "dao/userExample_test.go"
|
||||
|
||||
handlerFile = "types/userExample_types.go"
|
||||
handlerMgoFile = "types/userExample_types.go.mgo"
|
||||
handlerFileMark = "// todo generate the request and response struct to here"
|
||||
handlerTestFile = "handler/userExample_test.go"
|
||||
|
||||
|
@ -57,9 +61,10 @@ var (
|
|||
protoFile = "v1/userExample.proto"
|
||||
protoFileMark = "// todo generate the protobuf code here"
|
||||
|
||||
serviceTestFile = "service/userExample_test.go"
|
||||
serviceClientFile = "service/userExample_client_test.go"
|
||||
serviceFileMark = "// todo generate the service struct code here"
|
||||
serviceTestFile = "service/userExample_test.go"
|
||||
serviceClientFile = "service/userExample_client_test.go"
|
||||
serviceClientMgoFile = "service/userExample_client_test.go.mgo"
|
||||
serviceFileMark = "// todo generate the service struct code here"
|
||||
|
||||
dockerFile = "scripts/build/Dockerfile"
|
||||
dockerFileMark = "# todo generate dockerfile code for http or grpc here"
|
||||
|
@ -138,7 +143,10 @@ func convertProjectAndServerName(projectName, serverName string) (pn string, sn
|
|||
return pn, sn
|
||||
}
|
||||
|
||||
func adjustmentOfIDType(handlerCodes string) string {
|
||||
func adjustmentOfIDType(handlerCodes string, dbDriver string) string {
|
||||
if dbDriver == DBDriverMongodb {
|
||||
return idTypeToStr(handlerCodes)
|
||||
}
|
||||
return idTypeToStr(idTypeFixToUint64(handlerCodes))
|
||||
}
|
||||
|
||||
|
@ -374,33 +382,6 @@ func replacePackage(data []byte, moduleName string, serverName string) []byte {
|
|||
return data
|
||||
}
|
||||
|
||||
//func getDBConfigCode(dbDriver string, forDeployment ...bool) string {
|
||||
// isDeployment := false
|
||||
// if len(forDeployment) > 0 {
|
||||
// isDeployment = forDeployment[0]
|
||||
// }
|
||||
//
|
||||
// dbConfigCode := ""
|
||||
// switch strings.ToLower(dbDriver) {
|
||||
// case DBDriverMysql, DBDriverTidb:
|
||||
// if isDeployment {
|
||||
// dbConfigCode = mysqlConfigForDeploymentCode
|
||||
// } else {
|
||||
// dbConfigCode = mysqlConfigCode
|
||||
// }
|
||||
//
|
||||
// case DBDriverPostgresql:
|
||||
// if isDeployment {
|
||||
// dbConfigCode = postgresqlConfigForDeploymentCode
|
||||
// } else {
|
||||
// dbConfigCode = postgresqlConfigCode
|
||||
// }
|
||||
// default:
|
||||
// panic("getDBConfigCode error, unsupported database driver: " + dbDriver)
|
||||
// }
|
||||
// return dbConfigCode
|
||||
//}
|
||||
|
||||
func getDBConfigCode(dbDriver string) string {
|
||||
dbConfigCode := ""
|
||||
switch strings.ToLower(dbDriver) {
|
||||
|
@ -410,6 +391,8 @@ func getDBConfigCode(dbDriver string) string {
|
|||
dbConfigCode = postgresqlConfigCode
|
||||
case DBDriverSqlite:
|
||||
dbConfigCode = sqliteConfigCode
|
||||
case DBDriverMongodb:
|
||||
dbConfigCode = mongodbConfigCode
|
||||
default:
|
||||
panic("getDBConfigCode error, unsupported database driver: " + dbDriver)
|
||||
}
|
||||
|
@ -425,6 +408,8 @@ func getInitDBCode(dbDriver string) string {
|
|||
initDBCode = modelInitDBFilePostgresqlCode
|
||||
case DBDriverSqlite:
|
||||
initDBCode = modelInitDBFileSqliteCode
|
||||
case DBDriverMongodb:
|
||||
initDBCode = "" // do nothing
|
||||
default:
|
||||
panic("getInitDBCode error, unsupported database driver: " + dbDriver)
|
||||
}
|
||||
|
@ -509,3 +494,13 @@ func sqliteDSNAdaptation(dbDriver string, dsn string) string {
|
|||
}
|
||||
return dsn
|
||||
}
|
||||
|
||||
func removeElement(slice []string, element string) []string {
|
||||
result := make([]string, 0, len(slice)-1)
|
||||
for _, s := range slice {
|
||||
if s != element {
|
||||
result = append(result, s)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
|
@ -29,8 +29,8 @@ func DaoCommand(parentName string) *cobra.Command {
|
|||
|
||||
cmd := &cobra.Command{
|
||||
Use: "dao",
|
||||
Short: "Generate dao code based on mysql table",
|
||||
Long: fmt.Sprintf(`generate dao code based on mysql table.
|
||||
Short: "Generate dao code based on sql",
|
||||
Long: fmt.Sprintf(`generate dao code based on sql.
|
||||
|
||||
Examples:
|
||||
# generate dao code and embed gorm.model struct.
|
||||
|
@ -61,6 +61,9 @@ Examples:
|
|||
continue
|
||||
}
|
||||
|
||||
if sqlArgs.DBDriver == DBDriverMongodb {
|
||||
sqlArgs.IsEmbed = false
|
||||
}
|
||||
sqlArgs.DBTable = tableName
|
||||
codes, err := sql2code.Generate(&sqlArgs)
|
||||
if err != nil {
|
||||
|
@ -76,6 +79,7 @@ Examples:
|
|||
|
||||
g := &daoGenerator{
|
||||
moduleName: moduleName,
|
||||
dbDriver: sqlArgs.DBDriver,
|
||||
isIncludeInitDB: isIncludeInitDB,
|
||||
codes: codes,
|
||||
outPath: outPath,
|
||||
|
@ -98,7 +102,7 @@ using help:
|
|||
|
||||
cmd.Flags().StringVarP(&moduleName, "module-name", "m", "", "module-name is the name of the module in the go.mod file")
|
||||
//_ = cmd.MarkFlagRequired("module-name")
|
||||
cmd.Flags().StringVarP(&sqlArgs.DBDriver, "db-driver", "k", "mysql", "database driver, support mysql, postgresql, tidb, sqlite")
|
||||
cmd.Flags().StringVarP(&sqlArgs.DBDriver, "db-driver", "k", "mysql", "database driver, support mysql, mongodb, postgresql, tidb, sqlite")
|
||||
cmd.Flags().StringVarP(&sqlArgs.DBDsn, "db-dsn", "d", "", "database content address, e.g. user:password@(host:port)/database. Note: if db-driver=sqlite, db-dsn must be a local sqlite db file, e.g. --db-dsn=/tmp/sponge_sqlite.db") //nolint
|
||||
_ = cmd.MarkFlagRequired("db-dsn")
|
||||
cmd.Flags().StringVarP(&dbTables, "db-table", "t", "", "table name, multiple names separated by commas")
|
||||
|
@ -114,6 +118,7 @@ using help:
|
|||
|
||||
type daoGenerator struct {
|
||||
moduleName string
|
||||
dbDriver string
|
||||
isIncludeInitDB bool
|
||||
codes map[string]string
|
||||
outPath string
|
||||
|
@ -131,14 +136,28 @@ func (g *daoGenerator) generateCode() (string, error) {
|
|||
"internal/model", "internal/cache", "internal/dao",
|
||||
}
|
||||
ignoreDirs := []string{} // specify the directory in the subdirectory where processing is ignored
|
||||
ignoreFiles := []string{ // specify the files in the subdirectory to be ignored for processing
|
||||
"init.go", "init_test.go", // internal/model
|
||||
"doc.go", "cacheNameExample.go", "cacheNameExample_test.go", // internal/cache
|
||||
}
|
||||
if g.isIncludeInitDB {
|
||||
ignoreFiles = []string{
|
||||
"doc.go", "cacheNameExample.go", "cacheNameExample_test.go", // internal/cache
|
||||
var ignoreFiles []string
|
||||
switch strings.ToLower(g.dbDriver) {
|
||||
case DBDriverMysql, DBDriverPostgresql, DBDriverTidb, DBDriverSqlite:
|
||||
ignoreFiles = []string{ // specify the files in the subdirectory to be ignored for processing
|
||||
"init.go", "init_test.go", "init.go.mgo", // internal/model
|
||||
"doc.go", "cacheNameExample.go", "cacheNameExample_test.go", "cache/userExample.go.mgo", // internal/cache
|
||||
"dao/userExample.go.mgo", // internal/dao
|
||||
}
|
||||
if g.isIncludeInitDB {
|
||||
ignoreFiles = removeElement(ignoreFiles, "init.go")
|
||||
}
|
||||
case DBDriverMongodb:
|
||||
ignoreFiles = []string{ // specify the files in the subdirectory to be ignored for processing
|
||||
"init.go", "init_test.go", "init.go.mgo", // internal/model
|
||||
"doc.go", "cacheNameExample.go", "cacheNameExample_test.go", "cache/userExample.go", "cache/userExample_test.go", // internal/cache
|
||||
"dao/userExample_test.go", "dao/userExample.go", // internal/dao
|
||||
}
|
||||
if g.isIncludeInitDB {
|
||||
ignoreFiles = removeElement(ignoreFiles, "init.go.mgo")
|
||||
}
|
||||
default:
|
||||
return "", errors.New("unsupported db driver: " + g.dbDriver)
|
||||
}
|
||||
|
||||
r.SetSubDirsAndFiles(subDirs)
|
||||
|
@ -160,6 +179,7 @@ func (g *daoGenerator) addFields(r replacer.Replacer) []replacer.Field {
|
|||
|
||||
fields = append(fields, deleteFieldsMark(r, modelFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, daoFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, daoMgoFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, daoTestFile, startMark, endMark)...)
|
||||
fields = append(fields, []replacer.Field{
|
||||
{ // replace the contents of the model/userExample.go file
|
||||
|
@ -182,6 +202,14 @@ func (g *daoGenerator) addFields(r replacer.Replacer) []replacer.Field {
|
|||
Old: g.moduleName + "/pkg",
|
||||
New: "github.com/zhufuyi/sponge/pkg",
|
||||
},
|
||||
{
|
||||
Old: "init.go.mgo",
|
||||
New: "init.go",
|
||||
},
|
||||
{
|
||||
Old: "userExample.go.mgo",
|
||||
New: "userExample.go",
|
||||
},
|
||||
{
|
||||
Old: "UserExample",
|
||||
New: g.codes[parser.TableName],
|
||||
|
|
|
@ -32,8 +32,8 @@ func HandlerPbCommand() *cobra.Command {
|
|||
|
||||
cmd := &cobra.Command{
|
||||
Use: "handler-pb",
|
||||
Short: "Generate handler and protobuf code based on mysql table",
|
||||
Long: `generate handler and protobuf code based on mysql table.
|
||||
Short: "Generate handler and protobuf code based on sql",
|
||||
Long: `generate handler and protobuf code based on sql.
|
||||
|
||||
Examples:
|
||||
# generate handler and protobuf code and embed gorm.model struct.
|
||||
|
@ -64,6 +64,9 @@ Examples:
|
|||
}
|
||||
|
||||
serverName = convertServerName(serverName)
|
||||
if sqlArgs.DBDriver == DBDriverMongodb {
|
||||
sqlArgs.IsEmbed = false
|
||||
}
|
||||
|
||||
tableNames := strings.Split(dbTables, ",")
|
||||
for _, tableName := range tableNames {
|
||||
|
@ -80,6 +83,7 @@ Examples:
|
|||
g := &handlerPbGenerator{
|
||||
moduleName: moduleName,
|
||||
serverName: serverName,
|
||||
dbDriver: sqlArgs.DBDriver,
|
||||
isEmbed: sqlArgs.IsEmbed,
|
||||
codes: codes,
|
||||
outPath: outPath,
|
||||
|
@ -107,7 +111,7 @@ using help:
|
|||
//_ = cmd.MarkFlagRequired("module-name")
|
||||
cmd.Flags().StringVarP(&serverName, "server-name", "s", "", "server name")
|
||||
//_ = cmd.MarkFlagRequired("server-name")
|
||||
cmd.Flags().StringVarP(&sqlArgs.DBDriver, "db-driver", "k", "mysql", "database driver, support mysql, postgresql, tidb, sqlite")
|
||||
cmd.Flags().StringVarP(&sqlArgs.DBDriver, "db-driver", "k", "mysql", "database driver, support mysql, mongodb, postgresql, tidb, sqlite")
|
||||
cmd.Flags().StringVarP(&sqlArgs.DBDsn, "db-dsn", "d", "", "database content address, e.g. user:password@(host:port)/database. Note: if db-driver=sqlite, db-dsn must be a local sqlite db file, e.g. --db-dsn=/tmp/sponge_sqlite.db") //nolint
|
||||
_ = cmd.MarkFlagRequired("db-dsn")
|
||||
cmd.Flags().StringVarP(&dbTables, "db-table", "t", "", "table name, multiple names separated by commas")
|
||||
|
@ -123,6 +127,7 @@ using help:
|
|||
type handlerPbGenerator struct {
|
||||
moduleName string
|
||||
serverName string
|
||||
dbDriver string
|
||||
isEmbed bool
|
||||
codes map[string]string
|
||||
outPath string
|
||||
|
@ -143,12 +148,28 @@ func (g *handlerPbGenerator) generateCode() (string, error) {
|
|||
subDirs := []string{"internal/model", "internal/cache", "internal/dao", "internal/ecode",
|
||||
"internal/handler", "api/serverNameExample"} // only the specified subdirectory is processed, if empty or no subdirectory is specified, it means all files
|
||||
ignoreDirs := []string{} // specify the directory in the subdirectory where processing is ignored
|
||||
ignoreFiles := []string{ // specify the files in the subdirectory to be ignored for processing
|
||||
"userExample.pb.go", "userExample.pb.validate.go", "userExample_grpc.pb.go", "userExample_router.pb.go", // api/serverNameExample
|
||||
"systemCode_http.go", "systemCode_rpc.go", "userExample_rpc.go", // internal/ecode
|
||||
"init.go", "init_test.go", // internal/model
|
||||
"handler/userExample.go", "handler/userExample_test.go", "handler/userExample_logic_test.go", // internal/handler
|
||||
"doc.go", "cacheNameExample.go", "cacheNameExample_test.go", // internal/cache
|
||||
var ignoreFiles []string
|
||||
switch strings.ToLower(g.dbDriver) {
|
||||
case DBDriverMysql, DBDriverPostgresql, DBDriverTidb, DBDriverSqlite:
|
||||
ignoreFiles = []string{ // specify the files in the subdirectory to be ignored for processing
|
||||
"userExample.pb.go", "userExample.pb.validate.go", "userExample_grpc.pb.go", "userExample_router.pb.go", // api/serverNameExample
|
||||
"systemCode_http.go", "systemCode_rpc.go", "userExample_rpc.go", // internal/ecode
|
||||
"init.go", "init_test.go", "init.go.mgo", // internal/model
|
||||
"doc.go", "cacheNameExample.go", "cacheNameExample_test.go", "cache/userExample.go.mgo", // internal/cache
|
||||
"dao/userExample.go.mgo", // internal/dao
|
||||
"handler/userExample.go", "handler/userExample_test.go", "handler/userExample_logic_test.go", "handler/userExample_logic.go.mgo", // internal/handler
|
||||
}
|
||||
case DBDriverMongodb:
|
||||
ignoreFiles = []string{ // specify the files in the subdirectory to be ignored for processing
|
||||
"userExample.pb.go", "userExample.pb.validate.go", "userExample_grpc.pb.go", "userExample_router.pb.go", // api/serverNameExample
|
||||
"systemCode_http.go", "systemCode_rpc.go", "userExample_rpc.go", // internal/ecode
|
||||
"init.go", "init_test.go", "init.go.mgo", // internal/model
|
||||
"doc.go", "cacheNameExample.go", "cacheNameExample_test.go", "cache/userExample.go", "cache/userExample_test.go", // internal/cache
|
||||
"dao/userExample_test.go", "dao/userExample.go", // internal/dao
|
||||
"handler/userExample.go", "handler/userExample_test.go", "handler/userExample_logic_test.go", "handler/userExample_test.go", "handler/userExample_logic.go", // internal/handler
|
||||
}
|
||||
default:
|
||||
return "", errors.New("unsupported db driver: " + g.dbDriver)
|
||||
}
|
||||
|
||||
r.SetSubDirsAndFiles(subDirs)
|
||||
|
@ -169,6 +190,7 @@ func (g *handlerPbGenerator) addFields(r replacer.Replacer) []replacer.Field {
|
|||
|
||||
fields = append(fields, deleteFieldsMark(r, modelFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, daoFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, daoMgoFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, daoTestFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, handlerLogicFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, protoFile, startMark, endMark)...)
|
||||
|
@ -219,10 +241,18 @@ func (g *handlerPbGenerator) addFields(r replacer.Replacer) []replacer.Field {
|
|||
Old: g.moduleName + "/pkg",
|
||||
New: "github.com/zhufuyi/sponge/pkg",
|
||||
},
|
||||
{
|
||||
Old: "userExample_logic.go.mgo",
|
||||
New: "userExample.go",
|
||||
},
|
||||
{
|
||||
Old: "userExample_logic.go",
|
||||
New: "userExample.go",
|
||||
},
|
||||
{
|
||||
Old: "userExample.go.mgo",
|
||||
New: "userExample.go",
|
||||
},
|
||||
{
|
||||
Old: "UserExamplePb",
|
||||
New: "UserExample",
|
||||
|
|
|
@ -29,8 +29,8 @@ func HandlerCommand() *cobra.Command {
|
|||
|
||||
cmd := &cobra.Command{
|
||||
Use: "handler",
|
||||
Short: "Generate handler code based on mysql table",
|
||||
Long: `generate handler code based on mysql table.
|
||||
Short: "Generate handler code based on sql",
|
||||
Long: `generate handler code based on sql.
|
||||
|
||||
Examples:
|
||||
# generate handler code and embed gorm.model struct.
|
||||
|
@ -54,6 +54,9 @@ Examples:
|
|||
} else if moduleName == "" {
|
||||
return errors.New(`required flag(s) "module-name" not set, use "sponge web handler -h" for help`)
|
||||
}
|
||||
if sqlArgs.DBDriver == DBDriverMongodb {
|
||||
sqlArgs.IsEmbed = false
|
||||
}
|
||||
|
||||
tableNames := strings.Split(dbTables, ",")
|
||||
for _, tableName := range tableNames {
|
||||
|
@ -69,6 +72,7 @@ Examples:
|
|||
|
||||
g := &handlerGenerator{
|
||||
moduleName: moduleName,
|
||||
dbDriver: sqlArgs.DBDriver,
|
||||
codes: codes,
|
||||
outPath: outPath,
|
||||
}
|
||||
|
@ -93,7 +97,7 @@ using help:
|
|||
|
||||
cmd.Flags().StringVarP(&moduleName, "module-name", "m", "", "module-name is the name of the module in the go.mod file")
|
||||
//_ = cmd.MarkFlagRequired("module-name")
|
||||
cmd.Flags().StringVarP(&sqlArgs.DBDriver, "db-driver", "k", "mysql", "database driver, support mysql, postgresql, tidb, sqlite")
|
||||
cmd.Flags().StringVarP(&sqlArgs.DBDriver, "db-driver", "k", "mysql", "database driver, support mysql, mongodb, postgresql, tidb, sqlite")
|
||||
cmd.Flags().StringVarP(&sqlArgs.DBDsn, "db-dsn", "d", "", "database content address, e.g. user:password@(host:port)/database. Note: if db-driver=sqlite, db-dsn must be a local sqlite db file, e.g. --db-dsn=/tmp/sponge_sqlite.db") //nolint
|
||||
_ = cmd.MarkFlagRequired("db-dsn")
|
||||
cmd.Flags().StringVarP(&dbTables, "db-table", "t", "", "table name, multiple names separated by commas")
|
||||
|
@ -108,6 +112,7 @@ using help:
|
|||
|
||||
type handlerGenerator struct {
|
||||
moduleName string
|
||||
dbDriver string
|
||||
codes map[string]string
|
||||
outPath string
|
||||
}
|
||||
|
@ -123,13 +128,30 @@ func (g *handlerGenerator) generateCode() (string, error) {
|
|||
subDirs := []string{"internal/model", "internal/cache", "internal/dao",
|
||||
"internal/ecode", "internal/handler", "internal/routers", "internal/types"} // only the specified subdirectory is processed, if empty or no subdirectory is specified, it means all files
|
||||
ignoreDirs := []string{} // specify the directory in the subdirectory where processing is ignored
|
||||
ignoreFiles := []string{ // specify the files in the subdirectory to be ignored for processing
|
||||
"systemCode_http.go", "systemCode_rpc.go", "userExample_rpc.go", // internal/ecode
|
||||
"init.go", "init_test.go", // internal/model
|
||||
"routers.go", "routers_test.go", "routers_pbExample.go", "routers_pbExample_test.go", "userExample_router.go", // internal/routers
|
||||
"swagger_types.go", // internal/types
|
||||
"doc.go", "cacheNameExample.go", "cacheNameExample_test.go", // internal/cache
|
||||
"handler/userExample_logic.go", "handler/userExample_logic_test.go", // internal/handler
|
||||
var ignoreFiles []string
|
||||
switch strings.ToLower(g.dbDriver) {
|
||||
case DBDriverMysql, DBDriverPostgresql, DBDriverTidb, DBDriverSqlite:
|
||||
ignoreFiles = []string{ // specify the files in the subdirectory to be ignored for processing
|
||||
"systemCode_http.go", "systemCode_rpc.go", "userExample_rpc.go", // internal/ecode
|
||||
"routers.go", "routers_test.go", "routers_pbExample.go", "routers_pbExample_test.go", "userExample_router.go", // internal/routers
|
||||
"init.go", "init_test.go", "init.go.mgo", // internal/model
|
||||
"doc.go", "cacheNameExample.go", "cacheNameExample_test.go", "cache/userExample.go.mgo", // internal/cache
|
||||
"dao/userExample.go.mgo", // internal/dao
|
||||
"handler/userExample_logic.go", "handler/userExample_logic_test.go", "handler/userExample.go.mgo", "handler/userExample_logic.go.mgo", // internal/handler
|
||||
"swagger_types.go", "userExample_types.go.mgo", // internal/types
|
||||
}
|
||||
case DBDriverMongodb:
|
||||
ignoreFiles = []string{ // specify the files in the subdirectory to be ignored for processing
|
||||
"systemCode_http.go", "systemCode_rpc.go", "userExample_rpc.go", // internal/ecode
|
||||
"routers.go", "routers_test.go", "routers_pbExample.go", "routers_pbExample_test.go", "userExample_router.go", // internal/routers
|
||||
"init.go", "init_test.go", "init.go.mgo", // internal/model
|
||||
"doc.go", "cacheNameExample.go", "cacheNameExample_test.go", "cache/userExample.go", "cache/userExample_test.go", // internal/cache
|
||||
"dao/userExample_test.go", "dao/userExample.go", // internal/dao
|
||||
"handler/userExample_logic.go", "handler/userExample_logic_test.go", "handler/userExample.go", "handler/userExample_test.go", "handler/userExample_logic.go.mgo", // internal/handler
|
||||
"swagger_types.go", "userExample_types.go", // internal/types
|
||||
}
|
||||
default:
|
||||
return "", errors.New("unsupported db driver: " + g.dbDriver)
|
||||
}
|
||||
|
||||
r.SetSubDirsAndFiles(subDirs)
|
||||
|
@ -150,8 +172,10 @@ func (g *handlerGenerator) addFields(r replacer.Replacer) []replacer.Field {
|
|||
|
||||
fields = append(fields, deleteFieldsMark(r, modelFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, daoFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, daoMgoFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, daoTestFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, handlerFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, handlerMgoFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, handlerTestFile, startMark, endMark)...)
|
||||
fields = append(fields, []replacer.Field{
|
||||
{ // replace the contents of the model/userExample.go file
|
||||
|
@ -164,7 +188,7 @@ func (g *handlerGenerator) addFields(r replacer.Replacer) []replacer.Field {
|
|||
},
|
||||
{ // replace the contents of the handler/userExample.go file
|
||||
Old: handlerFileMark,
|
||||
New: adjustmentOfIDType(g.codes[parser.CodeTypeHandler]),
|
||||
New: adjustmentOfIDType(g.codes[parser.CodeTypeHandler], g.dbDriver),
|
||||
},
|
||||
{
|
||||
Old: selfPackageName + "/" + r.GetSourcePath(),
|
||||
|
@ -182,6 +206,14 @@ func (g *handlerGenerator) addFields(r replacer.Replacer) []replacer.Field {
|
|||
Old: g.moduleName + "/pkg",
|
||||
New: "github.com/zhufuyi/sponge/pkg",
|
||||
},
|
||||
{
|
||||
Old: "userExample_types.go.mgo",
|
||||
New: "userExample_types.go",
|
||||
},
|
||||
{
|
||||
Old: "userExample.go.mgo",
|
||||
New: "userExample.go",
|
||||
},
|
||||
{
|
||||
Old: "UserExample",
|
||||
New: g.codes[parser.TableName],
|
||||
|
|
|
@ -33,8 +33,8 @@ func HTTPCommand() *cobra.Command {
|
|||
//nolint
|
||||
cmd := &cobra.Command{
|
||||
Use: "http",
|
||||
Short: "Generate web service code based on mysql table",
|
||||
Long: `generate web service code based on mysql table.
|
||||
Short: "Generate web service code based on sql",
|
||||
Long: `generate web service code based on sql.
|
||||
|
||||
Examples:
|
||||
# generate web service code and embed gorm.model struct.
|
||||
|
@ -66,6 +66,9 @@ Examples:
|
|||
}
|
||||
|
||||
projectName, serverName = convertProjectAndServerName(projectName, serverName)
|
||||
if sqlArgs.DBDriver == DBDriverMongodb {
|
||||
sqlArgs.IsEmbed = false
|
||||
}
|
||||
|
||||
sqlArgs.DBTable = firstTable
|
||||
codes, err := sql2code.Generate(&sqlArgs)
|
||||
|
@ -100,6 +103,7 @@ Examples:
|
|||
|
||||
hg := &handlerGenerator{
|
||||
moduleName: moduleName,
|
||||
dbDriver: sqlArgs.DBDriver,
|
||||
codes: codes,
|
||||
outPath: outPath,
|
||||
}
|
||||
|
@ -129,7 +133,7 @@ using help:
|
|||
_ = cmd.MarkFlagRequired("server-name")
|
||||
cmd.Flags().StringVarP(&projectName, "project-name", "p", "", "project name")
|
||||
_ = cmd.MarkFlagRequired("project-name")
|
||||
cmd.Flags().StringVarP(&sqlArgs.DBDriver, "db-driver", "k", "mysql", "database driver, support mysql, postgresql, tidb, sqlite")
|
||||
cmd.Flags().StringVarP(&sqlArgs.DBDriver, "db-driver", "k", "mysql", "database driver, support mysql, mongodb, postgresql, tidb, sqlite")
|
||||
cmd.Flags().StringVarP(&sqlArgs.DBDsn, "db-dsn", "d", "", "database content address, e.g. user:password@(host:port)/database. Note: if db-driver=sqlite, db-dsn must be a local sqlite db file, e.g. --db-dsn=/tmp/sponge_sqlite.db") //nolint
|
||||
_ = cmd.MarkFlagRequired("db-dsn")
|
||||
cmd.Flags().StringVarP(&dbTables, "db-table", "t", "", "table name, multiple names separated by commas")
|
||||
|
@ -172,15 +176,36 @@ func (g *httpGenerator) generateCode() (string, error) {
|
|||
ignoreDirs := []string{ // specify the directory in the subdirectory where processing is ignored
|
||||
"internal/service", "internal/rpcclient", "cmd/sponge",
|
||||
}
|
||||
ignoreFiles := []string{ // specify the files in the subdirectory to be ignored for processing
|
||||
"swagger.json", "swagger.yaml", "apis.swagger.json", "apis.html", "apis.go", // sponge/docs
|
||||
"userExample_rpc.go", "systemCode_rpc.go", // internal/ecode
|
||||
"routers_pbExample.go", "routers_pbExample_test.go", "userExample_router.go", // internal/routers
|
||||
"grpc.go", "grpc_option.go", "grpc_test.go", // internal/server
|
||||
"doc.go", "cacheNameExample.go", "cacheNameExample_test.go", // internal/cache
|
||||
"handler/userExample_logic.go", "handler/userExample_logic_test.go", // internal/handler
|
||||
"scripts/image-rpc-test.sh", "scripts/patch.sh", "scripts/protoc.sh", "scripts/proto-doc.sh", // sponge/scripts
|
||||
"init_test.go", // model
|
||||
var ignoreFiles []string
|
||||
switch strings.ToLower(g.dbDriver) {
|
||||
case DBDriverMysql, DBDriverPostgresql, DBDriverTidb, DBDriverSqlite:
|
||||
ignoreFiles = []string{ // specify the files in the subdirectory to be ignored for processing
|
||||
"swagger.json", "swagger.yaml", "apis.swagger.json", "apis.html", "apis.go", // sponge/docs
|
||||
"userExample_rpc.go", "systemCode_rpc.go", // internal/ecode
|
||||
"routers_pbExample.go", "routers_pbExample_test.go", "userExample_router.go", // internal/routers
|
||||
"grpc.go", "grpc_option.go", "grpc_test.go", // internal/server
|
||||
"scripts/image-rpc-test.sh", "scripts/patch.sh", "scripts/protoc.sh", "scripts/proto-doc.sh", // sponge/scripts
|
||||
"init_test.go", "init.go.mgo", // model
|
||||
"doc.go", "cacheNameExample.go", "cacheNameExample_test.go", "cache/userExample.go.mgo", // internal/cache
|
||||
"dao/userExample.go.mgo", // internal/dao
|
||||
"handler/userExample_logic.go", "handler/userExample_logic_test.go", "handler/userExample.go.mgo", "handler/userExample_logic.go.mgo", // internal/handler
|
||||
"userExample_types.go.mgo", // internal/types
|
||||
}
|
||||
case DBDriverMongodb:
|
||||
ignoreFiles = []string{ // specify the files in the subdirectory to be ignored for processing
|
||||
"swagger.json", "swagger.yaml", "apis.swagger.json", "apis.html", "apis.go", // sponge/docs
|
||||
"userExample_rpc.go", "systemCode_rpc.go", // internal/ecode
|
||||
"routers_pbExample.go", "routers_pbExample_test.go", "userExample_router.go", // internal/routers
|
||||
"grpc.go", "grpc_option.go", "grpc_test.go", // internal/server
|
||||
"scripts/image-rpc-test.sh", "scripts/patch.sh", "scripts/protoc.sh", "scripts/proto-doc.sh", // sponge/scripts
|
||||
"init_test.go", "init.go", // model
|
||||
"doc.go", "cacheNameExample.go", "cacheNameExample_test.go", "cache/userExample.go", "cache/userExample_test.go", // internal/cache
|
||||
"dao/userExample_test.go", "dao/userExample.go", // internal/dao
|
||||
"handler/userExample_logic.go", "handler/userExample_logic_test.go", "handler/userExample.go", "handler/userExample_test.go", "handler/userExample_logic.go.mgo", // internal/handler
|
||||
"userExample_types.go", // internal/types
|
||||
}
|
||||
default:
|
||||
return "", errors.New("unsupported db driver: " + g.dbDriver)
|
||||
}
|
||||
|
||||
r.SetSubDirsAndFiles(subDirs, subFiles...)
|
||||
|
@ -205,8 +230,10 @@ func (g *httpGenerator) addFields(r replacer.Replacer) []replacer.Field {
|
|||
fields = append(fields, deleteFieldsMark(r, modelFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, modelInitDBFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, daoFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, daoMgoFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, daoTestFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, handlerFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, handlerMgoFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, handlerTestFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, httpFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, dockerFile, wellStartMark, wellEndMark)...)
|
||||
|
@ -245,7 +272,7 @@ func (g *httpGenerator) addFields(r replacer.Replacer) []replacer.Field {
|
|||
},
|
||||
{ // replace the contents of the handler/userExample.go file
|
||||
Old: handlerFileMark,
|
||||
New: adjustmentOfIDType(g.codes[parser.CodeTypeHandler]),
|
||||
New: adjustmentOfIDType(g.codes[parser.CodeTypeHandler], g.dbDriver),
|
||||
},
|
||||
{ // replace the contents of the Dockerfile file
|
||||
Old: dockerFileMark,
|
||||
|
@ -353,6 +380,10 @@ func (g *httpGenerator) addFields(r replacer.Replacer) []replacer.Field {
|
|||
Old: "root:123456@(192.168.3.37:3306)/account",
|
||||
New: g.dbDSN,
|
||||
},
|
||||
{
|
||||
Old: "root:123456@192.168.3.37:27017/account",
|
||||
New: g.dbDSN,
|
||||
},
|
||||
{
|
||||
Old: "root:123456@192.168.3.37:5432/account",
|
||||
New: g.dbDSN,
|
||||
|
@ -365,6 +396,18 @@ func (g *httpGenerator) addFields(r replacer.Replacer) []replacer.Field {
|
|||
Old: "Makefile-for-http",
|
||||
New: "Makefile",
|
||||
},
|
||||
{
|
||||
Old: "init.go.mgo",
|
||||
New: "init.go",
|
||||
},
|
||||
{
|
||||
Old: "userExample_types.go.mgo",
|
||||
New: "userExample_types.go",
|
||||
},
|
||||
{
|
||||
Old: "userExample.go.mgo",
|
||||
New: "userExample.go",
|
||||
},
|
||||
{
|
||||
Old: "UserExample",
|
||||
New: g.codes[parser.TableName],
|
||||
|
|
|
@ -27,8 +27,8 @@ func ModelCommand(parentName string) *cobra.Command {
|
|||
|
||||
cmd := &cobra.Command{
|
||||
Use: "model",
|
||||
Short: "Generate model code based on mysql table",
|
||||
Long: fmt.Sprintf(`generate model code based on mysql table.
|
||||
Short: "Generate model code based on sql",
|
||||
Long: fmt.Sprintf(`generate model code based on sql.
|
||||
|
||||
Examples:
|
||||
# generate model code and embed gorm.model struct.
|
||||
|
@ -52,6 +52,9 @@ Examples:
|
|||
continue
|
||||
}
|
||||
|
||||
if sqlArgs.DBDriver == DBDriverMongodb {
|
||||
sqlArgs.IsEmbed = false
|
||||
}
|
||||
sqlArgs.DBTable = tableName
|
||||
codes, err := sql2code.Generate(&sqlArgs)
|
||||
if err != nil {
|
||||
|
@ -78,7 +81,7 @@ using help:
|
|||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVarP(&sqlArgs.DBDriver, "db-driver", "k", "mysql", "database driver, support mysql, postgresql, tidb, sqlite")
|
||||
cmd.Flags().StringVarP(&sqlArgs.DBDriver, "db-driver", "k", "mysql", "database driver, support mysql, mongodb, postgresql, tidb, sqlite")
|
||||
cmd.Flags().StringVarP(&sqlArgs.DBDsn, "db-dsn", "d", "", "database content address, e.g. user:password@(host:port)/database. Note: if db-driver=sqlite, db-dsn must be a local sqlite db file, e.g. --db-dsn=/tmp/sponge_sqlite.db") //nolint
|
||||
_ = cmd.MarkFlagRequired("db-dsn")
|
||||
cmd.Flags().StringVarP(&dbTables, "db-table", "t", "", "table name, multiple names separated by commas")
|
||||
|
@ -106,7 +109,7 @@ func (g *modelGenerator) generateCode() (string, error) {
|
|||
subDirs := []string{"internal/model"} // only the specified subdirectory is processed, if empty or no subdirectory is specified, it means all files
|
||||
ignoreDirs := []string{} // specify the directory in the subdirectory where processing is ignored
|
||||
ignoreFiles := []string{ // specify the files in the subdirectory to be ignored for processing
|
||||
"init.go", "init_test.go",
|
||||
"init.go", "init_test.go", "init.go.mgo",
|
||||
}
|
||||
|
||||
r.SetSubDirsAndFiles(subDirs)
|
||||
|
|
|
@ -28,8 +28,8 @@ func ProtobufCommand() *cobra.Command {
|
|||
|
||||
cmd := &cobra.Command{
|
||||
Use: "protobuf",
|
||||
Short: "Generate protobuf code based on mysql table",
|
||||
Long: `generate protobuf code based on mysql table.
|
||||
Short: "Generate protobuf code based on sql",
|
||||
Long: `generate protobuf code based on sql.
|
||||
|
||||
Examples:
|
||||
# generate protobuf code.
|
||||
|
@ -60,6 +60,9 @@ Examples:
|
|||
}
|
||||
|
||||
serverName = convertServerName(serverName)
|
||||
if sqlArgs.DBDriver == DBDriverMongodb {
|
||||
sqlArgs.IsEmbed = false
|
||||
}
|
||||
|
||||
tableNames := strings.Split(dbTables, ",")
|
||||
for _, tableName := range tableNames {
|
||||
|
@ -100,7 +103,7 @@ using help:
|
|||
//_ = cmd.MarkFlagRequired("module-name")
|
||||
cmd.Flags().StringVarP(&serverName, "server-name", "s", "", "server name")
|
||||
//_ = cmd.MarkFlagRequired("server-name")
|
||||
cmd.Flags().StringVarP(&sqlArgs.DBDriver, "db-driver", "k", "mysql", "database driver, support mysql, postgresql, tidb, sqlite")
|
||||
cmd.Flags().StringVarP(&sqlArgs.DBDriver, "db-driver", "k", "mysql", "database driver, support mysql, mongodb, postgresql, tidb, sqlite")
|
||||
cmd.Flags().StringVarP(&sqlArgs.DBDsn, "db-dsn", "d", "", "database content address, e.g. user:password@(host:port)/database. Note: if db-driver=sqlite, db-dsn must be a local sqlite db file, e.g. --db-dsn=/tmp/sponge_sqlite.db") //nolint
|
||||
_ = cmd.MarkFlagRequired("db-dsn")
|
||||
cmd.Flags().StringVarP(&dbTables, "db-table", "t", "", "table name, multiple names separated by commas")
|
||||
|
|
|
@ -81,6 +81,7 @@ type rpcPbGenerator struct {
|
|||
outPath string
|
||||
}
|
||||
|
||||
// nolint
|
||||
func (g *rpcPbGenerator) generateCode() error {
|
||||
protobufFiles, isImportTypes, err := parseProtobufFiles(g.protobufFile)
|
||||
if err != nil {
|
||||
|
@ -108,7 +109,7 @@ func (g *rpcPbGenerator) generateCode() error {
|
|||
"types.pb.validate.go", "types.pb.go", // api/types
|
||||
"userExample_rpc.go", "systemCode_http.go", "userExample_http.go", // internal/ecode
|
||||
"http.go", "http_option.go", "http_test.go", // internal/server
|
||||
"userExample.go", "userExample_client_test.go", "userExample_logic.go", "userExample_logic_test.go", "userExample_test.go", // internal/service
|
||||
"userExample.go", "userExample_client_test.go", "userExample_logic.go", "userExample_logic_test.go", "userExample_test.go", "userExample.go.mgo", "userExample_client_test.go.mgo", // internal/service
|
||||
}
|
||||
|
||||
if !isImportTypes {
|
||||
|
|
|
@ -34,8 +34,8 @@ func RPCCommand() *cobra.Command {
|
|||
//nolint
|
||||
cmd := &cobra.Command{
|
||||
Use: "rpc",
|
||||
Short: "Generate grpc service code based on mysql table",
|
||||
Long: `generate grpc service code based on mysql table.
|
||||
Short: "Generate grpc service code based on sql",
|
||||
Long: `generate grpc service code based on sql.
|
||||
|
||||
Examples:
|
||||
# generate grpc service code and embed gorm.model struct.
|
||||
|
@ -67,6 +67,9 @@ Examples:
|
|||
}
|
||||
|
||||
projectName, serverName = convertProjectAndServerName(projectName, serverName)
|
||||
if sqlArgs.DBDriver == DBDriverMongodb {
|
||||
sqlArgs.IsEmbed = false
|
||||
}
|
||||
|
||||
sqlArgs.DBTable = firstTable
|
||||
codes, err := sql2code.Generate(&sqlArgs)
|
||||
|
@ -133,7 +136,7 @@ using help:
|
|||
_ = cmd.MarkFlagRequired("server-name")
|
||||
cmd.Flags().StringVarP(&projectName, "project-name", "p", "", "project name")
|
||||
_ = cmd.MarkFlagRequired("project-name")
|
||||
cmd.Flags().StringVarP(&sqlArgs.DBDriver, "db-driver", "k", "mysql", "database driver, support mysql, postgresql, tidb, sqlite")
|
||||
cmd.Flags().StringVarP(&sqlArgs.DBDriver, "db-driver", "k", "mysql", "database driver, support mysql, mongodb, postgresql, tidb, sqlite")
|
||||
cmd.Flags().StringVarP(&sqlArgs.DBDsn, "db-dsn", "d", "", "database content address, e.g. user:password@(host:port)/database. Note: if db-driver=sqlite, db-dsn must be a local sqlite db file, e.g. --db-dsn=/tmp/sponge_sqlite.db") //nolint
|
||||
_ = cmd.MarkFlagRequired("db-dsn")
|
||||
cmd.Flags().StringVarP(&dbTables, "db-table", "t", "", "table name, multiple names separated by commas")
|
||||
|
@ -177,15 +180,34 @@ func (g *rpcGenerator) generateCode() (string, error) {
|
|||
ignoreDirs := []string{ // specify the directory in the subdirectory where processing is ignored
|
||||
"internal/handler", "internal/rpcclient", "internal/routers", "internal/types", "cmd/sponge",
|
||||
}
|
||||
ignoreFiles := []string{ // specify the files in the subdirectory to be ignored for processing
|
||||
"types.pb.validate.go", "types.pb.go", // api/types
|
||||
"userExample.pb.go", "userExample.pb.validate.go", "userExample_grpc.pb.go", "userExample_router.pb.go", // api/serverNameExample/v1
|
||||
"userExample_http.go", "systemCode_http.go", // internal/ecode
|
||||
"http.go", "http_option.go", "http_test.go", // internal/server
|
||||
"userExample_logic.go", "userExample_logic_test.go", "service/userExample_test.go", // internal/service
|
||||
"scripts/swag-docs.sh", // sponge/scripts
|
||||
"doc.go", "cacheNameExample.go", "cacheNameExample_test.go", // internal/cache
|
||||
"init_test.go", // model
|
||||
var ignoreFiles []string
|
||||
switch strings.ToLower(g.dbDriver) {
|
||||
case DBDriverMysql, DBDriverPostgresql, DBDriverTidb, DBDriverSqlite:
|
||||
ignoreFiles = []string{ // specify the files in the subdirectory to be ignored for processing
|
||||
"userExample_http.go", "systemCode_http.go", // internal/ecode
|
||||
"http.go", "http_option.go", "http_test.go", // internal/server
|
||||
"scripts/swag-docs.sh", // sponge/scripts
|
||||
"types.pb.validate.go", "types.pb.go", // api/types
|
||||
"userExample.pb.go", "userExample.pb.validate.go", "userExample_grpc.pb.go", "userExample_router.pb.go", // api/serverNameExample/v1
|
||||
"init_test.go", "init.go.mgo", // model
|
||||
"doc.go", "cacheNameExample.go", "cacheNameExample_test.go", "cache/userExample.go.mgo", // internal/cache
|
||||
"dao/userExample.go.mgo", // internal/dao
|
||||
"userExample_logic.go", "userExample_logic_test.go", "service/userExample_test.go", "service/userExample.go.mgo", "service/userExample_client_test.go.mgo", // internal/service
|
||||
}
|
||||
case DBDriverMongodb:
|
||||
ignoreFiles = []string{ // specify the files in the subdirectory to be ignored for processing
|
||||
"userExample_http.go", "systemCode_http.go", // internal/ecode
|
||||
"http.go", "http_option.go", "http_test.go", // internal/server
|
||||
"scripts/swag-docs.sh", // sponge/scripts
|
||||
"types.pb.validate.go", "types.pb.go", // api/types
|
||||
"userExample.pb.go", "userExample.pb.validate.go", "userExample_grpc.pb.go", "userExample_router.pb.go", // api/serverNameExample/v1
|
||||
"init_test.go", "init.go", // model
|
||||
"doc.go", "cacheNameExample.go", "cacheNameExample_test.go", "cache/userExample.go", "cache/userExample_test.go", // internal/cache
|
||||
"dao/userExample_test.go", "dao/userExample.go", // internal/dao
|
||||
"userExample_logic.go", "userExample_logic_test.go", "service/userExample_test.go", "service/userExample.go", "service/userExample_client_test.go", // internal/service
|
||||
}
|
||||
default:
|
||||
return "", errors.New("unsupported db driver: " + g.dbDriver)
|
||||
}
|
||||
|
||||
r.SetSubDirsAndFiles(subDirs, subFiles...)
|
||||
|
@ -210,10 +232,12 @@ func (g *rpcGenerator) addFields(r replacer.Replacer) []replacer.Field {
|
|||
fields = append(fields, deleteFieldsMark(r, modelFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, modelInitDBFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, daoFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, daoMgoFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, daoTestFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, protoFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, serviceLogicFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, serviceClientFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, serviceClientMgoFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, serviceTestFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, dockerFile, wellStartMark, wellEndMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, dockerFileBuild, wellStartMark, wellEndMark)...)
|
||||
|
@ -267,7 +291,7 @@ func (g *rpcGenerator) addFields(r replacer.Replacer) []replacer.Field {
|
|||
},
|
||||
{ // replace the contents of the service/userExample_client_test.go file
|
||||
Old: serviceFileMark,
|
||||
New: adjustmentOfIDType(g.codes[parser.CodeTypeService]),
|
||||
New: adjustmentOfIDType(g.codes[parser.CodeTypeService], g.dbDriver),
|
||||
},
|
||||
{ // replace the contents of the Dockerfile file
|
||||
Old: dockerFileMark,
|
||||
|
@ -384,6 +408,18 @@ func (g *rpcGenerator) addFields(r replacer.Replacer) []replacer.Field {
|
|||
Old: "test/sql/sqlite/sponge.db",
|
||||
New: sqliteDSNAdaptation(g.dbDriver, g.dbDSN),
|
||||
},
|
||||
{
|
||||
Old: "init.go.mgo",
|
||||
New: "init.go",
|
||||
},
|
||||
{
|
||||
Old: "userExample_client_test.go.mgo",
|
||||
New: "userExample_client_test.go",
|
||||
},
|
||||
{
|
||||
Old: "userExample.go.mgo",
|
||||
New: "userExample.go",
|
||||
},
|
||||
{
|
||||
Old: "UserExample",
|
||||
New: g.codes[parser.TableName],
|
||||
|
|
|
@ -31,8 +31,8 @@ func ServiceCommand() *cobra.Command {
|
|||
|
||||
cmd := &cobra.Command{
|
||||
Use: "service",
|
||||
Short: "Generate grpc service code based on mysql table",
|
||||
Long: `generate grpc service code based on mysql table.
|
||||
Short: "Generate grpc service code based on sql",
|
||||
Long: `generate grpc service code based on sql.
|
||||
|
||||
Examples:
|
||||
# generate service code and embed gorm.model struct.
|
||||
|
@ -63,6 +63,9 @@ Examples:
|
|||
}
|
||||
|
||||
serverName = convertServerName(serverName)
|
||||
if sqlArgs.DBDriver == DBDriverMongodb {
|
||||
sqlArgs.IsEmbed = false
|
||||
}
|
||||
|
||||
tableNames := strings.Split(dbTables, ",")
|
||||
for _, tableName := range tableNames {
|
||||
|
@ -79,6 +82,7 @@ Examples:
|
|||
g := &serviceGenerator{
|
||||
moduleName: moduleName,
|
||||
serverName: serverName,
|
||||
dbDriver: sqlArgs.DBDriver,
|
||||
isEmbed: sqlArgs.IsEmbed,
|
||||
codes: codes,
|
||||
outPath: outPath,
|
||||
|
@ -106,7 +110,7 @@ using help:
|
|||
//_ = cmd.MarkFlagRequired("module-name")
|
||||
cmd.Flags().StringVarP(&serverName, "server-name", "s", "", "server name")
|
||||
//_ = cmd.MarkFlagRequired("server-name")
|
||||
cmd.Flags().StringVarP(&sqlArgs.DBDriver, "db-driver", "k", "mysql", "database driver, support mysql, postgresql, tidb, sqlite")
|
||||
cmd.Flags().StringVarP(&sqlArgs.DBDriver, "db-driver", "k", "mysql", "database driver, support mysql, mongodb, postgresql, tidb, sqlite")
|
||||
cmd.Flags().StringVarP(&sqlArgs.DBDsn, "db-dsn", "d", "", "database content address, e.g. user:password@(host:port)/database. Note: if db-driver=sqlite, db-dsn must be a local sqlite db file, e.g. --db-dsn=/tmp/sponge_sqlite.db") //nolint
|
||||
_ = cmd.MarkFlagRequired("db-dsn")
|
||||
cmd.Flags().StringVarP(&dbTables, "db-table", "t", "", "table name, multiple names separated by commas")
|
||||
|
@ -122,11 +126,13 @@ using help:
|
|||
type serviceGenerator struct {
|
||||
moduleName string
|
||||
serverName string
|
||||
dbDriver string
|
||||
isEmbed bool
|
||||
codes map[string]string
|
||||
outPath string
|
||||
}
|
||||
|
||||
// nolint
|
||||
func (g *serviceGenerator) generateCode() (string, error) {
|
||||
subTplName := "service"
|
||||
r := Replacers[TplNameSponge]
|
||||
|
@ -142,12 +148,28 @@ func (g *serviceGenerator) generateCode() (string, error) {
|
|||
subDirs := []string{"internal/model", "internal/cache", "internal/dao",
|
||||
"internal/ecode", "internal/service", "api/serverNameExample"} // only the specified subdirectory is processed, if empty or no subdirectory is specified, it means all files
|
||||
ignoreDirs := []string{} // specify the directory in the subdirectory where processing is ignored
|
||||
ignoreFiles := []string{ // specify the files in the subdirectory to be ignored for processing
|
||||
"userExample.pb.go", "userExample.pb.validate.go", "userExample_grpc.pb.go", "userExample_router.pb.go", // api/serverNameExample
|
||||
"systemCode_http.go", "systemCode_rpc.go", "userExample_http.go", // internal/ecode
|
||||
"init.go", "init_test.go", // internal/model
|
||||
"service.go", "service_test.go", "userExample_logic.go", "userExample_logic_test.go", "service/userExample_test.go", // internal/service
|
||||
"doc.go", "cacheNameExample.go", "cacheNameExample_test.go", // internal/cache
|
||||
var ignoreFiles []string
|
||||
switch strings.ToLower(g.dbDriver) {
|
||||
case DBDriverMysql, DBDriverPostgresql, DBDriverTidb, DBDriverSqlite:
|
||||
ignoreFiles = []string{ // specify the files in the subdirectory to be ignored for processing
|
||||
"systemCode_http.go", "systemCode_rpc.go", "userExample_http.go", // internal/ecode
|
||||
"userExample.pb.go", "userExample.pb.validate.go", "userExample_grpc.pb.go", "userExample_router.pb.go", // api/serverNameExample/v1
|
||||
"init.go", "init_test.go", "init.go.mgo", // internal/model
|
||||
"doc.go", "cacheNameExample.go", "cacheNameExample_test.go", "cache/userExample.go.mgo", // internal/cache
|
||||
"dao/userExample.go.mgo", // internal/dao
|
||||
"service.go", "service_test.go", "userExample_logic.go", "userExample_logic_test.go", "service/userExample_test.go", "service/userExample.go.mgo", "service/userExample_client_test.go.mgo", // internal/service
|
||||
}
|
||||
case DBDriverMongodb:
|
||||
ignoreFiles = []string{ // specify the files in the subdirectory to be ignored for processing
|
||||
"systemCode_http.go", "systemCode_rpc.go", "userExample_http.go", // internal/ecode
|
||||
"userExample.pb.go", "userExample.pb.validate.go", "userExample_grpc.pb.go", "userExample_router.pb.go", // api/serverNameExample/v1
|
||||
"init.go", "init_test.go", "init.go.mgo", // internal/model
|
||||
"doc.go", "cacheNameExample.go", "cacheNameExample_test.go", "cache/userExample.go", "cache/userExample_test.go", // internal/cache
|
||||
"dao/userExample_test.go", "dao/userExample.go", // internal/dao
|
||||
"service.go", "service_test.go", "userExample_logic.go", "userExample_logic_test.go", "service/userExample_test.go", "service/userExample.go", "service/userExample_client_test.go", // internal/service
|
||||
}
|
||||
default:
|
||||
return "", errors.New("unsupported db driver: " + g.dbDriver)
|
||||
}
|
||||
|
||||
r.SetSubDirsAndFiles(subDirs)
|
||||
|
@ -168,10 +190,12 @@ func (g *serviceGenerator) addFields(r replacer.Replacer) []replacer.Field {
|
|||
|
||||
fields = append(fields, deleteFieldsMark(r, modelFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, daoFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, daoMgoFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, daoTestFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, serviceLogicFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, protoFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, serviceClientFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, serviceClientMgoFile, startMark, endMark)...)
|
||||
fields = append(fields, deleteFieldsMark(r, serviceTestFile, startMark, endMark)...)
|
||||
fields = append(fields, []replacer.Field{
|
||||
{ // replace the contents of the model/userExample.go file
|
||||
|
@ -192,7 +216,7 @@ func (g *serviceGenerator) addFields(r replacer.Replacer) []replacer.Field {
|
|||
},
|
||||
{ // replace the contents of the service/userExample_client_test.go file
|
||||
Old: serviceFileMark,
|
||||
New: adjustmentOfIDType(g.codes[parser.CodeTypeService]),
|
||||
New: adjustmentOfIDType(g.codes[parser.CodeTypeService], g.dbDriver),
|
||||
},
|
||||
{
|
||||
Old: selfPackageName + "/" + r.GetSourcePath(),
|
||||
|
@ -228,6 +252,14 @@ func (g *serviceGenerator) addFields(r replacer.Replacer) []replacer.Field {
|
|||
Old: "serverNameExample",
|
||||
New: g.serverName,
|
||||
},
|
||||
{
|
||||
Old: "userExample_client_test.go.mgo",
|
||||
New: "userExample_client_test.go",
|
||||
},
|
||||
{
|
||||
Old: "userExample.go.mgo",
|
||||
New: "userExample.go",
|
||||
},
|
||||
{
|
||||
Old: "UserExample",
|
||||
New: g.codes[parser.TableName],
|
||||
|
|
|
@ -370,7 +370,7 @@ grpc:
|
|||
|
||||
# grpc client-side settings, support for setting up multiple grpc clients.
|
||||
grpcClient:
|
||||
- name: "serverNameExample" # grpc service name, used for service discovery
|
||||
- name: "your_grpc_service_name" # grpc service name, used for service discovery
|
||||
host: "127.0.0.1" # grpc service address, used for direct connection
|
||||
port: 8282 # grpc service port
|
||||
registryDiscoveryType: "" # registration and discovery types: consul, etcd, nacos, if empty, connecting to server using host and port
|
||||
|
@ -399,7 +399,7 @@ http:
|
|||
|
||||
# grpc client-side settings, support for setting up multiple grpc clients.
|
||||
grpcClient:
|
||||
- name: "serverNameExample" # grpc service name, used for service discovery
|
||||
- name: "your_grpc_service_name" # grpc service name, used for service discovery
|
||||
host: "127.0.0.1" # grpc service address, used for direct connection
|
||||
port: 8282 # grpc service port
|
||||
registryDiscoveryType: "" # registration and discovery types: consul, etcd, nacos, if empty, connecting to server using host and port
|
||||
|
@ -421,10 +421,10 @@ grpcClient:
|
|||
|
||||
mysqlConfigCode = `# database setting
|
||||
database:
|
||||
driver: "mysql" # database driver, currently support mysql, postgres, tidb, sqlite
|
||||
driver: "mysql" # database driver
|
||||
# mysql settings
|
||||
mysql:
|
||||
# dsn format, <user>:<pass>@(127.0.0.1:3306)/<db>?[k=v& ......]
|
||||
# dsn format, <username>:<password>@(<hostname>:<port>)/<db>?[k=v& ......]
|
||||
dsn: "root:123456@(192.168.3.37:3306)/account?parseTime=true&loc=Local&charset=utf8,utf8mb4"
|
||||
enableLog: true # whether to turn on printing of all logs
|
||||
maxIdleConns: 10 # set the maximum number of connections in the idle connection pool
|
||||
|
@ -436,29 +436,11 @@ database:
|
|||
#mastersDsn: # sets masters mysql dsn, array type, non-required field, if there is only one master, there is no need to set the mastersDsn field, the default dsn field is mysql master.
|
||||
# - "your master dsn`
|
||||
|
||||
//mysqlConfigForDeploymentCode = ` # database setting
|
||||
//database:
|
||||
// driver: "mysql" # database driver, currently support mysql, postgres, tidb, sqlite
|
||||
// # mysql settings
|
||||
// mysql:
|
||||
// # dsn format, <user>:<pass>@(127.0.0.1:3306)/<db>?[k=v& ......]
|
||||
// dsn: "root:123456@(192.168.3.37:3306)/account?parseTime=true&loc=Local&charset=utf8,utf8mb4"
|
||||
// enableLog: true # whether to turn on printing of all logs
|
||||
// slowThreshold: 0 # if greater than 0, only print logs with a time greater than the threshold, with a higher priority than enableLog, in (ms)
|
||||
// maxIdleConns: 3 # set the maximum number of connections in the idle connection pool
|
||||
// maxOpenConns: 100 # set the maximum number of open database connections
|
||||
// connMaxLifetime: 30 # sets the maximum time for which the connection can be reused, in minutes
|
||||
// #slavesDsn: # sets slaves mysql dsn, array type
|
||||
// # - "your slave dsn 1"
|
||||
// # - "your slave dsn 2"
|
||||
// #mastersDsn: # sets masters mysql dsn, array type, non-required field, if there is only one master, the default dsn field is mysql master.
|
||||
// # - "your master dsn"`
|
||||
|
||||
postgresqlConfigCode = `database:
|
||||
driver: "postgresql" # database driver, currently support mysql, postgres, tidb, sqlite
|
||||
driver: "postgresql" # database driver
|
||||
# postgresql settings
|
||||
postgresql:
|
||||
# dsn format, <user>:<pass>@127.0.0.1:5432/<db>?[k=v& ......]
|
||||
# dsn format, <username>:<password>@<hostname>:<port>/<db>?[k=v& ......]
|
||||
dsn: "root:123456@192.168.3.37:5432/account?sslmode=disable"
|
||||
enableLog: true # whether to turn on printing of all logs
|
||||
maxIdleConns: 10 # set the maximum number of connections in the idle connection pool
|
||||
|
@ -466,7 +448,7 @@ database:
|
|||
connMaxLifetime: 30 # sets the maximum time for which the connection can be reused, in minutes`
|
||||
|
||||
sqliteConfigCode = `database:
|
||||
driver: "sqlite" # database driver, currently support mysql, postgres, tidb, sqlite
|
||||
driver: "sqlite" # database driver
|
||||
# sqlite settings
|
||||
sqlite:
|
||||
dbFile: "test/sql/sqlite/sponge.db"
|
||||
|
@ -475,17 +457,13 @@ database:
|
|||
maxOpenConns: 100 # set the maximum number of open database connections
|
||||
connMaxLifetime: 30 # sets the maximum time for which the connection can be reused, in minutes`
|
||||
|
||||
//postgresqlConfigForDeploymentCode = ` # database setting
|
||||
//database:
|
||||
// driver: "postgresql" # database driver, currently support mysql, postgres, tidb, sqlite
|
||||
// # postgresql settings
|
||||
// postgresql:
|
||||
// # dsn format, <user>:<pass>@127.0.0.1:5432/<db>?[k=v& ......]
|
||||
// dsn: "root:123456@192.168.3.37:5432/account?sslmode=disable"
|
||||
// enableLog: true # whether to turn on printing of all logs
|
||||
// maxIdleConns: 10 # set the maximum number of connections in the idle connection pool
|
||||
// maxOpenConns: 100 # set the maximum number of open database connections
|
||||
// connMaxLifetime: 30 # sets the maximum time for which the connection can be reused, in minutes`
|
||||
mongodbConfigCode = `database:
|
||||
driver: "mongodb" # database driver
|
||||
# mongodb settings
|
||||
mongodb:
|
||||
# dsn format, <username>:<password>@<hostname1>:<port1>[,<hostname2>:<port2>,......]/<db>?[k=v& ......]
|
||||
# parameter k=v see https://www.mongodb.com/docs/drivers/go/current/fundamentals/connections/connection-guide/#connection-options
|
||||
dsn: "root:123456@192.168.3.37:27017/account?connectTimeoutMS=15000"`
|
||||
|
||||
modelInitDBFileMysqlCode = `// InitDB connect database
|
||||
func InitDB() {
|
||||
|
@ -493,7 +471,7 @@ func InitDB() {
|
|||
case ggorm.DBDriverMysql, ggorm.DBDriverTidb:
|
||||
InitMysql()
|
||||
default:
|
||||
panic("unsupported database driver: " + config.Get().Database.Driver)
|
||||
panic("InitDB error, unsupported database driver: " + config.Get().Database.Driver)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -529,7 +507,7 @@ func InitMysql() {
|
|||
var err error
|
||||
db, err = ggorm.InitMysql(dsn, opts...)
|
||||
if err != nil {
|
||||
panic("ggorm.InitMysql error: " + err.Error())
|
||||
panic("InitMysql error: " + err.Error())
|
||||
}
|
||||
}`
|
||||
|
||||
|
@ -539,7 +517,7 @@ func InitDB() {
|
|||
case ggorm.DBDriverPostgresql:
|
||||
InitPostgresql()
|
||||
default:
|
||||
panic("unsupported database driver: " + config.Get().Database.Driver)
|
||||
panic("InitDB error, unsupported database driver: " + config.Get().Database.Driver)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -568,7 +546,7 @@ func InitPostgresql() {
|
|||
var err error
|
||||
db, err = ggorm.InitPostgresql(dsn, opts...)
|
||||
if err != nil {
|
||||
panic("ggorm.InitPostgresql error: " + err.Error())
|
||||
panic("InitPostgresql error: " + err.Error())
|
||||
}
|
||||
}`
|
||||
|
||||
|
@ -578,7 +556,7 @@ func InitDB() {
|
|||
case ggorm.DBDriverSqlite:
|
||||
InitSqlite()
|
||||
default:
|
||||
panic("unsupported database driver: " + config.Get().Database.Driver)
|
||||
panic("InitDB error, unsupported database driver: " + config.Get().Database.Driver)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -601,12 +579,13 @@ func InitSqlite() {
|
|||
}
|
||||
|
||||
var err error
|
||||
db, err = ggorm.InitSqlite(config.Get().Database.Sqlite.DBFile, opts...)
|
||||
var dbFile = utils.AdaptiveSqlite(config.Get().Database.Sqlite.DBFile)
|
||||
db, err = ggorm.InitSqlite(dbFile, opts...)
|
||||
if err != nil {
|
||||
panic("ggorm.InitSqlite error: " + err.Error())
|
||||
panic("InitSqlite error: " + err.Error())
|
||||
}
|
||||
}`
|
||||
|
||||
embedTimeCode = ` value.CreatedAt = record.CreatedAt.Unix()
|
||||
embedTimeCode = `value.CreatedAt = record.CreatedAt.Unix()
|
||||
value.UpdatedAt = record.UpdatedAt.Unix()`
|
||||
)
|
||||
|
|
|
@ -16,7 +16,7 @@ import (
|
|||
func GenerateDBInitCommand() *cobra.Command {
|
||||
var (
|
||||
moduleName string // go.mod module name
|
||||
dbDriver string // database driver e.g. mysql, postgresql, tidb
|
||||
dbDriver string // database driver e.g. mysql, mongodb, postgresql, tidb, sqlite
|
||||
outPath string // output directory
|
||||
targetFile = "internal/model/init.go"
|
||||
)
|
||||
|
@ -24,13 +24,13 @@ func GenerateDBInitCommand() *cobra.Command {
|
|||
cmd := &cobra.Command{
|
||||
Use: "gen-db-init",
|
||||
Short: "Generate database initialization code",
|
||||
Long: `generate database initialization code
|
||||
Long: `generate database initialization code.
|
||||
|
||||
Examples:
|
||||
# generate mysql initialization code.
|
||||
sponge patch gen-db-init --module-name=yourModuleName --db-driver=mysql
|
||||
|
||||
# generate database initialization code, and specify the server directory, Note: code generation will be canceled when the latest generated file already exists.
|
||||
# generate mysql initialization code, and specify the server directory, Note: code generation will be canceled when the latest generated file already exists.
|
||||
sponge patch gen-db-init --db-driver=mysql --out=./yourServerDir
|
||||
`,
|
||||
SilenceErrors: true,
|
||||
|
@ -40,7 +40,7 @@ Examples:
|
|||
if mdName != "" {
|
||||
moduleName = mdName
|
||||
} else if moduleName == "" {
|
||||
return fmt.Errorf(`required flag(s) "module-name" not set, use "sponge patch gen-mysql-init -h" for help`)
|
||||
return fmt.Errorf(`required flag(s) "module-name" not set, use "sponge patch gen-db-init -h" for help`)
|
||||
}
|
||||
|
||||
var isEmpty bool
|
||||
|
@ -82,7 +82,7 @@ using help:
|
|||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVarP(&dbDriver, "db-driver", "k", "mysql", "database driver, support mysql, postgresql, tidb, sqlite")
|
||||
cmd.Flags().StringVarP(&dbDriver, "db-driver", "k", "mysql", "database driver, support mysql, mongodb, postgresql, tidb, sqlite")
|
||||
cmd.Flags().StringVarP(&moduleName, "module-name", "m", "", "module-name is the name of the module in the 'go.mod' file")
|
||||
cmd.Flags().StringVarP(&outPath, "out", "o", "", "output directory, default is ./mysql-init_<time>, "+
|
||||
"if you specify the directory where the web or microservice generated by sponge, the module-name flag can be ignored")
|
||||
|
@ -97,7 +97,8 @@ type dbInitGenerator struct {
|
|||
}
|
||||
|
||||
func (g *dbInitGenerator) generateCode() (string, error) {
|
||||
subTplName := g.dbDriver + "-init"
|
||||
fmt.Println(*g)
|
||||
subTplName := "init-" + g.dbDriver
|
||||
r := generate.Replacers[generate.TplNameSponge]
|
||||
if r == nil {
|
||||
return "", errors.New("replacer is nil")
|
||||
|
@ -106,8 +107,18 @@ func (g *dbInitGenerator) generateCode() (string, error) {
|
|||
// setting up template information
|
||||
subDirs := []string{"internal/model"} // only the specified subdirectory is processed, if empty or no subdirectory is specified, it means all files
|
||||
ignoreDirs := []string{} // specify the directory in the subdirectory where processing is ignored
|
||||
ignoreFiles := []string{ // specify the files in the subdirectory to be ignored for processing
|
||||
"userExample.go", "init_test.go",
|
||||
var ignoreFiles []string
|
||||
switch strings.ToLower(g.dbDriver) {
|
||||
case generate.DBDriverMysql, generate.DBDriverPostgresql, generate.DBDriverTidb, generate.DBDriverSqlite:
|
||||
ignoreFiles = []string{ // specify the files in the subdirectory to be ignored for processing
|
||||
"userExample.go", "init_test.go", "init.go.mgo",
|
||||
}
|
||||
case generate.DBDriverMongodb:
|
||||
ignoreFiles = []string{ // specify the files in the subdirectory to be ignored for processing
|
||||
"userExample.go", "init_test.go", "init.go",
|
||||
}
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported database driver: %s", g.dbDriver)
|
||||
}
|
||||
|
||||
r.SetSubDirsAndFiles(subDirs)
|
||||
|
@ -138,6 +149,10 @@ func (g *dbInitGenerator) addFields(r replacer.Replacer) []replacer.Field {
|
|||
New: g.moduleName + "/configs",
|
||||
IsCaseSensitive: false,
|
||||
},
|
||||
{ // rename init.go.mgo --> init.go
|
||||
Old: "init.go.mgo",
|
||||
New: "init.go",
|
||||
},
|
||||
{ // replace the contents of the model/init.go file
|
||||
Old: generate.ModelInitDBFileMark,
|
||||
New: generate.GetInitDataBaseCode(g.dbDriver),
|
||||
|
|
|
@ -19,8 +19,7 @@ var (
|
|||
func NewRootCMD() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "sponge",
|
||||
Long: `Sponge is a powerful golang basic development framework that integrates automatic code generation,
|
||||
web and microservice framework.
|
||||
Long: `Sponge is a powerful Go development framework, it's easy to develop web and microservice projects.
|
||||
repo: https://github.com/zhufuyi/sponge
|
||||
docs: https://go-sponge.com`,
|
||||
SilenceErrors: true,
|
||||
|
|
|
@ -128,6 +128,7 @@ func copyToTempDir(targetVersion string) (string, error) {
|
|||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
_ = executeCommand("rm", "-rf", targetDir+"/cmd/sponge")
|
||||
|
||||
versionNum := strings.Replace(spongeDirName, "sponge@", "", 1)
|
||||
err = os.WriteFile(versionFile, []byte(versionNum), 0644)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Package main sponge is a powerful tool for generating web and microservice code, a microservice
|
||||
// framework based on gin and grpc encapsulation, and an open source framework for rapid application
|
||||
// development. Sponge has a wealth of code generation commands, sponge generate code unified in
|
||||
// the UI interface operation, it is easy to build a complete project engineering code.
|
||||
// Package main sponge is a basic development framework that integrates code auto generation,
|
||||
// Gin and GRPC, a microservice framework. it is easy to build a complete project from development
|
||||
// to deployment, just fill in the business logic code on the generated template code, greatly improved
|
||||
// development efficiency and reduced development difficulty, the use of Go can also be "low-code development".
|
||||
package main
|
||||
|
||||
import (
|
||||
|
|
|
@ -6,22 +6,24 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/zhufuyi/sponge/internal/ecode"
|
||||
"github.com/zhufuyi/sponge/pkg/errcode"
|
||||
"github.com/zhufuyi/sponge/pkg/ggorm"
|
||||
"github.com/zhufuyi/sponge/pkg/gin/response"
|
||||
"github.com/zhufuyi/sponge/pkg/gobash"
|
||||
"github.com/zhufuyi/sponge/pkg/gofile"
|
||||
"github.com/zhufuyi/sponge/pkg/krand"
|
||||
"github.com/zhufuyi/sponge/pkg/mgo"
|
||||
"github.com/zhufuyi/sponge/pkg/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -43,6 +45,7 @@ type kv struct {
|
|||
func ListDbDrivers(c *gin.Context) {
|
||||
dbDrivers := []string{
|
||||
ggorm.DBDriverMysql,
|
||||
mgo.DBDriverName,
|
||||
ggorm.DBDriverPostgresql,
|
||||
ggorm.DBDriverTidb,
|
||||
ggorm.DBDriverSqlite,
|
||||
|
@ -64,7 +67,7 @@ func ListTables(c *gin.Context) {
|
|||
form := &dbInfoForm{}
|
||||
err := c.ShouldBindJSON(form)
|
||||
if err != nil {
|
||||
response.Error(c, ecode.InvalidParams.WithDetails(err.Error()))
|
||||
response.Error(c, errcode.InvalidParams.WithDetails(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -76,15 +79,17 @@ func ListTables(c *gin.Context) {
|
|||
tables, err = getPostgresqlTables(form.Dsn)
|
||||
case ggorm.DBDriverSqlite:
|
||||
tables, err = getSqliteTables(form.Dsn)
|
||||
case mgo.DBDriverName:
|
||||
tables, err = getMongodbTables(form.Dsn)
|
||||
case "":
|
||||
response.Error(c, ecode.InternalServerError.WithDetails("database type is empty"))
|
||||
response.Error(c, errcode.InternalServerError.WithDetails("database type is empty"))
|
||||
return
|
||||
default:
|
||||
response.Error(c, ecode.InternalServerError.WithDetails("unsupported database driver: "+form.DbDriver))
|
||||
response.Error(c, errcode.InternalServerError.WithDetails("unsupported database driver: "+form.DbDriver))
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Error(c, ecode.InternalServerError.WithDetails(err.Error()))
|
||||
response.Error(c, errcode.InternalServerError.WithDetails(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -113,7 +118,7 @@ func GenerateCode(c *gin.Context) {
|
|||
form := &GenerateCodeForm{}
|
||||
err := c.ShouldBindJSON(form)
|
||||
if err != nil {
|
||||
responseErr(c, err, ecode.InvalidParams)
|
||||
responseErr(c, err, errcode.InvalidParams)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -150,20 +155,20 @@ func handleGenerateCode(c *gin.Context, outPath string, arg string) {
|
|||
_ = v
|
||||
}
|
||||
if result.Err != nil {
|
||||
responseErr(c, result.Err, ecode.InternalServerError)
|
||||
responseErr(c, result.Err, errcode.InternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
zipFile := out + ".zip"
|
||||
err := CompressPathToZip(out, zipFile)
|
||||
if err != nil {
|
||||
responseErr(c, err, ecode.InternalServerError)
|
||||
responseErr(c, err, errcode.InternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if !gofile.IsExists(zipFile) {
|
||||
err = errors.New("no found file " + zipFile)
|
||||
responseErr(c, err, ecode.InternalServerError)
|
||||
responseErr(c, err, errcode.InternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -186,7 +191,7 @@ func handleGenerateCode(c *gin.Context, outPath string, arg string) {
|
|||
func GetRecord(c *gin.Context) {
|
||||
pathParam := c.Param("path")
|
||||
if pathParam == "" {
|
||||
response.Out(c, ecode.InvalidParams.WithDetails("path param is empty"))
|
||||
response.Out(c, errcode.InvalidParams.WithDetails("path param is empty"))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -209,18 +214,18 @@ func responseErr(c *gin.Context, err error, ec *errcode.Error) {
|
|||
func UploadFiles(c *gin.Context) {
|
||||
form, err := c.MultipartForm()
|
||||
if err != nil {
|
||||
response.Error(c, ecode.InvalidParams.WithDetails(err.Error()))
|
||||
response.Error(c, errcode.InvalidParams.WithDetails(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
if len(form.File) == 0 {
|
||||
response.Error(c, ecode.InvalidParams.WithDetails("upload file is empty"))
|
||||
response.Error(c, errcode.InvalidParams.WithDetails("upload file is empty"))
|
||||
return
|
||||
}
|
||||
|
||||
//spongeArg, err := getFormValue(form.Value, "spongeArg")
|
||||
//if err != nil {
|
||||
// response.Error(c, ecode.InvalidParams.WithDetails("the field 'spongeArg' cannot be empty"))
|
||||
// response.Error(c, errcode.InvalidParams.WithDetails("the field 'spongeArg' cannot be empty"))
|
||||
// return
|
||||
//}
|
||||
|
||||
|
@ -233,7 +238,7 @@ func UploadFiles(c *gin.Context) {
|
|||
filename := filepath.Base(file.Filename)
|
||||
fileType = path.Ext(filename)
|
||||
if !checkFileType(fileType) {
|
||||
response.Error(c, ecode.InvalidParams.WithDetails("only .proto or yaml files are allowed to be uploaded"))
|
||||
response.Error(c, errcode.InvalidParams.WithDetails("only .proto or yaml files are allowed to be uploaded"))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -242,7 +247,7 @@ func UploadFiles(c *gin.Context) {
|
|||
continue
|
||||
}
|
||||
if err = c.SaveUploadedFile(file, filePath); err != nil {
|
||||
response.Error(c, ecode.InternalServerError.WithDetails(err.Error()))
|
||||
response.Error(c, errcode.InternalServerError.WithDetails(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -431,3 +436,24 @@ func getSqliteTables(dbFile string) ([]string, error) {
|
|||
|
||||
return filteredTables, nil
|
||||
}
|
||||
|
||||
func getMongodbTables(dsn string) ([]string, error) {
|
||||
dsn = utils.AdaptiveMongodbDsn(dsn)
|
||||
db, err := mgo.Init(dsn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer mgo.Close(db) //nolint
|
||||
|
||||
tables, err := db.ListCollectionNames(context.Background(), bson.M{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(tables) == 0 {
|
||||
u, _ := url.Parse(dsn)
|
||||
return nil, fmt.Errorf("mongodb db %s has no tables", strings.TrimLeft(u.Path, "/"))
|
||||
}
|
||||
|
||||
return tables, nil
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,2 +1,2 @@
|
|||
<!DOCTYPE html><html><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>Sponge Generate Code</title><link rel=icon type=image/png sizes=32x32 href="/static/favicon.png?v=1.0"><script type=text/javascript src=/static/appConfig.js async></script><link href=/static/css/app.7695fd620e1371d8c60dcc9e799e6a9e.css rel=stylesheet></head><body style="margin: 0px; padding: 0px;"><style>.el-tooltip__popper {box-shadow: 3px 3px 10px 5px #d3d3d6;border-width: 0px !important;}
|
||||
.el-tooltip__popper[x-placement^="top"] .popper__arrow:after {border-top-color: #dcdfe6 !important;}</style><div id=app></div><script type=text/javascript src=/static/js/manifest.2ae2e69a05c33dfc65f8.js></script><script type=text/javascript src=/static/js/vendor.d8cdb3748af43d2ae51a.js></script><script type=text/javascript src=/static/js/app.f030f300c40167960866.js></script></body></html>
|
||||
<!DOCTYPE html><html><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>Sponge Generate Code</title><link rel=icon type=image/png sizes=32x32 href="/static/favicon.png?v=1.0"><script type=text/javascript src=/static/appConfig.js async></script><link href=/static/css/app.01a6ed84310104903a9dfae0136cb164.css rel=stylesheet></head><body style="margin: 0px; padding: 0px;"><style>.el-tooltip__popper {box-shadow: 3px 3px 10px 5px #d3d3d6;border-width: 0px !important;}
|
||||
.el-tooltip__popper[x-placement^="top"] .popper__arrow:after {border-top-color: #dcdfe6 !important;}</style><div id=app></div><script type=text/javascript src=/static/js/manifest.2ae2e69a05c33dfc65f8.js></script><script type=text/javascript src=/static/js/vendor.d8cdb3748af43d2ae51a.js></script><script type=text/javascript src=/static/js/app.18ec0c08047b3a2f8f6f.js></script></body></html>
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1 +1 @@
|
|||
{"version":3,"sources":["webpack:///webpack/bootstrap 6ce38f5eef2f14b1055e"],"names":["parentJsonpFunction","window","chunkIds","moreModules","executeModules","moduleId","chunkId","result","i","resolves","length","installedChunks","push","Object","prototype","hasOwnProperty","call","modules","shift","__webpack_require__","s","installedModules","2","exports","module","l","m","c","d","name","getter","o","defineProperty","configurable","enumerable","get","n","__esModule","object","property","p","oe","err","console","error"],"mappings":"aACA,IAAAA,EAAAC,OAAA,aACAA,OAAA,sBAAAC,EAAAC,EAAAC,GAIA,IADA,IAAAC,EAAAC,EAAAC,EAAAC,EAAA,EAAAC,KACQD,EAAAN,EAAAQ,OAAoBF,IAC5BF,EAAAJ,EAAAM,GACAG,EAAAL,IACAG,EAAAG,KAAAD,EAAAL,GAAA,IAEAK,EAAAL,GAAA,EAEA,IAAAD,KAAAF,EACAU,OAAAC,UAAAC,eAAAC,KAAAb,EAAAE,KACAY,EAAAZ,GAAAF,EAAAE,IAIA,IADAL,KAAAE,EAAAC,EAAAC,GACAK,EAAAC,QACAD,EAAAS,OAAAT,GAEA,GAAAL,EACA,IAAAI,EAAA,EAAYA,EAAAJ,EAAAM,OAA2BF,IACvCD,EAAAY,IAAAC,EAAAhB,EAAAI,IAGA,OAAAD,GAIA,IAAAc,KAGAV,GACAW,EAAA,GAIA,SAAAH,EAAAd,GAGA,GAAAgB,EAAAhB,GACA,OAAAgB,EAAAhB,GAAAkB,QAGA,IAAAC,EAAAH,EAAAhB,IACAG,EAAAH,EACAoB,GAAA,EACAF,YAUA,OANAN,EAAAZ,GAAAW,KAAAQ,EAAAD,QAAAC,IAAAD,QAAAJ,GAGAK,EAAAC,GAAA,EAGAD,EAAAD,QAKAJ,EAAAO,EAAAT,EAGAE,EAAAQ,EAAAN,EAGAF,EAAAS,EAAA,SAAAL,EAAAM,EAAAC,GACAX,EAAAY,EAAAR,EAAAM,IACAhB,OAAAmB,eAAAT,EAAAM,GACAI,cAAA,EACAC,YAAA,EACAC,IAAAL,KAMAX,EAAAiB,EAAA,SAAAZ,GACA,IAAAM,EAAAN,KAAAa,WACA,WAA2B,OAAAb,EAAA,SAC3B,WAAiC,OAAAA,GAEjC,OADAL,EAAAS,EAAAE,EAAA,IAAAA,GACAA,GAIAX,EAAAY,EAAA,SAAAO,EAAAC,GAAsD,OAAA1B,OAAAC,UAAAC,eAAAC,KAAAsB,EAAAC,IAGtDpB,EAAAqB,EAAA,IAGArB,EAAAsB,GAAA,SAAAC,GAA8D,MAApBC,QAAAC,MAAAF,GAAoBA","file":"static/js/manifest.2ae2e69a05c33dfc65f8.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tvar parentJsonpFunction = window[\"webpackJsonp\"];\n \twindow[\"webpackJsonp\"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [], result;\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n \t\tif(executeModules) {\n \t\t\tfor(i=0; i < executeModules.length; i++) {\n \t\t\t\tresult = __webpack_require__(__webpack_require__.s = executeModules[i]);\n \t\t\t}\n \t\t}\n \t\treturn result;\n \t};\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// objects to store loaded and loading chunks\n \tvar installedChunks = {\n \t\t2: 0\n \t};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/\";\n\n \t// on error function for async loading\n \t__webpack_require__.oe = function(err) { console.error(err); throw err; };\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap 6ce38f5eef2f14b1055e"],"sourceRoot":""}
|
||||
{"version":3,"sources":["webpack:///webpack/bootstrap 732b61e485086b18e05b"],"names":["parentJsonpFunction","window","chunkIds","moreModules","executeModules","moduleId","chunkId","result","i","resolves","length","installedChunks","push","Object","prototype","hasOwnProperty","call","modules","shift","__webpack_require__","s","installedModules","2","exports","module","l","m","c","d","name","getter","o","defineProperty","configurable","enumerable","get","n","__esModule","object","property","p","oe","err","console","error"],"mappings":"aACA,IAAAA,EAAAC,OAAA,aACAA,OAAA,sBAAAC,EAAAC,EAAAC,GAIA,IADA,IAAAC,EAAAC,EAAAC,EAAAC,EAAA,EAAAC,KACQD,EAAAN,EAAAQ,OAAoBF,IAC5BF,EAAAJ,EAAAM,GACAG,EAAAL,IACAG,EAAAG,KAAAD,EAAAL,GAAA,IAEAK,EAAAL,GAAA,EAEA,IAAAD,KAAAF,EACAU,OAAAC,UAAAC,eAAAC,KAAAb,EAAAE,KACAY,EAAAZ,GAAAF,EAAAE,IAIA,IADAL,KAAAE,EAAAC,EAAAC,GACAK,EAAAC,QACAD,EAAAS,OAAAT,GAEA,GAAAL,EACA,IAAAI,EAAA,EAAYA,EAAAJ,EAAAM,OAA2BF,IACvCD,EAAAY,IAAAC,EAAAhB,EAAAI,IAGA,OAAAD,GAIA,IAAAc,KAGAV,GACAW,EAAA,GAIA,SAAAH,EAAAd,GAGA,GAAAgB,EAAAhB,GACA,OAAAgB,EAAAhB,GAAAkB,QAGA,IAAAC,EAAAH,EAAAhB,IACAG,EAAAH,EACAoB,GAAA,EACAF,YAUA,OANAN,EAAAZ,GAAAW,KAAAQ,EAAAD,QAAAC,IAAAD,QAAAJ,GAGAK,EAAAC,GAAA,EAGAD,EAAAD,QAKAJ,EAAAO,EAAAT,EAGAE,EAAAQ,EAAAN,EAGAF,EAAAS,EAAA,SAAAL,EAAAM,EAAAC,GACAX,EAAAY,EAAAR,EAAAM,IACAhB,OAAAmB,eAAAT,EAAAM,GACAI,cAAA,EACAC,YAAA,EACAC,IAAAL,KAMAX,EAAAiB,EAAA,SAAAZ,GACA,IAAAM,EAAAN,KAAAa,WACA,WAA2B,OAAAb,EAAA,SAC3B,WAAiC,OAAAA,GAEjC,OADAL,EAAAS,EAAAE,EAAA,IAAAA,GACAA,GAIAX,EAAAY,EAAA,SAAAO,EAAAC,GAAsD,OAAA1B,OAAAC,UAAAC,eAAAC,KAAAsB,EAAAC,IAGtDpB,EAAAqB,EAAA,IAGArB,EAAAsB,GAAA,SAAAC,GAA8D,MAApBC,QAAAC,MAAAF,GAAoBA","file":"static/js/manifest.2ae2e69a05c33dfc65f8.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tvar parentJsonpFunction = window[\"webpackJsonp\"];\n \twindow[\"webpackJsonp\"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [], result;\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n \t\tif(executeModules) {\n \t\t\tfor(i=0; i < executeModules.length; i++) {\n \t\t\t\tresult = __webpack_require__(__webpack_require__.s = executeModules[i]);\n \t\t\t}\n \t\t}\n \t\treturn result;\n \t};\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// objects to store loaded and loading chunks\n \tvar installedChunks = {\n \t\t2: 0\n \t};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/\";\n\n \t// on error function for async loading\n \t__webpack_require__.oe = function(err) { console.error(err); throw err; };\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap 732b61e485086b18e05b"],"sourceRoot":""}
|
13
go.mod
13
go.mod
|
@ -40,6 +40,7 @@ require (
|
|||
github.com/uptrace/opentelemetry-go-extra/otelgorm v0.1.15
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible
|
||||
go.etcd.io/etcd/client/v3 v3.5.4
|
||||
go.mongodb.org/mongo-driver v1.14.0
|
||||
go.opentelemetry.io/contrib v1.9.0
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.34.0
|
||||
go.opentelemetry.io/otel v1.9.0
|
||||
|
@ -48,7 +49,7 @@ require (
|
|||
go.opentelemetry.io/otel/sdk v1.9.0
|
||||
go.opentelemetry.io/otel/trace v1.9.0
|
||||
go.uber.org/zap v1.21.0
|
||||
golang.org/x/crypto v0.14.0
|
||||
golang.org/x/crypto v0.17.0
|
||||
golang.org/x/sync v0.1.0
|
||||
google.golang.org/genproto v0.0.0-20230223222841-637eb2293923
|
||||
google.golang.org/grpc v1.53.0
|
||||
|
@ -130,6 +131,7 @@ require (
|
|||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/juju/errors v0.0.0-20170703010042-c7d06af17c68 // indirect
|
||||
github.com/klauspost/compress v1.13.6 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||
github.com/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
|
@ -145,6 +147,7 @@ require (
|
|||
github.com/mitchellh/reflectwalk v1.0.1 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
|
@ -164,6 +167,10 @@ require (
|
|||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
github.com/uptrace/opentelemetry-go-extra/otelsql v0.1.15 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
github.com/xdg-go/scram v1.1.2 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
||||
github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.5.4 // indirect
|
||||
|
@ -174,8 +181,8 @@ require (
|
|||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/net v0.10.0 // indirect
|
||||
golang.org/x/oauth2 v0.5.0 // indirect
|
||||
golang.org/x/sys v0.13.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect
|
||||
golang.org/x/tools v0.7.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
|
|
34
go.sum
34
go.sum
|
@ -428,6 +428,8 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
|
|||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
|
||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
|
@ -500,6 +502,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
|
|||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nacos-group/nacos-sdk-go/v2 v2.1.0 h1:PxRwOzHhnK6eGGvioEGkn8s6XRXmUVuXu91i2yQcdDs=
|
||||
|
@ -669,12 +673,21 @@ github.com/uptrace/opentelemetry-go-extra/otelsql v0.1.15/go.mod h1:aZXwJzbTHnhh
|
|||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI=
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
||||
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
||||
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
||||
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 h1:k/gmLsJDWwWqbLCur2yWnJzwQEKRcAHXo6seXGuSwWw=
|
||||
github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA=
|
||||
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
|
||||
|
@ -685,6 +698,8 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.4 h1:lrneYvz923dvC14R54XcA7FXoZ3mlGZAgmwhfm7H
|
|||
go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
||||
go.etcd.io/etcd/client/v3 v3.5.4 h1:p83BUL3tAYS0OT/r0qglgc3M1JjhM0diV8DSWAhVXv4=
|
||||
go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY=
|
||||
go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80=
|
||||
go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
|
@ -742,9 +757,10 @@ golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPh
|
|||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
|
@ -781,6 +797,7 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -829,6 +846,7 @@ golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qx
|
|||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
|
@ -934,13 +952,15 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
@ -953,8 +973,9 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
@ -1018,6 +1039,7 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
|||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
|
||||
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/zhufuyi/sponge/internal/model"
|
||||
|
||||
"github.com/zhufuyi/sponge/pkg/cache"
|
||||
"github.com/zhufuyi/sponge/pkg/encoding"
|
||||
)
|
||||
|
||||
const (
|
||||
// cache prefix key, must end with a colon
|
||||
userExampleCachePrefixKey = "userExample:"
|
||||
// UserExampleExpireTime expire time
|
||||
UserExampleExpireTime = 5 * time.Minute
|
||||
)
|
||||
|
||||
var _ UserExampleCache = (*userExampleCache)(nil)
|
||||
|
||||
// UserExampleCache cache interface
|
||||
type UserExampleCache interface {
|
||||
Set(ctx context.Context, id string, data *model.UserExample, duration time.Duration) error
|
||||
Get(ctx context.Context, id string) (*model.UserExample, error)
|
||||
MultiGet(ctx context.Context, ids []string) (map[string]*model.UserExample, error)
|
||||
MultiSet(ctx context.Context, data []*model.UserExample, duration time.Duration) error
|
||||
Del(ctx context.Context, id string) error
|
||||
SetCacheWithNotFound(ctx context.Context, id string) error
|
||||
}
|
||||
|
||||
// userExampleCache define a cache struct
|
||||
type userExampleCache struct {
|
||||
cache cache.Cache
|
||||
}
|
||||
|
||||
// NewUserExampleCache new a cache
|
||||
func NewUserExampleCache(cacheType *model.CacheType) UserExampleCache {
|
||||
jsonEncoding := encoding.JSONEncoding{}
|
||||
cachePrefix := ""
|
||||
|
||||
cType := strings.ToLower(cacheType.CType)
|
||||
switch cType {
|
||||
case "redis":
|
||||
c := cache.NewRedisCache(cacheType.Rdb, cachePrefix, jsonEncoding, func() interface{} {
|
||||
return &model.UserExample{}
|
||||
})
|
||||
return &userExampleCache{cache: c}
|
||||
case "memory":
|
||||
c := cache.NewMemoryCache(cachePrefix, jsonEncoding, func() interface{} {
|
||||
return &model.UserExample{}
|
||||
})
|
||||
return &userExampleCache{cache: c}
|
||||
}
|
||||
|
||||
return nil // no cache
|
||||
}
|
||||
|
||||
// GetUserExampleCacheKey cache key
|
||||
func (c *userExampleCache) GetUserExampleCacheKey(id string) string {
|
||||
return userExampleCachePrefixKey + id
|
||||
}
|
||||
|
||||
// Set write to cache
|
||||
func (c *userExampleCache) Set(ctx context.Context, id string, data *model.UserExample, duration time.Duration) error {
|
||||
if data == nil || id == "" {
|
||||
return nil
|
||||
}
|
||||
cacheKey := c.GetUserExampleCacheKey(id)
|
||||
err := c.cache.Set(ctx, cacheKey, data, duration)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get cache value
|
||||
func (c *userExampleCache) Get(ctx context.Context, id string) (*model.UserExample, error) {
|
||||
var data *model.UserExample
|
||||
cacheKey := c.GetUserExampleCacheKey(id)
|
||||
err := c.cache.Get(ctx, cacheKey, &data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// MultiSet multiple set cache
|
||||
func (c *userExampleCache) MultiSet(ctx context.Context, data []*model.UserExample, duration time.Duration) error {
|
||||
valMap := make(map[string]interface{})
|
||||
for _, v := range data {
|
||||
cacheKey := c.GetUserExampleCacheKey(v.ID.Hex())
|
||||
valMap[cacheKey] = v
|
||||
}
|
||||
|
||||
err := c.cache.MultiSet(ctx, valMap, duration)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MultiGet multiple get cache, return key in map is id value
|
||||
func (c *userExampleCache) MultiGet(ctx context.Context, ids []string) (map[string]*model.UserExample, error) {
|
||||
var keys []string
|
||||
for _, v := range ids {
|
||||
cacheKey := c.GetUserExampleCacheKey(v)
|
||||
keys = append(keys, cacheKey)
|
||||
}
|
||||
|
||||
itemMap := make(map[string]*model.UserExample)
|
||||
err := c.cache.MultiGet(ctx, keys, itemMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
retMap := make(map[string]*model.UserExample)
|
||||
for _, id := range ids {
|
||||
val, ok := itemMap[c.GetUserExampleCacheKey(id)]
|
||||
if ok {
|
||||
retMap[id] = val
|
||||
}
|
||||
}
|
||||
|
||||
return retMap, nil
|
||||
}
|
||||
|
||||
// Del delete cache
|
||||
func (c *userExampleCache) Del(ctx context.Context, id string) error {
|
||||
cacheKey := c.GetUserExampleCacheKey(id)
|
||||
err := c.cache.Del(ctx, cacheKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetCacheWithNotFound set empty cache
|
||||
func (c *userExampleCache) SetCacheWithNotFound(ctx context.Context, id string) error {
|
||||
cacheKey := c.GetUserExampleCacheKey(id)
|
||||
err := c.cache.SetCacheWithNotFound(ctx, cacheKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -136,10 +136,15 @@ type Redis struct {
|
|||
}
|
||||
|
||||
type Database struct {
|
||||
Driver string `yaml:"driver" json:"driver"`
|
||||
Mysql Mysql `yaml:"mysql" json:"mysql"`
|
||||
Postgresql Postgresql `yaml:"postgresql" json:"postgresql"`
|
||||
Sqlite Sqlite `yaml:"sqlite" json:"sqlite"`
|
||||
Driver string `yaml:"driver" json:"driver"`
|
||||
Mongodb Mongodb `yaml:"mongodb" json:"mongodb"`
|
||||
Mysql Mysql `yaml:"mysql" json:"mysql"`
|
||||
Postgresql Mysql `yaml:"postgresql" json:"postgresql"`
|
||||
Sqlite Sqlite `yaml:"sqlite" json:"sqlite"`
|
||||
}
|
||||
|
||||
type Mongodb struct {
|
||||
Dsn string `yaml:"dsn" json:"dsn"`
|
||||
}
|
||||
|
||||
type Grpc struct {
|
||||
|
@ -167,4 +172,4 @@ type HTTP struct {
|
|||
Port int `yaml:"port" json:"port"`
|
||||
ReadTimeout int `yaml:"readTimeout" json:"readTimeout"`
|
||||
WriteTimeout int `yaml:"writeTimeout" json:"writeTimeout"`
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,418 @@
|
|||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/zhufuyi/sponge/internal/cache"
|
||||
"github.com/zhufuyi/sponge/internal/model"
|
||||
|
||||
cacheBase "github.com/zhufuyi/sponge/pkg/cache"
|
||||
"github.com/zhufuyi/sponge/pkg/mgo"
|
||||
"github.com/zhufuyi/sponge/pkg/mgo/query"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"golang.org/x/sync/singleflight"
|
||||
)
|
||||
|
||||
var _ UserExampleDao = (*userExampleDao)(nil)
|
||||
|
||||
// UserExampleDao defining the dao interface
|
||||
type UserExampleDao interface {
|
||||
Create(ctx context.Context, record *model.UserExample) error
|
||||
DeleteByID(ctx context.Context, id string) error
|
||||
DeleteByIDs(ctx context.Context, ids []string) error
|
||||
UpdateByID(ctx context.Context, record *model.UserExample) error
|
||||
GetByID(ctx context.Context, id string) (*model.UserExample, error)
|
||||
GetByCondition(ctx context.Context, condition *query.Conditions) (*model.UserExample, error)
|
||||
GetByIDs(ctx context.Context, ids []string) (map[string]*model.UserExample, error)
|
||||
GetByLastID(ctx context.Context, lastID string, limit int, sort string) ([]*model.UserExample, error)
|
||||
GetByColumns(ctx context.Context, params *query.Params) ([]*model.UserExample, int64, error)
|
||||
}
|
||||
|
||||
type userExampleDao struct {
|
||||
collection *mongo.Collection
|
||||
cache cache.UserExampleCache // if nil, the cache is not used.
|
||||
sfg *singleflight.Group // if cache is nil, the sfg is not used.
|
||||
}
|
||||
|
||||
// NewUserExampleDao creating the dao interface
|
||||
func NewUserExampleDao(collection *mongo.Collection, xCache cache.UserExampleCache) UserExampleDao {
|
||||
if xCache == nil {
|
||||
return &userExampleDao{collection: collection}
|
||||
}
|
||||
return &userExampleDao{
|
||||
collection: collection,
|
||||
cache: xCache,
|
||||
sfg: new(singleflight.Group),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *userExampleDao) deleteCache(ctx context.Context, id string) error {
|
||||
if d.cache != nil {
|
||||
return d.cache.Del(ctx, id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a record, insert the record and the id value is written back to the table
|
||||
func (d *userExampleDao) Create(ctx context.Context, record *model.UserExample) error {
|
||||
if record.ID.IsZero() {
|
||||
record.ID = primitive.NewObjectID()
|
||||
}
|
||||
if record.CreatedAt.IsZero() {
|
||||
record.CreatedAt = time.Now()
|
||||
record.UpdatedAt = time.Now()
|
||||
}
|
||||
_, err := d.collection.InsertOne(ctx, record)
|
||||
|
||||
_ = d.deleteCache(ctx, record.ID.Hex())
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteByID soft delete a record by id
|
||||
func (d *userExampleDao) DeleteByID(ctx context.Context, id string) error {
|
||||
filter := bson.M{"_id": model.ToObjectID(id)}
|
||||
_, err := d.collection.UpdateOne(ctx, mgo.ExcludeDeleted(filter), mgo.EmbedDeletedAt(bson.M{}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// delete cache
|
||||
_ = d.deleteCache(ctx, id)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteByIDs soft delete records by batch id
|
||||
func (d *userExampleDao) DeleteByIDs(ctx context.Context, ids []string) error {
|
||||
oids := mgo.ConvertToObjectIDs(ids)
|
||||
filter := bson.M{"_id": bson.M{"$in": oids}}
|
||||
_, err := d.collection.UpdateMany(ctx, mgo.ExcludeDeleted(filter), mgo.EmbedDeletedAt(bson.M{}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// delete cache
|
||||
for _, id := range ids {
|
||||
_ = d.deleteCache(ctx, id)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateByID update a record by id
|
||||
func (d *userExampleDao) UpdateByID(ctx context.Context, record *model.UserExample) error {
|
||||
err := d.updateDataByID(ctx, d.collection, record)
|
||||
|
||||
// delete cache
|
||||
_ = d.deleteCache(ctx, record.ID.Hex())
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *userExampleDao) updateDataByID(ctx context.Context, collection *mongo.Collection, table *model.UserExample) error {
|
||||
if table.ID.IsZero() {
|
||||
return errors.New("id is empty or invalid")
|
||||
}
|
||||
|
||||
update := bson.M{}
|
||||
// todo generate the update fields code to here
|
||||
// delete the templates code start
|
||||
if table.Name != "" {
|
||||
update["name"] = table.Name
|
||||
}
|
||||
if table.Password != "" {
|
||||
update["password"] = table.Password
|
||||
}
|
||||
if table.Email != "" {
|
||||
update["email"] = table.Email
|
||||
}
|
||||
if table.Phone != "" {
|
||||
update["phone"] = table.Phone
|
||||
}
|
||||
if table.Avatar != "" {
|
||||
update["avatar"] = table.Avatar
|
||||
}
|
||||
if table.Age > 0 {
|
||||
update["age"] = table.Age
|
||||
}
|
||||
if table.Gender > 0 {
|
||||
update["gender"] = table.Gender
|
||||
}
|
||||
if table.LoginAt > 0 {
|
||||
update["login_at"] = table.LoginAt
|
||||
}
|
||||
// delete the templates code end
|
||||
|
||||
filter := bson.M{"_id": table.ID}
|
||||
_, err := collection.UpdateOne(ctx, mgo.ExcludeDeleted(filter), mgo.EmbedUpdatedAt(update))
|
||||
return err
|
||||
}
|
||||
|
||||
// GetByID get a record by id
|
||||
func (d *userExampleDao) GetByID(ctx context.Context, id string) (*model.UserExample, error) {
|
||||
oid := model.ToObjectID(id)
|
||||
if oid.IsZero() {
|
||||
return nil, model.ErrRecordNotFound
|
||||
}
|
||||
filter := bson.M{"_id": oid}
|
||||
// no cache
|
||||
if d.cache == nil {
|
||||
record := &model.UserExample{}
|
||||
err := d.collection.FindOne(ctx, mgo.ExcludeDeleted(filter)).Decode(record)
|
||||
return record, err
|
||||
}
|
||||
|
||||
// get from cache or mongodb
|
||||
cacheRecord, err := d.cache.Get(ctx, id)
|
||||
if err == nil {
|
||||
return cacheRecord, nil
|
||||
}
|
||||
|
||||
if errors.Is(err, model.ErrCacheNotFound) {
|
||||
// for the same id, prevent high concurrent simultaneous access to mongodb
|
||||
val, err, _ := d.sfg.Do(id, func() (interface{}, error) { //nolint
|
||||
record := &model.UserExample{}
|
||||
err = d.collection.FindOne(ctx, mgo.ExcludeDeleted(filter)).Decode(record)
|
||||
if err != nil {
|
||||
// if data is empty, set not found cache to prevent cache penetration, default expiration time 10 minutes
|
||||
if errors.Is(err, model.ErrRecordNotFound) {
|
||||
err = d.cache.SetCacheWithNotFound(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, model.ErrRecordNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
// set cache
|
||||
err = d.cache.Set(ctx, id, record, cache.UserExampleExpireTime)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cache.Set error: %v, id=%d", err, id)
|
||||
}
|
||||
return record, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
record, ok := val.(*model.UserExample)
|
||||
if !ok {
|
||||
return nil, model.ErrRecordNotFound
|
||||
}
|
||||
return record, nil
|
||||
} else if errors.Is(err, cacheBase.ErrPlaceholder) {
|
||||
return nil, model.ErrRecordNotFound
|
||||
}
|
||||
|
||||
// fail fast, if cache error return, don't request to db
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// GetByCondition get a record by condition
|
||||
// query conditions:
|
||||
//
|
||||
// name: column name, if value is of type objectId, the suffix :oid must be added, e.g. post_id:oid
|
||||
// exp: expressions, which default is "=", support =, !=, >, >=, <, <=, like, in
|
||||
// value: column value, if exp=in, multiple values are separated by commas
|
||||
// logic: logical type, defaults to and when value is null, only &(and), ||(or)
|
||||
//
|
||||
// example: query the id of the post under the user James
|
||||
//
|
||||
// condition = &query.Conditions{
|
||||
// Columns: []query.Column{
|
||||
// {
|
||||
// Name: "user_name",
|
||||
// Value: "James",
|
||||
// },
|
||||
// {
|
||||
// Name: "post_id:oid",
|
||||
// Value: "65ce48483f11aff697e30d6d",
|
||||
// },
|
||||
// }
|
||||
func (d *userExampleDao) GetByCondition(ctx context.Context, c *query.Conditions) (*model.UserExample, error) {
|
||||
filter, err := c.ConvertToMongo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
record := &model.UserExample{}
|
||||
err = d.collection.FindOne(ctx, mgo.ExcludeDeleted(filter)).Decode(record)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return record, nil
|
||||
}
|
||||
|
||||
// GetByIDs get records by batch id
|
||||
func (d *userExampleDao) GetByIDs(ctx context.Context, ids []string) (map[string]*model.UserExample, error) {
|
||||
// no cache
|
||||
if d.cache == nil {
|
||||
records := []*model.UserExample{}
|
||||
oids := mgo.ConvertToObjectIDs(ids)
|
||||
filter := bson.M{"_id": bson.M{"$in": oids}}
|
||||
cursor, err := d.collection.Find(ctx, mgo.ExcludeDeleted(filter))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = cursor.All(ctx, &records)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
itemMap := make(map[string]*model.UserExample)
|
||||
for _, record := range records {
|
||||
itemMap[record.ID.Hex()] = record
|
||||
}
|
||||
return itemMap, nil
|
||||
}
|
||||
|
||||
// get form cache or mongodb
|
||||
itemMap, err := d.cache.MultiGet(ctx, ids)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var missedIDs []string
|
||||
for _, id := range ids {
|
||||
_, ok := itemMap[id]
|
||||
if !ok {
|
||||
missedIDs = append(missedIDs, id)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// get missed data
|
||||
if len(missedIDs) > 0 {
|
||||
// find the id of an active placeholder, i.e. an id that does not exist in mongodb
|
||||
var realMissedIDs []string
|
||||
for _, id := range missedIDs {
|
||||
_, err = d.cache.Get(ctx, id)
|
||||
if errors.Is(err, cacheBase.ErrPlaceholder) {
|
||||
continue
|
||||
}
|
||||
realMissedIDs = append(realMissedIDs, id)
|
||||
}
|
||||
|
||||
if len(realMissedIDs) > 0 {
|
||||
missedData := []*model.UserExample{}
|
||||
oids := mgo.ConvertToObjectIDs(realMissedIDs)
|
||||
filter := bson.M{"_id": bson.M{"$in": oids}}
|
||||
cursor, err := d.collection.Find(ctx, mgo.ExcludeDeleted(filter))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = cursor.All(ctx, &missedData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(missedData) > 0 {
|
||||
for _, data := range missedData {
|
||||
itemMap[data.ID.Hex()] = data
|
||||
}
|
||||
err = d.cache.MultiSet(ctx, missedData, cache.UserExampleExpireTime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
for _, id := range realMissedIDs {
|
||||
_ = d.cache.SetCacheWithNotFound(ctx, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return itemMap, nil
|
||||
}
|
||||
|
||||
// GetByLastID get paging records by last id and limit
|
||||
func (d *userExampleDao) GetByLastID(ctx context.Context, lastID string, limit int, sort string) ([]*model.UserExample, error) {
|
||||
page := query.NewPage(0, limit, sort)
|
||||
|
||||
findOpts := new(options.FindOptions)
|
||||
findOpts.SetLimit(int64(page.Size())).SetSkip(int64(page.Skip()))
|
||||
findOpts.Sort = page.Sort()
|
||||
|
||||
records := []*model.UserExample{}
|
||||
filter := bson.M{"_id": bson.M{"$lt": model.ToObjectID(lastID)}}
|
||||
|
||||
cursor, err := d.collection.Find(ctx, mgo.ExcludeDeleted(filter), findOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = cursor.All(ctx, &records)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return records, nil
|
||||
}
|
||||
|
||||
// GetByColumns get paging records by column information,
|
||||
// Note: query performance degrades when table rows are very large because of the use of offset.
|
||||
//
|
||||
// params includes paging parameters and query parameters
|
||||
// paging parameters (required):
|
||||
//
|
||||
// page: page number, starting from 0
|
||||
// size: lines per page
|
||||
// sort: sort fields, default is id backwards, you can add - sign before the field to indicate reverse order, no - sign to indicate ascending order, multiple fields separated by comma
|
||||
//
|
||||
// query parameters (not required):
|
||||
//
|
||||
// name: column name, if value is of type objectId, the suffix :oid must be added, e.g. order_id:oid
|
||||
// exp: expressions, which default is "=", support =, !=, >, >=, <, <=, like, in
|
||||
// value: column value, if exp=in, multiple values are separated by commas
|
||||
// logic: logical type, defaults to and when value is null, only &(and), ||(or)
|
||||
//
|
||||
// example: search for a male over 20 years of age
|
||||
//
|
||||
// params = &query.Params{
|
||||
// Page: 0,
|
||||
// Size: 20,
|
||||
// Columns: []query.Column{
|
||||
// {
|
||||
// Name: "age",
|
||||
// Exp: ">",
|
||||
// Value: 20,
|
||||
// },
|
||||
// {
|
||||
// Name: "gender",
|
||||
// Value: "male",
|
||||
// },
|
||||
// }
|
||||
func (d *userExampleDao) GetByColumns(ctx context.Context, params *query.Params) ([]*model.UserExample, int64, error) {
|
||||
filter, err := params.ConvertToMongoFilter()
|
||||
if err != nil {
|
||||
return nil, 0, errors.New("query params error: " + err.Error())
|
||||
}
|
||||
|
||||
total, err := d.collection.CountDocuments(ctx, mgo.ExcludeDeleted(filter))
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if total == 0 {
|
||||
return nil, total, nil
|
||||
}
|
||||
|
||||
records := []*model.UserExample{}
|
||||
sort, limit, skip := params.ConvertToPage()
|
||||
findOpts := new(options.FindOptions)
|
||||
findOpts.SetLimit(int64(limit)).SetSkip(int64(skip))
|
||||
findOpts.Sort = sort
|
||||
|
||||
cursor, err := d.collection.Find(ctx, mgo.ExcludeDeleted(filter), findOpts)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
err = cursor.All(ctx, &records)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return records, total, err
|
||||
}
|
|
@ -0,0 +1,414 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/zhufuyi/sponge/internal/cache"
|
||||
"github.com/zhufuyi/sponge/internal/dao"
|
||||
"github.com/zhufuyi/sponge/internal/ecode"
|
||||
"github.com/zhufuyi/sponge/internal/model"
|
||||
"github.com/zhufuyi/sponge/internal/types"
|
||||
|
||||
"github.com/zhufuyi/sponge/pkg/gin/middleware"
|
||||
"github.com/zhufuyi/sponge/pkg/gin/response"
|
||||
"github.com/zhufuyi/sponge/pkg/logger"
|
||||
"github.com/zhufuyi/sponge/pkg/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/jinzhu/copier"
|
||||
)
|
||||
|
||||
var _ UserExampleHandler = (*userExampleHandler)(nil)
|
||||
|
||||
// UserExampleHandler defining the handler interface
|
||||
type UserExampleHandler interface {
|
||||
Create(c *gin.Context)
|
||||
DeleteByID(c *gin.Context)
|
||||
DeleteByIDs(c *gin.Context)
|
||||
UpdateByID(c *gin.Context)
|
||||
GetByID(c *gin.Context)
|
||||
GetByCondition(c *gin.Context)
|
||||
ListByIDs(c *gin.Context)
|
||||
ListByLastID(c *gin.Context)
|
||||
List(c *gin.Context)
|
||||
}
|
||||
|
||||
type userExampleHandler struct {
|
||||
iDao dao.UserExampleDao
|
||||
}
|
||||
|
||||
// NewUserExampleHandler creating the handler interface
|
||||
func NewUserExampleHandler() UserExampleHandler {
|
||||
collectionName := new(model.UserExample).TableName()
|
||||
return &userExampleHandler{
|
||||
iDao: dao.NewUserExampleDao(
|
||||
model.GetDB().Collection(collectionName),
|
||||
cache.NewUserExampleCache(model.GetCacheType()),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
// Create a record
|
||||
// @Summary create userExample
|
||||
// @Description submit information to create userExample
|
||||
// @Tags userExample
|
||||
// @accept json
|
||||
// @Produce json
|
||||
// @Param data body types.CreateUserExampleRequest true "userExample information"
|
||||
// @Success 200 {object} types.CreateUserExampleRespond{}
|
||||
// @Router /api/v1/userExample [post]
|
||||
func (h *userExampleHandler) Create(c *gin.Context) {
|
||||
form := &types.CreateUserExampleRequest{}
|
||||
err := c.ShouldBindJSON(form)
|
||||
if err != nil {
|
||||
logger.Warn("ShouldBindJSON error: ", logger.Err(err), middleware.GCtxRequestIDField(c))
|
||||
response.Error(c, ecode.InvalidParams)
|
||||
return
|
||||
}
|
||||
|
||||
userExample := &model.UserExample{}
|
||||
err = copier.Copy(userExample, form)
|
||||
if err != nil {
|
||||
response.Error(c, ecode.ErrCreateUserExample)
|
||||
return
|
||||
}
|
||||
// todo if copier.Copy cannot assign a value to a field, add it here
|
||||
|
||||
ctx := middleware.WrapCtx(c)
|
||||
err = h.iDao.Create(ctx, userExample)
|
||||
if err != nil {
|
||||
logger.Error("Create error", logger.Err(err), logger.Any("form", form), middleware.GCtxRequestIDField(c))
|
||||
response.Output(c, ecode.InternalServerError.ToHTTPCode())
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{"id": userExample.ID})
|
||||
}
|
||||
|
||||
// DeleteByID delete a record by id
|
||||
// @Summary delete userExample
|
||||
// @Description delete userExample by id
|
||||
// @Tags userExample
|
||||
// @accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "id"
|
||||
// @Success 200 {object} types.DeleteUserExampleByIDRespond{}
|
||||
// @Router /api/v1/userExample/{id} [delete]
|
||||
func (h *userExampleHandler) DeleteByID(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
ctx := middleware.WrapCtx(c)
|
||||
err := h.iDao.DeleteByID(ctx, id)
|
||||
if err != nil {
|
||||
logger.Error("DeleteByID error", logger.Err(err), logger.Any("id", id), middleware.GCtxRequestIDField(c))
|
||||
response.Output(c, ecode.InternalServerError.ToHTTPCode())
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c)
|
||||
}
|
||||
|
||||
// DeleteByIDs delete records by batch id
|
||||
// @Summary delete userExamples
|
||||
// @Description delete userExamples by batch id
|
||||
// @Tags userExample
|
||||
// @Param data body types.DeleteUserExamplesByIDsRequest true "id array"
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} types.DeleteUserExamplesByIDsRespond{}
|
||||
// @Router /api/v1/userExample/delete/ids [post]
|
||||
func (h *userExampleHandler) DeleteByIDs(c *gin.Context) {
|
||||
form := &types.DeleteUserExamplesByIDsRequest{}
|
||||
err := c.ShouldBindJSON(form)
|
||||
if err != nil {
|
||||
logger.Warn("ShouldBindJSON error: ", logger.Err(err), middleware.GCtxRequestIDField(c))
|
||||
response.Error(c, ecode.InvalidParams)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := middleware.WrapCtx(c)
|
||||
err = h.iDao.DeleteByIDs(ctx, form.IDs)
|
||||
if err != nil {
|
||||
logger.Error("GetByIDs error", logger.Err(err), logger.Any("form", form), middleware.GCtxRequestIDField(c))
|
||||
response.Output(c, ecode.InternalServerError.ToHTTPCode())
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c)
|
||||
}
|
||||
|
||||
// UpdateByID update information by id
|
||||
// @Summary update userExample
|
||||
// @Description update userExample information by id
|
||||
// @Tags userExample
|
||||
// @accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "id"
|
||||
// @Param data body types.UpdateUserExampleByIDRequest true "userExample information"
|
||||
// @Success 200 {object} types.UpdateUserExampleByIDRespond{}
|
||||
// @Router /api/v1/userExample/{id} [put]
|
||||
func (h *userExampleHandler) UpdateByID(c *gin.Context) {
|
||||
oid := model.ToObjectID(c.Param("id"))
|
||||
if oid.IsZero() {
|
||||
logger.Warn("id invalid error", middleware.GCtxRequestIDField(c))
|
||||
response.Error(c, ecode.InvalidParams)
|
||||
return
|
||||
}
|
||||
form := &types.UpdateUserExampleByIDRequest{}
|
||||
err := c.ShouldBindJSON(form)
|
||||
if err != nil {
|
||||
logger.Warn("ShouldBindJSON error: ", logger.Err(err), middleware.GCtxRequestIDField(c))
|
||||
response.Error(c, ecode.InvalidParams)
|
||||
return
|
||||
}
|
||||
|
||||
userExample := &model.UserExample{}
|
||||
err = copier.Copy(userExample, form)
|
||||
if err != nil {
|
||||
response.Error(c, ecode.ErrUpdateByIDUserExample)
|
||||
return
|
||||
}
|
||||
userExample.ID = oid
|
||||
// todo if copier.Copy cannot assign a value to a field, add it here
|
||||
|
||||
ctx := middleware.WrapCtx(c)
|
||||
err = h.iDao.UpdateByID(ctx, userExample)
|
||||
if err != nil {
|
||||
logger.Error("UpdateByID error", logger.Err(err), logger.Any("form", form), middleware.GCtxRequestIDField(c))
|
||||
response.Output(c, ecode.InternalServerError.ToHTTPCode())
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c)
|
||||
}
|
||||
|
||||
// GetByID get a record by id
|
||||
// @Summary get userExample detail
|
||||
// @Description get userExample detail by id
|
||||
// @Tags userExample
|
||||
// @Param id path string true "id"
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} types.GetUserExampleByIDRespond{}
|
||||
// @Router /api/v1/userExample/{id} [get]
|
||||
func (h *userExampleHandler) GetByID(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
ctx := middleware.WrapCtx(c)
|
||||
userExample, err := h.iDao.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrRecordNotFound) {
|
||||
logger.Warn("GetByID not found", logger.Err(err), logger.Any("id", id), middleware.GCtxRequestIDField(c))
|
||||
response.Error(c, ecode.NotFound)
|
||||
} else {
|
||||
logger.Error("GetByID error", logger.Err(err), logger.Any("id", id), middleware.GCtxRequestIDField(c))
|
||||
response.Output(c, ecode.InternalServerError.ToHTTPCode())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
data := &types.UserExampleObjDetail{}
|
||||
err = copier.Copy(data, userExample)
|
||||
if err != nil {
|
||||
response.Error(c, ecode.ErrGetByIDUserExample)
|
||||
return
|
||||
}
|
||||
data.ID = userExample.ID.Hex()
|
||||
// todo if copier.Copy cannot assign a value to a field, add it here
|
||||
|
||||
response.Success(c, gin.H{"userExample": data})
|
||||
}
|
||||
|
||||
// GetByCondition get a record by condition
|
||||
// @Summary get userExample by condition
|
||||
// @Description get userExample by condition
|
||||
// @Tags userExample
|
||||
// @Param data body types.Conditions true "query condition"
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} types.GetUserExampleByConditionRespond{}
|
||||
// @Router /api/v1/userExample/condition [post]
|
||||
func (h *userExampleHandler) GetByCondition(c *gin.Context) {
|
||||
form := &types.GetUserExampleByConditionRequest{}
|
||||
err := c.ShouldBindJSON(form)
|
||||
if err != nil {
|
||||
logger.Warn("ShouldBindJSON error: ", logger.Err(err), middleware.GCtxRequestIDField(c))
|
||||
response.Error(c, ecode.InvalidParams)
|
||||
return
|
||||
}
|
||||
err = form.Conditions.CheckValid()
|
||||
if err != nil {
|
||||
logger.Warn("Parameters error: ", logger.Err(err), middleware.GCtxRequestIDField(c))
|
||||
response.Error(c, ecode.InvalidParams)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := middleware.WrapCtx(c)
|
||||
userExample, err := h.iDao.GetByCondition(ctx, &form.Conditions)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrRecordNotFound) {
|
||||
logger.Warn("GetByCondition not found", logger.Err(err), logger.Any("form", form), middleware.GCtxRequestIDField(c))
|
||||
response.Error(c, ecode.NotFound)
|
||||
} else {
|
||||
logger.Error("GetByCondition error", logger.Err(err), logger.Any("form", form), middleware.GCtxRequestIDField(c))
|
||||
response.Output(c, ecode.InternalServerError.ToHTTPCode())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
data := &types.UserExampleObjDetail{}
|
||||
err = copier.Copy(data, userExample)
|
||||
if err != nil {
|
||||
response.Error(c, ecode.ErrGetByIDUserExample)
|
||||
return
|
||||
}
|
||||
data.ID = userExample.ID.Hex()
|
||||
// todo if copier.Copy cannot assign a value to a field, add it here
|
||||
|
||||
response.Success(c, gin.H{"userExample": data})
|
||||
}
|
||||
|
||||
// ListByIDs list of records by batch id
|
||||
// @Summary list of userExamples by batch id
|
||||
// @Description list of userExamples by batch id
|
||||
// @Tags userExample
|
||||
// @Param data body types.ListUserExamplesByIDsRequest true "id array"
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} types.ListUserExamplesByIDsRespond{}
|
||||
// @Router /api/v1/userExample/list/ids [post]
|
||||
func (h *userExampleHandler) ListByIDs(c *gin.Context) {
|
||||
form := &types.ListUserExamplesByIDsRequest{}
|
||||
err := c.ShouldBindJSON(form)
|
||||
if err != nil {
|
||||
logger.Warn("ShouldBindJSON error: ", logger.Err(err), middleware.GCtxRequestIDField(c))
|
||||
response.Error(c, ecode.InvalidParams)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := middleware.WrapCtx(c)
|
||||
userExampleMap, err := h.iDao.GetByIDs(ctx, form.IDs)
|
||||
if err != nil {
|
||||
logger.Error("GetByIDs error", logger.Err(err), logger.Any("form", form), middleware.GCtxRequestIDField(c))
|
||||
response.Output(c, ecode.InternalServerError.ToHTTPCode())
|
||||
return
|
||||
}
|
||||
|
||||
userExamples := []*types.UserExampleObjDetail{}
|
||||
for _, id := range form.IDs {
|
||||
if v, ok := userExampleMap[id]; ok {
|
||||
record, err := convertUserExample(v)
|
||||
if err != nil {
|
||||
response.Error(c, ecode.ErrListUserExample)
|
||||
return
|
||||
}
|
||||
userExamples = append(userExamples, record)
|
||||
}
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"userExamples": userExamples,
|
||||
})
|
||||
}
|
||||
|
||||
// ListByLastID get records by last id and limit
|
||||
// @Summary list of userExamples by last id and limit
|
||||
// @Description list of userExamples by last id and limit
|
||||
// @Tags userExample
|
||||
// @accept json
|
||||
// @Produce json
|
||||
// @Param lastID query string false "last id, default()"
|
||||
// @Param limit query int false "size per page" default(10)
|
||||
// @Param sort query string false "sort by column name of table, and the "-" sign before column name indicates reverse order" default(-id)
|
||||
// @Success 200 {object} types.ListUserExamplesRespond{}
|
||||
// @Router /api/v1/userExample/list [get]
|
||||
func (h *userExampleHandler) ListByLastID(c *gin.Context) {
|
||||
lastID := c.Query("lastID")
|
||||
if lastID == "" {
|
||||
lastID = model.MaxObjectID
|
||||
}
|
||||
limit := utils.StrToInt(c.Query("limit"))
|
||||
if limit == 0 {
|
||||
limit = 10
|
||||
}
|
||||
sort := c.Query("sort")
|
||||
|
||||
ctx := middleware.WrapCtx(c)
|
||||
userExamples, err := h.iDao.GetByLastID(ctx, lastID, limit, sort)
|
||||
if err != nil {
|
||||
logger.Error("GetByLastID error", logger.Err(err), logger.String("latsID", lastID), logger.Int("limit", limit), middleware.GCtxRequestIDField(c))
|
||||
response.Output(c, ecode.InternalServerError.ToHTTPCode())
|
||||
return
|
||||
}
|
||||
|
||||
data, err := convertUserExamples(userExamples)
|
||||
if err != nil {
|
||||
response.Error(c, ecode.ErrListByLastIDUserExample)
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"userExamples": data,
|
||||
})
|
||||
}
|
||||
|
||||
// List of records by query parameters
|
||||
// @Summary list of userExamples by query parameters
|
||||
// @Description list of userExamples by paging and conditions
|
||||
// @Tags userExample
|
||||
// @accept json
|
||||
// @Produce json
|
||||
// @Param data body types.Params true "query parameters"
|
||||
// @Success 200 {object} types.ListUserExamplesRespond{}
|
||||
// @Router /api/v1/userExample/list [post]
|
||||
func (h *userExampleHandler) List(c *gin.Context) {
|
||||
form := &types.ListUserExamplesRequest{}
|
||||
err := c.ShouldBindJSON(form)
|
||||
if err != nil {
|
||||
logger.Warn("ShouldBindJSON error: ", logger.Err(err), middleware.GCtxRequestIDField(c))
|
||||
response.Error(c, ecode.InvalidParams)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := middleware.WrapCtx(c)
|
||||
userExamples, total, err := h.iDao.GetByColumns(ctx, &form.Params)
|
||||
if err != nil {
|
||||
logger.Error("GetByColumns error", logger.Err(err), logger.Any("form", form), middleware.GCtxRequestIDField(c))
|
||||
response.Output(c, ecode.InternalServerError.ToHTTPCode())
|
||||
return
|
||||
}
|
||||
|
||||
data, err := convertUserExamples(userExamples)
|
||||
if err != nil {
|
||||
response.Error(c, ecode.ErrListUserExample)
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"userExamples": data,
|
||||
"total": total,
|
||||
})
|
||||
}
|
||||
|
||||
func convertUserExample(userExample *model.UserExample) (*types.UserExampleObjDetail, error) {
|
||||
data := &types.UserExampleObjDetail{}
|
||||
err := copier.Copy(data, userExample)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data.ID = userExample.ID.Hex()
|
||||
// todo if copier.Copy cannot assign a value to a field, add it here
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func convertUserExamples(fromValues []*model.UserExample) ([]*types.UserExampleObjDetail, error) {
|
||||
toValues := []*types.UserExampleObjDetail{}
|
||||
for _, v := range fromValues {
|
||||
data, err := convertUserExample(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
toValues = append(toValues, data)
|
||||
}
|
||||
|
||||
return toValues, nil
|
||||
}
|
|
@ -297,6 +297,7 @@ func convertUserExamplePb(record *model.UserExample) (*serverNameExampleV1.UserE
|
|||
return nil, err
|
||||
}
|
||||
value.Id = record.ID
|
||||
// todo if copier.Copy cannot assign a value to a field, add it here, e.g. CreatedAt, UpdatedAt
|
||||
// todo generate the conversion createdAt and updatedAt code here
|
||||
// delete the templates code start
|
||||
value.CreatedAt = record.CreatedAt.Unix()
|
||||
|
|
|
@ -0,0 +1,310 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
serverNameExampleV1 "github.com/zhufuyi/sponge/api/serverNameExample/v1"
|
||||
"github.com/zhufuyi/sponge/internal/cache"
|
||||
"github.com/zhufuyi/sponge/internal/dao"
|
||||
"github.com/zhufuyi/sponge/internal/ecode"
|
||||
"github.com/zhufuyi/sponge/internal/model"
|
||||
|
||||
"github.com/zhufuyi/sponge/pkg/gin/middleware"
|
||||
"github.com/zhufuyi/sponge/pkg/logger"
|
||||
"github.com/zhufuyi/sponge/pkg/mgo/query"
|
||||
|
||||
"github.com/jinzhu/copier"
|
||||
)
|
||||
|
||||
var _ serverNameExampleV1.UserExampleLogicer = (*userExamplePbHandler)(nil)
|
||||
|
||||
type userExamplePbHandler struct {
|
||||
userExampleDao dao.UserExampleDao
|
||||
}
|
||||
|
||||
// NewUserExamplePbHandler create a handler
|
||||
func NewUserExamplePbHandler() serverNameExampleV1.UserExampleLogicer {
|
||||
collectionName := new(model.UserExample).TableName()
|
||||
return &userExamplePbHandler{
|
||||
userExampleDao: dao.NewUserExampleDao(
|
||||
model.GetDB().Collection(collectionName),
|
||||
cache.NewUserExampleCache(model.GetCacheType()),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
// Create a record
|
||||
func (h *userExamplePbHandler) Create(ctx context.Context, req *serverNameExampleV1.CreateUserExampleRequest) (*serverNameExampleV1.CreateUserExampleReply, error) {
|
||||
err := req.Validate()
|
||||
if err != nil {
|
||||
logger.Warn("req.Validate error", logger.Err(err), logger.Any("req", req), middleware.CtxRequestIDField(ctx))
|
||||
return nil, ecode.InvalidParams.Err()
|
||||
}
|
||||
|
||||
userExample := &model.UserExample{}
|
||||
err = copier.Copy(userExample, req)
|
||||
if err != nil {
|
||||
return nil, ecode.ErrCreateUserExample.Err()
|
||||
}
|
||||
// todo if copier.Copy cannot assign a value to a field, add it here
|
||||
|
||||
err = h.userExampleDao.Create(ctx, userExample)
|
||||
if err != nil {
|
||||
logger.Error("Create error", logger.Err(err), logger.Any("userExample", userExample), middleware.CtxRequestIDField(ctx))
|
||||
return nil, ecode.InternalServerError.Err()
|
||||
}
|
||||
|
||||
return &serverNameExampleV1.CreateUserExampleReply{Id: userExample.ID.Hex()}, nil
|
||||
}
|
||||
|
||||
// DeleteByID delete a record by id
|
||||
func (h *userExamplePbHandler) DeleteByID(ctx context.Context, req *serverNameExampleV1.DeleteUserExampleByIDRequest) (*serverNameExampleV1.DeleteUserExampleByIDReply, error) {
|
||||
err := req.Validate()
|
||||
if err != nil {
|
||||
logger.Warn("req.Validate error", logger.Err(err), logger.Any("req", req), middleware.CtxRequestIDField(ctx))
|
||||
return nil, ecode.InvalidParams.Err()
|
||||
}
|
||||
|
||||
err = h.userExampleDao.DeleteByID(ctx, req.Id)
|
||||
if err != nil {
|
||||
logger.Warn("DeleteByID error", logger.Err(err), middleware.CtxRequestIDField(ctx))
|
||||
return nil, ecode.InternalServerError.Err()
|
||||
}
|
||||
|
||||
return &serverNameExampleV1.DeleteUserExampleByIDReply{}, nil
|
||||
}
|
||||
|
||||
// DeleteByIDs delete records by batch id
|
||||
func (h *userExamplePbHandler) DeleteByIDs(ctx context.Context, req *serverNameExampleV1.DeleteUserExampleByIDsRequest) (*serverNameExampleV1.DeleteUserExampleByIDsReply, error) {
|
||||
err := req.Validate()
|
||||
if err != nil {
|
||||
logger.Warn("req.Validate error", logger.Err(err), logger.Any("req", req), middleware.CtxRequestIDField(ctx))
|
||||
return nil, ecode.InvalidParams.Err()
|
||||
}
|
||||
|
||||
err = h.userExampleDao.DeleteByIDs(ctx, req.Ids)
|
||||
if err != nil {
|
||||
logger.Warn("DeleteByIDs error", logger.Err(err), middleware.CtxRequestIDField(ctx))
|
||||
return nil, ecode.InternalServerError.Err()
|
||||
}
|
||||
|
||||
return &serverNameExampleV1.DeleteUserExampleByIDsReply{}, nil
|
||||
}
|
||||
|
||||
// UpdateByID update a record by id
|
||||
func (h *userExamplePbHandler) UpdateByID(ctx context.Context, req *serverNameExampleV1.UpdateUserExampleByIDRequest) (*serverNameExampleV1.UpdateUserExampleByIDReply, error) {
|
||||
err := req.Validate()
|
||||
if err != nil {
|
||||
logger.Warn("req.Validate error", logger.Err(err), logger.Any("req", req), middleware.CtxRequestIDField(ctx))
|
||||
return nil, ecode.InvalidParams.Err()
|
||||
}
|
||||
|
||||
userExample := &model.UserExample{}
|
||||
err = copier.Copy(userExample, req)
|
||||
if err != nil {
|
||||
return nil, ecode.ErrUpdateByIDUserExample.Err()
|
||||
}
|
||||
userExample.ID = model.ToObjectID(req.Id)
|
||||
// todo if copier.Copy cannot assign a value to a field, add it here
|
||||
|
||||
err = h.userExampleDao.UpdateByID(ctx, userExample)
|
||||
if err != nil {
|
||||
logger.Error("UpdateByID error", logger.Err(err), logger.Any("userExample", userExample), middleware.CtxRequestIDField(ctx))
|
||||
return nil, ecode.InternalServerError.Err()
|
||||
}
|
||||
|
||||
return &serverNameExampleV1.UpdateUserExampleByIDReply{}, nil
|
||||
}
|
||||
|
||||
// GetByID get a record by id
|
||||
func (h *userExamplePbHandler) GetByID(ctx context.Context, req *serverNameExampleV1.GetUserExampleByIDRequest) (*serverNameExampleV1.GetUserExampleByIDReply, error) {
|
||||
err := req.Validate()
|
||||
if err != nil {
|
||||
logger.Warn("req.Validate error", logger.Err(err), logger.Any("req", req), middleware.CtxRequestIDField(ctx))
|
||||
return nil, ecode.InvalidParams.Err()
|
||||
}
|
||||
|
||||
record, err := h.userExampleDao.GetByID(ctx, req.Id)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrRecordNotFound) {
|
||||
logger.Warn("GetByID error", logger.Err(err), logger.Any("id", req.Id), middleware.CtxRequestIDField(ctx))
|
||||
return nil, ecode.NotFound.Err()
|
||||
}
|
||||
logger.Error("GetByID error", logger.Err(err), logger.Any("id", req.Id), middleware.CtxRequestIDField(ctx))
|
||||
return nil, ecode.InternalServerError.Err()
|
||||
}
|
||||
|
||||
data, err := convertUserExamplePb(record)
|
||||
if err != nil {
|
||||
logger.Warn("convertUserExample error", logger.Err(err), logger.Any("userExample", record), middleware.CtxRequestIDField(ctx))
|
||||
return nil, ecode.ErrGetByIDUserExample.Err()
|
||||
}
|
||||
|
||||
return &serverNameExampleV1.GetUserExampleByIDReply{
|
||||
UserExample: data,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetByCondition get a record by condition
|
||||
func (h *userExamplePbHandler) GetByCondition(ctx context.Context, req *serverNameExampleV1.GetUserExampleByConditionRequest) (*serverNameExampleV1.GetUserExampleByConditionReply, error) {
|
||||
err := req.Validate()
|
||||
if err != nil {
|
||||
logger.Warn("req.Validate error", logger.Err(err), logger.Any("req", req), middleware.CtxRequestIDField(ctx))
|
||||
return nil, ecode.InvalidParams.Err()
|
||||
}
|
||||
|
||||
conditions := &query.Conditions{}
|
||||
for _, v := range req.Conditions.GetColumns() {
|
||||
column := query.Column{}
|
||||
_ = copier.Copy(&column, v)
|
||||
conditions.Columns = append(conditions.Columns, column)
|
||||
}
|
||||
err = conditions.CheckValid()
|
||||
if err != nil {
|
||||
logger.Warn("Parameters error", logger.Err(err), logger.Any("conditions", conditions), middleware.CtxRequestIDField(ctx))
|
||||
return nil, ecode.InvalidParams.Err()
|
||||
}
|
||||
|
||||
record, err := h.userExampleDao.GetByCondition(ctx, conditions)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrRecordNotFound) {
|
||||
logger.Warn("GetByID error", logger.Err(err), logger.Any("req", req), middleware.CtxRequestIDField(ctx))
|
||||
return nil, ecode.NotFound.Err()
|
||||
}
|
||||
logger.Error("GetByID error", logger.Err(err), logger.Any("req", req), middleware.CtxRequestIDField(ctx))
|
||||
return nil, ecode.InternalServerError.Err()
|
||||
}
|
||||
|
||||
data, err := convertUserExamplePb(record)
|
||||
if err != nil {
|
||||
logger.Warn("convertUserExample error", logger.Err(err), logger.Any("userExample", record), middleware.CtxRequestIDField(ctx))
|
||||
return nil, ecode.ErrGetByIDUserExample.Err()
|
||||
}
|
||||
|
||||
return &serverNameExampleV1.GetUserExampleByConditionReply{
|
||||
UserExample: data,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ListByIDs list of records by batch id
|
||||
func (h *userExamplePbHandler) ListByIDs(ctx context.Context, req *serverNameExampleV1.ListUserExampleByIDsRequest) (*serverNameExampleV1.ListUserExampleByIDsReply, error) {
|
||||
err := req.Validate()
|
||||
if err != nil {
|
||||
logger.Warn("req.Validate error", logger.Err(err), logger.Any("req", req), middleware.CtxRequestIDField(ctx))
|
||||
return nil, ecode.InvalidParams.Err()
|
||||
}
|
||||
|
||||
userExampleMap, err := h.userExampleDao.GetByIDs(ctx, req.Ids)
|
||||
if err != nil {
|
||||
logger.Error("GetByIDs error", logger.Err(err), logger.Any("ids", req.Ids), middleware.CtxRequestIDField(ctx))
|
||||
return nil, ecode.InternalServerError.Err()
|
||||
}
|
||||
|
||||
userExamples := []*serverNameExampleV1.UserExample{}
|
||||
for _, id := range req.Ids {
|
||||
if v, ok := userExampleMap[id]; ok {
|
||||
record, err := convertUserExamplePb(v)
|
||||
if err != nil {
|
||||
logger.Warn("convertUserExample error", logger.Err(err), logger.Any("userExample", v), middleware.CtxRequestIDField(ctx))
|
||||
return nil, ecode.InternalServerError.Err()
|
||||
}
|
||||
userExamples = append(userExamples, record)
|
||||
}
|
||||
}
|
||||
|
||||
return &serverNameExampleV1.ListUserExampleByIDsReply{
|
||||
UserExamples: userExamples,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ListByLastID get records by last id
|
||||
func (h *userExamplePbHandler) ListByLastID(ctx context.Context, req *serverNameExampleV1.ListUserExampleByLastIDRequest) (*serverNameExampleV1.ListUserExampleByLastIDReply, error) {
|
||||
err := req.Validate()
|
||||
if err != nil {
|
||||
logger.Warn("req.Validate error", logger.Err(err), logger.Any("req", req), middleware.CtxRequestIDField(ctx))
|
||||
return nil, ecode.InvalidParams.Err()
|
||||
}
|
||||
if req.LastID == "" {
|
||||
req.LastID = model.MaxObjectID
|
||||
}
|
||||
|
||||
records, err := h.userExampleDao.GetByLastID(ctx, req.LastID, int(req.Limit), req.Sort)
|
||||
if err != nil {
|
||||
logger.Error("GetByColumns error", logger.Err(err), logger.Any("req", req), middleware.CtxRequestIDField(ctx))
|
||||
return nil, ecode.InternalServerError.Err()
|
||||
}
|
||||
|
||||
userExamples := []*serverNameExampleV1.UserExample{}
|
||||
for _, record := range records {
|
||||
data, err := convertUserExamplePb(record)
|
||||
if err != nil {
|
||||
logger.Warn("convertUserExample error", logger.Err(err), logger.Any("id", record.ID), middleware.CtxRequestIDField(ctx))
|
||||
continue
|
||||
}
|
||||
userExamples = append(userExamples, data)
|
||||
}
|
||||
|
||||
return &serverNameExampleV1.ListUserExampleByLastIDReply{
|
||||
UserExamples: userExamples,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// List of records by query parameters
|
||||
func (h *userExamplePbHandler) List(ctx context.Context, req *serverNameExampleV1.ListUserExampleRequest) (*serverNameExampleV1.ListUserExampleReply, error) {
|
||||
err := req.Validate()
|
||||
if err != nil {
|
||||
logger.Warn("req.Validate error", logger.Err(err), logger.Any("req", req), middleware.CtxRequestIDField(ctx))
|
||||
return nil, ecode.InvalidParams.Err()
|
||||
}
|
||||
|
||||
params := &query.Params{}
|
||||
err = copier.Copy(params, req.Params)
|
||||
if err != nil {
|
||||
return nil, ecode.ErrListUserExample.Err()
|
||||
}
|
||||
params.Size = int(req.Params.Limit)
|
||||
// todo if copier.Copy cannot assign a value to a field, add it here
|
||||
|
||||
records, total, err := h.userExampleDao.GetByColumns(ctx, params)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "query params error:") {
|
||||
logger.Warn("GetByColumns error", logger.Err(err), logger.Any("params", params), middleware.CtxRequestIDField(ctx))
|
||||
return nil, ecode.InvalidParams.Err()
|
||||
}
|
||||
logger.Error("GetByColumns error", logger.Err(err), logger.Any("params", params), middleware.CtxRequestIDField(ctx))
|
||||
return nil, ecode.InternalServerError.Err()
|
||||
}
|
||||
|
||||
userExamples := []*serverNameExampleV1.UserExample{}
|
||||
for _, record := range records {
|
||||
data, err := convertUserExamplePb(record)
|
||||
if err != nil {
|
||||
logger.Warn("convertUserExample error", logger.Err(err), logger.Any("id", record.ID), middleware.CtxRequestIDField(ctx))
|
||||
continue
|
||||
}
|
||||
userExamples = append(userExamples, data)
|
||||
}
|
||||
|
||||
return &serverNameExampleV1.ListUserExampleReply{
|
||||
Total: total,
|
||||
UserExamples: userExamples,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func convertUserExamplePb(record *model.UserExample) (*serverNameExampleV1.UserExample, error) {
|
||||
value := &serverNameExampleV1.UserExample{}
|
||||
err := copier.Copy(value, record)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
value.Id = record.ID.Hex()
|
||||
// todo if copier.Copy cannot assign a value to a field, add it here, e.g. CreatedAt, UpdatedAt
|
||||
// todo generate the conversion createdAt and updatedAt code here
|
||||
// delete the templates code start
|
||||
value.CreatedAt = record.CreatedAt.Unix()
|
||||
value.UpdatedAt = record.UpdatedAt.Unix()
|
||||
// delete the templates code end
|
||||
return value, nil
|
||||
}
|
|
@ -7,6 +7,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/zhufuyi/sponge/internal/config"
|
||||
|
||||
"github.com/zhufuyi/sponge/pkg/ggorm"
|
||||
"github.com/zhufuyi/sponge/pkg/goredis"
|
||||
"github.com/zhufuyi/sponge/pkg/logger"
|
||||
|
@ -121,7 +122,7 @@ func InitDB() {
|
|||
case ggorm.DBDriverSqlite:
|
||||
InitSqlite()
|
||||
default:
|
||||
panic("unsupported database driver: " + config.Get().Database.Driver)
|
||||
panic("InitDB error, unsupported database driver: " + config.Get().Database.Driver)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -157,7 +158,7 @@ func InitMysql() {
|
|||
var err error
|
||||
db, err = ggorm.InitMysql(dsn, opts...)
|
||||
if err != nil {
|
||||
panic("ggorm.InitMysql error: " + err.Error())
|
||||
panic("InitMysql error: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -186,7 +187,7 @@ func InitPostgresql() {
|
|||
var err error
|
||||
db, err = ggorm.InitPostgresql(dsn, opts...)
|
||||
if err != nil {
|
||||
panic("ggorm.InitPostgresql error: " + err.Error())
|
||||
panic("InitPostgresql error: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,9 +210,10 @@ func InitSqlite() {
|
|||
}
|
||||
|
||||
var err error
|
||||
db, err = ggorm.InitSqlite(config.Get().Database.Sqlite.DBFile, opts...)
|
||||
var dbFile = utils.AdaptiveSqlite(config.Get().Database.Sqlite.DBFile)
|
||||
db, err = ggorm.InitSqlite(dbFile, opts...)
|
||||
if err != nil {
|
||||
panic("ggorm.InitSqlite error: " + err.Error())
|
||||
panic("InitSqlite error: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/zhufuyi/sponge/internal/config"
|
||||
|
||||
"github.com/zhufuyi/sponge/pkg/goredis"
|
||||
"github.com/zhufuyi/sponge/pkg/mgo"
|
||||
"github.com/zhufuyi/sponge/pkg/utils"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
)
|
||||
|
||||
// MaxObjectID max object id
|
||||
const MaxObjectID = "fffffffffffffffffffffffe"
|
||||
|
||||
var (
|
||||
// ErrCacheNotFound No hit cache
|
||||
ErrCacheNotFound = redis.Nil
|
||||
|
||||
// ErrRecordNotFound no records found
|
||||
ErrRecordNotFound = mongo.ErrNoDocuments
|
||||
)
|
||||
|
||||
var (
|
||||
db *mongo.Database
|
||||
once1 sync.Once
|
||||
|
||||
redisCli *redis.Client
|
||||
once2 sync.Once
|
||||
|
||||
cacheType *CacheType
|
||||
once3 sync.Once
|
||||
)
|
||||
|
||||
// CacheType cache type
|
||||
type CacheType struct {
|
||||
CType string // cache type memory or redis
|
||||
Rdb *redis.Client // if CType=redis, Rdb cannot be empty
|
||||
}
|
||||
|
||||
// InitCache initial cache
|
||||
func InitCache(cType string) {
|
||||
cacheType = &CacheType{
|
||||
CType: cType,
|
||||
}
|
||||
|
||||
if cType == "redis" {
|
||||
cacheType.Rdb = GetRedisCli()
|
||||
}
|
||||
}
|
||||
|
||||
// GetCacheType get cacheType
|
||||
func GetCacheType() *CacheType {
|
||||
if cacheType == nil {
|
||||
once3.Do(func() {
|
||||
InitCache(config.Get().App.CacheType)
|
||||
})
|
||||
}
|
||||
|
||||
return cacheType
|
||||
}
|
||||
|
||||
// InitRedis connect redis
|
||||
func InitRedis() {
|
||||
opts := []goredis.Option{
|
||||
goredis.WithDialTimeout(time.Duration(config.Get().Redis.DialTimeout) * time.Second),
|
||||
goredis.WithReadTimeout(time.Duration(config.Get().Redis.ReadTimeout) * time.Second),
|
||||
goredis.WithWriteTimeout(time.Duration(config.Get().Redis.WriteTimeout) * time.Second),
|
||||
}
|
||||
if config.Get().App.EnableTrace {
|
||||
opts = append(opts, goredis.WithEnableTrace())
|
||||
}
|
||||
|
||||
var err error
|
||||
redisCli, err = goredis.Init(config.Get().Redis.Dsn, opts...)
|
||||
if err != nil {
|
||||
panic("goredis.Init error: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// GetRedisCli get redis client
|
||||
func GetRedisCli() *redis.Client {
|
||||
if redisCli == nil {
|
||||
once2.Do(func() {
|
||||
InitRedis()
|
||||
})
|
||||
}
|
||||
|
||||
return redisCli
|
||||
}
|
||||
|
||||
// CloseRedis close redis
|
||||
func CloseRedis() error {
|
||||
if redisCli == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := redisCli.Close()
|
||||
if err != nil && err.Error() != redis.ErrClosed.Error() {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
|
||||
// InitDB connect database
|
||||
func InitDB() {
|
||||
dbDriver := config.Get().Database.Driver
|
||||
switch strings.ToLower(dbDriver) {
|
||||
case mgo.DBDriverName:
|
||||
InitMongodb()
|
||||
default:
|
||||
panic("InitDB error, unsupported database driver: " + dbDriver)
|
||||
}
|
||||
}
|
||||
|
||||
// GetDB get db
|
||||
func GetDB() *mongo.Database {
|
||||
if db == nil {
|
||||
once1.Do(func() {
|
||||
InitDB()
|
||||
})
|
||||
}
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
// CloseDB close db
|
||||
func CloseDB() error {
|
||||
return mgo.Close(db)
|
||||
}
|
||||
|
||||
// InitMongodb connect mongodb
|
||||
func InitMongodb() {
|
||||
var err error
|
||||
var dsn = utils.AdaptiveMongodbDsn(config.Get().Database.Mongodb.Dsn)
|
||||
db, err = mgo.Init(dsn)
|
||||
if err != nil {
|
||||
panic("mgo.Init error: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// ToObjectID convert string to ObjectID
|
||||
func ToObjectID(id string) primitive.ObjectID {
|
||||
oid, _ := primitive.ObjectIDFromHex(id)
|
||||
return oid
|
||||
}
|
|
@ -313,6 +313,7 @@ func convertUserExample(record *model.UserExample) (*serverNameExampleV1.UserExa
|
|||
return nil, err
|
||||
}
|
||||
value.Id = record.ID
|
||||
// todo if copier.Copy cannot assign a value to a field, add it here, e.g. CreatedAt, UpdatedAt
|
||||
// todo generate the conversion createdAt and updatedAt code here
|
||||
// delete the templates code start
|
||||
value.CreatedAt = record.CreatedAt.Unix()
|
||||
|
|
|
@ -0,0 +1,326 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
serverNameExampleV1 "github.com/zhufuyi/sponge/api/serverNameExample/v1"
|
||||
"github.com/zhufuyi/sponge/internal/cache"
|
||||
"github.com/zhufuyi/sponge/internal/dao"
|
||||
"github.com/zhufuyi/sponge/internal/ecode"
|
||||
"github.com/zhufuyi/sponge/internal/model"
|
||||
|
||||
"github.com/zhufuyi/sponge/pkg/grpc/interceptor"
|
||||
"github.com/zhufuyi/sponge/pkg/logger"
|
||||
"github.com/zhufuyi/sponge/pkg/mgo/query"
|
||||
|
||||
"github.com/jinzhu/copier"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registerFns = append(registerFns, func(server *grpc.Server) {
|
||||
serverNameExampleV1.RegisterUserExampleServer(server, NewUserExampleServer()) // register service to the rpc service
|
||||
})
|
||||
}
|
||||
|
||||
var _ serverNameExampleV1.UserExampleServer = (*userExample)(nil)
|
||||
|
||||
type userExample struct {
|
||||
serverNameExampleV1.UnimplementedUserExampleServer
|
||||
|
||||
iDao dao.UserExampleDao
|
||||
}
|
||||
|
||||
// NewUserExampleServer create a new service
|
||||
func NewUserExampleServer() serverNameExampleV1.UserExampleServer {
|
||||
collectionName := new(model.UserExample).TableName()
|
||||
return &userExample{
|
||||
iDao: dao.NewUserExampleDao(
|
||||
model.GetDB().Collection(collectionName),
|
||||
cache.NewUserExampleCache(model.GetCacheType()),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
// Create a record
|
||||
func (s *userExample) Create(ctx context.Context, req *serverNameExampleV1.CreateUserExampleRequest) (*serverNameExampleV1.CreateUserExampleReply, error) {
|
||||
err := req.Validate()
|
||||
if err != nil {
|
||||
logger.Warn("req.Validate error", logger.Err(err), logger.Any("req", req), interceptor.ServerCtxRequestIDField(ctx))
|
||||
return nil, ecode.StatusInvalidParams.Err()
|
||||
}
|
||||
ctx = interceptor.WrapServerCtx(ctx)
|
||||
|
||||
record := &model.UserExample{}
|
||||
err = copier.Copy(record, req)
|
||||
if err != nil {
|
||||
return nil, ecode.StatusCreateUserExample.Err()
|
||||
}
|
||||
// todo if copier.Copy cannot assign a value to a field, add it here
|
||||
|
||||
err = s.iDao.Create(ctx, record)
|
||||
if err != nil {
|
||||
logger.Error("Create error", logger.Err(err), logger.Any("userExample", record), interceptor.ServerCtxRequestIDField(ctx))
|
||||
return nil, ecode.StatusInternalServerError.ToRPCErr()
|
||||
}
|
||||
|
||||
return &serverNameExampleV1.CreateUserExampleReply{Id: record.ID.Hex()}, nil
|
||||
}
|
||||
|
||||
// DeleteByID delete a record by id
|
||||
func (s *userExample) DeleteByID(ctx context.Context, req *serverNameExampleV1.DeleteUserExampleByIDRequest) (*serverNameExampleV1.DeleteUserExampleByIDReply, error) {
|
||||
err := req.Validate()
|
||||
if err != nil {
|
||||
logger.Warn("req.Validate error", logger.Err(err), logger.Any("req", req), interceptor.ServerCtxRequestIDField(ctx))
|
||||
return nil, ecode.StatusInvalidParams.Err()
|
||||
}
|
||||
ctx = interceptor.WrapServerCtx(ctx)
|
||||
|
||||
err = s.iDao.DeleteByID(ctx, req.Id)
|
||||
if err != nil {
|
||||
logger.Error("DeleteByID error", logger.Err(err), logger.Any("id", req.Id), interceptor.ServerCtxRequestIDField(ctx))
|
||||
return nil, ecode.StatusInternalServerError.ToRPCErr()
|
||||
}
|
||||
|
||||
return &serverNameExampleV1.DeleteUserExampleByIDReply{}, nil
|
||||
}
|
||||
|
||||
// DeleteByIDs delete records by batch id
|
||||
func (s *userExample) DeleteByIDs(ctx context.Context, req *serverNameExampleV1.DeleteUserExampleByIDsRequest) (*serverNameExampleV1.DeleteUserExampleByIDsReply, error) {
|
||||
err := req.Validate()
|
||||
if err != nil {
|
||||
logger.Warn("req.Validate error", logger.Err(err), logger.Any("req", req), interceptor.ServerCtxRequestIDField(ctx))
|
||||
return nil, ecode.StatusInvalidParams.Err()
|
||||
}
|
||||
ctx = interceptor.WrapServerCtx(ctx)
|
||||
|
||||
err = s.iDao.DeleteByIDs(ctx, req.Ids)
|
||||
if err != nil {
|
||||
logger.Error("DeleteByID error", logger.Err(err), logger.Any("ids", req.Ids), interceptor.ServerCtxRequestIDField(ctx))
|
||||
return nil, ecode.StatusInternalServerError.ToRPCErr()
|
||||
}
|
||||
|
||||
return &serverNameExampleV1.DeleteUserExampleByIDsReply{}, nil
|
||||
}
|
||||
|
||||
// UpdateByID update a record by id
|
||||
func (s *userExample) UpdateByID(ctx context.Context, req *serverNameExampleV1.UpdateUserExampleByIDRequest) (*serverNameExampleV1.UpdateUserExampleByIDReply, error) {
|
||||
err := req.Validate()
|
||||
if err != nil {
|
||||
logger.Warn("req.Validate error", logger.Err(err), logger.Any("req", req), interceptor.ServerCtxRequestIDField(ctx))
|
||||
return nil, ecode.StatusInvalidParams.Err()
|
||||
}
|
||||
ctx = interceptor.WrapServerCtx(ctx)
|
||||
|
||||
record := &model.UserExample{}
|
||||
err = copier.Copy(record, req)
|
||||
if err != nil {
|
||||
return nil, ecode.StatusUpdateByIDUserExample.Err()
|
||||
}
|
||||
record.ID = model.ToObjectID(req.Id)
|
||||
// todo if copier.Copy cannot assign a value to a field, add it here
|
||||
|
||||
err = s.iDao.UpdateByID(ctx, record)
|
||||
if err != nil {
|
||||
logger.Error("UpdateByID error", logger.Err(err), logger.Any("userExample", record), interceptor.ServerCtxRequestIDField(ctx))
|
||||
return nil, ecode.StatusInternalServerError.ToRPCErr()
|
||||
}
|
||||
|
||||
return &serverNameExampleV1.UpdateUserExampleByIDReply{}, nil
|
||||
}
|
||||
|
||||
// GetByID get a record by id
|
||||
func (s *userExample) GetByID(ctx context.Context, req *serverNameExampleV1.GetUserExampleByIDRequest) (*serverNameExampleV1.GetUserExampleByIDReply, error) {
|
||||
err := req.Validate()
|
||||
if err != nil {
|
||||
logger.Warn("req.Validate error", logger.Err(err), logger.Any("req", req), interceptor.ServerCtxRequestIDField(ctx))
|
||||
return nil, ecode.StatusInvalidParams.Err()
|
||||
}
|
||||
ctx = interceptor.WrapServerCtx(ctx)
|
||||
|
||||
record, err := s.iDao.GetByID(ctx, req.Id)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrRecordNotFound) {
|
||||
logger.Warn("GetByID error", logger.Err(err), logger.Any("id", req.Id), interceptor.ServerCtxRequestIDField(ctx))
|
||||
return nil, ecode.StatusNotFound.Err()
|
||||
}
|
||||
logger.Error("GetByID error", logger.Err(err), logger.Any("id", req.Id), interceptor.ServerCtxRequestIDField(ctx))
|
||||
return nil, ecode.StatusInternalServerError.ToRPCErr()
|
||||
}
|
||||
|
||||
data, err := convertUserExample(record)
|
||||
if err != nil {
|
||||
logger.Warn("convertUserExample error", logger.Err(err), logger.Any("userExample", record), interceptor.ServerCtxRequestIDField(ctx))
|
||||
return nil, ecode.StatusGetByIDUserExample.Err()
|
||||
}
|
||||
|
||||
return &serverNameExampleV1.GetUserExampleByIDReply{UserExample: data}, nil
|
||||
}
|
||||
|
||||
// GetByCondition get a record by id
|
||||
func (s *userExample) GetByCondition(ctx context.Context, req *serverNameExampleV1.GetUserExampleByConditionRequest) (*serverNameExampleV1.GetUserExampleByConditionReply, error) {
|
||||
err := req.Validate()
|
||||
if err != nil {
|
||||
logger.Warn("req.Validate error", logger.Err(err), logger.Any("req", req), interceptor.ServerCtxRequestIDField(ctx))
|
||||
return nil, ecode.StatusInvalidParams.Err()
|
||||
}
|
||||
ctx = interceptor.WrapServerCtx(ctx)
|
||||
|
||||
conditions := &query.Conditions{}
|
||||
for _, v := range req.Conditions.GetColumns() {
|
||||
column := query.Column{}
|
||||
_ = copier.Copy(&column, v)
|
||||
conditions.Columns = append(conditions.Columns, column)
|
||||
}
|
||||
err = conditions.CheckValid()
|
||||
if err != nil {
|
||||
logger.Warn("Parameters error", logger.Err(err), logger.Any("conditions", conditions), interceptor.ServerCtxRequestIDField(ctx))
|
||||
return nil, ecode.StatusInvalidParams.Err()
|
||||
}
|
||||
|
||||
record, err := s.iDao.GetByCondition(ctx, conditions)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrRecordNotFound) {
|
||||
logger.Warn("GetByCondition error", logger.Err(err), logger.Any("req", req), interceptor.ServerCtxRequestIDField(ctx))
|
||||
return nil, ecode.StatusNotFound.Err()
|
||||
}
|
||||
logger.Error("GetByCondition error", logger.Err(err), logger.Any("req", req), interceptor.ServerCtxRequestIDField(ctx))
|
||||
return nil, ecode.StatusInternalServerError.ToRPCErr()
|
||||
}
|
||||
|
||||
data, err := convertUserExample(record)
|
||||
if err != nil {
|
||||
logger.Warn("convertUserExample error", logger.Err(err), logger.Any("userExample", record), interceptor.ServerCtxRequestIDField(ctx))
|
||||
return nil, ecode.StatusGetByConditionUserExample.Err()
|
||||
}
|
||||
|
||||
return &serverNameExampleV1.GetUserExampleByConditionReply{
|
||||
UserExample: data,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ListByIDs list of records by batch id
|
||||
func (s *userExample) ListByIDs(ctx context.Context, req *serverNameExampleV1.ListUserExampleByIDsRequest) (*serverNameExampleV1.ListUserExampleByIDsReply, error) {
|
||||
err := req.Validate()
|
||||
if err != nil {
|
||||
logger.Warn("req.Validate error", logger.Err(err), logger.Any("req", req), interceptor.ServerCtxRequestIDField(ctx))
|
||||
return nil, ecode.StatusInvalidParams.Err()
|
||||
}
|
||||
ctx = interceptor.WrapServerCtx(ctx)
|
||||
|
||||
userExampleMap, err := s.iDao.GetByIDs(ctx, req.Ids)
|
||||
if err != nil {
|
||||
logger.Error("GetByIDs error", logger.Err(err), logger.Any("ids", req.Ids), interceptor.ServerCtxRequestIDField(ctx))
|
||||
return nil, ecode.StatusInternalServerError.ToRPCErr()
|
||||
}
|
||||
|
||||
userExamples := []*serverNameExampleV1.UserExample{}
|
||||
for _, id := range req.Ids {
|
||||
if v, ok := userExampleMap[id]; ok {
|
||||
record, err := convertUserExample(v)
|
||||
if err != nil {
|
||||
logger.Warn("convertUserExample error", logger.Err(err), logger.Any("userExample", v), interceptor.ServerCtxRequestIDField(ctx))
|
||||
return nil, ecode.StatusInternalServerError.ToRPCErr()
|
||||
}
|
||||
userExamples = append(userExamples, record)
|
||||
}
|
||||
}
|
||||
|
||||
return &serverNameExampleV1.ListUserExampleByIDsReply{UserExamples: userExamples}, nil
|
||||
}
|
||||
|
||||
// ListByLastID list userExample by last id
|
||||
func (s *userExample) ListByLastID(ctx context.Context, req *serverNameExampleV1.ListUserExampleByLastIDRequest) (*serverNameExampleV1.ListUserExampleByLastIDReply, error) {
|
||||
err := req.Validate()
|
||||
if err != nil {
|
||||
logger.Warn("req.Validate error", logger.Err(err), logger.Any("req", req), interceptor.CtxRequestIDField(ctx))
|
||||
return nil, ecode.StatusInvalidParams.Err()
|
||||
}
|
||||
if req.LastID == "" {
|
||||
req.LastID = model.MaxObjectID
|
||||
}
|
||||
if req.Limit == 0 {
|
||||
req.Limit = 10
|
||||
}
|
||||
|
||||
records, err := s.iDao.GetByLastID(ctx, req.LastID, int(req.Limit), req.Sort)
|
||||
if err != nil {
|
||||
logger.Error("ListByLastID error", logger.Err(err), interceptor.CtxRequestIDField(ctx))
|
||||
return nil, ecode.StatusInternalServerError.ToRPCErr()
|
||||
}
|
||||
|
||||
userExamples := []*serverNameExampleV1.UserExample{}
|
||||
for _, record := range records {
|
||||
data, err := convertUserExample(record)
|
||||
if err != nil {
|
||||
logger.Warn("convertUserExample error", logger.Err(err), logger.Any("id", record.ID), interceptor.ServerCtxRequestIDField(ctx))
|
||||
continue
|
||||
}
|
||||
userExamples = append(userExamples, data)
|
||||
}
|
||||
|
||||
return &serverNameExampleV1.ListUserExampleByLastIDReply{
|
||||
UserExamples: userExamples,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// List of records by query parameters
|
||||
func (s *userExample) List(ctx context.Context, req *serverNameExampleV1.ListUserExampleRequest) (*serverNameExampleV1.ListUserExampleReply, error) {
|
||||
err := req.Validate()
|
||||
if err != nil {
|
||||
logger.Warn("req.Validate error", logger.Err(err), logger.Any("req", req), interceptor.ServerCtxRequestIDField(ctx))
|
||||
return nil, ecode.StatusInvalidParams.Err()
|
||||
}
|
||||
ctx = interceptor.WrapServerCtx(ctx)
|
||||
|
||||
params := &query.Params{}
|
||||
err = copier.Copy(params, req.Params)
|
||||
if err != nil {
|
||||
return nil, ecode.StatusListUserExample.Err()
|
||||
}
|
||||
params.Size = int(req.Params.Limit)
|
||||
// todo if copier.Copy cannot assign a value to a field, add it here
|
||||
|
||||
records, total, err := s.iDao.GetByColumns(ctx, params)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "query params error:") {
|
||||
logger.Warn("GetByColumns error", logger.Err(err), logger.Any("params", params), interceptor.ServerCtxRequestIDField(ctx))
|
||||
return nil, ecode.StatusInvalidParams.Err()
|
||||
}
|
||||
logger.Error("GetByColumns error", logger.Err(err), logger.Any("params", params), interceptor.ServerCtxRequestIDField(ctx))
|
||||
return nil, ecode.StatusInternalServerError.ToRPCErr()
|
||||
}
|
||||
|
||||
userExamples := []*serverNameExampleV1.UserExample{}
|
||||
for _, record := range records {
|
||||
data, err := convertUserExample(record)
|
||||
if err != nil {
|
||||
logger.Warn("convertUserExample error", logger.Err(err), logger.Any("id", record.ID), interceptor.ServerCtxRequestIDField(ctx))
|
||||
continue
|
||||
}
|
||||
userExamples = append(userExamples, data)
|
||||
}
|
||||
|
||||
return &serverNameExampleV1.ListUserExampleReply{
|
||||
Total: total,
|
||||
UserExamples: userExamples,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func convertUserExample(record *model.UserExample) (*serverNameExampleV1.UserExample, error) {
|
||||
value := &serverNameExampleV1.UserExample{}
|
||||
err := copier.Copy(value, record)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
value.Id = record.ID.Hex()
|
||||
// todo if copier.Copy cannot assign a value to a field, add it here, e.g. CreatedAt, UpdatedAt
|
||||
// todo generate the conversion createdAt and updatedAt code here
|
||||
// delete the templates code start
|
||||
value.CreatedAt = record.CreatedAt.Unix()
|
||||
value.UpdatedAt = record.UpdatedAt.Unix()
|
||||
// delete the templates code end
|
||||
return value, nil
|
||||
}
|
|
@ -0,0 +1,296 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/zhufuyi/sponge/configs"
|
||||
serverNameExampleV1 "github.com/zhufuyi/sponge/api/serverNameExample/v1"
|
||||
"github.com/zhufuyi/sponge/api/types"
|
||||
"github.com/zhufuyi/sponge/internal/config"
|
||||
|
||||
"github.com/zhufuyi/sponge/pkg/grpc/benchmark"
|
||||
)
|
||||
|
||||
// Test each method of userExample via the rpc client
|
||||
func Test_service_userExample_methods(t *testing.T) {
|
||||
conn := getRPCClientConnForTest()
|
||||
cli := serverNameExampleV1.NewUserExampleClient(conn)
|
||||
ctx, _ := context.WithTimeout(context.Background(), time.Second*3)
|
||||
//ctx = interceptor.SetJwtTokenToCtx(ctx, "Bearer jwt-token-value")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
fn func() (interface{}, error)
|
||||
wantErr bool
|
||||
}{
|
||||
// todo generate the service struct code here
|
||||
// delete the templates code start
|
||||
{
|
||||
name: "Create",
|
||||
fn: func() (interface{}, error) {
|
||||
// todo type in the parameters to test
|
||||
req := &serverNameExampleV1.CreateUserExampleRequest{
|
||||
Name: "foo9",
|
||||
Email: "foo9@bar.com",
|
||||
Password: "f447b20a7fcbf53a5d5be013ea0b15af",
|
||||
Phone: "16000000009",
|
||||
Avatar: "http://internal.com/9.jpg",
|
||||
Age: 19,
|
||||
Gender: 2,
|
||||
}
|
||||
return cli.Create(ctx, req)
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "UpdateByID",
|
||||
fn: func() (interface{}, error) {
|
||||
// todo type in the parameters to test
|
||||
req := &serverNameExampleV1.UpdateUserExampleByIDRequest{
|
||||
Id: "65cf5a5ad6abda94a8c24ed3",
|
||||
Phone: "16000000019",
|
||||
Age: 0,
|
||||
}
|
||||
return cli.UpdateByID(ctx, req)
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
// delete the templates code end
|
||||
{
|
||||
name: "DeleteByID",
|
||||
fn: func() (interface{}, error) {
|
||||
// todo type in the parameters to test
|
||||
req := &serverNameExampleV1.DeleteUserExampleByIDRequest{
|
||||
Id: "",
|
||||
}
|
||||
return cli.DeleteByID(ctx, req)
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "DeleteByIDs",
|
||||
fn: func() (interface{}, error) {
|
||||
// todo type in the parameters to test
|
||||
req := &serverNameExampleV1.DeleteUserExampleByIDsRequest{
|
||||
Ids: []string{""},
|
||||
}
|
||||
return cli.DeleteByIDs(ctx, req)
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "GetByID",
|
||||
fn: func() (interface{}, error) {
|
||||
// todo type in the parameters to test
|
||||
req := &serverNameExampleV1.GetUserExampleByIDRequest{
|
||||
Id: "",
|
||||
}
|
||||
return cli.GetByID(ctx, req)
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "GetByCondition",
|
||||
fn: func() (interface{}, error) {
|
||||
// todo type in the parameters to test
|
||||
req := &serverNameExampleV1.GetUserExampleByConditionRequest{
|
||||
Conditions: &types.Conditions{
|
||||
Columns: []*types.Column{
|
||||
{
|
||||
Name: "",
|
||||
Value: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return cli.GetByCondition(ctx, req)
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "ListByIDs",
|
||||
fn: func() (interface{}, error) {
|
||||
// todo type in the parameters to test
|
||||
req := &serverNameExampleV1.ListUserExampleByIDsRequest{
|
||||
Ids: []string{"65cf5b4cd6abda94a8c24ed5", "65c9ae1b1378ae7f0787a03a", "65c9ae1b1378ae7f0787a03b"},
|
||||
}
|
||||
return cli.ListByIDs(ctx, req)
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "ListByLastID",
|
||||
fn: func() (interface{}, error) {
|
||||
// todo type in the parameters to test
|
||||
req := &serverNameExampleV1.ListUserExampleByLastIDRequest{
|
||||
LastID: "",
|
||||
Limit: 10,
|
||||
Sort: "",
|
||||
}
|
||||
return cli.ListByLastID(ctx, req)
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "List",
|
||||
fn: func() (interface{}, error) {
|
||||
// todo type in the parameters to test
|
||||
req := &serverNameExampleV1.ListUserExampleRequest{
|
||||
Params: &types.Params{
|
||||
Page: 0,
|
||||
Limit: 10,
|
||||
Sort: "",
|
||||
//Columns: []*types.Column{
|
||||
// {
|
||||
// Name: "_id",
|
||||
// //Exp: ">=",
|
||||
// Value: "11",
|
||||
// Logic: "",
|
||||
// },
|
||||
//},
|
||||
},
|
||||
}
|
||||
return cli.List(ctx, req)
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := tt.fn()
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Logf("test '%s' error = %v, wantErr %v", tt.name, err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
data, _ := json.MarshalIndent(got, "", " ")
|
||||
fmt.Println(string(data))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Perform a stress test on {{.LowerName}}'s method and
|
||||
// copy the press test report to your browser when you are finished.
|
||||
func Test_service_userExample_benchmark(t *testing.T) {
|
||||
err := config.Init(configs.Path("serverNameExample.yml"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
host := fmt.Sprintf("127.0.0.1:%d", config.Get().Grpc.Port)
|
||||
protoFile := configs.Path("../api/serverNameExample/v1/userExample.proto")
|
||||
// If third-party dependencies are missing during the press test,
|
||||
// copy them to the project's third_party directory.
|
||||
importPaths := []string{
|
||||
configs.Path("../third_party"), // third_party directory
|
||||
configs.Path(".."), // Previous level of third_party
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
fn func() error
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "GetByID",
|
||||
fn: func() error {
|
||||
// todo type in the parameters to test
|
||||
message := &serverNameExampleV1.GetUserExampleByIDRequest{
|
||||
Id: "",
|
||||
}
|
||||
var total uint = 1000 // total number of requests
|
||||
b, err := benchmark.New(host, protoFile, "GetByID", message, total, importPaths...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return b.Run()
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "ListByIDs",
|
||||
fn: func() error {
|
||||
// todo type in the parameters to test
|
||||
message := &serverNameExampleV1.ListUserExampleByIDsRequest{
|
||||
Ids: []string{""},
|
||||
}
|
||||
var total uint = 1000 // total number of requests
|
||||
b, err := benchmark.New(host, protoFile, "ListByIDs", message, total, importPaths...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return b.Run()
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "ListByLastID",
|
||||
fn: func() error {
|
||||
// todo type in the parameters to test
|
||||
message := &serverNameExampleV1.ListUserExampleByLastIDRequest{
|
||||
LastID: "",
|
||||
Limit: 5,
|
||||
Sort: "-id",
|
||||
}
|
||||
var total uint = 100 // total number of requests
|
||||
b, err := benchmark.New(host, protoFile, "ListByLastID", message, total, importPaths...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return b.Run()
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "List",
|
||||
fn: func() error {
|
||||
// todo type in the parameters to test
|
||||
message := &serverNameExampleV1.ListUserExampleRequest{
|
||||
Params: &types.Params{
|
||||
Page: 0,
|
||||
Limit: 10,
|
||||
Sort: "",
|
||||
Columns: []*types.Column{
|
||||
{
|
||||
Name: "id",
|
||||
Exp: ">=",
|
||||
Value: "1",
|
||||
Logic: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
var total uint = 100 // total number of requests
|
||||
b, err := benchmark.New(host, protoFile, "List", message, total, importPaths...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return b.Run()
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := tt.fn()
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("test '%s' error = %v, wantErr %v", tt.name, err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
// Package types define the structure of request parameters and respond results in this package
|
||||
package types
|
||||
|
||||
// This file is public struct, only used to generate swagger documents, it is recommended
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
// Package types define the structure of request parameters and respond results in this package
|
||||
package types
|
||||
|
||||
import (
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/zhufuyi/sponge/internal/model"
|
||||
|
||||
"github.com/zhufuyi/sponge/pkg/mgo/query"
|
||||
)
|
||||
|
||||
var _ time.Time
|
||||
var _ = model.MaxObjectID
|
||||
|
||||
// Tip: suggested filling in the binding rules https://github.com/go-playground/validator in request struct fields tag.
|
||||
|
||||
// todo generate the request and response struct to here
|
||||
// delete the templates code start
|
||||
|
||||
// CreateUserExampleRequest request params
|
||||
type CreateUserExampleRequest struct {
|
||||
Name string `json:"name" binding:"min=2"` // username
|
||||
Email string `json:"email" binding:"email"` // email
|
||||
Password string `json:"password" binding:"md5"` // password
|
||||
Phone string `json:"phone" binding:"e164"` // phone number, e164 rules, e.g. +8612345678901
|
||||
Avatar string `json:"avatar" binding:"min=5"` // avatar
|
||||
Age int `json:"age" binding:"gt=0,lt=120"` // age
|
||||
Gender int `json:"gender" binding:"gte=0,lte=2"` // gender, 1:Male, 2:Female, other values:unknown
|
||||
}
|
||||
|
||||
// UpdateUserExampleByIDRequest request params
|
||||
type UpdateUserExampleByIDRequest struct {
|
||||
ID string `json:"id" binding:"-"` // id
|
||||
Name string `json:"name" binding:""` // username
|
||||
Email string `json:"email" binding:""` // email
|
||||
Password string `json:"password" binding:""` // password
|
||||
Phone string `json:"phone" binding:""` // phone number
|
||||
Avatar string `json:"avatar" binding:""` // avatar
|
||||
Age int `json:"age" binding:""` // age
|
||||
Gender int `json:"gender" binding:""` // gender, 1:Male, 2:Female, other values:unknown
|
||||
}
|
||||
|
||||
// UserExampleObjDetail detail
|
||||
type UserExampleObjDetail struct {
|
||||
ID string `json:"id"` // id
|
||||
Name string `json:"name"` // username
|
||||
Email string `json:"email"` // email
|
||||
Phone string `json:"phone"` // phone number
|
||||
Avatar string `json:"avatar"` // avatar
|
||||
Age int `json:"age"` // age
|
||||
Gender int `json:"gender"` // gender, 1:Male, 2:Female, other values:unknown
|
||||
Status int `json:"status"` // account status, 1:inactive, 2:activated, 3:blocked
|
||||
LoginAt int64 `json:"loginAt"` // login timestamp
|
||||
CreatedAt time.Time `json:"createdAt"` // create time
|
||||
UpdatedAt time.Time `json:"updatedAt"` // update time
|
||||
}
|
||||
|
||||
// delete the templates code end
|
||||
|
||||
// CreateUserExampleRespond only for api docs
|
||||
type CreateUserExampleRespond struct {
|
||||
Code int `json:"code"` // return code
|
||||
Msg string `json:"msg"` // return information description
|
||||
Data struct {
|
||||
ID uint64 `json:"id"` // id
|
||||
} `json:"data"` // return data
|
||||
}
|
||||
|
||||
// UpdateUserExampleByIDRespond only for api docs
|
||||
type UpdateUserExampleByIDRespond struct {
|
||||
Result
|
||||
}
|
||||
|
||||
// GetUserExampleByIDRespond only for api docs
|
||||
type GetUserExampleByIDRespond struct {
|
||||
Code int `json:"code"` // return code
|
||||
Msg string `json:"msg"` // return information description
|
||||
Data struct {
|
||||
UserExample UserExampleObjDetail `json:"userExample"`
|
||||
} `json:"data"` // return data
|
||||
}
|
||||
|
||||
// DeleteUserExampleByIDRespond only for api docs
|
||||
type DeleteUserExampleByIDRespond struct {
|
||||
Result
|
||||
}
|
||||
|
||||
// DeleteUserExamplesByIDsRequest request params
|
||||
type DeleteUserExamplesByIDsRequest struct {
|
||||
IDs []string `json:"ids" binding:"min=1"` // id list
|
||||
}
|
||||
|
||||
// DeleteUserExamplesByIDsRespond only for api docs
|
||||
type DeleteUserExamplesByIDsRespond struct {
|
||||
Result
|
||||
}
|
||||
|
||||
// GetUserExampleByConditionRequest request params
|
||||
type GetUserExampleByConditionRequest struct {
|
||||
query.Conditions
|
||||
}
|
||||
|
||||
// GetUserExampleByConditionRespond only for api docs
|
||||
type GetUserExampleByConditionRespond struct {
|
||||
Code int `json:"code"` // return code
|
||||
Msg string `json:"msg"` // return information description
|
||||
Data struct {
|
||||
UserExample UserExampleObjDetail `json:"userExample"`
|
||||
} `json:"data"` // return data
|
||||
}
|
||||
|
||||
// ListUserExamplesByIDsRequest request params
|
||||
type ListUserExamplesByIDsRequest struct {
|
||||
IDs []string `json:"ids" binding:"min=1"` // id list
|
||||
}
|
||||
|
||||
// ListUserExamplesByIDsRespond only for api docs
|
||||
type ListUserExamplesByIDsRespond struct {
|
||||
Code int `json:"code"` // return code
|
||||
Msg string `json:"msg"` // return information description
|
||||
Data struct {
|
||||
UserExamples []UserExampleObjDetail `json:"userExamples"`
|
||||
} `json:"data"` // return data
|
||||
}
|
||||
|
||||
// ListUserExamplesRequest request params
|
||||
type ListUserExamplesRequest struct {
|
||||
query.Params
|
||||
}
|
||||
|
||||
// ListUserExamplesRespond only for api docs
|
||||
type ListUserExamplesRespond struct {
|
||||
Code int `json:"code"` // return code
|
||||
Msg string `json:"msg"` // return information description
|
||||
Data struct {
|
||||
UserExamples []UserExampleObjDetail `json:"userExamples"`
|
||||
} `json:"data"` // return data
|
||||
}
|
|
@ -169,7 +169,7 @@ func checkInUse(sqlDB *sql.DB, duration time.Duration) {
|
|||
ctx, _ := context.WithTimeout(context.Background(), duration) //nolint
|
||||
for {
|
||||
select {
|
||||
case <-time.After(time.Millisecond * 100):
|
||||
case <-time.After(time.Millisecond * 250):
|
||||
if v := sqlDB.Stats().InUse; v == 0 {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -71,10 +71,10 @@ type Params struct {
|
|||
|
||||
// Column query info
|
||||
type Column struct {
|
||||
Name string `json:"name" form:"columns"` // column name
|
||||
Exp string `json:"exp" form:"columns"` // expressions, which default to = when the value is null, have =, !=, >, >=, <, <=, like, in
|
||||
Value interface{} `json:"value" form:"columns"` // column value
|
||||
Logic string `json:"logic" form:"columns"` // logical type, defaults to and when the value is null, with &(and), ||(or)
|
||||
Name string `json:"name" form:"name"` // column name
|
||||
Exp string `json:"exp" form:"exp"` // expressions, which default to = when the value is null, have =, !=, >, >=, <, <=, like, in
|
||||
Value interface{} `json:"value" form:"value"` // column value
|
||||
Logic string `json:"logic" form:"logic"` // logical type, defaults to and when the value is null, with &(and), ||(or)
|
||||
}
|
||||
|
||||
func (c *Column) checkValid() error {
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,19 @@
|
|||
## mgo
|
||||
|
||||
Mgo is based on the official library [mongo](https://github.com/mongodb/mongo-go-driver).
|
||||
|
||||
<br>
|
||||
|
||||
### Example of use
|
||||
|
||||
```go
|
||||
import "github.com/zhufuyi/sponge/pkg/mgo"
|
||||
|
||||
var (
|
||||
dsn = "mongodb://root:123456@192.168.3.37:27017"
|
||||
dbName = "account"
|
||||
)
|
||||
db, err := mgo.Init(dsn, dbName)
|
||||
|
||||
defer Close(db)
|
||||
```
|
|
@ -0,0 +1,80 @@
|
|||
package mgo
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// Model embedded structs, add `bson: ",inline"` when defining table structs
|
||||
type Model struct {
|
||||
ID primitive.ObjectID `bson:"_id" json:"id"`
|
||||
CreatedAt time.Time `bson:"created_at" json:"createdAt"`
|
||||
UpdatedAt time.Time `bson:"updated_at" json:"updatedAt"`
|
||||
DeletedAt *time.Time `bson:"deleted_at,omitempty" json:"deletedAt,omitempty"`
|
||||
}
|
||||
|
||||
// SetModelValue set model fields
|
||||
func (p *Model) SetModelValue() {
|
||||
now := time.Now()
|
||||
if !p.ID.IsZero() {
|
||||
p.ID = primitive.NewObjectID()
|
||||
}
|
||||
|
||||
if p.CreatedAt.IsZero() {
|
||||
p.CreatedAt = now
|
||||
p.UpdatedAt = now
|
||||
}
|
||||
}
|
||||
|
||||
// ExcludeDeleted exclude soft deleted records
|
||||
func ExcludeDeleted(filter bson.M) bson.M {
|
||||
if filter == nil {
|
||||
filter = bson.M{}
|
||||
}
|
||||
filter["deleted_at"] = bson.M{"$exists": false}
|
||||
return filter
|
||||
}
|
||||
|
||||
// EmbedUpdatedAt embed updated_at datetime column
|
||||
func EmbedUpdatedAt(update bson.M) bson.M {
|
||||
updateM := bson.M{}
|
||||
if v, ok := update["$set"]; ok {
|
||||
if m, ok2 := v.(bson.M); ok2 {
|
||||
m["updated_at"] = time.Now()
|
||||
updateM["$set"] = m
|
||||
}
|
||||
} else {
|
||||
update["updated_at"] = time.Now()
|
||||
updateM["$set"] = update
|
||||
}
|
||||
return updateM
|
||||
}
|
||||
|
||||
// EmbedDeletedAt embed deleted_at datetime column
|
||||
func EmbedDeletedAt(update bson.M) bson.M {
|
||||
updateM := bson.M{}
|
||||
if v, ok := update["$set"]; ok {
|
||||
if m, ok2 := v.(bson.M); ok2 {
|
||||
m["deleted_at"] = time.Now()
|
||||
updateM["$set"] = m
|
||||
}
|
||||
} else {
|
||||
updateM["$set"] = bson.M{"deleted_at": time.Now()}
|
||||
}
|
||||
return updateM
|
||||
}
|
||||
|
||||
// ConvertToObjectIDs convert ids to objectIDs
|
||||
func ConvertToObjectIDs(ids []string) []primitive.ObjectID {
|
||||
oids := []primitive.ObjectID{}
|
||||
for _, id := range ids {
|
||||
oid, err := primitive.ObjectIDFromHex(id)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
oids = append(oids, oid)
|
||||
}
|
||||
return oids
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
// Package mgo is a library wrapped on go.mongodb.org/mongo-driver/mongo, with added features paging queries, etc.
|
||||
package mgo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
const (
|
||||
// DBDriverName mongodb driver
|
||||
DBDriverName = "mongodb"
|
||||
)
|
||||
|
||||
// Init connecting to mongo
|
||||
func Init(dsn string, opts ...*options.ClientOptions) (*mongo.Database, error) {
|
||||
u, err := url.Parse(dsn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbName := strings.TrimLeft(u.Path, "/")
|
||||
if dbName == "" {
|
||||
return nil, errors.New("database name is empty")
|
||||
}
|
||||
|
||||
var uri string
|
||||
if u.RawQuery == "" {
|
||||
uri = strings.TrimRight(dsn, u.Path)
|
||||
} else {
|
||||
tmp := strings.TrimRight(dsn, u.RawQuery)
|
||||
uri = strings.TrimRight(tmp, dbName+"?") + "?" + u.RawQuery
|
||||
}
|
||||
|
||||
return Init2(uri, dbName, opts...)
|
||||
}
|
||||
|
||||
// Init2 connecting to mongo using uri
|
||||
func Init2(uri string, dbName string, opts ...*options.ClientOptions) (*mongo.Database, error) {
|
||||
ctx := context.Background()
|
||||
mongoOpts := []*options.ClientOptions{
|
||||
options.Client().ApplyURI(uri),
|
||||
}
|
||||
mongoOpts = append(mongoOpts, opts...)
|
||||
client, err := mongo.Connect(ctx, mongoOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = client.Ping(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
db := client.Database(dbName)
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// Close mongodb
|
||||
func Close(db *mongo.Database) error {
|
||||
return db.Client().Disconnect(context.Background())
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
package mgo
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
mgoOptions "go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
func TestInit(t *testing.T) {
|
||||
dsns := []string{
|
||||
"mongodb://root:123456@192.168.3.37:27017/account",
|
||||
"mongodb://root:123456@192.168.3.37:27017/account?connectTimeoutMS=2000",
|
||||
}
|
||||
for _, dsn := range dsns {
|
||||
timeout := time.Second * 2
|
||||
opts := &mgoOptions.ClientOptions{Timeout: &timeout}
|
||||
db, err := Init(dsn, opts)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
return
|
||||
}
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
defer Close(db)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInit2(t *testing.T) {
|
||||
uri := "mongodb://root:123456@192.168.3.37:27017"
|
||||
dbName := "account"
|
||||
timeout := time.Second * 3
|
||||
opts := &mgoOptions.ClientOptions{Timeout: &timeout}
|
||||
db, err := Init2(uri, dbName, opts)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
return
|
||||
}
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
defer Close(db)
|
||||
}
|
||||
|
||||
func TestModel_SetModelValue(t *testing.T) {
|
||||
m := new(Model)
|
||||
m.SetModelValue()
|
||||
|
||||
assert.NotNil(t, m.ID)
|
||||
assert.NotNil(t, m.CreatedAt)
|
||||
assert.NotNil(t, m.UpdatedAt)
|
||||
}
|
||||
|
||||
func TestExcludeDeleted(t *testing.T) {
|
||||
filter := bson.M{"foo": "bar"}
|
||||
filter = ExcludeDeleted(filter)
|
||||
assert.NotNil(t, filter["deleted_at"])
|
||||
|
||||
filter = ExcludeDeleted(nil)
|
||||
assert.NotNil(t, filter["deleted_at"])
|
||||
}
|
||||
|
||||
func TestEmbedUpdatedAt(t *testing.T) {
|
||||
update := bson.M{"$set": bson.M{"foo": "bar"}}
|
||||
update = EmbedUpdatedAt(update)
|
||||
m := update["$set"].(bson.M)
|
||||
assert.NotNil(t, m["updated_at"])
|
||||
|
||||
update = bson.M{"foo": "bar"}
|
||||
update = EmbedUpdatedAt(update)
|
||||
m = update["$set"].(bson.M)
|
||||
assert.NotNil(t, m["updated_at"])
|
||||
}
|
||||
|
||||
func TestEmbedDeletedAt(t *testing.T) {
|
||||
update := bson.M{"$set": bson.M{"foo": "bar"}}
|
||||
update = EmbedDeletedAt(update)
|
||||
m := update["$set"].(bson.M)
|
||||
assert.NotNil(t, m["deleted_at"])
|
||||
|
||||
update = bson.M{"foo": "bar"}
|
||||
update = EmbedDeletedAt(update)
|
||||
m = update["$set"].(bson.M)
|
||||
assert.NotNil(t, m["deleted_at"])
|
||||
}
|
||||
|
||||
func TestConvertToObjectIDs(t *testing.T) {
|
||||
ids := []string{"65c9ae1b1378ae7f0787a039", "invalid_id"}
|
||||
oids := ConvertToObjectIDs(ids)
|
||||
assert.Equal(t, len(oids), 1)
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
package query
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
var defaultMaxSize = 1000
|
||||
|
||||
const oidName = "_id"
|
||||
|
||||
// SetMaxSize change the default maximum number of pages per page
|
||||
func SetMaxSize(max int) {
|
||||
if max < 10 {
|
||||
max = 10
|
||||
}
|
||||
defaultMaxSize = max
|
||||
}
|
||||
|
||||
// Page info
|
||||
type Page struct {
|
||||
page int // page number, starting from page 0
|
||||
size int // number per page
|
||||
sort bson.D // sort fields, default is id backwards, you can add - sign before the field to indicate reverse order, no - sign to indicate ascending order, multiple fields separated by comma
|
||||
}
|
||||
|
||||
// Page get page value
|
||||
func (p *Page) Page() int {
|
||||
return p.page
|
||||
}
|
||||
|
||||
// Size number per page
|
||||
func (p *Page) Size() int {
|
||||
return p.size
|
||||
}
|
||||
|
||||
// Sort get sort field
|
||||
func (p *Page) Sort() bson.D {
|
||||
return p.sort
|
||||
}
|
||||
|
||||
// Skip get offset value
|
||||
func (p *Page) Skip() int {
|
||||
return p.page * p.size
|
||||
}
|
||||
|
||||
// DefaultPage default page, number 20 per page, sorted by id backwards
|
||||
func DefaultPage(page int) *Page {
|
||||
if page < 0 {
|
||||
page = 0
|
||||
}
|
||||
return &Page{
|
||||
page: page,
|
||||
size: 10,
|
||||
sort: bson.D{{oidName, -1}}, //nolint
|
||||
}
|
||||
}
|
||||
|
||||
// NewPage custom page, starting from page 0.
|
||||
// the parameter columnNames indicates a sort field, if empty means id descending, if there are multiple column names, separated by a comma,
|
||||
// a '-' sign in front of each column name indicates descending order, otherwise ascending order.
|
||||
func NewPage(page int, size int, columnNames string) *Page {
|
||||
if page < 0 {
|
||||
page = 0
|
||||
}
|
||||
if size > defaultMaxSize {
|
||||
size = defaultMaxSize
|
||||
} else if size < 1 {
|
||||
size = 10
|
||||
}
|
||||
|
||||
return &Page{
|
||||
page: page,
|
||||
size: size,
|
||||
sort: getSort(columnNames),
|
||||
}
|
||||
}
|
||||
|
||||
// convert to mysql sort, each column name preceded by a '-' sign, indicating descending order, otherwise ascending order, example:
|
||||
//
|
||||
// columnNames="name" means sort by name in ascending order,
|
||||
// columnNames="-name" means sort by name descending,
|
||||
// columnNames="name,age" means sort by name in ascending order, otherwise sort by age in ascending order,
|
||||
// columnNames="-name,-age" means sort by name descending before sorting by age descending.
|
||||
func getSort(columnNames string) bson.D {
|
||||
d := bson.D{}
|
||||
columnNames = strings.Replace(columnNames, " ", "", -1)
|
||||
if columnNames == "" {
|
||||
d = bson.D{{oidName, -1}} //nolint
|
||||
return d
|
||||
}
|
||||
|
||||
names := strings.Split(columnNames, ",")
|
||||
for _, name := range names {
|
||||
if name[0] == '-' && len(name) > 1 {
|
||||
col := name[1:]
|
||||
if col == "id" {
|
||||
col = oidName
|
||||
}
|
||||
d = append(d, bson.E{col, -1}) //nolint
|
||||
} else {
|
||||
if name == "id" {
|
||||
name = oidName
|
||||
}
|
||||
d = append(d, bson.E{name, 1}) //nolint
|
||||
}
|
||||
}
|
||||
|
||||
return d
|
||||
}
|
|
@ -0,0 +1,372 @@
|
|||
// Package query is a library of custom condition queries, support for complex conditional paging queries.
|
||||
package query
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
const (
|
||||
// Eq equal
|
||||
Eq = "eq"
|
||||
eqSymbol = "="
|
||||
// Neq not equal
|
||||
Neq = "neq"
|
||||
neqSymbol = "!="
|
||||
// Gt greater than
|
||||
Gt = "gt"
|
||||
gtSymbol = ">"
|
||||
// Gte greater than or equal
|
||||
Gte = "gte"
|
||||
gteSymbol = ">="
|
||||
// Lt less than
|
||||
Lt = "lt"
|
||||
ltSymbol = "<"
|
||||
// Lte less than or equal
|
||||
Lte = "lte"
|
||||
lteSymbol = "<="
|
||||
// Like fuzzy lookup
|
||||
Like = "like"
|
||||
// In include
|
||||
In = "in"
|
||||
|
||||
// AND logic and
|
||||
AND string = "and" //nolint
|
||||
andSymbol1 = "&"
|
||||
andSymbol2 = "&&"
|
||||
// OR logic or
|
||||
OR string = "or" //nolint
|
||||
orSymbol1 = "|"
|
||||
orSymbol2 = "||"
|
||||
|
||||
allLogicAnd = 1
|
||||
allLogicOr = 2
|
||||
)
|
||||
|
||||
var expMap = map[string]string{
|
||||
Eq: eqSymbol,
|
||||
eqSymbol: eqSymbol,
|
||||
Neq: neqSymbol,
|
||||
neqSymbol: neqSymbol,
|
||||
Gt: gtSymbol,
|
||||
gtSymbol: gtSymbol,
|
||||
Gte: gteSymbol,
|
||||
gteSymbol: gteSymbol,
|
||||
Lt: ltSymbol,
|
||||
ltSymbol: ltSymbol,
|
||||
Lte: lteSymbol,
|
||||
lteSymbol: lteSymbol,
|
||||
Like: Like,
|
||||
In: In,
|
||||
}
|
||||
|
||||
var logicMap = map[string]string{
|
||||
AND: andSymbol1,
|
||||
andSymbol1: andSymbol1,
|
||||
andSymbol2: andSymbol1,
|
||||
OR: orSymbol1,
|
||||
orSymbol1: orSymbol1,
|
||||
orSymbol2: orSymbol1,
|
||||
}
|
||||
|
||||
// Params query parameters
|
||||
type Params struct {
|
||||
Page int `json:"page" form:"page" binding:"gte=0"`
|
||||
Size int `json:"size" form:"size" binding:"gt=0"`
|
||||
Sort string `json:"sort,omitempty" form:"sort" binding:""`
|
||||
|
||||
Columns []Column `json:"columns,omitempty" form:"columns"` // not required
|
||||
}
|
||||
|
||||
// Column query info
|
||||
type Column struct {
|
||||
Name string `json:"name" form:"name"` // column name
|
||||
Exp string `json:"exp" form:"exp"` // expressions, which default to = when the value is null, have =, !=, >, >=, <, <=, like, in
|
||||
Value interface{} `json:"value" form:"value"` // column value
|
||||
Logic string `json:"logic" form:"logic"` // logical type, defaults to and when the value is null, with &(and), ||(or)
|
||||
}
|
||||
|
||||
func (c *Column) checkValid() error {
|
||||
if c.Name == "" {
|
||||
return fmt.Errorf("field 'name' cannot be empty")
|
||||
}
|
||||
if c.Value == nil {
|
||||
return fmt.Errorf("field 'value' cannot be nil")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Column) convertLogic() error {
|
||||
if c.Logic == "" {
|
||||
c.Logic = AND
|
||||
}
|
||||
if v, ok := logicMap[strings.ToLower(c.Logic)]; ok { //nolint
|
||||
c.Logic = v
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("unknown logic type '%s'", c.Logic)
|
||||
}
|
||||
|
||||
// converting ExpType to sql expressions and LogicType to sql using characters
|
||||
func (c *Column) convert() error {
|
||||
if err := c.checkValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.Name == "id" || c.Name == "_id" {
|
||||
if str, ok := c.Value.(string); ok {
|
||||
c.Name = "_id"
|
||||
c.Value, _ = primitive.ObjectIDFromHex(str)
|
||||
}
|
||||
} else if strings.Contains(c.Name, ":oid") {
|
||||
if str, ok := c.Value.(string); ok {
|
||||
c.Name = strings.Replace(c.Name, ":oid", "", 1)
|
||||
c.Value, _ = primitive.ObjectIDFromHex(str)
|
||||
}
|
||||
}
|
||||
|
||||
if c.Exp == "" {
|
||||
c.Exp = Eq
|
||||
}
|
||||
if v, ok := expMap[strings.ToLower(c.Exp)]; ok { //nolint
|
||||
c.Exp = v
|
||||
switch c.Exp {
|
||||
//case eqSymbol:
|
||||
case neqSymbol:
|
||||
c.Value = bson.M{"$neq": c.Value}
|
||||
case gtSymbol:
|
||||
c.Value = bson.M{"$gt": c.Value}
|
||||
case gteSymbol:
|
||||
c.Value = bson.M{"$gte": c.Value}
|
||||
case ltSymbol:
|
||||
c.Value = bson.M{"$lt": c.Value}
|
||||
case lteSymbol:
|
||||
c.Value = bson.M{"$lte": c.Value}
|
||||
case Like:
|
||||
c.Value = bson.M{"$regex": fmt.Sprintf("%v", c.Value)}
|
||||
case In:
|
||||
val, ok := c.Value.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid value type '%s'", c.Value)
|
||||
}
|
||||
values := []interface{}{}
|
||||
ss := strings.Split(val, ",")
|
||||
for _, s := range ss {
|
||||
values = append(values, s)
|
||||
}
|
||||
c.Value = bson.M{"$in": values}
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("unknown exp type '%s'", c.Exp)
|
||||
}
|
||||
|
||||
return c.convertLogic()
|
||||
}
|
||||
|
||||
// ConvertToPage converted to conform to mongo rules based on the page size sort parameter
|
||||
func (p *Params) ConvertToPage() (sort bson.D, limit int, skip int) { //nolint
|
||||
page := NewPage(p.Page, p.Size, p.Sort)
|
||||
sort = page.sort
|
||||
limit = page.size
|
||||
skip = page.page * page.size
|
||||
return //nolint
|
||||
}
|
||||
|
||||
// ConvertToMongoFilter conversion to mongo-compliant parameters based on the Columns parameter
|
||||
// ignore the logical type of the last column, whether it is a one-column or multi-column query
|
||||
func (p *Params) ConvertToMongoFilter() (bson.M, error) {
|
||||
filter := bson.M{}
|
||||
l := len(p.Columns)
|
||||
switch l {
|
||||
case 0:
|
||||
return bson.M{}, nil
|
||||
|
||||
case 1: // l == 1
|
||||
err := p.Columns[0].convert()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
filter[p.Columns[0].Name] = p.Columns[0].Value
|
||||
return filter, nil
|
||||
|
||||
case 2: // l == 2
|
||||
err := p.Columns[0].convert()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = p.Columns[1].convert()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if p.Columns[0].Logic == andSymbol1 {
|
||||
filter = bson.M{"$and": []bson.M{
|
||||
{p.Columns[0].Name: p.Columns[0].Value},
|
||||
{p.Columns[1].Name: p.Columns[1].Value}}}
|
||||
} else {
|
||||
filter = bson.M{"$or": []bson.M{
|
||||
{p.Columns[0].Name: p.Columns[0].Value},
|
||||
{p.Columns[1].Name: p.Columns[1].Value}}}
|
||||
}
|
||||
return filter, nil
|
||||
|
||||
default: // l >=3
|
||||
return p.convertMultiColumns()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Params) convertMultiColumns() (bson.M, error) {
|
||||
filter := bson.M{}
|
||||
logicType, groupIndexes, err := checkSameLogic(p.Columns)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if logicType == allLogicAnd {
|
||||
for _, column := range p.Columns {
|
||||
err := column.convert()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if v, ok := filter["$and"]; !ok {
|
||||
filter["$and"] = []bson.M{{column.Name: column.Value}}
|
||||
} else {
|
||||
if cols, ok1 := v.([]bson.M); ok1 {
|
||||
cols = append(cols, bson.M{column.Name: column.Value})
|
||||
filter["$and"] = cols
|
||||
}
|
||||
}
|
||||
}
|
||||
return filter, nil
|
||||
} else if logicType == allLogicOr {
|
||||
for _, column := range p.Columns {
|
||||
err := column.convert()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if v, ok := filter["$or"]; !ok {
|
||||
filter["$or"] = []bson.M{{column.Name: column.Value}}
|
||||
} else {
|
||||
if cols, ok1 := v.([]bson.M); ok1 {
|
||||
cols = append(cols, bson.M{column.Name: column.Value})
|
||||
filter["$or"] = cols
|
||||
}
|
||||
}
|
||||
}
|
||||
return filter, nil
|
||||
}
|
||||
orConditions := []bson.M{}
|
||||
for _, indexes := range groupIndexes {
|
||||
if len(indexes) == 1 {
|
||||
column := p.Columns[indexes[0]]
|
||||
err := column.convert()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
orConditions = append(orConditions, bson.M{column.Name: column.Value})
|
||||
} else {
|
||||
andConditions := []bson.M{}
|
||||
for _, index := range indexes {
|
||||
column := p.Columns[index]
|
||||
err := column.convert()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
andConditions = append(andConditions, bson.M{column.Name: column.Value})
|
||||
}
|
||||
orConditions = append(orConditions, bson.M{"$and": andConditions})
|
||||
}
|
||||
}
|
||||
filter["$or"] = orConditions
|
||||
|
||||
return filter, nil
|
||||
}
|
||||
|
||||
func checkSameLogic(columns []Column) (int, [][]int, error) {
|
||||
orIndexes := []int{}
|
||||
l := len(columns)
|
||||
for i, column := range columns {
|
||||
if i == l-1 { // ignore the logical type of the last column
|
||||
break
|
||||
}
|
||||
err := column.convertLogic()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
if column.Logic == orSymbol1 {
|
||||
orIndexes = append(orIndexes, i)
|
||||
}
|
||||
}
|
||||
|
||||
if len(orIndexes) == 0 {
|
||||
return allLogicAnd, nil, nil
|
||||
} else if len(orIndexes) == l-1 {
|
||||
return allLogicOr, nil, nil
|
||||
}
|
||||
// mix and or
|
||||
groupIndexes := groupingIndex(l, orIndexes)
|
||||
|
||||
return 0, groupIndexes, nil
|
||||
}
|
||||
|
||||
func groupingIndex(l int, orIndexes []int) [][]int {
|
||||
groupIndexes := [][]int{}
|
||||
lastIndex := 0
|
||||
for _, index := range orIndexes {
|
||||
group := []int{}
|
||||
for i := lastIndex; i <= index; i++ {
|
||||
group = append(group, i)
|
||||
}
|
||||
groupIndexes = append(groupIndexes, group)
|
||||
if lastIndex == index {
|
||||
lastIndex++
|
||||
} else {
|
||||
lastIndex = index
|
||||
}
|
||||
}
|
||||
group := []int{}
|
||||
for i := lastIndex + 1; i < l; i++ {
|
||||
group = append(group, i)
|
||||
}
|
||||
groupIndexes = append(groupIndexes, group)
|
||||
return groupIndexes
|
||||
}
|
||||
|
||||
// Conditions query conditions
|
||||
type Conditions struct {
|
||||
Columns []Column `json:"columns" form:"columns" binding:"min=1"` // columns info
|
||||
}
|
||||
|
||||
// CheckValid check valid
|
||||
func (c *Conditions) CheckValid() error {
|
||||
if len(c.Columns) == 0 {
|
||||
return fmt.Errorf("field 'columns' cannot be empty")
|
||||
}
|
||||
|
||||
for _, column := range c.Columns {
|
||||
err := column.checkValid()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if column.Exp != "" {
|
||||
if _, ok := expMap[column.Exp]; !ok {
|
||||
return fmt.Errorf("unknown exp type '%s'", column.Exp)
|
||||
}
|
||||
}
|
||||
if column.Logic != "" {
|
||||
if _, ok := logicMap[column.Logic]; !ok {
|
||||
return fmt.Errorf("unknown logic type '%s'", column.Logic)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConvertToMongo conversion to mongo-compliant parameters based on the Columns parameter
|
||||
// ignore the logical type of the last column, whether it is a one-column or multi-column query
|
||||
func (c *Conditions) ConvertToMongo() (bson.M, error) {
|
||||
p := &Params{Columns: c.Columns}
|
||||
return p.ConvertToMongoFilter()
|
||||
}
|
|
@ -0,0 +1,669 @@
|
|||
package query
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
func TestPage(t *testing.T) {
|
||||
page := DefaultPage(-1)
|
||||
t.Log(page.Page(), page.Size(), page.Sort(), page.Skip())
|
||||
page = NewPage(0, 20, "")
|
||||
t.Log(page.Page(), page.Size(), page.Sort(), page.Skip())
|
||||
|
||||
SetMaxSize(1)
|
||||
page = NewPage(0, 20, "_id")
|
||||
t.Log(page.Page(), page.Size(), page.Sort(), page.Skip())
|
||||
}
|
||||
|
||||
func TestParams_ConvertToPage(t *testing.T) {
|
||||
p := &Params{
|
||||
Page: 0,
|
||||
Size: 20,
|
||||
Sort: "age,-name",
|
||||
}
|
||||
order, limit, offset := p.ConvertToPage()
|
||||
t.Logf("order=%v, limit=%d, skip=%d", order, limit, offset)
|
||||
}
|
||||
|
||||
func TestParams_ConvertToMongoFilter(t *testing.T) {
|
||||
type args struct {
|
||||
columns []Column
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bson.M
|
||||
wantErr bool
|
||||
}{
|
||||
// --------------------------- only 1 column query ------------------------------
|
||||
{
|
||||
name: "1 column eq",
|
||||
args: args{
|
||||
columns: []Column{
|
||||
{
|
||||
Name: "name",
|
||||
Value: "ZhangSan",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: bson.M{"name": "ZhangSan"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "1 column neq",
|
||||
args: args{
|
||||
columns: []Column{
|
||||
{
|
||||
Name: "name",
|
||||
Exp: "!=",
|
||||
Value: "ZhangSan",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: bson.M{"name": bson.M{"$neq": "ZhangSan"}},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "1 column gt",
|
||||
args: args{
|
||||
columns: []Column{
|
||||
{
|
||||
Name: "age",
|
||||
Exp: ">",
|
||||
Value: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: bson.M{"age": bson.M{"$gt": 20}},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "1 column gte",
|
||||
args: args{
|
||||
columns: []Column{
|
||||
{
|
||||
Name: "age",
|
||||
Exp: ">=",
|
||||
Value: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: bson.M{"age": bson.M{"$gte": 20}},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "1 column lt",
|
||||
args: args{
|
||||
columns: []Column{
|
||||
{
|
||||
Name: "age",
|
||||
Exp: "<",
|
||||
Value: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: bson.M{"age": bson.M{"$lt": 20}},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "1 column lte",
|
||||
args: args{
|
||||
columns: []Column{
|
||||
{
|
||||
Name: "age",
|
||||
Exp: "<=",
|
||||
Value: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: bson.M{"age": bson.M{"$lte": 20}},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "1 column like",
|
||||
args: args{
|
||||
columns: []Column{
|
||||
{
|
||||
Name: "name",
|
||||
Exp: Like,
|
||||
Value: "Li",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: bson.M{"name": bson.M{"$regex": "Li"}},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "1 column IN",
|
||||
args: args{
|
||||
columns: []Column{
|
||||
{
|
||||
Name: "name",
|
||||
Exp: In,
|
||||
Value: "ab,cd,ef",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: bson.M{"name": bson.M{"$in": []interface{}{"ab", "cd", "ef"}}},
|
||||
wantErr: false,
|
||||
},
|
||||
|
||||
// --------------------------- query 2 columns ------------------------------
|
||||
{
|
||||
name: "2 columns eq and",
|
||||
args: args{
|
||||
columns: []Column{
|
||||
{
|
||||
Name: "name",
|
||||
Value: "ZhangSan",
|
||||
},
|
||||
{
|
||||
Name: "gender",
|
||||
Value: "male",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: bson.M{"$and": []bson.M{{"name": "ZhangSan"}, {"gender": "male"}}},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "2 columns neq and",
|
||||
args: args{
|
||||
columns: []Column{
|
||||
{
|
||||
Name: "name",
|
||||
Exp: "!=",
|
||||
Value: "ZhangSan",
|
||||
},
|
||||
{
|
||||
Name: "name",
|
||||
Exp: "!=",
|
||||
Value: "LiSi",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: bson.M{"$and": []bson.M{{"name": bson.M{"$neq": "ZhangSan"}}, {"name": bson.M{"$neq": "LiSi"}}}},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "2 columns gt and",
|
||||
args: args{
|
||||
columns: []Column{
|
||||
{
|
||||
Name: "gender",
|
||||
Value: "male",
|
||||
},
|
||||
{
|
||||
Name: "age",
|
||||
Exp: ">",
|
||||
Value: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: bson.M{"$and": []bson.M{{"gender": "male"}, {"age": bson.M{"$gt": 20}}}},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "2 columns gte and",
|
||||
args: args{
|
||||
columns: []Column{
|
||||
{
|
||||
Name: "gender",
|
||||
Value: "male",
|
||||
},
|
||||
{
|
||||
Name: "age",
|
||||
Exp: ">=",
|
||||
Value: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: bson.M{"$and": []bson.M{{"gender": "male"}, {"age": bson.M{"$gte": 20}}}},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "2 columns lt and",
|
||||
args: args{
|
||||
columns: []Column{
|
||||
{
|
||||
Name: "gender",
|
||||
Value: "female",
|
||||
},
|
||||
{
|
||||
Name: "age",
|
||||
Exp: "<",
|
||||
Value: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: bson.M{"$and": []bson.M{{"gender": "female"}, {"age": bson.M{"$lt": 20}}}},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "2 columns lte and",
|
||||
args: args{
|
||||
columns: []Column{
|
||||
{
|
||||
Name: "gender",
|
||||
Value: "female",
|
||||
},
|
||||
{
|
||||
Name: "age",
|
||||
Exp: "<=",
|
||||
Value: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: bson.M{"$and": []bson.M{{"gender": "female"}, {"age": bson.M{"$lte": 20}}}},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "2 columns range and",
|
||||
args: args{
|
||||
columns: []Column{
|
||||
{
|
||||
Name: "age",
|
||||
Exp: ">=",
|
||||
Value: 10,
|
||||
},
|
||||
{
|
||||
Name: "age",
|
||||
Exp: "<=",
|
||||
Value: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: bson.M{"$and": []bson.M{{"age": bson.M{"$gte": 10}}, {"age": bson.M{"$lte": 20}}}},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "2 columns eq or",
|
||||
args: args{
|
||||
columns: []Column{
|
||||
{
|
||||
Name: "name",
|
||||
Value: "LiSi",
|
||||
Logic: "||",
|
||||
},
|
||||
{
|
||||
Name: "gender",
|
||||
Value: "female",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: bson.M{"$or": []bson.M{{"name": "LiSi"}, {"gender": "female"}}},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "2 columns neq or",
|
||||
args: args{
|
||||
columns: []Column{
|
||||
{
|
||||
Name: "name",
|
||||
Value: "LiSi",
|
||||
Logic: "||",
|
||||
},
|
||||
{
|
||||
Name: "gender",
|
||||
Exp: "!=",
|
||||
Value: "male",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: bson.M{"$or": []bson.M{{"name": "LiSi"}, {"gender": bson.M{"$neq": "male"}}}},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "2 columns eq and in",
|
||||
args: args{
|
||||
columns: []Column{
|
||||
{
|
||||
Name: "gender",
|
||||
Value: "male",
|
||||
},
|
||||
{
|
||||
Name: "name",
|
||||
Exp: In,
|
||||
Value: "LiSi,ZhangSan,WangWu",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: bson.M{"$and": []bson.M{{"gender": "male"}, {"name": bson.M{"$in": []interface{}{"LiSi", "ZhangSan", "WangWu"}}}}},
|
||||
wantErr: false,
|
||||
},
|
||||
|
||||
// --------------------------- query 3 columns ------------------------------
|
||||
{
|
||||
name: "3 columns and",
|
||||
args: args{
|
||||
columns: []Column{
|
||||
{
|
||||
Name: "gender",
|
||||
Value: "male",
|
||||
},
|
||||
{
|
||||
Name: "name",
|
||||
Value: "ZhangSan",
|
||||
},
|
||||
{
|
||||
Name: "age",
|
||||
Exp: "<",
|
||||
Value: 12,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: bson.M{"$and": []bson.M{{"gender": "male"}, {"name": "ZhangSan"}, {"age": bson.M{"$lt": 12}}}},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "3 columns or",
|
||||
args: args{
|
||||
columns: []Column{
|
||||
{
|
||||
Name: "gender",
|
||||
Value: "male",
|
||||
Logic: "||",
|
||||
},
|
||||
{
|
||||
Name: "name",
|
||||
Value: "ZhangSan",
|
||||
Logic: "||",
|
||||
},
|
||||
{
|
||||
Name: "age",
|
||||
Exp: "<",
|
||||
Value: 12,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: bson.M{"$or": []bson.M{{"gender": "male"}, {"name": "ZhangSan"}, {"age": bson.M{"$lt": 12}}}},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "3 columns mix (or and)",
|
||||
args: args{
|
||||
columns: []Column{
|
||||
{
|
||||
Name: "gender",
|
||||
Value: "male",
|
||||
Logic: "and",
|
||||
},
|
||||
{
|
||||
Name: "name",
|
||||
Value: "ZhangSan",
|
||||
Logic: "||",
|
||||
},
|
||||
{
|
||||
Name: "age",
|
||||
Exp: "<",
|
||||
Value: 12,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: bson.M{"$or": []bson.M{{"$and": []bson.M{{"gender": "male"}, {"name": "ZhangSan"}}}, {"age": bson.M{"$lt": 12}}}},
|
||||
wantErr: false,
|
||||
},
|
||||
|
||||
// --------------------------- query 4 columns ------------------------------
|
||||
{
|
||||
name: "4 columns mix (or and)",
|
||||
args: args{
|
||||
columns: []Column{
|
||||
{
|
||||
Name: "gender",
|
||||
Value: "male",
|
||||
Logic: "||",
|
||||
},
|
||||
{
|
||||
Name: "name",
|
||||
Value: "ZhangSan",
|
||||
},
|
||||
{
|
||||
Name: "age",
|
||||
Exp: "<",
|
||||
Value: 12,
|
||||
Logic: "||",
|
||||
},
|
||||
{
|
||||
Name: "city",
|
||||
Value: "canton",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: bson.M{"$or": []bson.M{{"gender": "male"}, {"$and": []bson.M{{"name": "ZhangSan"}, {"age": bson.M{"$lt": 12}}}}, {"city": "canton"}}},
|
||||
wantErr: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "convert to object id",
|
||||
args: args{
|
||||
columns: []Column{
|
||||
{
|
||||
Name: "id",
|
||||
Value: "65ce48483f11aff697e30d6d",
|
||||
},
|
||||
{
|
||||
Name: "order_id:oid",
|
||||
Value: "65ce48483f11aff697e30d6d",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: bson.M{"$and": []bson.M{{"_id": primitive.ObjectID{0x65, 0xce, 0x48, 0x48, 0x3f, 0x11, 0xaf, 0xf6, 0x97, 0xe3, 0xd, 0x6d}}, {"order_id": primitive.ObjectID{0x65, 0xce, 0x48, 0x48, 0x3f, 0x11, 0xaf, 0xf6, 0x97, 0xe3, 0xd, 0x6d}}}},
|
||||
wantErr: false,
|
||||
},
|
||||
|
||||
// ---------------------------- error ----------------------------------------------
|
||||
{
|
||||
name: "exp type err",
|
||||
args: args{
|
||||
columns: []Column{
|
||||
{
|
||||
Name: "gender",
|
||||
Exp: "xxxxxx",
|
||||
Value: "male",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "logic type err",
|
||||
args: args{
|
||||
columns: []Column{
|
||||
{
|
||||
Name: "gender",
|
||||
Value: "male",
|
||||
Logic: "xxxxxx",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "name empty",
|
||||
args: args{
|
||||
columns: []Column{
|
||||
{
|
||||
Name: "",
|
||||
Value: "male",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "value empty",
|
||||
args: args{
|
||||
columns: []Column{
|
||||
{
|
||||
Name: "name",
|
||||
Value: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty",
|
||||
args: args{
|
||||
columns: nil,
|
||||
},
|
||||
want: primitive.M{},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
params := &Params{
|
||||
Columns: tt.args.columns,
|
||||
}
|
||||
got, err := params.ConvertToMongoFilter()
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ConvertToMongoFilter() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("ConvertToMongoFilter() got = %#v, want = %#v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConditions_ConvertToMongo(t *testing.T) {
|
||||
c := Conditions{
|
||||
Columns: []Column{
|
||||
{
|
||||
Name: "name",
|
||||
Value: "ZhangSan",
|
||||
},
|
||||
{
|
||||
Name: "gender",
|
||||
Value: "male",
|
||||
},
|
||||
}}
|
||||
got, err := c.ConvertToMongo()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
want := bson.M{"$and": []bson.M{{"name": "ZhangSan"}, {"gender": "male"}}}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("ConvertToMongo() got = %+v, want %+v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConditions_checkValid(t *testing.T) {
|
||||
// empty error
|
||||
c := Conditions{}
|
||||
err := c.CheckValid()
|
||||
assert.Error(t, err)
|
||||
|
||||
// value is empty error
|
||||
c = Conditions{
|
||||
Columns: []Column{
|
||||
{
|
||||
Name: "foo",
|
||||
Value: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
err = c.CheckValid()
|
||||
assert.Error(t, err)
|
||||
|
||||
// exp error
|
||||
c = Conditions{
|
||||
Columns: []Column{
|
||||
{
|
||||
Name: "foo",
|
||||
Value: "bar",
|
||||
Exp: "unknown-exp",
|
||||
},
|
||||
},
|
||||
}
|
||||
err = c.CheckValid()
|
||||
assert.Error(t, err)
|
||||
|
||||
// logic error
|
||||
c = Conditions{
|
||||
Columns: []Column{
|
||||
{
|
||||
Name: "foo",
|
||||
Value: "bar",
|
||||
Logic: "unknown-logic",
|
||||
},
|
||||
},
|
||||
}
|
||||
err = c.CheckValid()
|
||||
assert.Error(t, err)
|
||||
|
||||
// success
|
||||
c = Conditions{
|
||||
Columns: []Column{
|
||||
{
|
||||
Name: "name",
|
||||
Value: "ZhangSan",
|
||||
},
|
||||
{
|
||||
Name: "gender",
|
||||
Value: "male",
|
||||
},
|
||||
}}
|
||||
err = c.CheckValid()
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func Test_groupingIndex(t *testing.T) {
|
||||
type args struct {
|
||||
l int
|
||||
orIndexes []int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want [][]int
|
||||
}{
|
||||
{
|
||||
name: "4 index 1",
|
||||
args: args{
|
||||
l: 4,
|
||||
orIndexes: []int{0, 2},
|
||||
},
|
||||
want: [][]int{{0}, {1, 2}, {3}},
|
||||
},
|
||||
{
|
||||
name: "4 index 2",
|
||||
args: args{
|
||||
l: 4,
|
||||
orIndexes: []int{1},
|
||||
},
|
||||
want: [][]int{{0, 1}, {2, 3}},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := groupingIndex(tt.args.l, tt.args.orIndexes)
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("groupingIndex got = %#v, want = %#v", got, tt.want)
|
||||
}
|
||||
t.Log(got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getSort(t *testing.T) {
|
||||
names := []string{
|
||||
"", "id", "-id", "gender", "gender,id", "-gender,-id",
|
||||
}
|
||||
for _, name := range names {
|
||||
d := getSort(name)
|
||||
t.Log(d)
|
||||
}
|
||||
}
|
|
@ -71,10 +71,10 @@ type Params struct {
|
|||
|
||||
// Column query info
|
||||
type Column struct {
|
||||
Name string `json:"name" form:"columns"` // column name
|
||||
Exp string `json:"exp" form:"columns"` // expressions, which default to = when the value is null, have =, !=, >, >=, <, <=, like, in
|
||||
Value interface{} `json:"value" form:"columns"` // column value
|
||||
Logic string `json:"logic" form:"columns"` // logical type, defaults to and when the value is null, with &(and), ||(or)
|
||||
Name string `json:"name" form:"name"` // column name
|
||||
Exp string `json:"exp" form:"exp"` // expressions, which default to = when the value is null, have =, !=, >, >=, <, <=, like, in
|
||||
Value interface{} `json:"value" form:"value"` // column value
|
||||
Logic string `json:"logic" form:"logic"` // logical type, defaults to and when the value is null, with &(and), ||(or)
|
||||
}
|
||||
|
||||
func (c *Column) checkValid() error {
|
||||
|
|
|
@ -0,0 +1,391 @@
|
|||
package parser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/zhufuyi/sponge/pkg/mgo"
|
||||
"github.com/zhufuyi/sponge/pkg/utils"
|
||||
|
||||
"github.com/huandu/xstrings"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/bsontype"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
mgoOptions "go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
const (
|
||||
goTypeOID = "primitive.ObjectID"
|
||||
goTypeInt = "int"
|
||||
goTypeInt64 = "int64"
|
||||
goTypeFloat64 = "float64"
|
||||
goTypeString = "string"
|
||||
goTypeTime = "time.Time"
|
||||
goTypeBool = "bool"
|
||||
goTypeNil = "nil"
|
||||
goTypeBytes = "[]byte"
|
||||
goTypeStrings = "[]string"
|
||||
goTypeInts = "[]int"
|
||||
goTypeInterface = "interface{}"
|
||||
goTypeSliceInterface = "[]interface{}"
|
||||
|
||||
// SubStructKey sub struct key
|
||||
SubStructKey = "_sub_struct_"
|
||||
// ProtoSubStructKey proto sub struct key
|
||||
ProtoSubStructKey = "_proto_sub_struct_"
|
||||
|
||||
oidName = "_id"
|
||||
)
|
||||
|
||||
var mgoTypeToGo = map[bsontype.Type]string{
|
||||
bson.TypeObjectID: goTypeOID,
|
||||
bson.TypeInt32: goTypeInt,
|
||||
bson.TypeInt64: goTypeInt64,
|
||||
bson.TypeDouble: goTypeFloat64,
|
||||
bson.TypeString: goTypeString,
|
||||
bson.TypeArray: goTypeSliceInterface,
|
||||
bson.TypeEmbeddedDocument: goTypeInterface,
|
||||
bson.TypeTimestamp: goTypeTime,
|
||||
bson.TypeDateTime: goTypeTime,
|
||||
bson.TypeBoolean: goTypeBool,
|
||||
bson.TypeNull: goTypeNil,
|
||||
bson.TypeBinary: goTypeBytes,
|
||||
bson.TypeUndefined: goTypeInterface,
|
||||
bson.TypeCodeWithScope: goTypeString,
|
||||
bson.TypeSymbol: goTypeString,
|
||||
bson.TypeRegex: goTypeString,
|
||||
bson.TypeDecimal128: goTypeInterface,
|
||||
bson.TypeDBPointer: goTypeInterface,
|
||||
bson.TypeMinKey: goTypeInt,
|
||||
bson.TypeMaxKey: goTypeInt,
|
||||
bson.TypeJavaScript: goTypeString,
|
||||
}
|
||||
|
||||
var jsonTagFormat int32 // 0: camel case, 1: snake case
|
||||
|
||||
// SetJSONTagSnakeCase set json tag format to snake case
|
||||
func SetJSONTagSnakeCase() {
|
||||
atomic.AddInt32(&jsonTagFormat, 1)
|
||||
}
|
||||
|
||||
// SetJSONTagCamelCase set json tag format to camel case
|
||||
func SetJSONTagCamelCase() {
|
||||
atomic.AddInt32(&jsonTagFormat, -jsonTagFormat)
|
||||
}
|
||||
|
||||
// MgoField mongo field
|
||||
type MgoField struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Comment string `json:"comment"`
|
||||
ObjectStr string `json:"objectStr"`
|
||||
ProtoObjectStr string `json:"protoObjectStr"`
|
||||
}
|
||||
|
||||
// GetMongodbTableInfo get table info from mongodb
|
||||
func GetMongodbTableInfo(dsn string, tableName string) ([]*MgoField, error) {
|
||||
timeout := time.Second * 5
|
||||
opts := &mgoOptions.ClientOptions{Timeout: &timeout}
|
||||
dsn = utils.AdaptiveMongodbDsn(dsn)
|
||||
db, err := mgo.Init(dsn, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return getMongodbTableFields(db, tableName)
|
||||
}
|
||||
|
||||
func getMongodbTableFields(db *mongo.Database, collectionName string) ([]*MgoField, error) {
|
||||
findOpts := new(mgoOptions.FindOneOptions)
|
||||
findOpts.Sort = bson.M{oidName: -1}
|
||||
result := db.Collection(collectionName).FindOne(context.Background(), bson.M{}, findOpts)
|
||||
raw, err := result.Raw()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
elements, err := raw.Elements()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fields := []*MgoField{}
|
||||
names := []string{}
|
||||
for _, element := range elements {
|
||||
name := element.Key()
|
||||
if name == "deleted_at" { // filter deleted_at, used for soft delete
|
||||
continue
|
||||
}
|
||||
names = append(names, name)
|
||||
t, o, p := getTypeFromMgo(name, element)
|
||||
fields = append(fields, &MgoField{
|
||||
Name: name,
|
||||
Type: t,
|
||||
ObjectStr: o,
|
||||
ProtoObjectStr: p,
|
||||
})
|
||||
}
|
||||
|
||||
return embedTimeField(names, fields), nil
|
||||
}
|
||||
|
||||
func getTypeFromMgo(name string, element bson.RawElement) (goTypeStr string, goObjectStr string, protoObjectStr string) {
|
||||
v := element.Value()
|
||||
switch v.Type {
|
||||
case bson.TypeEmbeddedDocument:
|
||||
var br bson.Raw = v.Value
|
||||
es, err := br.Elements()
|
||||
if err != nil {
|
||||
return goTypeInterface, "", ""
|
||||
}
|
||||
return parseObject(name, es)
|
||||
|
||||
case bson.TypeArray:
|
||||
var br bson.Raw = v.Value
|
||||
es, err := br.Elements()
|
||||
if err != nil {
|
||||
return goTypeInterface, "", ""
|
||||
}
|
||||
if len(es) == 0 {
|
||||
return goTypeInterface, "", ""
|
||||
}
|
||||
t, o, p := parseArray(name, es[0])
|
||||
return convertToSingular(t, o, p)
|
||||
}
|
||||
|
||||
if goType, ok := mgoTypeToGo[v.Type]; !ok {
|
||||
return goTypeInterface, "", ""
|
||||
} else { //nolint
|
||||
return goType, "", ""
|
||||
}
|
||||
}
|
||||
|
||||
func parseObject(name string, elements []bson.RawElement) (goTypeStr string, goObjectStr string, protoObjectStr string) {
|
||||
var goObjStr string
|
||||
var protoObjStr string
|
||||
for num, element := range elements {
|
||||
t, _, _ := getTypeFromMgo(name, element)
|
||||
k := element.Key()
|
||||
|
||||
var jsonTag string
|
||||
if jsonTagFormat == 0 {
|
||||
jsonTag = toLowerFirst(xstrings.ToCamelCase(k))
|
||||
} else {
|
||||
jsonTag = xstrings.ToSnakeCase(k)
|
||||
}
|
||||
|
||||
goObjStr += fmt.Sprintf(" %s %s `bson:\"%s\" json:\"%s\"`\n", xstrings.ToCamelCase(k), t, k, jsonTag)
|
||||
num++
|
||||
protoObjStr += fmt.Sprintf(" %s %s = %d;\n", convertToProtoFieldType(name, t), k, num)
|
||||
}
|
||||
return "*" + xstrings.ToCamelCase(name),
|
||||
fmt.Sprintf("type %s struct {\n%s}\n", xstrings.ToCamelCase(name), goObjStr),
|
||||
fmt.Sprintf("message %s {\n%s}\n", xstrings.ToCamelCase(name), protoObjStr)
|
||||
}
|
||||
|
||||
func parseArray(name string, element bson.RawElement) (goTypeStr string, goObjectStr string, protoObjectStr string) {
|
||||
t, o, p := getTypeFromMgo(name, element)
|
||||
if o != "" {
|
||||
return "[]" + t, o, p
|
||||
}
|
||||
return "[]" + t, "", ""
|
||||
}
|
||||
|
||||
func toLowerFirst(str string) string {
|
||||
if len(str) == 0 {
|
||||
return str
|
||||
}
|
||||
return strings.ToLower(string(str[0])) + str[1:]
|
||||
}
|
||||
|
||||
func embedTimeField(names []string, fields []*MgoField) []*MgoField {
|
||||
isHaveCreatedAt, isHaveUpdatedAt := false, false
|
||||
for _, name := range names {
|
||||
if name == "created_at" {
|
||||
isHaveCreatedAt = true
|
||||
}
|
||||
if name == "updated_at" {
|
||||
isHaveUpdatedAt = true
|
||||
}
|
||||
names = append(names, name)
|
||||
}
|
||||
|
||||
var timeFields []*MgoField
|
||||
if !isHaveCreatedAt {
|
||||
timeFields = append(timeFields, &MgoField{
|
||||
Name: "created_at",
|
||||
Type: goTypeTime,
|
||||
})
|
||||
}
|
||||
if !isHaveUpdatedAt {
|
||||
timeFields = append(timeFields, &MgoField{
|
||||
Name: "updated_at",
|
||||
Type: goTypeTime,
|
||||
})
|
||||
}
|
||||
|
||||
if len(timeFields) == 0 {
|
||||
return fields
|
||||
}
|
||||
|
||||
return append(fields, timeFields...)
|
||||
}
|
||||
|
||||
// ConvertToSQLByMgoFields convert to mysql table ddl
|
||||
func ConvertToSQLByMgoFields(tableName string, fields []*MgoField) (string, map[string]string) {
|
||||
isHaveID := false
|
||||
fieldStr := ""
|
||||
srcMongoTypeMap := make(map[string]string) // name:type
|
||||
objectStrs := []string{}
|
||||
protoObjectStrs := []string{}
|
||||
|
||||
for _, field := range fields {
|
||||
switch field.Type {
|
||||
case goTypeInterface, goTypeSliceInterface:
|
||||
srcMongoTypeMap[field.Name] = xstrings.ToCamelCase(field.Name)
|
||||
default:
|
||||
srcMongoTypeMap[field.Name] = field.Type
|
||||
}
|
||||
if field.Name == oidName {
|
||||
isHaveID = true
|
||||
srcMongoTypeMap["id"] = field.Type
|
||||
continue
|
||||
}
|
||||
|
||||
fieldStr += fmt.Sprintf(" %s %s,\n", field.Name, convertMongoToMysqlType(field.Type))
|
||||
if field.ObjectStr != "" {
|
||||
objectStrs = append(objectStrs, field.ObjectStr)
|
||||
protoObjectStrs = append(protoObjectStrs, field.ProtoObjectStr)
|
||||
}
|
||||
}
|
||||
if isHaveID {
|
||||
fieldStr = " id bigint unsigned primary key,\n" + fieldStr
|
||||
}
|
||||
fieldStr = strings.TrimSuffix(fieldStr, ",\n")
|
||||
|
||||
if len(objectStrs) > 0 {
|
||||
srcMongoTypeMap[SubStructKey] = strings.Join(objectStrs, "\n") + "\n"
|
||||
srcMongoTypeMap[ProtoSubStructKey] = strings.Join(protoObjectStrs, "\n") + "\n"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("CREATE TABLE %s (\n%s\n);", tableName, fieldStr), srcMongoTypeMap
|
||||
}
|
||||
|
||||
// nolint
|
||||
func convertMongoToMysqlType(goType string) string {
|
||||
switch goType {
|
||||
case goTypeInt:
|
||||
return "int"
|
||||
case goTypeInt64:
|
||||
return "bigint"
|
||||
case goTypeFloat64:
|
||||
return "double"
|
||||
case goTypeString:
|
||||
return "varchar(255)"
|
||||
case goTypeTime:
|
||||
return "timestamp" //nolint
|
||||
case goTypeBool:
|
||||
return "tinyint(1)"
|
||||
case goTypeOID, goTypeNil, goTypeBytes, goTypeInterface, goTypeSliceInterface, goTypeInts, goTypeStrings:
|
||||
return "json"
|
||||
}
|
||||
return "json"
|
||||
}
|
||||
|
||||
// nolint
|
||||
func convertToProtoFieldType(name string, goType string) string {
|
||||
switch goType {
|
||||
case "int":
|
||||
return "int32"
|
||||
case "uint":
|
||||
return "uint32" //nolint
|
||||
case "time.Time":
|
||||
return "int64"
|
||||
case "float32":
|
||||
return "float"
|
||||
case "float64":
|
||||
return "double"
|
||||
case goTypeInts, "[]int64":
|
||||
return "repeated int64"
|
||||
case "[]int32":
|
||||
return "repeated int32"
|
||||
case "[]byte":
|
||||
return "string"
|
||||
case goTypeStrings:
|
||||
return "repeated string"
|
||||
}
|
||||
|
||||
if strings.Contains(goType, "[]") {
|
||||
t := strings.TrimLeft(goType, "[]")
|
||||
if strings.Contains(name, t) {
|
||||
return "repeated " + t
|
||||
}
|
||||
}
|
||||
|
||||
return goType
|
||||
}
|
||||
|
||||
// MgoFieldToGoStruct convert to go struct
|
||||
func MgoFieldToGoStruct(name string, fs []*MgoField) string {
|
||||
var str = ""
|
||||
var objStr string
|
||||
|
||||
for _, f := range fs {
|
||||
if f.Name == oidName {
|
||||
str += " ID primitive.ObjectID `bson:\"_id\" json:\"id\"`\n"
|
||||
continue
|
||||
}
|
||||
if f.Type == goTypeInterface || f.Type == goTypeSliceInterface {
|
||||
f.Type = xstrings.ToCamelCase(f.Name)
|
||||
}
|
||||
str += fmt.Sprintf(" %s %s `bson:\"%s\" json:\"%s\"`\n", xstrings.ToCamelCase(f.Name), f.Type, f.Name, f.Name)
|
||||
if f.ObjectStr != "" {
|
||||
objStr += f.ObjectStr + "\n"
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("type %s struct {\n%s}\n\n%s\n", xstrings.ToCamelCase(name), str, objStr)
|
||||
}
|
||||
|
||||
func toSingular(word string) string {
|
||||
if strings.HasSuffix(word, "es") {
|
||||
if len(word) > 2 {
|
||||
return word[:len(word)-2]
|
||||
}
|
||||
} else if strings.HasSuffix(word, "s") {
|
||||
if len(word) > 1 {
|
||||
return word[:len(word)-1]
|
||||
}
|
||||
}
|
||||
return word
|
||||
}
|
||||
|
||||
func nameToSingular(goTypeStr string, targetObjectStr string, markStr string) string {
|
||||
name := strings.ReplaceAll(goTypeStr, "[]*", "")
|
||||
prefixStr := markStr + " " + name
|
||||
l := len(prefixStr)
|
||||
if len(targetObjectStr) <= l {
|
||||
return targetObjectStr
|
||||
}
|
||||
|
||||
if prefixStr == targetObjectStr[:l] {
|
||||
targetObjectStr = toSingular(prefixStr) + " " + targetObjectStr[l:]
|
||||
return targetObjectStr
|
||||
}
|
||||
return targetObjectStr
|
||||
}
|
||||
|
||||
func convertToSingular(goTypeStr string, objectStr string, protoObjectStr string) (tStr string, oStr string, pObjStr string) {
|
||||
if !strings.Contains(goTypeStr, "[]*") || objectStr == "" {
|
||||
return goTypeStr, objectStr, protoObjectStr
|
||||
}
|
||||
|
||||
objectStr = nameToSingular(goTypeStr, objectStr, "type")
|
||||
protoObjectStr = nameToSingular(goTypeStr, protoObjectStr, "message")
|
||||
goTypeStr = toSingular(goTypeStr)
|
||||
|
||||
return goTypeStr, objectStr, protoObjectStr
|
||||
}
|
|
@ -43,6 +43,8 @@ const (
|
|||
DBDriverTidb = "tidb"
|
||||
// DBDriverSqlite sqlite driver
|
||||
DBDriverSqlite = "sqlite"
|
||||
// DBDriverMongodb mongodb driver
|
||||
DBDriverMongodb = "mongodb"
|
||||
)
|
||||
|
||||
// Codes content
|
||||
|
@ -126,12 +128,15 @@ func ParseSQL(sql string, options ...Option) (map[string]string, error) {
|
|||
}
|
||||
|
||||
type tmplData struct {
|
||||
TableName string
|
||||
TName string
|
||||
NameFunc bool
|
||||
RawTableName string
|
||||
Fields []tmplField
|
||||
Comment string
|
||||
TableName string
|
||||
TName string
|
||||
NameFunc bool
|
||||
RawTableName string
|
||||
Fields []tmplField
|
||||
Comment string
|
||||
SubStructs string // sub structs for model
|
||||
ProtoSubStructs string // sub structs for protobuf
|
||||
DBDriver string
|
||||
}
|
||||
|
||||
type tmplField struct {
|
||||
|
@ -141,9 +146,10 @@ type tmplField struct {
|
|||
Tag string
|
||||
Comment string
|
||||
JSONName string
|
||||
DBDriver string
|
||||
}
|
||||
|
||||
// ConditionZero type of condition 0
|
||||
// ConditionZero type of condition 0, used in dao template code
|
||||
func (t tmplField) ConditionZero() string {
|
||||
switch t.GoType {
|
||||
case "int8", "int16", "int32", "int64", "int", "uint8", "uint16", "uint32", "uint64", "uint", "float64", "float32", //nolint
|
||||
|
@ -153,12 +159,28 @@ func (t tmplField) ConditionZero() string {
|
|||
return `!= ""`
|
||||
case "time.Time", "*time.Time", "sql.NullTime": //nolint
|
||||
return `.IsZero() == false`
|
||||
case "[]byte", "[]string", "[]int", "interface{}": //nolint
|
||||
return `!= nil` //nolint
|
||||
case "bool": //nolint
|
||||
return `!= false /*Warning: if the value itself is false, can't be updated*/`
|
||||
}
|
||||
|
||||
if t.DBDriver == DBDriverMongodb {
|
||||
if t.GoType == goTypeOID {
|
||||
return `!= primitive.NilObjectID`
|
||||
}
|
||||
if t.GoType == "*"+t.Name {
|
||||
return `!= nil`
|
||||
}
|
||||
if strings.Contains(t.GoType, "[]") {
|
||||
return `!= nil`
|
||||
}
|
||||
}
|
||||
|
||||
return `!= ` + t.GoType
|
||||
}
|
||||
|
||||
// GoZero type of 0
|
||||
// GoZero type of 0, used in model to json template code
|
||||
func (t tmplField) GoZero() string {
|
||||
switch t.GoType {
|
||||
case "int8", "int16", "int32", "int64", "int", "uint8", "uint16", "uint32", "uint64", "uint", "float64", "float32",
|
||||
|
@ -168,12 +190,28 @@ func (t tmplField) GoZero() string {
|
|||
return `= "string"`
|
||||
case "time.Time", "*time.Time", "sql.NullTime":
|
||||
return `= "0000-01-00T00:00:00.000+08:00"`
|
||||
case "[]byte", "[]string", "[]int", "interface{}": //nolint
|
||||
return `= nil` //nolint
|
||||
case "bool": //nolint
|
||||
return `= false`
|
||||
}
|
||||
|
||||
if t.DBDriver == DBDriverMongodb {
|
||||
if t.GoType == goTypeOID {
|
||||
return `= primitive.NilObjectID`
|
||||
}
|
||||
if t.GoType == "*"+t.Name {
|
||||
return `= nil`
|
||||
}
|
||||
if strings.Contains(t.GoType, "[]") {
|
||||
return `= nil`
|
||||
}
|
||||
}
|
||||
|
||||
return `= ` + t.GoType
|
||||
}
|
||||
|
||||
// GoTypeZero type of 0
|
||||
// GoTypeZero type of 0, used in service template code
|
||||
func (t tmplField) GoTypeZero() string {
|
||||
switch t.GoType {
|
||||
case "int8", "int16", "int32", "int64", "int", "uint8", "uint16", "uint32", "uint64", "uint", "float64", "float32",
|
||||
|
@ -183,6 +221,22 @@ func (t tmplField) GoTypeZero() string {
|
|||
return `""`
|
||||
case "time.Time", "*time.Time", "sql.NullTime":
|
||||
return `0 /*time.Now().Second()*/`
|
||||
case "[]byte", "[]string", "[]int", "interface{}": //nolint
|
||||
return `nil` //nolint
|
||||
case "bool": //nolint
|
||||
return `false`
|
||||
}
|
||||
|
||||
if t.DBDriver == DBDriverMongodb {
|
||||
if t.GoType == goTypeOID {
|
||||
return `primitive.NilObjectID`
|
||||
}
|
||||
if t.GoType == "*"+t.Name {
|
||||
return `nil` //nolint
|
||||
}
|
||||
if strings.Contains(t.GoType, "[]") {
|
||||
return `nil` //nolint
|
||||
}
|
||||
}
|
||||
|
||||
return t.GoType
|
||||
|
@ -214,6 +268,7 @@ var replaceFields = map[string]string{
|
|||
|
||||
const (
|
||||
columnID = "id"
|
||||
_columnID = "_id"
|
||||
columnCreatedAt = "created_at"
|
||||
columnUpdatedAt = "updated_at"
|
||||
columnDeletedAt = "deleted_at"
|
||||
|
@ -349,30 +404,56 @@ func makeCode(stmt *ast.CreateTableStmt, opt options) (*codeText, error) {
|
|||
//return "", nil, errors.Errorf(" unsupport option %d\n", o.Tp)
|
||||
}
|
||||
}
|
||||
if !isPrimaryKey[colName] && isNotNull {
|
||||
gormTag.WriteString(";NOT NULL")
|
||||
}
|
||||
tags = append(tags, "gorm", gormTag.String())
|
||||
|
||||
if opt.JSONTag {
|
||||
tags = append(tags, "json", jsonName)
|
||||
}
|
||||
field.DBDriver = opt.DBDriver
|
||||
switch opt.DBDriver {
|
||||
case DBDriverMongodb: // mongodb
|
||||
tags = append(tags, "bson", gormTag.String())
|
||||
if opt.JSONTag {
|
||||
if strings.ToLower(jsonName) == "_id" {
|
||||
jsonName = "id"
|
||||
}
|
||||
field.JSONName = jsonName
|
||||
tags = append(tags, "json", jsonName)
|
||||
}
|
||||
field.Tag = makeTagStr(tags)
|
||||
field.GoType = opt.FieldTypes[colName]
|
||||
if field.GoType == "time.Time" {
|
||||
importPath = append(importPath, "time")
|
||||
}
|
||||
|
||||
field.Tag = makeTagStr(tags)
|
||||
default: // gorm
|
||||
if !isPrimaryKey[colName] && isNotNull {
|
||||
gormTag.WriteString(";NOT NULL")
|
||||
}
|
||||
tags = append(tags, "gorm", gormTag.String())
|
||||
|
||||
// get type in golang
|
||||
nullStyle := opt.NullStyle
|
||||
if !canNull {
|
||||
nullStyle = NullDisable
|
||||
if opt.JSONTag {
|
||||
tags = append(tags, "json", jsonName)
|
||||
}
|
||||
field.Tag = makeTagStr(tags)
|
||||
|
||||
// get type in golang
|
||||
nullStyle := opt.NullStyle
|
||||
if !canNull {
|
||||
nullStyle = NullDisable
|
||||
}
|
||||
goType, pkg := mysqlToGoType(col.Tp, nullStyle)
|
||||
if pkg != "" {
|
||||
importPath = append(importPath, pkg)
|
||||
}
|
||||
field.GoType = goType
|
||||
}
|
||||
goType, pkg := mysqlToGoType(col.Tp, nullStyle)
|
||||
if pkg != "" {
|
||||
importPath = append(importPath, pkg)
|
||||
}
|
||||
field.GoType = goType
|
||||
|
||||
data.Fields = append(data.Fields, field)
|
||||
}
|
||||
if v, ok := opt.FieldTypes[SubStructKey]; ok {
|
||||
data.SubStructs = v
|
||||
}
|
||||
if v, ok := opt.FieldTypes[ProtoSubStructKey]; ok {
|
||||
data.ProtoSubStructs = v
|
||||
}
|
||||
data.DBDriver = opt.DBDriver
|
||||
|
||||
updateFieldsCode, err := getUpdateFieldsCode(data, opt.IsEmbed)
|
||||
if err != nil {
|
||||
|
@ -454,13 +535,22 @@ func getModelStructCode(data tmplData, importPaths []string, isEmbed bool) (stri
|
|||
newImportPaths = append(newImportPaths, "github.com/zhufuyi/sponge/pkg/ggorm")
|
||||
} else {
|
||||
for i, field := range data.Fields {
|
||||
if strings.Contains(field.GoType, "time.Time") {
|
||||
data.Fields[i].GoType = "*time.Time"
|
||||
continue
|
||||
}
|
||||
// force conversion of ID field to uint64 type
|
||||
if field.Name == "ID" {
|
||||
data.Fields[i].GoType = "uint64"
|
||||
switch field.DBDriver {
|
||||
case DBDriverMongodb:
|
||||
if field.Name == "ID" {
|
||||
data.Fields[i].GoType = goTypeOID
|
||||
importPaths = append(importPaths, "go.mongodb.org/mongo-driver/bson/primitive")
|
||||
}
|
||||
|
||||
default:
|
||||
if strings.Contains(field.GoType, "time.Time") {
|
||||
data.Fields[i].GoType = "*time.Time"
|
||||
continue
|
||||
}
|
||||
// force conversion of ID field to uint64 type
|
||||
if field.Name == "ID" {
|
||||
data.Fields[i].GoType = "uint64"
|
||||
}
|
||||
}
|
||||
}
|
||||
newImportPaths = importPaths
|
||||
|
@ -482,6 +572,16 @@ func getModelStructCode(data tmplData, importPaths []string, isEmbed bool) (stri
|
|||
structCode = strings.ReplaceAll(structCode, __type__, replaceFields[__type__])
|
||||
}
|
||||
|
||||
if data.SubStructs != "" {
|
||||
structCode += data.SubStructs
|
||||
}
|
||||
if data.DBDriver == DBDriverMongodb {
|
||||
structCode = strings.ReplaceAll(structCode, `bson:"column:`, `bson:"`)
|
||||
structCode = strings.ReplaceAll(structCode, `;type:"`, `"`)
|
||||
structCode = strings.ReplaceAll(structCode, `;type:;primary_key`, ``)
|
||||
structCode = strings.ReplaceAll(structCode, `bson:"id" json:"id"`, `bson:"_id" json:"id"`)
|
||||
}
|
||||
|
||||
return structCode, newImportPaths, nil
|
||||
}
|
||||
|
||||
|
@ -507,28 +607,39 @@ func getUpdateFieldsCode(data tmplData, isEmbed bool) (string, error) {
|
|||
var newFields = []tmplField{}
|
||||
for _, field := range data.Fields {
|
||||
falseColumns := []string{}
|
||||
if isIgnoreFields(field.ColName, falseColumns...) {
|
||||
if isIgnoreFields(field.ColName, falseColumns...) || field.ColName == columnID || field.ColName == _columnID {
|
||||
continue
|
||||
}
|
||||
newFields = append(newFields, field)
|
||||
}
|
||||
data.Fields = newFields
|
||||
|
||||
builder := strings.Builder{}
|
||||
err := updateFieldTmpl.Execute(&builder, data)
|
||||
buf := new(bytes.Buffer)
|
||||
err := updateFieldTmpl.Execute(buf, data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
code, err := format.Source([]byte(builder.String()))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(code), nil
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func getHandlerStructCodes(data tmplData) (string, error) {
|
||||
newFields := []tmplField{}
|
||||
for _, field := range data.Fields {
|
||||
if field.DBDriver == DBDriverMongodb { // mongodb
|
||||
if field.Name == "ID" {
|
||||
field.GoType = "string"
|
||||
}
|
||||
if "*"+field.Name == field.GoType {
|
||||
field.GoType = "*model." + field.Name
|
||||
}
|
||||
if strings.Contains(field.GoType, "[]*") {
|
||||
field.GoType = "[]*model." + strings.ReplaceAll(field.GoType, "[]*", "")
|
||||
}
|
||||
}
|
||||
newFields = append(newFields, field)
|
||||
}
|
||||
data.Fields = newFields
|
||||
|
||||
postStructCode, err := tmplExecuteWithFilter(data, handlerCreateStructTmpl)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("handlerCreateStructTmpl error: %v", err)
|
||||
|
@ -554,6 +665,11 @@ func tmplExecuteWithFilter(data tmplData, tmpl *template.Template, reservedColum
|
|||
if isIgnoreFields(field.ColName, reservedColumns...) {
|
||||
continue
|
||||
}
|
||||
if field.DBDriver == DBDriverMongodb { // mongodb
|
||||
if strings.ToLower(field.Name) == "id" {
|
||||
field.GoType = "string"
|
||||
}
|
||||
}
|
||||
newFields = append(newFields, field)
|
||||
}
|
||||
data.Fields = newFields
|
||||
|
@ -624,10 +740,86 @@ func getProtoFileCode(data tmplData, isWebProto bool) (string, error) {
|
|||
code = strings.ReplaceAll(code, "// protoMessageDetailCode", protoMessageDetailCode)
|
||||
code = strings.ReplaceAll(code, "*time.Time", "int64")
|
||||
code = strings.ReplaceAll(code, "time.Time", "int64")
|
||||
code = adaptedDbType(data, isWebProto, code)
|
||||
|
||||
return code, nil
|
||||
}
|
||||
|
||||
const (
|
||||
createTableReplyFieldCodeMark = "// createTableReplyFieldCode"
|
||||
deleteTableByIDRequestFieldCodeMark = "// deleteTableByIDRequestFieldCode"
|
||||
deleteTableByIDsRequestFieldCodeMark = "// deleteTableByIDsRequestFieldCode"
|
||||
getTableByIDRequestFieldCodeMark = "// getTableByIDRequestFieldCode"
|
||||
getTableByIDsRequestFieldCodeMark = "// getTableByIDsRequestFieldCode"
|
||||
listTableByLastIDRequestFieldCodeMark = "// listTableByLastIDRequestFieldCode"
|
||||
)
|
||||
|
||||
var grpcDefaultProtoMessageFieldCodes = map[string]string{
|
||||
createTableReplyFieldCodeMark: "uint64 id = 1;",
|
||||
deleteTableByIDRequestFieldCodeMark: "uint64 id = 1 [(validate.rules).uint64.gt = 0];",
|
||||
deleteTableByIDsRequestFieldCodeMark: "repeated uint64 ids = 1 [(validate.rules).repeated.min_items = 1];",
|
||||
getTableByIDRequestFieldCodeMark: "uint64 id = 1 [(validate.rules).uint64.gt = 0];",
|
||||
getTableByIDsRequestFieldCodeMark: "repeated uint64 ids = 1 [(validate.rules).repeated.min_items = 1];",
|
||||
listTableByLastIDRequestFieldCodeMark: "uint64 lastID = 1; // last id",
|
||||
}
|
||||
|
||||
var webDefaultProtoMessageFieldCodes = map[string]string{
|
||||
createTableReplyFieldCodeMark: "uint64 id = 1;",
|
||||
deleteTableByIDRequestFieldCodeMark: `uint64 id =1 [(validate.rules).uint64.gt = 0, (tagger.tags) = "uri:\"id\""];`,
|
||||
deleteTableByIDsRequestFieldCodeMark: "repeated uint64 ids = 1 [(validate.rules).repeated.min_items = 1];",
|
||||
getTableByIDRequestFieldCodeMark: `uint64 id =1 [(validate.rules).uint64.gt = 0, (tagger.tags) = "uri:\"id\"" ];`,
|
||||
getTableByIDsRequestFieldCodeMark: "repeated uint64 ids = 1 [(validate.rules).repeated.min_items = 1];",
|
||||
listTableByLastIDRequestFieldCodeMark: `uint64 lastID = 1 [(tagger.tags) = "form:\"lastID\""]; // last id`,
|
||||
}
|
||||
|
||||
var grpcProtoMessageFieldCodes = map[string]string{
|
||||
createTableReplyFieldCodeMark: "string id = 1;",
|
||||
deleteTableByIDRequestFieldCodeMark: "string id = 1 [(validate.rules).string.min_len = 6];",
|
||||
deleteTableByIDsRequestFieldCodeMark: "repeated string ids = 1 [(validate.rules).repeated.min_items = 1];",
|
||||
getTableByIDRequestFieldCodeMark: "string id = 1 [(validate.rules).string.min_len = 6];",
|
||||
getTableByIDsRequestFieldCodeMark: "repeated string ids = 1 [(validate.rules).repeated.min_items = 1];",
|
||||
listTableByLastIDRequestFieldCodeMark: "string lastID = 1; // last id",
|
||||
}
|
||||
|
||||
var webProtoMessageFieldCodes = map[string]string{
|
||||
createTableReplyFieldCodeMark: "string id = 1;",
|
||||
deleteTableByIDRequestFieldCodeMark: `string id =1 [(validate.rules).string.min_len = 6, (tagger.tags) = "uri:\"id\""];`,
|
||||
deleteTableByIDsRequestFieldCodeMark: "repeated string ids = 1 [(validate.rules).repeated.min_items = 1];",
|
||||
getTableByIDRequestFieldCodeMark: `string id =1 [(validate.rules).string.min_len = 6, (tagger.tags) = "uri:\"id\"" ];`,
|
||||
getTableByIDsRequestFieldCodeMark: "repeated string ids = 1 [(validate.rules).repeated.min_items = 1];",
|
||||
listTableByLastIDRequestFieldCodeMark: `string lastID = 1 [(tagger.tags) = "form:\"lastID\""]; // last id`,
|
||||
}
|
||||
|
||||
func adaptedDbType(data tmplData, isWebProto bool, code string) string {
|
||||
switch data.DBDriver {
|
||||
case DBDriverMongodb: // mongodb
|
||||
if isWebProto {
|
||||
code = replaceProtoMessageFieldCode(code, webProtoMessageFieldCodes)
|
||||
} else {
|
||||
code = replaceProtoMessageFieldCode(code, grpcProtoMessageFieldCodes)
|
||||
}
|
||||
default:
|
||||
if isWebProto {
|
||||
code = replaceProtoMessageFieldCode(code, webDefaultProtoMessageFieldCodes)
|
||||
} else {
|
||||
code = replaceProtoMessageFieldCode(code, grpcDefaultProtoMessageFieldCodes)
|
||||
}
|
||||
}
|
||||
|
||||
if data.ProtoSubStructs != "" {
|
||||
code += "\n" + data.ProtoSubStructs
|
||||
}
|
||||
|
||||
return code
|
||||
}
|
||||
|
||||
func replaceProtoMessageFieldCode(code string, messageFields map[string]string) string {
|
||||
for k, v := range messageFields {
|
||||
code = strings.ReplaceAll(code, k, v)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
func getServiceStructCode(data tmplData) (string, error) {
|
||||
builder := strings.Builder{}
|
||||
err := serviceStructTmpl.Execute(&builder, data)
|
||||
|
@ -688,6 +880,7 @@ func addCommaToJSON(modelJSONCode string) string {
|
|||
return out
|
||||
}
|
||||
|
||||
// nolint
|
||||
func mysqlToGoType(colTp *types.FieldType, style NullStyle) (name string, path string) {
|
||||
if style == NullInSql {
|
||||
path = "database/sql"
|
||||
|
@ -746,6 +939,7 @@ func mysqlToGoType(colTp *types.FieldType, style NullStyle) (name string, path s
|
|||
return name, path
|
||||
}
|
||||
|
||||
// nolint
|
||||
func goTypeToProto(fields []tmplField) []tmplField {
|
||||
var newFields []tmplField
|
||||
for _, field := range fields {
|
||||
|
@ -760,7 +954,24 @@ func goTypeToProto(fields []tmplField) []tmplField {
|
|||
field.GoType = "float"
|
||||
case "float64":
|
||||
field.GoType = "double"
|
||||
case goTypeInts, "[]int64":
|
||||
field.GoType = "repeated int64"
|
||||
case "[]int32":
|
||||
field.GoType = "repeated int32"
|
||||
case "[]byte":
|
||||
field.GoType = "string"
|
||||
case goTypeStrings:
|
||||
field.GoType = "repeated string"
|
||||
}
|
||||
|
||||
if field.DBDriver == DBDriverMongodb {
|
||||
if field.GoType[0] == '*' {
|
||||
field.GoType = field.GoType[1:]
|
||||
} else if strings.Contains(field.GoType, "[]*") {
|
||||
field.GoType = "repeated " + strings.ReplaceAll(field.GoType, "[]*", "")
|
||||
}
|
||||
}
|
||||
|
||||
newFields = append(newFields, field)
|
||||
}
|
||||
return newFields
|
||||
|
|
|
@ -11,22 +11,25 @@ import (
|
|||
|
||||
func TestParseSql(t *testing.T) {
|
||||
sql := `CREATE TABLE t_person_info (
|
||||
age INT(11) unsigned NULL,
|
||||
id BIGINT(11) PRIMARY KEY AUTO_INCREMENT NOT NULL COMMENT 'id',
|
||||
age INT(11) unsigned NULL,
|
||||
name VARCHAR(30) NOT NULL DEFAULT 'default_name' COMMENT 'name',
|
||||
created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
login_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
sex VARCHAR(2) NULL,
|
||||
gender INT(8) NULL,
|
||||
num INT(11) DEFAULT 3 NULL,
|
||||
comment TEXT
|
||||
) COMMENT="person info";`
|
||||
|
||||
codes, err := ParseSQL(sql, WithTablePrefix("t_"), WithJSONTag(0))
|
||||
codes, err := ParseSQL(sql, WithTablePrefix("t_"), WithJSONTag(0), WithNullStyle(NullDisable))
|
||||
assert.Nil(t, err)
|
||||
for k, v := range codes {
|
||||
assert.NotEmpty(t, k)
|
||||
assert.NotEmpty(t, v)
|
||||
}
|
||||
t.Log(codes[CodeTypeJSON])
|
||||
return
|
||||
//printCode(codes)
|
||||
|
||||
codes, err = ParseSQL(sql, WithTablePrefix("t_"), WithJSONTag(0), WithEmbed())
|
||||
assert.Nil(t, err)
|
||||
|
@ -34,6 +37,23 @@ func TestParseSql(t *testing.T) {
|
|||
assert.NotEmpty(t, k)
|
||||
assert.NotEmpty(t, v)
|
||||
}
|
||||
//printCode(codes)
|
||||
|
||||
codes, err = ParseSQL(sql, WithTablePrefix("t_"), WithJSONTag(0), WithWebProto())
|
||||
assert.Nil(t, err)
|
||||
for k, v := range codes {
|
||||
assert.NotEmpty(t, k)
|
||||
assert.NotEmpty(t, v)
|
||||
}
|
||||
//printCode(codes)
|
||||
|
||||
codes, err = ParseSQL(sql, WithTablePrefix("t_"), WithJSONTag(0), WithDBDriver(DBDriverPostgresql))
|
||||
assert.Nil(t, err)
|
||||
for k, v := range codes {
|
||||
assert.NotEmpty(t, k)
|
||||
assert.NotEmpty(t, v)
|
||||
}
|
||||
//printCode(codes)
|
||||
}
|
||||
|
||||
var testData = [][]string{
|
||||
|
@ -121,7 +141,7 @@ func Test_parseOption(t *testing.T) {
|
|||
}
|
||||
|
||||
func Test_mysqlToGoType(t *testing.T) {
|
||||
testData := []*types.FieldType{
|
||||
fields := []*types.FieldType{
|
||||
{Tp: uint8('n')},
|
||||
{Tp: mysql.TypeTiny},
|
||||
{Tp: mysql.TypeLonglong},
|
||||
|
@ -132,7 +152,7 @@ func Test_mysqlToGoType(t *testing.T) {
|
|||
{Tp: mysql.TypeJSON},
|
||||
}
|
||||
var names []string
|
||||
for _, d := range testData {
|
||||
for _, d := range fields {
|
||||
name1, _ := mysqlToGoType(d, NullInSql)
|
||||
name2, _ := mysqlToGoType(d, NullInPointer)
|
||||
names = append(names, name1, name2)
|
||||
|
@ -141,12 +161,12 @@ func Test_mysqlToGoType(t *testing.T) {
|
|||
}
|
||||
|
||||
func Test_goTypeToProto(t *testing.T) {
|
||||
testData := []tmplField{
|
||||
fields := []tmplField{
|
||||
{GoType: "int"},
|
||||
{GoType: "uint"},
|
||||
{GoType: "time.Time"},
|
||||
}
|
||||
v := goTypeToProto(testData)
|
||||
v := goTypeToProto(fields)
|
||||
assert.NotNil(t, v)
|
||||
}
|
||||
|
||||
|
@ -179,8 +199,11 @@ func TestGetMysqlTableInfo(t *testing.T) {
|
|||
|
||||
func TestGetPostgresqlTableInfo(t *testing.T) {
|
||||
fields, err := GetPostgresqlTableInfo("host=192.168.3.37 port=5432 user=root password=123456 dbname=account sslmode=disable", "user_example")
|
||||
t.Log(fields, err)
|
||||
sql, fieldTypes := ConvertToMysqlTable("user_example", fields)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
return
|
||||
}
|
||||
sql, fieldTypes := ConvertToSQLByPgFields("user_example", fields)
|
||||
t.Log(sql, fieldTypes)
|
||||
}
|
||||
|
||||
|
@ -189,13 +212,23 @@ func TestGetSqliteTableInfo(t *testing.T) {
|
|||
t.Log(err, info)
|
||||
}
|
||||
|
||||
func TestConvertToMysqlTable(t *testing.T) {
|
||||
func TestGetMongodbTableInfo(t *testing.T) {
|
||||
fields, err := GetMongodbTableInfo("mongodb://root:123456@192.168.3.37:27017/account", "people")
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
return
|
||||
}
|
||||
sql, fieldTypes := ConvertToSQLByMgoFields("people", fields)
|
||||
t.Log(sql, fieldTypes)
|
||||
}
|
||||
|
||||
func TestConvertToSQLByPgFields(t *testing.T) {
|
||||
fields := []*PGField{
|
||||
{Name: "id", Type: "smallint"},
|
||||
{Name: "name", Type: "character", Lengthvar: 24, Notnull: false},
|
||||
{Name: "age", Type: "smallint", Notnull: true},
|
||||
}
|
||||
sql, tps := ConvertToMysqlTable("foobar", fields)
|
||||
sql, tps := ConvertToSQLByPgFields("foobar", fields)
|
||||
t.Log(sql, tps)
|
||||
}
|
||||
|
||||
|
@ -219,3 +252,155 @@ func Test_toMysqlTable(t *testing.T) {
|
|||
t.Log(toMysqlType(field), getType(field))
|
||||
}
|
||||
}
|
||||
|
||||
func printCode(code map[string]string) {
|
||||
for k, v := range code {
|
||||
fmt.Printf("\n\n----------------- %s --------------------\n%s\n", k, v)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getMongodbTableFields(t *testing.T) {
|
||||
fields := []*MgoField{
|
||||
{
|
||||
Name: "_id",
|
||||
Type: "primitive.ObjectID",
|
||||
ObjectStr: "",
|
||||
ProtoObjectStr: "",
|
||||
},
|
||||
{
|
||||
Name: "age",
|
||||
Type: "int",
|
||||
ObjectStr: "",
|
||||
ProtoObjectStr: "",
|
||||
},
|
||||
{
|
||||
Name: "birthday",
|
||||
Type: "time.Time",
|
||||
ObjectStr: "",
|
||||
ProtoObjectStr: "",
|
||||
},
|
||||
{
|
||||
Name: "home_address",
|
||||
Type: "HomeAddress",
|
||||
ObjectStr: "type HomeAddress struct { Street string `bson:\"street\" json:\"street\"`; City string `bson:\"city\" json:\"city\"`; State string `bson:\"state\" json:\"state\"`; Zip int `bson:\"zip\" json:\"zip\"` } ",
|
||||
ProtoObjectStr: `message HomeAddress {
|
||||
string street = 1;
|
||||
string city = 2;
|
||||
string state = 3;
|
||||
int32 zip = 4;
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
Name: "interests",
|
||||
Type: "[]string",
|
||||
ObjectStr: "",
|
||||
ProtoObjectStr: "",
|
||||
},
|
||||
{
|
||||
Name: "is_child",
|
||||
Type: "bool",
|
||||
ObjectStr: "",
|
||||
ProtoObjectStr: "",
|
||||
},
|
||||
{
|
||||
Name: "name",
|
||||
Type: "string",
|
||||
ObjectStr: "",
|
||||
ProtoObjectStr: "",
|
||||
},
|
||||
{
|
||||
Name: "numbers",
|
||||
Type: "[]int",
|
||||
ObjectStr: "",
|
||||
ProtoObjectStr: "",
|
||||
},
|
||||
{
|
||||
Name: "shop_addresses",
|
||||
Type: "[]ShopAddress",
|
||||
ObjectStr: "type ShopAddress struct { CityO string `bson:\"city_o\" json:\"cityO\"`; StateO string `bson:\"state_o\" json:\"stateO\"` }",
|
||||
ProtoObjectStr: `message ShopAddress {
|
||||
string city_o = 1;
|
||||
string state_o = 2;
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
Name: "created_at",
|
||||
Type: "time.Time",
|
||||
ObjectStr: "",
|
||||
ProtoObjectStr: "",
|
||||
},
|
||||
{
|
||||
Name: "updated_at",
|
||||
Type: "time.Time",
|
||||
ObjectStr: "",
|
||||
ProtoObjectStr: "",
|
||||
},
|
||||
{
|
||||
Name: "deleted_at",
|
||||
Type: "*time.Time",
|
||||
ObjectStr: "",
|
||||
ProtoObjectStr: "",
|
||||
},
|
||||
}
|
||||
|
||||
goStructs := MgoFieldToGoStruct("foobar", fields)
|
||||
t.Log(goStructs)
|
||||
|
||||
sql, fieldsMap := ConvertToSQLByMgoFields("foobar", fields)
|
||||
t.Log(sql)
|
||||
opts := []Option{
|
||||
WithDBDriver(DBDriverMongodb),
|
||||
WithFieldTypes(fieldsMap),
|
||||
WithJSONTag(1),
|
||||
}
|
||||
codes, err := ParseSQL(sql, opts...)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
_ = codes
|
||||
//printCode(codes)
|
||||
|
||||
sql, fieldsMap = ConvertToSQLByMgoFields("foobar", fields)
|
||||
t.Log(sql)
|
||||
opts = []Option{
|
||||
WithDBDriver(DBDriverMongodb),
|
||||
WithFieldTypes(fieldsMap),
|
||||
WithJSONTag(1),
|
||||
WithWebProto(),
|
||||
}
|
||||
codes, err = ParseSQL(sql, opts...)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
//printCode(codes)
|
||||
}
|
||||
|
||||
func Test_toSingular(t *testing.T) {
|
||||
strs := []string{
|
||||
"users",
|
||||
"address",
|
||||
"addresses",
|
||||
}
|
||||
for _, str := range strs {
|
||||
t.Log(str, toSingular(str))
|
||||
}
|
||||
}
|
||||
|
||||
func Test_embedTimeFields(t *testing.T) {
|
||||
names := []string{"age"}
|
||||
|
||||
fields := embedTimeField(names, []*MgoField{})
|
||||
t.Log(fields)
|
||||
|
||||
names = []string{
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
}
|
||||
fields = embedTimeField(names, []*MgoField{})
|
||||
t.Log(fields)
|
||||
}
|
||||
|
|
|
@ -50,8 +50,8 @@ ORDER BY a.attnum;`, tableName)
|
|||
return fields, nil
|
||||
}
|
||||
|
||||
// ConvertToMysqlTable convert to mysql table ddl
|
||||
func ConvertToMysqlTable(tableName string, fields []*PGField) (string, map[string]string) {
|
||||
// ConvertToSQLByPgFields convert to mysql table ddl
|
||||
func ConvertToSQLByPgFields(tableName string, fields []*PGField) (string, map[string]string) {
|
||||
fieldStr := ""
|
||||
pgTypeMap := make(map[string]string) // name:type
|
||||
for _, field := range fields {
|
||||
|
@ -71,14 +71,13 @@ func ConvertToMysqlTable(tableName string, fields []*PGField) (string, map[strin
|
|||
return fmt.Sprintf("CREATE TABLE %s (\n%s\n);", tableName, fieldStr), pgTypeMap
|
||||
}
|
||||
|
||||
// nolint
|
||||
func toMysqlType(field *PGField) string {
|
||||
switch field.Type {
|
||||
// 整型
|
||||
case "smallint", "integer", "smallserial", "serial", "int2", "int4":
|
||||
return "int"
|
||||
case "bigint", "bigserial", "int8":
|
||||
return "bigint"
|
||||
// 浮点数
|
||||
case "real":
|
||||
return "float"
|
||||
case "decimal", "numeric":
|
||||
|
@ -87,14 +86,12 @@ func toMysqlType(field *PGField) string {
|
|||
return "double"
|
||||
case "money":
|
||||
return "varchar(30)"
|
||||
// 字符串
|
||||
case "character", "character varying", "varchar", "char", "bpchar":
|
||||
if field.Lengthvar > 4 {
|
||||
return fmt.Sprintf("varchar(%d)", field.Lengthvar-4)
|
||||
}
|
||||
case "text":
|
||||
return "text"
|
||||
// 日期时间
|
||||
case "timestamp":
|
||||
return "timestamp"
|
||||
case "date":
|
||||
|
|
|
@ -42,7 +42,7 @@ import (
|
|||
updateFieldTmpl *template.Template
|
||||
updateFieldTmplRaw = `
|
||||
{{- range .Fields}}
|
||||
if table.{{.Name}} {{.ConditionZero}} {
|
||||
if table.{{.Name}}{{.ConditionZero}} {
|
||||
update["{{.ColName}}"] = table.{{.Name}}
|
||||
}
|
||||
{{- end}}`
|
||||
|
@ -129,11 +129,11 @@ service {{.TName}} {
|
|||
// protoMessageCreateCode
|
||||
|
||||
message Create{{.TableName}}Reply {
|
||||
uint64 id = 1;
|
||||
// createTableReplyFieldCode
|
||||
}
|
||||
|
||||
message Delete{{.TableName}}ByIDRequest {
|
||||
uint64 id = 1 [(validate.rules).uint64.gt = 0];
|
||||
// deleteTableByIDRequestFieldCode
|
||||
}
|
||||
|
||||
message Delete{{.TableName}}ByIDReply {
|
||||
|
@ -141,7 +141,7 @@ message Delete{{.TableName}}ByIDReply {
|
|||
}
|
||||
|
||||
message Delete{{.TableName}}ByIDsRequest {
|
||||
repeated uint64 ids = 1 [(validate.rules).repeated.min_items = 1];
|
||||
// deleteTableByIDsRequestFieldCode
|
||||
}
|
||||
|
||||
message Delete{{.TableName}}ByIDsReply {
|
||||
|
@ -157,7 +157,7 @@ message Update{{.TableName}}ByIDReply {
|
|||
// protoMessageDetailCode
|
||||
|
||||
message Get{{.TableName}}ByIDRequest {
|
||||
uint64 id = 1 [(validate.rules).uint64.gt = 0];
|
||||
// getTableByIDRequestFieldCode
|
||||
}
|
||||
|
||||
message Get{{.TableName}}ByIDReply {
|
||||
|
@ -173,7 +173,7 @@ message Get{{.TableName}}ByConditionReply {
|
|||
}
|
||||
|
||||
message List{{.TableName}}ByIDsRequest {
|
||||
repeated uint64 ids = 1 [(validate.rules).repeated.min_items = 1];
|
||||
// getTableByIDsRequestFieldCode
|
||||
}
|
||||
|
||||
message List{{.TableName}}ByIDsReply {
|
||||
|
@ -181,8 +181,8 @@ message List{{.TableName}}ByIDsReply {
|
|||
}
|
||||
|
||||
message List{{.TableName}}ByLastIDRequest {
|
||||
uint64 lastID = 1; // last id
|
||||
uint32 limit = 2 [(validate.rules).uint32.gt = 0]; // limit size per page
|
||||
// listTableByLastIDRequestFieldCode
|
||||
uint32 limit = 2 [(validate.rules).uint32.gt = 0]; // limit size per page
|
||||
string sort = 3; // sort by column name of table, default is -id, the - sign indicates descending order.
|
||||
}
|
||||
|
||||
|
@ -411,11 +411,11 @@ service {{.TName}} {
|
|||
// protoMessageCreateCode
|
||||
|
||||
message Create{{.TableName}}Reply {
|
||||
uint64 id = 1;
|
||||
// createTableReplyFieldCode
|
||||
}
|
||||
|
||||
message Delete{{.TableName}}ByIDRequest {
|
||||
uint64 id =1 [(validate.rules).uint64.gte = 1, (tagger.tags) = "uri:\"id\"" ];
|
||||
// deleteTableByIDRequestFieldCode
|
||||
}
|
||||
|
||||
message Delete{{.TableName}}ByIDReply {
|
||||
|
@ -423,7 +423,7 @@ message Delete{{.TableName}}ByIDReply {
|
|||
}
|
||||
|
||||
message Delete{{.TableName}}ByIDsRequest {
|
||||
repeated uint64 ids = 1 [(validate.rules).repeated.min_items = 1];
|
||||
// deleteTableByIDsRequestFieldCode
|
||||
}
|
||||
|
||||
message Delete{{.TableName}}ByIDsReply {
|
||||
|
@ -439,7 +439,7 @@ message Update{{.TableName}}ByIDReply {
|
|||
// protoMessageDetailCode
|
||||
|
||||
message Get{{.TableName}}ByIDRequest {
|
||||
uint64 id =1 [(validate.rules).uint64.gte = 1, (tagger.tags) = "uri:\"id\"" ];
|
||||
// getTableByIDRequestFieldCode
|
||||
}
|
||||
|
||||
message Get{{.TableName}}ByIDReply {
|
||||
|
@ -455,7 +455,7 @@ message Get{{.TableName}}ByConditionReply {
|
|||
}
|
||||
|
||||
message List{{.TableName}}ByIDsRequest {
|
||||
repeated uint64 ids = 1 [(validate.rules).repeated.min_items = 1];
|
||||
// getTableByIDsRequestFieldCode
|
||||
}
|
||||
|
||||
message List{{.TableName}}ByIDsReply {
|
||||
|
@ -463,8 +463,8 @@ message List{{.TableName}}ByIDsReply {
|
|||
}
|
||||
|
||||
message List{{.TableName}}ByLastIDRequest {
|
||||
uint64 lastID = 1 [(tagger.tags) = "form:\"lastID\""]; // last id
|
||||
uint32 limit = 2 [(validate.rules).uint32.gt = 0, (tagger.tags) = "form:\"limit\""]; // limit size per page
|
||||
// listTableByLastIDRequestFieldCode
|
||||
uint32 limit = 2 [(validate.rules).uint32.gt = 0, (tagger.tags) = "form:\"limit\""]; // limit size per page
|
||||
string sort = 3 [(tagger.tags) = "form:\"sort\""]; // sort by column name of table, default is -id, the - sign indicates descending order.
|
||||
}
|
||||
|
||||
|
|
|
@ -52,6 +52,9 @@ func (a *Args) checkValid() error {
|
|||
return fmt.Errorf("sqlite db file %s not found in local host", a.DBDsn)
|
||||
}
|
||||
}
|
||||
if a.fieldTypes == nil {
|
||||
a.fieldTypes = make(map[string]string)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -87,13 +90,21 @@ func getSQL(args *Args) (string, map[string]string, error) {
|
|||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
sqlStr, pgTypeMap := parser.ConvertToMysqlTable(args.DBTable, fields)
|
||||
sqlStr, pgTypeMap := parser.ConvertToSQLByPgFields(args.DBTable, fields)
|
||||
return sqlStr, pgTypeMap, nil
|
||||
case parser.DBDriverSqlite:
|
||||
sqlStr, err := parser.GetSqliteTableInfo(args.DBDsn, args.DBTable)
|
||||
return sqlStr, nil, err
|
||||
case parser.DBDriverMongodb:
|
||||
dsn := utils.AdaptiveMongodbDsn(args.DBDsn)
|
||||
fields, err := parser.GetMongodbTableInfo(dsn, args.DBTable)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
sqlStr, mongoTypeMap := parser.ConvertToSQLByMgoFields(args.DBTable, fields)
|
||||
return sqlStr, mongoTypeMap, nil
|
||||
default:
|
||||
return "", nil, fmt.Errorf("unsupported database driver: " + dbDriverName)
|
||||
return "", nil, fmt.Errorf("getsql error, unsupported database driver: " + dbDriverName)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,21 @@ func AdaptivePostgresqlDsn(dsn string) string {
|
|||
u.Hostname(), u.Port(), u.User.Username(), password, u.Path[1:], strings.Join(ss, " "))
|
||||
}
|
||||
|
||||
// AdaptiveSqlite adaptive sqlite
|
||||
func AdaptiveSqlite(dbFile string) string {
|
||||
// todo convert to absolute path
|
||||
return dbFile
|
||||
}
|
||||
|
||||
// AdaptiveMongodbDsn adaptive mongodb dsn
|
||||
func AdaptiveMongodbDsn(dsn string) string {
|
||||
if !strings.Contains(dsn, "mongodb://") {
|
||||
dsn = "mongodb://" + dsn
|
||||
}
|
||||
|
||||
return deleteBrackets(dsn)
|
||||
}
|
||||
|
||||
func deleteBrackets(str string) string {
|
||||
start := strings.Index(str, "@(")
|
||||
end := strings.LastIndex(str, ")/")
|
||||
|
|
|
@ -34,3 +34,17 @@ func TestAdaptiveMysqlDsn(t *testing.T) {
|
|||
t.Log(dsn)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdaptiveMongodbDsn(t *testing.T) {
|
||||
mongoDsns := []string{
|
||||
"root:123456@192.168.3.37:27017/account",
|
||||
"root:123456@(192.168.3.37:27017)/account?connectTimeoutMS=15000",
|
||||
|
||||
"mongodb://root:123456@192.168.3.37:27017/account",
|
||||
"mongodb://root:123456@(192.168.3.37:27017)/account?connectTimeoutMS=15000",
|
||||
}
|
||||
for _, v := range mongoDsns {
|
||||
dsn := AdaptiveMongodbDsn(v)
|
||||
t.Log(dsn)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue