diff --git a/.gitignore b/.gitignore index 213eb4ae1..638a8dbb0 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ gitpush.sh pkg/ bin/ cbuild +*/.DS_Store diff --git a/TODO b/TODO index 0e8f12df6..17b77638a 100644 --- a/TODO +++ b/TODO @@ -15,7 +15,7 @@ ghttp获取参数支持直接转struct功能; map转struct增加对tag的支持; gcache检查在i386下的int64->int转换问题; gfsnotify增加对于目录的监控; - +orm增加sqlite对Save方法的支持(去掉触发器语句); DONE: 1. gconv完善针对不同类型的判断,例如:尽量减少sprintf("%v", xxx)来执行string类型的转换; diff --git a/g/crypto/gaes/gaes.go b/g/crypto/gaes/gaes.go index 9d15ba81d..2af9618b8 100644 --- a/g/crypto/gaes/gaes.go +++ b/g/crypto/gaes/gaes.go @@ -9,31 +9,38 @@ package gaes import ( "bytes" + "errors" "crypto/aes" "crypto/cipher" - "errors" ) const ( ivDefValue = "I Love Go Frame!" ) -func Encrypt(plaintext []byte, key []byte) ([]byte, error) { +// AES加密, 使用CBC模式,注意key必须为16/24/32位长度,iv初始化向量为非必需参数 +func Encrypt(plainText []byte, key []byte, iv...[]byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } blockSize := block.BlockSize() - plaintext = PKCS5Padding(plaintext, blockSize) - iv := []byte(ivDefValue) - blockMode := cipher.NewCBCEncrypter(block, iv) - ciphertext := make([]byte, len(plaintext)) - blockMode.CryptBlocks(ciphertext, plaintext) + plainText = PKCS5Padding(plainText, blockSize) + ivValue := ([]byte)(nil) + if len(iv) > 0 { + ivValue = iv[0] + } else { + ivValue = []byte(ivDefValue) + } + blockMode := cipher.NewCBCEncrypter(block, ivValue) + ciphertext := make([]byte, len(plainText)) + blockMode.CryptBlocks(ciphertext, plainText) return ciphertext, nil } -func Decrypt(cipherText []byte, key []byte) ([]byte, error) { +// AES解密, 使用CBC模式,注意key必须为16/24/32位长度,iv初始化向量为非必需参数 +func Decrypt(cipherText []byte, key []byte, iv...[]byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err @@ -42,16 +49,21 @@ func Decrypt(cipherText []byte, key []byte) ([]byte, error) { if len(cipherText) < blockSize { return nil, errors.New("cipherText too short") } - iv := []byte(ivDefValue) + ivValue := ([]byte)(nil) + if len(iv) > 0 { + ivValue = iv[0] + } else { + ivValue = []byte(ivDefValue) + } if len(cipherText)%blockSize != 0 { return nil, errors.New("cipherText is not a multiple of the block size") } - blockModel := cipher.NewCBCDecrypter(block, iv) - plaintext := make([]byte, len(cipherText)) - blockModel.CryptBlocks(plaintext, cipherText) - plaintext = PKCS5UnPadding(plaintext) + blockModel := cipher.NewCBCDecrypter(block, ivValue) + plainText := make([]byte, len(cipherText)) + blockModel.CryptBlocks(plainText, cipherText) + plainText = PKCS5UnPadding(plainText) - return plaintext, nil + return plainText, nil } func PKCS5Padding(src []byte, blockSize int) []byte { @@ -61,7 +73,7 @@ func PKCS5Padding(src []byte, blockSize int) []byte { } func PKCS5UnPadding(src []byte) []byte { - length := len(src) - unpadding := int(src[length-1]) + length := len(src) + unpadding := int(src[length - 1]) return src[:(length - unpadding)] } \ No newline at end of file diff --git a/g/database/gdb/gdb.go b/g/database/gdb/gdb.go index 8c44a28f9..aaeb3ce71 100644 --- a/g/database/gdb/gdb.go +++ b/g/database/gdb/gdb.go @@ -5,123 +5,122 @@ // You can obtain one at https://gitee.com/johng/gf. // 数据库ORM. +// 默认内置支持MySQL, 其他数据库需要手动import对应的数据库引擎第三方包. package gdb import ( - "fmt" + "fmt" + "time" "errors" "database/sql" - "gitee.com/johng/gf/g/util/grand" - _ "github.com/lib/pq" - _ "github.com/go-sql-driver/mysql" - "gitee.com/johng/gf/g/container/gtype" - "gitee.com/johng/gf/g/container/gring" - "gitee.com/johng/gf/g/os/gcache" - "gitee.com/johng/gf/g/container/gmap" - "time" + "gitee.com/johng/gf/g/container/gmap" + "gitee.com/johng/gf/g/container/gring" + "gitee.com/johng/gf/g/container/gtype" + "gitee.com/johng/gf/g/os/gcache" + "gitee.com/johng/gf/g/util/grand" + _ "github.com/go-sql-driver/mysql" ) const ( - OPTION_INSERT = 0 - OPTION_REPLACE = 1 - OPTION_SAVE = 2 - OPTION_IGNORE = 3 + OPTION_INSERT = 0 + OPTION_REPLACE = 1 + OPTION_SAVE = 2 + OPTION_IGNORE = 3 ) // 数据库操作接口 type Link interface { - // 打开数据库连接,建立数据库操作对象 - Open (c *ConfigNode) (*sql.DB, error) + // 打开数据库连接,建立数据库操作对象 + Open(c *ConfigNode) (*sql.DB, error) - // SQL操作方法 - Query(q string, args ...interface{}) (*sql.Rows, error) - Exec(q string, args ...interface{}) (sql.Result, error) - Prepare(q string) (*sql.Stmt, error) + // SQL操作方法 + Query(q string, args ...interface{}) (*sql.Rows, error) + Exec(q string, args ...interface{}) (sql.Result, error) + Prepare(q string) (*sql.Stmt, error) - // 数据库查询 - GetAll(q string, args ...interface{}) (Result, error) - GetOne(q string, args ...interface{}) (Record, error) - GetValue(q string, args ...interface{}) (Value, error) + // 数据库查询 + GetAll(q string, args ...interface{}) (Result, error) + GetOne(q string, args ...interface{}) (Record, error) + GetValue(q string, args ...interface{}) (Value, error) - // Ping - PingMaster() error - PingSlave() error + // Ping + PingMaster() error + PingSlave() error - // 连接属性设置 - SetMaxIdleConns(n int) - SetMaxOpenConns(n int) - SetConnMaxLifetime(d time.Duration) + // 连接属性设置 + SetMaxIdleConns(n int) + SetMaxOpenConns(n int) + SetConnMaxLifetime(d time.Duration) - // 开启事务操作 - Begin() (*Tx, error) + // 开启事务操作 + Begin() (*Tx, error) - // 数据表插入/更新/保存操作 - Insert(table string, data Map) (sql.Result, error) - Replace(table string, data Map) (sql.Result, error) - Save(table string, data Map) (sql.Result, error) + // 数据表插入/更新/保存操作 + Insert(table string, data Map) (sql.Result, error) + Replace(table string, data Map) (sql.Result, error) + Save(table string, data Map) (sql.Result, error) - // 数据表插入/更新/保存操作(批量) - BatchInsert(table string, list List, batch int) (sql.Result, error) - BatchReplace(table string, list List, batch int) (sql.Result, error) - BatchSave(table string, list List, batch int) (sql.Result, error) + // 数据表插入/更新/保存操作(批量) + BatchInsert(table string, list List, batch int) (sql.Result, error) + BatchReplace(table string, list List, batch int) (sql.Result, error) + BatchSave(table string, list List, batch int) (sql.Result, error) - // 数据修改/删除 - Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) - Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error) + // 数据修改/删除 + 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为From的别名) + Table(tables string) *Model + From(tables string) *Model - // 关闭数据库操作对象 - Close() error + // 关闭数据库操作对象 + Close() error - // 内部方法 - insert(table string, data Map, option uint8) (sql.Result, error) - batchInsert(table string, list List, batch int, option uint8) (sql.Result, error) + // 内部方法 + insert(table string, data Map, option uint8) (sql.Result, error) + batchInsert(table string, list List, batch int, option uint8) (sql.Result, error) - getQuoteCharLeft () string - getQuoteCharRight () string - handleSqlBeforeExec(q *string) *string + getQuoteCharLeft() string + getQuoteCharRight() string + handleSqlBeforeExec(q *string) *string } // 数据库链接对象 type Db struct { - link Link // 底层数据库类型管理对象 - master *sql.DB // 实例化数据库链接(master) - slave *sql.DB // 实例化数据库链接(slave,可能会与master相同) - charl string // SQL安全符号(左) - charr string // SQL安全符号(右) - debug *gtype.Bool // (默认关闭)是否开启调试模式,当开启时会启用一些调试特性 - sqls *gring.Ring // (debug=true时有效)已执行的SQL列表 - cache *gcache.Cache // 查询缓存,需要注意的是,事务查询不支持缓存 + link Link // 底层数据库类型管理对象 + master *sql.DB // 实例化数据库链接(master) + slave *sql.DB // 实例化数据库链接(slave,可能会与master相同) + charl string // SQL安全符号(左) + charr string // SQL安全符号(右) + debug *gtype.Bool // (默认关闭)是否开启调试模式,当开启时会启用一些调试特性 + sqls *gring.Ring // (debug=true时有效)已执行的SQL列表 + cache *gcache.Cache // 查询缓存,需要注意的是,事务查询不支持缓存 } // 执行的SQL对象 type Sql struct { - Sql string // SQL语句(可能带有预处理占位符) - Args []interface{} // 预处理参数值列表 - Error error // 执行结果(nil为成功) - Cost int64 // 执行时间消耗(毫秒) - Func string // 执行方法名称 + Sql string // SQL语句(可能带有预处理占位符) + Args []interface{} // 预处理参数值列表 + Error error // 执行结果(nil为成功) + Start int64 // 执行开始时间(毫秒) + End int64 // 执行结束时间(毫秒) + Func string // 执行方法名称 } // 返回数据表记录值 -type Value []byte +type Value []byte // 返回数据表记录Map -type Record map[string]Value +type Record map[string]Value // 返回数据表记录List -type Result []Record - +type Result []Record // 关联数组,绑定一条数据表记录(使用别名) -type Map = map[string]interface{} +type Map = map[string]interface{} // 关联数组列表(索引从0开始的数组),绑定多条记录(使用别名) -type List = []Map - +type List = []Map // MySQL接口对象 var linkMysql = &dbmysql{} @@ -129,45 +128,49 @@ var linkMysql = &dbmysql{} // PostgreSQL接口对象 var linkPgsql = &dbpgsql{} +// Sqlite接口对象 +// @author wxkj +var linkSqlite = &dbsqlite{} + // 数据库查询缓存对象map,使用数据库连接名称作为键名,键值为查询缓存对象 -var dbCaches = gmap.NewStringInterfaceMap() +var dbCaches = gmap.NewStringInterfaceMap() // 使用默认/指定分组配置进行连接,数据库集群配置项:default -func New(groupName...string) (*Db, error) { - name := config.d - if len(groupName) > 0 { - name = groupName[0] - } - config.RLock() - defer config.RUnlock() +func New(groupName ...string) (*Db, error) { + name := config.d + if len(groupName) > 0 { + name = groupName[0] + } + config.RLock() + defer config.RUnlock() - if len(config.c) < 1 { - return nil, errors.New("empty database configuration") - } - if list, ok := config.c[name]; ok { - // 将master, slave集群列表拆分出来 - masterList := make(ConfigGroup, 0) - slaveList := make(ConfigGroup, 0) - for i := 0; i < len(list); i++ { - if list[i].Role == "slave" { - slaveList = append(slaveList, list[i]) - } else { - // 默认配置项的角色为master - masterList = append(masterList, list[i]) - } - } - if len(masterList) < 1 { - return nil, errors.New("at least one master node configuration's need to make sense") - } - masterNode := getConfigNodeByPriority(masterList) - var slaveNode *ConfigNode - if len(slaveList) > 0 { - slaveNode = getConfigNodeByPriority(slaveList) - } - return newDb(masterNode, slaveNode, name) - } else { - return nil, errors.New(fmt.Sprintf("empty database configuration for item name '%s'", name)) - } + if len(config.c) < 1 { + return nil, errors.New("empty database configuration") + } + if list, ok := config.c[name]; ok { + // 将master, slave集群列表拆分出来 + masterList := make(ConfigGroup, 0) + slaveList := make(ConfigGroup, 0) + for i := 0; i < len(list); i++ { + if list[i].Role == "slave" { + slaveList = append(slaveList, list[i]) + } else { + // 默认配置项的角色为master + masterList = append(masterList, list[i]) + } + } + if len(masterList) < 1 { + return nil, errors.New("at least one master node configuration's need to make sense") + } + masterNode := getConfigNodeByPriority(masterList) + var slaveNode *ConfigNode + if len(slaveList) > 0 { + slaveNode = getConfigNodeByPriority(slaveList) + } + return newDb(masterNode, slaveNode, name) + } else { + return nil, errors.New(fmt.Sprintf("empty database configuration for item name '%s'", name)) + } } // 按照负载均衡算法(优先级配置)从数据库集群中选择一个配置节点出来使用 @@ -176,84 +179,87 @@ func New(groupName...string) (*Db, error) { // 2、那么节点1的权重范围为[0, 99],节点2的权重范围为[100, 199],比例为1:1; // 3、假如计算出的随机数为99; // 4、那么选择的配置为节点1; -func getConfigNodeByPriority (cg ConfigGroup) *ConfigNode { - if len(cg) < 2 { - return &cg[0] - } - var total int - for i := 0; i < len(cg); i++ { - total += cg[i].Priority * 100 - } - // 不能取到末尾的边界点 - r := grand.Rand(0, total) - if r > 0 { - r -= 1 - } - min := 0 - max := 0 - for i := 0; i < len(cg); i++ { - max = min + cg[i].Priority * 100 - //fmt.Printf("r: %d, min: %d, max: %d\n", r, min, max) - if r >= min && r < max { - return &cg[i] - } else { - min = max - } - } - return nil +func getConfigNodeByPriority(cg ConfigGroup) *ConfigNode { + if len(cg) < 2 { + return &cg[0] + } + var total int + for i := 0; i < len(cg); i++ { + total += cg[i].Priority * 100 + } + // 不能取到末尾的边界点 + r := grand.Rand(0, total) + if r > 0 { + r -= 1 + } + min := 0 + max := 0 + for i := 0; i < len(cg); i++ { + max = min + cg[i].Priority*100 + //fmt.Printf("r: %d, min: %d, max: %d\n", r, min, max) + if r >= min && r < max { + return &cg[i] + } else { + min = max + } + } + return nil } // 创建数据库链接对象 -func newDb (masterNode *ConfigNode, slaveNode *ConfigNode, groupName string) (*Db, error) { - var link Link - switch masterNode.Type { - case "mysql": link = linkMysql - case "pgsql": link = linkPgsql - default: - return nil, errors.New(fmt.Sprintf("unsupported db type '%s'", masterNode.Type)) - } - master, err := link.Open(masterNode) - if err != nil { - return nil, err - } - slave := master - if slaveNode != nil { - slave, err = link.Open(slaveNode) - if err != nil { - return nil, err - } - } - db := &Db { - link : link, - master : master, - slave : slave, - charl : link.getQuoteCharLeft(), - charr : link.getQuoteCharRight(), - debug : gtype.NewBool(), - } - // 设置连接属性,master和slave必须是一致的,所以这里使用的是master的属性设置 - if masterNode.MaxIdleConnCount > 0 { - db.SetMaxIdleConns(masterNode.MaxIdleConnCount) - } - if masterNode.MaxOpenConnCount > 0 { - db.SetMaxOpenConns(masterNode.MaxOpenConnCount) - } - if masterNode.MaxConnLifetime > 0 { - db.SetConnMaxLifetime(time.Duration(masterNode.MaxConnLifetime)*time.Second) - } - if v := dbCaches.Get(groupName); v == nil { - dbCaches.LockFunc(func(m map[string]interface{}) { - if v, ok := m[groupName]; !ok { - db.cache = gcache.New() - m[groupName] = db.cache - } else { - db.cache = v.(*gcache.Cache) - } - }) - } else { - db.cache = v.(*gcache.Cache) - } +func newDb(masterNode *ConfigNode, slaveNode *ConfigNode, groupName string) (*Db, error) { + var link Link + switch masterNode.Type { + case "mysql": + link = linkMysql + case "pgsql": + link = linkPgsql + case "sqlite": + link = linkSqlite + default: + return nil, errors.New(fmt.Sprintf("unsupported db type '%s'", masterNode.Type)) + } + master, err := link.Open(masterNode) + if err != nil { + return nil, err + } + slave := master + if slaveNode != nil { + slave, err = link.Open(slaveNode) + if err != nil { + return nil, err + } + } + db := &Db{ + link: link, + master: master, + slave: slave, + charl: link.getQuoteCharLeft(), + charr: link.getQuoteCharRight(), + debug: gtype.NewBool(), + } + // 设置连接属性,master和slave必须是一致的,所以这里使用的是master的属性设置 + if masterNode.MaxIdleConnCount > 0 { + db.SetMaxIdleConns(masterNode.MaxIdleConnCount) + } + if masterNode.MaxOpenConnCount > 0 { + db.SetMaxOpenConns(masterNode.MaxOpenConnCount) + } + if masterNode.MaxConnLifetime > 0 { + db.SetConnMaxLifetime(time.Duration(masterNode.MaxConnLifetime) * time.Second) + } + if v := dbCaches.Get(groupName); v == nil { + dbCaches.LockFunc(func(m map[string]interface{}) { + if v, ok := m[groupName]; !ok { + db.cache = gcache.New() + m[groupName] = db.cache + } else { + db.cache = v.(*gcache.Cache) + } + }) + } else { + db.cache = v.(*gcache.Cache) + } - return db, nil + return db, nil } - diff --git a/g/database/gdb/gdb_base.go b/g/database/gdb/gdb_base.go index f3754a942..032bdbf23 100644 --- a/g/database/gdb/gdb_base.go +++ b/g/database/gdb/gdb_base.go @@ -32,7 +32,7 @@ func (db *Db) SetDebug(debug bool) { } } -// 获取已经执行的SQL列表 +// 获取已经执行的SQL列表(仅在debug=true时有效) func (db *Db) GetQueriedSqls() []*Sql { if db.sqls == nil { return nil @@ -49,6 +49,21 @@ func (db *Db) GetQueriedSqls() []*Sql { return sqls } +// 打印已经执行的SQL列表(仅在debug=true时有效) +func (db *Db) PrintQueriedSqls() { + sqls := db.GetQueriedSqls() + for k, v := range sqls { + fmt.Println(len(sqls) - k, ":") + fmt.Println(" Sql :", v.Sql) + fmt.Println(" Args :", v.Args) + fmt.Println(" Error:", v.Error) + fmt.Println(" Start:", gtime.NewFromTimeStamp(v.Start).Format("Y-m-d H:i:s.u")) + fmt.Println(" End :", gtime.NewFromTimeStamp(v.End).Format("Y-m-d H:i:s.u")) + fmt.Println(" Cost :", v.End - v.Start, "ms") + fmt.Println(" Func :", v.Func) + } +} + // 关闭链接 func (db *Db) Close() error { if db.master != nil { @@ -81,7 +96,8 @@ func (db *Db) Query(query string, args ...interface{}) (*sql.Rows, error) { Sql : *p, Args : args, Error : err, - Cost : militime2 - militime1, + Start : militime1, + End : militime2, Func : "DB:Query", }) } else { @@ -108,7 +124,8 @@ func (db *Db) Exec(query string, args ...interface{}) (sql.Result, error) { Sql : *p, Args : args, Error : err, - Cost : militime2 - militime1, + Start : militime1, + End : militime2, Func : "DB:Exec", }) } else { @@ -291,7 +308,6 @@ func (db *Db) Begin() (*Tx, error) { func (db *Db) getInsertOperationByOption(option uint8) string { oper := "INSERT" switch option { - case OPTION_INSERT: case OPTION_REPLACE: oper = "REPLACE" case OPTION_SAVE: diff --git a/g/database/gdb/gdb_pgsql.go b/g/database/gdb/gdb_pgsql.go index bf4bf903b..7164ff513 100644 --- a/g/database/gdb/gdb_pgsql.go +++ b/g/database/gdb/gdb_pgsql.go @@ -13,7 +13,9 @@ import ( "database/sql" ) -// postgresql的适配 +// PostgreSQL的适配. +// 使用时需要import: +// _ "github.com/lib/pq" // @todo 需要完善replace和save的操作覆盖 // 数据库链接对象 diff --git a/g/database/gdb/gdb_sqlite.go b/g/database/gdb/gdb_sqlite.go new file mode 100644 index 000000000..2dda7fe9c --- /dev/null +++ b/g/database/gdb/gdb_sqlite.go @@ -0,0 +1,52 @@ +// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://gitee.com/johng/gf. +// @author wxkj + +package gdb + +import ( + "database/sql" +) + +// 使用时需要import: +// _ "github.com/mattn/go-sqlite3" + +// 数据库链接对象 +type dbsqlite struct { + Db +} + +func (db *dbsqlite) Open(c *ConfigNode) (*sql.DB, error) { + var source string + if c.Linkinfo != "" { + source = c.Linkinfo + } else { + source = c.Name + } + if db, err := sql.Open("sqlite3", source); err == nil { + return db, nil + } else { + return nil, err + } +} + +// 获得关键字操作符 - 左 +func (db *dbsqlite) getQuoteCharLeft() string { + return "`" +} + +// 获得关键字操作符 - 右 +func (db *dbsqlite) getQuoteCharRight() string { + return "`" +} + +// 在执行sql之前对sql进行进一步处理 +// @todo 需要增加对Save方法的支持,可使用正则来实现替换, +// @todo 将ON DUPLICATE KEY UPDATE触发器修改为两条SQL语句(INSERT OR IGNORE & UPDATE) +func (db *dbsqlite) handleSqlBeforeExec(q *string) *string { + + return q +} diff --git a/g/database/gdb/gdb_transaction.go b/g/database/gdb/gdb_transaction.go index 09d0cc94f..86d51d7dd 100644 --- a/g/database/gdb/gdb_transaction.go +++ b/g/database/gdb/gdb_transaction.go @@ -10,12 +10,11 @@ import ( "fmt" "errors" "strings" - "database/sql" - _ "github.com/lib/pq" - _ "github.com/go-sql-driver/mysql" - "gitee.com/johng/gf/g/util/gconv" "reflect" + "database/sql" "gitee.com/johng/gf/g/os/gtime" + "gitee.com/johng/gf/g/util/gconv" + _ "github.com/go-sql-driver/mysql" ) // 数据库事务对象 @@ -47,7 +46,8 @@ func (tx *Tx) Query(query string, args ...interface{}) (*sql.Rows, error) { Sql : *p, Args : args, Error : err, - Cost : militime2 - militime1, + Start : militime1, + End : militime2, Func : "TX:Query", }) } else { @@ -74,7 +74,8 @@ func (tx *Tx) Exec(query string, args ...interface{}) (sql.Result, error) { Sql : *p, Args : args, Error : err, - Cost : militime2 - militime1, + Start : militime1, + End : militime2, Func : "TX:Exec", }) } else { diff --git a/g/net/ghttp/ghttp_server_service_controller.go b/g/net/ghttp/ghttp_server_service_controller.go index 825edbd57..336da858f 100644 --- a/g/net/ghttp/ghttp_server_service_controller.go +++ b/g/net/ghttp/ghttp_server_service_controller.go @@ -40,6 +40,9 @@ func (s *Server)BindController(pattern string, c Controller, methods...string) e p := key if strings.EqualFold(p[len(p) - 6:], "/index") { p = p[0 : len(p) - 6] + if len(p) == 0 { + p = "/" + } } m[p] = &handlerItem { ctype : v.Elem().Type(), @@ -75,6 +78,9 @@ func (s *Server)BindControllerMethod(pattern string, c Controller, methods strin p := key if strings.EqualFold(p[len(p) - 6:], "/index") { p = p[0 : len(p) - 6] + if len(p) == 0 { + p = "/" + } } m[p] = &handlerItem { ctype : t, diff --git a/g/net/ghttp/ghttp_server_service_object.go b/g/net/ghttp/ghttp_server_service_object.go index e3467b0be..52ebea54f 100644 --- a/g/net/ghttp/ghttp_server_service_object.go +++ b/g/net/ghttp/ghttp_server_service_object.go @@ -36,6 +36,9 @@ func (s *Server)BindObject(pattern string, obj interface{}, methods...string) er p := key if strings.EqualFold(p[len(p) - 6:], "/index") { p = p[0 : len(p) - 6] + if len(p) == 0 { + p = "/" + } } m[p] = &handlerItem { ctype : nil, @@ -71,6 +74,9 @@ func (s *Server)BindObjectMethod(pattern string, obj interface{}, methods string p := key if strings.EqualFold(p[len(p) - 6:], "/index") { p = p[0 : len(p) - 6] + if len(p) == 0 { + p = "/" + } } m[p] = &handlerItem { ctype : nil, diff --git a/g/os/gtime/gtime_time.go b/g/os/gtime/gtime_time.go index 1c4a4f8c6..b94043ff8 100644 --- a/g/os/gtime/gtime_time.go +++ b/g/os/gtime/gtime_time.go @@ -66,10 +66,13 @@ func NewFromStrLayout (str string, layout string) *Time { return nil } -// 时间戳转换为时间对象 +// 时间戳转换为时间对象,时间戳支持到纳秒的数值 func NewFromTimeStamp (timestamp int64) *Time { - return &Time{ - time.Unix(timestamp, 0), + for timestamp < 1e18 { + timestamp *= 10 + } + return &Time { + time.Unix(int64(timestamp/1e9), timestamp%1e9), } } diff --git a/geg/database/mysql/gdb_debug.go b/geg/database/mysql/gdb_debug.go deleted file mode 100644 index a5bcbfccd..000000000 --- a/geg/database/mysql/gdb_debug.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import ( - "fmt" - "gitee.com/johng/gf/g/database/gdb" -) - -func main() { - gdb.AddDefaultConfigNode(gdb.ConfigNode { - Host : "127.0.0.1", - Port : "3306", - User : "root", - Pass : "123456", - Name : "test", - Type : "mysql", - Role : "master", - Charset : "utf8", - }) - db, err := gdb.New() - if err != nil { - panic(err) - } - - db.SetDebug(true) - // 执行3条SQL查询 - for i := 1; i <= 3; i++ { - db.Table("user").Where("uid=?", i).One() - } - // 构造一条错误查询 - db.Table("user").Where("no_such_field=?", "just_test").One() - - for k, v := range db.GetQueriedSqls() { - fmt.Println(k, ":") - fmt.Println("Sql :", v.Sql) - fmt.Println("Args :", v.Args) - fmt.Println("Error:", v.Error) - fmt.Println("Cost :", v.Cost) - fmt.Println("Func :", v.Func) - } -} \ No newline at end of file diff --git a/geg/database/mysql/gdb.go b/geg/database/orm/mysql/gdb.go similarity index 100% rename from geg/database/mysql/gdb.go rename to geg/database/orm/mysql/gdb.go diff --git a/geg/database/mysql/gdb_binary.go b/geg/database/orm/mysql/gdb_binary.go similarity index 100% rename from geg/database/mysql/gdb_binary.go rename to geg/database/orm/mysql/gdb_binary.go diff --git a/geg/database/mysql/gdb_cache.go b/geg/database/orm/mysql/gdb_cache.go similarity index 100% rename from geg/database/mysql/gdb_cache.go rename to geg/database/orm/mysql/gdb_cache.go diff --git a/geg/database/orm/mysql/gdb_debug.go b/geg/database/orm/mysql/gdb_debug.go new file mode 100644 index 000000000..9d17c4bf1 --- /dev/null +++ b/geg/database/orm/mysql/gdb_debug.go @@ -0,0 +1,32 @@ +package main + +import ( + "gitee.com/johng/gf/g/database/gdb" +) + +func main() { + gdb.AddDefaultConfigNode(gdb.ConfigNode{ + Host: "127.0.0.1", + Port: "3306", + User: "root", + Pass: "123456", + Name: "test", + Type: "mysql", + Role: "master", + Charset: "utf8", + }) + db, err := gdb.New() + if err != nil { + panic(err) + } + + db.SetDebug(true) + // 执行3条SQL查询 + for i := 1; i <= 3; i++ { + db.Table("user").Where("uid=?", i).One() + } + // 构造一条错误查询 + db.Table("user").Where("no_such_field=?", "just_test").One() + + db.PrintQueriedSqls() +} \ No newline at end of file diff --git a/geg/database/mysql/gdb_json_xml.go b/geg/database/orm/mysql/gdb_json_xml.go similarity index 100% rename from geg/database/mysql/gdb_json_xml.go rename to geg/database/orm/mysql/gdb_json_xml.go diff --git a/geg/database/orm/sqlite/sqlite.go b/geg/database/orm/sqlite/sqlite.go new file mode 100644 index 000000000..8ee930bd1 --- /dev/null +++ b/geg/database/orm/sqlite/sqlite.go @@ -0,0 +1,47 @@ +package main + +//import ( +// _ "github.com/mattn/go-sqlite3" +// "gitee.com/johng/gf/g/database/gdb" +// "gitee.com/johng/gf/g" +// "fmt" +//) +// +//func main() { +// gdb.SetConfig(gdb.Config{ +// "default": gdb.ConfigGroup{ +// gdb.ConfigNode{ +// Name: "/tmp/my.db", +// Type: "sqlite", +// }, +// }, +// }) +// db := g.Database() +// if db == nil { +// panic("db create failed") +// } +// defer db.Close() +// +// // 创建表 +// sql := `CREATE TABLE user ( +// uid INT PRIMARY KEY NOT NULL, +// name VARCHAR(30) NOT NULL +// );` +// if _, err := db.Exec(sql); err != nil { +// fmt.Println(err) +// } +// +// // 写入数据 +// result, err := db.Table("user").Data(g.Map{"uid" : 1, "name" : "john"}).Save() +// if err == nil { +// fmt.Println(result.RowsAffected()) +// } else { +// fmt.Println(err) +// } +// +// // 删除表 +// sql = `DROP TABLE user;` +// if _, err := db.Exec(sql); err != nil { +// fmt.Println(err) +// } +//} \ No newline at end of file diff --git a/geg/other/test.go b/geg/other/test.go index f48b7eb3d..744fb4fea 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,75 +1,9 @@ package main import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "errors" - "fmt" - "gitee.com/johng/gf/g/encoding/gbase64" -) - -const ( - ivDefValue = "I Love Go Frame!" + "fmt" ) func main() { - v := "1234567890123456789012345678901234567890123456789012345678901234567890" - k := "123456789012345 " - e, err := AesEncrypt([]byte(v), []byte(k)) - fmt.Println(err) - fmt.Println(len(e)) - fmt.Println(string(gbase64.Encode(string(e)))) - d, err := AesDecrypt([]byte(e), []byte(k)) - fmt.Println(err) - fmt.Println(string(d)) -} - -func AesEncrypt(plaintext []byte, key []byte) ([]byte, error) { - block, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - blockSize := block.BlockSize() - plaintext = PKCS5Padding(plaintext, blockSize) - iv := []byte(ivDefValue) - blockMode := cipher.NewCBCEncrypter(block, iv) - - ciphertext := make([]byte, len(plaintext)) - blockMode.CryptBlocks(ciphertext, plaintext) - - return ciphertext, nil -} - -func AesDecrypt(cipherText []byte, key []byte) ([]byte, error) { - block, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - blockSize := block.BlockSize() - if len(cipherText) < blockSize { - return nil, errors.New("cipherText too short") - } - iv := []byte(ivDefValue) - if len(cipherText)%blockSize != 0 { - return nil, errors.New("cipherText is not a multiple of the block size") - } - blockModel := cipher.NewCBCDecrypter(block, iv) - plaintext := make([]byte, len(cipherText)) - blockModel.CryptBlocks(plaintext, cipherText) - plaintext = PKCS5UnPadding(plaintext) - - return plaintext, nil -} - -func PKCS5Padding(src []byte, blockSize int) []byte { - padding := blockSize - len(src)%blockSize - padtext := bytes.Repeat([]byte{byte(padding)}, padding) - return append(src, padtext...) -} - -func PKCS5UnPadding(src []byte) []byte { - length := len(src) - unpadding := int(src[length-1]) - return src[:(length - unpadding)] + fmt.Println(1) } \ No newline at end of file