From 66131512883dbc11e9f2168002ee18d4c777764a Mon Sep 17 00:00:00 2001 From: dandan <1033719135@qq.com> Date: Sun, 27 Oct 2024 17:54:21 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=B8=80=E4=BA=9B=E6=96=B0?= =?UTF-8?q?=E6=8C=87=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/config/config.go | 1 + app/http/kernel.go | 8 + app/http/module/demo/model.go | 10 + app/http/register.go | 12 + app/http/route.go | 1 + app/provider/demo/contract.go | 31 +++ config/development/database.yaml | 24 +- framework/command/cron.go | 2 +- framework/command/database.go | 144 +++++++++++ framework/command/kernel.go | 1 + framework/command/provider.go | 246 ++++++++++++++----- framework/contract/type_register.go | 10 + framework/provider/type_register/provider.go | 29 +++ framework/provider/type_register/serivce.go | 30 +++ main.go | 2 + 15 files changed, 486 insertions(+), 65 deletions(-) create mode 100644 app/config/config.go create mode 100644 app/http/register.go create mode 100644 framework/command/database.go create mode 100644 framework/contract/type_register.go create mode 100644 framework/provider/type_register/provider.go create mode 100644 framework/provider/type_register/serivce.go diff --git a/app/config/config.go b/app/config/config.go new file mode 100644 index 0000000..d912156 --- /dev/null +++ b/app/config/config.go @@ -0,0 +1 @@ +package config diff --git a/app/http/kernel.go b/app/http/kernel.go index 6474305..5bd5f27 100644 --- a/app/http/kernel.go +++ b/app/http/kernel.go @@ -1,6 +1,7 @@ package http import ( + "github.com/Superdanda/hade/app/provider/database_connect" "github.com/Superdanda/hade/framework" "github.com/Superdanda/hade/framework/gin" ) @@ -15,5 +16,12 @@ func NewHttpEngine(container *framework.HadeContainer) (*gin.Engine, error) { // 业务绑定路由操作 Routes(r) // 返回绑定路由后的Web引擎 + + // 对业务模型进行注册,通过注册名获取业务模型类型信息 + TypeRegister(container) + + //绑定服务 + container.Bind(&database_connect.DatabaseConnectProvider{}) + return r, nil } diff --git a/app/http/module/demo/model.go b/app/http/module/demo/model.go index db31a5b..cf105e3 100644 --- a/app/http/module/demo/model.go +++ b/app/http/module/demo/model.go @@ -2,9 +2,19 @@ package demo import ( "database/sql" + "github.com/Superdanda/hade/app/provider/demo" + "github.com/Superdanda/hade/framework/contract" "time" ) +func init() { +} + +func TypeRegister(typeRegister contract.TypeRegisterService) { + typeRegister.Register("UserThree", demo.UserThree{}) + typeRegister.Register("UserTwo", demo.UserTwo{}) +} + type UserModel struct { UserId int Name string diff --git a/app/http/register.go b/app/http/register.go new file mode 100644 index 0000000..7193053 --- /dev/null +++ b/app/http/register.go @@ -0,0 +1,12 @@ +package http + +import ( + "github.com/Superdanda/hade/app/http/module/demo" + "github.com/Superdanda/hade/framework" + "github.com/Superdanda/hade/framework/contract" +) + +func TypeRegister(container framework.Container) { + typeRegister := container.MustMake(contract.TypeRegisterKey).(contract.TypeRegisterService) + demo.TypeRegister(typeRegister) +} diff --git a/app/http/route.go b/app/http/route.go index 10d70ce..be80f18 100644 --- a/app/http/route.go +++ b/app/http/route.go @@ -22,6 +22,7 @@ func Routes(core *gin.Engine) { core.Use(static.Serve("/", static.LocalFile("./dist", false))) err := demo.Register(core) + if err != nil { return } diff --git a/app/provider/demo/contract.go b/app/provider/demo/contract.go index bed5a16..599d9d3 100644 --- a/app/provider/demo/contract.go +++ b/app/provider/demo/contract.go @@ -1 +1,32 @@ package demo + +import ( + "database/sql" + "time" +) + +// User is gorm model +type UserTwo struct { + ID uint + Name string + Email *string + Age uint8 + Birthday *time.Time + MemberNumber sql.NullString + ActivatedAt sql.NullTime + CreatedAt time.Time + UpdatedAt time.Time +} + +// User is gorm model +type UserThree struct { + ID uint + Name string + Email *string + Age uint8 + Birthday *time.Time + MemberNumber sql.NullString + ActivatedAt sql.NullTime + CreatedAt time.Time + UpdatedAt time.Time +} diff --git a/config/development/database.yaml b/config/development/database.yaml index cf0d39b..a094028 100644 --- a/config/development/database.yaml +++ b/config/development/database.yaml @@ -4,12 +4,27 @@ conn_max_lifetime: 1h # 通用配置,连接数最大生命周期 protocol: tcp # 通用配置,传输协议 loc: Local # 通用配置,时区 -default: +local: + driver: mysql # 连接驱动 + dsn: "" # dsn,如果设置了dsn, 以下的所有设置都不生效 + host: 127.0.0.1 # ip地址 + port: 3306 # 端口 + database: bbs # 数据库 + username: root # 用户名 + password: "123456" # 密码 + charset: utf8mb4 # 字符集 + collation: utf8mb4_unicode_ci # 字符序 + timeout: 10s # 连接超时 + read_timeout: 2s # 读超时 + write_timeout: 2s # 写超时 + parse_time: true # 是否解析时间 + +ali: driver: mysql # 连接驱动 dsn: "" # dsn,如果设置了dsn, 以下的所有设置都不生效 host: 47.97.21.20 # ip地址 port: 3306 # 端口 - database: questionnaire # 数据库 + database: bbs # 数据库 username: root # 用户名 password: "kerowsqw34" # 密码 charset: utf8mb4 # 字符集 @@ -18,6 +33,7 @@ default: read_timeout: 2s # 读超时 write_timeout: 2s # 写超时 parse_time: true # 是否解析时间 - protocol: tcp # 传输协议 - loc: Local # 时区 + +sync: + filePath: /app/provider \ No newline at end of file diff --git a/framework/command/cron.go b/framework/command/cron.go index 5483f56..2f729f5 100644 --- a/framework/command/cron.go +++ b/framework/command/cron.go @@ -35,7 +35,7 @@ var cronCommand = &cobra.Command{ Short: "定时任务控制命令", Long: "定时任务控制命令,包含展示、重启、启动、常驻状态、停止等命令", RunE: func(c *cobra.Command, args []string) error { - if len(args) != 0 { + if len(args) == 0 { c.Help() } return nil diff --git a/framework/command/database.go b/framework/command/database.go new file mode 100644 index 0000000..8dfcb0d --- /dev/null +++ b/framework/command/database.go @@ -0,0 +1,144 @@ +package command + +import ( + "errors" + "fmt" + "github.com/Superdanda/hade/framework" + "github.com/Superdanda/hade/framework/cobra" + "github.com/Superdanda/hade/framework/contract" + "github.com/Superdanda/hade/framework/provider/orm" + "go/ast" + "go/parser" + "go/token" + "gorm.io/gorm" + "os" + "path/filepath" + "reflect" +) + +func initDatabaseCommand() *cobra.Command { + databaseCommand.AddCommand(databaseModelSyncCommand) + return databaseCommand +} + +var databaseCommand = &cobra.Command{ + Use: "database", + Short: "数据库相关命令", + Long: "操控数据库相关命令,同步数据模型到数据库", + RunE: func(c *cobra.Command, args []string) error { + if len(args) == 0 { + c.Help() + } + return nil + }, +} + +var databaseModelSyncCommand = &cobra.Command{ + Use: "modelSync", + Short: "同步数据模型到数据库", + RunE: func(c *cobra.Command, args []string) error { + if len(args) != 1 { + fmt.Println("请输入数据库连接名称") + } + container := c.GetContainer() + appService := container.MustMake(contract.AppKey).(contract.App) + configService := container.MustMake(contract.ConfigKey).(contract.Config) + syncPathFolder := filepath.Join(appService.BaseFolder(), configService.GetString("database.sync.filePath")) + + ormService := container.MustMake(contract.ORMKey).(contract.ORMService) + db, err := ormService.GetDB(orm.WithConfigPath("database." + args[0])) + + if err != nil { + return err + } + err = AutoMigrateStructs(db, syncPathFolder, container) + if err != nil { + return err + } + return nil + }, +} + +// ScanStructsInPackage 扫描给定包路径下的所有结构体 +func ScanStructsInPackage(pkgPath string, container framework.Container) ([]interface{}, error) { + var structs []interface{} + + // 遍历包路径下的所有 Go 文件 + err := filepath.Walk(pkgPath, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + // 只处理 .go 文件 + if filepath.Ext(path) != ".go" { + return nil + } + + // 解析 Go 文件,构建 AST + fset := token.NewFileSet() + node, err := parser.ParseFile(fset, path, nil, parser.AllErrors) + if err != nil { + return err + } + + // 遍历 AST,找到所有类型声明 + for _, decl := range node.Decls { + genDecl, ok := decl.(*ast.GenDecl) + if !ok || genDecl.Tok != token.TYPE { + continue + } + + // 遍历所有类型声明,找到结构体类型 + for _, spec := range genDecl.Specs { + typeSpec, ok := spec.(*ast.TypeSpec) + if !ok { + continue + } + + // 检查是否为结构体类型 + _, ok = typeSpec.Type.(*ast.StructType) + if ok { + // 创建结构体的零值并添加到结果中 + structName := typeSpec.Name.Name + instance, err := createStructInstance(structName, container) + if err != nil { + fmt.Println(err.Error()) + } + if instance != nil { + structs = append(structs, instance) + } + } + } + } + return nil + }) + return structs, err +} + +// createStructInstance 使用反射创建结构体的实例 +func createStructInstance(structName string, container framework.Container) (interface{}, error) { + typeRegister := container.MustMake(contract.TypeRegisterKey).(contract.TypeRegisterService) + // 假设结构体已导入,使用反射获取类型 + structType, b := typeRegister.GetType(structName) + if b { + instance := reflect.New(structType).Interface() + return instance, nil + } + return nil, errors.New(structName + "类型没有注册") +} + +func AutoMigrateStructs(db *gorm.DB, pkgPath string, container framework.Container) error { + // 扫描包路径下的所有结构体 + structs, err := ScanStructsInPackage(pkgPath, container) + if err != nil { + return err + } + // 使用 GORM 进行自动迁移 + for _, model := range structs { + if err := db.AutoMigrate(model); err != nil { + return fmt.Errorf("自动迁移失败: %v", err) + } + fmt.Printf("成功迁移模型:%T\n", model) + } + return nil +} diff --git a/framework/command/kernel.go b/framework/command/kernel.go index 45d6190..b90db08 100644 --- a/framework/command/kernel.go +++ b/framework/command/kernel.go @@ -20,6 +20,7 @@ func AddKernelCommands(root *cobra.Command) { root.AddCommand(initNewCommand()) root.AddCommand(initSwaggerCommand()) root.AddCommand(initDeployCommand()) + root.AddCommand(initDatabaseCommand()) } // 封装通用的命令执行器 diff --git a/framework/command/provider.go b/framework/command/provider.go index 037fbc1..1d6ea76 100644 --- a/framework/command/provider.go +++ b/framework/command/provider.go @@ -3,15 +3,15 @@ package command import ( "fmt" "github.com/AlecAivazis/survey/v2" - "os" - "path/filepath" - "strings" - "text/template" - "github.com/Superdanda/hade/framework" "github.com/Superdanda/hade/framework/cobra" "github.com/Superdanda/hade/framework/contract" "github.com/Superdanda/hade/framework/util" + "os" + "path/filepath" + "reflect" + "strings" + "text/template" "github.com/jianfengye/collection" "github.com/pkg/errors" @@ -56,7 +56,7 @@ var providerCreateCommand = &cobra.Command{ container := c.GetContainer() fmt.Println("创建一个服务") var name, folder string - var interfaceNames []string + interfaceNames := &RouteNode{} { prompt := &survey.Input{ Message: "请输入服务名称(服务凭证):", @@ -101,10 +101,10 @@ var providerCreateCommand = &cobra.Command{ return nil } - // Keep asking for interface names until the user presses Enter to stop + // 收集用户输入并填充嵌套映射 for { prompt := &survey.Input{ - Message: "请输入接口名称(直接按回车结束输入):", + Message: "请输入接口路径(格式:/user/login,直接按回车结束输入):", } var input string @@ -113,23 +113,27 @@ var providerCreateCommand = &cobra.Command{ return err } - // If the user presses Enter without input, break the loop + // 如果用户直接按回车,结束输入 if strings.TrimSpace(input) == "" { fmt.Println("接口输入结束") break } - // Add the valid interface name to the slice - interfaceNames = append(interfaceNames, input) + // 解析输入的路径为路径部分 + pathParts := strings.Split(strings.TrimPrefix(input, "/"), "/") + if len(pathParts) == 0 { + fmt.Println("路径格式错误,请输入正确格式:/节点/接口") + continue + } + + // 将路径部分插入到路由树中 + insertIntoRouteTree(pathParts, []string{}, interfaceNames) fmt.Printf("已添加接口:%s\n", input) } + interfaceNames.NeedExtra = false - // Print all the interface names added by the user - if len(interfaceNames) > 0 { - fmt.Println("已添加的接口名称:", interfaceNames) - } else { - fmt.Println("未添加任何接口") - } + // 打印所有添加的接口(测试用) + printRouteTree(interfaceNames, 0) // 开始创建文件 if err := os.Mkdir(filepath.Join(pFolder, folder), 0700); err != nil { @@ -141,9 +145,30 @@ var providerCreateCommand = &cobra.Command{ "appName": config.GetAppName(), "packageName": name, "interfaces": interfaceNames, + "structName": name, } // 创建title这个模版方法 - funcs := template.FuncMap{"title": strings.Title} + funcs := template.FuncMap{ + "title": strings.Title, + "dict": func(values ...interface{}) (map[string]interface{}, error) { + if len(values)%2 != 0 { + return nil, fmt.Errorf("invalid dict call: missing key or value") + } + dict := make(map[string]interface{}, len(values)/2) + for i := 0; i < len(values); i += 2 { + key, ok := values[i].(string) + if !ok { + return nil, fmt.Errorf("dict keys must be strings") + } + dict[key] = values[i+1] + } + return dict, nil + }, + "len": func(v interface{}) int { + return reflect.ValueOf(v).Len() + }, + } + { // 创建contract.go file := filepath.Join(pFolder, folder, "contract.go") @@ -184,6 +209,10 @@ var providerCreateCommand = &cobra.Command{ } } + if interfaceNames.Children == nil || len(interfaceNames.Children) == 0 { + return nil + } + moduleFolder := app.HttpModuleFolder() pModuleFolder := filepath.Join(moduleFolder, name) util.EnsureDir(pModuleFolder) @@ -192,12 +221,13 @@ var providerCreateCommand = &cobra.Command{ // 创建api 我呢见 { - // 创建api.go + // 创建 api.go file := filepath.Join(pModuleFolder, "api.go") f, err := os.Create(file) if err != nil { return err } + data["interfaces"] = interfaceNames // 传递嵌套的接口名称映射 t := template.Must(template.New("api").Funcs(funcs).Parse(apiTmp)) if err := t.Execute(f, data); err != nil { return err @@ -206,35 +236,15 @@ var providerCreateCommand = &cobra.Command{ // 创建api_controller文件 { - tmpl, err := template.New("controller").Funcs(funcs).Parse(apiControllerTmp) - if err != nil { - fmt.Println("Failed to parse template:", err) + tmpl := template.Must(template.New("controller").Funcs(funcs).Parse(apiControllerTmp)) + data["packageName"] = name + data["structName"] = name + + // 递归生成控制器文件 + if err := generateControllers(interfaceNames, []string{}, tmpl, data, pModuleFolder); err != nil { + fmt.Println("生成控制器失败:", err) return err } - // Generate a file for each interface - for _, interfaceName := range interfaceNames { - data := map[string]interface{}{ - "packageName": name, - "appName": config.GetAppName(), - "interfaceName": interfaceName, - } - - // Create the output file - filePath := filepath.Join(pModuleFolder, "api_"+interfaceName+".go") - file, err := os.Create(filePath) - if err != nil { - fmt.Println("Failed to create file:", err) - return errors.New("Failed to create file: " + filePath) - } - defer file.Close() - - // Execute the template with the data - if err := tmpl.Execute(file, data); err != nil { - fmt.Println("Failed to execute template:", err) - return errors.New("Failed to execute template: " + filePath) - } - fmt.Printf("Generated file: %s\n", filePath) - } } //创建 dto 文件 @@ -271,6 +281,93 @@ var providerCreateCommand = &cobra.Command{ }, } +func generateControllers(node *RouteNode, pathParts []string, tmpl *template.Template, data map[string]interface{}, moduleFolder string) error { + // 更新路径部分 + newPathParts := append(pathParts, node.Path) + + // 如果当前节点需要生成处理函数 + if node.NeedExtra { + data["interfaceName"] = strings.Join(newPathParts, "_") + data["methodName"] = node.HandlerName + + // 生成文件名,例如 api_user_edit_auto.go + fileName := "api" + strings.Join(newPathParts, "_") + ".go" + filePath := filepath.Join(moduleFolder, fileName) + + // 创建控制器文件 + file, err := os.Create(filePath) + if err != nil { + return fmt.Errorf("创建文件失败: %s", filePath) + } + defer file.Close() + + // 渲染模板 + if err := tmpl.Execute(file, data); err != nil { + return fmt.Errorf("渲染模板失败: %s", filePath) + } + fmt.Printf("生成接口控制器文件: %s\n", filePath) + } + + // 递归处理子节点 + for _, child := range node.Children { + if err := generateControllers(child, newPathParts, tmpl, data, moduleFolder); err != nil { + return err + } + } + return nil +} + +func insertIntoRouteTree(pathParts []string, fullPathParts []string, currentNode *RouteNode) { + if len(pathParts) == 0 { + currentNode.NeedExtra = true + return + } + + // 获取当前路径部分 + part := pathParts[0] + fullPathParts = append(fullPathParts, part) + + // 查找是否已存在该路径部分的子节点 + var child *RouteNode + for _, node := range currentNode.Children { + if node.Path == part { + child = node + break + } + } + + // 如果子节点不存在,则创建新的子节点 + if child == nil { + handlerName := "" + for _, v := range fullPathParts { + handlerName += strings.Title(v) + } + child = &RouteNode{ + Path: part, + HandlerName: handlerName, + } + currentNode.Children = append(currentNode.Children, child) + } + + // 递归处理剩余路径部分 + insertIntoRouteTree(pathParts[1:], fullPathParts, child) +} + +func printRouteTree(node *RouteNode, level int) { + indent := strings.Repeat(" ", level) + fmt.Printf("%s- %s\n", indent, node.Path) + for _, child := range node.Children { + printRouteTree(child, level+1) + } +} + +type RouteNode struct { + Path string // 路由路径 + Children []*RouteNode // 子路由节点列表 + NeedExtra bool // 是否需要额外生成接口 + HandlerName string +} + var contractTmp = `package {{.packageName}} const {{.packageName | title}}Key = "{{.appName}}:{{.packageName}}" @@ -279,6 +376,8 @@ type Service interface { // 请在这里定义你的方法 Foo() string } + +type {{.packageName | title}} struct {} ` var providerTmp = `package {{.packageName}} @@ -335,37 +434,64 @@ func (s *{{.packageName | title}}Service) Foo() string { var apiTmp = `package {{.packageName}} import ( - "github.com/Superdanda/hade/framework/gin" + "github.com/Superdanda/hade/framework/gin" ) type {{.packageName | title}}Api struct{} +// 注册路由 func RegisterRoutes(r *gin.Engine) error { - api := {{.packageName | title}}Api{} + + api := {{.packageName | title}}Api{} if !r.IsBind({{.packageName}}.{{.packageName | title}}Key) { r.Bind(&{{.packageName}}.{{.packageName | title}}Provider{}) } - {{.packageName}}Group := r.Group("/{{.packageName}}") - { - {{range .interfaces}} - // {{.}} route - {{$.packageName}}Group.POST("/{{.}}", api.{{. | title}}) - {{end}} - } - return nil + {{template "registerRoutes" dict "node" .interfaces "groupVar" "r" "apiVar" "api"}} + + return nil } + +{{- define "registerRoutes"}} +{{- $node := .node -}} +{{- $groupVar := .groupVar -}} +{{- $apiVar := .apiVar -}} + +{{- if ne $node.Path "root" -}} + {{- $hasChildren := gt (len $node.Children) 0 -}} + {{- $groupName := (printf "%sGroup" $node.Path) -}} + {{- if $hasChildren -}} + {{$groupName}} := {{$groupVar}}.Group("/{{$node.Path}}") + { + {{- if $node.NeedExtra -}} + {{$groupName}}.POST("/", {{$apiVar}}.{{ $node.HandlerName }}) + {{- end}} + {{range $child := $node.Children}} + {{template "registerRoutes" dict "node" $child "groupVar" $groupName "apiVar" $apiVar}} + {{- end}} + } + {{- else}} + {{- if $node.NeedExtra}} + {{$groupVar}}.POST("/{{$node.Path}}", {{$apiVar}}.{{ $node.HandlerName }}) + {{- end}} + {{end}} +{{- else}} + {{range $child := $node.Children}} + {{template "registerRoutes" dict "node" $child "groupVar" $groupVar "apiVar" $apiVar}} + {{- end}} +{{- end}} +{{- end}} ` var apiControllerTmp = `package {{.packageName}} import ( - "github.com/Superdanda/hade/framework/gin" + "github.com/Superdanda/hade/framework/gin" ) -// {{.interfaceName | title}} handler -func (api *{{.packageName | title}}Api) {{.interfaceName | title}}(c *gin.Context) { - // TODO: Add logic for {{.interfaceName}} +// {{.methodName}} handler +func (api *{{.structName | title}}Api) {{.methodName}}(c *gin.Context) { + // TODO: Implement {{.methodName}} } ` diff --git a/framework/contract/type_register.go b/framework/contract/type_register.go new file mode 100644 index 0000000..7627351 --- /dev/null +++ b/framework/contract/type_register.go @@ -0,0 +1,10 @@ +package contract + +import "reflect" + +const TypeRegisterKey = "hade:typeRegister" + +type TypeRegisterService interface { + Register(typeName string, instance interface{}) + GetType(typeName string) (reflect.Type, bool) +} diff --git a/framework/provider/type_register/provider.go b/framework/provider/type_register/provider.go new file mode 100644 index 0000000..70ea1db --- /dev/null +++ b/framework/provider/type_register/provider.go @@ -0,0 +1,29 @@ +package type_register + +import ( + "github.com/Superdanda/hade/framework" + "github.com/Superdanda/hade/framework/contract" +) + +type TypeRegisterProvider struct { +} + +func (t TypeRegisterProvider) Register(container framework.Container) framework.NewInstance { + return NewHadeTypeRegisterService +} + +func (t TypeRegisterProvider) Boot(container framework.Container) error { + return nil +} + +func (t TypeRegisterProvider) IsDefer() bool { + return false +} + +func (t TypeRegisterProvider) Params(container framework.Container) []interface{} { + return []interface{}{container} +} + +func (t TypeRegisterProvider) Name() string { + return contract.TypeRegisterKey +} diff --git a/framework/provider/type_register/serivce.go b/framework/provider/type_register/serivce.go new file mode 100644 index 0000000..616a4e2 --- /dev/null +++ b/framework/provider/type_register/serivce.go @@ -0,0 +1,30 @@ +package type_register + +import ( + "reflect" + "sync" +) + +type HadeTypeRegisterService struct { + mu sync.RWMutex + types map[string]reflect.Type +} + +func NewHadeTypeRegisterService(params ...interface{}) (interface{}, error) { + return &HadeTypeRegisterService{ + types: make(map[string]reflect.Type), + }, nil +} + +func (r *HadeTypeRegisterService) Register(typeName string, instance interface{}) { + r.mu.Lock() + defer r.mu.Unlock() + r.types[typeName] = reflect.TypeOf(instance) +} + +func (r *HadeTypeRegisterService) GetType(typeName string) (reflect.Type, bool) { + r.mu.RLock() + defer r.mu.RUnlock() + typ, exists := r.types[typeName] + return typ, exists +} diff --git a/main.go b/main.go index 74f5f61..98c4ba8 100644 --- a/main.go +++ b/main.go @@ -14,6 +14,7 @@ import ( "github.com/Superdanda/hade/framework/provider/orm" "github.com/Superdanda/hade/framework/provider/redis" "github.com/Superdanda/hade/framework/provider/ssh" + "github.com/Superdanda/hade/framework/provider/type_register" ) func main() { @@ -32,6 +33,7 @@ func main() { container.Bind(&redis.RedisProvider{}) container.Bind(&cache.HadeCacheProvider{}) container.Bind(&ssh.SSHProvider{}) + container.Bind(&type_register.TypeRegisterProvider{}) // 将HTTP引擎初始化,并且作为服务提供者绑定到服务容器中 if engine, err := http.NewHttpEngine(container); err == nil {