mirror of
https://gitee.com/johng/gf
synced 2026-06-09 02:57:43 +08:00
Compare commits
513 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 69fa5bf464 | |||
| e7dc58ac6c | |||
| 2033299632 | |||
| 639d34d5d9 | |||
| 605181da32 | |||
| 183f631190 | |||
| 64c99b9871 | |||
| d29b0a27ff | |||
| aaa726e6dc | |||
| 05cc0c4644 | |||
| e1c0a92e60 | |||
| c770e4779a | |||
| c135122ca1 | |||
| 1d87df2afe | |||
| 8efc0ca0ea | |||
| 001d524ff7 | |||
| 5c5dce9dc3 | |||
| 293256c2ca | |||
| 4e027c1de3 | |||
| 6712a33164 | |||
| 98531143a6 | |||
| 71127ba308 | |||
| 0f6f571ccb | |||
| 9b6936a4fb | |||
| 8b78609412 | |||
| 4a31082f8c | |||
| 3643a69d8d | |||
| adbaa4aa89 | |||
| 0e025eda1b | |||
| 7c44bf8e94 | |||
| e6718f1113 | |||
| f3f6adb03a | |||
| 977827e453 | |||
| bcc9153991 | |||
| c04be14cd9 | |||
| de8f29751d | |||
| 12d58e4d08 | |||
| 3ae44185f4 | |||
| 1290f42f75 | |||
| 17b91dcad7 | |||
| 72da1642ee | |||
| 76d93b3a61 | |||
| 7be0ee9566 | |||
| 326e1f8da5 | |||
| e4e44ddd19 | |||
| 46bdde9265 | |||
| 09eba58927 | |||
| 0e884c78f5 | |||
| 2f44721086 | |||
| efc0501548 | |||
| bb07c60838 | |||
| 330ea05ca3 | |||
| 1f534b48a3 | |||
| aceae5eee3 | |||
| d4d66fd529 | |||
| b7686d6d37 | |||
| b97e397354 | |||
| ab8fbf171e | |||
| 2c0cfa24b0 | |||
| f28a76d470 | |||
| f3525c84a3 | |||
| abe9b54112 | |||
| b2aa59d893 | |||
| 9e9865afa7 | |||
| ecc439d4e8 | |||
| 452f0fc99e | |||
| 54f47845f6 | |||
| cb238cd6d9 | |||
| 897a9f4584 | |||
| 55f9b121de | |||
| 386f38af5e | |||
| 65cea430c2 | |||
| 4d38b508a3 | |||
| 5c774fd391 | |||
| f6d760e90f | |||
| c3ffa40bad | |||
| 5ce5d0e593 | |||
| b83f1efde8 | |||
| ca5f14c366 | |||
| 95a8b51fb4 | |||
| 2b64979730 | |||
| 508cb7db88 | |||
| e8d6d96883 | |||
| 9378a6e7bf | |||
| 1d609fc5c7 | |||
| 49d1e3dbaf | |||
| b0e9334852 | |||
| 95bd369959 | |||
| a433890097 | |||
| c9c3be517c | |||
| 89e122cd31 | |||
| 27c2a03ea8 | |||
| b56eb3a948 | |||
| fb1b0bfd88 | |||
| dd10167ec2 | |||
| 3138ad10ec | |||
| 080ca82605 | |||
| 0290de2360 | |||
| 9b330adc1d | |||
| f3ff1ae08b | |||
| 4f699af051 | |||
| 7ebc7259cb | |||
| 9d22edbdb8 | |||
| 7e95058cb5 | |||
| f33753e0cd | |||
| ac71e1d753 | |||
| 8151b6efd6 | |||
| 94de306c93 | |||
| 51f8ea26de | |||
| 29d9bb17cd | |||
| 30501a882d | |||
| f2f98e1d16 | |||
| cbf465eeea | |||
| 79c400e912 | |||
| cc4c49b5b0 | |||
| 96ea2c911d | |||
| 131b11680a | |||
| 3c6ab96283 | |||
| d2c4fa921a | |||
| dbb51a9328 | |||
| 55bfc3fa73 | |||
| d9c5182d1a | |||
| 899fcbf2da | |||
| 6e2c0d8706 | |||
| c4e599ce63 | |||
| 9cad9e4467 | |||
| 48019444ea | |||
| 76d31a7fbb | |||
| facb9d93c0 | |||
| 1d6bd46c5e | |||
| 8646dc1825 | |||
| 2992bdeed7 | |||
| c5601e6c88 | |||
| ef1d9a561c | |||
| 2d3b32c94a | |||
| 269378aa0d | |||
| 6889542801 | |||
| 8e6f1f1740 | |||
| b6ab1a992c | |||
| a5a267567c | |||
| f05f855c07 | |||
| bc5f773ba6 | |||
| 55308cc37c | |||
| 4d9db6edf0 | |||
| 38111a64e3 | |||
| bd8d3fca08 | |||
| 89ccaa3915 | |||
| 7c2cff7d99 | |||
| 788e15dbb6 | |||
| a0172d9d7e | |||
| bc1a7a1644 | |||
| c98234d3e6 | |||
| 45a94d23d5 | |||
| 351de5ee6a | |||
| 3559436d1e | |||
| 189f4c1637 | |||
| caead810e2 | |||
| ebdc5d9c9d | |||
| 4164059211 | |||
| bd27258c46 | |||
| ddf0605bc4 | |||
| 991dbe50e0 | |||
| 8050efb835 | |||
| 9355bc73a2 | |||
| acc2a6a353 | |||
| 09e83e7b8d | |||
| c0df8a3d80 | |||
| 33e890d225 | |||
| 750e5df962 | |||
| 74c65439fc | |||
| f290bd7170 | |||
| d398b749d4 | |||
| 2fd5a1574a | |||
| a7504f0629 | |||
| 3c35bb85ee | |||
| 6621edb04c | |||
| b0c722e297 | |||
| 47a6284274 | |||
| 150b8edb70 | |||
| aa5d3285eb | |||
| 993d6e3076 | |||
| 78ad9d8c2d | |||
| feff3ddce3 | |||
| 80fddad64d | |||
| 1e6dd0be02 | |||
| 22ecf4f1b6 | |||
| 5d21148657 | |||
| edb745ed92 | |||
| 61f4e0da6f | |||
| 767afa3106 | |||
| 16779359ee | |||
| 770653fafa | |||
| 0c021b6da7 | |||
| ec92b08f25 | |||
| 13e2353729 | |||
| 90d19a84ce | |||
| b4c0b95d8a | |||
| b7e8255066 | |||
| 5f8e6ad9ed | |||
| c8d253eb56 | |||
| 4814624cff | |||
| cc67f3d388 | |||
| f7c2a51c9f | |||
| 3db83e1159 | |||
| 3bb002796c | |||
| 45170bc53e | |||
| b79ff84c6f | |||
| 938c46fec9 | |||
| 8a13d94526 | |||
| 1e844d505a | |||
| a123a2c086 | |||
| 1eeeeb853e | |||
| 6e7224e306 | |||
| 9e064e2651 | |||
| 8d9dd17eac | |||
| cf1d3d3d2b | |||
| 9480ffcdc0 | |||
| 5db10add4a | |||
| fa66bf5d9d | |||
| f69da3ace1 | |||
| e01bfa05c3 | |||
| 231238c157 | |||
| 7edec099ab | |||
| 83eb8be064 | |||
| 7af30df494 | |||
| f026686fda | |||
| 35a50b9c6c | |||
| 010e2f951a | |||
| f7f86ad65a | |||
| 1e19f447d1 | |||
| 8cc378331d | |||
| 4721f68fd8 | |||
| 0d11c0a1f8 | |||
| 5076613a8f | |||
| c5a44daa65 | |||
| 520970b71f | |||
| ebfb08ee3f | |||
| 9e38b2cb90 | |||
| 71b1f00dc5 | |||
| 59d6830ab1 | |||
| 8779a3f211 | |||
| c10149baa0 | |||
| 4f87668780 | |||
| 63f33d1d8c | |||
| 734aa5a6fe | |||
| 371aef224d | |||
| 362e380ada | |||
| e995bd8c9a | |||
| 81b211dd1a | |||
| 0515fc94cb | |||
| 46ee070f0a | |||
| b17e3a6804 | |||
| 9160bee1af | |||
| 8e6018cfff | |||
| c08739b5a3 | |||
| 385a503d31 | |||
| ef286b0c15 | |||
| 53aba2d4b8 | |||
| f22da4ba3a | |||
| 23c2f12672 | |||
| 7fd53673ce | |||
| e64fd088b9 | |||
| 15672e7a09 | |||
| 2ea1d2c7b2 | |||
| 90d751f98d | |||
| 25a91c732c | |||
| 01995f5501 | |||
| 68abb3cf3d | |||
| 77cc323d0e | |||
| c7a9c03495 | |||
| f7850e3ed3 | |||
| 82125416a2 | |||
| 2c1e2155e3 | |||
| 2d30a53c3a | |||
| ccceeae29c | |||
| f22b98456f | |||
| e7f1bd692b | |||
| f82f7ab199 | |||
| 4d33b527b6 | |||
| 7bcc596308 | |||
| be2d4b080e | |||
| c96e5f5a9e | |||
| 3c23766674 | |||
| 718089fc11 | |||
| 8ab44dcb44 | |||
| 992522342c | |||
| 040898cdc3 | |||
| 343126ef22 | |||
| 05760d1eac | |||
| c10f73f1f7 | |||
| 7e0fa8e0cd | |||
| 6fe6218505 | |||
| 6059782de8 | |||
| 4844eea0ab | |||
| 8ecd62d3de | |||
| 4c610b4f58 | |||
| 1932c4ec44 | |||
| bd2e51ddca | |||
| ddcb7121c1 | |||
| f1f575fd5c | |||
| 99adb7cdc4 | |||
| 6b7ea97777 | |||
| 495b5758ec | |||
| ba56eb87b1 | |||
| 4258a3bbc9 | |||
| 4912331ddc | |||
| e87b7ecf5d | |||
| 23bce7bde6 | |||
| 926b664615 | |||
| fa8257c85b | |||
| abb9c88c23 | |||
| f89976cad5 | |||
| 0389778725 | |||
| 3c36285126 | |||
| 75054ee109 | |||
| 8447b1a42b | |||
| e5265a1c46 | |||
| 6e8ef8d0b0 | |||
| 75c081afc9 | |||
| 060fd9eaba | |||
| 63e5a60344 | |||
| 75dc1d82c1 | |||
| a5e048eb5f | |||
| 65bc1d5eb8 | |||
| bfa64705b5 | |||
| 17aea8d7d4 | |||
| a6a01fd7f2 | |||
| 41a9e91b4c | |||
| e2c1e11f95 | |||
| c2966817ce | |||
| 16958413bb | |||
| c0a0913d4b | |||
| 6d47810782 | |||
| 7a9ea2e546 | |||
| 7881b2dee4 | |||
| 5f223ef049 | |||
| e57942b374 | |||
| f18e6f078c | |||
| f667cbc2a2 | |||
| 07e65c14a9 | |||
| 0b6d04485e | |||
| 36401a063d | |||
| 849e7370d1 | |||
| f01dca0895 | |||
| 55137b2aa3 | |||
| 22540921b3 | |||
| 6174097a07 | |||
| 52de11b1fe | |||
| 5a646179ad | |||
| 33ae93e050 | |||
| f3d859159d | |||
| 4fb01e68f7 | |||
| 2812a247aa | |||
| 2b46e765c4 | |||
| c7f911cae2 | |||
| 9f0548c03d | |||
| c578df06a3 | |||
| 8ed3cf9c97 | |||
| 2438f565e9 | |||
| 8230c72ec6 | |||
| d716037caa | |||
| 855a4ddb2c | |||
| 74be9fac18 | |||
| e9fba5a166 | |||
| 0a92df691b | |||
| e513cd10ed | |||
| b702d98700 | |||
| 99f1d9d0ed | |||
| 707b08c585 | |||
| f44c19868c | |||
| 13ab139afc | |||
| cacb2f142b | |||
| 11f0317e92 | |||
| 9a667c8803 | |||
| c1cce17934 | |||
| 5a92d7de0d | |||
| 53bf378868 | |||
| cd7c45c00c | |||
| f13a5ad82e | |||
| 2e9be609c8 | |||
| 2f2f6e1ffe | |||
| f3bd2b67f7 | |||
| 645cecdffb | |||
| 22e9965629 | |||
| 24ea9f9245 | |||
| 9dbde6e8f1 | |||
| fe0b34544d | |||
| 5e489d59b4 | |||
| 61f49574a9 | |||
| 2fabcb62a8 | |||
| 3bc3b652c1 | |||
| 042a6f12f5 | |||
| 5acce82e63 | |||
| 4732bf46ad | |||
| 5bed5a1532 | |||
| 5b7576430f | |||
| 356f4cd701 | |||
| c444630d1e | |||
| 8e40cded42 | |||
| 0e52d467d3 | |||
| 6665d62e7e | |||
| 5bdf1a71b8 | |||
| a34ca0ff4b | |||
| 7f0163d958 | |||
| 31f19b0eee | |||
| 93d0760898 | |||
| 4863e7a6ae | |||
| a161b44cc7 | |||
| 7072244420 | |||
| f68b66e606 | |||
| 4e7c6c1fb4 | |||
| d8a7e36478 | |||
| 8971ad8445 | |||
| 270af8accb | |||
| b0ef63fc9d | |||
| 6e1f8c3cfc | |||
| e58d7e8dda | |||
| 3a3384cf06 | |||
| ef2a9f6fd1 | |||
| 63f756f731 | |||
| bb1c27c36a | |||
| 87cd0703c0 | |||
| 6317d9de53 | |||
| 7acf16fdba | |||
| a52b454d3e | |||
| 816e075c52 | |||
| 9882b361a8 | |||
| 4415dcf1c1 | |||
| 42fd583bfd | |||
| c70bc7c96a | |||
| 02bd780a33 | |||
| 24a2192ce2 | |||
| d8ef8a1f5d | |||
| 745a913cfb | |||
| 13dba407a2 | |||
| 34e7c5f809 | |||
| d570624caa | |||
| f18312419b | |||
| 89f869dd44 | |||
| 20b64507b1 | |||
| 7443246e05 | |||
| f9e7823c14 | |||
| 52943b283c | |||
| 36403fdc08 | |||
| 0317f6812e | |||
| 5169137069 | |||
| 7d9bccf912 | |||
| 14f56ea18f | |||
| 1736d271d2 | |||
| 19755ad233 | |||
| cfdd043e4e | |||
| 78917ed5cb | |||
| 88684ca00a | |||
| 784983806a | |||
| cdb3b94e22 | |||
| 83dcc4a5e0 | |||
| a6c0b281a3 | |||
| 3120f24553 | |||
| ac9be6134b | |||
| 4c1b4f7858 | |||
| 1e45bf93d8 | |||
| e8dd3979b6 | |||
| 374ee4c0ea | |||
| 95411aff77 | |||
| 1999ef95c1 | |||
| b15075fdfe | |||
| 4d2b244319 | |||
| 4c3af63076 | |||
| 91bbff6ced | |||
| 26aab44ec8 | |||
| 2e10ce421b | |||
| 8eda69b11e | |||
| 7d7b242968 | |||
| 202419202f | |||
| 55078beed1 | |||
| d9f4e6eaa6 | |||
| 2ba0913bea | |||
| 8f2dcf21ff | |||
| 01b06e0745 | |||
| 665b5960c8 | |||
| eb6a7a4728 | |||
| 7df53ff55e | |||
| 8021f39710 | |||
| f2af08270b | |||
| c2f028848c | |||
| d9c7224861 | |||
| f59a1ada88 | |||
| 705ab1d33f | |||
| c07c4d7217 | |||
| b867b2a0bc | |||
| 872d674182 | |||
| 4682abafdf | |||
| b7d194cf52 | |||
| edf2366296 | |||
| 22af5be71f | |||
| f662ff8051 | |||
| 8c51121b3b | |||
| e9a0805801 | |||
| afadbc6621 | |||
| ca546fc30b | |||
| 7c7c168c3d | |||
| 16f0bb96db | |||
| 33a899d32e | |||
| f3a208f02f | |||
| 81fd3d06bb | |||
| 9227139cf8 | |||
| f2190e50b2 | |||
| c4537b4753 | |||
| 167d58490b | |||
| d36aceb9f1 | |||
| eb31922124 | |||
| bec9f5a847 | |||
| d6e6ddf996 |
@ -9,7 +9,7 @@ import (
|
||||
|
||||
func main() {
|
||||
// 创建一个对象池,过期时间为1000毫秒
|
||||
p := gpool.New(1000, nil)
|
||||
p := gpool.New(1000*time.Millisecond, nil)
|
||||
|
||||
// 从池中取一个对象,返回nil及错误信息
|
||||
fmt.Println(p.Get())
|
||||
|
||||
@ -11,7 +11,7 @@ import (
|
||||
|
||||
func main() {
|
||||
// 创建对象复用池,对象过期时间为3000毫秒,并给定创建及销毁方法
|
||||
p := gpool.New(3000, func() (interface{}, error) {
|
||||
p := gpool.New(3000*time.Millisecond, func() (interface{}, error) {
|
||||
return gtcp.NewConn("www.baidu.com:80")
|
||||
}, func(i interface{}) {
|
||||
glog.Println("expired")
|
||||
|
||||
79
.example/database/gdb/driver/driver/driver.go
Normal file
79
.example/database/gdb/driver/driver/driver.go
Normal file
@ -0,0 +1,79 @@
|
||||
// Copyright 2017 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 driver
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"github.com/gogf/gf/database/gdb"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
)
|
||||
|
||||
// MyDriver is a custom database driver, which is used for testing only.
|
||||
// For simplifying the unit testing case purpose, MyDriver struct inherits the mysql driver
|
||||
// gdb.DriverMysql and overwrites its functions DoQuery and DoExec.
|
||||
// So if there's any sql execution, it goes through MyDriver.DoQuery/MyDriver.DoExec firstly
|
||||
// and then gdb.DriverMysql.DoQuery/gdb.DriverMysql.DoExec.
|
||||
// You can call it sql "HOOK" or "HiJack" as your will.
|
||||
type MyDriver struct {
|
||||
*gdb.DriverMysql
|
||||
}
|
||||
|
||||
var (
|
||||
// customDriverName is my driver name, which is used for registering.
|
||||
customDriverName = "MyDriver"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// It here registers my custom driver in package initialization function "init".
|
||||
// You can later use this type in the database configuration.
|
||||
if err := gdb.Register(customDriverName, &MyDriver{}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// New creates and returns a database object for mysql.
|
||||
// It implements the interface of gdb.Driver for extra database driver installation.
|
||||
func (d *MyDriver) New(core *gdb.Core, node *gdb.ConfigNode) (gdb.DB, error) {
|
||||
return &MyDriver{
|
||||
&gdb.DriverMysql{
|
||||
Core: core,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DoQuery commits the sql string and its arguments to underlying driver
|
||||
// through given link object and returns the execution result.
|
||||
func (d *MyDriver) DoQuery(link gdb.Link, sql string, args ...interface{}) (rows *sql.Rows, err error) {
|
||||
tsMilli := gtime.TimestampMilli()
|
||||
rows, err = d.DriverMysql.DoQuery(link, sql, args...)
|
||||
if _, err := d.DriverMysql.InsertIgnore("monitor", g.Map{
|
||||
"sql": gdb.FormatSqlWithArgs(sql, args),
|
||||
"cost": gtime.TimestampMilli() - tsMilli,
|
||||
"time": gtime.Now(),
|
||||
"error": err.Error(),
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DoExec commits the query string and its arguments to underlying driver
|
||||
// through given link object and returns the execution result.
|
||||
func (d *MyDriver) DoExec(link gdb.Link, sql string, args ...interface{}) (result sql.Result, err error) {
|
||||
tsMilli := gtime.TimestampMilli()
|
||||
result, err = d.DriverMysql.DoExec(link, sql, args...)
|
||||
if _, err := d.DriverMysql.InsertIgnore("monitor", g.Map{
|
||||
"sql": gdb.FormatSqlWithArgs(sql, args),
|
||||
"cost": gtime.TimestampMilli() - tsMilli,
|
||||
"time": gtime.Now(),
|
||||
"error": err.Error(),
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
1
.example/database/gdb/driver/main.go
Normal file
1
.example/database/gdb/driver/main.go
Normal file
@ -0,0 +1 @@
|
||||
package main
|
||||
@ -1,8 +1,3 @@
|
||||
|
||||
[database]
|
||||
type = "mssql"
|
||||
host = "127.0.0.1"
|
||||
port = "1451"
|
||||
user = "sa"
|
||||
pass = "eno@123"
|
||||
name = "frpc"
|
||||
linkinfo = "mssql:user id=test;password=test1;server=122.152.202.91;port=1433;database=test;encrypt=disable"
|
||||
@ -2,13 +2,22 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
|
||||
_ "github.com/denisenkom/go-mssqldb"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
r, err := g.DB().GetAll(`SELECT TOP 10 * FROM KF_PatInfo_Emergency`)
|
||||
fmt.Println(err)
|
||||
g.Dump(r.ToList())
|
||||
type Table2 struct {
|
||||
Id string `orm:"id;pr" json:"id"` //ID
|
||||
Createtime gtime.Time `orm:"createtime" json:"createtime"` //创建时间
|
||||
Updatetime gtime.Time `orm:"updatetime" json:"updatetime"` //更新时间
|
||||
}
|
||||
var table2 Table2
|
||||
err := g.DB().Table("table2").Where("id=?", 1).Struct(&table2)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(table2.Createtime)
|
||||
}
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
|
||||
# MySQL数据库配置
|
||||
[database]
|
||||
# debug = true
|
||||
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true&loc=Local"
|
||||
debug = true
|
||||
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true&loc=Local"
|
||||
MaxOpen = 100
|
||||
|
||||
#[database]
|
||||
# [[database.default]]
|
||||
|
||||
@ -2,7 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
@ -11,11 +10,19 @@ func main() {
|
||||
// 开启调试模式,以便于记录所有执行的SQL
|
||||
db.SetDebug(true)
|
||||
|
||||
r, e := db.Table("test").OrderBy("id asc").All()
|
||||
r, e := db.GetAll("SELECT * from `user` where id in(?)", g.Slice{})
|
||||
if e != nil {
|
||||
panic(e)
|
||||
fmt.Println(e)
|
||||
}
|
||||
if r != nil {
|
||||
fmt.Println(r.ToList())
|
||||
fmt.Println(r)
|
||||
}
|
||||
return
|
||||
//r, e := db.Table("user").Where("id in(?)", g.Slice{}).All()
|
||||
//if e != nil {
|
||||
// fmt.Println(e)
|
||||
//}
|
||||
//if r != nil {
|
||||
// fmt.Println(r.List())
|
||||
//}
|
||||
}
|
||||
|
||||
18
.example/database/gdb/mysql/gdb_reconnect.go
Normal file
18
.example/database/gdb/mysql/gdb_reconnect.go
Normal file
@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
db := g.DB()
|
||||
db.SetDebug(true)
|
||||
for {
|
||||
r, err := db.Table("user").All()
|
||||
fmt.Println(err)
|
||||
fmt.Println(r)
|
||||
time.Sleep(time.Second * 10)
|
||||
}
|
||||
}
|
||||
@ -2,23 +2,11 @@ package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
)
|
||||
|
||||
func main() {
|
||||
db := g.DB()
|
||||
//db.SetDebug(true)
|
||||
|
||||
type User struct {
|
||||
Id int
|
||||
Name *gtime.Time
|
||||
}
|
||||
|
||||
user := new(User)
|
||||
e := db.Table("test").Where("id", 10000).Struct(user)
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
g.Dump(user)
|
||||
db.SetDebug(true)
|
||||
|
||||
db.Table("user").Data("num=num+1").Where("id", 8).Update()
|
||||
}
|
||||
|
||||
@ -1,16 +1,68 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/encoding/gcompress"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := gcompress.ZipPath(
|
||||
`D:\Workspace\Go\GOPATH\src\github.com\gogf\gf\geg`,
|
||||
`D:\Workspace\Go\GOPATH\src\github.com\gogf\gf\geg\encoding\gcompress\data.zip`,
|
||||
"my-dir",
|
||||
)
|
||||
fmt.Println(err)
|
||||
// srcFile could be a single file or a directory
|
||||
func Zip(srcFile string, destZip string) error {
|
||||
zipfile, err := os.Create(destZip)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer zipfile.Close()
|
||||
|
||||
archive := zip.NewWriter(zipfile)
|
||||
defer archive.Close()
|
||||
|
||||
filepath.Walk(srcFile, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
header, err := zip.FileInfoHeader(info)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
header.Name = strings.TrimPrefix(path, filepath.Dir(srcFile)+"/")
|
||||
// header.Name = path
|
||||
if info.IsDir() {
|
||||
header.Name += "/"
|
||||
} else {
|
||||
header.Method = zip.Deflate
|
||||
}
|
||||
|
||||
writer, err := archive.CreateHeader(header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !info.IsDir() {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
_, err = io.Copy(writer, file)
|
||||
}
|
||||
return err
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func main() {
|
||||
src := `/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/test`
|
||||
dst := `/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/test.zip`
|
||||
//src := `/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/README.MD`
|
||||
//dst := `/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/README.MD.zip`
|
||||
fmt.Println(gcompress.ZipPath(src, dst))
|
||||
//fmt.Println(Zip(src, dst))
|
||||
}
|
||||
|
||||
@ -100,53 +100,53 @@ func testConvert() {
|
||||
func testSplitChar() {
|
||||
var v interface{}
|
||||
j := gjson.New(nil)
|
||||
t1 := gtime.Nanosecond()
|
||||
t1 := gtime.TimestampNano()
|
||||
j.Set("a.b.c.d.e.f.g.h.i.j.k", 1)
|
||||
t2 := gtime.Nanosecond()
|
||||
t2 := gtime.TimestampNano()
|
||||
fmt.Println(t2 - t1)
|
||||
|
||||
t5 := gtime.Nanosecond()
|
||||
t5 := gtime.TimestampNano()
|
||||
v = j.Get("a.b.c.d.e.f.g.h.i.j.k")
|
||||
t6 := gtime.Nanosecond()
|
||||
t6 := gtime.TimestampNano()
|
||||
fmt.Println(v)
|
||||
fmt.Println(t6 - t5)
|
||||
|
||||
j.SetSplitChar('#')
|
||||
|
||||
t7 := gtime.Nanosecond()
|
||||
t7 := gtime.TimestampNano()
|
||||
v = j.Get("a#b#c#d#e#f#g#h#i#j#k")
|
||||
t8 := gtime.Nanosecond()
|
||||
t8 := gtime.TimestampNano()
|
||||
fmt.Println(v)
|
||||
fmt.Println(t8 - t7)
|
||||
}
|
||||
|
||||
func testViolenceCheck() {
|
||||
j := gjson.New(nil)
|
||||
t1 := gtime.Nanosecond()
|
||||
t1 := gtime.TimestampNano()
|
||||
j.Set("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a", 1)
|
||||
t2 := gtime.Nanosecond()
|
||||
t2 := gtime.TimestampNano()
|
||||
fmt.Println(t2 - t1)
|
||||
|
||||
t3 := gtime.Nanosecond()
|
||||
t3 := gtime.TimestampNano()
|
||||
j.Set("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a", 1)
|
||||
t4 := gtime.Nanosecond()
|
||||
t4 := gtime.TimestampNano()
|
||||
fmt.Println(t4 - t3)
|
||||
|
||||
t5 := gtime.Nanosecond()
|
||||
t5 := gtime.TimestampNano()
|
||||
j.Get("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a")
|
||||
t6 := gtime.Nanosecond()
|
||||
t6 := gtime.TimestampNano()
|
||||
fmt.Println(t6 - t5)
|
||||
|
||||
j.SetViolenceCheck(false)
|
||||
|
||||
t7 := gtime.Nanosecond()
|
||||
t7 := gtime.TimestampNano()
|
||||
j.Set("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a", 1)
|
||||
t8 := gtime.Nanosecond()
|
||||
t8 := gtime.TimestampNano()
|
||||
fmt.Println(t8 - t7)
|
||||
|
||||
t9 := gtime.Nanosecond()
|
||||
t9 := gtime.TimestampNano()
|
||||
j.Get("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a")
|
||||
t10 := gtime.Nanosecond()
|
||||
t10 := gtime.TimestampNano()
|
||||
fmt.Println(t10 - t9)
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
package article
|
||||
|
||||
// Fill with you ideas below.
|
||||
@ -1,68 +0,0 @@
|
||||
// ==========================================================================
|
||||
// This is auto-generated by gf cli tool. You may not really want to edit it.
|
||||
// ==========================================================================
|
||||
|
||||
package article
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"github.com/gogf/gf/database/gdb"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
)
|
||||
|
||||
// Entity is the golang structure for table gf_article.
|
||||
type Entity struct {
|
||||
Id int `orm:"id,primary" json:"id"` //
|
||||
CatId int `orm:"cat_id" json:"cat_id"` // 分类ID
|
||||
Uid int `orm:"uid" json:"uid"` // 用户ID
|
||||
Title string `orm:"title" json:"title"` // 标题
|
||||
Content string `orm:"content" json:"content"` // 内容
|
||||
Order int `orm:"order" json:"order"` // 排序
|
||||
Brief string `orm:"brief" json:"brief"` // 摘要
|
||||
Thumb string `orm:"thumb" json:"thumb"` // 缩略图
|
||||
Tags string `orm:"tags" json:"tags"` // 标签
|
||||
Referer string `orm:"referer" json:"referer"` // 内容来源
|
||||
Status int `orm:"status" json:"status"` // 状态\n0: 禁用\n1: 正常
|
||||
CreateTime *gtime.Time `orm:"create_time" json:"create_time"` // 创建时间
|
||||
UpdateTime *gtime.Time `orm:"update_time" json:"update_time"` // 修改时间
|
||||
}
|
||||
|
||||
// Article is alias of Entity, which some developers say they just want.
|
||||
type Article = Entity
|
||||
|
||||
// OmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers
|
||||
// the data and where attributes for empty values.
|
||||
func (r *Entity) OmitEmpty() *arModel {
|
||||
return Model.Data(r).OmitEmpty()
|
||||
}
|
||||
|
||||
// Inserts does "INSERT...INTO..." statement for inserting current object into table.
|
||||
func (r *Entity) Insert() (result sql.Result, err error) {
|
||||
return Model.Data(r).Insert()
|
||||
}
|
||||
|
||||
// Replace does "REPLACE...INTO..." statement for inserting current object into table.
|
||||
// If there's already another same record in the table (it checks using primary key or unique index),
|
||||
// it deletes it and insert this one.
|
||||
func (r *Entity) Replace() (result sql.Result, err error) {
|
||||
return Model.Data(r).Replace()
|
||||
}
|
||||
|
||||
// Save does "INSERT...INTO..." statement for inserting/updating current object into table.
|
||||
// It updates the record if there's already another same record in the table
|
||||
// (it checks using primary key or unique index).
|
||||
func (r *Entity) Save() (result sql.Result, err error) {
|
||||
return Model.Data(r).Save()
|
||||
}
|
||||
|
||||
// Update does "UPDATE...WHERE..." statement for updating current object from table.
|
||||
// It updates the record if there's already another same record in the table
|
||||
// (it checks using primary key or unique index).
|
||||
func (r *Entity) Update() (result sql.Result, err error) {
|
||||
return Model.Data(r).Where(gdb.GetWhereConditionOfStruct(r)).Update()
|
||||
}
|
||||
|
||||
// Delete does "DELETE FROM...WHERE..." statement for deleting current object from table.
|
||||
func (r *Entity) Delete() (result sql.Result, err error) {
|
||||
return Model.Where(gdb.GetWhereConditionOfStruct(r)).Delete()
|
||||
}
|
||||
@ -1,362 +0,0 @@
|
||||
// ==========================================================================
|
||||
// This is auto-generated by gf cli tool. You may not really want to edit it.
|
||||
// ==========================================================================
|
||||
|
||||
package article
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"github.com/gogf/gf/database/gdb"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"time"
|
||||
)
|
||||
|
||||
// arModel is a active record design model for table gf_article operations.
|
||||
type arModel struct {
|
||||
M *gdb.Model
|
||||
}
|
||||
|
||||
var (
|
||||
// Table is the table name of gf_article.
|
||||
Table = "gf_article"
|
||||
// Model is the model object of gf_article.
|
||||
Model = &arModel{g.DB("default").Table(Table).Safe()}
|
||||
)
|
||||
|
||||
// FindOne is a convenience method for Model.FindOne.
|
||||
// See Model.FindOne.
|
||||
func FindOne(where ...interface{}) (*Entity, error) {
|
||||
return Model.FindOne(where...)
|
||||
}
|
||||
|
||||
// FindAll is a convenience method for Model.FindAll.
|
||||
// See Model.FindAll.
|
||||
func FindAll(where ...interface{}) ([]*Entity, error) {
|
||||
return Model.FindAll(where...)
|
||||
}
|
||||
|
||||
// FindValue is a convenience method for Model.FindValue.
|
||||
// See Model.FindValue.
|
||||
func FindValue(fieldsAndWhere ...interface{}) (gdb.Value, error) {
|
||||
return Model.FindValue(fieldsAndWhere...)
|
||||
}
|
||||
|
||||
// FindCount is a convenience method for Model.FindCount.
|
||||
// See Model.FindCount.
|
||||
func FindCount(where ...interface{}) (int, error) {
|
||||
return Model.FindCount(where...)
|
||||
}
|
||||
|
||||
// Insert is a convenience method for Model.Insert.
|
||||
func Insert(data ...interface{}) (result sql.Result, err error) {
|
||||
return Model.Insert(data...)
|
||||
}
|
||||
|
||||
// Replace is a convenience method for Model.Replace.
|
||||
func Replace(data ...interface{}) (result sql.Result, err error) {
|
||||
return Model.Replace(data...)
|
||||
}
|
||||
|
||||
// Save is a convenience method for Model.Save.
|
||||
func Save(data ...interface{}) (result sql.Result, err error) {
|
||||
return Model.Save(data...)
|
||||
}
|
||||
|
||||
// Update is a convenience method for Model.Update.
|
||||
func Update(dataAndWhere ...interface{}) (result sql.Result, err error) {
|
||||
return Model.Update(dataAndWhere...)
|
||||
}
|
||||
|
||||
// Delete is a convenience method for Model.Delete.
|
||||
func Delete(where ...interface{}) (result sql.Result, err error) {
|
||||
return Model.Delete(where...)
|
||||
}
|
||||
|
||||
// TX sets the transaction for current operation.
|
||||
func (m *arModel) TX(tx *gdb.TX) *arModel {
|
||||
return &arModel{m.M.TX(tx)}
|
||||
}
|
||||
|
||||
// Master marks the following operation on master node.
|
||||
func (m *arModel) Master() *arModel {
|
||||
return &arModel{m.M.Master()}
|
||||
}
|
||||
|
||||
// Slave marks the following operation on slave node.
|
||||
// Note that it makes sense only if there's any slave node configured.
|
||||
func (m *arModel) Slave() *arModel {
|
||||
return &arModel{m.M.Slave()}
|
||||
}
|
||||
|
||||
// LeftJoin does "LEFT JOIN ... ON ..." statement on the model.
|
||||
func (m *arModel) LeftJoin(joinTable string, on string) *arModel {
|
||||
return &arModel{m.M.LeftJoin(joinTable, on)}
|
||||
}
|
||||
|
||||
// RightJoin does "RIGHT JOIN ... ON ..." statement on the model.
|
||||
func (m *arModel) RightJoin(joinTable string, on string) *arModel {
|
||||
return &arModel{m.M.RightJoin(joinTable, on)}
|
||||
}
|
||||
|
||||
// InnerJoin does "INNER JOIN ... ON ..." statement on the model.
|
||||
func (m *arModel) InnerJoin(joinTable string, on string) *arModel {
|
||||
return &arModel{m.M.InnerJoin(joinTable, on)}
|
||||
}
|
||||
|
||||
// Fields sets the operation fields of the model, multiple fields joined using char ','.
|
||||
func (m *arModel) Fields(fields string) *arModel {
|
||||
return &arModel{m.M.Fields(fields)}
|
||||
}
|
||||
|
||||
// FieldsEx sets the excluded operation fields of the model, multiple fields joined using char ','.
|
||||
func (m *arModel) FieldsEx(fields string) *arModel {
|
||||
return &arModel{m.M.FieldsEx(fields)}
|
||||
}
|
||||
|
||||
// Option sets the extra operation option for the model.
|
||||
func (m *arModel) Option(option int) *arModel {
|
||||
return &arModel{m.M.Option(option)}
|
||||
}
|
||||
|
||||
// OmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers
|
||||
// the data and where attributes for empty values.
|
||||
func (m *arModel) OmitEmpty() *arModel {
|
||||
return &arModel{m.M.OmitEmpty()}
|
||||
}
|
||||
|
||||
// Filter marks filtering the fields which does not exist in the fields of the operated table.
|
||||
func (m *arModel) Filter() *arModel {
|
||||
return &arModel{m.M.Filter()}
|
||||
}
|
||||
|
||||
// Where sets the condition statement for the model. The parameter <where> can be type of
|
||||
// string/map/gmap/slice/struct/*struct, etc. Note that, if it's called more than one times,
|
||||
// multiple conditions will be joined into where statement using "AND".
|
||||
// Eg:
|
||||
// Where("uid=10000")
|
||||
// Where("uid", 10000)
|
||||
// Where("money>? AND name like ?", 99999, "vip_%")
|
||||
// Where("uid", 1).Where("name", "john")
|
||||
// Where("status IN (?)", g.Slice{1,2,3})
|
||||
// Where("age IN(?,?)", 18, 50)
|
||||
// Where(User{ Id : 1, UserName : "john"})
|
||||
func (m *arModel) Where(where interface{}, args ...interface{}) *arModel {
|
||||
return &arModel{m.M.Where(where, args...)}
|
||||
}
|
||||
|
||||
// And adds "AND" condition to the where statement.
|
||||
func (m *arModel) And(where interface{}, args ...interface{}) *arModel {
|
||||
return &arModel{m.M.And(where, args...)}
|
||||
}
|
||||
|
||||
// Or adds "OR" condition to the where statement.
|
||||
func (m *arModel) Or(where interface{}, args ...interface{}) *arModel {
|
||||
return &arModel{m.M.Or(where, args...)}
|
||||
}
|
||||
|
||||
// Group sets the "GROUP BY" statement for the model.
|
||||
func (m *arModel) Group(groupBy string) *arModel {
|
||||
return &arModel{m.M.Group(groupBy)}
|
||||
}
|
||||
|
||||
// Order sets the "ORDER BY" statement for the model.
|
||||
func (m *arModel) Order(orderBy string) *arModel {
|
||||
return &arModel{m.M.Order(orderBy)}
|
||||
}
|
||||
|
||||
// Limit sets the "LIMIT" statement for the model.
|
||||
// The parameter <limit> can be either one or two number, if passed two number is passed,
|
||||
// it then sets "LIMIT limit[0],limit[1]" statement for the model, or else it sets "LIMIT limit[0]"
|
||||
// statement.
|
||||
func (m *arModel) Limit(limit ...int) *arModel {
|
||||
return &arModel{m.M.Limit(limit...)}
|
||||
}
|
||||
|
||||
// Offset sets the "OFFSET" statement for the model.
|
||||
// It only makes sense for some databases like SQLServer, PostgreSQL, etc.
|
||||
func (m *arModel) Offset(offset int) *arModel {
|
||||
return &arModel{m.M.Offset(offset)}
|
||||
}
|
||||
|
||||
// Page sets the paging number for the model.
|
||||
// The parameter <page> is started from 1 for paging.
|
||||
// Note that, it differs that the Limit function start from 0 for "LIMIT" statement.
|
||||
func (m *arModel) Page(page, limit int) *arModel {
|
||||
return &arModel{m.M.Page(page, limit)}
|
||||
}
|
||||
|
||||
// Batch sets the batch operation number for the model.
|
||||
func (m *arModel) Batch(batch int) *arModel {
|
||||
return &arModel{m.M.Batch(batch)}
|
||||
}
|
||||
|
||||
// Cache sets the cache feature for the model. It caches the result of the sql, which means
|
||||
// if there's another same sql request, it just reads and returns the result from cache, it
|
||||
// but not committed and executed into the database.
|
||||
//
|
||||
// If the parameter <duration> < 0, which means it clear the cache with given <name>.
|
||||
// If the parameter <duration> = 0, which means it never expires.
|
||||
// If the parameter <duration> > 0, which means it expires after <duration>.
|
||||
//
|
||||
// The optional parameter <name> is used to bind a name to the cache, which means you can later
|
||||
// control the cache like changing the <duration> or clearing the cache with specified <name>.
|
||||
//
|
||||
// Note that, the cache feature is disabled if the model is operating on a transaction.
|
||||
func (m *arModel) Cache(expire time.Duration, name ...string) *arModel {
|
||||
return &arModel{m.M.Cache(expire, name...)}
|
||||
}
|
||||
|
||||
// Data sets the operation data for the model.
|
||||
// The parameter <data> can be type of string/map/gmap/slice/struct/*struct, etc.
|
||||
// Eg:
|
||||
// Data("uid=10000")
|
||||
// Data("uid", 10000)
|
||||
// Data(g.Map{"uid": 10000, "name":"john"})
|
||||
// Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"})
|
||||
func (m *arModel) Data(data ...interface{}) *arModel {
|
||||
return &arModel{m.M.Data(data...)}
|
||||
}
|
||||
|
||||
// Insert does "INSERT INTO ..." statement for the model.
|
||||
// The optional parameter <data> is the same as the parameter of Model.Data function,
|
||||
// see Model.Data.
|
||||
func (m *arModel) Insert(data ...interface{}) (result sql.Result, err error) {
|
||||
return m.M.Insert(data...)
|
||||
}
|
||||
|
||||
// Replace does "REPLACE INTO ..." statement for the model.
|
||||
// The optional parameter <data> is the same as the parameter of Model.Data function,
|
||||
// see Model.Data.
|
||||
func (m *arModel) Replace(data ...interface{}) (result sql.Result, err error) {
|
||||
return m.M.Replace(data...)
|
||||
}
|
||||
|
||||
// Save does "INSERT INTO ... ON DUPLICATE KEY UPDATE..." statement for the model.
|
||||
// It updates the record if there's primary or unique index in the saving data,
|
||||
// or else it inserts a new record into the table.
|
||||
//
|
||||
// The optional parameter <data> is the same as the parameter of Model.Data function,
|
||||
// see Model.Data.
|
||||
func (m *arModel) Save(data ...interface{}) (result sql.Result, err error) {
|
||||
return m.M.Save(data...)
|
||||
}
|
||||
|
||||
// Update does "UPDATE ... " statement for the model.
|
||||
//
|
||||
// If the optional parameter <dataAndWhere> is given, the dataAndWhere[0] is the updated
|
||||
// data field, and dataAndWhere[1:] is treated as where condition fields.
|
||||
// Also see Model.Data and Model.Where functions.
|
||||
func (m *arModel) Update(dataAndWhere ...interface{}) (result sql.Result, err error) {
|
||||
return m.M.Update(dataAndWhere...)
|
||||
}
|
||||
|
||||
// Delete does "DELETE FROM ... " statement for the model.
|
||||
// The optional parameter <where> is the same as the parameter of Model.Where function,
|
||||
// see Model.Where.
|
||||
func (m *arModel) Delete(where ...interface{}) (result sql.Result, err error) {
|
||||
return m.M.Delete(where...)
|
||||
}
|
||||
|
||||
// Count does "SELECT COUNT(x) FROM ..." statement for the model.
|
||||
// The optional parameter <where> is the same as the parameter of Model.Where function,
|
||||
// see Model.Where.
|
||||
func (m *arModel) Count(where ...interface{}) (int, error) {
|
||||
return m.M.Count(where...)
|
||||
}
|
||||
|
||||
// All does "SELECT FROM ..." statement for the model.
|
||||
// It retrieves the records from table and returns the result as []*Entity.
|
||||
// It returns nil if there's no record retrieved with the given conditions from table.
|
||||
//
|
||||
// The optional parameter <where> is the same as the parameter of Model.Where function,
|
||||
// see Model.Where.
|
||||
func (m *arModel) All(where ...interface{}) ([]*Entity, error) {
|
||||
all, err := m.M.All(where...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var entities []*Entity
|
||||
if err = all.Structs(&entities); err != nil && err != sql.ErrNoRows {
|
||||
return nil, err
|
||||
}
|
||||
return entities, nil
|
||||
}
|
||||
|
||||
// One retrieves one record from table and returns the result as *Entity.
|
||||
// It returns nil if there's no record retrieved with the given conditions from table.
|
||||
//
|
||||
// The optional parameter <where> is the same as the parameter of Model.Where function,
|
||||
// see Model.Where.
|
||||
func (m *arModel) One(where ...interface{}) (*Entity, error) {
|
||||
one, err := m.M.One(where...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var entity *Entity
|
||||
if err = one.Struct(&entity); err != nil && err != sql.ErrNoRows {
|
||||
return nil, err
|
||||
}
|
||||
return entity, nil
|
||||
}
|
||||
|
||||
// Value retrieves a specified record value from table and returns the result as interface type.
|
||||
// It returns nil if there's no record found with the given conditions from table.
|
||||
//
|
||||
// If the optional parameter <fieldsAndWhere> is given, the fieldsAndWhere[0] is the selected fields
|
||||
// and fieldsAndWhere[1:] is treated as where condition fields.
|
||||
// Also see Model.Fields and Model.Where functions.
|
||||
func (m *arModel) Value(fieldsAndWhere ...interface{}) (gdb.Value, error) {
|
||||
return m.M.Value(fieldsAndWhere...)
|
||||
}
|
||||
|
||||
// FindOne retrieves and returns a single Record by Model.WherePri and Model.One.
|
||||
// Also see Model.WherePri and Model.One.
|
||||
func (m *arModel) FindOne(where ...interface{}) (*Entity, error) {
|
||||
one, err := m.M.FindOne(where...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var entity *Entity
|
||||
if err = one.Struct(&entity); err != nil && err != sql.ErrNoRows {
|
||||
return nil, err
|
||||
}
|
||||
return entity, nil
|
||||
}
|
||||
|
||||
// FindAll retrieves and returns Result by by Model.WherePri and Model.All.
|
||||
// Also see Model.WherePri and Model.All.
|
||||
func (m *arModel) FindAll(where ...interface{}) ([]*Entity, error) {
|
||||
all, err := m.M.FindAll(where...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var entities []*Entity
|
||||
if err = all.Structs(&entities); err != nil && err != sql.ErrNoRows {
|
||||
return nil, err
|
||||
}
|
||||
return entities, nil
|
||||
}
|
||||
|
||||
// FindValue retrieves and returns single field value by Model.WherePri and Model.Value.
|
||||
// Also see Model.WherePri and Model.Value.
|
||||
func (m *arModel) FindValue(fieldsAndWhere ...interface{}) (gdb.Value, error) {
|
||||
return m.M.FindValue(fieldsAndWhere...)
|
||||
}
|
||||
|
||||
// FindCount retrieves and returns the record number by Model.WherePri and Model.Count.
|
||||
// Also see Model.WherePri and Model.Count.
|
||||
func (m *arModel) FindCount(where ...interface{}) (int, error) {
|
||||
return m.M.FindCount(where...)
|
||||
}
|
||||
|
||||
// Chunk iterates the table with given size and callback function.
|
||||
func (m *arModel) Chunk(limit int, callback func(entities []*Entity, err error) bool) {
|
||||
m.M.Chunk(limit, func(result gdb.Result, err error) bool {
|
||||
var entities []*Entity
|
||||
err = result.Structs(&entities)
|
||||
if err == sql.ErrNoRows {
|
||||
return false
|
||||
}
|
||||
return callback(entities, err)
|
||||
})
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
{
|
||||
"viewpath" : "/home/www/templates/",
|
||||
"database" : {
|
||||
"default" : [
|
||||
{
|
||||
"host" : "127.0.0.1",
|
||||
"port" : "3306",
|
||||
"user" : "root",
|
||||
"pass" : "123456",
|
||||
"name" : "test",
|
||||
"type" : "mysql",
|
||||
"role" : "master",
|
||||
"charset" : "utf8",
|
||||
"priority" : "1"
|
||||
},
|
||||
{
|
||||
"host" : "127.0.0.1",
|
||||
"port" : "3306",
|
||||
"user" : "root",
|
||||
"pass" : "123456",
|
||||
"name" : "test",
|
||||
"type" : "mysql",
|
||||
"role" : "master",
|
||||
"charset" : "utf8",
|
||||
"priority" : "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"redis" : {
|
||||
"disk" : "127.0.0.1:6379,0",
|
||||
"cache" : "127.0.0.1:6379,1"
|
||||
}
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
viewpath = "/home/www/templates"
|
||||
|
||||
# MySQL数据库配置
|
||||
[database]
|
||||
debug = true
|
||||
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/gf"
|
||||
|
||||
|
||||
[redis]
|
||||
disk = "127.0.0.1:6379,0"
|
||||
cache = "127.0.0.1:6379,1"
|
||||
@ -1,35 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<config>
|
||||
<!-- 模板引擎目录 -->
|
||||
<viewpath>/home/www/templates/</viewpath>
|
||||
<!-- MySQL数据库配置 -->
|
||||
<database>
|
||||
<default>
|
||||
<host>127.0.0.1</host>
|
||||
<port>3306</port>
|
||||
<user>root</user>
|
||||
<pass>123456</pass>
|
||||
<name>test</name>
|
||||
<role>master</role>
|
||||
<type>mysql</type>
|
||||
<charset>utf8</charset>
|
||||
<priority>1</priority>
|
||||
</default>
|
||||
<default>
|
||||
<host>127.0.0.1</host>
|
||||
<port>3306</port>
|
||||
<user>root</user>
|
||||
<pass>123456</pass>
|
||||
<name>test</name>
|
||||
<role>master</role>
|
||||
<type>mysql</type>
|
||||
<charset>utf8</charset>
|
||||
<priority>1</priority>
|
||||
</default>
|
||||
</database>
|
||||
<!-- Redis数据库配置 -->
|
||||
<redis>
|
||||
<disk>127.0.0.1:6379,0</disk>
|
||||
<cache>127.0.0.1:6379,1</cache>
|
||||
</redis>
|
||||
</config>
|
||||
@ -1,27 +0,0 @@
|
||||
# 模板引擎目录
|
||||
viewpath: /home/www/templates/
|
||||
# MySQL数据库配置
|
||||
database:
|
||||
default:
|
||||
- host: 127.0.0.1
|
||||
port: 3306
|
||||
user: root
|
||||
pass: "8692651"
|
||||
name: test
|
||||
type: mysql
|
||||
role: master
|
||||
charset: utf8
|
||||
priority: 1
|
||||
- host: 127.0.0.1
|
||||
port: 3306
|
||||
user: root
|
||||
pass: "8692651"
|
||||
name: test
|
||||
type: mysql
|
||||
role: master
|
||||
charset: utf8
|
||||
priority: 1
|
||||
# Redis数据库配置
|
||||
redis:
|
||||
default: 127.0.0.1:6379,0
|
||||
cache : 127.0.0.1:6379,2
|
||||
@ -1,25 +0,0 @@
|
||||
package demo
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
func init() {
|
||||
s := g.Server()
|
||||
s.BindHandler("/apple", Apple)
|
||||
s.BindHandler("/pen", Pen)
|
||||
s.BindHandler("/apple-pen", ApplePen)
|
||||
}
|
||||
|
||||
func Apple(r *ghttp.Request) {
|
||||
r.Response.Write("Apple")
|
||||
}
|
||||
|
||||
func Pen(r *ghttp.Request) {
|
||||
r.Response.Write("Pen")
|
||||
}
|
||||
|
||||
func ApplePen(r *ghttp.Request) {
|
||||
r.Response.Write("Apple-Pen")
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
package demo
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
type Order struct{}
|
||||
|
||||
func init() {
|
||||
g.Server().BindObject("/{.struct}-{.method}", new(Order))
|
||||
}
|
||||
|
||||
func (o *Order) List(r *ghttp.Request) {
|
||||
r.Response.Write("List")
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
package demo
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/gins"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
func init() {
|
||||
ghttp.GetServer().BindHandler("/config", func(r *ghttp.Request) {
|
||||
r.Response.Write(gins.Config().GetString("database.default.0.host"))
|
||||
})
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
package demo
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
)
|
||||
|
||||
func init() {
|
||||
ghttp.GetServer().BindHandler("/cookie", Cookie)
|
||||
}
|
||||
|
||||
func Cookie(r *ghttp.Request) {
|
||||
datetime := r.Cookie.Get("datetime")
|
||||
r.Cookie.Set("datetime", gtime.Datetime())
|
||||
r.Response.Write("datetime:" + datetime)
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
package demo
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
type ControllerDomain struct{}
|
||||
|
||||
// 初始化控制器对象,并绑定操作到Web Server
|
||||
func init() {
|
||||
// 只有localhost域名下才能访问该对象,
|
||||
// 对应URL为:http://localhost:8199/test/show
|
||||
// 通过该地址将无法访问到内容:http://127.0.0.1:8199/test/show
|
||||
ghttp.GetServer().Domain("localhost").BindObject("/domain", &ControllerDomain{})
|
||||
}
|
||||
|
||||
// 用于对象映射
|
||||
func (d *ControllerDomain) Show(r *ghttp.Request) {
|
||||
r.Response.Write("It's show time bibi!")
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
package demo
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/gmvc"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
type ControllerExit struct {
|
||||
gmvc.Controller
|
||||
}
|
||||
|
||||
func (c *ControllerExit) Init(r *ghttp.Request) {
|
||||
c.Controller.Init(r)
|
||||
c.Response.Write("exit, it will not print \"show\"")
|
||||
c.Request.Exit()
|
||||
}
|
||||
|
||||
func (c *ControllerExit) Show() {
|
||||
c.Response.Write("show")
|
||||
}
|
||||
|
||||
func init() {
|
||||
ghttp.GetServer().BindController("/exit", &ControllerExit{})
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
package demo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
func Form(r *ghttp.Request) {
|
||||
fmt.Println(r.GetPostMap())
|
||||
fmt.Println(r.GetPostString("name"))
|
||||
fmt.Println(r.GetPostString("age"))
|
||||
|
||||
}
|
||||
|
||||
func FormShow(r *ghttp.Request) {
|
||||
r.Response.Write(`
|
||||
<html>
|
||||
<head>
|
||||
<title>表单提交</title>
|
||||
</head>
|
||||
<body>
|
||||
<form enctype="application/x-www-form-urlencoded" action="/form" method="post">
|
||||
<input type="input" name="name" />
|
||||
<input type="input" name="age" />
|
||||
<input type="submit" value="submit" />
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
`)
|
||||
}
|
||||
|
||||
func init() {
|
||||
ghttp.GetServer().BindHandler("/form", Form)
|
||||
ghttp.GetServer().BindHandler("/form/show", FormShow)
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
package demo
|
||||
|
||||
import "github.com/gogf/gf/net/ghttp"
|
||||
|
||||
func init() {
|
||||
ghttp.GetServer().BindHandler("/", func(r *ghttp.Request) {
|
||||
r.Response.Write("Hello World!")
|
||||
})
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
package demo
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/frame/gmvc"
|
||||
)
|
||||
|
||||
type Method struct {
|
||||
gmvc.Controller
|
||||
}
|
||||
|
||||
func init() {
|
||||
// 第三个参数指定主要注册的方法,其他方法不注册,方法名称会自动追加到给定路由后面,构成新路由
|
||||
// 以下注册会中注册两个新路由: /method/name, /method/age
|
||||
g.Server().BindController("/method", new(Method), "Name, Age")
|
||||
// 绑定路由到指定的方法执行,以下注册只会注册一个路由: /method-name
|
||||
g.Server().BindControllerMethod("/method-name", new(Method), "Name")
|
||||
}
|
||||
|
||||
func (c *Method) Name() {
|
||||
c.Response.Write("John")
|
||||
}
|
||||
|
||||
func (c *Method) Age() {
|
||||
c.Response.Write("18")
|
||||
}
|
||||
|
||||
func (c *Method) Info() {
|
||||
c.Response.Write("Info")
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
package demo
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
type Object struct{}
|
||||
|
||||
func init() {
|
||||
g.Server().BindObject("/object", new(Object))
|
||||
}
|
||||
|
||||
func (o *Object) Index(r *ghttp.Request) {
|
||||
r.Response.Write("object index")
|
||||
}
|
||||
|
||||
func (o *Object) Show(r *ghttp.Request) {
|
||||
r.Response.Write("object show")
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
package demo
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
type ObjectMethod struct{}
|
||||
|
||||
func init() {
|
||||
obj := &ObjectMethod{}
|
||||
g.Server().BindObject("/object-method", obj, "Show1, Show2, Show3")
|
||||
g.Server().BindObjectMethod("/object-method-show1", obj, "Show1")
|
||||
g.Server().Domain("localhost").BindObject("/object-method", obj, "Show4")
|
||||
}
|
||||
|
||||
func (o *ObjectMethod) Show1(r *ghttp.Request) {
|
||||
r.Response.Write("show 1")
|
||||
}
|
||||
|
||||
func (o *ObjectMethod) Show2(r *ghttp.Request) {
|
||||
r.Response.Write("show 2")
|
||||
}
|
||||
|
||||
func (o *ObjectMethod) Show3(r *ghttp.Request) {
|
||||
r.Response.Write("show 3")
|
||||
}
|
||||
|
||||
func (o *ObjectMethod) Show4(r *ghttp.Request) {
|
||||
r.Response.Write("show 4")
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
package demo
|
||||
|
||||
import "github.com/gogf/gf/net/ghttp"
|
||||
|
||||
// 测试绑定对象
|
||||
type ObjectRest struct{}
|
||||
|
||||
func init() {
|
||||
ghttp.GetServer().BindObjectRest("/object-rest", &ObjectRest{})
|
||||
}
|
||||
|
||||
// RESTFul - GET
|
||||
func (o *ObjectRest) Get(r *ghttp.Request) {
|
||||
r.Response.Write("RESTFul HTTP Method GET")
|
||||
}
|
||||
|
||||
// RESTFul - POST
|
||||
func (c *ObjectRest) Post(r *ghttp.Request) {
|
||||
r.Response.Write("RESTFul HTTP Method POST")
|
||||
}
|
||||
|
||||
// RESTFul - DELETE
|
||||
func (c *ObjectRest) Delete(r *ghttp.Request) {
|
||||
r.Response.Write("RESTFul HTTP Method DELETE")
|
||||
}
|
||||
|
||||
// 该方法无法映射,将会无法访问到
|
||||
func (c *ObjectRest) Hello(r *ghttp.Request) {
|
||||
r.Response.Write("Hello")
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
package demo
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
type Product struct {
|
||||
total int
|
||||
}
|
||||
|
||||
func init() {
|
||||
p := &Product{}
|
||||
g.Server().BindHandler("/product/total", p.Total)
|
||||
g.Server().BindHandler("/product/list/{page}.html", p.List)
|
||||
}
|
||||
|
||||
func (p *Product) Total(r *ghttp.Request) {
|
||||
p.total++
|
||||
r.Response.Write("total: ", p.total)
|
||||
}
|
||||
|
||||
func (p *Product) List(r *ghttp.Request) {
|
||||
r.Response.Write("page: ", r.Get("page"))
|
||||
}
|
||||
@ -1,34 +0,0 @@
|
||||
package demo
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/frame/gmvc"
|
||||
)
|
||||
|
||||
type Rest struct {
|
||||
gmvc.Controller
|
||||
}
|
||||
|
||||
func init() {
|
||||
g.Server().BindControllerRest("/rest", &Rest{})
|
||||
}
|
||||
|
||||
// RESTFul - GET
|
||||
func (c *Rest) Get() {
|
||||
c.Response.Write("RESTFul HTTP Method GET")
|
||||
}
|
||||
|
||||
// RESTFul - POST
|
||||
func (c *Rest) Post() {
|
||||
c.Response.Write("RESTFul HTTP Method POST")
|
||||
}
|
||||
|
||||
// RESTFul - DELETE
|
||||
func (c *Rest) Delete() {
|
||||
c.Response.Write("RESTFul HTTP Method DELETE")
|
||||
}
|
||||
|
||||
// 该方法无法映射,将会无法访问到
|
||||
func (c *Rest) Hello() {
|
||||
c.Response.Write("Hello")
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
package demo
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/frame/gmvc"
|
||||
)
|
||||
|
||||
type ControllerRule struct {
|
||||
gmvc.Controller
|
||||
}
|
||||
|
||||
func init() {
|
||||
g.Server().BindController("/rule/{method}/:name", &ControllerRule{})
|
||||
}
|
||||
|
||||
func (c *ControllerRule) Show() {
|
||||
c.Response.Write(c.Request.Get("name"))
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
package demo
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
func init() {
|
||||
ghttp.GetServer().BindHandler("/session", Session)
|
||||
}
|
||||
|
||||
func Session(r *ghttp.Request) {
|
||||
id := r.Session.GetInt("id")
|
||||
r.Session.Set("id", id+1)
|
||||
r.Response.Write("id:" + strconv.Itoa(id))
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
package demo
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/gmvc"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
type ControllerTemplate struct {
|
||||
gmvc.Controller
|
||||
}
|
||||
|
||||
func (c *ControllerTemplate) Info() {
|
||||
c.View.Assign("name", "john")
|
||||
c.View.Assigns(map[string]interface{}{
|
||||
"age": 18,
|
||||
"score": 100,
|
||||
})
|
||||
c.View.Display("view/user/index.tpl")
|
||||
}
|
||||
|
||||
func init() {
|
||||
ghttp.GetServer().BindController("/template", &ControllerTemplate{})
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
package demo
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
func init() {
|
||||
ghttp.GetServer().BindHandler("/template2", func(r *ghttp.Request) {
|
||||
content, _ := g.View().Parse("index.tpl", map[string]interface{}{
|
||||
"id": 123,
|
||||
"name": "john",
|
||||
})
|
||||
r.Response.Write(content)
|
||||
})
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
package demo
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/gins"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
func init() {
|
||||
gins.View().SetPath("/home/www/template/")
|
||||
ghttp.GetServer().BindHandler("/template3", func(r *ghttp.Request) {
|
||||
content, _ := gins.View().Parse("index.tpl", map[string]interface{}{
|
||||
"id": 123,
|
||||
"name": "john",
|
||||
})
|
||||
r.Response.Write(content)
|
||||
})
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
package demo
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/gfile"
|
||||
)
|
||||
|
||||
func Upload(r *ghttp.Request) {
|
||||
if f, h, e := r.FormFile("upload-file"); e == nil {
|
||||
defer f.Close()
|
||||
fname := gfile.Basename(h.Filename)
|
||||
buffer := make([]byte, h.Size)
|
||||
f.Read(buffer)
|
||||
gfile.PutBytes("/tmp/"+fname, buffer)
|
||||
r.Response.Write(fname + " uploaded successly")
|
||||
} else {
|
||||
r.Response.Write(e.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func UploadShow(r *ghttp.Request) {
|
||||
r.Response.Write(`
|
||||
<html>
|
||||
<head>
|
||||
<title>上传文件</title>
|
||||
</head>
|
||||
<body>
|
||||
<form enctype="multipart/form-data" action="/upload" method="post">
|
||||
<input type="file" name="upload-file" />
|
||||
<input type="submit" value="upload" />
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
`)
|
||||
}
|
||||
|
||||
func init() {
|
||||
ghttp.GetServer().BindHandler("/upload", Upload)
|
||||
ghttp.GetServer().BindHandler("/upload/show", UploadShow)
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
package demo
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/frame/gmvc"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
gmvc.Controller
|
||||
}
|
||||
|
||||
func init() {
|
||||
s := g.Server()
|
||||
s.BindController("/user", new(User))
|
||||
s.BindController("/user/{.method}/{uid}", new(User), "Info")
|
||||
s.BindController("/user/{.method}/{page}.html", new(User), "List")
|
||||
}
|
||||
|
||||
func (u *User) Index() {
|
||||
u.Response.Write("User")
|
||||
}
|
||||
|
||||
func (u *User) Info() {
|
||||
u.Response.Write("Info - Uid: ", u.Request.Get("uid"))
|
||||
}
|
||||
|
||||
func (u *User) List() {
|
||||
u.Response.Write("List - Page: ", u.Request.Get("page"))
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
var (
|
||||
total int
|
||||
)
|
||||
|
||||
func init() {
|
||||
g.Server().BindHandler("/stats/total", showTotal)
|
||||
}
|
||||
|
||||
func showTotal(r *ghttp.Request) {
|
||||
total++
|
||||
r.Response.Write("total:", total)
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/.example/frame/mvc/app/model/article"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m := article.Model
|
||||
m.All()
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>上传文件</title>
|
||||
</head>
|
||||
<body>
|
||||
<form enctype="multipart/form-data" action="/upload" method="post">
|
||||
<input type="file" name="upload-file" />
|
||||
<input type="submit" value="upload" />
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,2 +0,0 @@
|
||||
<h3>This is footer</h3>
|
||||
<div style="color:red">tpl vals: {{.}}</div>
|
||||
@ -1,11 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h3>This is index</h3>
|
||||
<p>tpl vals: {{.}}</p>
|
||||
{{include "user/footer.tpl" }}
|
||||
</body>
|
||||
</html>
|
||||
@ -3,34 +3,16 @@ package main
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/gfile"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Upload uploads files to /tmp .
|
||||
func Upload(r *ghttp.Request) {
|
||||
saveDir := "/tmp/"
|
||||
for _, item := range r.GetMultipartFiles("upload-file") {
|
||||
file, err := item.Open()
|
||||
if err != nil {
|
||||
r.Response.Write(err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
f, err := gfile.Create(saveDir + gfile.Basename(item.Filename))
|
||||
if err != nil {
|
||||
r.Response.Write(err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if _, err := io.Copy(f, file); err != nil {
|
||||
r.Response.Write(err)
|
||||
return
|
||||
}
|
||||
saveDirPath := "/tmp/"
|
||||
files := r.GetUploadFiles("upload-file")
|
||||
if _, err := files.Save(saveDirPath); err != nil {
|
||||
r.Response.WriteExit(err)
|
||||
}
|
||||
r.Response.Write("upload successfully")
|
||||
r.Response.WriteExit("upload successfully")
|
||||
}
|
||||
|
||||
// UploadShow shows uploading simgle file page.
|
||||
@ -71,7 +53,7 @@ func UploadShowBatch(r *ghttp.Request) {
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.Group("/upload", func(group *ghttp.RouterGroup) {
|
||||
group.ALL("/", Upload)
|
||||
group.POST("/", Upload)
|
||||
group.ALL("/show", UploadShow)
|
||||
group.ALL("/batch", UploadShowBatch)
|
||||
})
|
||||
|
||||
@ -2,12 +2,34 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/glog"
|
||||
)
|
||||
|
||||
func SendXmlFile(gameId int, areaName string, filePath string) error {
|
||||
path := filepath.FromSlash(filePath)
|
||||
fmt.Println(path)
|
||||
data := g.Map{
|
||||
"gameName": gameId,
|
||||
"area": areaName,
|
||||
"file": "@file:" + path,
|
||||
"contentType": "json",
|
||||
}
|
||||
if r, err := ghttp.Post("http://127.0.0.1:8199/upload", data); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
defer r.Close()
|
||||
fmt.Println("ok")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
SendXmlFile(1, "xxx", "/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/net/ghttp/server/session.go")
|
||||
return
|
||||
path := "/home/john/Workspace/Go/github.com/gogf/gf/version.go"
|
||||
r, e := ghttp.Post("http://127.0.0.1:8199/upload", "upload-file=@file:"+path)
|
||||
if e != nil {
|
||||
|
||||
@ -3,34 +3,16 @@ package main
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/gfile"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Upload uploads files to /tmp .
|
||||
func Upload(r *ghttp.Request) {
|
||||
saveDir := "/tmp/"
|
||||
for _, item := range r.GetMultipartFiles("upload-file") {
|
||||
file, err := item.Open()
|
||||
if err != nil {
|
||||
r.Response.Write(err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
f, err := gfile.Create(saveDir + gfile.Basename(item.Filename))
|
||||
if err != nil {
|
||||
r.Response.Write(err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if _, err := io.Copy(f, file); err != nil {
|
||||
r.Response.Write(err)
|
||||
return
|
||||
}
|
||||
saveDirPath := "/tmp/"
|
||||
files := r.GetUploadFiles("file")
|
||||
if _, err := files.Save(saveDirPath); err != nil {
|
||||
r.Response.WriteExit(err)
|
||||
}
|
||||
r.Response.Write("upload successfully")
|
||||
r.Response.WriteExit("upload successfully")
|
||||
}
|
||||
|
||||
// UploadShow shows uploading simgle file page.
|
||||
@ -71,7 +53,7 @@ func UploadShowBatch(r *ghttp.Request) {
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.Group("/upload", func(group *ghttp.RouterGroup) {
|
||||
group.ALL("/", Upload)
|
||||
group.POST("/", Upload)
|
||||
group.ALL("/show", UploadShow)
|
||||
group.ALL("/batch", UploadShowBatch)
|
||||
})
|
||||
|
||||
22
.example/net/ghttp/server/body.go
Normal file
22
.example/net/ghttp/server/body.go
Normal file
@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.SetIndexFolder(true)
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
body1 := r.GetBody()
|
||||
body2, _ := ioutil.ReadAll(r.Body)
|
||||
fmt.Println(body1)
|
||||
fmt.Println(body2)
|
||||
r.Response.Write("hello world")
|
||||
})
|
||||
s.SetPort(8999)
|
||||
s.Run()
|
||||
}
|
||||
@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/glog"
|
||||
)
|
||||
|
||||
func MiddlewareCORS(r *ghttp.Request) {
|
||||
@ -11,6 +12,7 @@ func MiddlewareCORS(r *ghttp.Request) {
|
||||
}
|
||||
|
||||
func Order(r *ghttp.Request) {
|
||||
glog.Println("order")
|
||||
r.Response.Write("GET")
|
||||
}
|
||||
|
||||
@ -18,7 +20,7 @@ func main() {
|
||||
s := g.Server()
|
||||
s.Group("/api.v1", func(group *ghttp.RouterGroup) {
|
||||
group.Middleware(MiddlewareCORS)
|
||||
g.GET("/order", Order)
|
||||
group.GET("/order", Order)
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
|
||||
@ -20,7 +20,7 @@ func main() {
|
||||
s := g.Server()
|
||||
s.Group("/api.v1", func(group *ghttp.RouterGroup) {
|
||||
group.Middleware(MiddlewareCORS)
|
||||
g.GET("/order", Order)
|
||||
group.GET("/order", Order)
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
|
||||
@ -26,7 +26,7 @@ func main() {
|
||||
s := g.Server()
|
||||
s.Group("/api.v1", func(group *ghttp.RouterGroup) {
|
||||
group.Middleware(MiddlewareCORS)
|
||||
g.GET("/order", Order)
|
||||
group.GET("/order", Order)
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
|
||||
@ -19,7 +19,7 @@ func MiddlewareAuth(r *ghttp.Request) {
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.Group("/admin", func(group *ghttp.RouterGroup) {
|
||||
g.MiddlewarePattern("/*action", func(r *ghttp.Request) {
|
||||
group.Middleware(func(r *ghttp.Request) {
|
||||
if action := r.GetRouterString("action"); action != "" {
|
||||
switch action {
|
||||
case "login":
|
||||
|
||||
@ -25,14 +25,12 @@ func MiddlewareCORS(r *ghttp.Request) {
|
||||
|
||||
func MiddlewareLog(r *ghttp.Request) {
|
||||
r.Middleware.Next()
|
||||
glog.Println(r.Response.Status, r.URL.Path)
|
||||
g.Log().Println(r.Response.Status, r.URL.Path)
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.Middleware(MiddlewareLog)
|
||||
})
|
||||
s.Use(MiddlewareLog)
|
||||
s.Group("/api.v2", func(group *ghttp.RouterGroup) {
|
||||
group.Middleware(MiddlewareAuth, MiddlewareCORS)
|
||||
group.ALL("/user/list", func(r *ghttp.Request) {
|
||||
|
||||
@ -13,20 +13,20 @@ func main() {
|
||||
r.Middleware.Next()
|
||||
r.Response.Write("end")
|
||||
})
|
||||
g.Group("/order", func(group *ghttp.RouterGroup) {
|
||||
g.GET("/list", func(r *ghttp.Request) {
|
||||
group.Group("/order", func(group *ghttp.RouterGroup) {
|
||||
group.GET("/list", func(r *ghttp.Request) {
|
||||
r.Response.Write("list")
|
||||
})
|
||||
})
|
||||
g.Group("/user", func(group *ghttp.RouterGroup) {
|
||||
g.GET("/info", func(r *ghttp.Request) {
|
||||
group.Group("/user", func(group *ghttp.RouterGroup) {
|
||||
group.GET("/info", func(r *ghttp.Request) {
|
||||
r.Response.Write("info")
|
||||
})
|
||||
g.POST("/edit", func(r *ghttp.Request) {
|
||||
group.POST("/edit", func(r *ghttp.Request) {
|
||||
r.Response.Write("edit")
|
||||
})
|
||||
})
|
||||
g.Group("/hook", func(group *ghttp.RouterGroup) {
|
||||
group.Group("/hook", func(group *ghttp.RouterGroup) {
|
||||
group.Hook("/*", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
|
||||
r.Response.Write("hook any")
|
||||
})
|
||||
|
||||
@ -5,21 +5,18 @@ import (
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
}
|
||||
type User struct{}
|
||||
|
||||
func (c *User) Index(r *ghttp.Request) {
|
||||
r.Response.Write("Index")
|
||||
}
|
||||
|
||||
// 不符合规范,不会被注册
|
||||
func (c *User) Test(r *ghttp.Request, value interface{}) {
|
||||
func (c *User) Test(r *ghttp.Request) {
|
||||
r.Response.Write("Test")
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindObject("/user", new(User))
|
||||
u := new(User)
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.GET("/db-{table}/{id}", u, "Test")
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
|
||||
@ -7,11 +7,6 @@ import (
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.EnableAdmin()
|
||||
//s.BindHookHandler("/admin/*any", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
|
||||
// if !r.BasicAuth("admin", "123", "") {
|
||||
// r.Exit()
|
||||
// }
|
||||
//})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
|
||||
@ -9,14 +9,14 @@ import (
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
g.GET("/set", func(r *ghttp.Request) {
|
||||
r.Session.Set("time", gtime.Second())
|
||||
group.GET("/set", func(r *ghttp.Request) {
|
||||
r.Session.Set("time", gtime.Timestamp())
|
||||
r.Response.Write("ok")
|
||||
})
|
||||
g.GET("/get", func(r *ghttp.Request) {
|
||||
group.GET("/get", func(r *ghttp.Request) {
|
||||
r.Response.WriteJson(r.Session.Map())
|
||||
})
|
||||
g.GET("/clear", func(r *ghttp.Request) {
|
||||
group.GET("/clear", func(r *ghttp.Request) {
|
||||
r.Session.Clear()
|
||||
})
|
||||
})
|
||||
29
.example/net/ghttp/server/session/redis/redis.go
Normal file
29
.example/net/ghttp/server/session/redis/redis.go
Normal file
@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/gsession"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.SetSessionMaxAge(2 * time.Minute)
|
||||
s.SetSessionStorage(gsession.NewStorageRedis(g.Redis()))
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.GET("/set", func(r *ghttp.Request) {
|
||||
r.Session.Set("time", gtime.Timestamp())
|
||||
r.Response.Write("ok")
|
||||
})
|
||||
group.GET("/get", func(r *ghttp.Request) {
|
||||
r.Response.WriteJson(r.Session.Map())
|
||||
})
|
||||
group.GET("/clear", func(r *ghttp.Request) {
|
||||
r.Session.Clear()
|
||||
})
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
17
.example/net/ghttp/server/template/config/config.go
Normal file
17
.example/net/ghttp/server/template/config/config.go
Normal file
@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
r.Response.WriteTplContent(`${.name}`, g.Map{
|
||||
"name": "john",
|
||||
})
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
2
.example/net/ghttp/server/template/config/config.toml
Normal file
2
.example/net/ghttp/server/template/config/config.toml
Normal file
@ -0,0 +1,2 @@
|
||||
[viewer]
|
||||
delimiters = ["${", "}"]
|
||||
@ -8,7 +8,6 @@ import (
|
||||
func main() {
|
||||
s := ghttp.GetServer()
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
r.Response.Write("Hello World")
|
||||
r.Response.WriteTpl("index.tpl", g.Map{
|
||||
"title": "Test",
|
||||
"name": "John",
|
||||
|
||||
@ -27,7 +27,7 @@ func main() {
|
||||
},
|
||||
MemUsed: 15560320,
|
||||
MemTotal: 16333788,
|
||||
Time: int(gtime.Second()),
|
||||
Time: int(gtime.Timestamp()),
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
||||
@ -11,7 +11,7 @@ import (
|
||||
func main() {
|
||||
for {
|
||||
time.Sleep(time.Second)
|
||||
if f, err := gfpool.Open("/home/john/temp/log.log", os.O_RDONLY, 0666, 60000000*1000); err == nil {
|
||||
if f, err := gfpool.Open("/home/john/temp/log.log", os.O_RDONLY, 0666, time.Hour); err == nil {
|
||||
fmt.Println(f.Name())
|
||||
f.Close()
|
||||
} else {
|
||||
|
||||
14
.example/os/glog/glog_ctx.go
Normal file
14
.example/os/glog/glog_ctx.go
Normal file
@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/os/glog"
|
||||
)
|
||||
|
||||
func main() {
|
||||
glog.SetCtxKeys("Trace-Id", "Span-Id", "Test")
|
||||
ctx := context.WithValue(context.Background(), "Trace-Id", "1234567890")
|
||||
ctx = context.WithValue(ctx, "Span-Id", "abcdefg")
|
||||
|
||||
glog.Ctx(ctx).Print(1, 2, 3)
|
||||
}
|
||||
11
.example/os/glog/glog_level_prefix.go
Normal file
11
.example/os/glog/glog_level_prefix.go
Normal file
@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/os/glog"
|
||||
)
|
||||
|
||||
func main() {
|
||||
l := glog.New()
|
||||
l.SetLevelPrefix(glog.LEVEL_DEBU, "debug")
|
||||
l.Debug("test")
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/os/gres"
|
||||
_ "github.com/gogf/gf/os/gres/testdata"
|
||||
)
|
||||
|
||||
func main() {
|
||||
gres.Dump()
|
||||
//file := gres.Get("www")
|
||||
//fmt.Println(file.Open())
|
||||
//g.Dump(gres.ScanDir("/root/image", "*"))
|
||||
//g.Dump(gres.Scan("/root/image/", "*", true))
|
||||
//g.Dump(gres.Scan("/template", "*"))
|
||||
//g.Dump(gres.Scan("/template/layout2", "*.html", true))
|
||||
}
|
||||
20
.example/os/gres/gres_example.go
Normal file
20
.example/os/gres/gres_example.go
Normal file
@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "github.com/gogf/gf/os/gres/testdata/example/boot"
|
||||
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.GET("/template", func(r *ghttp.Request) {
|
||||
r.Response.WriteTplDefault(g.Map{
|
||||
"name": "GoFrame",
|
||||
})
|
||||
})
|
||||
})
|
||||
s.Run()
|
||||
}
|
||||
@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := gtime.Millisecond()
|
||||
start := gtime.TimestampMilli()
|
||||
wg := sync.WaitGroup{}
|
||||
for i := 0; i < 100000; i++ {
|
||||
wg.Add(1)
|
||||
@ -19,5 +19,5 @@ func main() {
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
fmt.Println("time spent:", gtime.Millisecond()-start)
|
||||
fmt.Println("time spent:", gtime.TimestampMilli()-start)
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := gtime.Millisecond()
|
||||
start := gtime.TimestampMilli()
|
||||
wg := sync.WaitGroup{}
|
||||
for i := 0; i < 100000; i++ {
|
||||
wg.Add(1)
|
||||
@ -21,5 +21,5 @@ func main() {
|
||||
}
|
||||
wg.Wait()
|
||||
fmt.Println(grpool.Size())
|
||||
fmt.Println("time spent:", gtime.Millisecond()-start)
|
||||
fmt.Println("time spent:", gtime.TimestampMilli()-start)
|
||||
}
|
||||
|
||||
@ -9,8 +9,8 @@ import (
|
||||
func main() {
|
||||
fmt.Println("Date :", gtime.Date())
|
||||
fmt.Println("Datetime :", gtime.Datetime())
|
||||
fmt.Println("Second :", gtime.Second())
|
||||
fmt.Println("Millisecond:", gtime.Millisecond())
|
||||
fmt.Println("Microsecond:", gtime.Microsecond())
|
||||
fmt.Println("Nanosecond :", gtime.Nanosecond())
|
||||
fmt.Println("Second :", gtime.Timestamp())
|
||||
fmt.Println("Millisecond:", gtime.TimestampMilli())
|
||||
fmt.Println("Microsecond:", gtime.TimestampMicro())
|
||||
fmt.Println("Nanosecond :", gtime.TimestampNano())
|
||||
}
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
|
||||
[database]
|
||||
debug = true
|
||||
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
|
||||
[redis]
|
||||
default = "127.0.0.1:6379,0"
|
||||
cache = "127.0.0.1:6379,1"
|
||||
@ -1,27 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type Base struct {
|
||||
Id int `c:"id"`
|
||||
CreateTime string `c:"create_time"`
|
||||
}
|
||||
type User struct {
|
||||
Base `c:"base"`
|
||||
Passport string `c:"passport"`
|
||||
Password string `c:"password"`
|
||||
Nickname string `c:"nickname"`
|
||||
}
|
||||
user := new(User)
|
||||
user.Id = 1
|
||||
user.Nickname = "John"
|
||||
user.Passport = "johng"
|
||||
user.Password = "123456"
|
||||
user.CreateTime = "2019"
|
||||
fmt.Println(gconv.Map(user))
|
||||
fmt.Println(gconv.MapDeep(user))
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
r := ghttp.PostContent("http://127.0.0.1:8199/test", `<doc><id>1</id><name>john</name><password1>123Abc!@#</password1><password2>123Abc!@#</password2></doc>`)
|
||||
fmt.Println(r)
|
||||
}
|
||||
@ -4,13 +4,12 @@ import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/gview"
|
||||
"github.com/gogf/gf/util/gpage"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := ghttp.GetServer()
|
||||
s := g.Server()
|
||||
s.BindHandler("/page/demo", func(r *ghttp.Request) {
|
||||
page := gpage.New(100, 10, r.Get("page"), r.URL.String())
|
||||
page := r.GetPage(100, 10)
|
||||
buffer, _ := gview.ParseContent(`
|
||||
<html>
|
||||
<head>
|
||||
|
||||
@ -4,14 +4,13 @@ import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/gview"
|
||||
"github.com/gogf/gf/util/gpage"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := ghttp.GetServer()
|
||||
s := g.Server()
|
||||
s.BindHandler("/page/ajax", func(r *ghttp.Request) {
|
||||
page := gpage.New(100, 10, r.Get("page"), r.URL.String(), r.Router)
|
||||
page.EnableAjax("DoAjax")
|
||||
page := r.GetPage(100, 10)
|
||||
page.AjaxActionName = "DoAjax"
|
||||
buffer, _ := gview.ParseContent(`
|
||||
<html>
|
||||
<head>
|
||||
@ -29,11 +28,17 @@ func main() {
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div>{{.page}}</div>
|
||||
<div>{{.page1}}</div>
|
||||
<div>{{.page2}}</div>
|
||||
<div>{{.page3}}</div>
|
||||
<div>{{.page4}}</div>
|
||||
</body>
|
||||
</html>
|
||||
`, g.Map{
|
||||
"page": page.GetContent(1),
|
||||
"page1": page.GetContent(1),
|
||||
"page2": page.GetContent(2),
|
||||
"page3": page.GetContent(3),
|
||||
"page4": page.GetContent(4),
|
||||
})
|
||||
r.Response.Write(buffer)
|
||||
})
|
||||
|
||||
@ -8,7 +8,7 @@ import (
|
||||
"github.com/gogf/gf/util/gpage"
|
||||
)
|
||||
|
||||
// 分页标签使用li标签包裹
|
||||
// wrapContent wraps each of the page tag with html li and ul.
|
||||
func wrapContent(page *gpage.Page) string {
|
||||
content := page.GetContent(4)
|
||||
content = gstr.ReplaceByMap(content, map[string]string{
|
||||
@ -21,9 +21,9 @@ func wrapContent(page *gpage.Page) string {
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := ghttp.GetServer()
|
||||
s := g.Server()
|
||||
s.BindHandler("/page/custom1/*page", func(r *ghttp.Request) {
|
||||
page := gpage.New(100, 10, r.Get("page"), r.URL.String(), r.Router)
|
||||
page := r.GetPage(100, 10)
|
||||
content := wrapContent(page)
|
||||
buffer, _ := gview.ParseContent(`
|
||||
<html>
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
"github.com/gogf/gf/util/gpage"
|
||||
)
|
||||
|
||||
// 自定义分页名称
|
||||
// pageContent customizes the page tag name.
|
||||
func pageContent(page *gpage.Page) string {
|
||||
page.NextPageTag = "NextPage"
|
||||
page.PrevPageTag = "PrevPage"
|
||||
@ -15,16 +15,16 @@ func pageContent(page *gpage.Page) string {
|
||||
page.LastPageTag = "LastPage"
|
||||
pageStr := page.FirstPage()
|
||||
pageStr += page.PrevPage()
|
||||
pageStr += page.PageBar("current-page")
|
||||
pageStr += page.PageBar()
|
||||
pageStr += page.NextPage()
|
||||
pageStr += page.LastPage()
|
||||
return pageStr
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := ghttp.GetServer()
|
||||
s := g.Server()
|
||||
s.BindHandler("/page/custom2/*page", func(r *ghttp.Request) {
|
||||
page := gpage.New(100, 10, r.Get("page"), r.URL.String(), r.Router)
|
||||
page := r.GetPage(100, 10)
|
||||
buffer, _ := gview.ParseContent(`
|
||||
<html>
|
||||
<head>
|
||||
|
||||
@ -4,13 +4,12 @@ import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/gview"
|
||||
"github.com/gogf/gf/util/gpage"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindHandler("/page/static/*page", func(r *ghttp.Request) {
|
||||
page := gpage.New(100, 10, r.Get("page"), r.URL.String(), r.Router)
|
||||
page := r.GetPage(100, 10)
|
||||
buffer, _ := gview.ParseContent(`
|
||||
<html>
|
||||
<head>
|
||||
|
||||
@ -4,13 +4,12 @@ import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/gview"
|
||||
"github.com/gogf/gf/util/gpage"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindHandler("/:obj/*action/{page}.html", func(r *ghttp.Request) {
|
||||
page := gpage.New(100, 10, r.Get("page"), r.URL.String(), r.Router)
|
||||
page := r.GetPage(100, 10)
|
||||
buffer, _ := gview.ParseContent(`
|
||||
<html>
|
||||
<head>
|
||||
|
||||
@ -4,14 +4,13 @@ import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/gview"
|
||||
"github.com/gogf/gf/util/gpage"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindHandler("/page/template/{page}.html", func(r *ghttp.Request) {
|
||||
page := gpage.New(100, 10, r.Get("page"), r.URL.String())
|
||||
page.SetUrlTemplate("/order/list/{.page}.html")
|
||||
page := r.GetPage(100, 10)
|
||||
page.UrlTemplate = "/order/list/{.page}.html"
|
||||
buffer, _ := gview.ParseContent(`
|
||||
<html>
|
||||
<head>
|
||||
|
||||
@ -8,6 +8,9 @@ import (
|
||||
|
||||
func main() {
|
||||
for i := 0; i < 100; i++ {
|
||||
fmt.Println(grand.Rand(0, 99999))
|
||||
fmt.Println(grand.S(16))
|
||||
}
|
||||
for i := 0; i < 100; i++ {
|
||||
fmt.Println(grand.N(0, 99999999))
|
||||
}
|
||||
}
|
||||
|
||||
13
.example/util/guid/guid.go
Normal file
13
.example/util/guid/guid.go
Normal file
@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/util/guid"
|
||||
)
|
||||
|
||||
func main() {
|
||||
for i := 0; i < 100; i++ {
|
||||
s := guid.S()
|
||||
fmt.Println(s, len(s))
|
||||
}
|
||||
}
|
||||
20
.example/util/guid/guid_length.go
Normal file
20
.example/util/guid/guid_length.go
Normal file
@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 36*36^2+36*36+36
|
||||
var s string
|
||||
fmt.Println(strconv.ParseUint("zzz", 36, 3))
|
||||
fmt.Println(1 << 1)
|
||||
// MaxInt64
|
||||
s = strconv.FormatUint(math.MaxUint64, 16)
|
||||
fmt.Println(s, len(s))
|
||||
// PID
|
||||
s = strconv.FormatInt(1000000, 36)
|
||||
fmt.Println(s, len(s))
|
||||
}
|
||||
23
.example/util/guid/guid_unique.go
Normal file
23
.example/util/guid/guid_unique.go
Normal file
@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/util/guid"
|
||||
)
|
||||
|
||||
func main() {
|
||||
for i := 0; i < 100; i++ {
|
||||
s := guid.S([]byte("123"))
|
||||
fmt.Println(s, len(s))
|
||||
}
|
||||
fmt.Println()
|
||||
for i := 0; i < 100; i++ {
|
||||
s := guid.S([]byte("123"), []byte("456"))
|
||||
fmt.Println(s, len(s))
|
||||
}
|
||||
fmt.Println()
|
||||
for i := 0; i < 100; i++ {
|
||||
s := guid.S([]byte("123"), []byte("456"), []byte("789"))
|
||||
fmt.Println(s, len(s))
|
||||
}
|
||||
}
|
||||
13
.example/util/gvalid/gvalid_custom_message.go
Normal file
13
.example/util/gvalid/gvalid_custom_message.go
Normal file
@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/util/gvalid"
|
||||
)
|
||||
|
||||
func main() {
|
||||
g.I18n().SetLanguage("cn")
|
||||
err := gvalid.Check("", "required", nil)
|
||||
fmt.Println(err.String())
|
||||
}
|
||||
14
.example/util/gvalid/i18n/cn.toml
Normal file
14
.example/util/gvalid/i18n/cn.toml
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
|
||||
"gf.gvalid.required" = "字段不能为空"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -6,7 +6,7 @@
|
||||
.idea/
|
||||
.settings/
|
||||
.vscode/
|
||||
vender/
|
||||
vendor/
|
||||
composer.lock
|
||||
gitpush.sh
|
||||
pkg/
|
||||
@ -15,3 +15,4 @@ cbuild
|
||||
**/.DS_Store
|
||||
.vscode/
|
||||
go.sum
|
||||
.example/other/
|
||||
|
||||
@ -4,6 +4,7 @@ go:
|
||||
- "1.11.x"
|
||||
- "1.12.x"
|
||||
- "1.13.x"
|
||||
- "1.14.x"
|
||||
|
||||
branches:
|
||||
only:
|
||||
@ -12,7 +13,7 @@ branches:
|
||||
- staging
|
||||
|
||||
env:
|
||||
- GF_DEV=1 GO111MODULE=on
|
||||
- GF_DEBUG=1 GO111MODULE=on
|
||||
|
||||
services:
|
||||
- mysql
|
||||
|
||||
62
DONATOR.MD
62
DONATOR.MD
@ -1,7 +1,7 @@
|
||||
# Donators
|
||||
|
||||
We currently accept donation by Alipay/WechatPay, please note your github/gitee account in your payment bill.
|
||||
|
||||
We currently accept donation by [Alipay](https://goframe.org/images/donate.png) / [Gitee](https://gitee.com/johng/gf),
|
||||
please note your github/gitee account in your payment bill.
|
||||
|
||||
| Name | Channel | Amount | Comment
|
||||
|---|---|--- | ---
|
||||
@ -14,7 +14,7 @@ We currently accept donation by Alipay/WechatPay, please note your github/gitee
|
||||
|[zhuhuan12](https://gitee.com/zhuhuan12)|gitee|¥50.00 |
|
||||
|[zfan_codes](https://gitee.com/zfan_codes)|gitee|¥10.00 |
|
||||
|[arden](https://github.com/arden)|alipay|¥10.00 |
|
||||
|[macnie](https://www.macnie.com)|wechat|¥100.00 |
|
||||
|[macnie](https://www.macnie.com)|wechat|¥110.00 |
|
||||
|lah|wechat|¥100.00 |
|
||||
|x*z|wechat|¥20.00 |
|
||||
|潘兄|wechat|¥100.00 |
|
||||
@ -39,9 +39,61 @@ We currently accept donation by Alipay/WechatPay, please note your github/gitee
|
||||
|R*s|wechat|¥18.88| 谢谢GF!辛苦了!
|
||||
|粟*e|wechat|¥50.00|
|
||||
|[李超](https://github.com/effortlee)|wechat|¥124.00|
|
||||
|张炳贤|wechat+qq|¥600.00|
|
||||
|soidea666|wechat+qq|¥800.00|
|
||||
|[王哈哈](https://gitee.com/develop1024)|wechat|¥6.66| 希望gf越来越好
|
||||
|夕景|alipay+qq|¥9.96+3.57|
|
||||
|struggler|alipay|¥18.80|
|
||||
|*铁|wechat|¥0.01|
|
||||
|C*e|wechat|¥66.66| GF越来越好,棒👍!
|
||||
|---P คิดถึง|wechat|¥6.66|
|
||||
|[王飞](https://gitee.com/wang_2018)|gitee|¥20.00| 感谢您的开源项目!
|
||||
|[Zeroing-ZY](https://gitee.com/yunjieg)|gitee|¥20.00| 感谢您的开源项目!
|
||||
|[katydid酱](https://gitee.com/katydid2005)|gitee|¥50.00| 感谢您的开源项目!框架给予了很大的帮助!谢谢大佬!
|
||||
|[李海峰](https://gitee.com/dlhf)|gitee|¥10.00| 希望GF越来越好,框架很牛逼!
|
||||
|陆昱天|alipay|¥100.00|
|
||||
|[Dockercore](https://github.com/dockercore)|wechat|¥200.00| 非常喜欢!简洁好用!文档超级全!
|
||||
|🚶|wechat|¥6.88| 喝杯冰阔落
|
||||
|a*l|wechat|¥10.00| gf
|
||||
|[wxkj](https://gitee.com/wxkj)|wechat|¥10.00|
|
||||
|[米司特包](https://github.com/misitebao)|wechat|¥9.99|
|
||||
|重庆宝尔威科技|wechat|¥6.66|
|
||||
|琦玉-QPT|wechat|¥6.66|
|
||||
|sailsea|wechat|¥11.00|
|
||||
|[seny0929](https://gitee.com/seny0929)|wechat|¥99.90|
|
||||
|*华|wechat|¥6.66| 感谢郭强的热心
|
||||
|[Playhi](https://github.com/Playhi)|alipay|¥10.00|
|
||||
|北京京纬互动科技|alipay|¥200.00|
|
||||
|[米司特包](https://github.com/misitebao)|wechat|¥99.99|
|
||||
|金毛|alipay|¥100.00|
|
||||
|1*1x|wechat|¥100.00|
|
||||
|[ywanbing](https://github.com/ywanbing)|wechat|¥66.66|
|
||||
|[侯哥](http://www.macnie.com)|wechat|¥10.00|
|
||||
|如果🍋|alipay|¥100.00| 错过的奶茶^_^
|
||||
|蔡蔡|wechat|¥666.00| gf真强大,让项目省心
|
||||
|jack|wechat|¥100.00|
|
||||
|sbilly|wechat|¥100.00| 祝好!
|
||||
|[米司特包](https://github.com/misitebao)|wechat|¥166.66| 大佬加油!
|
||||
|*秦|wechat|¥20.00| 给群主献上一杯咖啡...
|
||||
|[zhuhuan12](https://gitee.com/zhuhuan12)|wechat|¥50.00|
|
||||
|faddei|qq|¥9.99|
|
||||
|*天|wechat|¥10.00|
|
||||
|*.|wechat|¥20.00| 学生一个微薄之力,冲
|
||||
|李勇|wechat|¥50.00|
|
||||
|[米司特包](https://github.com/misitebao)|wechat|¥66.66|
|
||||
|千年|wechat|¥1.08|
|
||||
|[szzxing](https://github.com/szzxing)|wechat|¥50.00|
|
||||
|伟客互联|wechat|¥66.66|
|
||||
|[sbilly](https://github.com/sbilly)|wechat|¥99.00|
|
||||
|**阳|alipay|¥100.01|
|
||||
|**亮|alipay|¥10.00|
|
||||
|[mingzaily](https://github.com/mingzaily)|alipay|¥30.00|
|
||||
|[seny0929](https://gitee.com/seny0929)|alipay|¥9.90|
|
||||
|Fly的狐狸|alipay|¥100.00|
|
||||
|六七 ·|wechat|¥88.88| gf越来越好
|
||||
|Tom|wechat|¥500.00| 一点心意 希望GF越来越好
|
||||
|Chao|wechat|¥166.00|
|
||||
|
||||
|
||||
<img src="https://goframe.org/images/donate.png"/>
|
||||
|
||||
<img src="https://goframe.org/images/donate.jpeg"/>
|
||||
|
||||
|
||||
131
README.MD
131
README.MD
@ -1,6 +1,6 @@
|
||||
# GoFrame
|
||||
|
||||
[](https://godoc.org/github.com/gogf/gf/g#pkg-subdirectories)
|
||||
[](https://godoc.org/github.com/gogf/gf)
|
||||
[](https://travis-ci.org/gogf/gf)
|
||||
[](https://goreportcard.com/report/github.com/gogf/gf)
|
||||
[](https://codecov.io/gh/gogf/gf/branch/master)
|
||||
@ -9,13 +9,13 @@
|
||||
|
||||
English | [简体中文](README_ZH.MD)
|
||||
|
||||
`GF(GoFrame)` is a modular, full-featured and production-ready application development framework of golang.
|
||||
Providing a series of core components and dozens of practical modules,
|
||||
such as: memcache, configure, validator, logging, array/queue/set/map containers,
|
||||
timer/timing tasks, file/memory lock, object pool, database ORM, etc.
|
||||
Supporting web server integrated with router, cookie, session, middleware, logger,
|
||||
template, https, hooks, rewrites and many more features.
|
||||
`GF(GoFrame)` is a modular, powerful, high-performance and production-ready application development framework
|
||||
of golang. Providing a series of core components and dozens of practical modules, such as:
|
||||
cache, logging, containers, timer, resource, validator, database orm, etc.
|
||||
Supporting web server integrated with router, cookie, session, middleware, logger, configure,
|
||||
template, https, hooks, rewrites and many more features.
|
||||
|
||||
> If you're a newbie to `Go`, you may consider `GoFrame` easy and great as `Laravel` in `PHP`, `SpringBoot` in `Java` or `Django` in `Python`.
|
||||
|
||||
# Installation
|
||||
```
|
||||
@ -28,57 +28,122 @@ require github.com/gogf/gf latest
|
||||
|
||||
# Limitation
|
||||
```
|
||||
golang version >= 1.10
|
||||
golang version >= 1.11
|
||||
```
|
||||
|
||||
# Documentation
|
||||
|
||||
* [APIDoc](https://godoc.org/github.com/gogf/gf)
|
||||
* [中文文档](https://goframe.org)
|
||||
|
||||
# Architecture
|
||||
<div align=center>
|
||||
<img src="https://goframe.org/images/arch.png?v=10"/>
|
||||
<img src="https://goframe.org/images/arch.png?v=12"/>
|
||||
</div>
|
||||
|
||||
# Quick Start
|
||||
# Packages
|
||||
1. **Primary Package**
|
||||
|
||||
```go
|
||||
package main
|
||||
The `gf` repository maintains some basic and most commonly used packages, keeping it as lightweight and simple as possible.
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
1. **Community Package**
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
r.Response.Write("Hello World")
|
||||
})
|
||||
s.Run()
|
||||
}
|
||||
The community packages are contributed and maintained by community members, which are hosted in `gogf` organization. Some of the community packages are separated from the `gf` repository, which are not of common usage or are with heavy dependencies.
|
||||
|
||||
# Performance
|
||||
|
||||
Here's the most popular Golang frameworks and libraries performance testing result in `WEB Server`. Performance testing cases source codes are hosted at: https://github.com/gogf/gf-performance
|
||||
|
||||
## Environment
|
||||
|
||||
OS : Ubuntu 18.04 amd64
|
||||
CPU : AMD A8-6600K x 4
|
||||
MEM : 32GB
|
||||
GO : v1.13.4
|
||||
|
||||
## Testing Tool
|
||||
|
||||
`ab`: Apache HTTP server benchmarking tool.
|
||||
|
||||
Command:
|
||||
```
|
||||
ab -t 10 -c 100 http://127.0.0.1:3000/hello
|
||||
ab -t 10 -c 100 http://127.0.0.1:3000/query?id=10000
|
||||
ab -t 10 -c 100 http://127.0.0.1:3000/json
|
||||
```
|
||||
The concurrency starts from `100` to `10000`.
|
||||
|
||||
[More Features...](https://goframe.org/start/index)
|
||||
> Run `5` times for each case of each project and pick up the best testing result.
|
||||
|
||||
## 1. Hello World
|
||||
<table>
|
||||
<tr>
|
||||
<th>Throughputs</th>
|
||||
<th>Mean Latency</th>
|
||||
<th>P99 Latency</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/throughputs1.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/meanlatency1.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/p99latency1.jpeg"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## 2. Json Response
|
||||
<table>
|
||||
<tr>
|
||||
<th>Throughputs</th>
|
||||
<th>Mean Latency</th>
|
||||
<th>P99 Latency</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/throughputs3.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/meanlatency3.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/p99latency3.jpeg"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
# Documentation
|
||||
|
||||
* 中文官网: https://goframe.org
|
||||
* GoDoc API: https://godoc.org/github.com/gogf/gf
|
||||
|
||||
|
||||
# Discussion
|
||||
- QQ Group:[116707870](//shang.qq.com/wpa/qunwpa?idkey=195f91eceeb5d7fa76009b7cd5a4641f70bf4897b7f5a520635eb26ff17adfe7)
|
||||
- WX Group:Add friend`389961817` in WeChat, commenting `GF`
|
||||
- Issues:https://github.com/gogf/gf/issues
|
||||
|
||||
> It's recommended learning `GoFrame` through its awesome source codes and API reference.
|
||||
|
||||
# License
|
||||
|
||||
`GF` is licensed under the [MIT License](LICENSE), 100% free and open-source, forever.
|
||||
|
||||
# Known Users
|
||||
|
||||
Logos are not authorized to be shown due to trademark copyrights.
|
||||
|
||||
|
||||
# Contributors
|
||||
This project exists thanks to all the people who contribute. [[Contributors](https://github.com/gogf/gf/graphs/contributors)].
|
||||
<a href="https://github.com/gogf/gf/graphs/contributors"><img src="https://opencollective.com/goframe/contributors.svg?width=890&button=false" /></a>
|
||||
|
||||
|
||||
# Donators
|
||||
|
||||
We currently accept donation by Alipay/WechatPay, please note your github/gitee account in your payment bill. If you like `GF`, why not [buy developer a cup of coffee](DONATOR.MD)?
|
||||
|
||||
# Sponsors
|
||||
We appreciate any kind of sponsorship for `GF` development. If you've got some interesting, please contact WeChat `389961817` / Email `john@goframe.org`.
|
||||
|
||||
|
||||
|
||||
# Thanks
|
||||
<a href="https://www.jetbrains.com/?from=GoFrame"><img src="https://goframe.org/images/jetbrains.png" width="100" alt="JetBrains"/></a>
|
||||
|
||||
|
||||
<!--
|
||||
# Sponsor
|
||||
We appreciate any kind of sponsorship for `GF` development. If you've got some interested, please contact john@goframe.org.
|
||||
-->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
140
README_ZH.MD
140
README_ZH.MD
@ -1,5 +1,5 @@
|
||||
# GoFrame
|
||||
[](https://godoc.org/github.com/gogf/gf/g#pkg-subdirectories)
|
||||
[](https://godoc.org/github.com/gogf/gf)
|
||||
[](https://travis-ci.org/gogf/gf)
|
||||
[](https://goreportcard.com/report/github.com/gogf/gf)
|
||||
[](https://codecov.io/gh/gogf/gf/branch/master)
|
||||
@ -8,21 +8,28 @@
|
||||
|
||||
[English](README.MD) | 简体中文
|
||||
|
||||
`GF(Go Frame)`是一款模块化、高性能、生产级的Go基础开发框架。实现了比较完善的基础设施建设,包括常用的核心开发组件,
|
||||
如:缓存、日志、文件、时间、队列、数组、集合、字符串、定时器、命令行、文件锁、内存锁、对象池、连接池、资源管理、数据校验、数据编码、文件监控、
|
||||
定时任务、数据库ORM、TCP/UDP组件、进程管理/通信、并发安全容器等等。
|
||||
并提供了Web服务开发的系列核心组件,如:Router、Cookie、Session、Middleware、服务注册、配置管理、模板引擎等等,
|
||||
支持热重启、热更新、多域名、多端口、多服务、HTTPS、Rewrite等特性。
|
||||
`GF(Go Frame)`是一款模块化、高性能、生产级的Go基础开发框架。
|
||||
实现了比较完善的基础设施建设以及开发工具链,提供了常用的基础开发模块,
|
||||
如:缓存、日志、队列、数组、集合、容器、定时器、命令行、内存锁、对象池、
|
||||
配置管理、资源管理、数据校验、数据编码、定时任务、数据库ORM、TCP/UDP组件、进程管理/通信等等。
|
||||
并提供了Web服务开发的系列核心组件,如:Router、Cookie、Session、Middleware、服务注册、模板引擎等等,
|
||||
支持热重启、热更新、域名绑定、TLS/HTTPS、Rewrite等特性。
|
||||
|
||||
> 如果您初识`Go`语言,您可以将`GoFrame`类似于`PHP`中的`Laravel`, `Java`中的`SpringBoot`或者`Python`中的`Django`。
|
||||
|
||||
# 特点
|
||||
* 模块化、松耦合设计;
|
||||
* 模块丰富,开箱即用;
|
||||
* 简便及可维护性为宗旨;
|
||||
* 模块丰富、开箱即用;
|
||||
* 简便易用、易于维护;
|
||||
* 高代码质量、高单元测试覆盖率;
|
||||
* 社区活跃,大牛谦逊低调脾气好;
|
||||
* 详尽的开发文档及示例;
|
||||
* 完善的本地中文化支持;
|
||||
* 更适合企业及团队使用;
|
||||
* 更多请查阅文档及源码;
|
||||
* 设计为团队及企业使用;
|
||||
|
||||
# 地址
|
||||
- **主库**:https://github.com/gogf/gf
|
||||
- **码云**:https://gitee.com/johng/gf
|
||||
|
||||
# 安装
|
||||
```html
|
||||
@ -38,48 +45,115 @@ require github.com/gogf/gf latest
|
||||
golang版本 >= 1.11
|
||||
```
|
||||
|
||||
|
||||
|
||||
# 架构
|
||||
<div align=center>
|
||||
<img src="https://goframe.org/images/arch.png?v=10"/>
|
||||
<img src="https://goframe.org/images/arch.png?v=12"/>
|
||||
</div>
|
||||
|
||||
# 模块
|
||||
|
||||
1. **核心模块**
|
||||
|
||||
`GoFrame`提供了一些基础的、常用的模块,简单、易用和轻量级,并保持极少的外部依赖,这些模块由`gf`主仓库细致维护。
|
||||
|
||||
1. **社区模块**
|
||||
|
||||
社区模块主要由社区贡献并维护,大部分也是由`gf`主仓库的贡献者提供及维护,存放于`gogf`空间下,与`gf`主仓库处于同一级别。有的社区模块是从`gf`主仓库中剥离出来单独维护的模块,这些模块并不是特别常用,或者对外部依赖较重。
|
||||
|
||||
|
||||
# 性能
|
||||
|
||||
以下是目前最流行的`WEB Server` Golang框架/类库性能测试结果。
|
||||
性能测试用例源代码仓库: https://github.com/gogf/gf-performance
|
||||
|
||||
## 环境:
|
||||
|
||||
OS : Ubuntu 18.04 amd64
|
||||
CPU : AMD A8-6600K x 4
|
||||
MEM : 32GB
|
||||
GO : v1.13.4
|
||||
|
||||
## 工具
|
||||
|
||||
`ab`: Apache HTTP server benchmarking tool.
|
||||
|
||||
测试命令:
|
||||
```
|
||||
ab -t 10 -c 100 http://127.0.0.1:3000/hello
|
||||
ab -t 10 -c 100 http://127.0.0.1:3000/query?id=10000
|
||||
ab -t 10 -c 100 http://127.0.0.1:3000/json
|
||||
```
|
||||
并发客户端数量从 `100` 递增到 `10000`。
|
||||
|
||||
> 每个项目的每个用例均运行`5`次,取最优的结果展示。
|
||||
|
||||
## 1. Hello World
|
||||
<table>
|
||||
<tr>
|
||||
<th>Throughputs</th>
|
||||
<th>Mean Latency</th>
|
||||
<th>P99 Latency</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/throughputs1.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/meanlatency1.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/p99latency1.jpeg"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## 2. Json Response
|
||||
<table>
|
||||
<tr>
|
||||
<th>Throughputs</th>
|
||||
<th>Mean Latency</th>
|
||||
<th>P99 Latency</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/throughputs3.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/meanlatency3.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/p99latency3.jpeg"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
# 文档
|
||||
|
||||
开发文档:[https://goframe.org](https://goframe.org)
|
||||
开发文档:https://goframe.org
|
||||
|
||||
接口文档:[https://godoc.org/github.com/gogf/gf](https://godoc.org/github.com/gogf/gf)
|
||||
接口文档:https://godoc.org/github.com/gogf/gf
|
||||
|
||||
# 使用
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
r.Response.Write("Hello World")
|
||||
})
|
||||
s.Run()
|
||||
}
|
||||
```
|
||||
|
||||
[更多..](https://goframe.org/start/index)
|
||||
# 帮助
|
||||
- QQ交流群:[116707870](//shang.qq.com/wpa/qunwpa?idkey=195f91eceeb5d7fa76009b7cd5a4641f70bf4897b7f5a520635eb26ff17adfe7)
|
||||
- WX交流群:微信添加`389961817`备注`GF`
|
||||
- 主库ISSUE:https://github.com/gogf/gf/issues
|
||||
|
||||
> 建议通过阅读`GoFrame`的源码以及API文档深度学习`GoFrame`,了解更多的精妙设计。
|
||||
|
||||
# 协议
|
||||
|
||||
`GF` 使用非常友好的 [MIT](LICENSE) 开源协议进行发布,永久`100%`开源免费。
|
||||
|
||||
# 用户
|
||||
|
||||
由于商标版权缘故,未经厂商商务部授权允许无法用于宣传展示。
|
||||
|
||||
# 贡献
|
||||
|
||||
感谢所有参与`GoFrame`开发的贡献者。 [[贡献者列表](https://github.com/gogf/gf/graphs/contributors)].
|
||||
<a href="https://github.com/gogf/gf/graphs/contributors"><img src="https://opencollective.com/goframe/contributors.svg?width=890&button=false" /></a>
|
||||
|
||||
|
||||
# 捐赠
|
||||
|
||||
如果您喜欢`GF`,要不[给开发者来杯咖啡吧](DONATOR.MD)!
|
||||
请在捐赠时备注您的`github`/`gitee`账号名称。
|
||||
|
||||
# 赞助
|
||||
|
||||
赞助支持`GF`框架的快速研发,如果您感兴趣,请联系 微信 `389961817` / 邮件 `john@goframe.org`。
|
||||
|
||||
# 感谢
|
||||
<a href="https://www.jetbrains.com/?from=GoFrame"><img src="https://goframe.org/images/jetbrains.png" width="100" alt="JetBrains"/></a>
|
||||
<a href="https://www.jetbrains.com/?from=GoFrame"><img src="https://goframe.org/images/jetbrains.png" width="100" alt="JetBrains"/></a>
|
||||
|
||||
|
||||
558
RELEASE.2.MD
558
RELEASE.2.MD
@ -1,3 +1,561 @@
|
||||
# `v1.13.1` (2020-06-10)
|
||||
|
||||
# GoFrame
|
||||
|
||||
`GF(Go Frame)`是一款模块化、高性能、生产级的Go基础开发框架。实现了比较完善的基础设施建设以及开发工具链,提供了常用的基础开发模块,如:缓存、日志、队列、数组、集合、容器、定时器、命令行、内存锁、对象池、配置管理、资源管理、数据校验、数据编码、定时任务、数据库ORM、TCP/UDP组件、进程管理/通信等等。并提供了Web服务开发的系列核心组件,如:Router、Cookie、Session、Middleware、服务注册、模板引擎等等,支持热重启、热更新、域名绑定、TLS/HTTPS、Rewrite等特性。
|
||||
|
||||
## 特点
|
||||
|
||||
* 模块化、松耦合设计;
|
||||
* 模块丰富、开箱即用;
|
||||
* 简便易用、易于维护;
|
||||
* 高代码质量、高单元测试覆盖率;
|
||||
* 社区活跃,大牛谦逊低调脾气好;
|
||||
* 详尽的开发文档及示例;
|
||||
* 完善的本地中文化支持;
|
||||
* 设计为团队及企业使用;
|
||||
|
||||
## 发展
|
||||
|
||||
`GoFrame`开始得比较早,`2011`年始于北京一个智能物联网平台项目,那时还没有这么多物联网的现行标准,`Go`的标准库以及生态也未如此丰富。`2017`年的时候`GF`才开始发布测试版,`2018`年`1024`程序员节日的时候才发布`v1.0`正式版,为`Go`生态发展添砖加瓦。开源以来快速迭代、发展成长,广受开发者和企业的青睐,也有许多的开发者加入了贡献行列。`GF`原本是为开发团队设计的,因此她的开发效率和可维护性做得非常好,有着很高的代码质量以及丰富的单元测试和示例,并且`GF`是目前中文化文档做的最好的`Golang`开发框架。
|
||||
|
||||
# Change Log
|
||||
|
||||
1. 应多数开发者的要求,框架要求的最低`Golang`运行版本降级为了`v1.11`。
|
||||
1. 新增`GoFrame`视频教程地址:
|
||||
- bilibili:https://www.bilibili.com/video/av94410029
|
||||
- 西瓜视频: https://www.ixigua.com/pseries/6809291194665796100/
|
||||
1. 将不常用的`guuid`模块迁移到 github.com/gogf/guuid 作为社区模块维护,保持`gf`主仓库的轻量级。
|
||||
1. 新增`guid`模块,用于高效轻量级的唯一字符串生成:https://goframe.org/util/guid/index
|
||||
|
||||
|
||||
## `tool chain`
|
||||
|
||||
1. 工具链更新:https://goframe.org/toolchain/cli
|
||||
1. 新增`gf env`命令,更优雅地查看当前`Golang`环境变量信息。
|
||||
1. 新增`gf mod path`命令,用于将当前`go modules`包拷贝到`GOPATH`中,以便使用原始的`GOPATH`方式开发项目。
|
||||
1. 对现有`cli`命令进行了一些改进,提高使用体验;预编译二进制版本在部分平台下提供了`upx`压缩,使得下载的文件更小。
|
||||
|
||||
## `container`
|
||||
1. `garray`
|
||||
- https://goframe.org/container/garray/index
|
||||
- 简化数组使用方式,支持类似于`var garray.Array`的变量定义使用方式;
|
||||
- 增加`Walk`方法,用于自定义的数组元素处理方法;
|
||||
- 增加`ContainsI`方法,用于大小写忽略匹配的数组元素项存在性查找;
|
||||
- 完善单元测试,代码覆盖率`94%`;
|
||||
- 代码改进,提高性能;
|
||||
- 修复一些问题;
|
||||
1. `gchan`
|
||||
- 由于该封装包实际意义不是很大,因此从主框架中删除;
|
||||
1. `glist`
|
||||
- https://goframe.org/container/glist/index
|
||||
- 简化链表使用方式,支持类似于`var glist.List`的变量定义使用方式;
|
||||
- 完善单元测试,代码覆盖率`99%`;
|
||||
1. `gmap`
|
||||
- https://goframe.org/container/gmap/index
|
||||
- 简化`Map`使用方式,支持类似于`var gmap.Map`的变量定义使用方式;
|
||||
- 完善单元测试,代码覆盖率`81%`;
|
||||
- 代码改进,提高性能;
|
||||
1. `gset`
|
||||
- https://goframe.org/container/gset/index
|
||||
- 简化集合使用方式,支持类似于`var gset.Set`的变量定义使用方式;
|
||||
- 增加`Walk`方法,用于自定义的集合元素处理方法;
|
||||
- 完善单元测试,代码覆盖率`90%`;
|
||||
- 代码改进,提高性能;
|
||||
1. `gtree`
|
||||
- https://goframe.org/container/gtree/index
|
||||
- 简化树型使用方式,支持类似于`var gtree.BTree`的变量定义使用方式;
|
||||
- 完善单元测试,代码覆盖率`90%`;
|
||||
1. `gvar`
|
||||
- https://goframe.org/container/gvar/index
|
||||
- 完善单元测试,代码覆盖率`69%`;
|
||||
- 代码组织结构调整,提高维护性;
|
||||
- 代码改进,提高性能;
|
||||
|
||||
## `database`
|
||||
|
||||
1. `gdb`
|
||||
- 增加`Transaction(f func(tx *TX) error) (err error)`接口方法,用于通过闭包实现事务封装处理:https://goframe.org/database/gdb/transaction
|
||||
- 去掉不常用的`From`接口方法,改进`Table`及`Model`方法的参数为不定参数,并支持通过不定参数传递表别名:https://goframe.org/database/gdb/chaining/select
|
||||
- 增加`DryRun`特性,支持空跑时只执行查询不执行写入/更新/删除操作:https://goframe.org/database/gdb/senior
|
||||
- 增加`create_at`, `update_at`写入时间、更新时间字段自动填充特性:https://goframe.org/database/gdb/chaining/auto-time
|
||||
- 增加`delete_at`软删除特性:https://goframe.org/database/gdb/chaining/auto-time
|
||||
- 增加`Having`链式操作方法,用于`having`条件查询:https://goframe.org/database/gdb/chaining/select
|
||||
- `Result`结果对象增加`Chunk`方法,用于自定义的数据分批处理:https://goframe.org/database/gdb/result
|
||||
- 改进`Schema`数据库运行时切换特性;
|
||||
- 改进对`pgsql`, `mssql`, `sqlite`, `oracle`数据库字段类型的支持;
|
||||
- 进一步完善单元测试;
|
||||
- 代码组织结构调整,提高维护性;
|
||||
- 代码改进,提高性能;
|
||||
1. `gredis`
|
||||
- 增加`MaxActive`连接池参数默认配置为`100`,限制默认的连接数量;
|
||||
- 改进`Conn`连接对象的`Do`方法,支持对`map/slice/struct`类型进行自动的`json.Marshal`处理,注意获取数据时使用`DoVar`方法获取:https://goframe.org/database/gredis/usage
|
||||
- 完善单元测试,代码覆盖率`72%`;
|
||||
|
||||
## `net`
|
||||
1. `ghttp`
|
||||
- 增加`Prefix`及`Retry`客户端链式操作方法;
|
||||
- 增加客户端原始请求打印特性:https://goframe.org/net/ghttp/client/demo/dump
|
||||
- 增加`ClientMaxBodySize`的服务端配置,用于限制客户端提交的`Body`大小,默认为`8MB`;在涉及到上传的Server中需要增加该配置的大小,在配置文件中指定对应的大小即可,如`ClientMaxBodySize="100MB"`:https://goframe.org/net/ghttp/config
|
||||
- 改进`SessionId`生成的随机性,提高`Session`安全性:https://goframe.org/os/gsession/index
|
||||
- 改进`ghttp.Server`实现了标准库的`http.Handler`接口,便于与其他第三方的服务如`Prometheus`进行代码集成;
|
||||
- 其他大量的代码细节改进工作,提高性能及持久维护性;
|
||||
- 完善单元测试,代码覆盖率`61%`;
|
||||
|
||||
1. `gipv4`
|
||||
- 增加`GetIpArray`方法,用于获取当前主机的所有IPv4地址;
|
||||
- 增加`GetMacArray`及`GetMac`方法,用于获取当前主机的`MAC`地址信息;
|
||||
- 修改`IntranetIP`方法名称为`GetIntranetIp`,修改`IntranetIPArray`方法名称为`GetIntranetIpArray`;
|
||||
|
||||
## `encoding`
|
||||
1. `gjson`
|
||||
- 新增`GetMaps`获取`JSON`内部节点变量方法;
|
||||
- 改进`NewWithTag`方法对`map/struct`的处理;
|
||||
- 完善单元测试,代码覆盖率`77%`;
|
||||
1. `gyaml`
|
||||
- 升级依赖的第三方`yaml`解析包,解决了`map[interface{}]interface{}`转换问题;
|
||||
|
||||
## `error`
|
||||
1. `gerror`
|
||||
- 新增`NewfSkip`方法,用于创建`skip`指定堆栈的错误对象;
|
||||
- 放开框架所有的堆栈链路打印,展示错误时真实的链路调用详情;
|
||||
|
||||
## `os`
|
||||
1. `gcache`
|
||||
- 增加`GetVar`方法,用于获得可以便捷转换为其他数据类型的"泛型"变量;
|
||||
- 标记`Removes`方法废弃,改进`Remove`方法参数为不定参数,统一使用`Remove`方法删除单个/多个键值对;
|
||||
- 完善单元测试,代码覆盖率`96%`;
|
||||
|
||||
1. `genv`
|
||||
- 增加`GetVar`方法,用于获得可以便捷转换为其他数据类型的"泛型"变量;
|
||||
|
||||
1. `gfile`
|
||||
- 改进`CopyDir/CopyFile`复制目录/文件方法;
|
||||
- 新增`ScanDirFunc`方法,用于支持自定义处理回调的目录检索;
|
||||
- 完善单元测试,代码覆盖率`64%`;
|
||||
|
||||
1. `glog`
|
||||
- 增加支持`Context`上下文变量的日志打印特性:https://goframe.org/os/glog/context
|
||||
|
||||
1. `gres`
|
||||
- 改进打包特性,增强生成二进制文件及Go文件的压缩比,比旧版本增加`20%`压缩率,使得编译生成的二进制文件体积更小;
|
||||
- 代码结构改进,提高执行效率及可持久维护性;
|
||||
|
||||
1. `gsession`
|
||||
- 改进`SessionId`默认生成方法,采用`guid.S`方法生成;
|
||||
- 增加`SetId`及`SetIdFunc`方法,用于自定义`SessionId`及自定义的`SessionId`生成方法;
|
||||
|
||||
## `frame`
|
||||
1. `g`
|
||||
- 新增`g.Table`方法,用于快速创建数据库模型操作对象;
|
||||
|
||||
## `i18n`
|
||||
1. `gi18n`
|
||||
- 新增`GetContent`方法,用于获取指定`i18n`关键字为转译内容;
|
||||
- 改进代码细节,提高性能和持久可维护性;
|
||||
- 完善单元测试,代码覆盖率`74%`;
|
||||
|
||||
## `test`
|
||||
1. `gtest`
|
||||
- 增加`AssertNQ`断言方法,用于强类型的不相等判断;
|
||||
|
||||
## `text`
|
||||
1. `gstr`
|
||||
- 增加`SubStrRune`方法,用于支持`unicode`的字符串截取;
|
||||
- 增加`StrLimitRune`方法,用于支持`unicode`的字符串截断隐藏;
|
||||
- 增加`LenRune`方法,用于替换`RuneLen`方法,统一方法命名风格;
|
||||
- 增加`PosRune/PosIRune/PosRRune/PosRIRune`方法,用于支持`unicode`的字符串左右位置查找;
|
||||
- 增加`CompareVersionGo`方法,用于`Golang`风格的版本号大小比较;
|
||||
- 完善单元测试,代码覆盖率`75%`;
|
||||
|
||||
## `util`
|
||||
1. `gconv`
|
||||
- 改进`Convert`转换方法,支持常见`map`类型的转换;
|
||||
- 改进类型转换过程中异常错误的捕获,通过`error`返回;
|
||||
- 其他一些细节改进;
|
||||
- 完善单元测试,代码覆盖率`63%`;
|
||||
|
||||
1. `grand`
|
||||
- 增加`B`方法,用于获得随机的二进制数据;
|
||||
- 改进代码底层实现,部分接口性能提高`50%`;
|
||||
- 完善单元测试,代码覆盖率`74%`;
|
||||
|
||||
1. `guid`
|
||||
- 新增`guid`模块,用于高效轻量级的唯一字符串生成:https://goframe.org/util/guid/index
|
||||
|
||||
1. `gutil`
|
||||
- 增加`MapContains`方法,用于判断map中是否包含指定键名;
|
||||
- 增加`MapDelete`方法,用于删除map中指定的键名,可以为多个键名;
|
||||
- 增加`MapMerge`方法,用于合并两个map;
|
||||
- 增加`MapMergeCopy`方法,用于拷贝多个map;
|
||||
- 增加`MapContainsPossibleKey`方法,用于查找指定键名,忽略大小写及字符`'-'/'_'/'.'/' '`;
|
||||
|
||||
1. `gvalid`
|
||||
- 所有默认的错误提示改为了英文;
|
||||
- 错误提示的配置改为了通过`i18n`来配置实现,以便支持国际化:https://goframe.org/util/gvalid/message
|
||||
- 身份证号规则名称从`id-number`改为了`resident-id `;
|
||||
- 银行卡号规则名称从`luhn`改为了`bank-card`;
|
||||
- 完善单元测试,代码覆盖率`96%`;
|
||||
|
||||
## Bug Fix
|
||||
1. 修复`gcompress`的多文件`zip`压缩问题;
|
||||
1. 修复`ghttp.Client`获取返回的过期`Cookie`的问题;
|
||||
1. 修复`gres.File`对于`http.File`接口的实现细节;
|
||||
1. 修复`garray.Pop*`方法的边界问题;
|
||||
1. 修复`gres`中`Readdir`方法参数为`0`时报错的问题;
|
||||
1. 其他一些修复:https://github.com/gogf/gf/issues?q=is%3Aissue+label%3Abug
|
||||
|
||||
|
||||
|
||||
|
||||
# `v1.12.1` (2020-03-31)
|
||||
|
||||
大家好啊!久等啦!
|
||||
|
||||
由于自从上次版本的发布以来,越来越多小伙伴加入了`GF`的大家庭,并提供了许多不错的建议和反馈,这次版本对其中大部分反馈进行了处理,包括大部分的改进建议和部分新特性,因此这次的版本发布时隔了两个多月。`GF`非常注重代码质量以及可持续维护性,这次版本也进一步对框架大部分模块的示例、注释和单元测试用例进行了完善,目前单元测试用例数量约为`1991`例,代码覆盖率为`71%`,覆盖了所有模块的绝大部分主要功能。
|
||||
|
||||
`GF`框架提供了比较常用、高质量的基础开发模块,轻量级、模块化、高性能,推荐大家阅读框架源码了解更多细节。本次发布有个别的不兼容升级,往往批量替换即可,以下`change log`比较完善,建议升级前仔细阅读。
|
||||
|
||||
本次发布即意味下一版本开发计划的开启,欢迎更多小伙伴参与开源贡献:https://github.com/gogf/gf/projects/8
|
||||
|
||||
感谢大家支持!Enjoy your `GF`!
|
||||
|
||||
|
||||
# GoFrame
|
||||
|
||||
`GF(Go Frame)`是一款模块化、高性能、生产级的Go基础开发框架。实现了比较完善的基础设施建设以及开发工具链,提供了常用的基础开发模块,如:缓存、日志、队列、数组、集合、容器、定时器、命令行、内存锁、对象池、配置管理、资源管理、数据校验、数据编码、定时任务、数据库ORM、TCP/UDP组件、进程管理/通信等等。并提供了Web服务开发的系列核心组件,如:Router、Cookie、Session、Middleware、服务注册、模板引擎等等,支持热重启、热更新、域名绑定、TLS/HTTPS、Rewrite等特性。
|
||||
|
||||
## 特点
|
||||
* 模块化、松耦合设计;
|
||||
* 模块丰富,开箱即用;
|
||||
* 简便易用,易于维护;
|
||||
* 社区活跃,大牛谦逊低调脾气好;
|
||||
* 高代码质量、高单元测试覆盖率;
|
||||
* 详尽的开发文档及示例;
|
||||
* 完善的本地中文化支持;
|
||||
* 更适合企业及团队使用;
|
||||
|
||||
## 地址
|
||||
- 官网:https://goframe.org
|
||||
- 主库:https://github.com/gogf/gf
|
||||
- 码云:https://gitee.com/johng/gf
|
||||
|
||||
# Change Log
|
||||
|
||||
从`GF v1.12`版本开始,框架要求的最低`Golang`运行版本为`v1.13`,由于`Golang`新版本都是向后兼容的,因此推荐大家更新使用`Golang`新版本:https://golang.google.cn/dl/
|
||||
|
||||
> 本次版本也新增了`Swagger`的工具及插件支持,另行独立发布。
|
||||
|
||||
## `tool chain`
|
||||
1. `gen model`命令新增对`pgsql/mssql/sqlite/oracle`的模型生成支持。
|
||||
1. `gen model`命令生成模型新增公开包变量`Columns`用于获得表的字段名称。
|
||||
|
||||
## `net`
|
||||
1. `ghttp`
|
||||
- 注意:从该版本开始,`Server`默认关闭了平滑重启特性。开发者可以通过相应的配置选项打开。
|
||||
- 改进`Client.Get`方法,增加可选的请求参数。
|
||||
- 新增`Client`链式操作方法:`Header`, `HeaderRaw`, `Cookie`, `ContentType`, `ContentJson`, `ContentXml`, `Timeout`, `BasicAuth`, `Ctx`:https://goframe.org/net/ghttp/client/chain
|
||||
- 新增`Request.GetCtx/GetCtxVar/SetCtxVar/Context`上下文变量管理方法,用于请求内部的上下文变量特性:
|
||||
- 自定义变量:https://goframe.org/net/ghttp/request/custom
|
||||
- 上下文变量:https://goframe.org/net/ghttp/request/context
|
||||
- 新增`Request.GetUploadFile/GetUploadFiles`方法,以及`UploadFile`类型,极大简化文件上传处理逻辑:https://goframe.org/net/ghttp/client/demo/upload
|
||||
- 新增`Request.GetPage`方法,用于便捷地获得分页对象:
|
||||
- 基本介绍:https://goframe.org/util/gpage/index
|
||||
- 动态分页:https://goframe.org/util/gpage/dynamic
|
||||
- 静态分页:https://goframe.org/util/gpage/static
|
||||
- Ajax分页:https://goframe.org/util/gpage/ajax
|
||||
- URL模板:https://goframe.org/util/gpage/template
|
||||
- 自定义分页:https://goframe.org/util/gpage/custom
|
||||
- 改进`Response.Redirect*`方法,增加自定义的跳转HTTP状态码参数。
|
||||
- 改进`CORS`特性,完善跨域功能处理,并完全遵守`W3C`关于`OPTIONS`请求方法的规范约定:https://goframe.org/net/ghttp/cors
|
||||
- 模板视图对象增加`Request`内置变量,用于模板获得请求参数。由于`GF`框架的模板引擎采用两级缓存设计,减少`IO`开销的同时提升了执行效率,即使增加了大量的内置变量以及内置方法,经过大规模的性能测试,性能比其他`WebServer`库相同逻辑下高出`50% - 200%`的效率。
|
||||
- 新增`Server`实验性的`Plugin`特性。
|
||||
- 改进`Server`底层路由存储、检索逻辑,优化代码,提升效率。
|
||||
- 改进分组路由注册的源码位置记录功能,当路由注册冲突时能够精准定位及提示重复路由源码位置。
|
||||
- 改进`Server`日志处理。
|
||||
- 完善单元测试。
|
||||
|
||||
|
||||
## `database`
|
||||
1. `gdb`
|
||||
- 代码重构改进,增加`Driver`驱动接口设计,方便开发者自定义驱动实现。新增`Register`包方法,用于开发者注册自定义的数据库类型驱动:https://goframe.org/database/gdb/driver
|
||||
- 新增`GetArray`接口及实现,用于获取指定字段列的数据,构造成数组返回:https://goframe.org/database/gdb/chaining/select
|
||||
- 新增`InsertIgnore`接口及实现,用于写入时忽略写入冲突,仅对`mysql`数据库类型有效:https://goframe.org/database/gdb/chaining/insert-save
|
||||
- 新增`Schema`接口及实现,用于动态切换并获取指定名称的数据库对象:https://goframe.org/database/gdb/chaining/schema
|
||||
- 新增`FieldsStr/FieldsExStr`模型方法,用于获取表字段,并构造成字符串返回:hhttps://goframe.org/database/gdb/chaining/fields-retrieve
|
||||
- 新增`LockUpdate/LockShared`模型链式操作方法,用于实现悲观锁操作:https://goframe.org/database/gdb/chaining/lock
|
||||
- 改进`Where/Data`方法对更新参数输入方式的支持,提高灵活性:
|
||||
- https://goframe.org/database/gdb/chaining/select
|
||||
- https://goframe.org/database/gdb/chaining/update-delete
|
||||
- 查询结果对象`Result`新增`Array`方法,用于获得指定字段的数值,构造成数组返回:https://goframe.org/database/gdb/result
|
||||
- 改进`OmitEmpty`方法对`Data`输入参数的过滤,当给定的`Data`参数为空时间对象时,将会被过滤。
|
||||
- 增加默认的数据库连接池参数:`MaxIdleConns=10`。
|
||||
- 其他一些改进。
|
||||
- 完善单元测试。
|
||||
|
||||
1. `gredis`
|
||||
- 增加/修改默认的数据库连接池参数:`MaxIdle=10`, `IdleTimeout=10s`, `MaxConnLifetime=30s`, `Wait=true`。
|
||||
- 完善单元测试。
|
||||
|
||||
## `container`
|
||||
1. 所有容器对象新增`UnmarshalValue(interface{}) error`接口方法实现,用于`gconv`转换时根据任意类型变量创建/设置对象内容,`GF`框架的所有容器对象均实现了该接口。
|
||||
|
||||
1. `garray`
|
||||
- 新增`RemoveValue`方法,用于根据数值检索并删除元素项。
|
||||
- 新增`FilterNil`方法,用于遍历并删除数组中的`nil`元素项。
|
||||
- 新增`FilterEmpty`方法,用于遍历并删除数组中的空值元素项,空值包括:`0, nil, false, "", len(slice/map/chan) == 0`。
|
||||
- 改进`Set/Fill/InsertBefore/InsertAfter`方法严谨性,将原有的链式操作返回值对象修改为`error`返回值。
|
||||
- 改进`Get/Remove/PopRand/PopLeft/PopRight/Rand`方法严谨性,增加`found`的`bool`返回值,标识当前方法是否有数据返回,当空数组或者操作越界时`found`返回值为`false`。
|
||||
- 改变`Rands`方法原有逻辑,保证按照给定大小返回随机数组。
|
||||
- 完善单元测试。
|
||||
|
||||
1. `gpool`
|
||||
- 调整缓存池过期时间参数类型,旧版本为`int`类型表示毫秒,新版本为`time.Duration`类型使得时间控制更灵活。
|
||||
- 内部代码优化,性能改进。
|
||||
- 完善单元测试。
|
||||
- 完善注释。
|
||||
|
||||
## `os`
|
||||
1. `glog`
|
||||
- 增加`Rotation`日志滚动切分特性,新增按照文件大小或过期时间进行日志切分,并支持切分文件数量限制、对日志文件进行自动压缩、可自定义压缩级别(`1-9`)、支持对切分文件过期时间清理:https://goframe.org/os/glog/rotate
|
||||
- 新增`LevelPrefixes`特性,支持对日志级别的前缀名称进行自定义:https://goframe.org/os/glog/level
|
||||
- 新增`SetLevelStr`方法,并增加按照字符串进行日志级别配置的特性:
|
||||
- https://goframe.org/os/glog/level
|
||||
- https://goframe.org/os/glog/config
|
||||
- 新增`SetDefaultLogger`包方法,用于设置全局默认的`Logger`对象。
|
||||
|
||||
1. `gres`
|
||||
1. 改进资源内容编码设计,采用新的压缩算法,减少资源文件大小,例如原本`15MB`的网站静态资源文件(`css/js/html`等),资源文件打包后约为`4MB`左右:https://goframe.org/os/gres/index
|
||||
1. 注意:该改进与旧版本无法兼容,需要重新打包原有的资源文件。
|
||||
1. 完善单元测试。
|
||||
|
||||
1. `gcfg`
|
||||
- 去掉配置对象属性的原子并发安全控制,改为普通变量类型。由于配置管理是最常用模块之一,因此确保高效的设计及方法实现。
|
||||
- 单例对象做较大调整:为方便多文件场景下的配置文件调用,简便使用并提高开发效率,因此当给定的单例名称对应的`toml`配置文件在配置目录中存在时,将自动设置该单例对象的默认配置文件为该文件。例如:`g.Cfg("redis")`获取到的单例对象将会默认去检索并设置默认的配置文件为`redis.toml`,当该文件不存在时,则使用默认的配置文件(`config.toml`):https://goframe.org/net/ghttp/config
|
||||
- 完善单元测试。
|
||||
|
||||
1. `gview`
|
||||
- 新增`concat`内置字符串拼接方法:https://goframe.org/os/gview/function/buildin
|
||||
- 完善单元测试。
|
||||
|
||||
1. `gfile`
|
||||
- 改进`SelfPath`获取当前执行文件路径方法,提高执行效率。
|
||||
- 改进`Join`文件路径连接方法,防止多余的路径连接符号。
|
||||
- 改建`GetContents`文件内容获取方法执行效率,降低内存占用。
|
||||
- 新增`StrToSize`方法,用于将大小字符串转换为字节数字,大小字符串例如`10m`, `5KB`, `1.25Gib`等。
|
||||
- 新增`ReadLines/ReadByteLines`方法,用于按行读取指定文件内容,并给定读取回调函数。
|
||||
- 完善单元测试。
|
||||
|
||||
1. `gtime`
|
||||
- 改进`gtime.Time`对象实现,统一字符串打印时间格式为`Y-m-d H:i:s`,如:`2020-01-01 12:00:00`。
|
||||
|
||||
1. `gcmd`
|
||||
- 命令行解析方法增加`strict`参数,用于设置当前解析是否严格解析,严格解析下如果给定了非法的选项,将会停止解析并返回错误。默认情况下为非严格解析。
|
||||
|
||||
1. `genv`
|
||||
- 改进`Remove`删除环境变量键值对方法,增加对多个键值对环境变量的删除。
|
||||
|
||||
1. `gfpool`
|
||||
- 改进代码实现,提高效率。
|
||||
- 完善单元测试。
|
||||
|
||||
1. `gfsnotify`
|
||||
- 改进包初始化方法,当系统原因引起默认`Watcher`对象创建失败时,直接`panic`。
|
||||
|
||||
1. `gproc`
|
||||
- 改进`SearchBinaryPath`方法。
|
||||
- 改进`Process.Kill`方法在`windows`及`*niux`平台下不同表现的处理。
|
||||
|
||||
## `encoding`
|
||||
1. `gjson`
|
||||
- 代码改进、完善注释、新增大量代码示例。
|
||||
- 文档更新:
|
||||
- 基本介绍:https://goframe.org/encoding/gjson/index
|
||||
- 对象创建:https://goframe.org/encoding/gjson/object
|
||||
- 层级访问:https://goframe.org/encoding/gjson/pattern
|
||||
- Struct转换:https://goframe.org/encoding/gjson/conversion-struct
|
||||
- 动态创建修改:https://goframe.org/encoding/gjson/dataset
|
||||
- 数据格式转换:https://goframe.org/encoding/gjson/conversion-format
|
||||
|
||||
## `frame`
|
||||
1. `g`
|
||||
- 新增`IsNil`方法,用于判断当前给定的变量是否为`nil`,该方法有可能会使用到反射来实现判断。
|
||||
- 新增`IsEmpty`方法,用于判断当前给定的变量是否为`空`,该方法优先使用断言判断但有可能会使用到反射来实现判断。空值包括:`0, nil, false, "", len(slice/map/chan) == 0`。
|
||||
- 标记废弃`SetServerGraceful`方法,转而统一交给`Server`的配置来管理。
|
||||
1. `gins`
|
||||
- 改进代码结构,方便维护。
|
||||
- 改进、完善单元测试。
|
||||
1. `gmvc`
|
||||
- 新增`M`类型,为`*gdb.Model`的别名简称,用于工具链自动生成模型中的`M`属性。
|
||||
|
||||
## `text`
|
||||
1. `gstr`
|
||||
- 新增`HasPrefix/HasSuffix`方法。
|
||||
- 新增`OctStr`方法,用于将八进制字符串转换为其对应的`unicode`字符串,例如转换为中文。常用于`gRPC`底层通信编码中。
|
||||
- 完善单元测试。
|
||||
|
||||
## `debug`
|
||||
1. `gdebug`
|
||||
- 改进代码结构,方便维护。
|
||||
- 新增`TestDataPath`方法,用于当前包单元测试中获得当前包中`testdata`目录的绝对路径。
|
||||
|
||||
## `util`
|
||||
1. `gconv`
|
||||
- 改进`String`字符串转换方法,增加对`time.Time/*time.Time/gtime.Time`类型的内置支持。
|
||||
- 改进`Map/Struct`转换方法,增加对一些特殊场景的细节处理。经过大规模的使用,大量的反馈改进,不断完善了细节。
|
||||
- 改进`Struct`转换方法,增加对`UnmarshalValue(interface{}) error`接口的支持。
|
||||
- 完善单元测试。
|
||||
|
||||
1. `grand`
|
||||
- 注意:不兼容调整,原有的`Str`方法更名为`S`方法,用以获取指定长度的随机字符串、数字,并增加`symbol`参数,指定是否可以随机返回特殊可见字符。
|
||||
- 新增`Str`方法,用于从指定的字符串字符中随机获取指定长度的字符串。该方法同时支持`unicode`字符串,例如:中文:https://goframe.org/util/grand/index
|
||||
- 新增`Symbols`方法,用于随机返回指定场孤独的特殊可见字符:https://goframe.org/util/grand/index
|
||||
- 完善单元测试。
|
||||
|
||||
1. `gvalid`
|
||||
- 长度校验规则增加对`unicode`字符串的支持,例如:中文。
|
||||
|
||||
# Bug Fix
|
||||
1. 修复`Server`的视图对象配置失效问题。
|
||||
1. 修复`Server`中间件在中间件`panic`情况下,忽略`Middleware.Next`方法控制,导致鉴权中间件失效的问题。
|
||||
1. 修复`gudp.Server`在请求包大小超过`64bytes`时的断包问题,并调整默认缓冲区大小为`1024byte`,开发者可自定义缓冲区大小。
|
||||
1. 修复`gfile.MTimeMillisecond`方法返回错误的文件修改毫秒时间戳。
|
||||
1. 修复`gconv.Int64`对负数转换的支持。
|
||||
1. 其他一些修复。
|
||||
1. 详见:https://github.com/gogf/gf/issues?q=label%3Abug
|
||||
|
||||
|
||||
|
||||
# `v1.11.2` (2020-01-14)
|
||||
|
||||
`GF(Go Frame)` https://goframe.org 是一款模块化、高性能、生产级的Go基础开发框架。实现了比较完善的基础设施建设,包括常用的核心开发组件, 如:缓存、日志、文件、时间、队列、数组、集合、字符串、定时器、命令行、文件锁、内存锁、对象池、连接池、资源管理、数据校验、数据编码、文件监控、 定时任务、数据库ORM、TCP/UDP组件、进程管理/通信、并发安全容器等等。 并提供了Web服务开发的系列核心组件,如:Router、Cookie、Session、Middleware、服务注册、配置管理、模板引擎等等, 支持热重启、热更新、多域名、多端口、多服务、HTTPS、Rewrite等特性。
|
||||
|
||||
`GF`有着丰富的基础模块、完善的工具链、详尽的开发文档。开源近两年以来,`GF`得到越来越多小伙伴的肯定和支持,从寂寂无名到现在被广泛应用于微服务、物联网、区块链、电商系统、银行系统等企业级的生产项目中,经历了百万级、千万级项目的考验,2019年度被码云`gitee`评选为`GVP`最有价值开源项目。`GF`正在快速地成长中,目前保持着1-2个月迭代版本的发布规律,社区活跃,欢迎加入`GF`大家庭。
|
||||
|
||||
最后,祝大家2020新年快乐,鼠年大吉!
|
||||
|
||||
|
||||
|
||||
## 新特性
|
||||
|
||||
1. 新年新气象,官网文档大量更新:https://goframe.org/index
|
||||
1. `GF`工具链更新:https://goframe.org/toolchain/cli
|
||||
- 新增`gf run`热编译运行命令;
|
||||
- 新增`gf docker` Docker镜像编译命令;
|
||||
- 新增`gf gen model` 强大的模型自动生成命令;
|
||||
- `gf build`命令增加对配置文件配置支持;
|
||||
- 大量命令行工具改进工作;
|
||||
- 新增自动代理设置特性;
|
||||
1. 数据库`ORM`新特性:
|
||||
- 增加`prefix`数据表前缀支持:https://goframe.org/database/gdb/config
|
||||
- 新增`Schema`数据库对象并改进数据库切换特性:https://goframe.org/database/gdb/chaining/schema
|
||||
- 新增`WherePri`方法,用于自动识别主键的条件方法:https://goframe.org/database/gdb/chaining/select
|
||||
- 文档及示例大量更新,覆盖95%以上的功能特性;
|
||||
|
||||
|
||||
|
||||
## 功能改进
|
||||
|
||||
### `container`
|
||||
1. `garray`
|
||||
- 新增`New*ArrayRange`方法,用于初始化创建指定数值范围的数组。
|
||||
- 新增`Iterator*`方法,用于数组项元素回调遍历。
|
||||
- 完善单元测试。
|
||||
1. `gvar`
|
||||
- 改进`MapStrStr`、`MapStrStrDeep`方法实现。
|
||||
|
||||
### `net`
|
||||
1. `ghttp`
|
||||
- 改进HTTP客户端,增加对提交参数的自动`Content-Type`识别功能。
|
||||
- `Request`对象增加`Parse`方法,用于快捷的对象转换即参数校验。
|
||||
- `Request.GetPost*`方法全部标记为`deprecated`,统一客户端参数提交方式为`QueryString`, `Form`, `Body`。
|
||||
- 去掉`Response`模板解析时的`Get`/`Post`内置变量,新增`Query`, `Form`, `Request`内置变量:https://goframe.org/net/ghttp/response/template
|
||||
- 改进`Response.WriteJson*`及`Response.WriteXml*`方法,增加对`string`, `[]byte`类型参数的支持。
|
||||
- `Server`新增`GetRouterArray`方法,用于向应用层暴露并获取`Server`的路由列表。
|
||||
- `Server`新增`Use`方法,该方法为`BindMiddlewareDefault`的别名,用以全局中间件的注册。
|
||||
- `Server`新增`RouteOverWrite`配置项,用于控制是否在注册路由冲突时自动覆盖,默认关闭并提示。
|
||||
- `Server`新增`Graceful`配置项,用于在单服务场景下控制平滑重启特性的开启/关闭,默认开启。
|
||||
- 完善单元测试。
|
||||
1. `gtcp`
|
||||
- 改进简单协议下的数据包发送接收功能。
|
||||
- 将连接池默认的缓存过期时间`30`秒修改为`10`秒。
|
||||
- 完善单元测试。
|
||||
|
||||
### `database`
|
||||
1. `gdb`
|
||||
- 新增`As`数据表别名方法。
|
||||
- 改进数据表、字段的安全字符自动识别添加功能。
|
||||
- 新增`DB`数据库对象切换方法。
|
||||
- 新增`TX`链式操作事务支持方法。
|
||||
- 完善单元测试。
|
||||
### `os`
|
||||
1. `gcfg`
|
||||
- 新增`GetMapStrStr`方法。
|
||||
1. `gcmd`
|
||||
- 增加参数解析的`strict`严格参数,默认严格解析,不存在指定参数/选项名称时则报错返回。
|
||||
1. `genv`
|
||||
- 改进`Remove`方法支持多个环境变量的删除。
|
||||
1. `gfile`
|
||||
- 改进`TempDir`临时目录获取方法,在`*nix`系统下默认为`/tmp`目录。
|
||||
- 新增`ReadLines`, `ReadByteLines`方法,用以按行回调读取文件内容。
|
||||
- 新增`Copy*`方法,用以文件/目录的拷贝,支持递归。
|
||||
- 新增`Replace*`方法,用以目录下的文件内容替换,支持递归。
|
||||
- 改进`Scan*`方法,用以检索并返回指定目录下的所有文件/目录,支持文件模式指定,支持递归。
|
||||
- 完善单元测试。
|
||||
1. `gproc`
|
||||
- 改进命令行运行方法。
|
||||
- 改进`Shell`命令文件检索逻辑。
|
||||
- 改进实验性的进程间通信设计。
|
||||
1. `gtime`
|
||||
- 将包方法以及`Time`对象的时间戳方法`Second`, `Millisecond`, `Microsecond`, `Nanosecond`标记为废除,
|
||||
并新增`Timestamp`, `TimestampMilli`, `TimestampMicro`, `TimestampNano`替换。
|
||||
- 需要注意的是以上修改可能和老版本存在兼容性问题。
|
||||
|
||||
1. `gview`
|
||||
- 解析功能、缓存设计改进。
|
||||
- 新增`encode`, `decode`HTML编码/解码模板函数。
|
||||
- 新增`concat`字符串拼接模板函数。
|
||||
- 新增`dump`模板函数,功能类似于`g.Dump`方法。
|
||||
- 新增`AutoEncode`配置项,用于自动转码输出的`HTML`内容,常用于防止`XSS`,默认关闭。需要注意的是该特性并不会影响`include`内置函数: https://goframe.org/os/gview/xss
|
||||
- 单元测试完善。
|
||||
|
||||
### `crypto`
|
||||
1. `gmd5`
|
||||
- 增加`MustEncrypt`, `MustEncryptBytes`, `MustEncryptString`, `MustEncryptFile`方法。
|
||||
1. `gsha1`
|
||||
- 增加`MustEncryptFile`方法
|
||||
|
||||
### `encoding`
|
||||
1. `gbase64`
|
||||
- 新增`MustEncodeFile`, `MustEncodeFileToString`, `MustDecode`, `MustDecodeToString`方法。
|
||||
1. `gjson`/`gparser`
|
||||
- 新增`GetMapStrStr`方法。
|
||||
- 新增`Must*`方法,用于指定数据格式的转换失败时产生`panic`错误,而不会返回`error`参数。
|
||||
|
||||
### `util`
|
||||
1. `gconv`
|
||||
- 改进`Convert`方法增加对`[]int32`, `[]int64`, `[]uint`, `[]uint32`, `[]uint64`, `[]float32`, `[]float64`数据类型的转换支持。
|
||||
- 改进`String`字符串转换方法对指针参数的支持。
|
||||
- 改进`Map*` Map转换方法的代码结构及性能。
|
||||
- 新增`Floats`, `Float32s`, `Float64s`对`[]float32`, `[]float64`类型转换方法。
|
||||
- 新增`Ints`, `Int32s`, `Int64s`对`[]int`, `[]int32`, `[]int64`类型转换方法。
|
||||
- 新增`Uints`, `Uint32s`, `Uint64s`对`[]uint`, `[]uint32`, `[]uint64`类型转换方法。
|
||||
- 完善单元测试。
|
||||
|
||||
|
||||
### `frame`
|
||||
1. `gins`
|
||||
- 所有的单例对象在获取失败时产生`panic`错误。
|
||||
|
||||
## Bug Fix
|
||||
1. 增加对常见错误路由格式例如`/user//index`的兼容支持。
|
||||
1. 修复`gtcp`/`gudp`在数据接收时的间隔时间单位问题。
|
||||
1. 修复`gfile`/`gspath`/`gfsnotify`包对文件的存在性判断不严谨问题。
|
||||
1. 修复`gproc.Kill`方法在`windows`系统下的运行阻塞问题。
|
||||
1. 修复`gstr.TrimLeftStr`/`gstr.TrimRightStr`在被替换字符串长度小于替换字符串长度时的数组溢出问题。
|
||||
|
||||
|
||||
|
||||
# `v1.10.0` (2019-12-05)
|
||||
|
||||
各位`gfer`久等了,较上一次发布时间过去已有两个多月了,这段时间`GF`也在不断地迭代改进,细节比较多,拟了个大概,以下是`release log`。
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
> This markdown is deprecated and will be removed in future. All TODO features are staged in the issue: https://github.com/gogf/gf/issues
|
||||
|
||||
# ON THE WAY
|
||||
1. 增加图形验证码支持,至少支持数字和英文字母;
|
||||
1. Cookie&Session数据池化处理;
|
||||
@ -10,7 +12,6 @@
|
||||
1. gjson对大json数据的解析效率问题;
|
||||
1. ghttp增加route name特性,并同时支持backend和template(提供内置函数)引用,可以通过RedirectRoute方法给定route name和路由参数跳转到指定的路由地址上;
|
||||
1. gvalid校验支持当第一个规则失败后便不再校验后续的规则,最好做成链式操作;
|
||||
1. gvalid增加支持对[]rune的长度校验(一个中文占3个字节);
|
||||
1. ghttp.Request增加对输入参数的自动HtmlEncode机制;
|
||||
1. 常量命名风格根据golint进行修改;
|
||||
1. 开放rwmutex包,并将gjson的互斥锁使用自定义的mutex替换;
|
||||
@ -21,8 +22,6 @@
|
||||
- ghttp Server&Client basic auth、
|
||||
- glog分类&日志等级&链式操作、gdb debug自动输出调试信息、gmlock内存锁、
|
||||
1. 服务注册域名增加对泛域名的支持;
|
||||
1. Cookie设置中文失效问题;
|
||||
1. 使用gconv将slice映射到struct属性上,例如redis hscan的结果集;
|
||||
1. 项目参考:
|
||||
- https://github.com/namreg/godown
|
||||
- https://github.com/Masterminds/sprig
|
||||
@ -31,32 +30,36 @@
|
||||
1. 路由增加不区分大小写得匹配方式;
|
||||
1. 改进WebServer获取POST参数处理逻辑,当提交非form数据时,例如json数据,针对某些方法可以直接解析;
|
||||
1. WebServer增加可选择的路由覆盖配置,默认情况下不覆盖;
|
||||
1. grpool性能压测结果变慢的问题;
|
||||
1. 增加jumplist的数据结构容器;
|
||||
1. DelayQueue/PriorityQueue;
|
||||
1. 权限管理模块;
|
||||
1. 从ghttp中剥离SESSION功能构成单独的模块gsession;
|
||||
1. 改进gproc进程间通信处理逻辑,提高稳定性,以应对进程间大批量的数据发送/接收;
|
||||
1. ghttp的热重启的本地进程端口监听,在不使用该特性时默认关闭掉;
|
||||
1. gtcp增加对TLS加密通信的支持;
|
||||
1. 添加Save/Replace/BatchSave/BatchReplace方法对sqlite数据库的支持;
|
||||
1. 添加sqlite数据库的单元测试用例;
|
||||
1. gredis增加cluster支持;
|
||||
1. gset.Add/Remove/Contains方法增加批量操作支持;
|
||||
1. gmlock增加手动清理机制:当内存锁不再使用时,由调用端决定是否清理内存锁;
|
||||
1. gtimer增加DelayAdd*方法返回Entry对象,以便DelayAdd*的定时任务也能进行状态控制;gcron同理需要改进;
|
||||
1. 改进gdb对pgsql/mssql/oracle的支持,使用方法覆盖的方式改进操作,而不是完全依靠正则替换的方式;
|
||||
1. gdb的Cache缓存功能增加可自定义缓存接口,以便支持外部缓存功能,缓存接口可以通过io.ReadWriter接口实现;
|
||||
1. grpool增加支持阻塞添加任务接口;
|
||||
1. gdb.Model在链式安全的对象创建中增加sync.Pool的使用;
|
||||
1. 增加g.Table快捷方法以方便操作数据表,但是得考虑后续模型操作设计,特别是脚手架的模型管理;
|
||||
1. 改进ghttp分组路由中对hook的支持方式,以便格式与BindHookHandler统一;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# DONE
|
||||
1. Cookie设置中文失效问题;
|
||||
1. gvalid增加支持对[]rune的长度校验(一个中文占3个字节);
|
||||
1. grpool性能压测结果变慢的问题;
|
||||
1. ghttp的热重启的本地进程端口监听,在不使用该特性时默认关闭掉;
|
||||
1. gtcp增加对TLS加密通信的支持;
|
||||
1. 改进gdb对pgsql/mssql/oracle的支持,使用方法覆盖的方式改进操作,而不是完全依靠正则替换的方式;
|
||||
1. gdb的Cache缓存功能增加可自定义缓存接口,以便支持外部缓存功能,缓存接口可以通过io.ReadWriter接口实现;
|
||||
1. 改进ghttp分组路由中对hook的支持方式,以便格式与BindHookHandler统一;
|
||||
1. 使用gconv将slice映射到struct属性上,例如redis hscan的结果集;
|
||||
1. gconv完善针对不同类型的判断,例如:尽量减少sprintf("%v", xxx)来执行string类型的转换;
|
||||
2. ghttp.Server请求执行中增加服务退出的方法,不再执行后续操作;
|
||||
3. ghttp.Response对象完善并改进数据返回方法(Write/WriteString);
|
||||
@ -4,5 +4,5 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package garray provides concurrent-safe/unsafe arrays.
|
||||
// Package garray provides most commonly used array containers which also support concurrent-safe/unsafe switch feature.
|
||||
package garray
|
||||
|
||||
@ -8,6 +8,12 @@ package garray
|
||||
|
||||
import "strings"
|
||||
|
||||
// apiInterfaces is used for type assert api for Interfaces.
|
||||
type apiInterfaces interface {
|
||||
Interfaces() []interface{}
|
||||
}
|
||||
|
||||
// defaultComparatorInt for int comparison.
|
||||
func defaultComparatorInt(a, b int) int {
|
||||
if a < b {
|
||||
return -1
|
||||
@ -18,6 +24,7 @@ func defaultComparatorInt(a, b int) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// defaultComparatorStr for string comparison.
|
||||
func defaultComparatorStr(a, b string) int {
|
||||
return strings.Compare(a, b)
|
||||
}
|
||||
|
||||
@ -8,8 +8,10 @@ package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"math"
|
||||
"sort"
|
||||
@ -19,8 +21,11 @@ import (
|
||||
"github.com/gogf/gf/util/grand"
|
||||
)
|
||||
|
||||
// Array is a golang array with rich features.
|
||||
// It contains a concurrent-safe/unsafe switch, which should be set
|
||||
// when its initialization and cannot be changed then.
|
||||
type Array struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
array []interface{}
|
||||
}
|
||||
|
||||
@ -41,7 +46,7 @@ func NewArray(safe ...bool) *Array {
|
||||
// which is false in default.
|
||||
func NewArraySize(size int, cap int, safe ...bool) *Array {
|
||||
return &Array{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: make([]interface{}, size, cap),
|
||||
}
|
||||
}
|
||||
@ -76,7 +81,7 @@ func NewFromCopy(array []interface{}, safe ...bool) *Array {
|
||||
// which is false in default.
|
||||
func NewArrayFrom(array []interface{}, safe ...bool) *Array {
|
||||
return &Array{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: array,
|
||||
}
|
||||
}
|
||||
@ -88,26 +93,31 @@ func NewArrayFromCopy(array []interface{}, safe ...bool) *Array {
|
||||
newArray := make([]interface{}, len(array))
|
||||
copy(newArray, array)
|
||||
return &Array{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: newArray,
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the value of the specified index,
|
||||
// the caller should notice the boundary of the array.
|
||||
func (a *Array) Get(index int) interface{} {
|
||||
// Get returns the value by the specified index.
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
func (a *Array) Get(index int) (value interface{}, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
value := a.array[index]
|
||||
return value
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return nil, false
|
||||
}
|
||||
return a.array[index], true
|
||||
}
|
||||
|
||||
// Set sets value to specified index.
|
||||
func (a *Array) Set(index int, value interface{}) *Array {
|
||||
func (a *Array) Set(index int, value interface{}) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
}
|
||||
a.array[index] = value
|
||||
return a
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetArray sets the underlying slice array with the given <array>.
|
||||
@ -153,45 +163,70 @@ func (a *Array) SortFunc(less func(v1, v2 interface{}) bool) *Array {
|
||||
}
|
||||
|
||||
// InsertBefore inserts the <value> to the front of <index>.
|
||||
func (a *Array) InsertBefore(index int, value interface{}) *Array {
|
||||
func (a *Array) InsertBefore(index int, value interface{}) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
}
|
||||
rear := append([]interface{}{}, a.array[index:]...)
|
||||
a.array = append(a.array[0:index], value)
|
||||
a.array = append(a.array, rear...)
|
||||
return a
|
||||
return nil
|
||||
}
|
||||
|
||||
// InsertAfter inserts the <value> to the back of <index>.
|
||||
func (a *Array) InsertAfter(index int, value interface{}) *Array {
|
||||
func (a *Array) InsertAfter(index int, value interface{}) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
}
|
||||
rear := append([]interface{}{}, a.array[index+1:]...)
|
||||
a.array = append(a.array[0:index+1], value)
|
||||
a.array = append(a.array, rear...)
|
||||
return a
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove removes an item by index.
|
||||
func (a *Array) Remove(index int) interface{} {
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
func (a *Array) Remove(index int) (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
// Determine array boundaries when deleting to improve deletion efficiency。
|
||||
return a.doRemoveWithoutLock(index)
|
||||
}
|
||||
|
||||
// doRemoveWithoutLock removes an item by index without lock.
|
||||
func (a *Array) doRemoveWithoutLock(index int) (value interface{}, found bool) {
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return nil, false
|
||||
}
|
||||
// Determine array boundaries when deleting to improve deletion efficiency.
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value
|
||||
return value, true
|
||||
} else if index == len(a.array)-1 {
|
||||
value := a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
// If it is a non-boundary delete,
|
||||
// it will involve the creation of an array,
|
||||
// then the deletion is less efficient.
|
||||
value := a.array[index]
|
||||
value = a.array[index]
|
||||
a.array = append(a.array[:index], a.array[index+1:]...)
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// RemoveValue removes an item by value.
|
||||
// It returns true if value is found in the array, or else false if not found.
|
||||
func (a *Array) RemoveValue(value interface{}) bool {
|
||||
if i := a.Search(value); i != -1 {
|
||||
a.Remove(i)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PushLeft pushes one or multiple items to the beginning of array.
|
||||
@ -212,52 +247,68 @@ func (a *Array) PushRight(value ...interface{}) *Array {
|
||||
}
|
||||
|
||||
// PopRand randomly pops and return an item out of array.
|
||||
func (a *Array) PopRand() interface{} {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
func (a *Array) PopRand() (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands randomly pops and returns <size> items out of array.
|
||||
func (a *Array) PopRands(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size > len(a.array) {
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
if size >= len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
array := make([]interface{}, size)
|
||||
for i := 0; i < size; i++ {
|
||||
index := grand.Intn(len(a.array))
|
||||
array[i] = a.array[index]
|
||||
a.array = append(a.array[:index], a.array[index+1:]...)
|
||||
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// PopLeft pops and returns an item from the beginning of array.
|
||||
func (a *Array) PopLeft() interface{} {
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
func (a *Array) PopLeft() (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
value := a.array[0]
|
||||
if len(a.array) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
value = a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// PopRight pops and returns an item from the end of array.
|
||||
func (a *Array) PopRight() interface{} {
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
func (a *Array) PopRight() (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
value := a.array[index]
|
||||
if index < 0 {
|
||||
return nil, false
|
||||
}
|
||||
value = a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// PopLefts pops and returns <size> items from the beginning of array.
|
||||
func (a *Array) PopLefts(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
length := len(a.array)
|
||||
if size > length {
|
||||
size = length
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
if size >= len(a.array) {
|
||||
array := a.array
|
||||
a.array = a.array[:0]
|
||||
return array
|
||||
}
|
||||
value := a.array[0:size]
|
||||
a.array = a.array[size:]
|
||||
@ -268,9 +319,14 @@ func (a *Array) PopLefts(size int) []interface{} {
|
||||
func (a *Array) PopRights(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
index := len(a.array) - size
|
||||
if index < 0 {
|
||||
index = 0
|
||||
if index <= 0 {
|
||||
array := a.array
|
||||
a.array = a.array[:0]
|
||||
return array
|
||||
}
|
||||
value := a.array[index:]
|
||||
a.array = a.array[:index]
|
||||
@ -418,10 +474,11 @@ func (a *Array) Contains(value interface{}) bool {
|
||||
// Search searches array by <value>, returns the index of <value>,
|
||||
// or returns -1 if not exists.
|
||||
func (a *Array) Search(value interface{}) int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return -1
|
||||
}
|
||||
a.mu.RLock()
|
||||
result := -1
|
||||
for index, v := range a.array {
|
||||
if v == value {
|
||||
@ -429,18 +486,19 @@ func (a *Array) Search(value interface{}) int {
|
||||
break
|
||||
}
|
||||
}
|
||||
a.mu.RUnlock()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Unique uniques the array, clear repeated items.
|
||||
// Example: [1,1,2,3,2] -> [1,2,3]
|
||||
func (a *Array) Unique() *Array {
|
||||
a.mu.Lock()
|
||||
for i := 0; i < len(a.array)-1; i++ {
|
||||
for j := i + 1; j < len(a.array); j++ {
|
||||
for j := i + 1; j < len(a.array); {
|
||||
if a.array[i] == a.array[j] {
|
||||
a.array = append(a.array[:j], a.array[j+1:]...)
|
||||
} else {
|
||||
j++
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -469,32 +527,16 @@ func (a *Array) RLockFunc(f func(array []interface{})) *Array {
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *Array) Merge(array interface{}) *Array {
|
||||
switch v := array.(type) {
|
||||
case *Array:
|
||||
a.Append(gconv.Interfaces(v.Slice())...)
|
||||
case *IntArray:
|
||||
a.Append(gconv.Interfaces(v.Slice())...)
|
||||
case *StrArray:
|
||||
a.Append(gconv.Interfaces(v.Slice())...)
|
||||
case *SortedArray:
|
||||
a.Append(gconv.Interfaces(v.Slice())...)
|
||||
case *SortedIntArray:
|
||||
a.Append(gconv.Interfaces(v.Slice())...)
|
||||
case *SortedStrArray:
|
||||
a.Append(gconv.Interfaces(v.Slice())...)
|
||||
default:
|
||||
a.Append(gconv.Interfaces(array)...)
|
||||
}
|
||||
return a
|
||||
return a.Append(gconv.Interfaces(array)...)
|
||||
}
|
||||
|
||||
// Fill fills an array with num entries of the value <value>,
|
||||
// keys starting at the <startIndex> parameter.
|
||||
func (a *Array) Fill(startIndex int, num int, value interface{}) *Array {
|
||||
func (a *Array) Fill(startIndex int, num int, value interface{}) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if startIndex < 0 {
|
||||
startIndex = 0
|
||||
if startIndex < 0 || startIndex > len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", startIndex, len(a.array)))
|
||||
}
|
||||
for i := startIndex; i < startIndex+num; i++ {
|
||||
if i > len(a.array)-1 {
|
||||
@ -503,7 +545,7 @@ func (a *Array) Fill(startIndex int, num int, value interface{}) *Array {
|
||||
a.array[i] = value
|
||||
}
|
||||
}
|
||||
return a
|
||||
return nil
|
||||
}
|
||||
|
||||
// Chunk splits an array into multiple arrays,
|
||||
@ -557,27 +599,27 @@ func (a *Array) Pad(size int, val interface{}) *Array {
|
||||
}
|
||||
|
||||
// Rand randomly returns one item from array(no deleting).
|
||||
func (a *Array) Rand() interface{} {
|
||||
func (a *Array) Rand() (value interface{}, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return a.array[grand.Intn(len(a.array))]
|
||||
if len(a.array) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
return a.array[grand.Intn(len(a.array))], true
|
||||
}
|
||||
|
||||
// Rands randomly returns <size> items from array(no deleting).
|
||||
func (a *Array) Rands(size int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if size > len(a.array) {
|
||||
size = len(a.array)
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
n := make([]interface{}, size)
|
||||
for i, v := range grand.Perm(len(a.array)) {
|
||||
n[i] = a.array[v]
|
||||
if i == size-1 {
|
||||
break
|
||||
}
|
||||
array := make([]interface{}, size)
|
||||
for i := 0; i < size; i++ {
|
||||
array[i] = a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
return n
|
||||
return array
|
||||
}
|
||||
|
||||
// Shuffle randomly shuffles the array.
|
||||
@ -604,6 +646,9 @@ func (a *Array) Reverse() *Array {
|
||||
func (a *Array) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return ""
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(gconv.String(v))
|
||||
@ -630,7 +675,7 @@ func (a *Array) Iterator(f func(k int, v interface{}) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array in ascending order with given callback function <f>.
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *Array) IteratorAsc(f func(k int, v interface{}) bool) {
|
||||
a.mu.RLock()
|
||||
@ -642,7 +687,7 @@ func (a *Array) IteratorAsc(f func(k int, v interface{}) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the array in descending order with given callback function <f>.
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *Array) IteratorDesc(f func(k int, v interface{}) bool) {
|
||||
a.mu.RLock()
|
||||
@ -677,7 +722,8 @@ func (a *Array) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *Array) MarshalJSON() ([]byte, error) {
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (a Array) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
@ -685,8 +731,7 @@ func (a *Array) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (a *Array) UnmarshalJSON(b []byte) error {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
if a.array == nil {
|
||||
a.array = make([]interface{}, 0)
|
||||
}
|
||||
a.mu.Lock()
|
||||
@ -696,3 +741,60 @@ func (a *Array) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for array.
|
||||
func (a *Array) UnmarshalValue(value interface{}) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
return json.Unmarshal(gconv.Bytes(value), &a.array)
|
||||
default:
|
||||
a.array = gconv.SliceAny(value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FilterNil removes all nil value of the array.
|
||||
func (a *Array) FilterNil() *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i := 0; i < len(a.array); {
|
||||
if empty.IsNil(a.array[i]) {
|
||||
a.array = append(a.array[:i], a.array[i+1:]...)
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// FilterEmpty removes all empty value of the array.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (a *Array) FilterEmpty() *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i := 0; i < len(a.array); {
|
||||
if empty.IsEmpty(a.array[i]) {
|
||||
a.array = append(a.array[:i], a.array[i+1:]...)
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of array.
|
||||
func (a *Array) Walk(f func(value interface{}) interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i, v := range a.array {
|
||||
a.array[i] = f(v)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the array is empty.
|
||||
func (a *Array) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
}
|
||||
|
||||
@ -8,8 +8,9 @@ package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
@ -18,8 +19,11 @@ import (
|
||||
"github.com/gogf/gf/util/grand"
|
||||
)
|
||||
|
||||
// IntArray is a golang int array with rich features.
|
||||
// It contains a concurrent-safe/unsafe switch, which should be set
|
||||
// when its initialization and cannot be changed then.
|
||||
type IntArray struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
array []int
|
||||
}
|
||||
|
||||
@ -35,7 +39,7 @@ func NewIntArray(safe ...bool) *IntArray {
|
||||
// which is false in default.
|
||||
func NewIntArraySize(size int, cap int, safe ...bool) *IntArray {
|
||||
return &IntArray{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: make([]int, size, cap),
|
||||
}
|
||||
}
|
||||
@ -60,7 +64,7 @@ func NewIntArrayRange(start, end, step int, safe ...bool) *IntArray {
|
||||
// which is false in default.
|
||||
func NewIntArrayFrom(array []int, safe ...bool) *IntArray {
|
||||
return &IntArray{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: array,
|
||||
}
|
||||
}
|
||||
@ -72,26 +76,31 @@ func NewIntArrayFromCopy(array []int, safe ...bool) *IntArray {
|
||||
newArray := make([]int, len(array))
|
||||
copy(newArray, array)
|
||||
return &IntArray{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: newArray,
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the value of the specified index,
|
||||
// the caller should notice the boundary of the array.
|
||||
func (a *IntArray) Get(index int) int {
|
||||
// Get returns the value by the specified index.
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
func (a *IntArray) Get(index int) (value int, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
value := a.array[index]
|
||||
return value
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return 0, false
|
||||
}
|
||||
return a.array[index], true
|
||||
}
|
||||
|
||||
// Set sets value to specified index.
|
||||
func (a *IntArray) Set(index int, value int) *IntArray {
|
||||
func (a *IntArray) Set(index int, value int) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
}
|
||||
a.array[index] = value
|
||||
return a
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetArray sets the underlying slice array with the given <array>.
|
||||
@ -127,8 +136,7 @@ func (a *IntArray) Sum() (sum int) {
|
||||
}
|
||||
|
||||
// Sort sorts the array in increasing order.
|
||||
// The parameter <reverse> controls whether sort
|
||||
// in increasing order(default) or decreasing order
|
||||
// The parameter <reverse> controls whether sort in increasing order(default) or decreasing order.
|
||||
func (a *IntArray) Sort(reverse ...bool) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -156,45 +164,70 @@ func (a *IntArray) SortFunc(less func(v1, v2 int) bool) *IntArray {
|
||||
}
|
||||
|
||||
// InsertBefore inserts the <value> to the front of <index>.
|
||||
func (a *IntArray) InsertBefore(index int, value int) *IntArray {
|
||||
func (a *IntArray) InsertBefore(index int, value int) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
}
|
||||
rear := append([]int{}, a.array[index:]...)
|
||||
a.array = append(a.array[0:index], value)
|
||||
a.array = append(a.array, rear...)
|
||||
return a
|
||||
return nil
|
||||
}
|
||||
|
||||
// InsertAfter inserts the <value> to the back of <index>.
|
||||
func (a *IntArray) InsertAfter(index int, value int) *IntArray {
|
||||
func (a *IntArray) InsertAfter(index int, value int) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
}
|
||||
rear := append([]int{}, a.array[index+1:]...)
|
||||
a.array = append(a.array[0:index+1], value)
|
||||
a.array = append(a.array, rear...)
|
||||
return a
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove removes an item by index.
|
||||
func (a *IntArray) Remove(index int) int {
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
func (a *IntArray) Remove(index int) (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(index)
|
||||
}
|
||||
|
||||
// doRemoveWithoutLock removes an item by index without lock.
|
||||
func (a *IntArray) doRemoveWithoutLock(index int) (value int, found bool) {
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return 0, false
|
||||
}
|
||||
// Determine array boundaries when deleting to improve deletion efficiency.
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value
|
||||
return value, true
|
||||
} else if index == len(a.array)-1 {
|
||||
value := a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
// If it is a non-boundary delete,
|
||||
// it will involve the creation of an array,
|
||||
// then the deletion is less efficient.
|
||||
value := a.array[index]
|
||||
value = a.array[index]
|
||||
a.array = append(a.array[:index], a.array[index+1:]...)
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// RemoveValue removes an item by value.
|
||||
// It returns true if value is found in the array, or else false if not found.
|
||||
func (a *IntArray) RemoveValue(value int) bool {
|
||||
if i := a.Search(value); i != -1 {
|
||||
_, found := a.Remove(i)
|
||||
return found
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PushLeft pushes one or multiple items to the beginning of array.
|
||||
@ -215,52 +248,72 @@ func (a *IntArray) PushRight(value ...int) *IntArray {
|
||||
}
|
||||
|
||||
// PopLeft pops and returns an item from the beginning of array.
|
||||
func (a *IntArray) PopLeft() int {
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
func (a *IntArray) PopLeft() (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
value := a.array[0]
|
||||
if len(a.array) == 0 {
|
||||
return 0, false
|
||||
}
|
||||
value = a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// PopRight pops and returns an item from the end of array.
|
||||
func (a *IntArray) PopRight() int {
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
func (a *IntArray) PopRight() (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
value := a.array[index]
|
||||
if index < 0 {
|
||||
return 0, false
|
||||
}
|
||||
value = a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// PopRand randomly pops and return an item out of array.
|
||||
func (a *IntArray) PopRand() int {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
func (a *IntArray) PopRand() (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands randomly pops and returns <size> items out of array.
|
||||
// If the given <size> is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given <size> <= 0 or the array is empty, it returns nil.
|
||||
func (a *IntArray) PopRands(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size > len(a.array) {
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
if size >= len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
array := make([]int, size)
|
||||
for i := 0; i < size; i++ {
|
||||
index := grand.Intn(len(a.array))
|
||||
array[i] = a.array[index]
|
||||
a.array = append(a.array[:index], a.array[index+1:]...)
|
||||
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// PopLefts pops and returns <size> items from the beginning of array.
|
||||
// If the given <size> is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given <size> <= 0 or the array is empty, it returns nil.
|
||||
func (a *IntArray) PopLefts(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
length := len(a.array)
|
||||
if size > length {
|
||||
size = length
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
if size >= len(a.array) {
|
||||
array := a.array
|
||||
a.array = a.array[:0]
|
||||
return array
|
||||
}
|
||||
value := a.array[0:size]
|
||||
a.array = a.array[size:]
|
||||
@ -268,12 +321,19 @@ func (a *IntArray) PopLefts(size int) []int {
|
||||
}
|
||||
|
||||
// PopRights pops and returns <size> items from the end of array.
|
||||
// If the given <size> is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given <size> <= 0 or the array is empty, it returns nil.
|
||||
func (a *IntArray) PopRights(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
index := len(a.array) - size
|
||||
if index < 0 {
|
||||
index = 0
|
||||
if index <= 0 {
|
||||
array := a.array
|
||||
a.array = a.array[:0]
|
||||
return array
|
||||
}
|
||||
value := a.array[index:]
|
||||
a.array = a.array[:index]
|
||||
@ -430,10 +490,11 @@ func (a *IntArray) Contains(value int) bool {
|
||||
// Search searches array by <value>, returns the index of <value>,
|
||||
// or returns -1 if not exists.
|
||||
func (a *IntArray) Search(value int) int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return -1
|
||||
}
|
||||
a.mu.RLock()
|
||||
result := -1
|
||||
for index, v := range a.array {
|
||||
if v == value {
|
||||
@ -441,18 +502,19 @@ func (a *IntArray) Search(value int) int {
|
||||
break
|
||||
}
|
||||
}
|
||||
a.mu.RUnlock()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Unique uniques the array, clear repeated items.
|
||||
// Example: [1,1,2,3,2] -> [1,2,3]
|
||||
func (a *IntArray) Unique() *IntArray {
|
||||
a.mu.Lock()
|
||||
for i := 0; i < len(a.array)-1; i++ {
|
||||
for j := i + 1; j < len(a.array); j++ {
|
||||
for j := i + 1; j < len(a.array); {
|
||||
if a.array[i] == a.array[j] {
|
||||
a.array = append(a.array[:j], a.array[j+1:]...)
|
||||
} else {
|
||||
j++
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -481,32 +543,16 @@ func (a *IntArray) RLockFunc(f func(array []int)) *IntArray {
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *IntArray) Merge(array interface{}) *IntArray {
|
||||
switch v := array.(type) {
|
||||
case *Array:
|
||||
a.Append(gconv.Ints(v.Slice())...)
|
||||
case *IntArray:
|
||||
a.Append(gconv.Ints(v.Slice())...)
|
||||
case *StrArray:
|
||||
a.Append(gconv.Ints(v.Slice())...)
|
||||
case *SortedArray:
|
||||
a.Append(gconv.Ints(v.Slice())...)
|
||||
case *SortedIntArray:
|
||||
a.Append(gconv.Ints(v.Slice())...)
|
||||
case *SortedStrArray:
|
||||
a.Append(gconv.Ints(v.Slice())...)
|
||||
default:
|
||||
a.Append(gconv.Ints(array)...)
|
||||
}
|
||||
return a
|
||||
return a.Append(gconv.Ints(array)...)
|
||||
}
|
||||
|
||||
// Fill fills an array with num entries of the value <value>,
|
||||
// keys starting at the <startIndex> parameter.
|
||||
func (a *IntArray) Fill(startIndex int, num int, value int) *IntArray {
|
||||
func (a *IntArray) Fill(startIndex int, num int, value int) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if startIndex < 0 {
|
||||
startIndex = 0
|
||||
if startIndex < 0 || startIndex > len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", startIndex, len(a.array)))
|
||||
}
|
||||
for i := startIndex; i < startIndex+num; i++ {
|
||||
if i > len(a.array)-1 {
|
||||
@ -515,7 +561,7 @@ func (a *IntArray) Fill(startIndex int, num int, value int) *IntArray {
|
||||
a.array[i] = value
|
||||
}
|
||||
}
|
||||
return a
|
||||
return nil
|
||||
}
|
||||
|
||||
// Chunk splits an array into multiple arrays,
|
||||
@ -569,27 +615,27 @@ func (a *IntArray) Pad(size int, value int) *IntArray {
|
||||
}
|
||||
|
||||
// Rand randomly returns one item from array(no deleting).
|
||||
func (a *IntArray) Rand() int {
|
||||
func (a *IntArray) Rand() (value int, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return a.array[grand.Intn(len(a.array))]
|
||||
if len(a.array) == 0 {
|
||||
return 0, false
|
||||
}
|
||||
return a.array[grand.Intn(len(a.array))], true
|
||||
}
|
||||
|
||||
// Rands randomly returns <size> items from array(no deleting).
|
||||
func (a *IntArray) Rands(size int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if size > len(a.array) {
|
||||
size = len(a.array)
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
n := make([]int, size)
|
||||
for i, v := range grand.Perm(len(a.array)) {
|
||||
n[i] = a.array[v]
|
||||
if i == size-1 {
|
||||
break
|
||||
}
|
||||
array := make([]int, size)
|
||||
for i := 0; i < size; i++ {
|
||||
array[i] = a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
return n
|
||||
return array
|
||||
}
|
||||
|
||||
// Shuffle randomly shuffles the array.
|
||||
@ -616,6 +662,9 @@ func (a *IntArray) Reverse() *IntArray {
|
||||
func (a *IntArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return ""
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(gconv.String(v))
|
||||
@ -642,7 +691,7 @@ func (a *IntArray) Iterator(f func(k int, v int) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array in ascending order with given callback function <f>.
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *IntArray) IteratorAsc(f func(k int, v int) bool) {
|
||||
a.mu.RLock()
|
||||
@ -654,7 +703,7 @@ func (a *IntArray) IteratorAsc(f func(k int, v int) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the array in descending order with given callback function <f>.
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *IntArray) IteratorDesc(f func(k int, v int) bool) {
|
||||
a.mu.RLock()
|
||||
@ -672,7 +721,8 @@ func (a *IntArray) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *IntArray) MarshalJSON() ([]byte, error) {
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (a IntArray) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
@ -680,8 +730,7 @@ func (a *IntArray) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (a *IntArray) UnmarshalJSON(b []byte) error {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
if a.array == nil {
|
||||
a.array = make([]int, 0)
|
||||
}
|
||||
a.mu.Lock()
|
||||
@ -691,3 +740,45 @@ func (a *IntArray) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for array.
|
||||
func (a *IntArray) UnmarshalValue(value interface{}) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
return json.Unmarshal(gconv.Bytes(value), &a.array)
|
||||
default:
|
||||
a.array = gconv.SliceInt(value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FilterEmpty removes all zero value of the array.
|
||||
func (a *IntArray) FilterEmpty() *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i := 0; i < len(a.array); {
|
||||
if a.array[i] == 0 {
|
||||
a.array = append(a.array[:i], a.array[i+1:]...)
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of array.
|
||||
func (a *IntArray) Walk(f func(value int) int) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i, v := range a.array {
|
||||
a.array[i] = f(v)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the array is empty.
|
||||
func (a *IntArray) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user