Merge branch 'master' of https://gitee.com/johng/gf into develop

This commit is contained in:
john
2018-11-01 09:06:14 +08:00
29 changed files with 587 additions and 156 deletions

View File

@ -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)

1
TODO
View File

@ -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请求执行中增加服务退出的方法不再执行后续操作

View File

@ -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{}
@ -160,6 +161,9 @@ func Database(name...string) *gdb.Db {
if value, ok := nodem["priority"]; ok {
node.Priority = gconv.Int(value)
}
if value, ok := nodem["linkinfo"]; ok {
node.Linkinfo = gconv.String(value)
}
if value, ok := nodem["max-idle"]; ok {
node.MaxIdleConnCount = gconv.Int(value)
}
@ -181,7 +185,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 +217,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
})

View File

@ -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...)

View File

@ -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()
}

View File

@ -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()))

View File

@ -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
}
// 获取

View File

@ -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] == '*' {

View File

@ -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,
}

View File

@ -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)
}

View File

@ -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)
}
// 按照优先级搜索文件,返回搜索到的文件绝对路径,找不到该文件时,返回空字符串

View File

@ -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 (

View File

@ -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(`<span style="color:%s;">%s</span>`, 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))
}

View File

@ -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
}

137
g/util/gconv/gconv_test.go Normal file
View File

@ -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)
}
}

View File

@ -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

View File

@ -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中的<br>标签。
func Nl2Br(str string) string {
str = Replace(str, "\r\n", "\n")
str = Replace(str, "\n\r", "\n")
str = Replace(str, "\n", "<br />")
return str
}

View File

@ -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)

View File

@ -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()
}

View File

@ -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",

View File

@ -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)

View File

@ -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()
}

View File

@ -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)

View File

@ -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)
}
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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)
}
}

View File

@ -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, "*"))
}