From 7434dfe6fa2534152edde783ee34e2a71633ac61 Mon Sep 17 00:00:00 2001 From: John Date: Sun, 16 Dec 2018 22:22:07 +0800 Subject: [PATCH] done refacting gdb package --- g/container/gvar/gvar.go | 3 ++ g/container/gvar/gvar_read.go | 6 ++- g/database/gdb/gdb.go | 5 +- g/database/gdb/gdb_base.go | 33 ++++++++++++ g/database/gdb/gdb_func.go | 10 ++-- g/database/gdb/gdb_model.go | 84 +++++++++++++++++++++++++---- g/database/gdb/gdb_unit_2_test.go | 7 ++- g/g.go | 23 ++++++-- g/net/ghttp/ghttp_server.go | 5 ++ g/net/ghttp/ghttp_server_log.go | 7 ++- g/os/gfile/gfile.go | 3 +- g/util/gconv/gconv_map.go | 7 ++- g/util/gconv/gconv_time.go | 29 +++++----- g/util/gutil/gutil.go | 2 +- g/util/gvalid/gvalid_check_map.go | 4 ++ geg/database/orm/mysql/gdb_value.go | 2 +- 16 files changed, 190 insertions(+), 40 deletions(-) diff --git a/g/container/gvar/gvar.go b/g/container/gvar/gvar.go index 2f65f7a38..48be7e36c 100644 --- a/g/container/gvar/gvar.go +++ b/g/container/gvar/gvar.go @@ -10,6 +10,7 @@ package gvar import ( "gitee.com/johng/gf/g/container/gtype" + "gitee.com/johng/gf/g/os/gtime" "gitee.com/johng/gf/g/util/gconv" "time" ) @@ -92,6 +93,8 @@ func (v *Var) Interfaces() []interface{} { return gconv.Interfaces(v.Val() func (v *Var) Time(format...string) time.Time { return gconv.Time(v.Val(), format...) } func (v *Var) TimeDuration() time.Duration { return gconv.TimeDuration(v.Val()) } +func (v *Var) GTime(format...string) *gtime.Time { return gconv.GTime(v.Val(), format...) } + // 将变量转换为对象,注意 objPointer 参数必须为struct指针 func (v *Var) Struct(objPointer interface{}, attrMapping...map[string]string) error { return gconv.Struct(v.Val(), objPointer, attrMapping...) diff --git a/g/container/gvar/gvar_read.go b/g/container/gvar/gvar_read.go index bd4b7f8ef..4b75f250b 100644 --- a/g/container/gvar/gvar_read.go +++ b/g/container/gvar/gvar_read.go @@ -6,7 +6,10 @@ package gvar -import "time" +import ( + "gitee.com/johng/gf/g/os/gtime" + "time" +) // 只读变量接口 type VarRead interface { @@ -34,5 +37,6 @@ type VarRead interface { Interfaces() []interface{} Time(format ...string) time.Time TimeDuration() time.Duration + GTime(format...string) *gtime.Time Struct(objPointer interface{}, attrMapping ...map[string]string) error } \ No newline at end of file diff --git a/g/database/gdb/gdb.go b/g/database/gdb/gdb.go index cae550e8d..65b97f494 100644 --- a/g/database/gdb/gdb.go +++ b/g/database/gdb/gdb.go @@ -4,6 +4,7 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://gitee.com/johng/gf. +// Package gdb provides ORM features for popular relationship databases. // 数据库ORM. // 默认内置支持MySQL, 其他数据库需要手动import对应的数据库引擎第三方包. package gdb @@ -94,7 +95,9 @@ type DB interface { getCache() (*gcache.Cache) getChars() (charLeft string, charRight string) getDebug() bool - handleSqlBeforeExec(sql string) string + filterFields(table string, data map[string]interface{}) map[string]interface{} + getTableFields(table string) (map[string]string, error) + handleSqlBeforeExec(sql string) string } // 执行底层数据库操作的核心接口 diff --git a/g/database/gdb/gdb_base.go b/g/database/gdb/gdb_base.go index 07ab10b63..ad774aff4 100644 --- a/g/database/gdb/gdb_base.go +++ b/g/database/gdb/gdb_base.go @@ -431,3 +431,36 @@ func (bs *dbBase) doDelete(link dbLink, table string, condition interface{}, arg func (bs *dbBase) getCache() *gcache.Cache { return bs.cache } + +// 将map的数据按照fields进行过滤,只保留与表字段同名的数据 +func (bs *dbBase) filterFields(table string, data map[string]interface{}) map[string]interface{} { + if fields, err := bs.db.getTableFields(table); err == nil { + for k, _ := range data { + if _, ok := fields[k]; !ok { + delete(data, k) + } + } + } + return data +} + +// 获得指定表表的数据结构map +func (bs *dbBase) getTableFields(table string) (fields map[string]string, err error) { + v := bs.cache.GetOrSetFunc("table_fields_" + table, func() interface{} { + result := (Result)(nil) + charl, charr := bs.db.getChars() + result, err = bs.GetAll(fmt.Sprintf(`SHOW COLUMNS FROM %s%s%s`, charl, table, charr)) + if err != nil { + return nil + } + fields = make(map[string]string) + for _, m := range result { + fields[m["Field"].String()] = m["Type"].String() + } + return fields + }, 0) + if err == nil { + fields = v.(map[string]string) + } + return +} \ No newline at end of file diff --git a/g/database/gdb/gdb_func.go b/g/database/gdb/gdb_func.go index 1d782c81d..42210e115 100644 --- a/g/database/gdb/gdb_func.go +++ b/g/database/gdb/gdb_func.go @@ -41,9 +41,13 @@ func rowsToResult(rows *sql.Rows) (Result, error) { row := make(Record) // 注意col字段是一个[]byte类型(slice类型本身是一个指针),多个记录循环时该变量指向的是同一个内存地址 for i, col := range values { - v := make([]byte, len(col)) - copy(v, col) - row[columns[i]] = gvar.New(v, false) + if col == nil { + row[columns[i]] = gvar.New(nil, false) + } else { + v := make([]byte, len(col)) + copy(v, col) + row[columns[i]] = gvar.New(v, false) + } } records = append(records, row) } diff --git a/g/database/gdb/gdb_model.go b/g/database/gdb/gdb_model.go index 8ce73c338..95780f6d1 100644 --- a/g/database/gdb/gdb_model.go +++ b/g/database/gdb/gdb_model.go @@ -12,6 +12,7 @@ import ( "database/sql" "gitee.com/johng/gf/g/util/gconv" _ "gitee.com/johng/gf/third/github.com/go-sql-driver/mysql" + "reflect" "strings" ) @@ -30,6 +31,7 @@ type Model struct { limit int // 分页条数 data interface{} // 操作记录(支持Map/List/string类型) batch int // 批量操作条数 + filter bool // 是否按照表字段过滤data参数 cacheEnabled bool // 当前SQL操作是否开启查询缓存功能 cacheTime int // 查询缓存时间 cacheName string // 查询缓存名称 @@ -98,6 +100,12 @@ func (md *Model) Fields(fields string) (*Model) { return md } +// 链式操作,过滤字段 +func (md *Model) Filter() (*Model) { + md.filter = true + return md +} + // 链式操作,condition,支持string & gdb.Map func (md *Model) Where(where interface{}, args ...interface{}) (*Model) { md.where = formatCondition(where) @@ -159,7 +167,32 @@ func (md *Model) Data(data ...interface{}) (*Model) { } md.data = m } else { - md.data = data[0] + switch data[0].(type) { + case List: + md.data = data[0] + case Map: + md.data = data[0] + default: + rv := reflect.ValueOf(data[0]) + kind := rv.Kind() + if kind == reflect.Ptr { + rv = rv.Elem() + kind = rv.Kind() + } + switch kind { + case reflect.Slice: fallthrough + case reflect.Array: + list := make(List, rv.Len()) + for i := 0; i < rv.Len(); i++ { + list[i] = gconv.Map(rv.Index(i).Interface()) + } + md.data = list + case reflect.Map: + md.data = gconv.Map(data[0]) + default: + md.data = data[0] + } + } } return md } @@ -181,16 +214,24 @@ func (md *Model) Insert() (result sql.Result, err error) { if md.batch > 0 { batch = md.batch } + if md.filter { + for k, m := range list { + list[k] = md.db.filterFields(md.tables, m) + } + } if md.tx == nil { return md.db.BatchInsert(md.tables, list, batch) } else { return md.tx.BatchInsert(md.tables, list, batch) } - } else if dataMap, ok := md.data.(Map); ok { + } else if data, ok := md.data.(Map); ok { + if md.filter { + data = md.db.filterFields(md.tables, data) + } if md.tx == nil { - return md.db.Insert(md.tables, dataMap) + return md.db.Insert(md.tables, data) } else { - return md.tx.Insert(md.tables, dataMap) + return md.tx.Insert(md.tables, data) } } return nil, errors.New("inserting into table with invalid data type") @@ -213,16 +254,24 @@ func (md *Model) Replace() (result sql.Result, err error) { if md.batch > 0 { batch = md.batch } + if md.filter { + for k, m := range list { + list[k] = md.db.filterFields(md.tables, m) + } + } if md.tx == nil { return md.db.BatchReplace(md.tables, list, batch) } else { return md.tx.BatchReplace(md.tables, list, batch) } - } else if dataMap, ok := md.data.(Map); ok { + } else if data, ok := md.data.(Map); ok { + if md.filter { + data = md.db.filterFields(md.tables, data) + } if md.tx == nil { - return md.db.Replace(md.tables, dataMap) + return md.db.Replace(md.tables, data) } else { - return md.tx.Replace(md.tables, dataMap) + return md.tx.Replace(md.tables, data) } } return nil, errors.New("replacing into table with invalid data type") @@ -245,16 +294,24 @@ func (md *Model) Save() (result sql.Result, err error) { if md.batch > 0 { batch = md.batch } + if md.filter { + for k, m := range list { + list[k] = md.db.filterFields(md.tables, m) + } + } if md.tx == nil { return md.db.BatchSave(md.tables, list, batch) } else { return md.tx.BatchSave(md.tables, list, batch) } - } else if dataMap, ok := md.data.(Map); ok { + } else if data, ok := md.data.(Map); ok { + if md.filter { + data = md.db.filterFields(md.tables, data) + } if md.tx == nil { - return md.db.Save(md.tables, dataMap) + return md.db.Save(md.tables, data) } else { - return md.tx.Save(md.tables, dataMap) + return md.tx.Save(md.tables, data) } } return nil, errors.New("saving into table with invalid data type") @@ -271,6 +328,13 @@ func (md *Model) Update() (result sql.Result, err error) { if md.data == nil { return nil, errors.New("updating table with empty data") } + if md.filter { + if data, ok := md.data.(Map); ok { + if md.filter { + md.data = md.db.filterFields(md.tables, data) + } + } + } if md.tx == nil { return md.db.Update(md.tables, md.data, md.where, md.whereArgs ...) } else { diff --git a/g/database/gdb/gdb_unit_2_test.go b/g/database/gdb/gdb_unit_2_test.go index 48bdb8e77..469db382f 100644 --- a/g/database/gdb/gdb_unit_2_test.go +++ b/g/database/gdb/gdb_unit_2_test.go @@ -8,8 +8,9 @@ import ( ) func TestModel_Insert(t *testing.T) { - result, err := db.Table("user").Data(g.Map{ + result, err := db.Table("user").Filter().Data(g.Map{ "id" : 1, + "uid" : 1, "passport" : "t1", "password" : "25d55ad283aa400af464c76d713c07ad", "nickname" : "T1", @@ -23,9 +24,10 @@ func TestModel_Insert(t *testing.T) { } func TestModel_Batch(t *testing.T) { - result, err := db.Table("user").Data(g.List{ + result, err := db.Table("user").Filter().Data(g.List{ { "id" : 2, + "uid" : 2, "passport" : "t2", "password" : "25d55ad283aa400af464c76d713c07ad", "nickname" : "T2", @@ -33,6 +35,7 @@ func TestModel_Batch(t *testing.T) { }, { "id" : 3, + "uid" : 3, "passport" : "t3", "password" : "25d55ad283aa400af464c76d713c07ad", "nickname" : "T3", diff --git a/g/g.go b/g/g.go index c3eb2d107..e73b91b6e 100644 --- a/g/g.go +++ b/g/g.go @@ -10,14 +10,27 @@ package g import "gitee.com/johng/gf/g/container/gvar" // 框架动态变量,可以用该类型替代interface{}类型 -type Var = gvar.Var +type Var = gvar.Var // 常用map数据结构(使用别名) -type Map = map[string]interface{} +type Map = map[string]interface{} +type MapStrStr = map[string]string +type MapStrInt = map[string]int +type MapIntStr = map[int]string +type MapIntInt = map[int]int // 常用list数据结构(使用别名) -type List = []Map +type List = []Map +type ListStrStr = []map[string]string +type ListStrInt = []map[string]int +type ListIntStr = []map[int]string +type ListIntInt = []map[int]int + // 常用slice数据结构(使用别名) -type Slice = []interface{} -type Array = Slice +type Slice = []interface{} +type SliceStr = []string +type SliceInt = []int +type Array = Slice +type ArrayStr = SliceStr +type ArrayInt = SliceInt diff --git a/g/net/ghttp/ghttp_server.go b/g/net/ghttp/ghttp_server.go index d79125abe..40f340382 100644 --- a/g/net/ghttp/ghttp_server.go +++ b/g/net/ghttp/ghttp_server.go @@ -15,6 +15,7 @@ import ( "gitee.com/johng/gf/g/container/gtype" "gitee.com/johng/gf/g/os/gcache" "gitee.com/johng/gf/g/os/genv" + "gitee.com/johng/gf/g/os/gfile" "gitee.com/johng/gf/g/os/glog" "gitee.com/johng/gf/g/os/gproc" "gitee.com/johng/gf/g/os/gtime" @@ -254,6 +255,10 @@ func (s *Server) Start() error { } }) } + // 是否处于开发环境 + if gfile.MainPkgPath() != "" { + glog.Backtrace(false, 0).Notice("GF notices that you're in develop environment, so error logs are auto enabled to stdout.") + } // 打印展示路由表 s.DumpRoutesMap() diff --git a/g/net/ghttp/ghttp_server_log.go b/g/net/ghttp/ghttp_server_log.go index a4cd12f0c..62ace6fda 100644 --- a/g/net/ghttp/ghttp_server_log.go +++ b/g/net/ghttp/ghttp_server_log.go @@ -9,6 +9,7 @@ package ghttp import ( "fmt" + "gitee.com/johng/gf/g/os/gfile" "net/http" ) @@ -36,7 +37,7 @@ func (s *Server) handleErrorLog(error interface{}, r *Request) { r.Response.WriteStatus(http.StatusInternalServerError) // 错误输出默认是开启的 - if !s.IsErrorLogEnabled() { + if !s.IsErrorLogEnabled() && gfile.MainPkgPath() == "" { return } @@ -56,5 +57,9 @@ func (s *Server) handleErrorLog(error interface{}, r *Request) { s.logger.Cat("error").Backtrace(true, 2).StdPrint(true).Error(content) } else { s.logger.Cat("error").Backtrace(true, 2).Error(content) + // 开发环境下(MainPkgPath)自动输出错误信息到标准输出 + if gfile.MainPkgPath() != "" { + s.logger.Cat("error").Backtrace(true, 2).StdPrint(true).Error(content) + } } } diff --git a/g/os/gfile/gfile.go b/g/os/gfile/gfile.go index 420c8f10a..23f82c3d3 100644 --- a/g/os/gfile/gfile.go +++ b/g/os/gfile/gfile.go @@ -370,7 +370,7 @@ func homeWindows() (string, error) { return home, nil } -// 获取入口函数文件所在目录(main包文件目录), +// 获取入口函数文件所在目录(main包文件目录), // **仅对源码开发环境有效(即仅对生成该可执行文件的系统下有效)** func MainPkgPath() string { path := mainPkgPath.Val() @@ -401,6 +401,7 @@ func MainPkgPath() string { if p == f { break } + // 会自动扫描源码,寻找main包 if paths, err := ScanDir(p, "*.go"); err == nil && len(paths) > 0 { for _, path := range paths { if gregex.IsMatchString(`package\s+main`, GetContents(path)) { diff --git a/g/util/gconv/gconv_map.go b/g/util/gconv/gconv_map.go index 2bb3f573c..5f046ffc9 100644 --- a/g/util/gconv/gconv_map.go +++ b/g/util/gconv/gconv_map.go @@ -99,8 +99,11 @@ func Map(i interface{}, noTagCheck...bool) map[string]interface{} { rt := rv.Type() name := "" for i := 0; i < rv.NumField(); i++ { - if name = rt.Field(i).Tag.Get("json"); name == "" { - name = rt.Field(i).Name + // 检查json tag + if len(noTagCheck) == 0 || !noTagCheck[0] { + if name = rt.Field(i).Tag.Get("json"); name == "" { + name = rt.Field(i).Name + } } m[name] = rv.Field(i).Interface() } diff --git a/g/util/gconv/gconv_time.go b/g/util/gconv/gconv_time.go index fefc07b83..42a13712b 100644 --- a/g/util/gconv/gconv_time.go +++ b/g/util/gconv/gconv_time.go @@ -14,21 +14,26 @@ import ( // 将变量i转换为time.Time类型 func Time(i interface{}, format...string) time.Time { - s := String(i) - // 优先使用用户输入日期格式进行转换 - if len(format) > 0 { - t, _ := gtime.StrToTimeFormat(s, format[0]) - return t.Time - } - if gstr.IsNumeric(s) { - return gtime.NewFromTimeStamp(Int64(s)).Time - } else { - t, _ := gtime.StrToTime(s) - return t.Time - } + return GTime(i, format...).Time } // 将变量i转换为time.Time类型 func TimeDuration(i interface{}) time.Duration { return time.Duration(Int64(i)) +} + +// 将变量i转换为time.Time类型 +func GTime(i interface{}, format...string) *gtime.Time { + s := String(i) + // 优先使用用户输入日期格式进行转换 + if len(format) > 0 { + t, _ := gtime.StrToTimeFormat(s, format[0]) + return t + } + if gstr.IsNumeric(s) { + return gtime.NewFromTimeStamp(Int64(s)) + } else { + t, _ := gtime.StrToTime(s) + return t + } } \ No newline at end of file diff --git a/g/util/gutil/gutil.go b/g/util/gutil/gutil.go index ff315f1ed..5d1ed1288 100644 --- a/g/util/gutil/gutil.go +++ b/g/util/gutil/gutil.go @@ -4,7 +4,7 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://gitee.com/johng/gf. -// 其他工具包 +// 工具包 package gutil import ( diff --git a/g/util/gvalid/gvalid_check_map.go b/g/util/gvalid/gvalid_check_map.go index 1ad486ac7..5ced2acc1 100644 --- a/g/util/gvalid/gvalid_check_map.go +++ b/g/util/gvalid/gvalid_check_map.go @@ -78,6 +78,10 @@ func CheckMap(params interface{}, rules interface{}, msgs...CustomMsg) *Error { value := (interface{})(nil) // 这里的rule变量为多条校验规则,不包含名字或者错误信息定义 for key, rule := range checkRules { + // 如果规则为空,那么不执行校验 + if len(rule) == 0 { + continue + } value = nil if v, ok := data[key]; ok { value = v diff --git a/geg/database/orm/mysql/gdb_value.go b/geg/database/orm/mysql/gdb_value.go index 82a48b0c0..31747b2ec 100644 --- a/geg/database/orm/mysql/gdb_value.go +++ b/geg/database/orm/mysql/gdb_value.go @@ -10,7 +10,7 @@ func main() { // 开启调试模式,以便于记录所有执行的SQL db.SetDebug(true) - r, _ := db.Table("user").All() + r, _ := db.Table("test").Where("id IN (?,?)", 1,2).All() if r != nil { fmt.Println(r.ToList()) }