diff --git a/database/gdb/gdb_core_structure.go b/database/gdb/gdb_core_structure.go index 09ec8ce15..ca60f6906 100644 --- a/database/gdb/gdb_core_structure.go +++ b/database/gdb/gdb_core_structure.go @@ -159,6 +159,7 @@ func (c *Core) convertFieldValueToLocalValue(fieldValue interface{}, fieldType s return gconv.Bool(fieldValue) case "date": + // Date without time. if t, ok := fieldValue.(time.Time); ok { return gtime.NewFromTime(t).Format("Y-m-d") } @@ -173,7 +174,7 @@ func (c *Core) convertFieldValueToLocalValue(fieldValue interface{}, fieldType s return gtime.NewFromTime(t) } t, _ := gtime.StrToTime(gconv.String(fieldValue)) - return t.String() + return t default: // Auto-detect field type, using key match. @@ -199,7 +200,7 @@ func (c *Core) convertFieldValueToLocalValue(fieldValue interface{}, fieldType s if err != nil { return s } - return t.String() + return t case strings.Contains(typeName, "date"): s := gconv.String(fieldValue) @@ -207,7 +208,7 @@ func (c *Core) convertFieldValueToLocalValue(fieldValue interface{}, fieldType s if err != nil { return s } - return t.Format("Y-m-d") + return t default: return gconv.String(fieldValue) diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 0b6047ec2..4c20d26a7 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -374,13 +374,11 @@ func formatWhereHolder(db DB, in formatWhereHolderInput) (newWhere string, newAr case reflect.Map: for key, value := range DataToMapDeep(in.Where) { - if gregex.IsMatchString(regularFieldNameRegPattern, key) { - if in.OmitNil && empty.IsNil(value) { - continue - } - if in.OmitEmpty && empty.IsEmpty(value) { - continue - } + if in.OmitNil && empty.IsNil(value) { + continue + } + if in.OmitEmpty && empty.IsEmpty(value) { + continue } newArgs = formatWhereKeyValue(formatWhereKeyValueInput{ Db: db, @@ -406,13 +404,11 @@ func formatWhereHolder(db DB, in formatWhereHolderInput) (newWhere string, newAr if iterator, ok := in.Where.(iIterator); ok { iterator.Iterator(func(key, value interface{}) bool { ketStr := gconv.String(key) - if gregex.IsMatchString(regularFieldNameRegPattern, ketStr) { - if in.OmitNil && empty.IsNil(value) { - return true - } - if in.OmitEmpty && empty.IsEmpty(value) { - return true - } + if in.OmitNil && empty.IsNil(value) { + return true + } + if in.OmitEmpty && empty.IsEmpty(value) { + return true } newArgs = formatWhereKeyValue(formatWhereKeyValueInput{ Db: db, diff --git a/database/gdb/gdb_z_mysql_core_test.go b/database/gdb/gdb_z_mysql_core_test.go index 531efaf41..8b3fb8b95 100644 --- a/database/gdb/gdb_z_mysql_core_test.go +++ b/database/gdb/gdb_z_mysql_core_test.go @@ -1520,6 +1520,7 @@ func Test_Types(t *testing.T) { %s binary(8) NOT NULL, %s date NOT NULL, %s time NOT NULL, + %s timestamp(6) NOT NULL, %s decimal(5,2) NOT NULL, %s double NOT NULL, %s bit(2) NOT NULL, @@ -1532,6 +1533,7 @@ func Test_Types(t *testing.T) { "`binary`", "`date`", "`time`", + "`timestamp`", "`decimal`", "`double`", "`bit`", @@ -1541,16 +1543,17 @@ func Test_Types(t *testing.T) { } defer dropTable("types") data := g.Map{ - "id": 1, - "blob": "i love gf", - "binary": []byte("abcdefgh"), - "date": "1880-10-24", - "time": "10:00:01", - "decimal": -123.456, - "double": -123.456, - "bit": 2, - "tinyint": true, - "bool": false, + "id": 1, + "blob": "i love gf", + "binary": []byte("abcdefgh"), + "date": "1880-10-24", + "time": "10:00:01", + "timestamp": "2022-02-14 12:00:01.123456", + "decimal": -123.456, + "double": -123.456, + "bit": 2, + "tinyint": true, + "bool": false, } r, err := db.Model("types").Data(data).Insert() t.AssertNil(err) @@ -1564,21 +1567,23 @@ func Test_Types(t *testing.T) { t.Assert(one["binary"].String(), data["binary"]) t.Assert(one["date"].String(), data["date"]) t.Assert(one["time"].String(), `10:00:01`) + t.Assert(one["timestamp"].GTime().Format(`Y-m-d H:i:s.u`), `2022-02-14 12:00:01.123`) t.Assert(one["decimal"].String(), -123.46) t.Assert(one["double"].String(), data["double"]) t.Assert(one["bit"].Int(), data["bit"]) t.Assert(one["tinyint"].Bool(), data["tinyint"]) type T struct { - Id int - Blob []byte - Binary []byte - Date *gtime.Time - Time *gtime.Time - Decimal float64 - Double float64 - Bit int8 - TinyInt bool + Id int + Blob []byte + Binary []byte + Date *gtime.Time + Time *gtime.Time + Timestamp *gtime.Time + Decimal float64 + Double float64 + Bit int8 + TinyInt bool } var obj *T err = db.Model("types").Scan(&obj) @@ -1588,6 +1593,7 @@ func Test_Types(t *testing.T) { t.Assert(obj.Binary, data["binary"]) t.Assert(obj.Date.Format("Y-m-d"), data["date"]) t.Assert(obj.Time.String(), `10:00:01`) + t.Assert(obj.Timestamp.Format(`Y-m-d H:i:s.u`), `2022-02-14 12:00:01.123`) t.Assert(obj.Decimal, -123.46) t.Assert(obj.Double, data["double"]) t.Assert(obj.Bit, data["bit"]) diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index 7af800d37..c68e9eeb5 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -1454,7 +1454,7 @@ func Test_Model_Where_ISNULL_2(t *testing.T) { "create_time > 0": nil, "id": g.Slice{1, 2, 3}, } - result, err := db.Model(table).WherePri(conditions).Order("id asc").All() + result, err := db.Model(table).Where(conditions).Order("id asc").All() t.AssertNil(err) t.Assert(len(result), 3) t.Assert(result[0]["id"].Int(), 1) @@ -1468,19 +1468,19 @@ func Test_Model_Where_OmitEmpty(t *testing.T) { conditions := g.Map{ "id < 4": "", } - result, err := db.Model(table).WherePri(conditions).Order("id asc").All() + result, err := db.Model(table).Where(conditions).Order("id desc").All() t.AssertNil(err) t.Assert(len(result), 3) - t.Assert(result[0]["id"].Int(), 1) + t.Assert(result[0]["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { conditions := g.Map{ "id < 4": "", } - result, err := db.Model(table).WherePri(conditions).OmitEmpty().Order("id asc").All() + result, err := db.Model(table).Where(conditions).OmitEmpty().Order("id desc").All() t.AssertNil(err) - t.Assert(len(result), 3) - t.Assert(result[0]["id"].Int(), 1) + t.Assert(len(result), 10) + t.Assert(result[0]["id"].Int(), 10) }) } diff --git a/net/ghttp/ghttp_server_router.go b/net/ghttp/ghttp_server_router.go index f2a67048a..1fdcd5629 100644 --- a/net/ghttp/ghttp_server_router.go +++ b/net/ghttp/ghttp_server_router.go @@ -117,6 +117,10 @@ func (s *Server) setHandler(ctx context.Context, in setHandlerInput) { if prefix != "" { uri = prefix + "/" + strings.TrimLeft(uri, "/") } + uri = strings.TrimRight(uri, "/") + if uri == "" { + uri = "/" + } if len(uri) == 0 || uri[0] != '/' { s.Logger().Fatalf(ctx, `invalid pattern "%s", URI should lead with '/'`, pattern) diff --git a/net/ghttp/ghttp_z_unit_feature_request_json_test.go b/net/ghttp/ghttp_z_unit_feature_request_json_test.go index 78b77a80f..2ccbf81e4 100644 --- a/net/ghttp/ghttp_z_unit_feature_request_json_test.go +++ b/net/ghttp/ghttp_z_unit_feature_request_json_test.go @@ -58,7 +58,7 @@ func Test_Params_Json_Request(t *testing.T) { t.Assert(client.GetContent(ctx, "/get", `{"id":1,"name":"john","password1":"123Abc!@#","password2":"123Abc!@#"}`), ``) t.Assert(client.GetContent(ctx, "/map", `{"id":1,"name":"john","password1":"123Abc!@#","password2":"123Abc!@#"}`), ``) t.Assert(client.PostContent(ctx, "/parse", `{"id":1,"name":"john","password1":"123Abc!@#","password2":"123Abc!@#"}`), `1john123Abc!@#123Abc!@#`) - t.Assert(client.PostContent(ctx, "/parse", `{"id":1,"name":"john","password1":"123Abc!@#","password2":"123"}`), `密码强度不足; 两次密码不一致`) + t.Assert(client.PostContent(ctx, "/parse", `{"id":1,"name":"john","password1":"123Abc!@#","password2":"123"}`), `密码强度不足`) }) } diff --git a/net/ghttp/ghttp_z_unit_feature_request_struct_test.go b/net/ghttp/ghttp_z_unit_feature_request_struct_test.go index 45673c7f8..dc476c664 100644 --- a/net/ghttp/ghttp_z_unit_feature_request_struct_test.go +++ b/net/ghttp/ghttp_z_unit_feature_request_struct_test.go @@ -453,7 +453,7 @@ func Test_Params_Struct(t *testing.T) { t.Assert(client.PostContent(ctx, "/struct2", `id=1&name=john&password1=123&password2=456`), `1john123456`) t.Assert(client.PostContent(ctx, "/struct2", ``), ``) t.Assert(client.PostContent(ctx, "/struct-valid", `id=1&name=john&password1=123&password2=0`), "The password2 value `0` length must be between 2 and 20; 密码强度不足") - t.Assert(client.PostContent(ctx, "/parse", `id=1&name=john&password1=123&password2=0`), "The password2 value `0` length must be between 2 and 20; 密码强度不足") + t.Assert(client.PostContent(ctx, "/parse", `id=1&name=john&password1=123&password2=0`), "The password2 value `0` length must be between 2 and 20") t.Assert(client.PostContent(ctx, "/parse", `{"id":1,"name":"john","password1":"123Abc!@#","password2":"123Abc!@#"}`), `1john123Abc!@#123Abc!@#`) }) } diff --git a/net/ghttp/ghttp_z_unit_feature_request_test.go b/net/ghttp/ghttp_z_unit_feature_request_test.go index 23f1dc4cc..2c8646117 100644 --- a/net/ghttp/ghttp_z_unit_feature_request_test.go +++ b/net/ghttp/ghttp_z_unit_feature_request_test.go @@ -564,8 +564,8 @@ func Test_Params_Parse_Validation(t *testing.T) { client := g.Client() client.SetPrefix(prefix) - t.Assert(client.GetContent(ctx, "/parse"), `请输入账号; 账号长度为6到30位; 请输入密码; 密码长度不够; 请确认密码; 密码长度不够; 两次密码不一致`) - t.Assert(client.GetContent(ctx, "/parse?name=john11&password1=123456&password2=123"), `密码长度不够; 两次密码不一致`) + t.Assert(client.GetContent(ctx, "/parse"), `请输入账号`) + t.Assert(client.GetContent(ctx, "/parse?name=john11&password1=123456&password2=123"), `密码长度不够`) t.Assert(client.GetContent(ctx, "/parse?name=john&password1=123456&password2=123456"), `账号长度为6到30位`) t.Assert(client.GetContent(ctx, "/parse?name=john11&password1=123456&password2=123456"), `ok`) }) diff --git a/net/ghttp/ghttp_z_unit_feature_request_xml_test.go b/net/ghttp/ghttp_z_unit_feature_request_xml_test.go index cdb3e72f7..c0ac37e2e 100644 --- a/net/ghttp/ghttp_z_unit_feature_request_xml_test.go +++ b/net/ghttp/ghttp_z_unit_feature_request_xml_test.go @@ -61,6 +61,6 @@ func Test_Params_Xml_Request(t *testing.T) { t.Assert(client.GetContent(ctx, "/map", content1), ``) t.Assert(client.PostContent(ctx, "/map", content1), `1john123Abc!@#123Abc!@#`) t.Assert(client.PostContent(ctx, "/parse", content1), `1john123Abc!@#123Abc!@#`) - t.Assert(client.PostContent(ctx, "/parse", content2), `密码强度不足; 两次密码不一致`) + t.Assert(client.PostContent(ctx, "/parse", content2), `密码强度不足`) }) } diff --git a/net/ghttp/ghttp_z_unit_feature_router_group_test.go b/net/ghttp/ghttp_z_unit_feature_router_group_test.go index 0a4151c56..c7a253ea0 100644 --- a/net/ghttp/ghttp_z_unit_feature_router_group_test.go +++ b/net/ghttp/ghttp_z_unit_feature_router_group_test.go @@ -189,3 +189,26 @@ func Test_Router_Group_Map(t *testing.T) { t.Assert(c.PostContent(ctx, "/test"), "post") }) } + +// https://github.com/gogf/gf/issues/1609 +func Test_Issue1609(t *testing.T) { + p, _ := gtcp.GetFreePort() + s := g.Server(p) + group := s.Group("/api/get") + group.GET("/", func(r *ghttp.Request) { + r.Response.Write("get") + }) + s.SetPort(p) + s.SetDumpRouterMap(false) + gtest.Assert(s.Start(), nil) + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + gtest.C(t, func(t *gtest.T) { + c := g.Client() + c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) + + t.Assert(c.GetContent(ctx, "/api/get"), "get") + t.Assert(c.PostContent(ctx, "/test"), "Not Found") + }) +} diff --git a/os/gtimer/gtimer_z_unit_test.go b/os/gtimer/gtimer_z_unit_test.go index 61035c487..68ad0639a 100644 --- a/os/gtimer/gtimer_z_unit_test.go +++ b/os/gtimer/gtimer_z_unit_test.go @@ -121,12 +121,12 @@ func TestDelayAddSingleton(t *testing.T) { func TestDelayAddOnce(t *testing.T) { gtest.C(t, func(t *gtest.T) { array := garray.New(true) - gtimer.DelayAddOnce(ctx, 200*time.Millisecond, 200*time.Millisecond, func(ctx context.Context) { + gtimer.DelayAddOnce(ctx, 1000*time.Millisecond, 2000*time.Millisecond, func(ctx context.Context) { array.Append(1) }) - time.Sleep(300 * time.Millisecond) + time.Sleep(2000 * time.Millisecond) t.Assert(array.Len(), 0) - time.Sleep(1000 * time.Millisecond) + time.Sleep(2000 * time.Millisecond) t.Assert(array.Len(), 1) }) } diff --git a/util/gconv/gconv.go b/util/gconv/gconv.go index 358ba5a06..8ca25c547 100644 --- a/util/gconv/gconv.go +++ b/util/gconv/gconv.go @@ -279,9 +279,7 @@ func doConvert(in doConvertInput) interface{} { default: if in.ReferValue != nil { - var ( - referReflectValue reflect.Value - ) + var referReflectValue reflect.Value if v, ok := in.ReferValue.(reflect.Value); ok { referReflectValue = v } else { diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index e73026549..aaddf01be 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -338,6 +338,12 @@ func bindVarToStructAttr(elem reflect.Value, name string, value interface{}, map if empty.IsNil(value) { structFieldValue.Set(reflect.Zero(structFieldValue.Type())) } else { + // Common interface check. + var ok bool + if err, ok = bindVarToReflectValueWithInterfaceCheck(structFieldValue, value); ok { + return err + } + // Default converting. structFieldValue.Set(reflect.ValueOf(doConvert( doConvertInput{ FromValue: value, @@ -420,11 +426,6 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, ma return nil } - // Common interface check. - if err, ok = bindVarToReflectValueWithInterfaceCheck(structFieldValue, value); ok { - return err - } - kind := structFieldValue.Kind() // Converting using interface, for some kinds. switch kind { diff --git a/util/gconv/gconv_z_unit_scan_test.go b/util/gconv/gconv_z_unit_scan_test.go index e34f91d99..12d488d90 100644 --- a/util/gconv/gconv_z_unit_scan_test.go +++ b/util/gconv/gconv_z_unit_scan_test.go @@ -8,11 +8,13 @@ package gconv_test import ( "fmt" + "math/big" + "testing" + "github.com/gogf/gf/v2/container/gvar" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/test/gtest" "github.com/gogf/gf/v2/util/gconv" - "testing" ) func Test_Scan_StructStructs(t *testing.T) { @@ -601,3 +603,30 @@ func Test_ScanList_Embedded(t *testing.T) { t.Assert(len(entities[2].UserScores), 0) }) } + +type Float64 float64 + +func (f *Float64) UnmarshalValue(value interface{}) error { + if v, ok := value.(*big.Rat); ok { + f64, _ := v.Float64() + *f = Float64(f64) + } + return nil +} + +func Test_Issue1607(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type Demo struct { + B Float64 + } + rat := &big.Rat{} + rat.SetFloat64(1.5) + + var demos = make([]Demo, 1) + err := gconv.Scan([]map[string]interface{}{ + {"A": 1, "B": rat}, + }, &demos) + t.AssertNil(err) + t.Assert(demos[0].B, 1.5) + }) +}