diff --git a/README.MD b/README.MD index f4c4c887b..4f1d1f23c 100644 --- a/README.MD +++ b/README.MD @@ -36,10 +36,10 @@ golang版本 >= 1.9.2 1. 提供了对基本数据类型的并发安全封装,提供了常用的数据结构容器; 1. 支持Go变量/Json/Xml/Yml/Toml任意数据格式之间的相互转换及创建; 1. 强大的数据库ORM,支持应用层级的集群管理、读写分离、负载均衡,查询缓存、方法及链式ORM操作; -1. 更多特点请查阅框架[手册](http://gf.johng.cn)和[源码](https://godoc.org/github.com/johng-cn/gf); +1. 更多特点请查阅框架[手册](https://gfer.me)和[源码](https://godoc.org/github.com/johng-cn/gf); # 文档 -GoFrame开发文档:[http://gf.johng.cn](http://gf.johng.cn) +GoFrame开发文档:[gfer.me](https://gfer.me) # 使用 @@ -178,4 +178,4 @@ if tx, err := db.Begin(); err == nil { ... -更多特性及示例请查看官方开发文档:[gf.johng.cn](http://gf.johng.cn) +更多特性及示例请查看官方开发文档:[gfer.me](https://gfer.me) diff --git a/TODO b/TODO index 7ba360abe..ae76a2dcc 100644 --- a/TODO +++ b/TODO @@ -45,6 +45,7 @@ gform参考 https://gohouse.github.io/gorose/dist/index.html 进行改进 完善gform配置管理说明,g.DB/Database和gdb.New的区别; + DONE: 1. gconv完善针对不同类型的判断,例如:尽量减少sprintf("%v", xxx)来执行string类型的转换; 2. ghttp.Server请求执行中增加服务退出的方法,不再执行后续操作; diff --git a/g/frame/gins/gins.go b/g/frame/gins/gins.go index 76d409407..6404848e6 100644 --- a/g/frame/gins/gins.go +++ b/g/frame/gins/gins.go @@ -12,6 +12,7 @@ import ( "gitee.com/johng/gf/g/os/gcfg" "gitee.com/johng/gf/g/os/gcmd" "gitee.com/johng/gf/g/os/genv" + "gitee.com/johng/gf/g/os/glog" "gitee.com/johng/gf/g/os/gview" "gitee.com/johng/gf/g/os/gfile" "gitee.com/johng/gf/g/container/gmap" @@ -125,7 +126,7 @@ func Database(name...string) *gdb.Db { db := instances.GetOrSetFuncLock(key, func() interface{} { m := config.GetMap("database") if m == nil { - panic(fmt.Sprintf(`incomplete configuration for database: "database" node not found in config file "%s"`, config.GetFilePath())) + glog.Errorfln(`incomplete configuration for database: "database" node not found in config file "%s"`, config.GetFilePath()) } for group, v := range m { cg := gdb.ConfigGroup{} @@ -181,7 +182,7 @@ func Database(name...string) *gdb.Db { if db, err := gdb.New(name...); err == nil { return db } else { - panic(err) + glog.Error(err) } return nil }) @@ -213,13 +214,13 @@ func Redis(name...string) *gredis.Redis { Pass : array[4], }) } else { - panic(fmt.Sprintf(`invalid redis node configuration: "%s"`, line)) + glog.Errorfln(`invalid redis node configuration: "%s"`, line) } } else { - panic(fmt.Sprintf(`configuration for redis not found for group "%s"`, group)) + glog.Errorfln(`configuration for redis not found for group "%s"`, group) } } else { - panic(fmt.Sprintf(`incomplete configuration for redis: "redis" node not found in config file "%s"`, config.GetFilePath())) + glog.Errorfln(`incomplete configuration for redis: "redis" node not found in config file "%s"`, config.GetFilePath()) } return nil }) diff --git a/g/g_func.go b/g/g_func.go index bef7e7fd5..78459c0e0 100644 --- a/g/g_func.go +++ b/g/g_func.go @@ -33,16 +33,6 @@ func Wait() { ghttp.Wait() } -// 是否显示调试信息 -func SetDebug(debug bool) { - glog.SetDebug(debug) -} - -// 设置日志的显示等级 -func SetLogLevel(level int) { - glog.SetLevel(level) -} - // 打印变量 func Dump(i...interface{}) { gutil.Dump(i...) diff --git a/g/g_logger.go b/g/g_logger.go index 6532bc601..ac9f1d6cf 100644 --- a/g/g_logger.go +++ b/g/g_logger.go @@ -6,3 +6,19 @@ package g +import "gitee.com/johng/gf/g/os/glog" + +// 是否显示调试信息 +func SetDebug(debug bool) { + glog.SetDebug(debug) +} + +// 设置日志的显示等级 +func SetLogLevel(level int) { + glog.SetLevel(level) +} + +// 获取设置的日志显示等级 +func GetLogLevel() int { + return glog.GetLevel() +} \ No newline at end of file diff --git a/g/net/ghttp/ghttp_server.go b/g/net/ghttp/ghttp_server.go index 3ac8f322f..954864822 100644 --- a/g/net/ghttp/ghttp_server.go +++ b/g/net/ghttp/ghttp_server.go @@ -213,9 +213,14 @@ func (s *Server) Start() error { // 如果设置了静态文件目录,那么优先按照静态文件目录进行检索,其次是当前可执行文件工作目录; // 并且如果是开发环境,默认也会添加main包的源码目录路径做为二级检索。 if s.config.ServerRoot != "" { - s.paths.Set(s.config.ServerRoot) + if rp, err := s.paths.Set(s.config.ServerRoot); err != nil { + glog.Error("ghttp.SetServerRoot failed:", err.Error()) + return err + } else { + glog.Debug("ghttp.SetServerRoot:", rp) + } } - s.paths.Add(gfile.SelfDir()) + s.AddSearchPath(gfile.SelfDir()) if p := gfile.MainPkgPath(); p != "" && gfile.Exists(p) { s.paths.Add(p) } @@ -271,7 +276,7 @@ func (s *Server) Start() error { // 打印展示路由表 func (s *Server) DumpRoutesMap() { - if s.config.DumpRouteMap { + if s.config.DumpRouteMap && len(s.routesMap) > 0 { // (等待一定时间后)当所有框架初始化信息打印完毕之后才打印路由表信息 gtime.SetTimeout(50*time.Millisecond, func() { glog.Header(false).Println(fmt.Sprintf("\n%s\n", s.GetRouteMap())) diff --git a/g/net/ghttp/ghttp_server_config.go b/g/net/ghttp/ghttp_server_config.go index 3b84a7652..ae3bd90ed 100644 --- a/g/net/ghttp/ghttp_server_config.go +++ b/g/net/ghttp/ghttp_server_config.go @@ -303,7 +303,13 @@ func (s *Server) SetDumpRouteMap(enabled bool) { // 添加静态文件搜索目录,必须给定目录的绝对路径 func (s *Server) AddSearchPath(path string) error { - return s.paths.Add(path) + if rp, err := s.paths.Add(path); err != nil { + glog.Error("ghttp.AddSearchPath failed:", err.Error()) + return err + } else { + glog.Debug("ghttp.AddSearchPath:", rp) + } + return nil } // 获取 diff --git a/g/net/ghttp/ghttp_server_service_controller.go b/g/net/ghttp/ghttp_server_service_controller.go index 4455f4697..e20fddb94 100644 --- a/g/net/ghttp/ghttp_server_service_controller.go +++ b/g/net/ghttp/ghttp_server_service_controller.go @@ -9,6 +9,7 @@ package ghttp import ( "errors" + "gitee.com/johng/gf/g/os/glog" "strings" "reflect" "fmt" @@ -42,6 +43,14 @@ func (s *Server)BindController(pattern string, c Controller, methods...string) e if mname == "Init" || mname == "Shut" || mname == "Exit" { continue } + if _, ok := v.Method(i).Interface().(func()); !ok { + if methodMap != nil { + s := fmt.Sprintf(`invalid medthod definition "%s", while "func()" is required`, v.Method(i).Type().String()) + glog.Error(s) + return errors.New(s) + } + continue + } ctlName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "") if ctlName[0] == '*' { ctlName = fmt.Sprintf(`(%s)`, ctlName) @@ -82,9 +91,15 @@ func (s *Server)BindControllerMethod(pattern string, c Controller, method string t := v.Type() sname := t.Elem().Name() mname := strings.TrimSpace(method) - if !v.MethodByName(mname).IsValid() { + fval := v.MethodByName(mname) + if !fval.IsValid() { return errors.New("invalid method name:" + mname) } + if _, ok := fval.Interface().(func()); !ok { + s := fmt.Sprintf(`invalid medthod definition "%s", while "func()" is required`, fval.Type().String()) + glog.Error(s) + return errors.New(s) + } pkgPath := t.Elem().PkgPath() pkgName := gfile.Basename(pkgPath) ctlName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "") @@ -119,6 +134,11 @@ func (s *Server)BindControllerRest(pattern string, c Controller) error { if _, ok := s.methodsMap[method]; !ok { continue } + if _, ok := v.Method(i).Interface().(func()); !ok { + s := fmt.Sprintf(`invalid medthod definition "%s", while "func()" is required`, v.Method(i).Type().String()) + glog.Error(s) + return errors.New(s) + } pkgName := gfile.Basename(pkgPath) ctlName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "") if ctlName[0] == '*' { diff --git a/g/net/ghttp/ghttp_server_service_object.go b/g/net/ghttp/ghttp_server_service_object.go index 8603ab6f1..ef64df4a1 100644 --- a/g/net/ghttp/ghttp_server_service_object.go +++ b/g/net/ghttp/ghttp_server_service_object.go @@ -9,6 +9,7 @@ package ghttp import ( "errors" + "gitee.com/johng/gf/g/os/glog" "strings" "reflect" "fmt" @@ -48,6 +49,15 @@ func (s *Server)BindObject(pattern string, obj interface{}, methods...string) er if mname == "Init" || mname == "Shut" { continue } + faddr, ok := v.Method(i).Interface().(func(*Request)) + if !ok { + if methodMap != nil { + s := fmt.Sprintf(`invalid medthod definition "%s", while "func(*Request))" is required`, v.Method(i).Type().String()) + glog.Error(s) + return errors.New(s) + } + continue + } objName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "") if objName[0] == '*' { objName = fmt.Sprintf(`(%s)`, objName) @@ -58,7 +68,7 @@ func (s *Server)BindObject(pattern string, obj interface{}, methods...string) er rtype : gROUTE_REGISTER_OBJECT, ctype : nil, fname : "", - faddr : v.Method(i).Interface().(func(*Request)), + faddr : faddr, finit : finit, fshut : fshut, } @@ -76,7 +86,7 @@ func (s *Server)BindObject(pattern string, obj interface{}, methods...string) er rtype : gROUTE_REGISTER_OBJECT, ctype : nil, fname : "", - faddr : v.Method(i).Interface().(func(*Request)), + faddr : faddr, finit : finit, fshut : fshut, } @@ -97,6 +107,12 @@ func (s *Server)BindObjectMethod(pattern string, obj interface{}, method string) if !fval.IsValid() { return errors.New("invalid method name:" + mname) } + faddr, ok := fval.Interface().(func(*Request)) + if !ok { + s := fmt.Sprintf(`invalid medthod definition "%s", while "func(*Request)" is required`, fval.Type().String()) + glog.Error(s) + return errors.New(s) + } finit := (func(*Request))(nil) fshut := (func(*Request))(nil) if v.MethodByName("Init").IsValid() { @@ -117,7 +133,7 @@ func (s *Server)BindObjectMethod(pattern string, obj interface{}, method string) rtype : gROUTE_REGISTER_OBJECT, ctype : nil, fname : "", - faddr : fval.Interface().(func(*Request)), + faddr : faddr, finit : finit, fshut : fshut, } @@ -146,6 +162,12 @@ func (s *Server)BindObjectRest(pattern string, obj interface{}) error { if _, ok := s.methodsMap[method]; !ok { continue } + faddr, ok := v.Method(i).Interface().(func(*Request)) + if !ok { + s := fmt.Sprintf(`invalid medthod definition "%s", while "func()" is required`, v.Method(i).Type().String()) + glog.Error(s) + return errors.New(s) + } pkgName := gfile.Basename(pkgPath) objName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "") if objName[0] == '*' { @@ -157,7 +179,7 @@ func (s *Server)BindObjectRest(pattern string, obj interface{}) error { rtype : gROUTE_REGISTER_OBJECT, ctype : nil, fname : "", - faddr : v.Method(i).Interface().(func(*Request)), + faddr : faddr, finit : finit, fshut : fshut, } diff --git a/g/os/gcfg/gcfg.go b/g/os/gcfg/gcfg.go index a7e5094d4..08530608e 100644 --- a/g/os/gcfg/gcfg.go +++ b/g/os/gcfg/gcfg.go @@ -9,14 +9,14 @@ package gcfg import ( - "gitee.com/johng/gf/g/container/gvar" - "gitee.com/johng/gf/g/os/gspath" - "gitee.com/johng/gf/g/os/gfsnotify" - "gitee.com/johng/gf/g/container/gmap" - "gitee.com/johng/gf/g/encoding/gjson" - "gitee.com/johng/gf/g/container/gtype" "errors" + "gitee.com/johng/gf/g/container/gmap" + "gitee.com/johng/gf/g/container/gtype" + "gitee.com/johng/gf/g/container/gvar" + "gitee.com/johng/gf/g/encoding/gjson" + "gitee.com/johng/gf/g/os/gfsnotify" "gitee.com/johng/gf/g/os/glog" + "gitee.com/johng/gf/g/os/gspath" ) const ( @@ -37,14 +37,14 @@ func New(path string, file...string) *Config { if len(file) > 0 { name = file[0] } - s := gspath.New() - s.Set(path) - return &Config { + c := &Config { name : gtype.NewString(name), - paths : s, + paths : gspath.New(), jsons : gmap.NewStringInterfaceMap(), vc : gtype.NewBool(), } + c.SetPath(path) + return c } // 判断从哪个配置文件中获取内容,返回配置文件的绝对路径 @@ -58,12 +58,13 @@ func (c *Config) filePath(file...string) string { // 设置配置管理器的配置文件存放目录绝对路径 func (c *Config) SetPath(path string) error { - if err := c.paths.Set(path); err != nil { - glog.Error("gcfg.SetPath failed:", path, err) + if rp, err := c.paths.Set(path); err != nil { + glog.Error("gcfg.SetPath failed:", err.Error()) return err + } else { + c.jsons.Clear() + glog.Debug("gcfg.SetPath:", rp) } - c.jsons.Clear() - glog.Debug("gcfg.SetPath:", path) return nil } @@ -76,11 +77,12 @@ func (c *Config) SetViolenceCheck(check bool) { // 添加配置管理器的配置文件搜索路径 func (c *Config) AddPath(path string) error { - if err := c.paths.Add(path); err != nil { - glog.Debug("gcfg.AddPath failed:", path, err) + if rp, err := c.paths.Add(path); err != nil { + glog.Debug("gcfg.AddPath failed:", err.Error()) return err + } else { + glog.Debug("gcfg.AddPath:", rp) } - glog.Debug("gcfg.AddPath:", path) return nil } @@ -95,6 +97,7 @@ func (c *Config) GetFilePath(file...string) string { // 设置配置管理对象的默认文件名称 func (c *Config) SetFileName(name string) { + glog.Debug("gcfg.SetFileName:", name) c.name.Set(name) } diff --git a/g/os/gspath/gspath.go b/g/os/gspath/gspath.go index c0d25edee..38d0bde35 100644 --- a/g/os/gspath/gspath.go +++ b/g/os/gspath/gspath.go @@ -10,6 +10,7 @@ package gspath import ( "errors" + "fmt" "gitee.com/johng/gf/g/container/gmap" "gitee.com/johng/gf/g/os/gfile" "gitee.com/johng/gf/g/os/gfsnotify" @@ -32,46 +33,52 @@ func New () *SPath { } // 设置搜索路径,只保留当前设置项,其他搜索路径被清空 -func (sp *SPath) Set(path string) error { - r := gfile.RealPath(path) - if r == "" { - r = sp.Search(path) - if r == "" { - r = gfile.RealPath(gfile.Pwd() + gfile.Separator + path) +func (sp *SPath) Set(path string) (realpath string, err error) { + realpath = gfile.RealPath(path) + if realpath == "" { + realpath = sp.Search(path) + if realpath == "" { + realpath = gfile.RealPath(gfile.Pwd() + gfile.Separator + path) } } - if r != "" && gfile.IsDir(r) { - r = strings.TrimRight(r, gfile.Separator) + if realpath == "" { + return realpath, errors.New(fmt.Sprintf(`path "%s" does not exist`, path)) + } + if realpath != "" && gfile.IsDir(realpath) { + realpath = strings.TrimRight(realpath, gfile.Separator) sp.mu.Lock() - sp.paths = []string{r} + sp.paths = []string{realpath} sp.mu.Unlock() sp.cache.Clear() //glog.Debug("gspath.SetPath:", r) - return nil + return realpath, nil } //glog.Warning("gspath.SetPath failed:", path) - return errors.New("invalid path:" + path) + return realpath, errors.New("invalid path:" + path) } // 添加搜索路径 -func (sp *SPath) Add(path string) error { - r := gfile.RealPath(path) - if r == "" { - r = sp.Search(path) - if r == "" { - r = gfile.RealPath(gfile.Pwd() + gfile.Separator + path) +func (sp *SPath) Add(path string) (realpath string, err error) { + realpath = gfile.RealPath(path) + if realpath == "" { + realpath = sp.Search(path) + if realpath == "" { + realpath = gfile.RealPath(gfile.Pwd() + gfile.Separator + path) } } - if r != "" && gfile.IsDir(r) { - r = strings.TrimRight(r, gfile.Separator) + if realpath == "" { + return realpath, errors.New(fmt.Sprintf(`path "%s" does not exist`, path)) + } + if realpath != "" && gfile.IsDir(realpath) { + realpath = strings.TrimRight(realpath, gfile.Separator) sp.mu.Lock() - sp.paths = append(sp.paths, r) + sp.paths = append(sp.paths, realpath) sp.mu.Unlock() //glog.Debug("gspath.Add:", r) - return nil + return realpath, nil } //glog.Warning("gspath.Add failed:", path) - return errors.New("invalid path:" + path) + return realpath, errors.New("invalid path:" + path) } // 按照优先级搜索文件,返回搜索到的文件绝对路径,找不到该文件时,返回空字符串 diff --git a/g/os/gtime/gtime.go b/g/os/gtime/gtime.go index 0b8647f8e..cc1f6d619 100644 --- a/g/os/gtime/gtime.go +++ b/g/os/gtime/gtime.go @@ -25,9 +25,10 @@ const ( // "2018-02-09 20:46:17.897", // "2018-02-09T20:46:17Z", // "2018-02-09 20:46:17", + // "2018/10/31 - 16:38:46" // "2018-02-09", // 日期连接符号支持'-'或者'/' - TIME_REAGEX_PATTERN = `(\d{2,4}[-/]\d{2}[-/]\d{2})[\sT]{0,1}(\d{0,2}:{0,1}\d{0,2}:{0,1}\d{0,2}){0,1}\.{0,1}(\d{0,9})([\sZ]{0,1})([\+-]{0,1})([:\d]*)` + TIME_REAGEX_PATTERN = `(\d{2,4}[-/]\d{2}[-/]\d{2})[\sT-]*(\d{0,2}:{0,1}\d{0,2}:{0,1}\d{0,2}){0,1}\.{0,1}(\d{0,9})([\sZ]{0,1})([\+-]{0,1})([:\d]*)` ) var ( diff --git a/g/os/gview/gview.go b/g/os/gview/gview.go index 7a16c80d6..76852c9cf 100644 --- a/g/os/gview/gview.go +++ b/g/os/gview/gview.go @@ -8,7 +8,9 @@ package gview import ( + "fmt" "gitee.com/johng/gf/g/encoding/gurl" + "gitee.com/johng/gf/g/os/glog" "gitee.com/johng/gf/g/os/gtime" "gitee.com/johng/gf/g/util/gstr" "strings" @@ -70,40 +72,55 @@ func Get(path string) *View { // 生成一个视图对象 func New(path string) *View { - s := gspath.New() - s.Set(path) view := &View { - paths : s, + paths : gspath.New(), data : make(map[string]interface{}), funcmap : make(map[string]interface{}), delimiters : make([]string, 2), } + view.SetPath(path) view.SetDelimiters("{{", "}}") // 内置方法 view.BindFunc("text", view.funcText) view.BindFunc("html", view.funcHtmlEncode) view.BindFunc("htmlencode", view.funcHtmlEncode) view.BindFunc("htmldecode", view.funcHtmlDecode) - //view.BindFunc("htmlchars", view.funcHtmlChars) - //view.BindFunc("htmldechars", view.funcHtmlCharsDecode) view.BindFunc("url", view.funcUrlEncode) view.BindFunc("urlencode", view.funcUrlEncode) view.BindFunc("urldecode", view.funcUrlDecode) view.BindFunc("date", view.funcDate) view.BindFunc("substr", view.funcSubStr) + view.BindFunc("strlimit", view.funcStrLimit) view.BindFunc("compare", view.funcCompare) + view.BindFunc("hidestr", view.funcHideStr) + view.BindFunc("highlight", view.funcHighlight) + view.BindFunc("toupper", view.funcToUpper) + view.BindFunc("tolower", view.funcToLower) + view.BindFunc("nl2br", view.funcNl2Br) view.BindFunc("include", view.funcInclude) return view } // 设置模板目录绝对路径 func (view *View) SetPath(path string) error { - return view.paths.Set(path) + if rp, err := view.paths.Set(path); err != nil { + glog.Error("gview.SetPath failed:", err.Error()) + return err + } else { + glog.Debug("gview.SetPath:", rp) + } + return nil } // 添加模板目录搜索路径 func (view *View) AddPath(path string) error { - return view.paths.Add(path) + if rp, err := view.paths.Add(path); err != nil { + glog.Error("gview.AddPath failed:", err.Error()) + return err + } else { + glog.Debug("gview.SetPath:", rp) + } + return nil } // 批量绑定模板变量,即调用之后每个线程都会生效,因此有并发安全控制 @@ -244,16 +261,6 @@ func (view *View) funcHtmlDecode(html interface{}) string { return ghtml.EntitiesDecode(gconv.String(html)) } -// 模板内置方法:htmlchars -func (view *View) funcHtmlChars(html interface{}) string { - return ghtml.SpecialChars(gconv.String(html)) -} - -// 模板内置方法:htmlcharsdecode -func (view *View) funcHtmlCharsDecode(html interface{}) string { - return ghtml.SpecialCharsDecode(gconv.String(html)) -} - // 模板内置方法:url func (view *View) funcUrlEncode(url interface{}) string { return gurl.Encode(gconv.String(url)) @@ -283,4 +290,34 @@ func (view *View) funcSubStr(start, end int, str interface{}) string { return gstr.SubStr(gconv.String(str), start, end) } +// 模板内置方法:strlimit +func (view *View) funcStrLimit(length int, suffix string, str interface{}) string { + return gstr.StrLimit(gconv.String(str), length, suffix) +} + +// 模板内置方法:highlight +func (view *View) funcHighlight(key string, color string, str interface{}) string { + return gstr.Replace(gconv.String(str), key, fmt.Sprintf(`%s`, color, key)) +} + +// 模板内置方法:hidestr +func (view *View) funcHideStr(percent int, hide string, str interface{}) string { + return gstr.HideStr(gconv.String(str), percent, hide) +} + +// 模板内置方法:toupper +func (view *View) funcToUpper(str interface{}) string { + return gstr.ToUpper(gconv.String(str)) +} + +// 模板内置方法:toupper +func (view *View) funcToLower(str interface{}) string { + return gstr.ToLower(gconv.String(str)) +} + +// 模板内置方法:nl2br +func (view *View) funcNl2Br(str interface{}) string { + return gstr.Nl2Br(gconv.String(str)) +} + diff --git a/g/util/gconv/gconv_struct.go b/g/util/gconv/gconv_struct.go index 2ed0e0819..24ad7c9c6 100644 --- a/g/util/gconv/gconv_struct.go +++ b/g/util/gconv/gconv_struct.go @@ -7,6 +7,7 @@ package gconv import ( + "gitee.com/johng/gf/g/container/gset" "gitee.com/johng/gf/g/util/gstr" "reflect" "gitee.com/johng/gf/third/github.com/fatih/structs" @@ -83,16 +84,38 @@ func Struct(params interface{}, objPointer interface{}, attrMapping...map[string } } // 最后按照默认规则进行匹配 + attrset := gset.NewStringSet(false) + elemtype := elem.Type() + for i := 0; i < elem.NumField(); i++ { + attrset.Add(elemtype.Field(i).Name) + } for mapk, mapv := range paramsMap { - name := gstr.UcFirst(mapk) - if _, ok := dmap[name]; ok { + name := "" + for _, v := range []string{gstr.UcFirst(mapk), gstr.ToLower(mapk), gstr.ToUpper(mapk)} { + if _, ok := dmap[v]; ok { + continue + } + if _, ok := tagmap[v]; ok { + continue + } + // 循环查找属性名称进行匹配 + attrset.Iterator(func(value string) bool { + if strings.EqualFold(value, v) { + name = value + return false + } + return true + }) + if name != "" { + break + } + } + // 如果没有匹配到属性名称,放弃 + if name == "" { continue } - // 后续tag逻辑中会处理的key(重复的键名)这里便不处理 - if _, ok := tagmap[mapk]; !ok { - if err := bindVarToStruct(elem, name, mapv); err != nil { - return err - } + if err := bindVarToStruct(elem, name, mapv); err != nil { + return err } } return nil @@ -120,7 +143,7 @@ func getTagMapOfStruct(objPointer interface{}) map[string]string { } // 将参数值绑定到对象指定名称的属性上 -func bindVarToStruct(elem reflect.Value, name string, value interface{}) error { +func bindVarToStruct(elem reflect.Value, name string, value interface{}) (err error) { structFieldValue := elem.FieldByName(name) // 键名与对象属性匹配检测,map中如果有struct不存在的属性,那么不做处理,直接return if !structFieldValue.IsValid() { @@ -136,7 +159,7 @@ func bindVarToStruct(elem reflect.Value, name string, value interface{}) error { defer func() { // 如果转换失败,那么可能是类型不匹配造成(例如属性包含自定义类型),那么执行递归转换 if recover() != nil { - bindVarToStructIfDefaultConvertionFailed(structFieldValue, value) + err = bindVarToStructIfDefaultConvertionFailed(structFieldValue, value) } }() structFieldValue.Set(reflect.ValueOf(Convert(value, structFieldValue.Type().String()))) @@ -144,7 +167,7 @@ func bindVarToStruct(elem reflect.Value, name string, value interface{}) error { } // 将参数值绑定到对象指定索引位置的属性上 -func bindVarToStructByIndex(elem reflect.Value, index int, value interface{}) error { +func bindVarToStructByIndex(elem reflect.Value, index int, value interface{}) (err error) { structFieldValue := elem.FieldByIndex([]int{index}) // 键名与对象属性匹配检测 if !structFieldValue.IsValid() { @@ -160,7 +183,7 @@ func bindVarToStructByIndex(elem reflect.Value, index int, value interface{}) er defer func() { // 如果转换失败,那么可能是类型不匹配造成(例如属性包含自定义类型),那么执行递归转换 if recover() != nil { - bindVarToStructIfDefaultConvertionFailed(structFieldValue, value) + err = bindVarToStructIfDefaultConvertionFailed(structFieldValue, value) } }() structFieldValue.Set(reflect.ValueOf(Convert(value, structFieldValue.Type().String()))) @@ -168,7 +191,7 @@ func bindVarToStructByIndex(elem reflect.Value, index int, value interface{}) er } // 当默认的基本类型转换失败时,通过recover判断后执行反射类型转换 -func bindVarToStructIfDefaultConvertionFailed(structFieldValue reflect.Value, value interface{}) { +func bindVarToStructIfDefaultConvertionFailed(structFieldValue reflect.Value, value interface{}) error { switch structFieldValue.Kind() { case reflect.Struct: Struct(value, structFieldValue) @@ -190,7 +213,8 @@ func bindVarToStructIfDefaultConvertionFailed(structFieldValue reflect.Value, va } structFieldValue.Set(a) default: - panic(errors.New(fmt.Sprintf(`cannot convert to type "%s"`, structFieldValue.Type().String()))) + return errors.New(fmt.Sprintf(`cannot convert to type "%s"`, structFieldValue.Type().String())) } + return nil } diff --git a/g/util/gconv/gconv_test.go b/g/util/gconv/gconv_test.go new file mode 100644 index 000000000..b740eb2ff --- /dev/null +++ b/g/util/gconv/gconv_test.go @@ -0,0 +1,137 @@ +// Copyright 2017-2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://gitee.com/johng/gf. + +// go test *.go -bench=".*" -benchmem + +package gconv + +import ( + "testing" +) + +var value = 123456789 + +func BenchmarkString(b *testing.B) { + for i := 0; i < b.N; i++ { + String(value) + } +} + +func BenchmarkInt(b *testing.B) { + for i := 0; i < b.N; i++ { + Int(value) + } +} + +func BenchmarkInt8(b *testing.B) { + for i := 0; i < b.N; i++ { + Int8(value) + } +} + +func BenchmarkInt16(b *testing.B) { + for i := 0; i < b.N; i++ { + Int16(value) + } +} + +func BenchmarkInt32(b *testing.B) { + for i := 0; i < b.N; i++ { + Int32(value) + } +} + +func BenchmarkInt64(b *testing.B) { + for i := 0; i < b.N; i++ { + Int(value) + } +} + +func BenchmarkUint(b *testing.B) { + for i := 0; i < b.N; i++ { + Uint(value) + } +} + +func BenchmarkUint8(b *testing.B) { + for i := 0; i < b.N; i++ { + Uint8(value) + } +} + +func BenchmarkUint16(b *testing.B) { + for i := 0; i < b.N; i++ { + Uint16(value) + } +} + +func BenchmarkUint32(b *testing.B) { + for i := 0; i < b.N; i++ { + Uint32(value) + } +} + +func BenchmarkUint64(b *testing.B) { + for i := 0; i < b.N; i++ { + Uint64(value) + } +} + +func BenchmarkFloat32(b *testing.B) { + for i := 0; i < b.N; i++ { + Float32(value) + } +} + +func BenchmarkFloat64(b *testing.B) { + for i := 0; i < b.N; i++ { + Float64(value) + } +} + + +func BenchmarkTime(b *testing.B) { + for i := 0; i < b.N; i++ { + Time(value) + } +} + +func BenchmarkTimeDuration(b *testing.B) { + for i := 0; i < b.N; i++ { + TimeDuration(value) + } +} + + +func BenchmarkBytes(b *testing.B) { + for i := 0; i < b.N; i++ { + Bytes(value) + } +} + +func BenchmarkStrings(b *testing.B) { + for i := 0; i < b.N; i++ { + Strings(value) + } +} + +func BenchmarkInts(b *testing.B) { + for i := 0; i < b.N; i++ { + Ints(value) + } +} + +func BenchmarkFloats(b *testing.B) { + for i := 0; i < b.N; i++ { + Floats(value) + } +} + +func BenchmarkInterfaces(b *testing.B) { + for i := 0; i < b.N; i++ { + Interfaces(value) + } +} \ No newline at end of file diff --git a/g/util/gregex/gregex.go b/g/util/gregex/gregex.go index 73d92ae88..c6c74ebbc 100644 --- a/g/util/gregex/gregex.go +++ b/g/util/gregex/gregex.go @@ -8,12 +8,12 @@ package gregex import ( + "gitee.com/johng/gf/g/container/gmap" "regexp" - "gitee.com/johng/gf/g/os/gcache" ) // 缓存对象,主要用于缓存底层regx对象 -var regxCache = gcache.New() +var regxCache = gmap.NewStringInterfaceMap(true) // 根据pattern生成对应的regexp正则对象 func getRegexp(pattern string) (*regexp.Regexp, error) { @@ -21,7 +21,7 @@ func getRegexp(pattern string) (*regexp.Regexp, error) { return v.(*regexp.Regexp), nil } if r, err := regexp.Compile(pattern); err == nil { - regxCache.Set(pattern, r, 0) + regxCache.Set(pattern, r) return r, nil } else { return nil, err diff --git a/g/util/gstr/gstr.go b/g/util/gstr/gstr.go index eeb46a7da..8decd74ba 100644 --- a/g/util/gstr/gstr.go +++ b/g/util/gstr/gstr.go @@ -7,11 +7,19 @@ // 字符串操作. package gstr -import "strings" +import ( + "bytes" + "math" + "strings" +) // 字符串替换 -func Replace(origin, search, replace string) string { - return strings.Replace(origin, search, replace, -1) +func Replace(origin, search, replace string, count...int) string { + n := -1 + if len(count) > 0 { + n = count[0] + } + return strings.Replace(origin, search, replace, n) } // 使用map进行字符串替换 @@ -23,6 +31,16 @@ func ReplaceByMap(origin string, replaces map[string]string) string { return result } +// 字符串转换为小写 +func ToLower(s string) string { + return strings.ToLower(s) +} + +// 字符串转换为大写 +func ToUpper(s string) string { + return strings.ToUpper(s) +} + // 字符串首字母转换为大写 func UcFirst(s string) string { if len(s) == 0 { @@ -110,4 +128,51 @@ func SubStr(str string, start int, length...int) (substr string) { } // 返回子串 return string(rs[start : end]) +} + +// 字符串长度截取限制,超过长度限制被截取并在字符串末尾追加指定的内容,支持中文 +func StrLimit(str string, length int, suffix...string) (string) { + rs := []rune(str) + if len(str) < length { + return str + } + addstr := "..." + if len(suffix) > 0 { + addstr = suffix[0] + } + return string(rs[0 : length]) + addstr +} + +// 按照百分比从字符串中间向两边隐藏字符(主要用于姓名、手机号、邮箱地址、身份证号等的隐藏),支持utf-8中文,支持email格式。 +func HideStr(str string, percent int, hide string) string { + array := strings.Split(str, "@") + if len(array) > 1 { + str = array[0] + } + rs := []rune(str) + length := len(rs) + mid := math.Floor(float64(length/2)) + hideLen := int(math.Floor(float64(length) * (float64(percent)/100))) + start := int(mid - math.Floor(float64(hideLen) / 2)) + hideStr := []rune("") + hideRune := []rune(hide) + for i := 0; i < int(hideLen); i++ { + hideStr = append(hideStr, hideRune...) + } + buffer := bytes.NewBuffer(nil) + buffer.WriteString(string(rs[0 : start])) + buffer.WriteString(string(hideStr)) + buffer.WriteString(string(rs[start + hideLen : ])) + if len(array) > 1 { + buffer.WriteString(array[1]) + } + return buffer.String() +} + +// 将\n\r替换为html中的
标签。 +func Nl2Br(str string) string { + str = Replace(str, "\r\n", "\n") + str = Replace(str, "\n\r", "\n") + str = Replace(str, "\n", "
") + return str } \ No newline at end of file diff --git a/geg/net/ghttp/server/controller/user.go b/geg/net/ghttp/server/controller/user.go index f0283ec90..50db001c9 100644 --- a/geg/net/ghttp/server/controller/user.go +++ b/geg/net/ghttp/server/controller/user.go @@ -1,8 +1,8 @@ package main import ( - "gitee.com/johng/gf/g/frame/gmvc" "gitee.com/johng/gf/g" + "gitee.com/johng/gf/g/frame/gmvc" ) type User struct { @@ -13,8 +13,13 @@ func (c *User) Index() { c.View.Display("index.html") } +// 不符合规范,不会被自动注册 +func (c *User) Test(value interface{}) { + c.View.Display("index.html") +} + func main() { - g.View().SetPath("C:/www/static") + //g.View().SetPath("C:/www/static") s := g.Server() s.BindController("/user", new(User)) s.SetPort(8199) diff --git a/geg/net/ghttp/server/object/user.go b/geg/net/ghttp/server/object/user.go new file mode 100644 index 000000000..cd39824c8 --- /dev/null +++ b/geg/net/ghttp/server/object/user.go @@ -0,0 +1,31 @@ +package main + +import ( + "gitee.com/johng/gf/g" + "gitee.com/johng/gf/g/net/ghttp" +) + +type User struct { + +} + +func (c *User) Index(r *ghttp.Request) { + r.Response.Write("Index") +} + +// 不符合规范,不会被注册 +func (c *User) Test(r *ghttp.Request, value interface{}) { + r.Response.Write("Test") +} + +func main() { + s := g.Server() + s.BindObjectMethod("/user", new(User), "Test") + s.SetPort(8199) + s.Run() +} + + + + + diff --git a/geg/os/gtime/gtime_regex.go b/geg/os/gtime/gtime_regex.go index 4f4463a28..03f0d51fa 100644 --- a/geg/os/gtime/gtime_regex.go +++ b/geg/os/gtime/gtime_regex.go @@ -19,6 +19,7 @@ func main() { "2018-02-09 20:46:17.897", "2018-02-09T20:46:17Z", "2018-02-09 20:46:17", + "2018/10/31 - 16:38:46", "2018-02-09", "2017/12/14 04:51:34 +0805 LMT", "2018/02/09 12:00:15", diff --git a/geg/os/gview/build_in_funcs/build_in_funcs.go b/geg/os/gview/build_in_funcs/build_in_funcs.go index 462b8d763..d39c683dd 100644 --- a/geg/os/gview/build_in_funcs/build_in_funcs.go +++ b/geg/os/gview/build_in_funcs/build_in_funcs.go @@ -20,6 +20,13 @@ func main() { {{compare 1 1}} {{"我是中国人" | substr 2 -1}} {{"我是中国人" | substr 2 2}} +{{"我是中国人" | strlimit 2 "..."}} +{{"热爱GF热爱生活" | hidestr 20 "*"}} +{{"热爱GF热爱生活" | hidestr 50 "*"}} +{{"热爱GF热爱生活" | highlight "GF" "red"}} +{{"gf" | toupper}} +{{"GF" | tolower}} +{{"Go\nFrame" | nl2br}} ` content, err := g.View().ParseContent(tplContent, nil) fmt.Println(err) diff --git a/geg/other/test2.go b/geg/other/test2.go index d51d46561..e2811e825 100644 --- a/geg/other/test2.go +++ b/geg/other/test2.go @@ -1,10 +1,20 @@ package main import ( - "fmt" - "gitee.com/johng/gf/g/os/gfile" + "gitee.com/johng/gf/g" + "gitee.com/johng/gf/g/net/ghttp" ) + func main() { - fmt.Println(gfile.Dir("c:\111\222")) + s := g.Server() + s.Domain("www.a.com").BindHandler("/*", func(r *ghttp.Request) { + r.Response.ServeFile("/home/john/www1" + r.URL.Path) + }) + s.Domain("www.b.com").BindHandler("/*", func(r *ghttp.Request) { + r.Response.ServeFile("/home/john/www2" + r.URL.Path) + }) + s.SetIndexFolder(true) + s.SetPort(8080) + s.Run() } \ No newline at end of file diff --git a/geg/util/gconv/gconv_struct1.go b/geg/util/gconv/gconv_struct1.go index 2e6a971e7..5481c1599 100644 --- a/geg/util/gconv/gconv_struct1.go +++ b/geg/util/gconv/gconv_struct1.go @@ -16,13 +16,13 @@ type User struct { func main() { user := (*User)(nil) - // 使用map直接映射绑定属性值到对象 + // 使用默认映射规则绑定属性值到对象 user = new(User) params1 := g.Map{ "uid" : 1, - "name" : "john", - "pass1" : "123", - "pass2" : "123", + "Name" : "john", + "PASS1" : "123", + "PaSs2" : "456", } if err := gconv.Struct(params1, user); err == nil { fmt.Println(user) @@ -33,8 +33,8 @@ func main() { params2 := g.Map { "uid" : 2, "name" : "smith", - "password1" : "456", - "password2" : "456", + "password1" : "111", + "password2" : "222", } if err := gconv.Struct(params2, user); err == nil { fmt.Println(user) diff --git a/geg/util/gconv/gconv_struct2.go b/geg/util/gconv/gconv_struct2.go index 0b3bf6132..c9ad3a369 100644 --- a/geg/util/gconv/gconv_struct2.go +++ b/geg/util/gconv/gconv_struct2.go @@ -1,31 +1,28 @@ package main import ( - "gitee.com/johng/gf/g/util/gconv" - "gitee.com/johng/gf/g" "fmt" + "gitee.com/johng/gf/g" + "gitee.com/johng/gf/g/util/gconv" ) -// 演示slice类型属性的赋值 + +// 使用默认映射规则绑定属性值到对象 func main() { type User struct { - Scores []int + Uid int + Name string + Pass1 string + Pass2 string } - - user := new(User) - scores := []interface{}{99, 100, 60, 140} - - // 通过map映射转换 - if err := gconv.Struct(g.Map{"Scores" : scores}, user); err != nil { - fmt.Println(err) - } else { - g.Dump(user) + user := new(User) + params := g.Map { + "uid" : 1, + "Name" : "john", + "PASS1" : "123", + "PASS2" : "456", } - - // 通过变量映射转换,直接slice赋值 - if err := gconv.Struct(scores, user); err != nil { - fmt.Println(err) - } else { - g.Dump(user) + if err := gconv.Struct(params, user); err == nil { + fmt.Println(user) } } \ No newline at end of file diff --git a/geg/util/gconv/gconv_struct3.go b/geg/util/gconv/gconv_struct3.go index 797ce13db..0b3bf6132 100644 --- a/geg/util/gconv/gconv_struct3.go +++ b/geg/util/gconv/gconv_struct3.go @@ -6,24 +6,23 @@ import ( "fmt" ) +// 演示slice类型属性的赋值 func main() { - type Score struct { - Name string - Result int - } type User struct { - Scores Score + Scores []int } user := new(User) - scores := map[string]interface{}{ - "Scores" : map[string]interface{}{ - "Name" : "john", - "Result" : 100, - }, + scores := []interface{}{99, 100, 60, 140} + + // 通过map映射转换 + if err := gconv.Struct(g.Map{"Scores" : scores}, user); err != nil { + fmt.Println(err) + } else { + g.Dump(user) } - // 嵌套struct转换 + // 通过变量映射转换,直接slice赋值 if err := gconv.Struct(scores, user); err != nil { fmt.Println(err) } else { diff --git a/geg/util/gconv/gconv_struct4.go b/geg/util/gconv/gconv_struct4.go index 99dcb221b..797ce13db 100644 --- a/geg/util/gconv/gconv_struct4.go +++ b/geg/util/gconv/gconv_struct4.go @@ -12,7 +12,7 @@ func main() { Result int } type User struct { - Scores []Score + Scores Score } user := new(User) @@ -23,7 +23,7 @@ func main() { }, } - // 嵌套struct转换,属性为slice类型,数值为map类型 + // 嵌套struct转换 if err := gconv.Struct(scores, user); err != nil { fmt.Println(err) } else { diff --git a/geg/util/gconv/gconv_struct5.go b/geg/util/gconv/gconv_struct5.go index 5e31d47e7..99dcb221b 100644 --- a/geg/util/gconv/gconv_struct5.go +++ b/geg/util/gconv/gconv_struct5.go @@ -17,19 +17,13 @@ func main() { user := new(User) scores := map[string]interface{}{ - "Scores" : []interface{}{ - map[string]interface{}{ - "Name" : "john", - "Result" : 100, - }, - map[string]interface{}{ - "Name" : "smith", - "Result" : 60, - }, + "Scores" : map[string]interface{}{ + "Name" : "john", + "Result" : 100, }, } - // 嵌套struct转换,属性为slice类型,数值为slice map类型 + // 嵌套struct转换,属性为slice类型,数值为map类型 if err := gconv.Struct(scores, user); err != nil { fmt.Println(err) } else { diff --git a/geg/util/gconv/gconv_struct6.go b/geg/util/gconv/gconv_struct6.go new file mode 100644 index 000000000..5e31d47e7 --- /dev/null +++ b/geg/util/gconv/gconv_struct6.go @@ -0,0 +1,38 @@ +package main + +import ( + "gitee.com/johng/gf/g/util/gconv" + "gitee.com/johng/gf/g" + "fmt" +) + +func main() { + type Score struct { + Name string + Result int + } + type User struct { + Scores []Score + } + + user := new(User) + scores := map[string]interface{}{ + "Scores" : []interface{}{ + map[string]interface{}{ + "Name" : "john", + "Result" : 100, + }, + map[string]interface{}{ + "Name" : "smith", + "Result" : 60, + }, + }, + } + + // 嵌套struct转换,属性为slice类型,数值为slice map类型 + if err := gconv.Struct(scores, user); err != nil { + fmt.Println(err) + } else { + g.Dump(user) + } +} \ No newline at end of file diff --git a/geg/util/gstr/gstr_hidestr.go b/geg/util/gstr/gstr_hidestr.go new file mode 100644 index 000000000..3cc38d4f2 --- /dev/null +++ b/geg/util/gstr/gstr_hidestr.go @@ -0,0 +1,11 @@ +package main + +import ( + "fmt" + "gitee.com/johng/gf/g/util/gstr" +) + +func main() { + fmt.Println(gstr.HideStr("热爱GF热爱生活", 20, "*")) + fmt.Println(gstr.HideStr("热爱GF热爱生活", 50, "*")) +}