mirror of
https://gitee.com/johng/gf
synced 2026-06-06 16:21:40 +08:00
1
TODO.MD
1
TODO.MD
@ -49,6 +49,7 @@
|
||||
1. gdb的Cache缓存功能增加可自定义缓存接口,以便支持外部缓存功能,缓存接口可以通过io.ReadWriter接口实现;
|
||||
1. grpool增加支持阻塞添加任务接口;
|
||||
1. gdb.Model在链式安全的对象创建中增加sync.Pool的使用;
|
||||
1. 增加g.Table快捷方法以方便操作数据表,但是得考虑后续模型操作设计,特别是脚手架的模型管理;
|
||||
|
||||
|
||||
# DONE
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
// 数据库查询,获取查询字段值
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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(objPointerSlice interface{}) (err error) {
|
||||
l := len(r)
|
||||
if l == 0 {
|
||||
return nil
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
t := reflect.TypeOf(objPointerSlice)
|
||||
if t.Kind() != reflect.Ptr {
|
||||
return fmt.Errorf("params 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(objPointerSlice).Elem().Set(array)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
package gdb_test
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g"
|
||||
@ -357,6 +358,19 @@ func TestModel_Struct(t *testing.T) {
|
||||
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) {
|
||||
@ -404,6 +418,19 @@ func TestModel_Structs(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
|
||||
}
|
||||
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 +510,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) {
|
||||
|
||||
15
g/internal/structs/structs.go
Normal file
15
g/internal/structs/structs.go
Normal file
@ -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
|
||||
64
g/internal/structs/structs_map.go
Normal file
64
g/internal/structs/structs_map.go
Normal file
@ -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 <pointer>, and returns it.
|
||||
//
|
||||
// The parameter <recursive> 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
|
||||
}
|
||||
81
g/internal/structs/structs_tag.go
Normal file
81
g/internal/structs/structs_tag.go
Normal file
@ -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 <pointer>, and returns it.
|
||||
//
|
||||
// The parameter <recursive> 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 <pointer>, and returns it.
|
||||
//
|
||||
// The parameter <recursive> 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
|
||||
}
|
||||
73
g/internal/structs/structs_test.go
Normal file
73
g/internal/structs/structs_test.go
Normal file
@ -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"})
|
||||
})
|
||||
}
|
||||
@ -6,3 +6,7 @@
|
||||
|
||||
// Package ghttp provides powerful http server and simple client implements.
|
||||
package ghttp
|
||||
|
||||
var (
|
||||
paramTagPriority = []string{"param", "params"}
|
||||
)
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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")
|
||||
})
|
||||
}
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -9,13 +9,14 @@ package gtest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
// Case creates an unit test case.
|
||||
@ -284,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 {
|
||||
|
||||
@ -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 <i> to the type <t>, the type <t> is specified by string.
|
||||
|
||||
@ -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 <value> to map[string]interface{}.
|
||||
// If the parameter <value> is not a map type, then the conversion will fail and returns nil.
|
||||
// If <value> is a struct object, the second parameter <tags> specifies the most priority
|
||||
// tags that will be detected, otherwise it detects the tags in order of: gconv, json.
|
||||
//
|
||||
// If the parameter <value> is not a map/struct/*struct type, then the conversion will fail and returns nil.
|
||||
//
|
||||
// If <value> is a struct/*struct object, the second parameter <tags> 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.
|
||||
|
||||
@ -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.
|
||||
@ -67,7 +68,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 +168,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)
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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[规则名]错误信息
|
||||
|
||||
@ -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": "您两次输入的密码不一致"})
|
||||
})
|
||||
}
|
||||
|
||||
@ -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 := "/home/john/temp"
|
||||
_, err := gfsnotify.Add(path, func(event *gfsnotify.Event) {
|
||||
glog.Println(event)
|
||||
}, true)
|
||||
})
|
||||
if err != nil {
|
||||
glog.Fatal(err)
|
||||
} else {
|
||||
|
||||
@ -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")
|
||||
})
|
||||
|
||||
@ -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")
|
||||
})
|
||||
|
||||
@ -13,7 +13,7 @@ func main() {
|
||||
glog.Println(event)
|
||||
})
|
||||
if err != nil {
|
||||
glog.Fatalln(err)
|
||||
glog.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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("")
|
||||
|
||||
@ -1,28 +1,40 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/os/glog"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/g/os/gcache"
|
||||
"github.com/gogf/gf/g"
|
||||
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
|
||||
"github.com/gogf/gf/g/encoding/gparser"
|
||||
)
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
TestCache()
|
||||
type User struct {
|
||||
Uid int
|
||||
Name string
|
||||
SiteUrl string `gconv:"-"`
|
||||
NickName string `gconv:"nickname, omitempty"`
|
||||
Pass1 string `gconv:"password1"`
|
||||
Pass2 string `gconv:"password2"`
|
||||
}
|
||||
|
||||
g.Dump(gconv.Map(User{
|
||||
Uid: 100,
|
||||
Name: "john",
|
||||
SiteUrl: "https://goframe.org",
|
||||
Pass1: "123",
|
||||
Pass2: "456",
|
||||
}))
|
||||
|
||||
s, err := gparser.VarToJsonString(User{
|
||||
Uid: 100,
|
||||
Name: "john",
|
||||
SiteUrl: "https://goframe.org",
|
||||
Pass1: "123",
|
||||
Pass2: "456",
|
||||
})
|
||||
fmt.Println(err)
|
||||
fmt.Println(s)
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package gf
|
||||
|
||||
const VERSION = "v1.7.1"
|
||||
const VERSION = "v1.7.2"
|
||||
const AUTHORS = "john<john@goframe.org>"
|
||||
|
||||
Reference in New Issue
Block a user