diff --git a/TODO.MD b/TODO.MD index 1f48705ae..357d77c94 100644 --- a/TODO.MD +++ b/TODO.MD @@ -49,7 +49,7 @@ 1. gdb的Cache缓存功能增加可自定义缓存接口,以便支持外部缓存功能,缓存接口可以通过io.ReadWriter接口实现; 1. grpool增加支持阻塞添加任务接口; 1. gdb.Model在链式安全的对象创建中增加sync.Pool的使用; - +1. 增加g.Table快捷方法以方便操作数据表,但是得考虑后续模型操作设计,特别是脚手架的模型管理; # DONE 1. gconv完善针对不同类型的判断,例如:尽量减少sprintf("%v", xxx)来执行string类型的转换; diff --git a/g/database/gdb/gdb.go b/g/database/gdb/gdb.go index 4ea743d86..2a37eba86 100644 --- a/g/database/gdb/gdb.go +++ b/g/database/gdb/gdb.go @@ -77,9 +77,9 @@ type DB interface { Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error) - // 创建链式操作对象(Table为From的别名) - Table(tables string) *Model + // 创建链式操作对象 From(tables string) *Model + Table(tables string) *Model // 设置管理 SetDebug(debug bool) diff --git a/g/database/gdb/gdb_base.go b/g/database/gdb/gdb_base.go index 045d5e450..00ab5afb6 100644 --- a/g/database/gdb/gdb_base.go +++ b/g/database/gdb/gdb_base.go @@ -206,15 +206,12 @@ func (bs *dbBase) GetScan(objPointer interface{}, query string, args ...interfac } k = t.Elem().Kind() switch k { - case reflect.Array: - case reflect.Slice: + case reflect.Array, reflect.Slice: return bs.db.GetStructs(objPointer, query, args...) case reflect.Struct: return bs.db.GetStruct(objPointer, query, args...) - default: - return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k) } - return nil + return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k) } // 数据库查询,获取查询字段值 diff --git a/g/database/gdb/gdb_func.go b/g/database/gdb/gdb_func.go index 972504fdf..090694155 100644 --- a/g/database/gdb/gdb_func.go +++ b/g/database/gdb/gdb_func.go @@ -8,6 +8,7 @@ package gdb import ( "bytes" + "database/sql" "errors" "fmt" "reflect" @@ -182,7 +183,7 @@ func printSql(v *Sql) { // 格式化错误信息 func formatError(err error, query string, args ...interface{}) error { - if err != nil { + if err != nil && err != sql.ErrNoRows { errStr := fmt.Sprintf("DB ERROR: %s\n", err.Error()) errStr += fmt.Sprintf("DB QUERY: %s\n", query) if len(args) > 0 { diff --git a/g/database/gdb/gdb_model.go b/g/database/gdb/gdb_model.go index b9168016f..ef61c8041 100644 --- a/g/database/gdb/gdb_model.go +++ b/g/database/gdb/gdb_model.go @@ -511,21 +511,18 @@ func (md *Model) Structs(objPointerSlice interface{}) error { // 链式操作,将结果转换为指定的struct/*struct/[]struct/[]*struct, // 参数应该为指针类型,否则返回失败。 // 该方法自动识别参数类型,调用Struct/Structs方法。 -func (md *Model) Scan(objPointer interface{}) error { - t := reflect.TypeOf(objPointer) +func (md *Model) Scan(pointer interface{}) error { + t := reflect.TypeOf(pointer) k := t.Kind() if k != reflect.Ptr { return fmt.Errorf("params should be type of pointer, but got: %v", k) } - k = t.Elem().Kind() - switch k { + switch t.Elem().Kind() { case reflect.Array: case reflect.Slice: - return md.Structs(objPointer) - case reflect.Struct: - return md.Struct(objPointer) + return md.Structs(pointer) default: - return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k) + return md.Struct(pointer) } return nil } diff --git a/g/database/gdb/gdb_type_record.go b/g/database/gdb/gdb_type_record.go index ce4047f88..27f4e3a3c 100644 --- a/g/database/gdb/gdb_type_record.go +++ b/g/database/gdb/gdb_type_record.go @@ -7,6 +7,8 @@ package gdb import ( + "database/sql" + "github.com/gogf/gf/g/encoding/gparser" ) @@ -33,5 +35,8 @@ func (r Record) ToMap() Map { // 将Map变量映射到指定的struct对象中,注意参数应当是一个对象的指针 func (r Record) ToStruct(pointer interface{}) error { + if r == nil { + return sql.ErrNoRows + } return mapToStruct(r.ToMap(), pointer) } diff --git a/g/database/gdb/gdb_type_result.go b/g/database/gdb/gdb_type_result.go index c5872b622..91854d043 100644 --- a/g/database/gdb/gdb_type_result.go +++ b/g/database/gdb/gdb_type_result.go @@ -7,9 +7,11 @@ package gdb import ( + "database/sql" "fmt" - "github.com/gogf/gf/g/encoding/gparser" "reflect" + + "github.com/gogf/gf/g/encoding/gparser" ) // 将结果集转换为JSON字符串 @@ -100,28 +102,32 @@ func (r Result) ToUintRecord(key string) map[uint]Record { } // 将结果列表转换为指定对象的slice。 -func (r Result) ToStructs(objPointerSlice interface{}) error { +func (r Result) ToStructs(pointer interface{}) (err error) { l := len(r) if l == 0 { - return nil + return sql.ErrNoRows } - t := reflect.TypeOf(objPointerSlice) + t := reflect.TypeOf(pointer) if t.Kind() != reflect.Ptr { - return fmt.Errorf("params should be type of pointer, but got: %v", t.Kind()) + return fmt.Errorf("pointer should be type of pointer, but got: %v", t.Kind()) } - a := reflect.MakeSlice(t.Elem(), l, l) - itemType := a.Index(0).Type() + array := reflect.MakeSlice(t.Elem(), l, l) + itemType := array.Index(0).Type() for i := 0; i < l; i++ { if itemType.Kind() == reflect.Ptr { e := reflect.New(itemType.Elem()).Elem() - r[i].ToStruct(e) - a.Index(i).Set(e.Addr()) + if err = r[i].ToStruct(e); err != nil { + return err + } + array.Index(i).Set(e.Addr()) } else { e := reflect.New(itemType).Elem() - r[i].ToStruct(e) - a.Index(i).Set(e) + if err = r[i].ToStruct(e); err != nil { + return err + } + array.Index(i).Set(e) } } - reflect.ValueOf(objPointerSlice).Elem().Set(a) + reflect.ValueOf(pointer).Elem().Set(array) return nil } diff --git a/g/database/gdb/gdb_unit_model_test.go b/g/database/gdb/gdb_unit_model_test.go index 9422a6c53..1daf31db4 100644 --- a/g/database/gdb/gdb_unit_model_test.go +++ b/g/database/gdb/gdb_unit_model_test.go @@ -7,6 +7,7 @@ package gdb_test import ( + "database/sql" "testing" "github.com/gogf/gf/g" @@ -357,6 +358,53 @@ func TestModel_Struct(t *testing.T) { gtest.Assert(user.NickName, "T111") gtest.Assert(user.CreateTime.String(), "2018-10-10 00:01:10") }) + // Auto creating struct object. + gtest.Case(t, func() { + type User struct { + Id int + Passport string + Password string + NickName string + CreateTime *gtime.Time + } + user := (*User)(nil) + err := db.Table("user").Where("id=1").Struct(&user) + if err != nil { + gtest.Fatal(err) + } + gtest.Assert(user.NickName, "T111") + gtest.Assert(user.CreateTime.String(), "2018-10-10 00:01:10") + }) + // Just using Scan. + gtest.Case(t, func() { + type User struct { + Id int + Passport string + Password string + NickName string + CreateTime *gtime.Time + } + user := (*User)(nil) + err := db.Table("user").Where("id=1").Scan(&user) + if err != nil { + gtest.Fatal(err) + } + gtest.Assert(user.NickName, "T111") + gtest.Assert(user.CreateTime.String(), "2018-10-10 00:01:10") + }) + + gtest.Case(t, func() { + type User struct { + Id int + Passport string + Password string + NickName string + CreateTime *gtime.Time + } + user := new(User) + err := db.Table("user").Where("id=-1").Struct(user) + gtest.Assert(err, sql.ErrNoRows) + }) } func TestModel_Structs(t *testing.T) { @@ -382,6 +430,7 @@ func TestModel_Structs(t *testing.T) { gtest.Assert(users[2].NickName, "T3") gtest.Assert(users[0].CreateTime.String(), "2018-10-10 00:01:10") }) + // Auto create struct slice. gtest.Case(t, func() { type User struct { Id int @@ -404,6 +453,41 @@ func TestModel_Structs(t *testing.T) { gtest.Assert(users[2].NickName, "T3") gtest.Assert(users[0].CreateTime.String(), "2018-10-10 00:01:10") }) + // Just using Scan. + gtest.Case(t, func() { + type User struct { + Id int + Passport string + Password string + NickName string + CreateTime *gtime.Time + } + var users []*User + err := db.Table("user").OrderBy("id asc").Scan(&users) + if err != nil { + gtest.Fatal(err) + } + gtest.Assert(len(users), 3) + gtest.Assert(users[0].Id, 1) + gtest.Assert(users[1].Id, 2) + gtest.Assert(users[2].Id, 3) + gtest.Assert(users[0].NickName, "T111") + gtest.Assert(users[1].NickName, "T2") + gtest.Assert(users[2].NickName, "T3") + gtest.Assert(users[0].CreateTime.String(), "2018-10-10 00:01:10") + }) + gtest.Case(t, func() { + type User struct { + Id int + Passport string + Password string + NickName string + CreateTime *gtime.Time + } + var users []*User + err := db.Table("user").Where("id<0").Structs(&users) + gtest.Assert(err, sql.ErrNoRows) + }) } func TestModel_Scan(t *testing.T) { @@ -483,6 +567,22 @@ func TestModel_Scan(t *testing.T) { gtest.Assert(users[2].NickName, "T3") gtest.Assert(users[0].CreateTime.String(), "2018-10-10 00:01:10") }) + + gtest.Case(t, func() { + type User struct { + Id int + Passport string + Password string + NickName string + CreateTime *gtime.Time + } + user := new(User) + users := new([]*User) + err1 := db.Table("user").Where("id < 0").Scan(user) + err2 := db.Table("user").Where("id < 0").Scan(users) + gtest.Assert(err1, sql.ErrNoRows) + gtest.Assert(err2, sql.ErrNoRows) + }) } func TestModel_OrderBy(t *testing.T) { diff --git a/g/internal/structs/structs.go b/g/internal/structs/structs.go new file mode 100644 index 000000000..b3c2afdf8 --- /dev/null +++ b/g/internal/structs/structs.go @@ -0,0 +1,15 @@ +// Copyright 2019 gf Author(https://github.com/gogf/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://github.com/gogf/gf. + +// Package structs provides functions for struct conversion. +package structs + +import ( + "github.com/gogf/gf/third/github.com/fatih/structs" +) + +// Field is alias of structs.Field. +type Field = structs.Field diff --git a/g/internal/structs/structs_map.go b/g/internal/structs/structs_map.go new file mode 100644 index 000000000..fd720051a --- /dev/null +++ b/g/internal/structs/structs_map.go @@ -0,0 +1,64 @@ +// Copyright 2019 gf Author(https://github.com/gogf/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://github.com/gogf/gf. + +package structs + +import ( + "reflect" + + "github.com/gogf/gf/third/github.com/fatih/structs" +) + +// MapField retrieves struct field as map[name/tag]*Field from , and returns it. +// +// The parameter specifies whether retrieving the struct field recursively. +// +// Note that it only retrieves the exported attributes with first letter up-case from struct. +func MapField(pointer interface{}, priority []string, recursive bool) map[string]*Field { + fieldMap := make(map[string]*Field) + fields := ([]*structs.Field)(nil) + if v, ok := pointer.(reflect.Value); ok { + fields = structs.Fields(v.Interface()) + } else { + fields = structs.Fields(pointer) + } + tag := "" + name := "" + for _, field := range fields { + name = field.Name() + // Only retrieve exported attributes. + if name[0] < byte('A') || name[0] > byte('Z') { + continue + } + fieldMap[name] = field + tag = "" + for _, p := range priority { + tag = field.Tag(p) + if tag != "" { + break + } + } + if tag != "" { + fieldMap[tag] = field + } + if recursive { + rv := reflect.ValueOf(field.Value()) + kind := rv.Kind() + if kind == reflect.Ptr { + rv = rv.Elem() + kind = rv.Kind() + } + if kind == reflect.Struct { + for k, v := range MapField(rv, priority, true) { + if _, ok := fieldMap[k]; !ok { + fieldMap[k] = v + } + } + } + } + } + return fieldMap +} diff --git a/g/internal/structs/structs_tag.go b/g/internal/structs/structs_tag.go new file mode 100644 index 000000000..9040e9560 --- /dev/null +++ b/g/internal/structs/structs_tag.go @@ -0,0 +1,81 @@ +// Copyright 2019 gf Author(https://github.com/gogf/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://github.com/gogf/gf. + +package structs + +import ( + "reflect" + + "github.com/gogf/gf/third/github.com/fatih/structs" +) + +// TagMapName retrieves struct tags as map[tag]attribute from , and returns it. +// +// The parameter specifies whether retrieving the struct field recursively. +// +// Note that it only retrieves the exported attributes with first letter up-case from struct. +func TagMapName(pointer interface{}, priority []string, recursive bool) map[string]string { + tagMap := TagMapField(pointer, priority, recursive) + if len(tagMap) > 0 { + m := make(map[string]string, len(tagMap)) + for k, v := range tagMap { + m[k] = v.Name() + } + return m + } + return nil +} + +// TagMapField retrieves struct tags as map[tag]*Field from , and returns it. +// +// The parameter specifies whether retrieving the struct field recursively. +// +// Note that it only retrieves the exported attributes with first letter up-case from struct. +func TagMapField(pointer interface{}, priority []string, recursive bool) map[string]*Field { + tagMap := make(map[string]*Field) + fields := ([]*structs.Field)(nil) + if v, ok := pointer.(reflect.Value); ok { + fields = structs.Fields(v.Interface()) + } else { + fields = structs.Fields(pointer) + } + tag := "" + name := "" + for _, field := range fields { + name = field.Name() + // Only retrieve exported attributes. + if name[0] < byte('A') || name[0] > byte('Z') { + continue + } + + tag = "" + for _, p := range priority { + tag = field.Tag(p) + if tag != "" { + break + } + } + if tag != "" { + tagMap[tag] = field + } + if recursive { + rv := reflect.ValueOf(field.Value()) + kind := rv.Kind() + if kind == reflect.Ptr { + rv = rv.Elem() + kind = rv.Kind() + } + if kind == reflect.Struct { + for k, v := range TagMapField(rv, priority, true) { + if _, ok := tagMap[k]; !ok { + tagMap[k] = v + } + } + } + } + } + return tagMap +} diff --git a/g/internal/structs/structs_test.go b/g/internal/structs/structs_test.go new file mode 100644 index 000000000..54d63be74 --- /dev/null +++ b/g/internal/structs/structs_test.go @@ -0,0 +1,73 @@ +// Copyright 2019 gf Author(https://github.com/gogf/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://github.com/gogf/gf. + +package structs_test + +import ( + "testing" + + "github.com/gogf/gf/g/internal/structs" + + "github.com/gogf/gf/g" + + "github.com/gogf/gf/g/test/gtest" +) + +func Test_Basic(t *testing.T) { + gtest.Case(t, func() { + type User struct { + Id int + Name string `params:"name"` + Pass string `my-tag1:"pass1" my-tag2:"pass2" params:"pass"` + } + var user User + gtest.Assert(structs.TagMapName(user, []string{"params"}, true), g.Map{"name": "Name", "pass": "Pass"}) + gtest.Assert(structs.TagMapName(&user, []string{"params"}, true), g.Map{"name": "Name", "pass": "Pass"}) + + gtest.Assert(structs.TagMapName(&user, []string{"params", "my-tag1"}, true), g.Map{"name": "Name", "pass": "Pass"}) + gtest.Assert(structs.TagMapName(&user, []string{"my-tag1", "params"}, true), g.Map{"name": "Name", "pass1": "Pass"}) + gtest.Assert(structs.TagMapName(&user, []string{"my-tag2", "params"}, true), g.Map{"name": "Name", "pass2": "Pass"}) + }) + + gtest.Case(t, func() { + type Base struct { + Pass1 string `params:"password1"` + Pass2 string `params:"password2"` + } + type UserWithBase struct { + Id int + Name string + Base `params:"base"` + } + user := new(UserWithBase) + gtest.Assert(structs.TagMapName(user, []string{"params"}, true), g.Map{ + "base": "Base", + "password1": "Pass1", + "password2": "Pass2", + }) + }) + + gtest.Case(t, func() { + type Base struct { + Pass1 string `params:"password1"` + Pass2 string `params:"password2"` + } + type UserWithBase1 struct { + Id int + Name string + Base + } + type UserWithBase2 struct { + Id int + Name string + Pass Base + } + user1 := new(UserWithBase1) + user2 := new(UserWithBase2) + gtest.Assert(structs.TagMapName(user1, []string{"params"}, true), g.Map{"password1": "Pass1", "password2": "Pass2"}) + gtest.Assert(structs.TagMapName(user2, []string{"params"}, true), g.Map{"password1": "Pass1", "password2": "Pass2"}) + }) +} diff --git a/g/net/ghttp/ghttp.go b/g/net/ghttp/ghttp.go index bfff905be..53930655b 100644 --- a/g/net/ghttp/ghttp.go +++ b/g/net/ghttp/ghttp.go @@ -6,3 +6,7 @@ // Package ghttp provides powerful http server and simple client implements. package ghttp + +var ( + paramTagPriority = []string{"param", "params"} +) diff --git a/g/net/ghttp/ghttp_request.go b/g/net/ghttp/ghttp_request.go index 71e76a9ba..590b47669 100644 --- a/g/net/ghttp/ghttp_request.go +++ b/g/net/ghttp/ghttp_request.go @@ -8,14 +8,14 @@ package ghttp import ( "fmt" + "io/ioutil" + "net/http" + "strings" + "github.com/gogf/gf/g/container/gvar" "github.com/gogf/gf/g/encoding/gjson" "github.com/gogf/gf/g/os/gtime" "github.com/gogf/gf/g/text/gregex" - "github.com/gogf/gf/third/github.com/fatih/structs" - "io/ioutil" - "net/http" - "strings" ) // 请求对象 @@ -232,17 +232,3 @@ func (r *Request) GetUrl() string { func (r *Request) GetReferer() string { return r.Header.Get("Referer") } - -// 获得结构体对象的参数名称标签,构成map返回 -func (r *Request) getStructParamsTagMap(pointer interface{}) map[string]string { - tagMap := make(map[string]string) - fields := structs.Fields(pointer) - for _, field := range fields { - if tag := field.Tag("params"); tag != "" { - for _, v := range strings.Split(tag, ",") { - tagMap[strings.TrimSpace(v)] = field.Name() - } - } - } - return tagMap -} diff --git a/g/net/ghttp/ghttp_request_post.go b/g/net/ghttp/ghttp_request_post.go index 62b1ef995..6a594fd1b 100644 --- a/g/net/ghttp/ghttp_request_post.go +++ b/g/net/ghttp/ghttp_request_post.go @@ -7,6 +7,7 @@ package ghttp import ( + "github.com/gogf/gf/g/internal/structs" "github.com/gogf/gf/g/util/gconv" ) @@ -143,7 +144,7 @@ func (r *Request) GetPostMap(def ...map[string]string) map[string]string { // 将所有的request参数映射到struct属性上,参数object应当为一个struct对象的指针, mapping为非必需参数,自定义参数与属性的映射关系 func (r *Request) GetPostToStruct(pointer interface{}, mapping ...map[string]string) error { - tagMap := r.getStructParamsTagMap(pointer) + tagMap := structs.TagMapName(pointer, paramTagPriority, true) if len(mapping) > 0 { for k, v := range mapping[0] { tagMap[k] = v diff --git a/g/net/ghttp/ghttp_request_query.go b/g/net/ghttp/ghttp_request_query.go index 6600a8dbf..5b0eed70e 100644 --- a/g/net/ghttp/ghttp_request_query.go +++ b/g/net/ghttp/ghttp_request_query.go @@ -7,8 +7,10 @@ package ghttp import ( - "github.com/gogf/gf/g/util/gconv" "strings" + + "github.com/gogf/gf/g/internal/structs" + "github.com/gogf/gf/g/util/gconv" ) // 初始化GET请求参数 @@ -151,7 +153,7 @@ func (r *Request) GetQueryMap(def ...map[string]string) map[string]string { // 将所有的get参数映射到struct属性上,参数object应当为一个struct对象的指针, mapping为非必需参数,自定义参数与属性的映射关系 func (r *Request) GetQueryToStruct(pointer interface{}, mapping ...map[string]string) error { - tagmap := r.getStructParamsTagMap(pointer) + tagmap := structs.TagMapName(pointer, paramTagPriority, true) if len(mapping) > 0 { for k, v := range mapping[0] { tagmap[k] = v @@ -161,5 +163,5 @@ func (r *Request) GetQueryToStruct(pointer interface{}, mapping ...map[string]st for k, v := range r.GetQueryMap() { params[k] = v } - return gconv.Struct(params, pointer, tagmap) + return gconv.StructDeep(params, pointer, tagmap) } diff --git a/g/net/ghttp/ghttp_request_request.go b/g/net/ghttp/ghttp_request_request.go index 17b130eb8..e8386e2ad 100644 --- a/g/net/ghttp/ghttp_request_request.go +++ b/g/net/ghttp/ghttp_request_request.go @@ -8,6 +8,7 @@ package ghttp import ( "github.com/gogf/gf/g/container/gvar" + "github.com/gogf/gf/g/internal/structs" "github.com/gogf/gf/g/util/gconv" ) @@ -133,7 +134,7 @@ func (r *Request) GetRequestMap(def ...map[string]string) map[string]string { // 将所有的request参数映射到struct属性上,参数object应当为一个struct对象的指针, mapping为非必需参数,自定义参数与属性的映射关系 func (r *Request) GetRequestToStruct(pointer interface{}, mapping ...map[string]string) error { - tagMap := r.getStructParamsTagMap(pointer) + tagMap := structs.TagMapName(pointer, paramTagPriority, true) if len(mapping) > 0 { for k, v := range mapping[0] { tagMap[k] = v @@ -148,5 +149,5 @@ func (r *Request) GetRequestToStruct(pointer interface{}, mapping ...map[string] params = j.ToMap() } } - return gconv.Struct(params, pointer, tagMap) + return gconv.StructDeep(params, pointer, tagMap) } diff --git a/g/net/ghttp/ghttp_unit_param_test.go b/g/net/ghttp/ghttp_unit_param_test.go index 91ea6a4b1..e04278730 100644 --- a/g/net/ghttp/ghttp_unit_param_test.go +++ b/g/net/ghttp/ghttp_unit_param_test.go @@ -4,16 +4,16 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// 请求参数测试 package ghttp_test import ( "fmt" + "testing" + "time" + "github.com/gogf/gf/g" "github.com/gogf/gf/g/net/ghttp" "github.com/gogf/gf/g/test/gtest" - "testing" - "time" ) func Test_Params_Basic(t *testing.T) { @@ -97,6 +97,30 @@ func Test_Params_Basic(t *testing.T) { r.Response.Write(user.Id, user.Name, user.Pass1, user.Pass2) } }) + s.BindHandler("/struct-with-base", func(r *ghttp.Request) { + type Base struct { + Pass1 string `params:"password1"` + Pass2 string `params:"password2"` + } + type UserWithBase1 struct { + Id int + Name string + Base + } + type UserWithBase2 struct { + Id int + Name string + Pass Base + } + if m := r.GetPostMap(); len(m) > 0 { + user1 := new(UserWithBase1) + user2 := new(UserWithBase2) + r.GetToStruct(user1) + r.GetToStruct(user2) + r.Response.Write(user1.Id, user1.Name, user1.Pass1, user1.Pass2) + r.Response.Write(user2.Id, user2.Name, user2.Pass.Pass1, user2.Pass.Pass2) + } + }) s.SetPort(p) s.SetDumpRouteMap(false) s.Start() @@ -144,5 +168,6 @@ func Test_Params_Basic(t *testing.T) { // Struct gtest.Assert(client.GetContent("/struct", `id=1&name=john&password1=123&password2=456`), `1john123456`) gtest.Assert(client.PostContent("/struct", `id=1&name=john&password1=123&password2=456`), `1john123456`) + gtest.Assert(client.PostContent("/struct-with-base", `id=1&name=john&password1=123&password2=456`), "1john1234561john123456") }) } diff --git a/g/os/gfsnotify/gfsnotify_watcher.go b/g/os/gfsnotify/gfsnotify_watcher.go index 0e153d42d..ddd12bc35 100644 --- a/g/os/gfsnotify/gfsnotify_watcher.go +++ b/g/os/gfsnotify/gfsnotify_watcher.go @@ -44,9 +44,10 @@ func (w *Watcher) addWithCallbackFunc(path string, callbackFunc func(event *Even path = t } callback = &Callback{ - Id: callbackIdGenerator.Add(1), - Func: callbackFunc, - Path: path, + Id: callbackIdGenerator.Add(1), + Func: callbackFunc, + Path: path, + recursive: true, } if len(recursive) > 0 { callback.recursive = recursive[0] diff --git a/g/os/gfsnotify/gfsnotify_watcher_loop.go b/g/os/gfsnotify/gfsnotify_watcher_loop.go index 1199fb65f..0fe2215a3 100644 --- a/g/os/gfsnotify/gfsnotify_watcher_loop.go +++ b/g/os/gfsnotify/gfsnotify_watcher_loop.go @@ -7,6 +7,8 @@ package gfsnotify import ( + "fmt" + "github.com/gogf/gf/g/container/glist" ) @@ -21,7 +23,7 @@ func (w *Watcher) startWatchLoop() { // 监听事件 case ev := <-w.watcher.Events: - //fmt.Println("ev:", ev.String()) + fmt.Println("ev:", ev.String()) w.cache.SetIfNotExist(ev.String(), func() interface{} { w.events.Push(&Event{ event: ev, diff --git a/g/test/gtest/gtest.go b/g/test/gtest/gtest.go index d2c6eb40e..a27f91c34 100644 --- a/g/test/gtest/gtest.go +++ b/g/test/gtest/gtest.go @@ -285,7 +285,8 @@ func compareMap(value, expect interface{}) error { } for k, v := range mExpect { if v != mValue[k] { - return fmt.Errorf(`[ASSERT] EXPECT VALUE map["%v"]:%v == %v`, k, mValue[k], v) + return fmt.Errorf(`[ASSERT] EXPECT VALUE map["%v"]:%v == map["%v"]:%v`+ + "\nGIVEN : %v\nEXPECT: %v", k, mValue[k], k, v, mValue, mExpect) } } } else { diff --git a/g/util/gconv/gconv.go b/g/util/gconv/gconv.go index 9e7dfb535..15007083e 100644 --- a/g/util/gconv/gconv.go +++ b/g/util/gconv/gconv.go @@ -9,10 +9,11 @@ package gconv import ( "encoding/json" - "github.com/gogf/gf/g/encoding/gbinary" "reflect" "strconv" "strings" + + "github.com/gogf/gf/g/encoding/gbinary" ) // Type assert api for String(). @@ -25,6 +26,10 @@ type apiError interface { Error() string } +const ( + gGCONV_TAG = "gconv" +) + var ( // Empty strings. emptyStringMap = map[string]struct{}{ @@ -33,6 +38,9 @@ var ( "off": struct{}{}, "false": struct{}{}, } + + // Priority tags for Map*/Struct* functions. + structTagPriority = []string{gGCONV_TAG, "json"} ) // Convert converts the variable to the type , the type is specified by string. diff --git a/g/util/gconv/gconv_map.go b/g/util/gconv/gconv_map.go index 817d6a691..fcc3f691c 100644 --- a/g/util/gconv/gconv_map.go +++ b/g/util/gconv/gconv_map.go @@ -7,20 +7,19 @@ package gconv import ( - "github.com/gogf/gf/g/internal/empty" - "github.com/gogf/gf/g/text/gstr" "reflect" "strings" -) -const ( - gGCONV_TAG = "gconv" + "github.com/gogf/gf/g/internal/empty" + "github.com/gogf/gf/g/text/gstr" ) // Map converts any variable to map[string]interface{}. -// If the parameter is not a map type, then the conversion will fail and returns nil. -// If is a struct object, the second parameter specifies the most priority -// tags that will be detected, otherwise it detects the tags in order of: gconv, json. +// +// If the parameter is not a map/struct/*struct type, then the conversion will fail and returns nil. +// +// If is a struct/*struct object, the second parameter specifies the most priority +// tags that will be detected, otherwise it detects the tags in order of: gconv, json, and then the field name. func Map(value interface{}, tags ...string) map[string]interface{} { if value == nil { return nil @@ -105,17 +104,14 @@ func Map(value interface{}, tags ...string) map[string]interface{} { case reflect.Struct: rt := rv.Type() name := "" - tagArray := []string{gGCONV_TAG, "json"} + tagArray := structTagPriority switch len(tags) { case 0: // No need handle. case 1: - tagArray = strings.Split(tags[0], ",") + tagArray = append(strings.Split(tags[0], ","), structTagPriority...) default: - tagArray = tags - } - if gstr.SearchArray(tagArray, gGCONV_TAG) < 0 { - tagArray = append(tagArray, gGCONV_TAG) + tagArray = append(tags, structTagPriority...) } for i := 0; i < rv.NumField(); i++ { // Only convert the public attributes. diff --git a/g/util/gconv/gconv_slice.go b/g/util/gconv/gconv_slice.go index 28ef116e7..d9f57d500 100644 --- a/g/util/gconv/gconv_slice.go +++ b/g/util/gconv/gconv_slice.go @@ -7,8 +7,11 @@ package gconv import ( - "github.com/gogf/gf/g/text/gstr" + "errors" + "fmt" "reflect" + + "github.com/gogf/gf/g/text/gstr" ) // Ints converts to []int. @@ -306,9 +309,7 @@ func Interfaces(i interface{}) []interface{} { kind = rv.Kind() } switch kind { - case reflect.Slice: - fallthrough - case reflect.Array: + case reflect.Slice, reflect.Array: for i := 0; i < rv.Len(); i++ { array = append(array, rv.Index(i).Interface()) } @@ -348,3 +349,79 @@ func Maps(i interface{}) []map[string]interface{} { return list } } + +// Structs converts any slice to given struct slice. +func Structs(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { + return doStructs(params, pointer, false, mapping...) +} + +// StructsDeep converts any slice to given struct slice recursively. +func StructsDeep(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { + return doStructs(params, pointer, true, mapping...) +} + +// doStructs converts any slice to given struct slice. +// +// The parameter should be type of slice. +// +// The parameter should be type of pointer to slice of struct. +// Note that if is a pointer to another pointer of type of slice of struct, +// it will create the struct/pointer internally. +func doStructs(params interface{}, pointer interface{}, deep bool, mapping ...map[string]string) (err error) { + if params == nil { + return errors.New("params cannot be nil") + } + if pointer == nil { + return errors.New("object pointer cannot be nil") + } + pointerRt := reflect.TypeOf(pointer) + if kind := pointerRt.Kind(); kind != reflect.Ptr { + return fmt.Errorf("pointer should be type of pointer, but got: %v", kind) + } + + rv := reflect.ValueOf(params) + kind := rv.Kind() + if kind == reflect.Ptr { + rv = rv.Elem() + kind = rv.Kind() + } + switch kind { + case reflect.Slice, reflect.Array: + array := reflect.MakeSlice(pointerRt.Elem(), rv.Len(), rv.Len()) + itemType := array.Index(0).Type() + for i := 0; i < rv.Len(); i++ { + if itemType.Kind() == reflect.Ptr { + // Slice element is type pointer. + e := reflect.New(itemType.Elem()).Elem() + if deep { + if err = StructDeep(rv.Index(i).Interface(), e, mapping...); err != nil { + return err + } + } else { + if err = Struct(rv.Index(i).Interface(), e, mapping...); err != nil { + return err + } + } + array.Index(i).Set(e.Addr()) + } else { + // Slice element is not type of pointer. + e := reflect.New(itemType).Elem() + + if deep { + if err = StructDeep(rv.Index(i).Interface(), e, mapping...); err != nil { + return err + } + } else { + if err = Struct(rv.Index(i).Interface(), e, mapping...); err != nil { + return err + } + } + array.Index(i).Set(e) + } + } + reflect.ValueOf(pointer).Elem().Set(array) + return nil + default: + return fmt.Errorf("params should be type of slice, but got: %v", kind) + } +} diff --git a/g/util/gconv/gconv_struct.go b/g/util/gconv/gconv_struct.go index 44b9b436a..e3a0f5137 100644 --- a/g/util/gconv/gconv_struct.go +++ b/g/util/gconv/gconv_struct.go @@ -9,10 +9,11 @@ package gconv import ( "errors" "fmt" - "github.com/gogf/gf/g/text/gstr" - "github.com/gogf/gf/third/github.com/fatih/structs" "reflect" "strings" + + "github.com/gogf/gf/g/internal/structs" + "github.com/gogf/gf/g/text/gstr" ) // Struct maps the params key-value pairs to the corresponding struct object's properties. @@ -51,6 +52,13 @@ func Struct(params interface{}, pointer interface{}, mapping ...map[string]strin return errors.New("object pointer cannot be nil") } elem = rv.Elem() + // Auto create struct object. + // For example, if is **User, then is *User, which is a pointer to User. + if elem.Type().Kind() == reflect.Ptr && (!elem.IsValid() || elem.IsNil()) { + e := reflect.New(elem.Type().Elem()).Elem() + elem.Set(e.Addr()) + elem = e + } } // It only performs one converting to the same attribute. // doneMap is used to check repeated converting. @@ -67,7 +75,7 @@ func Struct(params interface{}, pointer interface{}, mapping ...map[string]strin } } // It secondly checks the tags of attributes. - tagMap := getTagMapOfStruct(pointer) + tagMap := structs.TagMapName(pointer, structTagPriority, true) for tagK, tagV := range tagMap { if _, ok := doneMap[tagV]; ok { continue @@ -167,31 +175,6 @@ func StructDeep(params interface{}, pointer interface{}, mapping ...map[string]s return nil } -// 解析指针对象的tag -func getTagMapOfStruct(pointer interface{}) map[string]string { - tagMap := make(map[string]string) - // 反射类型判断 - fields := ([]*structs.Field)(nil) - if v, ok := pointer.(reflect.Value); ok { - fields = structs.Fields(v.Interface()) - } else { - fields = structs.Fields(pointer) - } - // 将struct中定义的属性转换名称构建成tagmap - for _, field := range fields { - tag := field.Tag("gconv") - if tag == "" { - tag = field.Tag("json") - } - if tag != "" { - for _, v := range strings.Split(tag, ",") { - tagMap[strings.TrimSpace(v)] = field.Name() - } - } - } - return tagMap -} - // 将参数值绑定到对象指定名称的属性上 func bindVarToStructAttr(elem reflect.Value, name string, value interface{}) (err error) { structFieldValue := elem.FieldByName(name) diff --git a/g/util/gconv/gconv_z_all_test.go b/g/util/gconv/gconv_z_all_test.go index d0a92d3f1..ba56afed9 100644 --- a/g/util/gconv/gconv_z_all_test.go +++ b/g/util/gconv/gconv_z_all_test.go @@ -1,12 +1,19 @@ +// Copyright 2019 gf Author(https://github.com/gogf/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://github.com/gogf/gf. + package gconv_test import ( + "testing" + "time" + "github.com/gogf/gf/g" "github.com/gogf/gf/g/os/gtime" "github.com/gogf/gf/g/test/gtest" "github.com/gogf/gf/g/util/gconv" - "testing" - "time" ) type apiString interface { diff --git a/g/util/gconv/gconv_z_unit_slice_test.go b/g/util/gconv/gconv_z_unit_slice_test.go index 017a3d9b6..3b7c54322 100644 --- a/g/util/gconv/gconv_z_unit_slice_test.go +++ b/g/util/gconv/gconv_z_unit_slice_test.go @@ -7,10 +7,11 @@ package gconv_test import ( + "testing" + "github.com/gogf/gf/g" "github.com/gogf/gf/g/test/gtest" "github.com/gogf/gf/g/util/gconv" - "testing" ) func Test_Slice(t *testing.T) { @@ -35,3 +36,50 @@ func Test_Slice_PrivateAttribute(t *testing.T) { gtest.Assert(gconv.Interfaces(user), g.Slice{1}) }) } + +func Test_Slice_Structs(t *testing.T) { + type Base struct { + Age int + } + type User struct { + Id int + Name string + Base + } + + gtest.Case(t, func() { + users := make([]User, 0) + params := []g.Map{ + {"id": 1, "name": "john", "age": 18}, + {"id": 2, "name": "smith", "age": 20}, + } + err := gconv.Structs(params, &users) + gtest.Assert(err, nil) + gtest.Assert(len(users), 2) + gtest.Assert(users[0].Id, params[0]["id"]) + gtest.Assert(users[0].Name, params[0]["name"]) + gtest.Assert(users[0].Age, 0) + + gtest.Assert(users[1].Id, params[1]["id"]) + gtest.Assert(users[1].Name, params[1]["name"]) + gtest.Assert(users[1].Age, 0) + }) + + gtest.Case(t, func() { + users := make([]User, 0) + params := []g.Map{ + {"id": 1, "name": "john", "age": 18}, + {"id": 2, "name": "smith", "age": 20}, + } + err := gconv.StructsDeep(params, &users) + gtest.Assert(err, nil) + gtest.Assert(len(users), 2) + gtest.Assert(users[0].Id, params[0]["id"]) + gtest.Assert(users[0].Name, params[0]["name"]) + gtest.Assert(users[0].Age, params[0]["age"]) + + gtest.Assert(users[1].Id, params[1]["id"]) + gtest.Assert(users[1].Name, params[1]["name"]) + gtest.Assert(users[1].Age, params[1]["age"]) + }) +} diff --git a/g/util/gconv/gconv_z_unit_struct_test.go b/g/util/gconv/gconv_z_unit_struct_test.go index 629ae56d0..a424eab2e 100644 --- a/g/util/gconv/gconv_z_unit_struct_test.go +++ b/g/util/gconv/gconv_z_unit_struct_test.go @@ -7,12 +7,13 @@ package gconv_test import ( + "testing" + "time" + "github.com/gogf/gf/g" "github.com/gogf/gf/g/os/gtime" "github.com/gogf/gf/g/test/gtest" "github.com/gogf/gf/g/util/gconv" - "testing" - "time" ) func Test_Struct_Basic1(t *testing.T) { @@ -25,9 +26,8 @@ func Test_Struct_Basic1(t *testing.T) { Pass1 string `gconv:"password1"` Pass2 string `gconv:"password2"` } - user := (*User)(nil) // 使用默认映射规则绑定属性值到对象 - user = new(User) + user := new(User) params1 := g.Map{ "uid": 1, "Name": "john", @@ -338,6 +338,29 @@ func Test_Struct_PrivateAttribute(t *testing.T) { } func Test_Struct_Deep(t *testing.T) { + + gtest.Case(t, func() { + type Base struct { + Age int + } + type User struct { + Id int + Name string + Base + } + user := new(User) + params := g.Map{ + "id": 1, + "name": "john", + "age": 18, + } + err := gconv.StructDeep(params, user) + gtest.Assert(err, nil) + gtest.Assert(user.Id, params["id"]) + gtest.Assert(user.Name, params["name"]) + gtest.Assert(user.Age, params["age"]) + }) + gtest.Case(t, func() { type Ids struct { Id int `json:"id"` @@ -362,7 +385,8 @@ func Test_Struct_Deep(t *testing.T) { "create_time": "2019", } user := new(User) - gconv.StructDeep(data, user) + err := gconv.StructDeep(data, user) + gtest.Assert(err, nil) gtest.Assert(user.Id, 100) gtest.Assert(user.Uid, 101) gtest.Assert(user.Nickname, "T1") @@ -431,3 +455,38 @@ func Test_Struct_Time(t *testing.T) { gtest.Assert(user.CreateTime.Time.UTC().String(), now.UTC().String()) }) } + +// Auto create struct when given pointer. +func Test_Struct_Create(t *testing.T) { + gtest.Case(t, func() { + type User struct { + Uid int + Name string + } + user := (*User)(nil) + params := g.Map{ + "uid": 1, + "Name": "john", + } + err := gconv.Struct(params, &user) + gtest.Assert(err, nil) + gtest.Assert(user.Uid, 1) + gtest.Assert(user.Name, "john") + }) + + gtest.Case(t, func() { + type User struct { + Uid int + Name string + } + user := (*User)(nil) + params := g.Map{ + "uid": 1, + "Name": "john", + } + err := gconv.Struct(params, user) + gtest.AssertNE(err, nil) + gtest.Assert(user, nil) + }) + +} diff --git a/g/util/gvalid/gvalid_check_struct.go b/g/util/gvalid/gvalid_check_struct.go index cc025d722..fba014f95 100644 --- a/g/util/gvalid/gvalid_check_struct.go +++ b/g/util/gvalid/gvalid_check_struct.go @@ -9,15 +9,19 @@ package gvalid import ( "strings" - "github.com/gogf/gf/g/text/gstr" + "github.com/gogf/gf/g/internal/structs" + "github.com/gogf/gf/g/util/gconv" - "github.com/gogf/gf/third/github.com/fatih/structs" +) + +var ( + // 同时支持valid和gvalid标签,优先使用valid + structTagPriority = []string{"valid", "gvalid"} ) // 校验struct对象属性,object参数也可以是一个指向对象的指针,返回值同CheckMap方法。 // struct的数据校验结果信息是顺序的。 func CheckStruct(object interface{}, rules interface{}, msgs ...CustomMsg) *Error { - fields := structs.Fields(object) params := make(map[string]interface{}) checkRules := make(map[string]string) customMsgs := make(CustomMsg) @@ -57,26 +61,25 @@ func CheckStruct(object interface{}, rules interface{}, msgs ...CustomMsg) *Erro errorRules = append(errorRules, name+"@"+rule) } - // 不支持校验错误顺序: map[键名]校验规则 + // 不支持校验错误顺序: map[键名]校验规则 case map[string]string: checkRules = v } // 首先, 按照属性循环一遍将struct的属性、数值、tag解析 - for _, field := range fields { + tagValue := "" + for _, field := range structs.MapField(object, structTagPriority, true) { fieldName := field.Name() - // 只检测公开属性 - if !gstr.IsLetterUpper(fieldName[0]) { - continue - } params[fieldName] = field.Value() - // 同时支持valid和gvalid标签,优先使用valid - tag := field.Tag("valid") - if tag == "" { - tag = field.Tag("gvalid") + tagValue = "" + for _, v := range structTagPriority { + tagValue = field.Tag(v) + if tagValue != "" { + break + } } - if tag != "" { + if tagValue != "" { // sequence tag == struct tag, 这里的name为别名 - name, rule, msg := parseSequenceTag(tag) + name, rule, msg := parseSequenceTag(tagValue) if len(name) == 0 { name = fieldName } diff --git a/g/util/gvalid/gvalid_error.go b/g/util/gvalid/gvalid_error.go index d25c2d4bb..da7bd34d9 100644 --- a/g/util/gvalid/gvalid_error.go +++ b/g/util/gvalid/gvalid_error.go @@ -4,18 +4,16 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// 返回错误对象。 - package gvalid import "strings" // 校验错误对象 type Error struct { - rules []string // 校验结果顺序(可能为nil) - errors ErrorMap // 校验结果(map无序) - firstKey string // 第一条错误项键名(常用操作冗余数据) - firstItem map[string]string // 第一条错误项(常用操作冗余数据) + rules []string // 校验结果顺序(可能为nil),可保证返回校验错误的顺序性 + errors ErrorMap // 完整的数据校验结果存储(map无序) + firstKey string // 第一条错误项键名(常用操作冗余数据),默认为空 + firstItem map[string]string // 第一条错误项(常用操作冗余数据),默认为nil } // 校验错误信息: map[键名]map[规则名]错误信息 diff --git a/g/util/gvalid/gvalid_unit_checkstruct_test.go b/g/util/gvalid/gvalid_unit_checkstruct_test.go index 40e3178f0..d5461a62f 100644 --- a/g/util/gvalid/gvalid_unit_checkstruct_test.go +++ b/g/util/gvalid/gvalid_unit_checkstruct_test.go @@ -9,6 +9,8 @@ package gvalid_test import ( "testing" + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/test/gtest" "github.com/gogf/gf/g/util/gvalid" ) @@ -90,3 +92,29 @@ func Test_CheckStruct(t *testing.T) { gtest.Assert(err.Maps()["uid"]["min"], "ID不能为空") }) } + +func Test_CheckStruct_With_Inherit(t *testing.T) { + gtest.Case(t, func() { + type Pass struct { + Pass1 string `valid:"password1@required|same:password2#请输入您的密码|您两次输入的密码不一致"` + Pass2 string `valid:"password2@required|same:password1#请再次输入您的密码|您两次输入的密码不一致"` + } + type User struct { + Id int + Name string `valid:"name@required#请输入您的姓名"` + Pass Pass + } + user := &User{ + Name: "", + Pass: Pass{ + Pass1: "1", + Pass2: "2", + }, + } + err := gvalid.CheckStruct(user, nil) + gtest.AssertNE(err, nil) + gtest.Assert(err.Maps()["name"], g.Map{"required": "请输入您的姓名"}) + gtest.Assert(err.Maps()["password1"], g.Map{"same": "您两次输入的密码不一致"}) + gtest.Assert(err.Maps()["password2"], g.Map{"same": "您两次输入的密码不一致"}) + }) +} diff --git a/geg/database/gdb/mysql/gdb_struct.go b/geg/database/gdb/mysql/gdb_struct.go new file mode 100644 index 000000000..1a3cb26de --- /dev/null +++ b/geg/database/gdb/mysql/gdb_struct.go @@ -0,0 +1,23 @@ +package main + +import ( + "fmt" + + "github.com/gogf/gf/g" +) + +func main() { + db := g.DB() + // 开启调试模式,以便于记录所有执行的SQL + db.SetDebug(true) + + type User struct { + Uid int + Name string + } + user := (*User)(nil) + fmt.Println(user) + err := db.Table("test").Where("id=1").Struct(&user) + fmt.Println(err) + fmt.Println(user) +} diff --git a/geg/database/gdb/mysql/gdb_value.go b/geg/database/gdb/mysql/gdb_value.go index e53c42376..5174fcef5 100644 --- a/geg/database/gdb/mysql/gdb_value.go +++ b/geg/database/gdb/mysql/gdb_value.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "github.com/gogf/gf/g" ) diff --git a/geg/os/gfsnotify/gfsnotify.go b/geg/os/gfsnotify/gfsnotify.go index bc437a6f3..f93992764 100644 --- a/geg/os/gfsnotify/gfsnotify.go +++ b/geg/os/gfsnotify/gfsnotify.go @@ -6,11 +6,11 @@ import ( ) func main() { - //path := "D:\\Workspace\\Go\\GOPATH\\src\\gitee.com\\johng\\gf\\geg\\other\\test.go" - path := "/Users/john/Temp/test" + //path := `D:\temp` + path := "/Users/john/Temp" _, err := gfsnotify.Add(path, func(event *gfsnotify.Event) { glog.Println(event) - }, true) + }) if err != nil { glog.Fatal(err) } else { diff --git a/geg/os/gfsnotify/gfsnotify_callback.go b/geg/os/gfsnotify/gfsnotify_callback.go index 99ced3a76..a246ad2f2 100644 --- a/geg/os/gfsnotify/gfsnotify_callback.go +++ b/geg/os/gfsnotify/gfsnotify_callback.go @@ -3,7 +3,7 @@ package main import ( "github.com/gogf/gf/g/os/gfsnotify" "github.com/gogf/gf/g/os/glog" - "github.com/gogf/gf/g/os/gtime" + "github.com/gogf/gf/g/os/gtimer" "time" ) @@ -21,12 +21,12 @@ func main() { panic(err) } // 5秒后移除c1的回调函数注册,仅剩c2 - gtime.SetTimeout(5*time.Second, func() { + gtimer.SetTimeout(5*time.Second, func() { gfsnotify.RemoveCallback(c1.Id) glog.Println("remove callback c1") }) // 10秒后移除c2的回调函数注册,所有的回调都移除,不再有任何打印信息输出 - gtime.SetTimeout(10*time.Second, func() { + gtimer.SetTimeout(10*time.Second, func() { gfsnotify.RemoveCallback(c2.Id) glog.Println("remove callback c2") }) diff --git a/geg/os/gfsnotify/gfsnotify_callback_folder.go b/geg/os/gfsnotify/gfsnotify_callback_folder.go index 2d9bd17c5..1c9a83b1d 100644 --- a/geg/os/gfsnotify/gfsnotify_callback_folder.go +++ b/geg/os/gfsnotify/gfsnotify_callback_folder.go @@ -3,7 +3,7 @@ package main import ( "github.com/gogf/gf/g/os/gfsnotify" "github.com/gogf/gf/g/os/glog" - "github.com/gogf/gf/g/os/gtime" + "github.com/gogf/gf/g/os/gtimer" "time" ) @@ -18,7 +18,7 @@ func main() { // 在此期间创建文件、目录、修改文件、删除文件 // 20秒后移除回调函数注册,所有的回调都移除,不再有任何打印信息输出 - gtime.SetTimeout(20*time.Second, func() { + gtimer.SetTimeout(20*time.Second, func() { gfsnotify.RemoveCallback(callback.Id) glog.Println("remove callback") }) diff --git a/geg/os/gfsnotify/gfsnotify_limit.go b/geg/os/gfsnotify/gfsnotify_limit.go index 1e431e345..96776169e 100644 --- a/geg/os/gfsnotify/gfsnotify_limit.go +++ b/geg/os/gfsnotify/gfsnotify_limit.go @@ -13,7 +13,7 @@ func main() { glog.Println(event) }) if err != nil { - glog.Fatalln(err) + glog.Fatal(err) } } } diff --git a/geg/other/terminal-color.go b/geg/other/terminal-color.go index 042290cf6..cb0f1dd63 100644 --- a/geg/other/terminal-color.go +++ b/geg/other/terminal-color.go @@ -27,9 +27,12 @@ func main() { // 7 反白显示 // 8 不可见 - for b := 40; b <= 47; b++ { // 背景色彩 = 40-47 - for f := 30; f <= 37; f++ { // 前景色彩 = 30-37 - for d := range []int{0, 1, 4, 5, 7, 8} { // 显示方式 = 0,1,4,5,7,8 + // 背景色彩 = 40-47 + for b := 40; b <= 47; b++ { + // 前景色彩 = 30-37 + for f := 30; f <= 37; f++ { + // 显示方式 = 0,1,4,5,7,8 + for _, d := range []int{0, 1, 4, 5, 7, 8} { fmt.Printf(" %c[%d;%d;%dm%s(f=%d,b=%d,d=%d)%c[0m ", 0x1B, d, b, f, "", f, b, d, 0x1B) } fmt.Println("") diff --git a/geg/other/test.go b/geg/other/test.go index 60efffba1..b3630b512 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,6 +1,7 @@ package main import ( +<<<<<<< HEAD <<<<<<< HEAD "fmt" @@ -65,28 +66,33 @@ func main() { //fmt.Printf("%+v\n", Test2()) ======= "github.com/gogf/gf/g/os/glog" +======= + "fmt" +>>>>>>> d0fe2d2f75a91f44d59969bb25fda3729eabcdde - "github.com/gogf/gf/g/os/gcache" + "github.com/gogf/gf/g" ) -func localCache() { - result := gcache.GetOrSetFunc("test.key.1", func() interface{} { - return nil - }, 1000*60*2) - if result == nil { - glog.Error("未获取到值") - } else { - glog.Infofln("result is $v", result) - } -} - -func TestCache() { - for i := 0; i < 100; i++ { - localCache() - } +type User struct { + Uid int + Name string } func main() { +<<<<<<< HEAD TestCache() >>>>>>> c90ed0d4242527435a3b4c9d7c27742d29c9aaa1 +======= + if r, err := g.DB().Table("user").Where("uid=?", 1).One(); r != nil { + u := new(User) + if err := r.ToStruct(u); err == nil { + fmt.Println(" uid:", u.Uid) + fmt.Println("name:", u.Name) + } else { + fmt.Println(err) + } + } else if err != nil { + fmt.Println(err) + } +>>>>>>> d0fe2d2f75a91f44d59969bb25fda3729eabcdde } diff --git a/geg/util/gconv/gconv_map_tag.go b/geg/util/gconv/gconv_map_tag.go new file mode 100644 index 000000000..b7f653e44 --- /dev/null +++ b/geg/util/gconv/gconv_map_tag.go @@ -0,0 +1,18 @@ +package main + +import ( + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/util/gconv" +) + +func main() { + type User struct { + Id int `json:"uid"` + Name string `my-tag:"nick-name" json:"name"` + } + user := &User{ + Id: 1, + Name: "john", + } + g.Dump(gconv.Map(user, "my-tag")) +} diff --git a/geg/util/gconv/gconv_struct_create.go b/geg/util/gconv/gconv_struct_create.go new file mode 100644 index 000000000..54d22e54d --- /dev/null +++ b/geg/util/gconv/gconv_struct_create.go @@ -0,0 +1,23 @@ +package main + +import ( + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/util/gconv" +) + +func main() { + type User struct { + Uid int + Name string + } + user := (*User)(nil) + params := g.Map{ + "uid": 1, + "name": "john", + } + err := gconv.Struct(params, &user) + if err != nil { + panic(err) + } + g.Dump(user) +} diff --git a/version.go b/version.go index ca2f542ef..103986c4d 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package gf -const VERSION = "v1.7.1" +const VERSION = "v1.7.2" const AUTHORS = "john"