diff --git a/net/src/test/go/util/sliceutil/slice.go b/net/src/test/go/util/arrayutil/array.go similarity index 60% rename from net/src/test/go/util/sliceutil/slice.go rename to net/src/test/go/util/arrayutil/array.go index 5f012091..8fee2c35 100644 --- a/net/src/test/go/util/sliceutil/slice.go +++ b/net/src/test/go/util/arrayutil/array.go @@ -9,9 +9,11 @@ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and limitations under the License. */ -package sliceutil +package arrayutil import ( + "math" + "strconv" "strings" ) @@ -218,3 +220,158 @@ func DifferenceInterface(slice1, slice2 []interface{}) []interface{} { } return n } + + + +// 合并数组 +func MergeArray(dest []interface{}, src []interface{}) (result []interface{}) { + result = make([]interface{}, len(dest)+len(src)) + copy(result, dest) + copy(result[len(dest):], src) + return +} + +// 删除数组 +func DeleteArray(src []interface{}, index int) (result []interface{}) { + result = append(src[:index], src[(index+1):]...) + return +} + +// []string => []int +func ArrayStr2Int(data []string) []int { + var ( + arr = make([]int, 0, len(data)) + ) + if len(data) == 0 { + return arr + } + for i, _ := range data { + var num, _ = strconv.Atoi(data[i]) + arr = append(arr, num) + } + return arr +} + +// []int => []string +func ArrayInt2Str(data []int) []string { + var ( + arr = make([]string, 0, len(data)) + ) + if len(data) == 0 { + return arr + } + for i, _ := range data { + arr = append(arr, strconv.Itoa(data[i])) + } + return arr +} + +// str[TrimSpace] in string list +func TrimSpaceStrInArray(str string, data []string) bool { + if len(data) > 0 { + for _, row := range data { + if str == strings.TrimSpace(row) { + return true + } + } + } + return false +} + +// str in string list +func StrInArray(str string, data []string) bool { + if len(data) > 0 { + for _, row := range data { + if str == row { + return true + } + } + } + return false +} + +// str in int list +func IntInArray(num int, data []int) bool { + if len(data) > 0 { + for _, row := range data { + if num == row { + return true + } + } + } + return false +} + + +var defSep = "_" + +/** +笛卡尔组合 +测试用例 +cart := [][]string{ + {"a1", "a2"}, + {"b1", "b2"}, +} +CartCombine(cart) + */ +func CartCombine(data [][]string, sep string) []string { + var _sep = defSep + if sep != "" { + _sep = sep + } + var _r []string + lens := func(i int) int { return len(data[i]) } + for i := make([]int, len(data)); i[0] < lens(0); next(i, lens) { + var r []string + for j, k := range i { + r = append(r, data[j][k]) + } + _r = append(_r, strings.Join(r, _sep)) + } + return _r +} + +func next(i []int, lens func(i int) int) { + for j := len(i) - 1; j >= 0; j-- { + i[j]++ + if j == 0 || i[j] < lens(j) { + return + } + i[j] = 0 + } +} + + +const ( + TOTAL_PAGE_FIELD = "total_page" + PAGE_FIELD = "page" + ROWS_FIELD = "rows" + TOTAL_RECORD_FIELD = "total_record" +) + +/** + page 当前页 + listRow 每页行数 + total 数据总数 + + 分页数据填充 返回 + => map[string]int + ["total_page"] => 1, + ["page"] => 1, + ["rows"] => 20, + ["total_record"] => 3, +*/ +func CommaPaginator(page int, listRow int, total int) map[string]int { + totalpages := int(math.Ceil(float64(total) / float64(listRow))) + if page <= 0 { + page = 1 + } + paginator := make(map[string]int) + paginator[TOTAL_PAGE_FIELD] = totalpages + paginator[PAGE_FIELD] = page + paginator[ROWS_FIELD] = listRow + paginator[TOTAL_RECORD_FIELD] = total + return paginator +} + + diff --git a/net/src/test/go/util/byteutil/byte.go b/net/src/test/go/util/byteutil/byte.go index 25db42c7..f49a4da5 100644 --- a/net/src/test/go/util/byteutil/byte.go +++ b/net/src/test/go/util/byteutil/byte.go @@ -14,7 +14,12 @@ package byteutil import ( "bytes" "encoding/binary" + "encoding/gob" + "encoding/json" + "fmt" "reflect" + "strconv" + "time" "unsafe" ) @@ -64,3 +69,92 @@ func Split(buf []byte, limit int) [][]byte { func Join(s [][]byte) []byte { return bytes.Join(s, []byte("")) } + + +type RawBytes []byte + +func cloneBytes(b []byte) []byte { + if b == nil { + return nil + } else { + c := make([]byte, len(b)) + copy(c, b) + return c + } +} + +func AsString(src interface{}) string { + switch v := src.(type) { + case string: + return v + case []byte: + return string(v) + case int: + return strconv.Itoa(v) + case int32: + return strconv.FormatInt(int64(v), 10) + case int64: + return strconv.FormatInt(v, 10) + case float32: + return strconv.FormatFloat(float64(v), 'f', -1, 64) + case float64: + return strconv.FormatFloat(v, 'f', -1, 64) + case time.Time: + return time.Time.Format(v, "2006-01-02 15:04:05") + case bool: + return strconv.FormatBool(v) + default: + { + b, _ := json.Marshal(v) + return string(b) + } + } + return fmt.Sprintf("%v", src) +} + +// 编码二进制 +func EncodeByte(data interface{}) ([]byte, error) { + buf := bytes.NewBuffer(nil) + enc := gob.NewEncoder(buf) + err := enc.Encode(data) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// 解码二进制 +func DecodeByte(data []byte, to interface{}) error { + buf := bytes.NewBuffer(data) + dec := gob.NewDecoder(buf) + return dec.Decode(to) +} + +// byte转16进制字符串 +func ByteToHex(data []byte) string { + buffer := new(bytes.Buffer) + for _, b := range data { + + s := strconv.FormatInt(int64(b&0xff), 16) + if len(s) == 1 { + buffer.WriteString("0") + } + buffer.WriteString(s) + } + + return buffer.String() +} + +// 16进制字符串转[]byte +func HexToBye(hex string) []byte { + length := len(hex) / 2 + slice := make([]byte, length) + rs := []rune(hex) + + for i := 0; i < length; i++ { + s := string(rs[i*2 : i*2+2]) + value, _ := strconv.ParseInt(s, 16, 10) + slice[i] = byte(value & 0xFF) + } + return slice +} diff --git a/net/src/test/go/util/convert/convert.go b/net/src/test/go/util/convert/convert.go index c02503e2..48f62a39 100644 --- a/net/src/test/go/util/convert/convert.go +++ b/net/src/test/go/util/convert/convert.go @@ -164,3 +164,16 @@ func Base64Encode(src []byte) string { func Base64Decode(src string) ([]byte, error) { return base64.StdEncoding.DecodeString(src) } + +// 四舍五入 +func Float64Rand(v float64, dig int) float64 { + cDig := strconv.Itoa(dig) + val := fmt.Sprintf("%0."+cDig+"f", v) + return StringToFloat64(val) +} + +// 浮点数串化(左边是整数位置,右边是小数位,dig参数控制) +func FloatToFDig(floVal float64, dig int) string { + return fmt.Sprintf("%10."+strconv.Itoa(dig)+"f", floVal) //十位整数,8位小数 +} + diff --git a/net/src/test/go/util/errors/catch.go b/net/src/test/go/util/errors/catch.go new file mode 100644 index 00000000..7c2ef095 --- /dev/null +++ b/net/src/test/go/util/errors/catch.go @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ +package errors + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime/debug" + "time" +) + +/* + 用于抓取错误记录打印(panic信息,不可度量的) + 注意:每个 goroutine 开始部分都需要写上:defer mycatch.Dmp() + 类似于 + go func(){ + defer mycatch.Dmp() + }() +*/ + +func Dmp() { + errstr := "" + if err := recover(); err != nil { + errstr += (fmt.Sprintf("%v\r\n", err)) //输出panic信息 + errstr += ("--------------------------------------------\r\n") + } + + errstr += (string(debug.Stack())) //输出堆栈信息 + // OnWriteErrToFile(errstr) +} + +func OnWriteErrToFile(errstring string) { + path := GetModelPath() + "/err" + if !PathExists(path) { + os.MkdirAll(path, os.ModePerm) //生成多级目录 + } + + now := time.Now() //获取当前时间 + pid := os.Getpid() //获取进程ID + time_str := now.Format("2006-01-02") //设定时间格式 + fname := fmt.Sprintf("%s/panic_%s-%x.log", path, time_str, pid) //保存错误信息文件名:程序名-进程ID-当前时间(年月日时分秒) + fmt.Println("panic to file ", fname) + + f, err := os.OpenFile(fname, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666) + if err != nil { + return + } + defer f.Close() + + f.WriteString("=========================" + now.Format("2006-01-02 15:04:05 ========================= \r\n")) + f.WriteString(errstring) //输出堆栈信息 + f.WriteString("=========================end=========================") +} + +// 获取目录地址 +func GetModelPath() string { + file, _ := exec.LookPath(os.Args[0]) + path := filepath.Dir(file) + path, _ = filepath.Abs(path) + + return path +} + +func PathExists(path string) bool { + _, err := os.Stat(path) + if err == nil { + return true + } + if os.IsNotExist(err) { + return false + } + return false +} diff --git a/net/src/test/go/util/errors/catch_test.go b/net/src/test/go/util/errors/catch_test.go new file mode 100644 index 00000000..f0ccb6c6 --- /dev/null +++ b/net/src/test/go/util/errors/catch_test.go @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ +package errors + +import "testing" + +func TestDmp(t *testing.T) { + + Dmp() + panic(1) +} diff --git a/net/src/test/go/util/errors/errors.go b/net/src/test/go/util/errors/errors.go new file mode 100644 index 00000000..b5e34e44 --- /dev/null +++ b/net/src/test/go/util/errors/errors.go @@ -0,0 +1,283 @@ +// from https://github.com/pkg/errors +// Package errors provides simple error handling primitives. +// +// The traditional error handling idiom in Go is roughly akin to +// +// if err != nil { +// return err +// } +// +// which when applied recursively up the call stack results in error reports +// without context or debugging information. The errors package allows +// programmers to add context to the failure path in their code in a way +// that does not destroy the original value of the error. +// +// Adding context to an error +// +// The errors.Wrap function returns a new error that adds context to the +// original error by recording a stack trace at the point Wrap is called, +// together with the supplied message. For example +// +// _, err := ioutil.ReadAll(r) +// if err != nil { +// return errors.Wrap(err, "read failed") +// } +// +// If additional control is required, the errors.WithStack and +// errors.WithMessage functions destructure errors.Wrap into its component +// operations: annotating an error with a stack trace and with a message, +// respectively. +// +// Retrieving the cause of an error +// +// Using errors.Wrap constructs a stack of errors, adding context to the +// preceding error. Depending on the nature of the error it may be necessary +// to reverse the operation of errors.Wrap to retrieve the original error +// for inspection. Any error value which implements this interface +// +// type causer interface { +// Cause() error +// } +// +// can be inspected by errors.Cause. errors.Cause will recursively retrieve +// the topmost error that does not implement causer, which is assumed to be +// the original cause. For example: +// +// switch err := errors.Cause(err).(type) { +// case *MyError: +// // handle specifically +// default: +// // unknown error +// } +// +// Although the causer interface is not exported by this package, it is +// considered a part of its stable public interface. +// +// Formatted printing of errors +// +// All error values returned from this package implement fmt.Formatter and can +// be formatted by the fmt package. The following verbs are supported: +// +// %s print the error. If the error has a Cause it will be +// printed recursively. +// %v see %s +// %+v extended format. Each Frame of the error's StackTrace will +// be printed in detail. +// +// Retrieving the stack trace of an error or wrapper +// +// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are +// invoked. This information can be retrieved with the following interface: +// +// type stackTracer interface { +// StackTrace() errors.StackTrace +// } +// +// The returned errors.StackTrace type is defined as +// +// type StackTrace []Frame +// +// The Frame type represents a call site in the stack trace. Frame supports +// the fmt.Formatter interface that can be used for printing information about +// the stack trace of this error. For example: +// +// if err, ok := err.(stackTracer); ok { +// for _, f := range err.StackTrace() { +// fmt.Printf("%+s:%d", f) +// } +// } +// +// Although the stackTracer interface is not exported by this package, it is +// considered a part of its stable public interface. +// +// See the documentation for Frame.Format for more details. +package errors + +import ( + "fmt" + "io" +) + +// New returns an error with the supplied message. +// New also records the stack trace at the point it was called. +func New(message string) error { + return &fundamental{ + msg: message, + stack: callers(), + } +} + +// Errorf formats according to a format specifier and returns the string +// as a value that satisfies error. +// Errorf also records the stack trace at the point it was called. +func Errorf(format string, args ...interface{}) error { + return &fundamental{ + msg: fmt.Sprintf(format, args...), + stack: callers(), + } +} + +// fundamental is an error that has a message and a stack, but no caller. +type fundamental struct { + msg string + *stack +} + +func (f *fundamental) Error() string { return f.msg } + +func (f *fundamental) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + io.WriteString(s, f.msg) + f.stack.Format(s, verb) + return + } + fallthrough + case 's': + io.WriteString(s, f.msg) + case 'q': + fmt.Fprintf(s, "%q", f.msg) + } +} + +// WithStack annotates err with a stack trace at the point WithStack was called. +// If err is nil, WithStack returns nil. +func WithStack(err error) error { + if err == nil { + return nil + } + return &withStack{ + err, + callers(), + } +} + +type withStack struct { + error + *stack +} + +func (w *withStack) Cause() error { return w.error } + +func (w *withStack) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v", w.Cause()) + w.stack.Format(s, verb) + return + } + fallthrough + case 's': + io.WriteString(s, w.Error()) + case 'q': + fmt.Fprintf(s, "%q", w.Error()) + } +} + +// Wrap returns an error annotating err with a stack trace +// at the point Wrap is called, and the supplied message. +// If err is nil, Wrap returns nil. +func Wrap(err error, message string) error { + if err == nil { + return nil + } + err = &withMessage{ + cause: err, + msg: message, + } + return &withStack{ + err, + callers(), + } +} + +// Wrapf returns an error annotating err with a stack trace +// at the point Wrapf is called, and the format specifier. +// If err is nil, Wrapf returns nil. +func Wrapf(err error, format string, args ...interface{}) error { + if err == nil { + return nil + } + err = &withMessage{ + cause: err, + msg: fmt.Sprintf(format, args...), + } + return &withStack{ + err, + callers(), + } +} + +// WithMessage annotates err with a new message. +// If err is nil, WithMessage returns nil. +func WithMessage(err error, message string) error { + if err == nil { + return nil + } + return &withMessage{ + cause: err, + msg: message, + } +} + +// WithMessagef annotates err with the format specifier. +// If err is nil, WithMessagef returns nil. +func WithMessagef(err error, format string, args ...interface{}) error { + if err == nil { + return nil + } + return &withMessage{ + cause: err, + msg: fmt.Sprintf(format, args...), + } +} + +type withMessage struct { + cause error + msg string +} + +func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() } +func (w *withMessage) Cause() error { return w.cause } + +func (w *withMessage) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v\n", w.Cause()) + io.WriteString(s, w.msg) + return + } + fallthrough + case 's', 'q': + io.WriteString(s, w.Error()) + } +} + +// Cause returns the underlying cause of the error, if possible. +// An error value has a cause if it implements the following +// interface: +// +// type causer interface { +// Cause() error +// } +// +// If the error does not implement Cause, the original error will +// be returned. If the error is nil, nil will be returned without further +// investigation. +func Cause(err error) error { + type causer interface { + Cause() error + } + + for err != nil { + cause, ok := err.(causer) + if !ok { + break + } + err = cause.Cause() + } + return err +} diff --git a/net/src/test/go/util/errors/errors_test.go b/net/src/test/go/util/errors/errors_test.go new file mode 100644 index 00000000..f46c7635 --- /dev/null +++ b/net/src/test/go/util/errors/errors_test.go @@ -0,0 +1,36 @@ +package errors + +import ( + "fmt" + "testing" +) + +func test1() error { + return test2() +} + +func test2() error { + return Wrapf(New("something go wrong"), "自定义消息") +} + +func TestErr(t *testing.T) { + err := test1() + fmt.Println(fmt.Sprintf("%+v", err)) + err = Cause(err) //获取原始对象 + fmt.Println(fmt.Sprintf("%+v", err)) +} + +func test11() error { + return test21() +} + +func test21() error { + return New("something go wrong") +} + +func TestErr1(t *testing.T) { + err := test11() + fmt.Println(fmt.Sprintf("%+v", err)) + err = Cause(err) //获取原始对象 + fmt.Println(fmt.Sprintf("%+v", err)) +} diff --git a/net/src/test/go/util/errors/stack.go b/net/src/test/go/util/errors/stack.go new file mode 100644 index 00000000..2a2ab901 --- /dev/null +++ b/net/src/test/go/util/errors/stack.go @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ +package errors + +import ( + "fmt" + "io" + "path" + "runtime" + "strings" +) + +// Frame represents a program counter inside a stack frame. +type Frame uintptr + +// pc returns the program counter for this frame; +// multiple frames may have the same PC value. +func (f Frame) pc() uintptr { return uintptr(f) - 1 } + +// file returns the full path to the file that contains the +// function for this Frame's pc. +func (f Frame) file() string { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return "unknown" + } + file, _ := fn.FileLine(f.pc()) + return file +} + +// line returns the line number of source code of the +// function for this Frame's pc. +func (f Frame) line() int { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return 0 + } + _, line := fn.FileLine(f.pc()) + return line +} + +// Format formats the frame according to the fmt.Formatter interface. +// +// %s source file +// %d source line +// %n function name +// %v equivalent to %s:%d +// +// Format accepts flags that alter the printing of some verbs, as follows: +// +// %+s function name and path of source file relative to the compile time +// GOPATH separated by \n\t (\n\t) +// %+v equivalent to %+s:%d +func (f Frame) Format(s fmt.State, verb rune) { + switch verb { + case 's': + switch { + case s.Flag('+'): + pc := f.pc() + fn := runtime.FuncForPC(pc) + if fn == nil { + io.WriteString(s, "unknown") + } else { + file, _ := fn.FileLine(pc) + fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file) + } + default: + io.WriteString(s, path.Base(f.file())) + } + case 'd': + fmt.Fprintf(s, "%d", f.line()) + case 'n': + name := runtime.FuncForPC(f.pc()).Name() + io.WriteString(s, funcname(name)) + case 'v': + f.Format(s, 's') + io.WriteString(s, ":") + f.Format(s, 'd') + } +} + +// StackTrace is stack of Frames from innermost (newest) to outermost (oldest). +type StackTrace []Frame + +// Format formats the stack of Frames according to the fmt.Formatter interface. +// +// %s lists source files for each Frame in the stack +// %v lists the source file and line number for each Frame in the stack +// +// Format accepts flags that alter the printing of some verbs, as follows: +// +// %+v Prints filename, function, and line number for each Frame in the stack. +func (st StackTrace) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + switch { + case s.Flag('+'): + for _, f := range st { + fmt.Fprintf(s, "\n%+v", f) + } + case s.Flag('#'): + fmt.Fprintf(s, "%#v", []Frame(st)) + default: + fmt.Fprintf(s, "%v", []Frame(st)) + } + case 's': + fmt.Fprintf(s, "%s", []Frame(st)) + } +} + +// stack represents a stack of program counters. +type stack []uintptr + +func (s *stack) Format(st fmt.State, verb rune) { + switch verb { + case 'v': + switch { + case st.Flag('+'): + for _, pc := range *s { + f := Frame(pc) + fmt.Fprintf(st, "\n%+v", f) + } + } + } +} + +func (s *stack) StackTrace() StackTrace { + f := make([]Frame, len(*s)) + for i := 0; i < len(f); i++ { + f[i] = Frame((*s)[i]) + } + return f +} + +func callers() *stack { + const depth = 32 + var pcs [depth]uintptr + n := runtime.Callers(3, pcs[:]) + var st stack = pcs[0:n] + return &st +} + +// funcname removes the path prefix component of a function's name reported by func.Name(). +func funcname(name string) string { + i := strings.LastIndex(name, "/") + name = name[i+1:] + i = strings.Index(name, ".") + return name[i+1:] +} diff --git a/net/src/test/go/util/fileutil/dir.go b/net/src/test/go/util/fileutil/dir.go index 768c1c61..db241cf2 100644 --- a/net/src/test/go/util/fileutil/dir.go +++ b/net/src/test/go/util/fileutil/dir.go @@ -17,6 +17,7 @@ import ( "os" "path" "path/filepath" + "strings" ) // SelfPath gets compiled executable file absolute path. @@ -117,3 +118,56 @@ func RemoveDir(dirPath string) error { } return os.Remove(dirPath) } + +func PathExists(path string) bool { + _, err := os.Stat(path) + if err == nil { + return true + } + if os.IsNotExist(err) { + return false + } + return false +} + +// 创建目录 +func BuildDir(abs_dir string) error { + return os.MkdirAll(path.Dir(abs_dir), os.ModePerm) //生成多级目录 +} + +// 删除文件或文件夹 +func DeleteFile(abs_dir string) error { + return os.RemoveAll(abs_dir) +} + +// 获取目录所有文件夹 +func GetPathDirs(abs_dir string) (re []string) { + if PathExists(abs_dir) { + files, _ := ioutil.ReadDir(abs_dir) + for _, f := range files { + if f.IsDir() { + re = append(re, f.Name()) + } + } + } + return +} + +// 获取目录所有文件 +func GetPathFiles(abs_dir string) (re []string) { + if PathExists(abs_dir) { + files, _ := ioutil.ReadDir(abs_dir) + for _, f := range files { + if !f.IsDir() { + re = append(re, f.Name()) + } + } + } + return +} + +// 获取程序运行路径 +func GetCurrentDirectory() string { + dir, _ := filepath.Abs(filepath.Dir(os.Args[0])) + return strings.Replace(dir, "\\", "/", -1) +} diff --git a/net/src/test/go/util/httputil/http.go b/net/src/test/go/util/httputil/http.go index 4acd09d4..3cc66dc6 100644 --- a/net/src/test/go/util/httputil/http.go +++ b/net/src/test/go/util/httputil/http.go @@ -12,9 +12,16 @@ package httputil import ( + "bytes" + "encoding/binary" + "encoding/json" + "fmt" "io/ioutil" + "net" "net/http" + "net/url" "strings" + "time" ) func PostWithHeader(url string, msg []byte, headers map[string]string) (string, error) { @@ -42,3 +49,327 @@ func PostWithAuthorization(url, authorization string, msg []byte) (string, error headers["Content-Type"] = "application/json" return PostWithHeader(url, msg, headers) } + + + +const ( + CONN_TIME_OUT = time.Second * 2 +) + +func Get(apiURL string, params url.Values) (resData string, e error) { + var ( + Url *url.URL + err error + ) + Url, err = url.Parse(apiURL) + if err != nil { + return "", err + } + Url.RawQuery = params.Encode() + resp, err := http.Get(Url.String()) + if err != nil { + return "", err + } + defer resp.Body.Close() + res, err := ioutil.ReadAll(resp.Body) + if err != nil { + return "", err + } + return string(res), nil +} + +/** +Get with TimeOut +Code == 200 则返回,其他请打印错误 +第三个参数设置超时 单位:秒 【0:则默认两秒】 +*/ +func SendGetWithTimeOut(apiUrl string, params url.Values, time_out int) (resData string, e error) { + var ( + Url *url.URL + err error + b []byte + ) + Url, err = url.Parse(apiUrl) + if err != nil { + return "", err + } + Url.RawQuery = params.Encode() + client := _httpClient(time_out) + resp, err := client.Get(Url.String()) + if err != nil || resp == nil { + return "", err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return "", nil + } + if resp.Body != nil { + b, err = ioutil.ReadAll(resp.Body) + return string(b), nil + } + return "", err +} + +func _httpClient(time_out int) http.Client { + var ( + _sec time.Duration + ) + _sec = CONN_TIME_OUT + if time_out > 0 { + _sec = time.Second * time.Duration(time_out) + } + return http.Client{ + Transport: &http.Transport{ + Dial: func(netw, addr string) (net.Conn, error) { + conn, err := net.DialTimeout(netw, addr, _sec) + if err != nil { + return nil, err + } + conn.SetDeadline(time.Now().Add(_sec)) + return conn, nil + }, + ResponseHeaderTimeout: _sec, + }, + } +} + +/** +网络请求POST +*/ +func Post(apiURL string, params url.Values) (resData string, err error) { + resp, err := http.PostForm(apiURL, params) + if err != nil { + return "", err + } + defer resp.Body.Close() + res, err := ioutil.ReadAll(resp.Body) + if err != nil { + return "", err + } + return string(res), nil +} + +/** +Post with TimeOut +Code == 200 则返回,其他请打印错误 +第三个参数设置超时 单位:秒 【0:则默认两秒】 +*/ +func SendPostWithTimeOut(apiUrl string, params url.Values, time_out int) (resData string, e error) { + var ( + err error + b []byte + ) + client := _httpClient(time_out) + resp, err := client.PostForm(apiUrl, params) + if err != nil || resp == nil { + return "", err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return "", nil + } + if resp.Body != nil { + b, err = ioutil.ReadAll(resp.Body) + return string(b), nil + } + return "", err +} + + + +//OnPostJSON 发送修改密码 +func OnPostJSON(url, jsonstr string) []byte { + //解析这个 URL 并确保解析没有出错。 + body := bytes.NewBuffer([]byte(jsonstr)) + resp, err := http.Post(url, "application/json;charset=utf-8", body) + if err != nil { + return []byte("") + } + defer resp.Body.Close() + body1, err1 := ioutil.ReadAll(resp.Body) + if err1 != nil { + return []byte("") + } + + return body1 +} + +//OnGetJSON 发送get 请求 +func OnGetJSON(url, params string) string { + //解析这个 URL 并确保解析没有出错。 + var urls = url + if len(params) > 0 { + urls += "?" + params + } + resp, err := http.Get(urls) + if err != nil { + return "" + } + defer resp.Body.Close() + body1, err1 := ioutil.ReadAll(resp.Body) + if err1 != nil { + return "" + } + + return string(body1) +} + +//SendGet 发送get 请求 返回对象 +func SendGet(url, params string, obj interface{}) bool { + //解析这个 URL 并确保解析没有出错。 + var urls = url + if len(params) > 0 { + urls += "?" + params + } + resp, err := http.Get(urls) + if err != nil { + return false + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return false + } + //log.Println((string(body))) + err = json.Unmarshal([]byte(body), &obj) + if err != nil { + return false + } + + return true +} + +//SendGetEx 发送GET请求 +func SendGetEx(url string, reponse interface{}) bool { + resp, e := http.Get(url) + if e != nil { + return false + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return false + } + err = json.Unmarshal(body, &reponse) + if err != nil { + return false + } + + return true +} + +//OnPostForm form 方式发送post请求 +func OnPostForm(url string, data url.Values) (body []byte) { + resp, err := http.PostForm(url, data) + if err != nil { + return + } + defer resp.Body.Close() + body, err = ioutil.ReadAll(resp.Body) + if err != nil { + return + } + + return +} + +//SendPost 发送POST请求 +func SendPost(requestBody interface{}, responseBody interface{}, url string) bool { + postData, err := json.Marshal(requestBody) + client := &http.Client{} + req, _ := http.NewRequest("POST", url, bytes.NewReader(postData)) + req.Header.Add("Accept", "application/json") + req.Header.Add("Content-Type", "application/json;charset=utf-8") + // req.Header.Add("Authorization", authorization) + resp, e := client.Do(req) + if e != nil { + return false + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return false + } + // result := string(body) + + err = json.Unmarshal(body, &responseBody) + if err != nil { + return false + } + + return true +} + +//WriteJSON 像指定client 发送json 包 +//msg message.MessageBody +func WriteJSON(w http.ResponseWriter, msg interface{}) { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + js, err := json.Marshal(msg) + if err != nil { + panic(err) + } + fmt.Fprintf(w, string(js)) +} + + +// -------------------------------------------------------------------------------------------------------- +const ( + XForwardedFor = "X-Forwarded-For" + XRealIP = "X-Real-IP" +) + +// RemoteIp 返回远程客户端的 IP,如 192.168.1.1 +func RemoteIp(req *http.Request) string { + remoteAddr := req.RemoteAddr + if ip := req.Header.Get(XRealIP); ip != "" { + remoteAddr = ip + } else if ip = req.Header.Get(XForwardedFor); ip != "" { + remoteAddr = ip + } else { + remoteAddr, _, _ = net.SplitHostPort(remoteAddr) + } + + if remoteAddr == "::1" { + remoteAddr = "127.0.0.1" + } + + return remoteAddr +} + +// Ip2long 将 IPv4 字符串形式转为 uint32 +func Ip2long(ipstr string) uint32 { + ip := net.ParseIP(ipstr) + if ip == nil { + return 0 + } + ip = ip.To4() + return binary.BigEndian.Uint32(ip) +} + +// 获取本机网卡IP +func GetLocalIP() (ipv4 string, err error) { + var ( + addrs []net.Addr + addr net.Addr + ipNet *net.IPNet // IP地址 + isIpNet bool + ) + // 获取所有网卡 + if addrs, err = net.InterfaceAddrs(); err != nil { + return + } + // 取第一个非lo的网卡IP + for _, addr = range addrs { + // 这个网络地址是IP地址: ipv4, ipv6 + if ipNet, isIpNet = addr.(*net.IPNet); isIpNet && !ipNet.IP.IsLoopback() { + // 跳过IPV6 + if ipNet.IP.To4() != nil { + ipv4 = ipNet.IP.String() // 192.168.1.1 + return + } + } + } + + return +} diff --git a/net/src/test/go/util/jsonutil/json.go b/net/src/test/go/util/jsonutil/json.go new file mode 100644 index 00000000..2a79ae34 --- /dev/null +++ b/net/src/test/go/util/jsonutil/json.go @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ +package jsonutil + +import ( + "encoding/json" +) + +/** + JSON (map转json) +*/ + +func ToJsonString(data map[string]interface{}) string { + jsonData, err := json.Marshal(data) + if err != nil { + return "" + } + return string(jsonData) +} + +/* + 泛型比较麻烦,单独做一个 +*/ +func Struct2Json(data interface{}) (string, error) { + jsonData, err := json.Marshal(data) + if err != nil { + return "", err + } + return string(jsonData), err +} + +/** + JSON (json转map) +*/ +func StringToJson(data string) map[string]interface{} { + var jsonData map[string]interface{} + json.Unmarshal([]byte(data), &jsonData) + return jsonData +} + +/** + JSONstring (json转IntList) +*/ +func ToIntList(data string) []int { + var tmp = make([]int, 0) + json.Unmarshal([]byte(data), &tmp) + return tmp +} diff --git a/net/src/test/go/util/maputil/map.go b/net/src/test/go/util/maputil/map.go index cf8a08fe..c20fc9e0 100644 --- a/net/src/test/go/util/maputil/map.go +++ b/net/src/test/go/util/maputil/map.go @@ -11,7 +11,10 @@ */ package maputil -import "strings" +import ( + "reflect" + "strings" +) // IsEmpty ... func IsEmpty(mp map[string]string) bool { @@ -79,3 +82,16 @@ func Merge(src, dst map[string]string) map[string]string { } return dst } + +// 结构体转map +func Struct2Map(obj interface{}) map[string]interface{} { + t := reflect.TypeOf(obj) + v := reflect.ValueOf(obj) + + var data = make(map[string]interface{}) + for i := 0; i < t.NumField(); i++ { + data[t.Field(i).Name] = v.Field(i).Interface() + } + return data +} + diff --git a/net/src/test/go/util/security/aes.go b/net/src/test/go/util/security/aes.go new file mode 100644 index 00000000..70392f09 --- /dev/null +++ b/net/src/test/go/util/security/aes.go @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ +package security + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" +) + +/** + Aes加解密 + 填充方式【CBC】 + 收集 +*/ + +func AesEncrypt(origData, key []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + blockSize := block.BlockSize() + origData = PKCS5Padding(origData, blockSize) + // origData = ZeroPadding(origData, block.BlockSize()) + blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) + crypted := make([]byte, len(origData)) + // crypted := origData + blockMode.CryptBlocks(crypted, origData) + return crypted, nil +} + +func AesDecrypt(crypted, key []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + blockSize := block.BlockSize() + blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) + origData := make([]byte, len(crypted)) + // origData := crypted + blockMode.CryptBlocks(origData, crypted) + origData = PKCS5UnPadding(origData) + // origData = ZeroUnPadding(origData) + return origData, nil +} + +func ZeroPadding(ciphertext []byte, blockSize int) []byte { + padding := blockSize - len(ciphertext)%blockSize + padtext := bytes.Repeat([]byte{0}, padding) + return append(ciphertext, padtext...) +} + +func ZeroUnPadding(origData []byte) []byte { + length := len(origData) + unpadding := int(origData[length-1]) + return origData[:(length - unpadding)] +} + +func PKCS5Padding(ciphertext []byte, blockSize int) []byte { + padding := blockSize - len(ciphertext)%blockSize + padtext := bytes.Repeat([]byte{byte(padding)}, padding) + return append(ciphertext, padtext...) +} + +func PKCS5UnPadding(origData []byte) []byte { + length := len(origData) + // 去掉最后一个字节 unpadding 次 + unpadding := int(origData[length-1]) + return origData[:(length - unpadding)] +} diff --git a/net/src/test/go/util/rsautil/crypt.go b/net/src/test/go/util/security/crypt.go similarity index 99% rename from net/src/test/go/util/rsautil/crypt.go rename to net/src/test/go/util/security/crypt.go index 29f0aeec..a1866629 100644 --- a/net/src/test/go/util/rsautil/crypt.go +++ b/net/src/test/go/util/security/crypt.go @@ -9,7 +9,7 @@ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and limitations under the License. */ -package rsautil +package security import ( "bytes" diff --git a/net/src/test/go/util/security/md5.go b/net/src/test/go/util/security/md5.go new file mode 100644 index 00000000..c5b01de1 --- /dev/null +++ b/net/src/test/go/util/security/md5.go @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ +package dmd5 + +import ( + "crypto/md5" + "encoding/hex" +) + +/** + md5 +*/ + +func Md5EnCode(string string) string { + h := md5.New() + h.Write([]byte(string)) // 需要加密的字符串为 + return hex.EncodeToString(h.Sum(nil)) +} diff --git a/net/src/test/go/util/rsautil/rsa.go b/net/src/test/go/util/security/rsa.go similarity index 99% rename from net/src/test/go/util/rsautil/rsa.go rename to net/src/test/go/util/security/rsa.go index bee56d61..caa05ee3 100644 --- a/net/src/test/go/util/rsautil/rsa.go +++ b/net/src/test/go/util/security/rsa.go @@ -9,7 +9,7 @@ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and limitations under the License. */ -package rsautil +package security import ( "crypto/rand" diff --git a/net/src/test/go/util/stringutil/string.go b/net/src/test/go/util/stringutil/string.go index 59562a8b..81d13152 100644 --- a/net/src/test/go/util/stringutil/string.go +++ b/net/src/test/go/util/stringutil/string.go @@ -15,8 +15,10 @@ import ( "bytes" "net/url" "regexp" + "strconv" "strings" "unicode" + "unsafe" ) // Reverse 反转字符串 @@ -125,3 +127,126 @@ func TrimSpace(s string) string { s = strings.Trim(s, "\t") return s } + +func CamelCase(s string) string { + if s == "" { + return "" + } + t := make([]byte, 0, 32) + i := 0 + if s[0] == '_' { + t = append(t, 'X') + i++ + } + for ; i < len(s); i++ { + c := s[i] + if c == '_' && i+1 < len(s) && isASCIIUpper(s[i+1]) { + continue + } + if isASCIIDigit(c) { + t = append(t, c) + continue + } + + if isASCIIUpper(c) { + c ^= ' ' + } + t = append(t, c) + + for i+1 < len(s) && isASCIIUpper(s[i+1]) { + i++ + t = append(t, '_') + t = append(t, bytes.ToLower([]byte{s[i]})[0]) + } + } + return string(t) +} +func isASCIIUpper(c byte) bool { + return 'A' <= c && c <= 'Z' +} + +func isASCIIDigit(c byte) bool { + return '0' <= c && c <= '9' +} + +// 手机号码检测 +func CheckIsMobile(mobileNum string) bool { + var regular = "^1[345789]{1}\\d{9}$" + reg := regexp.MustCompile(regular) + return reg.MatchString(mobileNum) +} + +// 判断是否是18或15位身份证 +func IsIdCard(cardNo string) bool { + // 18位身份证 ^(\d{17})([0-9]|X)$ + if m, _ := regexp.MatchString(`(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)`, cardNo); !m { + return false + } + return true +} + +// 字节转字符串 +func BytesToString(data []byte) string { + return *(*string)(unsafe.Pointer(&data)) +} + +// 字符串转字节数组 +func StringToBytes(data string) []byte { + return *(*[]byte)(unsafe.Pointer(&data)) +} + +// 判断字符串是否为中文[精确度需要反复试验] +func IsContainCN(str string) bool { + var hzRegexp = regexp.MustCompile("[\u4e00-\u9fa5]+") + return hzRegexp.MatchString(str) +} + +// Emoji表情解码 +func UnicodeEmojiDecode(s string) string { + //emoji表情的数据表达式 + re := regexp.MustCompile("\\[[\\\\u0-9a-zA-Z]+\\]") + // 提取emoji数据表达式 + reg := regexp.MustCompile("\\[\\\\u|]") + src := re.FindAllString(s, -1) + for i := 0; i < len(src); i++ { + e := reg.ReplaceAllString(src[i], "") + p, err := strconv.ParseInt(e, 16, 32) + if err == nil { + s = strings.Replace(s, src[i], string(rune(p)), -1) + } + } + return s +} + +/** + 身份证手机号填充 +*/ + +func SignIdcard(idcard string) string { + cp := idcard + leth := len(cp) + return cp[0:4] + " **** **** " + cp[leth-4:] +} + +func SignMobile(mobile string) string { + cp := mobile + leth := len(cp) + return cp[0:3] + " **** " + cp[leth-4:] +} + + +// Emoji表情转换 +func UnicodeEmojiCode(s string) string { + ret := "" + rs := []rune(s) + for i := 0; i < len(rs); i++ { + if len(string(rs[i])) == 4 { + u := `[\u` + strconv.FormatInt(int64(rs[i]), 16) + `]` + ret += u + + } else { + ret += string(rs[i]) + } + } + return ret +} diff --git a/net/src/test/go/util/timeutil/time.go b/net/src/test/go/util/timeutil/time.go new file mode 100644 index 00000000..f4cf32d2 --- /dev/null +++ b/net/src/test/go/util/timeutil/time.go @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ +package dtime + +import ( + "bytes" + "math/rand" + "sync" + "time" +) + +/** + 时间获取 + 时间转换必须加入时区设置,请注意 +*/ + +var ( + randSeek = int64(1) + l sync.Mutex + zone = "CST" //时区 +) + +func TimeIntToDate(time_int int) string { + var cstZone = time.FixedZone(zone, 8*3600) + return time.Unix(int64(time_int), 0).In(cstZone).Format("2006-01-02 15:04:05") +} + +func GetNowDateTime() string { + var cstZone = time.FixedZone(zone, 8*3600) + return time.Now().In(cstZone).Format("2006-01-02 15:04:05") +} + +func GetDate() string { + var cstZone = time.FixedZone(zone, 8*3600) + return time.Now().In(cstZone).Format("2006-01-02") +} + +//防时间间隔 +func GetIntTime() int { + var _t = int(time.Now().Unix()) + return _t +} + +//暂时独立 +func _getRandomSring(num int, str ...string) string { + s := "123456789" + if len(str) > 0 { + s = str[0] + } + l := len(s) + r := rand.New(rand.NewSource(getRandSeek())) + var buf bytes.Buffer + for i := 0; i < num; i++ { + x := r.Intn(l) + buf.WriteString(s[x : x+1]) + } + return buf.String() +} + +func getRandSeek() int64 { + l.Lock() + if randSeek >= 100000000 { + randSeek = 1 + } + randSeek++ + l.Unlock() + return time.Now().UnixNano() + randSeek +} + +//获取今天时间戳 Today => 00:00:00 +func TodayTimeUnix() int { + t := time.Now() + tm1 := int(time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()).Unix()) + return tm1 +} + +//获取今天时间戳 Today => 23:59:59 +func TodayNightUnix() int { + tm1 := TodayTimeUnix() + 86400 - 1 + return tm1 +}