diff --git a/README.md b/README.md new file mode 100644 index 0000000..aa04926 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# public +golang 工具包。 + +- 里面包含很多公共函数 + +包括: + +`文件功能`,`leveldb`,`restful风格消息包头定义`,`cache缓存`,`绘图函数`,`elastic`,`echarts`,`http`,`日志`,`nsq抽取`,`线程安全队列`,`签名`,`gorm封装`,`时间函数`,`国际化i18n`,`gocui 界面类`,`驼峰命名转换工具`,`大驼峰到网络标准json串自动转换`,`剪切板`,`微信`,`ast`,`swagger 文档支持`,`mindoc/markdown 文档支持` +...... diff --git a/dev/dev.go b/dev/dev.go new file mode 100644 index 0000000..57aefca --- /dev/null +++ b/dev/dev.go @@ -0,0 +1,48 @@ +package dev + +import ( + "os" + "strings" +) + +var _isDev = true +var service = "service" +var fileHost = "file" + +//是否开发模式 +func OnSetDev(isDev bool) { + _isDev = isDev +} + +//OnIsDev ... 是否是开发版本 +func OnIsDev() bool { + return _isDev +} + +//判断是否在测试环境下使用 +func IsRunTesting() bool { + if len(os.Args) > 1 { + return strings.HasPrefix(os.Args[1], "-test") + } + return false +} + +//设置服务名 +func SetService(s string) { + service = s +} + +//获取服务名 +func GetService() string { + return service +} + +//设置服务名 +func SetFileHost(s string) { + fileHost = s +} + +//获取文件host +func GetFileHost() string { + return fileHost +} diff --git a/errors/errors.go b/errors/errors.go new file mode 100644 index 0000000..b5e34e4 --- /dev/null +++ b/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/errors/errors_test.go b/errors/errors_test.go new file mode 100644 index 0000000..f46c763 --- /dev/null +++ b/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/errors/stack.go b/errors/stack.go new file mode 100644 index 0000000..2874a04 --- /dev/null +++ b/errors/stack.go @@ -0,0 +1,147 @@ +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/fractional/fal_test.go b/fractional/fal_test.go new file mode 100644 index 0000000..2de51f7 --- /dev/null +++ b/fractional/fal_test.go @@ -0,0 +1,28 @@ +package fractional + +import ( + "fmt" + "testing" +) + +func Test_fal(t *testing.T) { + tmp := Model(7, 12) + tmp1 := Model(1, 12) + fmt.Println(tmp.Add(tmp1)) + + tmp = Model(1, 4) + tmp1 = Model(1, 3) + fmt.Println(tmp.Sub(tmp1)) + + tmp = Model(3, 4) + tmp1 = Model(2, 3) + fmt.Println(tmp.Mul(tmp1)) + + tmp = Model(3, 4) + tmp1 = Model(2, 3) + fmt.Println(tmp.Div(tmp1)) + + tmp = Model(1, 3) + fmt.Println(tmp.Verdict()) + +} diff --git a/fractional/fractional.go b/fractional/fractional.go new file mode 100644 index 0000000..c65437f --- /dev/null +++ b/fractional/fractional.go @@ -0,0 +1,93 @@ +/* +Package fractional 分数运算相关 +*/ +package fractional + +import ( + "fmt" + + "github.com/xxjwxc/public/mymath" +) + +//FAL 分数 +type FAL struct { + Nume int64 //numerator 分子 + Deno int64 //denominator 分母 (一定不为0) +} + +//Model Create a score (molecular, denominator) with a denominator default of 1 创建一个分数(分子,分母),分母默认为1: +func Model(nd ...int64) *FAL { + var f FAL + if len(nd) == 1 { + f.Nume = nd[0] + f.Deno = 1 + } else if len(nd) == 2 { + f.Nume = nd[0] + f.Deno = nd[1] + } + + if f.Deno == 0 { //分母为0 + panic(fmt.Sprintf("fractional init error. if denominator can't zero.")) + } + + return &f +} + +//阔张 +func (s *FAL) broad(lcm int64) { + s.Nume = s.Nume * (lcm / s.Deno) + s.Deno = lcm +} + +//压缩 整理 +func (s *FAL) offset() { + lcm := mymath.Gcd(s.Nume, s.Deno) + + s.Nume /= lcm + s.Deno /= lcm +} + +//Add 分数加法 +func (s *FAL) Add(f *FAL) *FAL { + //获取最小公倍数 + lcm := mymath.Lcm(f.Deno, s.Deno) + s.broad(lcm) + f.broad(lcm) + + s.Nume += f.Nume + s.offset() + return s +} + +//Sub 分数减法 +func (s *FAL) Sub(f *FAL) *FAL { + //获取最小公倍数 + lcm := mymath.Lcm(s.Deno, f.Deno) + s.broad(lcm) + f.broad(lcm) + + s.Nume -= f.Nume + s.offset() + return s +} + +//Mul 乘法 +func (s *FAL) Mul(f *FAL) *FAL { + s.Deno *= f.Deno + s.Nume *= f.Nume + s.offset() + return s +} + +//Div 乘法 +func (s *FAL) Div(f *FAL) *FAL { + tmp := Model(f.Deno, f.Nume) + s.Mul(tmp) + s.offset() + return s +} + +//Verdict 计算结果 +func (s *FAL) Verdict() float64 { + return float64(s.Nume) / float64(s.Deno) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..6c7e5e0 --- /dev/null +++ b/go.mod @@ -0,0 +1,32 @@ +module github.com/xxjwxc/public + +go 1.12 + +require ( + github.com/ant0ine/go-json-rest v3.3.2+incompatible + github.com/atotto/clipboard v0.1.2 + github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 + github.com/bitly/go-simplejson v0.5.0 + github.com/btcsuite/winsvc v1.0.0 // indirect + github.com/ezbuy/tgen v0.0.0-20180109020500-95ef13895032 // indirect + github.com/go-sql-driver/mysql v1.4.1 + github.com/gookit/color v1.1.7 + github.com/jander/golog v0.0.0-20150917071935-954a5be801fc + github.com/jinzhu/gorm v1.9.11 + github.com/jroimartin/gocui v0.4.0 + github.com/kardianos/service v1.0.1-0.20191017145738-4df36c9fc1c6 + github.com/mailru/easyjson v0.7.0 // indirect + github.com/mattn/go-runewidth v0.0.8 // indirect + github.com/muesli/cache2go v0.0.0-20191019095710-4098a3aa8c94 + github.com/nicksnyder/go-i18n/v2 v2.0.3 + github.com/nsf/termbox-go v0.0.0-20191229070316-58d4fcbce2a7 // indirect + github.com/olivere/elastic v6.2.26+incompatible + github.com/pkg/errors v0.8.1 // indirect + github.com/silenceper/wechat v1.2.3 + github.com/syndtr/goleveldb v1.0.0 + golang.org/x/net v0.0.0-20191125084936-ffdde1057850 + golang.org/x/sys v0.0.0-20191104094858-e8c54fb511f6 // indirect + golang.org/x/text v0.3.2 + gopkg.in/eapache/queue.v1 v1.1.0 + gopkg.in/go-with/wxpay.v1 v1.3.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..26bda4c --- /dev/null +++ b/go.sum @@ -0,0 +1,297 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= +github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/ant0ine/go-json-rest v3.3.2+incompatible h1:nBixrkLFiDNAW0hauKDLc8yJI6XfrQumWvytE1Hk14E= +github.com/ant0ine/go-json-rest v3.3.2+incompatible/go.mod h1:q6aCt0GfU6LhpBsnZ/2U+mwe+0XB5WStbmwyoPfc+sk= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/astaxie/beego v1.7.1/go.mod h1:0R4++1tUqERR0WYFWdfkcrsyoVBCG4DgpDGokT3yb+U= +github.com/atotto/clipboard v0.1.2 h1:YZCtFu5Ie8qX2VmVTBnrqLSiU9XOWwqNRmdT3gIQzbY= +github.com/atotto/clipboard v0.1.2/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= +github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ= +github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394/go.mod h1:Q8n74mJTIgjX4RBBcHnJ05h//6/k6foqmgE45jTQtxg= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y= +github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= +github.com/bradfitz/gomemcache v0.0.0-20160117192205-fb1f79c6b65a h1:k5TuEkqEYCRs8+66WdOkswWOj+L/YbP5ruainvn94wg= +github.com/bradfitz/gomemcache v0.0.0-20160117192205-fb1f79c6b65a/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60= +github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 h1:/P9/RL0xgWE+ehnCUUN5h3RpG3dmoMCOONO1CCvq23Y= +github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013/go.mod h1:pccXHIvs3TV/TUqSNyEvF99sxjX2r4FFRIyw6TZY9+w= +github.com/btcsuite/winsvc v1.0.0 h1:J9B4L7e3oqhXOcm+2IuNApwzQec85lE+QaikUcCs+dk= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= +github.com/ezbuy/tgen v0.0.0-20180109020500-95ef13895032 h1:diFmlmmibvulzbAoXmREHdqOsH9jIYkQLG6DZ6W2F7g= +github.com/ezbuy/tgen v0.0.0-20180109020500-95ef13895032/go.mod h1:OeW1N0acAlRaGTlOG8jRZZUKEtyiGa0qvD+lWNWb9vs= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-gonic/gin v1.1.4/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v0.0.0-20161117033126-8ee79997227b/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gomodule/redigo v2.0.1-0.20180627144507-2cd21d9966bf+incompatible h1:QJ4V3LjaRe/6NKoaaj2QzQZcezt5gNXdPv0axxS4VNA= +github.com/gomodule/redigo v2.0.1-0.20180627144507-2cd21d9966bf+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/gookit/color v1.1.7 h1:WR5I/mhSHzemW2DzG54hTsUb7OzaREvkcmUG4/WST4Q= +github.com/gookit/color v1.1.7/go.mod h1:R3ogXq2B9rTbXoSHJ1HyUVAZ3poOJHpd9nQmyGZsfvQ= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jander/golog v0.0.0-20150917071935-954a5be801fc h1:l+v2jwOsGum32nR8cN1MxAtvUWQCIcQGm4WHwizaAbA= +github.com/jander/golog v0.0.0-20150917071935-954a5be801fc/go.mod h1:uWhWXOR4dpfk9J8fegnMY7sP2GFXxe3PFI9Ps+TRXJs= +github.com/jinzhu/gorm v1.9.11 h1:gaHGvE+UnWGlbWG4Y3FUwY1EcZ5n6S9WtqBA/uySMLE= +github.com/jinzhu/gorm v1.9.11/go.mod h1:bu/pK8szGZ2puuErfU0RwyeNdsf3e6nCX/noXaVxkfw= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jroimartin/gocui v0.4.0 h1:52jnalstgmc25FmtGcWqa0tcbMEWS6RpFLsOIO+I+E8= +github.com/jroimartin/gocui v0.4.0/go.mod h1:7i7bbj99OgFHzo7kB2zPb8pXLqMBSQegY7azfqXMkyY= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kardianos/service v1.0.0 h1:HgQS3mFfOlyntWX8Oke98JcJLqt1DBcHR4kxShpYef0= +github.com/kardianos/service v1.0.0/go.mod h1:8CzDhVuCuugtsHyZoTvsOBuvonN/UDBvl0kH+BUxvbo= +github.com/kardianos/service v1.0.1-0.20191017145738-4df36c9fc1c6 h1:yXmAejtSQYYDbY5Zv4BQDV4irr8r88UjcWufn4PtF5I= +github.com/kardianos/service v1.0.1-0.20191017145738-4df36c9fc1c6/go.mod h1:8CzDhVuCuugtsHyZoTvsOBuvonN/UDBvl0kH+BUxvbo= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= +github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/manucorporat/sse v0.0.0-20160126180136-ee05b128a739/go.mod h1:zUx1mhth20V3VKgL5jbd1BSQcW4Fy6Qs4PZvQwRFwzM= +github.com/mattn/go-isatty v0.0.0-20161123143637-30a891c33c7c/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0= +github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/muesli/cache2go v0.0.0-20191019095710-4098a3aa8c94 h1:aJflkQlskelDMdsFUYWffZXg0xtmYtjQJGwzRx3byDw= +github.com/muesli/cache2go v0.0.0-20191019095710-4098a3aa8c94/go.mod h1:414R+qZrt4f9S2TO/s6YVQMNAXR2KdwqQ7pW+O4oYzU= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nicksnyder/go-i18n/v2 v2.0.3 h1:ks/JkQiOEhhuF6jpNvx+Wih1NIiXzUnZeZVnJuI8R8M= +github.com/nicksnyder/go-i18n/v2 v2.0.3/go.mod h1:oDab7q8XCYMRlcrBnaY/7B1eOectbvj6B1UPBT+p5jo= +github.com/nsf/termbox-go v0.0.0-20191229070316-58d4fcbce2a7 h1:OkWEy7aQeQTbgdrcGi9bifx+Y6bMM7ae7y42hDFaBvA= +github.com/nsf/termbox-go v0.0.0-20191229070316-58d4fcbce2a7/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olivere/elastic v6.2.26+incompatible h1:3PjUHKyt8xKwbFQpRC5cgtEY7Qz6ejopBkukhI7UWvE= +github.com/olivere/elastic v6.2.26+incompatible/go.mod h1:J+q1zQJTgAz9woqsbVRqGeB5G1iqDKVBWLNSYW8yfJ8= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/samuel/go-thrift v0.0.0-20190219015601-e8b6b52668fe h1:gD4vkYmuoWVgdV6UwI3tPo9MtMfVoIRY+Xn9919SJBg= +github.com/samuel/go-thrift v0.0.0-20190219015601-e8b6b52668fe/go.mod h1:Vrkh1pnjV9Bl8c3P9zH0/D4NlOHWP5d4/hF4YTULaec= +github.com/silenceper/wechat v1.2.3 h1:9ZrCdg6MkJTuZQ6PzxlzktktQLUzRd4VZag8zlUfag0= +github.com/silenceper/wechat v1.2.3/go.mod h1:sEqmYpYrwwMCXUSzTaxcA78+fd0p+srrj8qRkOnpbLQ= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.5.0 h1:GpsTwfsQ27oS/Aha/6d1oD7tpKIqWnOA6tgOX9HHkt4= +github.com/spf13/viper v1.5.0/go.mod h1:AkYRkVJF8TkSG/xet6PzXX+l39KhhXa2pdqVSxnTcn4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= +github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284 h1:rlLehGeYg6jfoyz/eDqDU1iRXLKfR42nnNh57ytKEWo= +golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20191125084936-ffdde1057850/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191104094858-e8c54fb511f6 h1:ZJUmhYTp8GbGC0ViZRc2U+MIYQ8xx9MscsdXnclfIhw= +golang.org/x/sys v0.0.0-20191104094858-e8c54fb511f6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/eapache/queue.v1 v1.1.0 h1:EldqoJEGtXYiVCMRo2C9mePO2UUGnYn2+qLmlQSqPdc= +gopkg.in/eapache/queue.v1 v1.1.0/go.mod h1:wNtmx1/O7kZSR9zNT1TTOJ7GLpm3Vn7srzlfylFbQwU= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v8 v8.18.1/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= +gopkg.in/go-with/wxpay.v1 v1.3.0 h1:RXVQck9qNIGkajbTMCvlRPHeewyAIItzOybTTykVdV0= +gopkg.in/go-with/wxpay.v1 v1.3.0/go.mod h1:12lWy92n19pAUSSE3BrOiEZbWRkl+9tneOd/aU/LU6g= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/message/message.go b/message/message.go index aabff01..26eab3f 100644 --- a/message/message.go +++ b/message/message.go @@ -4,13 +4,12 @@ package message import ( - "data/config" "encoding/json" - "fmt" "log" - "public/tools" "github.com/bitly/go-simplejson" + "github.com/xxjwxc/public/dev" + "github.com/xxjwxc/public/tools" ) const ( //消息id定义 @@ -106,6 +105,7 @@ const ( //消息id定义 ExistedError = 1084 //已存在 NotBindError = 1085 //未绑定 BindError = 1086 //绑定失败 + CalError = 1087 //计算错误 ) //消息翻译 @@ -202,9 +202,10 @@ var MessageMap = map[int]string{ ExistedError: "已存在", NotBindError: "未绑定", BindError: "绑定失败", + CalError: "计算错误", } -// +//MessageBody 消息头 type MessageBody struct { State bool `json:"state"` Code int `json:"code,omitempty"` @@ -212,7 +213,7 @@ type MessageBody struct { Data interface{} `json:"data,omitempty"` } -//获取错误消息 参数(int,string) +//GetErrorMsg 获取错误消息 参数(int,string) func GetErrorMsg(errorCode ...interface{}) (msg MessageBody) { if len(errorCode) == 0 { log.Println("未知") @@ -228,10 +229,11 @@ func GetErrorMsg(errorCode ...interface{}) (msg MessageBody) { msg.Error = MessageMap[msg.Code] case string: msg.Error = string(v) - fmt.Println(v) + case error: + msg.Error = v.Error() case interface{}: { - if config.OnIsDev() { + if dev.OnIsDev() { msg.Error = onCheckParam(v) } } @@ -261,12 +263,12 @@ func onCheckParam(op interface{}) string { } //成功消息 -func GetSuccessMsg(errorCode ...int) (msg MessageBody) { +func GetSuccessMsg(code ...int) (msg MessageBody) { msg.State = true - if len(errorCode) == 0 { + if len(code) == 0 { msg.Code = NormalMessageId } else { - msg.Code = errorCode[0] + msg.Code = code[0] } msg.Error = MessageMap[msg.Code] diff --git a/myast/common.go b/myast/common.go new file mode 100644 index 0000000..c500fa2 --- /dev/null +++ b/myast/common.go @@ -0,0 +1,166 @@ +package myast + +import ( + "fmt" + "go/ast" + "go/parser" + "go/token" + "os" + "runtime" + "strings" + + "github.com/xxjwxc/public/errors" + "github.com/xxjwxc/public/tools" +) + +// GetModuleInfo find and get module info , return module [ name ,path ] +// 通过model信息获取[model name] [和 根目录绝对地址] +func GetModuleInfo(n int) (string, string, bool) { + index := n + // This is used to support third-party package encapsulation + // 这样做用于支持第三方包封装,(主要找到main调用者) + for true { // find main file + _, filename, _, ok := runtime.Caller(index) + if ok { + if strings.HasSuffix(filename, "runtime/asm_amd64.s") { + index = index - 2 + break + } + index++ + } else { + panic(errors.New("package parsing failed:can not find main files")) + } + } + + _, filename, _, _ := runtime.Caller(index) + filename = strings.Replace(filename, "\\", "/", -1) // offset + for true { + n := strings.LastIndex(filename, "/") + if n > 0 { + filename = filename[0:n] + if tools.CheckFileIsExist(filename + "/go.mod") { + list := tools.ReadFile(filename + "/go.mod") + if len(list) > 0 { + line := strings.TrimSpace(list[0]) + if len(line) > 0 && strings.HasPrefix(line, "module") { // find it + return strings.TrimSpace(strings.TrimPrefix(line, "module")), filename, true + } + } + } + } else { + break + // panic(errors.New("package parsing failed:can not find module file[go.mod] , golang version must up 1.11")) + } + } + + // never reach + return "", "", false +} + +// EvalSymlinks Return to relative path . 通过module 游标返回包相对路径 +func EvalSymlinks(modPkg, modFile, objPkg string) string { + if strings.EqualFold(objPkg, "main") { // if main return default path + return modFile + } + + if strings.HasPrefix(objPkg, modPkg) { + return modFile + strings.Replace(objPkg[len(modPkg):], ".", "/", -1) + } + + // get the error space + panic(errors.Errorf("can not eval pkg:[%v] must include [%v]", objPkg, modPkg)) +} + +// Re +// GetAstPkgs Parsing source file ast structure (with main restriction).解析源文件ast结构(带 main 限制) +func GetAstPkgs(objPkg, objFile string) (*ast.Package, bool) { + fileSet := token.NewFileSet() + astPkgs, err := parser.ParseDir(fileSet, objFile, func(info os.FileInfo) bool { + name := info.Name() + return !info.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") + }, parser.ParseComments) + if err != nil { + return nil, false + } + + // check the package is same.判断 package 是否一致 + for _, pkg := range astPkgs { + if objPkg == pkg.Name || strings.HasSuffix(objPkg, "/"+pkg.Name) { // find it + return pkg, true + } + } + + // not find . maybe is main pakge and find main package + if objPkg == "main" { + dirs := tools.GetPathDirs(objFile) // get all of dir + for _, dir := range dirs { + if !strings.HasPrefix(dir, ".") { + pkg, b := GetAstPkgs(objPkg, objFile+"/"+dir) + if b { + return pkg, true + } + } + } + } + + // ast.Print(fileSet, astPkgs) + + return nil, false +} + +// GetObjFunMp find all exported func of sturct objName +// GetObjFunMp 类中的所有导出函数 +func GetObjFunMp(astPkg *ast.Package, objName string) map[string]*ast.FuncDecl { + funMp := make(map[string]*ast.FuncDecl) + // find all exported func of sturct objName + for _, fl := range astPkg.Files { + for _, d := range fl.Decls { + switch specDecl := d.(type) { + case *ast.FuncDecl: + if specDecl.Recv != nil { + if exp, ok := specDecl.Recv.List[0].Type.(*ast.StarExpr); ok { // Check that the type is correct first beforing throwing to parser + if strings.Compare(fmt.Sprint(exp.X), objName) == 0 { // is the same struct + funMp[specDecl.Name.String()] = specDecl // catch + } + } + } + } + } + } + + return funMp +} + +// AnalysisImport 分析整合import相关信息 +func AnalysisImport(astPkgs *ast.Package) map[string]string { + imports := make(map[string]string) + for _, f := range astPkgs.Files { + for _, p := range f.Imports { + k := "" + if p.Name != nil { + k = p.Name.Name + } + v := strings.Trim(p.Path.Value, `"`) + if len(k) == 0 { + n := strings.LastIndex(v, "/") + if n > 0 { + k = v[n+1:] + } else { + k = v + } + } + imports[k] = v + } + } + + return imports +} + +// GetImportPkg 分析得出 pkg +func GetImportPkg(i string) string { + n := strings.LastIndex(i, "/") + if n > 0 { + return i[n+1:] + } + return i +} diff --git a/myast/myast.go b/myast/myast.go new file mode 100644 index 0000000..03163a5 --- /dev/null +++ b/myast/myast.go @@ -0,0 +1,139 @@ +package myast + +import ( + "fmt" + "go/ast" + "go/token" + "strings" + + "github.com/xxjwxc/public/mydoc" + "github.com/xxjwxc/public/tools" +) + +type structAnalys struct { + ModPkg, ModFile string +} + +// NewStructAnalys 新建一个导出结构体类 +func NewStructAnalys(modPkg, modFile string) *structAnalys { + result := &structAnalys{ModPkg: modPkg, ModFile: modFile} + return result +} + +// ParserStruct 解析结构体定义及相关信息 +func (a *structAnalys) ParserStruct(astPkg *ast.Package, structName string) (info *mydoc.StructInfo) { + if astPkg == nil { + return nil + } + + // ast.Print(token.NewFileSet(), astPkg) + + for _, fl := range astPkg.Files { + for _, d := range fl.Decls { + switch specDecl := d.(type) { + case *ast.GenDecl: + for _, subitem := range specDecl.Specs { + switch specDecl.Tok { + case token.TYPE: + spec := subitem.(*ast.TypeSpec) + switch st := spec.Type.(type) { + case *ast.StructType: + if spec.Name.Name == structName { // find it + info = new(mydoc.StructInfo) + info.Pkg = astPkg.Name + for _, v := range specDecl.Doc.List { // 结构体注释 + t := strings.TrimSpace(strings.TrimPrefix(v.Text, "//")) + if strings.HasPrefix(t, structName) { // find note + t = strings.TrimSpace(strings.TrimPrefix(t, structName)) + info.Note += t + } + } + info.Name = structName + info.Items = a.structFieldInfo(astPkg, st) + return + } + } + } + } + } + } + } + return nil +} + +func (a *structAnalys) structFieldInfo(astPkg *ast.Package, sinfo *ast.StructType) (items []mydoc.ElementInfo) { + if sinfo == nil || sinfo.Fields == nil { + return + } + + importMP := AnalysisImport(astPkg) + + var info mydoc.ElementInfo + for _, field := range sinfo.Fields.List { + info = mydoc.ElementInfo{} + for _, fnames := range field.Names { + info.Name += fnames.Name + } + if field.Tag != nil { + info.Tag = strings.Trim(field.Tag.Value, "`") + } + if field.Comment != nil { + info.Note = strings.TrimSpace(field.Comment.Text()) + } + if field.Doc != nil { + info.Note += strings.TrimSpace(field.Doc.List[0].Text) + } + + switch exp := field.Type.(type) { + case *ast.SelectorExpr: // 非本文件包 + a.dealSelectorExpr(exp, &info, importMP) + case *ast.ArrayType: + info.IsArray = true + switch x := exp.Elt.(type) { + case *ast.SelectorExpr: // 非本文件包 + a.dealSelectorExpr(x, &info, importMP) + case *ast.Ident: + a.dealIdent(astPkg, x, &info) + } + case *ast.StarExpr: + switch x := exp.X.(type) { + case *ast.SelectorExpr: // 非本文件包 + a.dealSelectorExpr(x, &info, importMP) + case *ast.Ident: + a.dealIdent(astPkg, x, &info) + } + case *ast.Ident: // 本文件 + a.dealIdent(astPkg, exp, &info) + } + + if len(info.Type) == 0 { + panic(fmt.Sprintf("can not deal the type : %v", field.Type)) + } + + items = append(items, info) + } + return items +} + +func (a *structAnalys) dealSelectorExpr(exp *ast.SelectorExpr, info *mydoc.ElementInfo, importMP map[string]string) { // 非本文件包 + info.Type = exp.Sel.Name + if !tools.IsInternalType(info.Type) { // 非基础类型(time) + if x, ok := exp.X.(*ast.Ident); ok { + if v, ok := importMP[x.Name]; ok { + objFile := EvalSymlinks(a.ModPkg, a.ModFile, v) + objPkg := GetImportPkg(v) + astFile, _b := GetAstPkgs(objPkg, objFile) + if _b { + info.TypeRef = a.ParserStruct(astFile, info.Type) + } + } + } + } +} + +func (a *structAnalys) dealIdent(astPkg *ast.Package, exp *ast.Ident, info *mydoc.ElementInfo) { // 本文件 + info.Type = exp.Name + if !tools.IsInternalType(info.Type) { // 非基础类型 + info.TypeRef = a.ParserStruct(astPkg, info.Type) + } +} diff --git a/mybigcamel/def.go b/mybigcamel/def.go new file mode 100644 index 0000000..932b9f7 --- /dev/null +++ b/mybigcamel/def.go @@ -0,0 +1,21 @@ +package mybigcamel + +import ( + "strings" +) + +// Copied from golint +var commonInitialisms = []string{"ACL", "API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "LHS", "QPS", "RAM", "RHS", "RPC", "SLA", "SMTP", "SQL", "SSH", "TCP", "TLS", "TTL", "UDP", "UI", "UID", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XMPP", "XSRF", "XSS"} +var commonInitialismsReplacer *strings.Replacer +var uncommonInitialismsReplacer *strings.Replacer + +func init() { + var commonInitialismsForReplacer []string + var uncommonInitialismsForReplacer []string + for _, initialism := range commonInitialisms { + commonInitialismsForReplacer = append(commonInitialismsForReplacer, initialism, strings.Title(strings.ToLower(initialism))) + uncommonInitialismsForReplacer = append(uncommonInitialismsForReplacer, strings.Title(strings.ToLower(initialism)), initialism) + } + commonInitialismsReplacer = strings.NewReplacer(commonInitialismsForReplacer...) + uncommonInitialismsReplacer = strings.NewReplacer(uncommonInitialismsForReplacer...) +} diff --git a/mybigcamel/mybigcamel.go b/mybigcamel/mybigcamel.go new file mode 100644 index 0000000..d6d579c --- /dev/null +++ b/mybigcamel/mybigcamel.go @@ -0,0 +1,91 @@ +package mybigcamel + +import ( + "bytes" + "strings" +) + +/* +大驼峰命名规则转换 +基本满足大驼峰命名法则 首字母大写 “_” 忽略后大写 +device_id 对应 DeviceID create_time 对应 CreateTime location 对应 Location +*/ + +/* + 转换为大驼峰命名法则 + 首字母大写,“_” 忽略后大写 +*/ +func Marshal(name string) string { + if name == "" { + return "" + } + + temp := strings.Split(name, "_") + var s string + for _, v := range temp { + vv := []rune(v) + if len(vv) > 0 { + if bool(vv[0] >= 'a' && vv[0] <= 'z') { //首字母大写 + vv[0] -= 32 + } + s += string(vv) + } + } + + s = uncommonInitialismsReplacer.Replace(s) + //smap.Set(name, s) + return s +} + +/* + 回退网络模式 +*/ +func UnMarshal(name string) string { + const ( + lower = false + upper = true + ) + + if name == "" { + return "" + } + + var ( + value = commonInitialismsReplacer.Replace(name) + buf = bytes.NewBufferString("") + lastCase, currCase, nextCase, nextNumber bool + ) + + for i, v := range value[:len(value)-1] { + nextCase = bool(value[i+1] >= 'A' && value[i+1] <= 'Z') + nextNumber = bool(value[i+1] >= '0' && value[i+1] <= '9') + + if i > 0 { + if currCase == upper { + if lastCase == upper && (nextCase == upper || nextNumber == upper) { + buf.WriteRune(v) + } else { + if value[i-1] != '_' && value[i+1] != '_' { + buf.WriteRune('_') + } + buf.WriteRune(v) + } + } else { + buf.WriteRune(v) + if i == len(value)-2 && (nextCase == upper && nextNumber == lower) { + buf.WriteRune('_') + } + } + } else { + currCase = upper + buf.WriteRune(v) + } + lastCase = currCase + currCase = nextCase + } + + buf.WriteByte(value[len(value)-1]) + + s := strings.ToLower(buf.String()) + return s +} diff --git a/mybigcamel/mybigcamel_test.go b/mybigcamel/mybigcamel_test.go new file mode 100644 index 0000000..47b80f8 --- /dev/null +++ b/mybigcamel/mybigcamel_test.go @@ -0,0 +1,19 @@ +package mybigcamel + +import ( + "fmt" + "testing" +) + +func Test_cache(t *testing.T) { + SS := "OauthIDAPI" + + tmp0 := UnMarshal(SS) + fmt.Println(tmp0) + tmp1 := Marshal(tmp0) + fmt.Println(tmp1) + + if SS != tmp1 { + fmt.Println("false.") + } +} diff --git a/mycatch/mycatch.go b/mycatch/mycatch.go index c209658..a8dd9e0 100644 --- a/mycatch/mycatch.go +++ b/mycatch/mycatch.go @@ -11,10 +11,10 @@ package mycatch import ( "fmt" "os" - "os/exec" - "path/filepath" "runtime/debug" "time" + + "github.com/xxjwxc/public/tools" ) func Dmp() { @@ -29,17 +29,15 @@ func Dmp() { } func OnPrintErr(errstring string) { - file, _ := exec.LookPath(os.Args[0]) - path, _ := filepath.Abs(file) - path = filepath.Dir(path) + path := tools.GetModelPath() + "/err" + if !tools.CheckFileIsExist(path) { + os.MkdirAll(path, os.ModePerm) //生成多级目录 + } - now := time.Now() //获取当前时间 - pid := os.Getpid() //获取进程ID - - os.MkdirAll(path+"/err", os.ModePerm) //生成多级目录 - - time_str := now.Format("2006-01-02") //设定时间格式 - fname := fmt.Sprintf("%s/err/panic_%s-%x.log", path, time_str, pid) //保存错误信息文件名:程序名-进程ID-当前时间(年月日时分秒) + 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) diff --git a/myclipboard/myclipboard.go b/myclipboard/myclipboard.go new file mode 100644 index 0000000..5393672 --- /dev/null +++ b/myclipboard/myclipboard.go @@ -0,0 +1,22 @@ +package myclipboard + +import ( + "github.com/atotto/clipboard" + "github.com/xxjwxc/public/mylog" +) + +// Get 读取剪切板中的内容到字符串 +func Get() string { + // 读取剪切板中的内容到字符串 + content, err := clipboard.ReadAll() + if err != nil { + mylog.Error(err) + return "" + } + return content +} + +// Set 复制内容到剪切板 +func Set(cb string) { + clipboard.WriteAll(cb) +} diff --git a/mycui/button.go b/mycui/button.go new file mode 100644 index 0000000..4c94ac8 --- /dev/null +++ b/mycui/button.go @@ -0,0 +1,142 @@ +package mycui + +import ( + "fmt" + + "github.com/jroimartin/gocui" +) + +// Button button struct +type Button struct { + *gocui.Gui + label string + name string + handlers Handlers + ctype ComponentType + *Position + *Attributes +} + +// NewButton new button +func NewButton(gui *gocui.Gui, label, name string, x, y, width int) *Button { + if len(label) >= width { + width = len([]rune(name)) + 1 + } + + b := &Button{ + Gui: gui, + label: label, + name: name, + Position: &Position{ + x, + y, + x + width + 2, + y + 2, + }, + Attributes: &Attributes{ + textColor: gocui.ColorWhite | gocui.AttrBold, + textBgColor: gocui.ColorBlue, + hilightColor: gocui.ColorBlue | gocui.AttrBold, + hilightBgColor: gocui.ColorGreen, + }, + handlers: make(Handlers), + ctype: TypeButton, + } + + return b +} + +// AddHandler add handler +func (b *Button) AddHandler(key Key, handler Handler) *Button { + b.handlers[key] = handler + return b +} + +// SetTextColor add button fg and bg color +func (b *Button) SetTextColor(fgColor, bgColor gocui.Attribute) *Button { + b.textColor = fgColor + b.textBgColor = bgColor + return b +} + +// SetHilightColor add button fg and bg color +func (b *Button) SetHilightColor(fgColor, bgColor gocui.Attribute) *Button { + b.hilightColor = fgColor + b.hilightBgColor = bgColor + return b +} + +// GetLabel get button label +func (b *Button) GetLabel() string { + return b.label +} + +// GetPosition get button position +func (b *Button) GetPosition() *Position { + return b.Position +} + +// Focus focus to button +func (b *Button) Focus() { + b.Gui.Cursor = false + v, _ := b.Gui.SetCurrentView(b.label) + v.Highlight = true +} + +// UnFocus un focus +func (b *Button) UnFocus() { + v, _ := b.Gui.View(b.label) + v.Highlight = false +} + +// GetType get component type +func (b *Button) GetType() ComponentType { + return b.ctype +} + +// Draw draw button +func (b *Button) Draw() { + if v, err := b.Gui.SetView(b.label, b.X, b.Y, b.W, b.H); err != nil { + if err != gocui.ErrUnknownView { + panic(err) + } + + v.Title = b.name + v.Frame = false + + v.FgColor = b.textColor + v.BgColor = b.textBgColor + + v.SelFgColor = b.hilightColor + v.SelBgColor = b.hilightBgColor + + fmt.Fprint(v, fmt.Sprintf(" %s ", b.name)) + } + + if b.handlers != nil { + for key, handler := range b.handlers { + if err := b.Gui.SetKeybinding(b.label, key, gocui.ModNone, handler); err != nil { + panic(err) + } + } + } + +} + +// Close close button +func (b *Button) Close() { + if err := b.DeleteView(b.label); err != nil { + if err != gocui.ErrUnknownView { + panic(err) + } + } + + if b.handlers != nil { + b.DeleteKeybindings(b.label) + } +} + +// AddHandlerOnly ... +func (b *Button) AddHandlerOnly(key Key, handler Handler) { + b.handlers[key] = handler +} diff --git a/mycui/checkbox.go b/mycui/checkbox.go new file mode 100644 index 0000000..1d60efb --- /dev/null +++ b/mycui/checkbox.go @@ -0,0 +1,186 @@ +package mycui + +import ( + "fmt" + + "github.com/jroimartin/gocui" +) + +// CheckBox struct +type CheckBox struct { + *gocui.Gui + label string + isChecked bool + box *box + ctype ComponentType + *Position + *Attributes + handlers Handlers +} + +type box struct { + name string + *Position + *Attributes +} + +// NewCheckBox new checkbox +func NewCheckBox(gui *gocui.Gui, label string, x, y, labelWidth int) *CheckBox { + if len(label) > labelWidth { + labelWidth = len(label) + } + p := &Position{ + X: x, + Y: y, + W: x + labelWidth + 1, + H: y + 2, + } + + c := &CheckBox{ + Gui: gui, + label: label, + isChecked: false, + Position: p, + Attributes: &Attributes{ + textColor: gocui.ColorYellow | gocui.AttrBold, + textBgColor: gocui.ColorDefault, + }, + box: &box{ + name: label + "box", + Position: &Position{ + X: p.W, + Y: p.Y, + W: p.W + 2, + H: p.H, + }, + Attributes: &Attributes{ + textColor: gocui.ColorBlack, + textBgColor: gocui.ColorCyan, + }, + }, + handlers: make(Handlers), + ctype: TypeCheckBox, + } + + c.handlers[gocui.KeyEnter] = c.Check + c.handlers[gocui.KeySpace] = c.Check + return c +} + +// GetLabel get checkbox label +func (c *CheckBox) GetLabel() string { + return c.label +} + +// GetPosition get checkbox position +func (c *CheckBox) GetPosition() *Position { + return c.box.Position +} + +// Check check true or false +func (c *CheckBox) Check(g *gocui.Gui, v *gocui.View) error { + if v.Buffer() != "" { + v.Clear() + c.isChecked = false + } else { + fmt.Fprint(v, "X") + c.isChecked = true + } + + return nil +} + +// AddHandler add handler +func (c *CheckBox) AddHandler(key Key, handler Handler) *CheckBox { + c.handlers[key] = handler + return c +} + +// AddAttribute add text and bg color +func (c *CheckBox) AddAttribute(textColor, textBgColor gocui.Attribute) *CheckBox { + c.Attributes = &Attributes{ + textColor: textColor, + textBgColor: textBgColor, + } + + return c +} + +// IsChecked return check state +func (c *CheckBox) IsChecked() bool { + return c.isChecked +} + +// Focus focus to checkbox +func (c *CheckBox) Focus() { + c.Gui.Cursor = true + c.Gui.SetCurrentView(c.box.name) +} + +// UnFocus unfocus +func (c *CheckBox) UnFocus() { + c.Gui.Cursor = false +} + +// GetType get component type +func (c *CheckBox) GetType() ComponentType { + return c.ctype +} + +// Draw draw label and checkbox +func (c *CheckBox) Draw() { + // draw label + if v, err := c.Gui.SetView(c.label, c.X, c.Y, c.W, c.H); err != nil { + if err != gocui.ErrUnknownView { + panic(err) + } + + v.Frame = false + v.FgColor = c.textColor + v.BgColor = c.textBgColor + fmt.Fprint(v, c.label) + } + + // draw checkbox + b := c.box + if v, err := c.Gui.SetView(b.name, b.X, b.Y, b.W, b.H); err != nil { + if err != gocui.ErrUnknownView { + panic(err) + } + + v.Frame = false + v.FgColor = b.textColor + v.BgColor = b.textBgColor + + c.Gui.SetCurrentView(v.Name()) + + for key, handler := range c.handlers { + if err := c.Gui.SetKeybinding(v.Name(), key, gocui.ModNone, handler); err != nil { + panic(err) + } + } + } +} + +// Close close checkbox +func (c *CheckBox) Close() { + views := []string{ + c.label, + c.box.name, + } + + for _, v := range views { + if err := c.DeleteView(v); err != nil { + if err != gocui.ErrUnknownView { + panic(err) + } + } + } + + c.DeleteKeybindings(c.box.name) +} + +// AddHandlerOnly add handler not retrun +func (c *CheckBox) AddHandlerOnly(key Key, handler Handler) { + c.AddHandler(key, handler) +} diff --git a/mycui/form.go b/mycui/form.go new file mode 100644 index 0000000..9d92eb9 --- /dev/null +++ b/mycui/form.go @@ -0,0 +1,386 @@ +package mycui + +import ( + "github.com/jroimartin/gocui" +) + +// Form form struct +type Form struct { + *gocui.Gui + activeItem int + activeRadio int + id string + name string + inputs []*InputField + checkBoxs []*CheckBox + buttons []*Button + selects []*Select + //radios []*Radio + components []Component + closeFuncs []func() error + *Position +} + +// FormData form data struct +type FormData struct { + inputs map[string]string + checkBoxs map[string]bool + selects map[string]string + radio map[string]string +} + +// NewForm new form +func NewForm(gui *gocui.Gui, id, name string, x, y, w, h int) *Form { + f := &Form{ + Gui: gui, + activeItem: 0, + id: id, + name: name, + Position: &Position{ + X: x, + Y: y, + W: x + w, + H: y + h, + }, + } + + return f +} + +// AddInputField add input field to form +func (f *Form) AddInputField(label, name string, labelWidth, fieldWidth int) *InputField { + var y int + + p := f.getLastViewPosition() + if p != nil { + y = p.H + } else { + y = f.Y + 1 + } + + input := NewInputField( + f.Gui, + label, + name, + f.X+1, + y, + labelWidth, + fieldWidth, + ) + + f.inputs = append(f.inputs, input) + f.components = append(f.components, input) + + return input +} + +// AddButton add button to form +func (f *Form) AddButton(id, label string, handler Handler) *Button { + var x int + var y int + + p := f.getLastViewPosition() + if p != nil { + if f.isButtonLastView() { + x = p.W + y = p.Y - 1 + } else { + x = f.X + y = p.H + } + } else { + x = f.X + y = f.Y + } + + button := NewButton( + f.Gui, + id, + label, + x+1, + y+1, + len([]rune(label))+1, + ) + + button.AddHandler(gocui.KeyEnter, handler) + + f.buttons = append(f.buttons, button) + f.components = append(f.components, button) + + return button +} + +// AddCheckBox add checkbox +func (f *Form) AddCheckBox(label string, width int) *CheckBox { + var y int + + p := f.getLastViewPosition() + if p != nil { + y = p.H + } else { + y = f.Y + } + + checkbox := NewCheckBox( + f.Gui, + label, + f.X+1, + y, + width, + ) + + f.checkBoxs = append(f.checkBoxs, checkbox) + f.components = append(f.components, checkbox) + + return checkbox +} + +// AddSelect add select +func (f *Form) AddSelect(label, name string, labelWidth, listWidth int) *Select { + var y int + + p := f.getLastViewPosition() + if p != nil { + y = p.H + } else { + y = f.Y + } + + Select := NewSelect( + f.Gui, + label, + name, + f.X+1, + y, + labelWidth, + listWidth, + ) + + f.selects = append(f.selects, Select) + f.components = append(f.components, Select) + + return Select +} + +// AddCloseFunc add close function +func (f *Form) AddCloseFunc(function func() error) { + f.closeFuncs = append(f.closeFuncs, function) +} + +// GetFieldTexts form data +func (f *Form) GetFieldTexts() map[string]string { + data := make(map[string]string) + + if len(f.inputs) == 0 { + return data + } + + for _, item := range f.inputs { + data[item.GetLabel()] = item.GetFieldText() + } + + return data +} + +// GetFieldText get form data with field name +func (f *Form) GetFieldText(target string) string { + return f.GetFieldTexts()[target] +} + +// GetCheckBoxStates get checkbox states +func (f *Form) GetCheckBoxStates() map[string]bool { + state := make(map[string]bool) + + if len(f.checkBoxs) == 0 { + return state + } + + for _, box := range f.checkBoxs { + state[box.GetLabel()] = box.IsChecked() + } + + return state +} + +// GetCheckBoxState get checkbox states +func (f *Form) GetCheckBoxState(target string) bool { + return f.GetCheckBoxStates()[target] +} + +// GetSelectedOpts get selected options +func (f *Form) GetSelectedOpts() map[string]string { + opts := make(map[string]string) + + if len(f.selects) == 0 { + return opts + } + + for _, Select := range f.selects { + opts[Select.GetLabel()] = Select.GetSelected() + } + + return opts +} + +// GetSelectedOpt get selected options +func (f *Form) GetSelectedOpt(target string) string { + return f.GetSelectedOpts()[target] +} + +// GetSelectedRadio get selected radio +func (f *Form) GetSelectedRadio(target string) string { + return f.GetSelectedOpts()[target] +} + +// GetFormData get form data +func (f *Form) GetFormData() *FormData { + fd := &FormData{ + inputs: f.GetFieldTexts(), + checkBoxs: f.GetCheckBoxStates(), + selects: f.GetSelectedOpts(), + } + + return fd +} + +// GetInputs get inputs +func (f *Form) GetInputs() []*InputField { + return f.inputs +} + +// GetCheckBoxs get checkboxs +func (f *Form) GetCheckBoxs() []*CheckBox { + return f.checkBoxs +} + +// GetButtons get buttons +func (f *Form) GetButtons() []*Button { + return f.buttons +} + +// GetSelects get selects +func (f *Form) GetSelects() []*Select { + return f.selects +} + +// GetItems get items +func (f *Form) GetItems() []Component { + return f.components +} + +// SetCurrentItem set current item index +func (f *Form) SetCurrentItem(index int) *Form { + f.activeItem = index + f.components[index].Focus() + return f +} + +// GetCurrentItem get current item index +func (f *Form) GetCurrentItem() int { + return f.activeItem +} + +// Validate validate form items +func (f *Form) Validate() bool { + isValid := true + for _, item := range f.inputs { + if !item.Validate() { + isValid = false + } + } + + return isValid +} + +// NextItem to next item +func (f *Form) NextItem(g *gocui.Gui, v *gocui.View) error { + f.components[f.activeItem].UnFocus() + f.activeItem = (f.activeItem + 1) % len(f.components) + f.components[f.activeItem].Focus() + return nil +} + +// PreItem to pre item +func (f *Form) PreItem(g *gocui.Gui, v *gocui.View) error { + f.components[f.activeItem].UnFocus() + + if f.activeItem-1 < 0 { + f.activeItem = len(f.components) - 1 + } else { + f.activeItem = (f.activeItem - 1) % len(f.components) + } + + f.components[f.activeItem].Focus() + + return nil +} + +// Draw form +func (f *Form) Draw() { + if v, err := f.Gui.SetView(f.id, f.X, f.Y, f.W+1, f.H+1); err != nil { + if err != gocui.ErrUnknownView { + panic(err) + } + + v.Title = f.name + } + + for _, cp := range f.components { + p := cp.GetPosition() + if p.W > f.W { + f.W = p.W + } + if p.H > f.H { + f.H = p.H + } + cp.AddHandlerOnly(gocui.KeyTab, f.NextItem) + cp.AddHandlerOnly(gocui.KeyArrowDown, f.NextItem) + cp.AddHandlerOnly(gocui.KeyArrowUp, f.PreItem) + cp.Draw() + } + + f.SetView(f.id, f.X, f.Y, f.W+1, f.H+1) + + if len(f.components) != 0 { + f.components[0].Focus() + } +} + +// Close close form +func (f *Form) Close(g *gocui.Gui, v *gocui.View) error { + if err := f.Gui.DeleteView(f.id); err != nil { + if err != gocui.ErrUnknownView { + panic(err) + } + } + + for _, c := range f.components { + c.Close() + } + + if len(f.closeFuncs) != 0 { + for _, f := range f.closeFuncs { + f() + } + } + + return nil +} + +func (f *Form) getLastViewPosition() *Position { + cpl := len(f.components) + if cpl == 0 { + return nil + } + + return f.components[cpl-1].GetPosition() +} + +func (f *Form) isButtonLastView() bool { + cpl := len(f.components) + if cpl == 0 { + return false + } + + return f.components[cpl-1].GetType() == TypeButton +} diff --git a/mycui/inputField.go b/mycui/inputField.go new file mode 100644 index 0000000..b29905e --- /dev/null +++ b/mycui/inputField.go @@ -0,0 +1,376 @@ +package mycui + +import ( + "fmt" + "strings" + + "github.com/jroimartin/gocui" +) + +// Margin struct +type Margin struct { + top int + left int +} + +// InputField struct +type InputField struct { + *gocui.Gui + label *Label + field *Field +} + +// Label struct +type Label struct { + text string + name string + width int + drawFrame bool + *Position + *Attributes + margin *Margin +} + +// Field struct +type Field struct { + text string + width int + drawFrame bool + handlers Handlers + margin *Margin + mask bool + editable bool + ctype ComponentType + *Position + *Attributes + *Validator +} + +var labelPrefix = "label" + +// NewInputField new input label and field +func NewInputField(gui *gocui.Gui, labelText, name string, x, y, labelWidth, fieldWidth int) *InputField { + gui.Cursor = true + + // label psition + lp := &Position{ + x, + y, + x + labelWidth + 1, + y + 2, + } + + // field position + fp := &Position{ + lp.W, + lp.Y, + lp.W + fieldWidth, + lp.H, + } + + // new label + label := &Label{ + text: labelText, + name: name, + width: labelWidth, + Position: lp, + Attributes: &Attributes{ + textColor: gocui.ColorYellow, + textBgColor: gocui.ColorDefault, + }, + drawFrame: false, + margin: &Margin{ + top: 0, + left: 0, + }, + } + + // new field + field := &Field{ + width: fieldWidth, + Position: fp, + Attributes: &Attributes{ + textColor: gocui.ColorBlack, + textBgColor: gocui.ColorCyan, + }, + handlers: make(Handlers), + drawFrame: false, + margin: &Margin{ + top: 0, + left: 0, + }, + Validator: NewValidator(gui, label.text+"validator", fp.X, fp.Y+1, fp.W, fp.H+1), + editable: true, + ctype: TypeInputField, + } + + // new input field + i := &InputField{ + Gui: gui, + label: label, + field: field, + } + + return i +} + +// AddFieldAttribute add field colors +func (i *InputField) AddFieldAttribute(textColor, textBgColor, fgColor, bgColor gocui.Attribute) *InputField { + i.field.Attributes = &Attributes{ + textColor: textColor, + textBgColor: textBgColor, + hilightColor: fgColor, + hilightBgColor: bgColor, + } + return i +} + +// AddLabelAttribute add label colors +func (i *InputField) AddLabelAttribute(textColor, textBgColor gocui.Attribute) *InputField { + i.label.Attributes = &Attributes{ + textColor: textColor, + textBgColor: textBgColor, + } + + return i +} + +// AddHandler add keybinding +func (i *InputField) AddHandler(key Key, handler Handler) *InputField { + i.field.handlers[key] = handler + return i +} + +// AddMarginTop add margin top +func (i *InputField) AddMarginTop(top int) *InputField { + i.label.margin.top += top + i.field.margin.top += top + return i +} + +// AddMarginLeft add margin left +func (i *InputField) AddMarginLeft(left int) *InputField { + i.label.margin.left += left + i.field.margin.left += left + return i +} + +// AddValidate add input validator +func (i *InputField) AddValidate(errMsg string, validate func(value string) bool) *InputField { + i.field.AddValidate(errMsg, validate) + return i +} + +// SetLabelBorder draw label border +func (i *InputField) SetLabelBorder() *InputField { + i.label.drawFrame = true + return i +} + +// SetFieldBorder draw field border +func (i *InputField) SetFieldBorder() *InputField { + i.field.drawFrame = true + return i +} + +// SetMask set input field to mask '*' +func (i *InputField) SetMask() *InputField { + i.field.mask = true + return i +} + +// SetMaskKeybinding set or unset input field to mask '*' with key +func (i *InputField) SetMaskKeybinding(key Key) *InputField { + if err := i.Gui.SetKeybinding(i.label.text, key, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { + v.Mask ^= '*' + return nil + }); err != nil { + panic(err) + } + + return i +} + +// SetText set text +func (i *InputField) SetText(text string) *InputField { + i.field.text = text + return i +} + +// SetCursor set input field cursor +func (i *InputField) SetCursor(b bool) *InputField { + i.Gui.Cursor = b + return i +} + +// SetEditable if editmode is true can input +func (i *InputField) SetEditable(b bool) *InputField { + i.field.editable = b + return i +} + +// Focus focus to input field +func (i *InputField) Focus() { + i.Gui.Cursor = true + i.Gui.SetCurrentView(i.label.text) +} + +// UnFocus un focus +func (i *InputField) UnFocus() { + i.Gui.Cursor = false +} + +// Edit input field editor +func (i *InputField) Edit(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) { + switch { + case ch != 0 && mod == 0: + v.EditWrite(ch) + case key == gocui.KeySpace: + v.EditWrite(' ') + case key == gocui.KeyBackspace || key == gocui.KeyBackspace2: + v.EditDelete(true) + case key == gocui.KeyArrowLeft: + v.MoveCursor(-1, 0, false) + case key == gocui.KeyArrowRight: + v.MoveCursor(+1, 0, false) + } + + // get field text + i.field.text = i.cutNewline(v.Buffer()) + + // validate + i.field.Validate(i.GetFieldText()) +} + +// GetFieldText get input field text +func (i *InputField) GetFieldText() string { + return i.field.text +} + +// GetLabel get label text +func (i *InputField) GetLabel() string { + return i.label.text +} + +// GetPosition get input field position +func (i *InputField) GetPosition() *Position { + return i.field.Position +} + +// Validate validate field +func (i *InputField) Validate() bool { + i.field.Validate(i.GetFieldText()) + return i.field.IsValid() +} + +// IsValid valid field data will be return true +func (i *InputField) IsValid() bool { + return i.field.Validator.IsValid() +} + +// GetType get component type +func (i *InputField) GetType() ComponentType { + return i.field.ctype +} + +// Draw draw label and field +func (i *InputField) Draw() { + // draw label + x, y, w, h := i.addMargin(i.label) + if v, err := i.Gui.SetView(labelPrefix+i.label.text, x, y, w, h); err != nil { + if err != gocui.ErrUnknownView { + panic(err) + } + + v.Frame = i.label.drawFrame + + v.FgColor = i.label.textColor | gocui.AttrBold + v.BgColor = i.label.textBgColor + + fmt.Fprint(v, i.label.name) + } + + // draw input + x, y, w, h = i.addMargin(i.field) + if v, err := i.Gui.SetView(i.label.text, x, y, w, h); err != nil { + if err != gocui.ErrUnknownView { + panic(err) + } + + v.Frame = i.field.drawFrame + + v.FgColor = i.field.textColor + v.BgColor = i.field.textBgColor + + v.Editable = i.field.editable + v.Editor = i + + if i.field.mask { + v.Mask = '*' + } + + if i.field.text != "" { + fmt.Fprint(v, i.field.text) + } + + // focus input field + i.Focus() + } + + // set keybindings + if i.field.handlers != nil { + for key, handler := range i.field.handlers { + if err := i.Gui.SetKeybinding(i.label.text, key, gocui.ModNone, handler); err != nil { + panic(err) + } + } + } +} + +// Close close input field +func (i *InputField) Close() { + views := []string{ + i.label.text, + labelPrefix + i.label.text, + } + + for _, v := range views { + if err := i.DeleteView(v); err != nil { + if err != gocui.ErrUnknownView { + panic(err) + } + } + } + + if i.field.handlers != nil { + i.DeleteKeybindings(i.label.text) + } + + if i.field.Validator != nil { + i.field.Validator.CloseValidateMsg() + } +} + +func (i *InputField) addMargin(view interface{}) (int, int, int, int) { + switch v := view.(type) { + case *Field: + p := v.Position + m := v.margin + return p.X + m.left, p.Y + m.top, p.W + m.left, p.H + m.top + case *Label: + p := v.Position + m := v.margin + return p.X + m.left, p.Y + m.top, p.W + m.left, p.H + m.top + default: + panic("Unkown type") + } +} + +func (i *InputField) cutNewline(text string) string { + return strings.Replace(text, "\n", "", -1) +} + +// AddHandlerOnly add handler not return +func (i *InputField) AddHandlerOnly(key Key, handler Handler) { + i.AddHandler(key, handler) +} diff --git a/mycui/modal.go b/mycui/modal.go new file mode 100644 index 0000000..ff454d6 --- /dev/null +++ b/mycui/modal.go @@ -0,0 +1,200 @@ +package mycui + +import ( + "fmt" + "math" + "strings" + + "github.com/jroimartin/gocui" +) + +// Modal struct +type Modal struct { + *gocui.Gui + name string + textArea *textArea + activeButton int + buttons []*Button + *Attributes + *Position +} + +type textArea struct { + *gocui.Gui + name string + text string + *Attributes + *Position +} + +// NewModal new modal +func NewModal(gui *gocui.Gui, x, y, w int) *Modal { + p := &Position{ + X: x, + Y: y, + W: w, + H: y + 3, + } + + return &Modal{ + Gui: gui, + name: "modal", + activeButton: 0, + Attributes: &Attributes{ + textColor: gocui.ColorWhite, + textBgColor: gocui.ColorBlue, + }, + Position: p, + textArea: &textArea{ + Gui: gui, + name: "textArea", + Attributes: &Attributes{ + textColor: gocui.ColorWhite, + textBgColor: gocui.ColorBlue, + }, + Position: &Position{ + X: p.X + 1, + Y: p.Y + 1, + W: p.W - 1, + H: p.H - 1, + }, + }, + } +} + +// SetText set text +func (m *Modal) SetText(text string) *Modal { + m.textArea.text = text + h := int(roundUp(float64(len(text)/(m.W-m.X)), 0)) + + newLineCount := strings.Count(text, "\n") + if newLineCount > h { + h = newLineCount + } + + m.textArea.H += h + m.H += h + + return m +} + +// SetTextColor set text color +func (m *Modal) SetTextColor(textColor gocui.Attribute) *Modal { + m.textArea.textColor = textColor + return m +} + +// SetBgColor set bg color +func (m *Modal) SetBgColor(textColor gocui.Attribute) *Modal { + m.textArea.textBgColor = textColor + return m +} + +// AddButton add button +func (m *Modal) AddButton(id, label string, key Key, handler Handler) *Button { + var x, y, w int + if len(m.buttons) == 0 { + w = m.W - 5 + x = w - len(label) + y = m.H - 1 + m.H += 2 + } else { + p := m.buttons[len(m.buttons)-1].GetPosition() + w = p.W - 10 + x = w - len(label) + y = p.Y + } + + button := NewButton(m.Gui, id, label, x, y, len(label)). + AddHandler(gocui.KeyTab, m.nextButton). + AddHandler(key, handler). + SetTextColor(gocui.ColorWhite, gocui.ColorBlack). + SetHilightColor(gocui.ColorBlack, gocui.ColorWhite) + + m.buttons = append(m.buttons, button) + return button +} + +// GetPosition get modal position +func (m *Modal) GetPosition() *Position { + return m.Position +} + +// Draw draw modal +func (m *Modal) Draw() { + // modal + if v, err := m.Gui.SetView(m.name, m.X, m.Y, m.W, m.H); err != nil { + if err != gocui.ErrUnknownView { + panic(err) + } + + v.Frame = false + v.FgColor = m.textColor + v.BgColor = m.textBgColor + } + + // text area + area := m.textArea + if area.text != "" { + if v, err := area.Gui.SetView(area.name, area.X, area.Y, area.W, area.H); err != nil { + if err != gocui.ErrUnknownView { + panic(err) + } + + v.Wrap = true + v.Frame = false + + v.FgColor = area.textColor + v.BgColor = area.textBgColor + + fmt.Fprint(v, area.text) + } + } + + // button + for _, b := range m.buttons { + b.Draw() + } + + if len(m.buttons) != 0 { + m.activeButton = len(m.buttons) - 1 + m.buttons[m.activeButton].Focus() + } +} + +// Close close modal +func (m *Modal) Close() { + if err := m.DeleteView(m.name); err != nil { + if err != gocui.ErrUnknownView { + panic(err) + } + } + + if err := m.DeleteView(m.textArea.name); err != nil { + if err != gocui.ErrUnknownView { + panic(err) + } + } + + for _, b := range m.buttons { + b.Close() + } +} + +// nextButton focus netxt button +func (m *Modal) nextButton(g *gocui.Gui, v *gocui.View) error { + m.buttons[m.activeButton].UnFocus() + m.activeButton = (m.activeButton + 1) % len(m.buttons) + m.buttons[m.activeButton].Focus() + return nil +} + +func roundUp(num, places float64) float64 { + shift := math.Pow(10, places) + return roundUpInt(num*shift) / shift +} + +func roundUpInt(num float64) float64 { + t := math.Trunc(num) + return t + math.Copysign(1, num) +} diff --git a/mycui/select.go b/mycui/select.go new file mode 100644 index 0000000..294e8e0 --- /dev/null +++ b/mycui/select.go @@ -0,0 +1,242 @@ +package mycui + +import ( + "fmt" + "strings" + + "github.com/jroimartin/gocui" +) + +// Select struct +type Select struct { + *InputField + options []string + currentOpt int + isExpanded bool + ctype ComponentType + listColor *Attributes + listHandlers Handlers +} + +// NewSelect new select +func NewSelect(gui *gocui.Gui, label, name string, x, y, labelWidth, fieldWidth int) *Select { + + s := &Select{ + InputField: NewInputField(gui, label, name, x, y, labelWidth, fieldWidth), + listHandlers: make(Handlers), + ctype: TypeSelect, + } + + s.AddHandler(gocui.KeyEnter, s.expandOpt) + s.AddAttribute(gocui.ColorBlack, gocui.ColorWhite, gocui.ColorBlack, gocui.ColorGreen). + AddListHandler('j', s.nextOpt). + AddListHandler('k', s.preOpt). + AddListHandler(gocui.KeyArrowDown, s.nextOpt). + AddListHandler(gocui.KeyArrowUp, s.preOpt). + AddListHandler(gocui.KeyEnter, s.selectOpt). + SetEditable(false) + + return s +} + +// AddOptions add select options +func (s *Select) AddOptions(opts ...string) *Select { + for _, opt := range opts { + s.options = append(s.options, opt) + } + return s +} + +// AddOption add select option +func (s *Select) AddOption(opt string) *Select { + s.options = append(s.options, opt) + return s +} + +// AddAttribute add select attribute +func (s *Select) AddAttribute(textColor, textBgColor, fgColor, bgColor gocui.Attribute) *Select { + s.listColor = &Attributes{ + textColor: textColor, + textBgColor: textBgColor, + hilightColor: fgColor, + hilightBgColor: bgColor, + } + + return s +} + +// AddListHandler add list handler +func (s *Select) AddListHandler(key Key, handler Handler) *Select { + s.listHandlers[key] = handler + return s +} + +// GetSelected get selected option +func (s *Select) GetSelected() string { + return s.options[s.currentOpt] +} + +// SetSelected set the default selected +func (s *Select) SetSelected(str string) { + for i := 0; i < len(s.options); i++ { + if strings.EqualFold(s.options[i], str) { + s.currentOpt = i + break + } + } +} + +// Focus set focus to select +func (s *Select) Focus() { + s.Gui.Cursor = true + s.Gui.SetCurrentView(s.GetLabel()) +} + +// UnFocus un focus +func (s *Select) UnFocus() { + s.Gui.Cursor = false +} + +// GetType get component type +func (s *Select) GetType() ComponentType { + return s.ctype +} + +// Close close select +func (s *Select) Close() { + s.InputField.Close() + if s.isExpanded { + for _, opt := range s.options { + s.DeleteView(opt) + s.DeleteKeybindings(opt) + } + } +} + +// Draw draw select +func (s *Select) Draw() { + if len(s.options) > 0 { + s.InputField.SetText(s.options[s.currentOpt]) + } + s.InputField.Draw() +} + +func (s *Select) nextOpt(g *gocui.Gui, v *gocui.View) error { + maxOpt := len(s.options) + if maxOpt == 0 { + return nil + } + + v.Highlight = false + + next := s.currentOpt + 1 + if next >= maxOpt { + next = s.currentOpt + } + + s.currentOpt = next + v, _ = g.SetCurrentView(s.options[next]) + + v.Highlight = true + + return nil +} + +func (s *Select) preOpt(g *gocui.Gui, v *gocui.View) error { + maxOpt := len(s.options) + if maxOpt == 0 { + return nil + } + + v.Highlight = false + + next := s.currentOpt - 1 + if next < 0 { + next = 0 + } + + s.currentOpt = next + v, _ = g.SetCurrentView(s.options[next]) + + v.Highlight = true + + return nil +} + +func (s *Select) selectOpt(g *gocui.Gui, v *gocui.View) error { + if !s.isExpanded { + s.expandOpt(g, v) + } else { + s.closeOpt(g, v) + } + + return nil +} + +func (s *Select) expandOpt(g *gocui.Gui, vi *gocui.View) error { + if s.hasOpts() { + s.isExpanded = true + g.Cursor = false + + x := s.field.X + w := s.field.W + + y := s.field.Y + h := y + 2 + + for _, opt := range s.options { + y++ + h++ + if v, err := g.SetView(opt, x, y, w, h); err != nil { + if err != gocui.ErrUnknownView { + panic(err) + } + + v.Frame = false + v.SelFgColor = s.listColor.textColor + v.SelBgColor = s.listColor.textBgColor + v.FgColor = s.listColor.hilightColor + v.BgColor = s.listColor.hilightBgColor + + for key, handler := range s.listHandlers { + if err := g.SetKeybinding(v.Name(), key, gocui.ModNone, handler); err != nil { + panic(err) + } + } + + fmt.Fprint(v, opt) + } + + } + + v, _ := g.SetCurrentView(s.options[s.currentOpt]) + v.Highlight = true + } + + return nil +} + +func (s *Select) closeOpt(g *gocui.Gui, v *gocui.View) error { + s.isExpanded = false + g.Cursor = true + + for _, opt := range s.options { + g.DeleteView(opt) + g.DeleteKeybindings(opt) + } + + v, _ = g.SetCurrentView(s.GetLabel()) + + v.Clear() + + fmt.Fprint(v, s.GetSelected()) + + return nil +} + +func (s *Select) hasOpts() bool { + if len(s.options) > 0 { + return true + } + return false +} diff --git a/mycui/test.zip b/mycui/test.zip new file mode 100644 index 0000000..c681483 Binary files /dev/null and b/mycui/test.zip differ diff --git a/mycui/type.go b/mycui/type.go new file mode 100644 index 0000000..2b7c68a --- /dev/null +++ b/mycui/type.go @@ -0,0 +1,56 @@ +package mycui + +import "github.com/jroimartin/gocui" + +// Key define kye type +type Key interface{} + +// Handler define handler type +type Handler func(g *gocui.Gui, v *gocui.View) error + +// Handlers handlers +type Handlers map[Key]Handler + +// Component form component interface +type Component interface { + GetLabel() string + GetPosition() *Position + GetType() ComponentType + Focus() + UnFocus() + Draw() + Close() + AddHandlerOnly(Key, Handler) +} + +// Attributes text and hilight color +type Attributes struct { + textColor gocui.Attribute + textBgColor gocui.Attribute + hilightColor gocui.Attribute + hilightBgColor gocui.Attribute +} + +// Position component position +type Position struct { + X, Y int + W, H int +} + +// ComponentType component type +type ComponentType int + +const ( + // TypeInputField type is input component + TypeInputField ComponentType = iota + // TypeSelect type is select component + TypeSelect + // TypeButton type is button component + TypeButton + // TypeCheckBox type is checkbox component + TypeCheckBox + // TypeRadio type is radio component + TypeRadio + // TypeTable type is table component + TypeTable +) diff --git a/mycui/validator.go b/mycui/validator.go new file mode 100644 index 0000000..7d35548 --- /dev/null +++ b/mycui/validator.go @@ -0,0 +1,90 @@ +package mycui + +import ( + "fmt" + + "github.com/jroimartin/gocui" +) + +// Validate validate struct +type Validate struct { + ErrMsg string + Do func(value string) bool +} + +// Validator validate struct +type Validator struct { + *gocui.Gui + name string + errMsg string + isValid bool + validates []Validate + *Position +} + +// NewValidator new validator +func NewValidator(gui *gocui.Gui, name string, x, y, w, h int) *Validator { + return &Validator{ + Gui: gui, + name: name, + isValid: true, + Position: &Position{ + X: x, + Y: y, + W: w, + H: h, + }, + } +} + +// AddValidate add validate +func (v *Validator) AddValidate(errMsg string, validate func(value string) bool) { + v.validates = append(v.validates, Validate{ + ErrMsg: errMsg, + Do: validate, + }) + + if v.X+len(errMsg) > v.W { + v.W += len(errMsg) + } +} + +// DispValidateMsg display validate error message +func (v *Validator) DispValidateMsg() { + if vi, err := v.SetView(v.name, v.X, v.Y, v.W, v.H); err != nil { + if err != gocui.ErrUnknownView { + panic(err) + } + + vi.Frame = false + vi.BgColor = gocui.ColorDefault + vi.FgColor = gocui.ColorRed + + fmt.Fprint(vi, v.errMsg) + } +} + +// CloseValidateMsg close validate error message +func (v *Validator) CloseValidateMsg() { + v.DeleteView(v.name) +} + +// IsValid if valid return true +func (v *Validator) IsValid() bool { + return v.isValid +} + +// Validate validate value +func (v *Validator) Validate(value string) { + for _, validate := range v.validates { + if !validate.Do(value) { + v.errMsg = validate.ErrMsg + v.isValid = false + v.DispValidateMsg() + break + } else { + v.isValid = true + v.CloseValidateMsg() + } + } +} diff --git a/mydoc/common.go b/mydoc/common.go new file mode 100644 index 0000000..f9d3d4f --- /dev/null +++ b/mydoc/common.go @@ -0,0 +1,172 @@ +package mydoc + +import ( + "bytes" + "html/template" + "reflect" + "strings" + + "github.com/xxjwxc/public/mydoc/mymarkdown" + "github.com/xxjwxc/public/mydoc/myswagger" +) + +func (m *model) analysisStructInfo(info *StructInfo) { + if info != nil { + for i := 0; i < len(info.Items); i++ { + tag := reflect.StructTag(strings.Trim(info.Items[i].Tag, "`")) + + // json + tagStr := tag.Get("json") + if tagStr == "-" || tagStr == "" { + tagStr = tag.Get("url") + } + tagStrs := strings.Split(tagStr, ",") + if len(tagStrs[0]) > 0 { + info.Items[i].Name = tagStrs[0] + } + // -------- end + + // required + tagStr = tag.Get("binding") + tagStrs = strings.Split(tagStr, ",") + for _, v := range tagStrs { + if strings.EqualFold(v, "required") { + info.Items[i].Requierd = true + break + } + } + // ---------------end + + // default + info.Items[i].Default = tag.Get("default") + // ---------------end + + if info.Items[i].TypeRef != nil { + m.analysisStructInfo(info.Items[i].TypeRef) + } + } + + } +} + +func (m *model) setDefinition(doc *myswagger.DocSwagger, tmp *StructInfo) string { + if tmp != nil { + var def myswagger.Definition + def.Type = "object" + def.Properties = make(map[string]myswagger.Propertie) + for _, v2 := range tmp.Items { + if v2.TypeRef != nil { + def.Properties[v2.Name] = myswagger.Propertie{ + Ref: m.setDefinition(doc, v2.TypeRef), + } + } else { + def.Properties[v2.Name] = myswagger.Propertie{ + Type: myswagger.GetKvType(v2.Type, v2.IsArray, true), + Format: myswagger.GetKvType(v2.Type, v2.IsArray, false), + Description: v2.Note, + } + } + } + doc.AddDefinitions(tmp.Name, def) + return "#/definitions/" + tmp.Name + } + return "" +} + +func buildRelativePath(prepath, routerPath string) string { + if strings.HasSuffix(prepath, "/") { + if strings.HasPrefix(routerPath, "/") { + return prepath + strings.TrimPrefix(routerPath, "/") + } + return prepath + routerPath + } + + if strings.HasPrefix(routerPath, "/") { + return prepath + routerPath + } + + return prepath + "/" + routerPath +} + +func conType(pkg *StructInfo, tp string, isArray bool) string { + re := tp + if pkg != nil { + re = "`" + pkg.Pkg + "." + tp + "`" + } + if isArray { + re = "[]" + re + } + return re +} + +func (m *model) buildSubStructMD(doc *mymarkdown.DocMarkdown, tmp *StructInfo) (jsonMp map[string]interface{}) { + jsonMp = make(map[string]interface{}) + if tmp != nil { + var info mymarkdown.TmpTable + info.SC = "`" + info.Pkg = tmp.Pkg + info.Name = tmp.Name + info.Note = tmp.Note + for _, v := range tmp.Items { + if v.TypeRef != nil { + mp := m.buildSubStructMD(doc, v.TypeRef) + jsonMp[v.Name] = doc.GetTypeList(mp, v.IsArray) + } else { + jsonMp[v.Name] = doc.GetValueType(v.Type, v.Default, v.IsArray) + } + + info.Item = append(info.Item, mymarkdown.TmpElement{ + Name: v.Name, + Requierd: doc.GetBoolStr(v.Requierd), + Type: conType(v.TypeRef, v.Type, v.IsArray), + Note: v.Note, + }) + } + + tmpl, err := template.New("struct_mod"). + Parse(doc.GetTableInfo()) + if err != nil { + panic(err) + } + var buf bytes.Buffer + tmpl.Execute(&buf, info) + doc.AddBaseStruct(tmp.Pkg, tmp.Name, buf.String()) + } + return +} + +func (m *model) buildDefinitionMD(doc *mymarkdown.DocMarkdown, tmp *StructInfo) (rest string, jsonMp map[string]interface{}) { + jsonMp = make(map[string]interface{}) + if tmp != nil { + var info mymarkdown.TmpTable + info.SC = "`" + info.Name = tmp.Name + info.Note = tmp.Note + for _, v := range tmp.Items { + if v.TypeRef != nil { + mp := m.buildSubStructMD(doc, v.TypeRef) + jsonMp[v.Name] = doc.GetTypeList(mp, v.IsArray) + } else { + jsonMp[v.Name] = doc.GetValueType(v.Type, v.Default, v.IsArray) + } + + info.Item = append(info.Item, mymarkdown.TmpElement{ + Name: v.Name, + Requierd: doc.GetBoolStr(v.Requierd), + Type: conType(v.TypeRef, v.Type, v.IsArray), + Note: v.Note, + }) + } + + tmpl, err := template.New("struct_mod"). + Parse(doc.GetTableInfo()) + if err != nil { + panic(err) + } + var buf bytes.Buffer + tmpl.Execute(&buf, info) + rest = buf.String() + } + + return +} diff --git a/mydoc/def.go b/mydoc/def.go new file mode 100644 index 0000000..8539444 --- /dev/null +++ b/mydoc/def.go @@ -0,0 +1,30 @@ +package mydoc + +// ElementInfo 结构信息 +type ElementInfo struct { + Name string // 参数名 + // URL string // web 访问参数 + Tag string // 标签 + Type string // 类型 + TypeRef *StructInfo // 类型定义 + IsArray bool // 是否是数组 + Requierd bool // 是否必须 + Note string // 注释 + Default string // 默认值 +} + +// StructInfo struct define +type StructInfo struct { + Items []ElementInfo // 结构体元素 + Note string // 注释 + Name string //结构体名字 + Pkg string // 包名 +} + +// DocModel model +type DocModel struct { + RouterPath string + Methods []string + Note string + Req, Resp *StructInfo +} diff --git a/mydoc/mydoc.go b/mydoc/mydoc.go new file mode 100644 index 0000000..3b0f53f --- /dev/null +++ b/mydoc/mydoc.go @@ -0,0 +1,106 @@ +package mydoc + +import ( + "html/template" + + "github.com/xxjwxc/public/mydoc/mymarkdown" + "github.com/xxjwxc/public/mydoc/myswagger" + "github.com/xxjwxc/public/tools" +) + +type model struct { + Group string // group 标记 + MP map[string]map[string]DocModel +} + +// NewDoc 新建一个doc模板 +func NewDoc(group string) *model { + doc := &model{Group: group} + doc.MP = make(map[string]map[string]DocModel) + return doc +} + +// 添加一个 +func (m *model) AddOne(group string, routerPath string, methods []string, note string, req, resp *StructInfo) { + if m.MP[group] == nil { + m.MP[group] = make(map[string]DocModel) + } + + m.analysisStructInfo(req) + m.analysisStructInfo(resp) + m.MP[group][routerPath] = DocModel{ + RouterPath: routerPath, + Methods: methods, + Note: note, + Req: req, + Resp: resp, + } +} + +// GenSwagger 生成swagger文档 +func (m *model) GenSwagger(outPath string) { + doc := myswagger.NewDoc() + reqRef, _ := "", "" + + // define + for _, v := range m.MP { + for _, v1 := range v { + reqRef = m.setDefinition(doc, v1.Req) + //respRef = m.setDefinition(doc, v1.Resp) + } + } + // ------------------end + + for k, v := range m.MP { + tag := myswagger.Tag{Name: k} + doc.AddTag(tag) + for _, v1 := range v { + var p myswagger.Param + p.Tags = []string{k} + p.Summary = v1.Note + p.Description = v1.Note + // p.OperationID = "addPet" + p.Parameters = []myswagger.Element{myswagger.Element{ + In: "body", // body, header, formData, query, path + Name: "body", // body, header, formData, query, path + Description: v1.Note, + Required: true, + Schema: myswagger.Schema{ + Ref: reqRef, + }, + }} + doc.AddPatch(buildRelativePath(m.Group, v1.RouterPath), p, v1.Methods...) + } + } + + jsonsrc := doc.GetAPIString() + + tools.WriteFile(outPath+"swagger.json", []string{jsonsrc}, true) +} + +// GenMd 生成 markdown 文档 +func (m *model) GenMarkdown(outPath string) { + for k, v := range m.MP { + doc := mymarkdown.NewDoc() + var tmp mymarkdown.TmpInterface + tmp.Class = k + tmp.Note = "Waiting to write..." + for _, v1 := range v { + reqTable, reqMp := m.buildDefinitionMD(doc, v1.Req) + resTable, respMpon := m.buildDefinitionMD(doc, v1.Resp) + var sub mymarkdown.TmpSub + sub.ReqTab = reqTable + sub.ReqJSON = template.HTML(tools.GetJSONStr(reqMp, true)) + + sub.RespTab = resTable + sub.RespJSON = template.HTML(tools.GetJSONStr(respMpon, true)) + + sub.Methods = v1.Methods + sub.Note = v1.Note + sub.RouterPath = buildRelativePath(m.Group, v1.RouterPath) + tmp.Item = append(tmp.Item, sub) + } + jsonsrc := doc.GenMarkdown(tmp) + tools.WriteFile(outPath+k+".md", []string{jsonsrc}, true) + } +} diff --git a/mydoc/mymarkdown/def.go b/mydoc/mymarkdown/def.go new file mode 100644 index 0000000..32f6e81 --- /dev/null +++ b/mydoc/mymarkdown/def.go @@ -0,0 +1,197 @@ +package mymarkdown + +import "html/template" + +var headMod = []string{ + ` + #### Brief description: + + - [%s] + - [Waiting to write...] + `, + ` + #### 简要描述: + + - [%s] + - [等待写入] + `, +} + +var bTrue = []string{"`YES`", "`是`"} +var bFalse = []string{"NO", "否"} + +var tableMod = []string{ + `{{$obj := .}} +- {{$obj.SC}} {{$obj.Name}} {{$obj.SC}} : {{$obj.Note}} + +|Parameter| Requierd | Type | Description| +|:---- |:---|:----- |----- |{{range $oem := $obj.Item}} +|{{$obj.SC}}{{$oem.Name}}{{$obj.SC}} | {{$oem.Requierd}}|{{$oem.Type}}|{{$oem.Note}} |{{end}} + `, + `{{$obj := .}} +- {{$obj.SC}} {{$obj.Name}} {{$obj.SC}} : {{$obj.Note}} + +|参数名|是否必须|类型|说明| +|:---- |:---|:----- |----- |{{range $oem := $obj.Item}} +|{{$obj.SC}}{{$oem.Name}}{{$obj.SC}} | {{$oem.Requierd}}|{{$oem.Type}}|{{$oem.Note}} |{{end}} +`, +} + +var bodyMod = []string{ + ` +{{$obj := .}} +## [Viewing tools](https://www.iminho.me/) + +## Overview: +- [{{$obj.Class}}] +- [{{$obj.Note}}] +{{range $oem := $obj.Item}} +-------------------- + +#### Brief description: + +- [{{$oem.Note}}] + +#### Request URL: + +- {{$oem.RouterPath}} + +#### Methods: +{{range $me := $oem.Methods}} +- {{$me}}{{end}} + +#### Parameters: +{{$oem.ReqTab}} + +#### Request example: +{{$obj.SC}}{{$obj.SC}}{{$obj.SC}} +{{$oem.ReqJSON}} +{{$obj.SC}}{{$obj.SC}}{{$obj.SC}} + +#### Return parameter description: +$oem.RespTab + +#### Return example: + +{{$obj.SC}}{{$obj.SC}}{{$obj.SC}} +{{$oem.RespJSON}} +{{$obj.SC}}{{$obj.SC}}{{$obj.SC}} + +#### Remarks: + +- {{$oem.Note}} +{{end}} + +-------------------- +-------------------- + +#### Custom type: +{{range $k,$v := $obj.BMP}} +#### {{$obj.SC}} {{$k}} {{$obj.SC}} +{{range $v1 := $v}} +{{$v1.Context}} +{{end}} +{{end}} +`, + ` +{{$obj := .}} +## [推荐查看工具](https://www.iminho.me/) + +## 总览: +- [{{$obj.Class}}] +- [{{$obj.Note}}] +{{range $oem := $obj.Item}} +-------------------- + +#### 简要描述: + +- [{{$oem.Note}}] + +#### 请求URL: + +- {{$oem.RouterPath}} + +#### 请求方式: +{{range $me := $oem.Methods}} +- {{$me}}{{end}} + +#### 请求参数: +{{$oem.ReqTab}} + +#### 请求示例: +{{$obj.SC}}{{$obj.SC}}{{$obj.SC}} +{{$oem.ReqJSON}} +{{$obj.SC}}{{$obj.SC}}{{$obj.SC}} + +#### 返回参数说明: +$oem.RespTab + +#### 返回示例: + +{{$obj.SC}}{{$obj.SC}}{{$obj.SC}} +{{$oem.RespJSON}} +{{$obj.SC}}{{$obj.SC}}{{$obj.SC}} + +#### 备注: + +- {{$oem.Note}} +{{end}} + +-------------------- +-------------------- + +#### 自定义类型: +{{range $k,$v := $obj.BMP}} +#### {{$obj.SC}} {{$k}} {{$obj.SC}} +{{range $v1 := $v}} +{{$v1.Context}} +{{end}} +{{end}} +`, +} + +// TmpElement 元素 +type TmpElement struct { + Name string + Requierd string + Type string + Note string +} + +// TmpTable 模板 +type TmpTable struct { + SC string + Pkg string + Name string + Note string + Item []TmpElement +} + +// TmpSub 模板 +type TmpSub struct { + ReqTab string // 请求参数列表 + ReqJSON template.HTML // 请求示例 + + RespTab string // 返回参数列表 + RespJSON template.HTML // 返回示例 + + Methods []string // 请求方式 + + Note string // 注释 + RouterPath string // 请求url +} + +// TmpInterface 模板 +type TmpInterface struct { + SC string + Class string + Note string + Item []TmpSub + BMP map[string][]BaseStruct +} + +// BaseStruct 模板 +type BaseStruct struct { // 基础类型 + Class string + Context string +} diff --git a/mydoc/mymarkdown/demo.md b/mydoc/mymarkdown/demo.md new file mode 100644 index 0000000..36bacba --- /dev/null +++ b/mydoc/mymarkdown/demo.md @@ -0,0 +1,59 @@ + +#### 简要描述: + +- 用户登录接口 + +#### 请求URL: + +- http://xx.com/api/login + +#### 请求方式: + +- GET +- POST + + +#### 请求参数: + +|参数名|是否必须|类型|说明| +|:---- |:---|:----- |----- | +|username |是 |string |用户名 | +|password |是 |string | 密码 | + +#### 返回示例: + +**正确时返回:** + +``` +{ + "errcode": 0, + "data": { + "uid": "1", + "account": "admin", + "nickname": "Minho", + "group_level": 0 , + "create_time": "1436864169", + "last_login_time": "0", + } +} +``` + +**错误时返回:** + + +``` +{ + "errcode": 500, + "errmsg": "invalid appid" +} +``` + +#### 返回参数说明: + +|参数名|类型|说明| +|:----- |:-----|----- | +|group_level |int |用户组id,1:超级管理员;2:普通用户 | + +#### 备注: + +- 更多返回错误代码请看首页的错误代码描述 diff --git a/mydoc/mymarkdown/mymarkdown.go b/mydoc/mymarkdown/mymarkdown.go new file mode 100644 index 0000000..de93123 --- /dev/null +++ b/mydoc/mymarkdown/mymarkdown.go @@ -0,0 +1,149 @@ +package mymarkdown + +import ( + "bytes" + "html/template" + "strconv" + "strings" + "time" + + "github.com/xxjwxc/public/tools" +) + +// DocMarkdown +type DocMarkdown struct { + n int + baseStructMP map[string][]BaseStruct +} + +// NewDoc 新建一个markdown文件 +func NewDoc() *DocMarkdown { + doc := &DocMarkdown{} + if tools.GetLocalSystemLang(true) == "zh" { // en + doc.n = 1 + } + doc.baseStructMP = make(map[string][]BaseStruct) + return doc +} + +// AddBaseStruct 添加一个基础类型 +func (m *DocMarkdown) AddBaseStruct(pkg, class, context string) { + for _, v := range m.baseStructMP[pkg] { + if v.Class == class { + return + } + } + m.baseStructMP[pkg] = append(m.baseStructMP[pkg], BaseStruct{ + Class: class, + Context: context, + }) +} + +// GetBoolStr 获取bool类型字符串 +func (m *DocMarkdown) GetBoolStr(b bool) string { + if b { + return bTrue[m.n] + } + + return bFalse[m.n] +} + +// GetTableInfo 获取table表格 +func (m *DocMarkdown) GetTableInfo() string { + return tableMod[m.n] +} + +// GetBodyInfo 获取table表格 +func (m *DocMarkdown) GetBodyInfo() string { + return bodyMod[m.n] +} + +// GetTypeList 转换typelist +func (m *DocMarkdown) GetTypeList(k interface{}, isArray bool) interface{} { + if isArray { + return []interface{}{k} + } + return k +} + +// GetValueType 根据类型获取内容 +func (m *DocMarkdown) GetValueType(k, v string, isArray bool) interface{} { + array := strings.Split(v, ",") + k = strings.ToLower(k) + switch k { + case "string": + if isArray { + var list []string + for _, v := range array { + list = append(list, v) + } + return list + } + return v + case "bool": + if isArray { + var list []bool + for _, v := range array { + list = append(list, (v == "true" || v == "1")) + } + return list + } + + return (v == "true" || v == "1") + case "int", "uint", "byte", "rune", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "uintptr": + if isArray { + var list []int + for _, v := range array { + i, _ := strconv.Atoi(v) + list = append(list, i) + } + return list + } + + i, _ := strconv.Atoi(v) + return i + case "float32", "float64": + if isArray { + var list []float64 + for _, v := range array { + f, _ := strconv.ParseFloat(v, 64) + list = append(list, f) + } + return list + } + + f, _ := strconv.ParseFloat(v, 64) + return f + case "map": + return v + case "Time": + if isArray { + var list []tools.Time + for _, v := range array { + var t tools.Time + t.Time = time.Unix(tools.StringTimetoUnix(v), 0) + list = append(list, t) + } + return list + } + + var t tools.Time + t.Time = time.Unix(tools.StringTimetoUnix(v), 0) + return t + } + + return v +} + +// GenMarkdown 生成markdown +func (m *DocMarkdown) GenMarkdown(info TmpInterface) string { + info.SC = "`" + info.BMP = m.baseStructMP + tmpl, err := template.New("struct_markdown").Parse(m.GetBodyInfo()) + if err != nil { + panic(err) + } + var buf bytes.Buffer + tmpl.Execute(&buf, info) + return buf.String() +} diff --git a/mydoc/myswagger/def.go b/mydoc/myswagger/def.go new file mode 100644 index 0000000..bc6a002 --- /dev/null +++ b/mydoc/myswagger/def.go @@ -0,0 +1,96 @@ +package myswagger + +// Head Swagger 版本 +type Head struct { + Swagger string `json:"swagger"` +} + +// Info 指定 API 的 info-title +type Info struct { + Description string `json:"description"` + Version string `json:"version"` + Title string `json:"title"` +} + +// ExternalDocs tags of group +type ExternalDocs struct { + Description string `json:"description,omitempty"` // 描述 + URL string `json:"url,omitempty"` // url addr +} + +// Tag group of tags +type Tag struct { + Name string `json:"name"` // tags name + Description string `json:"description"` // 描述 + ExternalDocs *ExternalDocs `json:"externalDocs,omitempty"` // doc group of tags +} + +// Schema 引用 +type Schema struct { + Ref string `json:"$ref"` // 主体模式和响应主体模式中引用 +} + +// Element 元素定义 +type Element struct { + In string `json:"in"` // 入参 + Name string `json:"name"` // 参数名字 + Description string `json:"description"` // 描述 + Required bool `json:"required"` // 是否必须 + Type string `json:"type,omitempty"` // 类型 + Schema Schema `json:"schema"` // 引用 +} + +// Param API 路径 paths 和操作在 API 规范的全局部分定义 +type Param struct { + Tags []string `json:"tags"` // 分组标记 + Summary string `json:"summary"` // 摘要 + Description string `json:"description"` // 描述 + OperationID string `json:"operationId,omitempty"` // 操作id + Consumes []string `json:"consumes"` // Parameter content type + Produces []string `json:"produces"` // Response content type + Parameters []Element `json:"parameters"` // 请求参数 + Responses map[string]map[string]string `json:"responses"` // 返回参数 + Security interface{} `json:"security,omitempty"` // 认证信息 +} + +// SecurityDefinitions 安全验证 +type SecurityDefinitions struct { + PetstoreAuth interface{} `json:"petstore_auth,omitempty"` // 安全验证定义 + AIPKey interface{} `json:"api_key,omitempty"` // api key +} + +// Propertie 属性 +type Propertie struct { + Type string `json:"type,omitempty"` // 类型 + Format string `json:"format"` // format 类型 + Description string `json:"description"` // 描述 + Enum interface{} `json:"enum,omitempty"` // enum + Ref string `json:"$ref,omitempty"` // 主体模式和响应主体模式中引用 +} + +// XML xml +type XML struct { + Name string `json:"name"` + Wrapped bool `json:"wrapped"` +} + +// Definition 通用结构体定义 +type Definition struct { + Type string `json:"type"` // 类型 object + Properties map[string]Propertie `json:"properties"` // 属性列表 + XML XML `json:"xml"` +} + +// APIBody swagger api body info +type APIBody struct { + Head + Info Info `json:"info"` + Host string `json:"host"` // http host + BasePath string `json:"basePath"` // 根级别 + Tags []Tag `json:"tags"` + Schemes []string `json:"schemes"` // http/https + Patchs map[string]map[string]Param `json:"paths"` // API 路径 + SecurityDefinitions SecurityDefinitions `json:"securityDefinitions,omitempty"` // 安全验证 + Definitions map[string]Definition `json:"definitions"` // 通用结构体定义 + ExternalDocs ExternalDocs `json:"externalDocs"` // 外部链接 +} diff --git a/mydoc/myswagger/model_set.go b/mydoc/myswagger/model_set.go new file mode 100644 index 0000000..b26e8ed --- /dev/null +++ b/mydoc/myswagger/model_set.go @@ -0,0 +1,82 @@ +package myswagger + +import "strings" + +var version string = "2.0" +var host string = "localhost" +var basePath string = "/v1" +var schemes []string = []string{"http", "https"} +var reqCtxType []string = []string{"application/json", "application/xml"} +var respCtxType []string = []string{"application/json", "application/xml"} +var info Info = Info{ + Description: "swagger default desc", + Version: "1.0.0", + Title: "Swagger Petstore", +} +var externalDocs ExternalDocs = ExternalDocs{ + Description: "Find out more about Swagger", + URL: "https://github.com/xxjwxc/public", +} + +// SetVersion 设置版本号 +func SetVersion(v string) { + version = v +} + +// SetHost 设置host +func SetHost(h string) { + h = strings.TrimPrefix(h, "http://") + h = strings.TrimPrefix(h, "https://") + host = h +} + +// SetBasePath set basePath +func SetBasePath(b string) { + if !strings.HasPrefix(b, "/") { + b = "/" + b + } + basePath = b +} + +// SetSchemes 设置 http头 +func SetSchemes(isHTTP, isHTTPS bool) { + schemes = []string{} + if isHTTP { + schemes = append(schemes, "http") + } + if isHTTPS { + schemes = append(schemes, "https") + } +} + +// SetReqCtxType 设置请求数据传输方式 +func SetReqCtxType(isJSON, isXML bool) { + reqCtxType = []string{} + if isJSON { + reqCtxType = append(schemes, "application/json") + } + if isXML { + reqCtxType = append(schemes, "application/xml") + } +} + +// SetRespCtxType 设置响应(返回)求数据传输方式 +func SetRespCtxType(isJSON, isXML bool) { + respCtxType = []string{} + if isJSON { + respCtxType = append(schemes, "application/json") + } + if isXML { + respCtxType = append(schemes, "application/xml") + } +} + +// SetInfo 设置信息 +func SetInfo(i Info) { + info = i +} + +// SetExternalDocs 设置外部doc链接 +func SetExternalDocs(e ExternalDocs) { + externalDocs = e +} diff --git a/mydoc/myswagger/myswagger.go b/mydoc/myswagger/myswagger.go new file mode 100644 index 0000000..41a2a33 --- /dev/null +++ b/mydoc/myswagger/myswagger.go @@ -0,0 +1,132 @@ +package myswagger + +import ( + "strings" + + "github.com/xxjwxc/public/tools" +) + +// DocSwagger ... +type DocSwagger struct { + client *APIBody +} + +// NewDoc 新建一个swagger doc +func NewDoc() *DocSwagger { + doc := &DocSwagger{} + doc.client = &APIBody{ + Head: Head{Swagger: version}, + Info: info, + Host: host, + BasePath: basePath, + // Tags + Schemes: schemes, + // Patchs + // SecurityDefinitions + // Definitions + ExternalDocs: externalDocs, + } + doc.client.Patchs = make(map[string]map[string]Param) + return doc +} + +// AddTag add tag (排他) +func (doc *DocSwagger) AddTag(tag Tag) { + for _, v := range doc.client.Tags { + if v.Name == tag.Name { // find it + return + } + } + + doc.client.Tags = append(doc.client.Tags, tag) +} + +// AddDefinitions 添加 通用结构体定义 +func (doc *DocSwagger) AddDefinitions(key string, def Definition) { + // for k := range doc.client.Definitions { + // if k == key { // find it + // return + // } + // } + if doc.client.Definitions == nil { + doc.client.Definitions = make(map[string]Definition) + } + + doc.client.Definitions[key] = def +} + +// AddPatch ... API 路径 paths 和操作在 API 规范的全局部分定义 +func (doc *DocSwagger) AddPatch(url string, p Param, metheds ...string) { + if !strings.HasPrefix(url, "/") { + url = "/" + url + } + + if doc.client.Patchs[url] == nil { + doc.client.Patchs[url] = make(map[string]Param) + } + if len(p.Consumes) == 0 { + p.Consumes = reqCtxType + } + if len(p.Produces) == 0 { + p.Produces = respCtxType + } + if p.Responses == nil { + p.Responses = map[string]map[string]string{ + "400": {"description": "v"}, + "404": {"description": "not found"}, + "405": {"description": "Validation exception"}, + } + } + + for _, v := range metheds { + doc.client.Patchs[url][strings.ToLower(v)] = p + } +} + +// GetAPIString 获取返回数据 +func (doc *DocSwagger) GetAPIString() string { + return tools.GetJSONStr(doc.client, true) +} + +var kvType = map[string]string{ // array, boolean, integer, number, object, string + "int": "integer", + "uint": "integer", + "byte": "integer", + "rune": "integer", + "int8": "integer", + "int16": "integer", + "int32": "integer", + "int64": "integer", + "uint8": "integer", + "uint16": "integer", + "uint32": "integer", + "uint64": "integer", + "uintptr": "integer", + "float32": "integer", + "float64": "integer", + "bool": "boolean", + "map": "object", + "Time": "string"} + +var kvFormat = map[string]string{} + +// GetKvType 获取类型转换 +func GetKvType(k string, isArray, isType bool) string { + if isArray { + if isType { + return "object" + } + return "array" + } + + if isType { + if _, ok := kvType[k]; ok { + return kvType[k] + } + return k + } + if _, ok := kvFormat[k]; ok { + return kvFormat[k] + } + return k +} diff --git a/mydoc/myswagger/myswagger_test.go b/mydoc/myswagger/myswagger_test.go new file mode 100644 index 0000000..1b7189f --- /dev/null +++ b/mydoc/myswagger/myswagger_test.go @@ -0,0 +1,59 @@ +package myswagger + +import ( + "testing" + + "github.com/xxjwxc/public/tools" +) + +func TestDomain(t *testing.T) { + SetHost("http://localhost:8080") + SetBasePath("/v1") + doc := NewDoc() + var tag Tag + tag.Name = "pet" + tag.Description = "Everything about your Pets" + tag.ExternalDocs = &ExternalDocs{ + Description: "Find out more", + URL: "https://github.com/xxjwxc/public", + } + doc.AddTag(tag) + + var def Definition + def.Type = "object" + def.Properties = make(map[string]Propertie) + def.Properties["id"] = Propertie{ + Type: "integer", + Format: "int64", + Description: "des text", + } + def.Properties["status"] = Propertie{ + Type: "string", + Format: "string", + Description: "Order Status", + Enum: []string{"placed", "approved", "delivered"}, + } + + doc.AddDefinitions("Pet", def) + + var p Param + p.Tags = []string{"pet"} + p.Summary = "Add a new pet to the store" + p.Description = "描述" + // p.OperationID = "addPet" + p.Parameters = []Element{Element{ + In: "body", // body, header, formData, query, path + Name: "body", // body, header, formData, query, path + Description: "Pet object that needs to be added to the store", + Required: true, + Schema: Schema{ + Ref: "#/definitions/Pet", + }, + }} + doc.AddPatch("/pet", p, "post", "get") + + jsonsrc := doc.GetAPIString() + + tools.WriteFile("/Users/xxj/Downloads/out.json", []string{jsonsrc}, true) + +} diff --git a/myelastic/myelastic.go b/myelastic/myelastic.go index 6d5da26..1c0d97a 100644 --- a/myelastic/myelastic.go +++ b/myelastic/myelastic.go @@ -3,9 +3,10 @@ package myelastic import ( "context" "encoding/json" - "errors" "log" + "github.com/xxjwxc/public/errors" + "reflect" "strings" "time" @@ -149,10 +150,8 @@ func (es *MyElastic) SortQueryReturnHits(index_name string, from, size int, buil // log.Println("wwwwww", es_result.Aggregations) if es_result.Hits.TotalHits > 0 { - return true, es_result.Hits.Hits } else { - return true, nil } } @@ -314,7 +313,7 @@ func scanMapElement(fieldv reflect.Value, field reflect.StructField, objMap map[ bb := field.Tag sqlTag := bb.Get("json") - if bb.Get("json") == "-" || sqlTag == "-" || reflect.ValueOf(bb).String() == "-" { + if sqlTag == "-" || reflect.ValueOf(bb).String() == "-" { return nil } diff --git a/myfile/myfile.go b/myfile/myfile.go index 9a9b025..c1aaf7f 100644 --- a/myfile/myfile.go +++ b/myfile/myfile.go @@ -5,8 +5,9 @@ import ( "io" "net/http" "os" - "public/tools" "time" + + "github.com/xxjwxc/public/tools" ) //上传单个文件 diff --git a/myhttp/myfile.go b/myhttp/myfile.go index 81c2cc6..c557ad1 100644 --- a/myhttp/myfile.go +++ b/myhttp/myfile.go @@ -2,7 +2,6 @@ package myhttp import ( "bytes" - "data/config" "fmt" "io" "io/ioutil" @@ -10,15 +9,16 @@ import ( "net/http" "os" "path" - "public/mylog" - "public/tools" + + "github.com/xxjwxc/public/dev" + "time" + + "github.com/xxjwxc/public/mylog" + "github.com/xxjwxc/public/tools" ) -/* -多文件上传 -dir:空则使用文件后缀做dir -*/ +//UploadMoreFile 多文件上传,dir:空则使用文件后缀做dir func UploadMoreFile(r *http.Request, dir string) (result bool, optionDirs []string) { //接受post请求 if r.Method == "POST" { @@ -39,15 +39,14 @@ func UploadMoreFile(r *http.Request, dir string) (result bool, optionDirs []stri _dir = ext } - abs_dir := tools.GetModelPath() + config.File_host + "/" + _dir + "/" - file_name := getFileName(ext) - if !tools.CheckFileIsExist(abs_dir) { - tools.BuildDir(abs_dir) - //err := os.MkdirAll(tools.GetModelPath()+config.File_host+"/"+_dir+"/", os.ModePerm) //生成多级目录 + absDir := tools.GetCurrentDirectory() + "/" + dev.GetFileHost() + "/" + _dir + "/" + fileName := getFileName(ext) + if !tools.CheckFileIsExist(absDir) { + tools.BuildDir(absDir) } //存在则覆盖 - f, err := os.OpenFile(abs_dir+file_name, + f, err := os.OpenFile(absDir+fileName, os.O_WRONLY|os.O_CREATE, 0666) defer f.Close() if err != nil { @@ -57,7 +56,7 @@ func UploadMoreFile(r *http.Request, dir string) (result bool, optionDirs []stri } io.Copy(f, file) - optionDirs = append(optionDirs, config.Url_host+config.File_host+"/"+_dir+"/"+file_name) + optionDirs = append(optionDirs, "/"+dev.GetService()+"/"+dev.GetFileHost()+"/"+_dir+"/"+fileName) result = true } } @@ -81,9 +80,9 @@ func getFileType(exp string) string { return "" } -//模拟客戶端文件上传 +//PostFile 模拟客戶端文件上传 //fieldname注意与服务器端保持一致 -func PostFile(filename, fieldname string, targetUrl string) (e error, result string) { +func PostFile(filename, fieldname, targetURL string) (result string, e error) { bodyBuf := &bytes.Buffer{} bodyWriter := multipart.NewWriter(bodyBuf) @@ -114,19 +113,19 @@ func PostFile(filename, fieldname string, targetUrl string) (e error, result str contentType := bodyWriter.FormDataContentType() bodyWriter.Close() - resp, err := http.Post(targetUrl, contentType, bodyBuf) + resp, err := http.Post(targetURL, contentType, bodyBuf) if err != nil { e = err return } defer resp.Body.Close() - resp_body, err := ioutil.ReadAll(resp.Body) + respBody, err := ioutil.ReadAll(resp.Body) if err != nil { e = err return } fmt.Println(resp.Status) - fmt.Println(string(resp_body)) - result = string(resp_body) + fmt.Println(string(respBody)) + result = string(respBody) return } diff --git a/myhttp/myhttp.go b/myhttp/myhttp.go index 69d22dc..1497494 100644 --- a/myhttp/myhttp.go +++ b/myhttp/myhttp.go @@ -5,14 +5,14 @@ import ( "encoding/json" "fmt" "io/ioutil" - "log" "net/http" "net/url" - "public/mylog" + + "github.com/xxjwxc/public/mylog" ) -//发送修改密码 -func OnPostJson(url, jsonstr string) []byte { +//OnPostJSON 发送修改密码 +func OnPostJSON(url, jsonstr string) []byte { //解析这个 URL 并确保解析没有出错。 body := bytes.NewBuffer([]byte(jsonstr)) resp, err := http.Post(url, "application/json;charset=utf-8", body) @@ -22,14 +22,15 @@ func OnPostJson(url, jsonstr string) []byte { defer resp.Body.Close() body1, err1 := ioutil.ReadAll(resp.Body) if err1 != nil { + mylog.Error(err1) return []byte("") } return body1 } -//发送get 请求 -func OnGetJson(url, params string) string { +//OnGetJSON 发送get 请求 +func OnGetJSON(url, params string) string { //解析这个 URL 并确保解析没有出错。 var urls = url if len(params) > 0 { @@ -42,13 +43,14 @@ func OnGetJson(url, params string) string { defer resp.Body.Close() body1, err1 := ioutil.ReadAll(resp.Body) if err1 != nil { + mylog.Error(err1) return "" } return string(body1) } -//发送get 请求 返回对象 +//SendGet 发送get 请求 返回对象 func SendGet(url, params string, obj interface{}) bool { //解析这个 URL 并确保解析没有出错。 var urls = url @@ -66,12 +68,17 @@ func SendGet(url, params string, obj interface{}) bool { mylog.Error(err) return false } - log.Println((string(body))) + //log.Println((string(body))) err = json.Unmarshal([]byte(body), &obj) - return err == nil + if err != nil { + mylog.Error(err) + return false + } + + return true } -//发送GET请求 +//SendGetEx 发送GET请求 func SendGetEx(url string, reponse interface{}) bool { resp, e := http.Get(url) if e != nil { @@ -84,12 +91,17 @@ func SendGetEx(url string, reponse interface{}) bool { mylog.Error(e) return false } - mylog.Debug(string(body)) + //mylog.Debug(string(body)) err = json.Unmarshal(body, &reponse) - return err == nil + if err != nil { + mylog.Error(err) + return false + } + + return true } -//form 方式发送post请求 +//OnPostForm form 方式发送post请求 func OnPostForm(url string, data url.Values) (body []byte) { resp, err := http.PostForm(url, data) if err != nil { @@ -104,7 +116,7 @@ func OnPostForm(url string, data url.Values) (body []byte) { return } -//发送POST请求 +//SendPost 发送POST请求 func SendPost(requestBody interface{}, responseBody interface{}, url string) bool { postData, err := json.Marshal(requestBody) client := &http.Client{} @@ -125,15 +137,20 @@ func SendPost(requestBody interface{}, responseBody interface{}, url string) boo return false } // result := string(body) - mylog.Debug(string(body)) + //mylog.Debug(string(body)) err = json.Unmarshal(body, &responseBody) - return err == nil + if err != nil { + mylog.Error(err) + return false + } + + return true } -//像指定client 发送json 包 +//WriteJSON 像指定client 发送json 包 //msg message.MessageBody -func WriteJson(w http.ResponseWriter, msg interface{}) { +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 { diff --git a/myi18n/def.go b/myi18n/def.go new file mode 100644 index 0000000..8e2c3e3 --- /dev/null +++ b/myi18n/def.go @@ -0,0 +1,10 @@ +package myi18n + +import "github.com/nicksnyder/go-i18n/v2/i18n" + +/* + Internationalization 国际化 +*/ + +var tr *i18n.Localizer +var i18nBundle *i18n.Bundle diff --git a/myi18n/myi18n.go b/myi18n/myi18n.go new file mode 100644 index 0000000..a4d9706 --- /dev/null +++ b/myi18n/myi18n.go @@ -0,0 +1,52 @@ +package myi18n + +import ( + "github.com/nicksnyder/go-i18n/v2/i18n" + "github.com/xxjwxc/public/tools" + "golang.org/x/text/language" +) + +func init() { + ReSet() +} + +// SetLocalLG 设置本地语言包 (空 将获取系统值) +func SetLocalLG(lg string) { + if len(lg) == 0 { + lg = tools.GetLocalSystemLang(true) + } + // return the new localizer that can be used to translate text + tr = i18n.NewLocalizer(i18nBundle, lg) +} + +// AddMessages 添加语言 +func AddMessages(tag language.Tag, messages ...*i18n.Message) error { + return i18nBundle.AddMessages(tag, messages...) +} + +// AddKV 添加语言 +func AddKV(tag language.Tag, k, v string) error { + return i18nBundle.AddMessages(tag, &i18n.Message{ + ID: k, + Other: v, + }) +} + +// ReSet 重置 +func ReSet() { + i18nBundle = i18n.NewBundle(language.English) + SetLocalLG("") // default +} + +// Get 获取值 +func Get(Key string) string { + if tr != nil { + return tr.MustLocalize(&i18n.LocalizeConfig{ + DefaultMessage: &i18n.Message{ + ID: Key, + }, + }) + } + + return Key +} diff --git a/myleveldb/myleveldb.go b/myleveldb/myleveldb.go index 906d2b3..4ad34b5 100644 --- a/myleveldb/myleveldb.go +++ b/myleveldb/myleveldb.go @@ -1,24 +1,39 @@ package myleveldb import ( - "public/mylog" - "public/tools" "reflect" + "sync" "github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb/iterator" "github.com/syndtr/goleveldb/leveldb/util" + "github.com/xxjwxc/public/mylog" + "github.com/xxjwxc/public/tools" ) +var lock sync.Mutex +var locks = map[string]*sync.Mutex{} + type Param struct { Key string Value interface{} } func OnInitDB(dataSourceName string) MyLevelDB { + if _, ok := locks[dataSourceName]; !ok { + lock.Lock() + if _, ok := locks[dataSourceName]; !ok { + locks[dataSourceName] = &sync.Mutex{} + } + lock.Unlock() + } + + locks[dataSourceName].Lock() var L MyLevelDB + L.dataSourceName = dataSourceName L.DB, L.E = leveldb.OpenFile(dataSourceName, nil) if L.E != nil { + locks[dataSourceName].Unlock() mylog.Error(L.E) } // L.op = &opt.ReadOptions{ @@ -29,8 +44,9 @@ func OnInitDB(dataSourceName string) MyLevelDB { } type MyLevelDB struct { - DB *leveldb.DB - E error + DB *leveldb.DB + E error + dataSourceName string //op *opt.ReadOptions Value interface{} } @@ -39,6 +55,7 @@ func (L *MyLevelDB) OnDestoryDB() { if L.DB != nil { L.DB.Close() L.DB = nil + locks[L.dataSourceName].Unlock() } } diff --git a/mylog/.DS_Store b/mylog/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/mylog/.DS_Store differ diff --git a/mylog/myeslog.go b/mylog/myeslog.go deleted file mode 100644 index 1f081cd..0000000 --- a/mylog/myeslog.go +++ /dev/null @@ -1,122 +0,0 @@ -package mylog - -import ( - "data/config" - "encoding/json" - "fmt" - "log" - "os" - "os/exec" - "path/filepath" - "public/myelastic" - "public/myqueue" - "time" -) - -var ptr_que *myqueue.MyQueue = nil -var elastic myelastic.MyElastic -var isSaveFile bool = true //默认存文件 -var isSaveToEs bool = false //默认不保存 -var local_Log_file string = "log" //默认存放文件的目录 -var exe_path string - -func init() { - if config.IsRunTesting() { //测试时候不创建 - return - } - - file, _ := exec.LookPath(os.Args[0]) - path, _ := filepath.Abs(file) - exe_path = filepath.Dir(path) - - BuildDir(local_Log_file) - ptr_que = myqueue.NewSyncQueue() - es_path := config.GetEsAddrUrl() - if len(es_path) > 0 { - elastic = myelastic.OnInitES(es_path) - elastic.CreateIndex(Http_log_index, mapping) - } - go onConsumerLog() -} - -/* - 发送日志请求 -*/ -func OnLog(es_index, es_type, es_id string, data LogInfo) { - // local, _ := time.LoadLocation("Local") - // data.Creat_time = time.Now().In(local) - b, err := json.Marshal(data.Data) - if err != nil { - log.Println("OnLog error:", err) - } - data.Data = string(b) - var info EsLogInfo - info.Es_index = es_index - info.Es_type = es_type - info.Es_id = es_id - info.Info = data - - ptr_que.Push(info) //加入日志队列 -} - -/* - 更新本地存储文件地址 - isSave:是否本地存储 - LogFile:本地存储相对程序位置(log/ ==> 当前可执行文件的 log/目录) -*/ -func InitLogFileInfo(isSave, isSaveEs bool, LogFile string) { - isSaveFile = isSave - isSaveToEs = isSaveEs - local_Log_file = LogFile - if isSave { - BuildDir(local_Log_file) - } -} - -func BuildDir(logfile string) { - os.MkdirAll(exe_path+"/"+logfile, os.ModePerm) //生成多级目录 -} - -/* - 消费者 消费日志 -*/ -func onConsumerLog() { - for { - var info EsLogInfo - info = ptr_que.Pop().(EsLogInfo) - - if isSaveToEs && elastic.Client != nil { - if !elastic.Add(info.Es_index, info.Es_type, info.Es_id, info.Info) { - log.Println("elastic add error ") - } - } - - if isSaveFile { - saveLogTofile(info) - } - Debug(info) - } -} - -var _f *os.File -var _err error -var saveFaile string - -func saveLogTofile(info EsLogInfo) { - - time_str := time.Now().Format("2006-01-02-15") //设定时间格式 - fname := fmt.Sprintf("%s/%s/%s.log", exe_path, local_Log_file, time_str) - if saveFaile != fname { - if _f != nil { - _f.Close() - } - _f, _err = os.OpenFile(fname, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666) - if _err != nil { - log.Println(_err) - return - } - } - - b, _ := json.Marshal(info) - _f.WriteString(string(b) + "\r\n") //输出堆栈信息 -} diff --git a/mylog/myeslog.go.zip b/mylog/myeslog.go.zip new file mode 100644 index 0000000..e3ec164 Binary files /dev/null and b/mylog/myeslog.go.zip differ diff --git a/mylog/mylog.go b/mylog/mylog.go index b5a4db6..e49fe99 100644 --- a/mylog/mylog.go +++ b/mylog/mylog.go @@ -1,21 +1,25 @@ package mylog import ( - "data/config" "fmt" "log" "os" "os/exec" "path/filepath" "runtime/debug" + "sync" "time" + + "github.com/gookit/color" + "github.com/xxjwxc/public/dev" + "github.com/xxjwxc/public/errors" ) -func init() { - file, _ := exec.LookPath(os.Args[0]) - path, _ := filepath.Abs(file) - path = filepath.Dir(path) - os.MkdirAll(path+"/err", os.ModePerm) //生成多级目录 +var _logerTuple logerTuple + +type logerTuple struct { + once sync.Once + _path string } const ( // @@ -26,7 +30,7 @@ const ( // // func Print(log_level int, describ string) { - log.Println(describ) + log.Println(color.Info.Render(describ)) return } @@ -34,12 +38,12 @@ func Print(log_level int, describ string) { func Println(describ ...interface{}) { for _, e := range describ { switch v := e.(type) { - case string: - log.Println(v) + // case string: + // log.Println(color.Info.Render(v)) case []byte: - log.Println(string(v)) + log.Println(color.Info.Render(string(v))) default: - log.Println(v) + log.Println(color.Info.Render(v)) } } return @@ -47,25 +51,43 @@ func Println(describ ...interface{}) { // func Info(describ string) { - log.Println(describ) + log.Println(color.FgGreen.Render(describ)) return } // func Error(err error) { - log.Println(err) - SaveError(err.Error(), "err") + err = errors.Cause(err) //获取原始对象 + log.Println(color.Error.Render(fmt.Sprintf(":Cause:%+v", err))) + SaveError(fmt.Sprintf("%+v", err), "err") +} + +//打印错误信息 +func ErrorString(v ...interface{}) { + log.Output(2, color.Error.Render(fmt.Sprint(v...))) +} + +//Fatal 系统级错误 +func Fatal(v ...interface{}) { + log.Output(2, color.Error.Render(fmt.Sprint(v...))) + os.Exit(1) +} + +func initPath() { + file, _ := exec.LookPath(os.Args[0]) + path, _ := filepath.Abs(file) + path = filepath.Dir(path) + _logerTuple._path = path + "/err" + os.MkdirAll(_logerTuple._path, os.ModePerm) //生成多级目录 } //保存错误信息 func SaveError(errstring, flag string) { - file, _ := exec.LookPath(os.Args[0]) - path, _ := filepath.Abs(file) - path = filepath.Dir(path) + _logerTuple.once.Do(initPath) - now := time.Now() //获取当前时间 - time_str := now.Format("2006-01-02_15") //设定时间格式 - fname := fmt.Sprintf("%s/err/%s_%s.log", path, flag, time_str) //保存错误信息文件名:程序名-进程ID-当前时间(年月日时分秒) + now := time.Now() //获取当前时间 + time_str := now.Format("2006-01-02_15") //设定时间格式 + fname := fmt.Sprintf("%s/%s_%s.log", _logerTuple._path, flag, time_str) //保存错误信息文件名:程序名-进程ID-当前时间(年月日时分秒) f, err := os.OpenFile(fname, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666) if err != nil { @@ -81,21 +103,16 @@ func SaveError(errstring, flag string) { // func Debug(describ ...interface{}) { - if config.OnIsDev() { + if dev.OnIsDev() { for _, e := range describ { switch v := e.(type) { case string: - log.Println(v) + log.Println(color.Note.Render(v)) case []byte: - log.Println(string(v)) + log.Println(color.Note.Render(string(v))) default: - log.Println(v) + log.Println(color.Note.Render(fmt.Sprintf("%+v", v))) } } } } - -//刷新 -func Flush() { - -} diff --git a/mymath/my_test.go b/mymath/my_test.go new file mode 100644 index 0000000..b1d8d8d --- /dev/null +++ b/mymath/my_test.go @@ -0,0 +1,24 @@ +package mymath + +import ( + "fmt" + "testing" + "time" +) + +func Test_order(t *testing.T) { + ch := make(chan int) + go func() { + time.Sleep(10 * time.Second) + fmt.Println("aaaa") + ch <- 123 + }() + + h := <-ch + fmt.Println("bbbb") + fmt.Println(h) + + fmt.Println(Gcd(9, 21)) + fmt.Println(17 * 19) + fmt.Println(Lcm(17, 19)) +} diff --git a/mymath/mymath.go b/mymath/mymath.go new file mode 100644 index 0000000..981d61a --- /dev/null +++ b/mymath/mymath.go @@ -0,0 +1,25 @@ +package mymath + +import "math" + +//Gcd 最大公约数:(辗转相除法) +func Gcd(x, y int64) int64 { + x = int64(math.Abs(float64(x))) + y = int64(math.Abs(float64(y))) + + var tmp int64 + for { + tmp = (x % y) + if tmp > 0 { + x = y + y = tmp + } else { + return y + } + } +} + +//Lcm 最小公倍数:((x*y)/最大公约数) +func Lcm(x, y int64) int64 { + return (x * y) / Gcd(x, y) +} diff --git a/myqueue/myqueue.go b/myqueue/myqueue.go index 8e3ec65..96d2cd4 100644 --- a/myqueue/myqueue.go +++ b/myqueue/myqueue.go @@ -1,43 +1,52 @@ package myqueue import ( + "runtime" "sync" + "sync/atomic" "gopkg.in/eapache/queue.v1" ) +//MyQueue queue type MyQueue struct { - lock sync.Mutex + sync.Mutex popable *sync.Cond buffer *queue.Queue closed bool + count int32 } -// 创建 -func NewSyncQueue() *MyQueue { +//New 创建 +func New() *MyQueue { ch := &MyQueue{ buffer: queue.New(), } - ch.popable = sync.NewCond(&ch.lock) + ch.popable = sync.NewCond(&ch.Mutex) return ch } -// 取出队列,(阻塞模式) +//Pop 取出队列,(阻塞模式) func (q *MyQueue) Pop() (v interface{}) { c := q.popable buffer := q.buffer - q.lock.Lock() - for buffer.Length() == 0 && !q.closed { + q.Mutex.Lock() + defer q.Mutex.Unlock() + + for q.Len() == 0 && !q.closed { c.Wait() } - if buffer.Length() > 0 { - v = buffer.Peek() - buffer.Remove() + if q.closed { //已关闭 + return } - q.lock.Unlock() + if q.Len() > 0 { + v = buffer.Peek() + buffer.Remove() + atomic.AddInt32(&q.count, -1) + } return } @@ -45,45 +54,56 @@ func (q *MyQueue) Pop() (v interface{}) { func (q *MyQueue) TryPop() (v interface{}, ok bool) { buffer := q.buffer - q.lock.Lock() + q.Mutex.Lock() + defer q.Mutex.Unlock() - if buffer.Length() > 0 { + if q.Len() > 0 { v = buffer.Peek() buffer.Remove() + atomic.AddInt32(&q.count, -1) ok = true } else if q.closed { ok = true } - q.lock.Unlock() return } // 插入队列,非阻塞 func (q *MyQueue) Push(v interface{}) { - q.lock.Lock() + q.Mutex.Lock() + defer q.Mutex.Unlock() if !q.closed { q.buffer.Add(v) + atomic.AddInt32(&q.count, 1) q.popable.Signal() } - q.lock.Unlock() } // 获取队列长度 -func (q *MyQueue) Len() (l int) { - q.lock.Lock() - l = q.buffer.Length() - q.lock.Unlock() - return +func (q *MyQueue) Len() int { + return (int)(atomic.LoadInt32(&q.count)) } // Close MyQueue // After close, Pop will return nil without block, and TryPop will return v=nil, ok=True func (q *MyQueue) Close() { - q.lock.Lock() + q.Mutex.Lock() + defer q.Mutex.Unlock() if !q.closed { q.closed = true - q.popable.Signal() + atomic.StoreInt32(&q.count, 0) + q.popable.Broadcast() //广播 + } +} + +//Wait 等待队列消费完成 +func (q *MyQueue) Wait() { + for { + if q.closed || q.Len() == 0 { + break + } + + runtime.Gosched() //出让时间片 } - q.lock.Unlock() } diff --git a/myqueue/myqueue_test.go b/myqueue/myqueue_test.go new file mode 100644 index 0000000..e285279 --- /dev/null +++ b/myqueue/myqueue_test.go @@ -0,0 +1,105 @@ +package myqueue + +import ( + "fmt" + "runtime" + "testing" + "time" +) + +func TestWait(t *testing.T) { + que := New() + for i := 0; i < 10; i++ { //开启20个请求 + que.Push(i) + } + + go func() { + for { + fmt.Println(que.Pop().(int)) + time.Sleep(1 * time.Second) + } + }() + + go func() { + for { + fmt.Println(que.Pop().(int)) + time.Sleep(1 * time.Second) + } + }() + + que.Wait() + fmt.Println("down") +} + +func TestClose(t *testing.T) { + que := New() + for i := 0; i < 10; i++ { //开启20个请求 + que.Push(i) + } + + go func() { + for { + v := que.Pop() + if v != nil { + fmt.Println(v.(int)) + time.Sleep(1 * time.Second) + } + } + }() + + go func() { + for { + v := que.Pop() + if v != nil { + fmt.Println(v.(int)) + time.Sleep(1 * time.Second) + } + } + }() + + que.Close() + que.Wait() + fmt.Println("down") +} + +func TestTry(t *testing.T) { + que := New() + + go func() { + for { + v, ok := que.TryPop() + if !ok { + fmt.Println("no") + time.Sleep(time.Second / 2) + runtime.Gosched() //出让时间片 + } + + if v != nil { + fmt.Println(v.(int)) + } + } + }() + + go func() { + for { + v, ok := que.TryPop() + if !ok { + fmt.Println("no") + time.Sleep(time.Second / 2) + runtime.Gosched() //出让时间片 + } + + if v != nil { + fmt.Println(v.(int)) + } + } + }() + + for i := 0; i < 10; i++ { //开启20个请求 + que.Push(i) + time.Sleep(1 * time.Second) + } + + que.Wait() + fmt.Println("down") +} diff --git a/myreflect/myreflect.go b/myreflect/myreflect.go new file mode 100644 index 0000000..286faa5 --- /dev/null +++ b/myreflect/myreflect.go @@ -0,0 +1,31 @@ +package myreflect + +import ( + "reflect" + "strings" +) + +// FindTag find struct of tag string.查找struct 的tag信息 +func FindTag(obj interface{}, field, tag string) string { + dataStructType := reflect.Indirect(reflect.ValueOf(obj)).Type() + for i := 0; i < dataStructType.NumField(); i++ { + fd := dataStructType.Field(i) + if fd.Name == field { + bb := fd.Tag + sqlTag := bb.Get(tag) + + if sqlTag == "-" || bb == "-" { + return "" + } + + sqlTags := strings.Split(sqlTag, ",") + sqlFieldName := fd.Name // default + if len(sqlTags[0]) > 0 { + sqlFieldName = sqlTags[0] + } + return sqlFieldName + } + } + + return "" +} diff --git a/myreflect/myreflect_test.go b/myreflect/myreflect_test.go new file mode 100644 index 0000000..a2d0795 --- /dev/null +++ b/myreflect/myreflect_test.go @@ -0,0 +1,18 @@ +package myreflect + +import ( + "fmt" + "testing" +) + +type ReqTest1 struct { + AccessToken string `json:"access_token"` // access_token + UserName string `json:"user_name" binding:"required"` // user name + Password string `json:"password"` // password +} + +func TestJson(t *testing.T) { + var ts ReqTest1 + ts.UserName = `1111` + fmt.Println(FindTag(ts, "UserName", "json")) +} diff --git a/myrunner/error.go b/myrunner/error.go index 11a82de..6d4c3d1 100644 --- a/myrunner/error.go +++ b/myrunner/error.go @@ -1,6 +1,6 @@ package myrunner -import "errors" +import "github.com/xxjwxc/public/errors" //任务执行超时 var ErrTimeOut = errors.New("run time out") diff --git a/mysign/def.go b/mysign/def.go deleted file mode 100644 index 60c247d..0000000 --- a/mysign/def.go +++ /dev/null @@ -1,24 +0,0 @@ -package mysign - -import "time" - -const ( - _sign_data = "_sign_data" -) - -//签名地址 -type Sign_client_tbl struct { - Id int `gorm:"primary_key"` - App_key string //key - App_secret string //secret - Expire_time time.Time //超时时间 - Strict_sign int //是否强制验签:0:用户自定义,1:强制 - Strict_verify int //是否强制验证:0:用户自定义,1:强制 - Token_expire_time int //token过期时间 -} - -//签名必须带的头标记 -type Sing_head struct { - Appid string `json:"appid,omitempty"` //appid - Signature string `json:"signature,omitempty"` //签名 -} diff --git a/mysign/sign.go b/mysign/sign.go deleted file mode 100644 index 2cacbcf..0000000 --- a/mysign/sign.go +++ /dev/null @@ -1,96 +0,0 @@ -package mysign - -import ( - "data/config" - "public/mycache" - "public/mylog" - "public/mysqldb" - "public/tools" - "strings" - "time" -) - -func init() { - OnInit() -} - -func OnInit() { - str_db := config.GetDbUrl() - //fmt.Println("dddddddddddddd:", str_db) - if len(str_db) > 0 { - var db mysqldb.MySqlDB - defer db.OnDestoryDB() - orm := db.OnGetDBOrm(str_db) - if orm.HasTable(&Sign_client_tbl{}) { //有这个表 - now := time.Now() - var list []Sign_client_tbl - err := orm.Where("expire_time > ?", now).Find(&list).Error - if err != nil { - mylog.Error(err) - return - } - cache := mycache.OnGetCache(_sign_data) - for _, v := range list { //保存数据到缓存 - cache.Add(v.App_key, v, v.Expire_time.Sub(now)) - } - } - } -} - -func GetOne(appKey string) (sign Sign_client_tbl) { - cache := mycache.OnGetCache(_sign_data) - tp, b := cache.Value(appKey) - if b { - sign = tp.(Sign_client_tbl) - } else { - str_db := config.GetDbUrl() - if len(str_db) > 0 { - var db mysqldb.MySqlDB - defer db.OnDestoryDB() - orm := db.OnGetDBOrm(str_db) - if orm.HasTable(&Sign_client_tbl{}) { //有这个表 - now := time.Now() - err := orm.Where("app_key = ? and expire_time > ?", appKey, now).Find(&sign).Error - if err != nil { - mylog.Error(err) - return - } - cache := mycache.OnGetCache(_sign_data) - cache.Add(sign.App_key, sign, sign.Expire_time.Sub(now)) - } - } - } - - return -} - -/* - 生成验签 -*/ -func OnGetSign(appkey string, parm ...interface{}) string { - var sign Sign_client_tbl - if len(appkey) > 0 { - sign = GetOne(appkey) - } - //是否强制验证码 - if sign.Id == 0 || sign.Strict_sign == 0 { - return "" - } - - //开始验签 - var strKey string - for _, v := range parm { - strKey += tools.AsString(v) - } - - mylog.Debug("strKey:" + strKey) - mylog.Debug("md5:", tools.Md5Encoder(strKey)) - return tools.Md5Encoder(strKey) -} - -/* - 开始验签 -*/ -func OnCheckSign(appkey, signature string, parm ...interface{}) bool { - return strings.EqualFold(signature, OnGetSign(appkey, parm...)) -} diff --git a/mysign/sign_test.go b/mysign/sign_test.go deleted file mode 100644 index 133f6bc..0000000 --- a/mysign/sign_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package mysign - -import ( - "fmt" - "public/message" - "public/tools" - "testing" - "time" -) - -func Test_sing(t *testing.T) { - now := time.Now() - str := "1" + tools.GetTimeString(now) - str += "1.0001" - fmt.Println(str) - ttt := tools.Md5Encoder(str) - fmt.Println(ttt) - fmt.Println(OnCheckSign("wwwthings", ttt, 1, now, 1.0001)) - fmt.Println(message.GetSuccessMsg()) -} diff --git a/mysqldb/log.go b/mysqldb/log.go index fb1c731..32b9862 100644 --- a/mysqldb/log.go +++ b/mysqldb/log.go @@ -2,7 +2,8 @@ package mysqldb import ( "fmt" - "public/mylog" + + "github.com/xxjwxc/public/mylog" "github.com/jinzhu/gorm" ) diff --git a/mysqldb/mysqldb.go b/mysqldb/mysqldb.go index e895e17..fa8188a 100644 --- a/mysqldb/mysqldb.go +++ b/mysqldb/mysqldb.go @@ -1,36 +1,24 @@ -/* - orm := db.OnCreatDB() - var sum int64 = 0 - for { - sum++ - var user User_account_tbl - user.Id = sum - - orm.SetTable("user_account_tbls") - err := orm.Where("id=?", sum).Find(&user) - if err != nil { - log.Println("-----------:", err) - } else { - log.Println(user) - } - - time.Sleep(time.Second * 2) - } -*/ package mysqldb import ( - "fmt" - "public/mylog" + "github.com/xxjwxc/public/dev" + "github.com/xxjwxc/public/errors" - "data/config" + "github.com/xxjwxc/public/mylog" _ "github.com/go-sql-driver/mysql" "github.com/jinzhu/gorm" ) type MySqlDB struct { - DB *gorm.DB + *gorm.DB + IsInit bool +} + +func OnInitDBOrm(dataSourceName string) (orm *MySqlDB) { + orm = new(MySqlDB) + orm.OnGetDBOrm(dataSourceName) + return } func (i *MySqlDB) OnGetDBOrm(dataSourceName string) (orm *gorm.DB) { @@ -38,19 +26,20 @@ func (i *MySqlDB) OnGetDBOrm(dataSourceName string) (orm *gorm.DB) { var err error i.DB, err = gorm.Open("mysql", dataSourceName) if err != nil { - mylog.Print(mylog.Log_Error, fmt.Sprintf("Got error when connect database, the error is '%v'", err)) + mylog.Error(errors.Wrap(err, "Got error when connect database:"+dataSourceName)) + return nil } + i.IsInit = true } i.DB.SingularTable(true) //全局禁用表名复数 - orm = i.DB - - if config.OnIsDev() { - orm.LogMode(true) + if dev.OnIsDev() { + i.DB.LogMode(true) //beedb.OnDebug = true } else { - orm.SetLogger(DbLog{}) + i.DB.SetLogger(DbLog{}) } + orm = i.DB return } @@ -60,7 +49,3 @@ func (i *MySqlDB) OnDestoryDB() { i.DB = nil } } - -func init() { - -} diff --git a/server/.DS_Store b/server/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/server/.DS_Store differ diff --git a/server/server.go b/server/server.go index ba5904f..d2a7894 100644 --- a/server/server.go +++ b/server/server.go @@ -5,28 +5,48 @@ import ( "os" "time" - "data/config" + "github.com/xxjwxc/public/dev" + "github.com/gookit/color" "github.com/jander/golog/logger" "github.com/kardianos/service" ) -func OnStart(callBack func()) { - name, displayName, desc := config.GetServiceConfig() +type Service struct { + name string + displayName string + desc string +} + +func On(n, dn, d string) *Service { + return &Service{ + name: n, + displayName: dn, + desc: d, + } + +} + +func (sv *Service) Start(callBack func()) { + if len(sv.name) == 0 { + fmt.Println(color.Error.Render("Service init faild,must first call On.")) + return + } + //name, displayName, desc := config.GetServiceConfig() p := &program{callBack} sc := &service.Config{ - Name: name, - DisplayName: displayName, - Description: desc, + Name: sv.name, + DisplayName: sv.displayName, + Description: sv.desc, } s, err := service.New(p, sc) //var s, err = service.NewService(name, displayName, desc) if err != nil { - fmt.Printf("%s unable to start: %s", displayName, err) + fmt.Print(color.Error.Render(fmt.Sprintf("%s unable to start: %s", sv.displayName, err))) return } - fmt.Printf("Service \"%s\" do.\n", displayName) + fmt.Print(color.Info.Render(fmt.Sprintf("Service \"%s\" do.\n", sv.displayName))) if len(os.Args) > 1 { var err error @@ -36,82 +56,94 @@ func OnStart(callBack func()) { { err = s.Install() if err != nil { - fmt.Printf("Failed to install: %s\n", err) + fmt.Print(color.Error.Render(fmt.Sprintf("Failed to install: %s\n", err))) return } - fmt.Printf("Service \"%s\" installed.\n", displayName) + fmt.Print(color.Info.Render(fmt.Sprintf("Service \"%s\" installed.\n", sv.displayName))) } case "remove": { err = s.Uninstall() if err != nil { - fmt.Printf("Failed to remove: %s\n", err) + fmt.Print(color.Error.Render(fmt.Sprintf("Failed to remove: %s\n", err))) return } - fmt.Printf("Service \"%s\" removed.\n", displayName) + fmt.Print(color.Info.Render(fmt.Sprintf("Service \"%s\" removed.\n", sv.displayName))) } case "run": { err = s.Run() if err != nil { - fmt.Printf("Failed to run: %s\n", err) + fmt.Print(color.Error.Render(fmt.Sprintf("Failed to run: %s\n", err))) return } - fmt.Printf("Service \"%s\" run.\n", displayName) + fmt.Print(color.Info.Render(fmt.Sprintf("Service \"%s\" run.\n", sv.displayName))) + } + case "debug": + { + dev.OnSetDev(true) + err = s.Run() + if err != nil { + fmt.Print(color.Error.Render(fmt.Sprintf("Failed to debug: %s\n", err))) + return + } + fmt.Print(color.Info.Render(fmt.Sprintf("Service \"%s\" run.\n", sv.displayName))) } case "start": { err = s.Start() if err != nil { - fmt.Printf("Failed to start: %s\n", err) + fmt.Print(color.Error.Render(fmt.Sprintf("Failed to start: %s\n", err))) return } - fmt.Println("starting check service:", displayName) + fmt.Print(color.Info.Render(fmt.Sprintf("starting check service:%v\n", sv.displayName))) ticker := time.NewTicker(1 * time.Second) <-ticker.C - st, err := IsStart(name) + var sit ServiceTools + st, err := sit.IsStart(sv.name) if err != nil { - fmt.Println(err) + color.Error.Render(err) return - } else { - if st == Stopped || st == StopPending { - fmt.Printf("Service \"%s\" is Stopped.\n", displayName) - fmt.Println("can't to start service.") - return - } } - fmt.Printf("Service \"%s\" started.\n", displayName) + + if st == Stopped || st == StopPending { + fmt.Print(color.Error.Render(fmt.Sprintf("Service \"%s\" is Stopped.\n", sv.displayName))) + fmt.Print(color.Error.Render("can't to start service.")) + return + } + + fmt.Print(color.Info.Render(fmt.Sprintf("Service \"%s\" started.\n", sv.displayName))) } case "stop": { err = s.Stop() if err != nil { - fmt.Printf("Failed to stop: %s\n", err) + fmt.Print(color.Error.Render(fmt.Sprintf("Failed to stop: %s\n", err))) return } - - st, err := IsStart(name) + var sit ServiceTools + st, err := sit.IsStart(sv.name) if err != nil { fmt.Println(err) return - } else { - if st == Running || st == StartPending { - fmt.Printf("Service \"%s\" is Started.\n", displayName) - fmt.Println("can't to stop service.") - return - } } - fmt.Printf("Service \"%s\" stopped.\n", displayName) + + if st == Running || st == StartPending { + fmt.Print(color.Error.Render(fmt.Sprintf("Service \"%s\" is Started.\n", sv.displayName))) + fmt.Print(color.Error.Render("can't to stop service.")) + return + } + + fmt.Print(color.Info.Render(fmt.Sprintf("Service \"%s\" stopped.\n", sv.displayName))) } } return - } else { - fmt.Print("Failed to read args\n") - //return } + fmt.Print(color.Note.Render("Failed to read args\n")) + if err = s.Run(); err != nil { logger.Error(err) } @@ -132,6 +164,6 @@ func (p *program) Stop(s service.Service) error { return nil } -type ServiceTools interface { +type IServiceTools interface { IsStart(name string) (status int, err error) } diff --git a/server/server_darwin.go b/server/server_darwin.go new file mode 100644 index 0000000..7caccc1 --- /dev/null +++ b/server/server_darwin.go @@ -0,0 +1,10 @@ +package server + +type ServiceTools struct { + //i IServiceTools +} + +func (s *ServiceTools) IsStart(name string) (st int, err error) { + st = NOTFIND + return +} diff --git a/server/server_linux.go b/server/server_linux.go index 2e7ca2a..af2d98f 100644 --- a/server/server_linux.go +++ b/server/server_linux.go @@ -6,11 +6,11 @@ import ( "strings" ) -type WindowsServiceTools struct { - i ServiceTools +type ServiceTools struct { + //i IServiceTools } -func IsStart(name string) (st int, err error) { +func (s *ServiceTools) IsStart(name string) (st int, err error) { f, _ := exec.Command("service", name, "status").Output() st = NOTFIND diff --git a/server/server_test.go b/server/server_test.go new file mode 100644 index 0000000..870038b --- /dev/null +++ b/server/server_test.go @@ -0,0 +1,14 @@ +package server + +import ( + "fmt" + "testing" +) + +func CallBack() { + fmt.Println("aa") +} + +func TestDomain(t *testing.T) { + On("n", "dn", "d").Start(CallBack) +} diff --git a/server/server_windows.go b/server/server_windows.go index 675ea16..7fa15fe 100644 --- a/server/server_windows.go +++ b/server/server_windows.go @@ -5,11 +5,11 @@ import ( "github.com/btcsuite/winsvc/svc" ) -type WindowsServiceTools struct { - i ServiceTools +type ServiceTools struct { + //i IServiceTools } -func IsStart(name string) (st int, err error) { +func (s *ServiceTools) IsStart(name string) (st int, err error) { var m *mgr.Mgr m, err = mgr.Connect() if err != nil { @@ -17,14 +17,14 @@ func IsStart(name string) (st int, err error) { } defer m.Disconnect() - s, err := m.OpenService(name) + sv, err := m.OpenService(name) if err != nil { return 0, err } - defer s.Close() + defer sv.Close() var ss svc.Status - ss, err = s.Query() + ss, err = sv.Query() st = int(ss.State) return } diff --git a/timerDeal/timetask.go b/timerDeal/timetask.go index a9daec4..e55e07f 100644 --- a/timerDeal/timetask.go +++ b/timerDeal/timetask.go @@ -2,9 +2,11 @@ package timerDeal import ( "log" - "public/mydef" - "public/mylog" + "time" + + "github.com/xxjwxc/public/mydef" + "github.com/xxjwxc/public/mylog" ) /* diff --git a/tools/check.go b/tools/check.go index 3fc7a5a..034f83e 100644 --- a/tools/check.go +++ b/tools/check.go @@ -7,7 +7,7 @@ import ( "strings" ) -//检测参数 +// CheckParam 检测参数 func CheckParam(params ...string) bool { for _, value := range params { if len(value) == 0 { @@ -17,14 +17,14 @@ func CheckParam(params ...string) bool { return true } -//判断是否是手机号 +// IsPhone 判断是否是手机号 func IsPhone(mobileNum string) bool { tmp := `^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\d{8}$` reg := regexp.MustCompile(tmp) return reg.MatchString(mobileNum) } -//判断用户是否是邮件用户 +// IsMail 判断用户是否是邮件用户 func IsMail(username string) (isMail bool) { isMail = false if strings.Contains(username, "@") { @@ -33,7 +33,7 @@ func IsMail(username string) (isMail bool) { return } -//判断是否在测试环境下使用 +// IsRunTesting 判断是否在测试环境下使用 func IsRunTesting() bool { if len(os.Args) > 1 { fmt.Println(os.Args[1]) @@ -42,7 +42,7 @@ func IsRunTesting() bool { return false } -//判断是否是18或15位身份证 +// IsIdCard 判断是否是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 { @@ -50,3 +50,17 @@ func IsIdCard(cardNo string) bool { } return true } + +var internalType = []string{"string", "bool", "int", "uint", "byte", "rune", + "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "uintptr", + "float32", "float64", "map", "Time"} + +// IsInternalType 是否是内部类型 +func IsInternalType(t string) bool { + for _, v := range internalType { + if strings.EqualFold(t, v) { + return true + } + } + return false +} diff --git a/tools/convert.go b/tools/convert.go index 5309a8b..06f36f4 100644 --- a/tools/convert.go +++ b/tools/convert.go @@ -4,13 +4,15 @@ import ( "bytes" "encoding/gob" "encoding/json" - "errors" "fmt" - "public/mylog" "regexp" "strconv" "strings" "time" + + "github.com/xxjwxc/public/errors" + + "github.com/xxjwxc/public/mylog" ) type RawBytes []byte diff --git a/tools/file.go b/tools/file.go index 0878973..7d5bb2c 100644 --- a/tools/file.go +++ b/tools/file.go @@ -1,14 +1,19 @@ package tools import ( + "bufio" + "io" "io/ioutil" "os" "os/exec" + "path" "path/filepath" - "public/mylog" + "strings" + + "github.com/xxjwxc/public/mylog" ) -//检查目录是否存在 +// CheckFileIsExist 检查目录是否存在 func CheckFileIsExist(filename string) bool { var exist = true if _, err := os.Stat(filename); os.IsNotExist(err) { @@ -18,20 +23,20 @@ func CheckFileIsExist(filename string) bool { return exist } -//创建目录 -func BuildDir(abs_dir string) error { - return os.MkdirAll(abs_dir, os.ModePerm) //生成多级目录 +// BuildDir 创建目录 +func BuildDir(absDir string) error { + return os.MkdirAll(path.Dir(absDir), os.ModePerm) //生成多级目录 } -//删除文件或文件夹 -func DeleteFile(abs_dir string) error { - return os.RemoveAll(abs_dir) +// DeleteFile 删除文件或文件夹 +func DeleteFile(absDir string) error { + return os.RemoveAll(absDir) } -//获取目录所有文件夹 -func GetPathDirs(abs_dir string) (re []string) { - if CheckFileIsExist(abs_dir) { - files, _ := ioutil.ReadDir(abs_dir) +// GetPathDirs 获取目录所有文件夹 +func GetPathDirs(absDir string) (re []string) { + if CheckFileIsExist(absDir) { + files, _ := ioutil.ReadDir(absDir) for _, f := range files { if f.IsDir() { re = append(re, f.Name()) @@ -41,10 +46,10 @@ func GetPathDirs(abs_dir string) (re []string) { return } -//获取目录所有文件夹 -func GetPathFiles(abs_dir string) (re []string) { - if CheckFileIsExist(abs_dir) { - files, _ := ioutil.ReadDir(abs_dir) +// GetPathFiles 获取目录所有文件 +func GetPathFiles(absDir string) (re []string) { + if CheckFileIsExist(absDir) { + files, _ := ioutil.ReadDir(absDir) for _, f := range files { if !f.IsDir() { re = append(re, f.Name()) @@ -54,13 +59,64 @@ func GetPathFiles(abs_dir string) (re []string) { return } -//获取目录地址 +// GetModelPath 获取目录地址 func GetModelPath() string { file, _ := exec.LookPath(os.Args[0]) - path, _ := filepath.Abs(file) - // if len(path) > 0 { - // path += "/" - // } - path = filepath.Dir(path) + path := filepath.Dir(file) + path, _ = filepath.Abs(path) + return path } + +// GetCurrentDirectory 获取程序运行路径 +func GetCurrentDirectory() string { + dir, _ := filepath.Abs(filepath.Dir(os.Args[0])) + return strings.Replace(dir, "\\", "/", -1) +} + +// SaveToFile 写入文件 +func SaveToFile(fname string, src []string, isClear bool) bool { + return WriteFile(fname, src, isClear) +} + +// WriteFile 写入文件 +func WriteFile(fname string, src []string, isClear bool) bool { + BuildDir(fname) + flag := os.O_CREATE | os.O_WRONLY | os.O_TRUNC + if !isClear { + flag = os.O_CREATE | os.O_RDWR | os.O_APPEND + } + f, err := os.OpenFile(fname, flag, 0666) + if err != nil { + mylog.Error(err) + return false + } + defer f.Close() + + for _, v := range src { + f.WriteString(v) + f.WriteString("\r\n") + } + + return true +} + +// ReadFile 读取文件 +func ReadFile(fname string) (src []string) { + f, err := os.OpenFile(fname, os.O_RDONLY, 0666) + if err != nil { + return []string{} + } + defer f.Close() + + rd := bufio.NewReader(f) + for { + line, _, err := rd.ReadLine() + if err != nil || io.EOF == err { + break + } + src = append(src, string(line)) + } + + return src +} diff --git a/tools/ip.go b/tools/ip.go index d812ae7..b6ee465 100644 --- a/tools/ip.go +++ b/tools/ip.go @@ -5,8 +5,9 @@ import ( "io/ioutil" "net" "net/http" - "public/mylog" "strings" + + "github.com/xxjwxc/public/mylog" ) ///* @@ -31,7 +32,7 @@ import ( // return //} -//获取公网IP地址 +// GetWwwIP 获取公网IP地址 func GetWwwIP() (exip string) { resp, err := http.Get("http://myexternalip.com/raw") if err != nil { @@ -45,9 +46,7 @@ func GetWwwIP() (exip string) { return string(bytes.TrimSpace(b)) } -/* - 获取内网ip -*/ +// GetLocalIP 获取内网ip func GetLocalIP() (ip string) { addrs, err := net.InterfaceAddrs() if err != nil { @@ -65,8 +64,8 @@ func GetLocalIP() (ip string) { return } -//获取用户ip -func GetClientIp(r *http.Request) (ip string) { +// GetClientIP 获取用户ip +func GetClientIP(r *http.Request) (ip string) { ip = r.Header.Get("X-Real-Ip") if ip == "" { ip = strings.Split(r.RemoteAddr, ":")[0] diff --git a/tools/json.go b/tools/json.go index 3bc0883..d7a83a3 100644 --- a/tools/json.go +++ b/tools/json.go @@ -4,11 +4,10 @@ import ( "encoding/json" "io/ioutil" "net/http" - - "github.com/ant0ine/go-json-rest/rest" ) -func JsonToForm(r *http.Request) { +// JSONToForm tag json str to form +func JSONToForm(r *http.Request) { //添加支持json 操作 r.ParseForm() if len(r.Form) == 1 { //可能是json 支持json @@ -26,8 +25,8 @@ func JsonToForm(r *http.Request) { } body, _ := ioutil.ReadAll(r.Body) - body_str := string(body) - if len(body_str) > 0 { + bodyStr := string(body) + if len(bodyStr) > 0 { var m map[string]string if err := json.Unmarshal(body, &m); err == nil { for k, v := range m { @@ -39,30 +38,40 @@ func JsonToForm(r *http.Request) { return } -func GetRequestJsonObj(r *rest.Request, v interface{}) error { +// "github.com/ant0ine/go-json-rest/rest" +// func GetRequestJsonObj(r *rest.Request, v interface{}) error { - //添加支持json 操作 - body, err := ioutil.ReadAll(r.Body) - r.Body.Close() - json.Unmarshal(body, &v) - //-----------------------------end - return err -} +// //添加支持json 操作 +// body, err := ioutil.ReadAll(r.Body) +// r.Body.Close() +// json.Unmarshal(body, &v) +// //-----------------------------end +// return err +// } -func GetJsonStr(obj interface{}) string { - b, _ := json.Marshal(obj) +// GetJSONStr obj to json string +func GetJSONStr(obj interface{}, isFormat bool) string { + var b []byte + if isFormat { + b, _ = json.MarshalIndent(obj, "", " ") + } else { + b, _ = json.Marshal(obj) + } return string(b) } -func JsonDecode(obj interface{}) string { - return GetJsonStr(obj) +// JSONDecode Json Decode +func JSONDecode(obj interface{}) string { + return GetJSONStr(obj, false) } -func GetJsonObj(str string, out interface{}) { +// GetJSONObj string convert to obj +func GetJSONObj(str string, out interface{}) { json.Unmarshal([]byte(str), out) return } -func JsonEncode(str string, out interface{}) { - GetJsonObj(str, out) +// JSONEncode string convert to obj +func JSONEncode(str string, out interface{}) { + GetJSONObj(str, out) } diff --git a/tools/local.go b/tools/local.go new file mode 100644 index 0000000..81feb09 --- /dev/null +++ b/tools/local.go @@ -0,0 +1,36 @@ +package tools + +import ( + "os" + "strings" +) + +// Getenv 获取本地系统变量 +func Getenv(key string) string { + return os.Getenv(key) +} + +// GetLocalSystemLang 获取本地语言 (like:zh_CN.UTF-8)(simple:zh) +func GetLocalSystemLang(isSimple bool) (locale string) { + locale = Getenv("LC_ALL") + if locale == "" { + locale = Getenv("LANG") + } + if isSimple { + locale, _ = splitLocale(locale) + } + return +} + +func splitLocale(locale string) (string, string) { + formattedLocale := strings.Split(locale, ".")[0] + formattedLocale = strings.Replace(formattedLocale, "-", "_", -1) + + pieces := strings.Split(formattedLocale, "_") + language := pieces[0] + territory := "" + if len(pieces) > 1 { + territory = strings.Split(formattedLocale, "_")[1] + } + return language, territory +} diff --git a/tools/reflect.go b/tools/reflect.go new file mode 100644 index 0000000..0811907 --- /dev/null +++ b/tools/reflect.go @@ -0,0 +1,11 @@ +package tools + +import ( + "reflect" + "runtime" +) + +//获取函数名 +func GetFuncName(f interface{}) string { + return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name() +} diff --git a/tools/timeTools.go b/tools/timeTools.go index 02ad1f5..fb8da69 100644 --- a/tools/timeTools.go +++ b/tools/timeTools.go @@ -50,18 +50,18 @@ func GetTimeWeek(timestamp int64) int { } //获取向上整时时间 -func GetHour0(timestamp int64, timeZone *time.Location) time.Time { +func GetHour0(timestamp int64) time.Time { tm := time.Unix(timestamp, 0) tStr := tm.Format("2006-01-02 15") + ":00:00" - return StrToTime(tStr, "2006-01-02 15:04:05", timeZone) + return StrToTime(tStr, "2006-01-02 15:04:05", nil) } //获取给定日期的零点时间 -func GetDay0(timestamp int64, timeZone *time.Location) time.Time { +func GetDay0(timestamp int64) time.Time { tm := time.Unix(timestamp, 0) tStr := tm.Format("2006-01-02") + " 00:00:00" - return StrToTime(tStr, "2006-01-02 15:04:05", timeZone) + return StrToTime(tStr, "2006-01-02 15:04:05", nil) } //获取offset 0点时间 @@ -92,9 +92,16 @@ func StringTimetoUnix(timestr string) int64 { } //获取最近上个星期天的零点日期 -func GetTimeWeek0(timestamp int64) int64 { +func GetWeek0(timestamp int64) time.Time { weekday := GetTimeWeek(timestamp) - tm0 := GetDay0(timestamp, nil) + tm0 := GetDay0(timestamp) + return tm0.AddDate(0, 0, -1*weekday) +} + +//获取最近上个星期天的零点日期 +func GetUtcWeek0(timestamp int64) int64 { + weekday := GetTimeWeek(timestamp) + tm0 := GetDay0(timestamp) tm0 = tm0.AddDate(0, 0, -1*weekday) return tm0.Unix() @@ -105,7 +112,7 @@ func GetTimeWeek0(timestamp int64) int64 { */ func GetMonth0(timestamp int64) time.Time { - tm0 := GetDay0(timestamp, nil) + tm0 := GetDay0(timestamp) month0 := tm0.Day() - 1 tm0 = tm0.AddDate(0, 0, -1*month0) //这个月1号 return tm0 diff --git a/tools/tools.go b/tools/tools.go index 89a7758..3c3ee4b 100644 --- a/tools/tools.go +++ b/tools/tools.go @@ -6,31 +6,15 @@ import ( "crypto/sha1" "encoding/base64" "encoding/hex" - "errors" "fmt" - "html/template" "io" - "net/http" - "os" - "path/filepath" - "public/mylog" "reflect" "sort" "strings" - "data/config" - - "github.com/ant0ine/go-json-rest/rest" + "github.com/xxjwxc/public/errors" ) -/* -获取程序运行路径 -*/ -func GetCurrentDirectory() string { - dir, _ := filepath.Abs(filepath.Dir(os.Args[0])) - return strings.Replace(dir, "\\", "/", -1) -} - func Md5Encoder(src string) string { h := md5.New() h.Write([]byte(src)) // 需要加密的字符串 @@ -74,7 +58,7 @@ func UniqueId() string { return GetMd5String(base64.URLEncoding.EncodeToString(b)) } -//删除切片index +// DeleteSlice 删除切片index func DeleteSlice(slice interface{}, index int) (interface{}, error) { sliceValue := reflect.ValueOf(slice) length := sliceValue.Len() @@ -99,18 +83,18 @@ func MinimumInt(rest []int) int { return minimum } -func LoadTemplate(list ...string) *template.Template { - var tmp []string - for _, v := range list { - if CheckFileIsExist(GetModelPath() + config.Static_host[0] + v) { - tmp = append(tmp, GetModelPath()+config.Static_host[0]+v) - } else { - mylog.Debug("file does not exist:" + GetModelPath() + config.Static_host[0] + v) - panic(GetModelPath() + config.Static_host[0] + v) - } - } - return template.Must(template.ParseFiles(tmp...)) -} +// func LoadTemplate(list ...string) *template.Template { +// var tmp []string +// for _, v := range list { +// if CheckFileIsExist(GetModelPath() + config.Static_host[0] + v) { +// tmp = append(tmp, GetModelPath()+config.Static_host[0]+v) +// } else { +// mylog.Debug("file does not exist:" + GetModelPath() + config.Static_host[0] + v) +// panic(GetModelPath() + config.Static_host[0] + v) +// } +// } +// return template.Must(template.ParseFiles(tmp...)) +// } /* 执行模版渲染, @@ -118,16 +102,16 @@ func LoadTemplate(list ...string) *template.Template { data:传参列表 list:模版列表 */ -func ExecuteTemplate(w rest.ResponseWriter, name string, data interface{}, list ...string) error { - t := LoadTemplate(list...) - w.(http.ResponseWriter).Header().Set("Content-Type", "text/html; charset=utf-8") +// func ExecuteTemplate(w rest.ResponseWriter, name string, data interface{}, list ...string) error { +// t := LoadTemplate(list...) +// w.(http.ResponseWriter).Header().Set("Content-Type", "text/html; charset=utf-8") - if len(name) == 0 { - return t.Execute(w.(http.ResponseWriter), data) - } else { - return t.ExecuteTemplate(w.(http.ResponseWriter), name, data) - } -} +// if len(name) == 0 { +// return t.Execute(w.(http.ResponseWriter), data) +// } else { +// return t.ExecuteTemplate(w.(http.ResponseWriter), name, data) +// } +// } //按字典顺序排序 func DictSort(res []string) (str string) { diff --git a/weixin/base.go b/weixin/base.go index e466f5d..d5a0db8 100644 --- a/weixin/base.go +++ b/weixin/base.go @@ -4,51 +4,79 @@ import ( "encoding/json" "io/ioutil" "net/http" - "public/mycache" - "public/mylog" "time" - "github.com/silenceper/wechat" + "github.com/bitly/go-simplejson" + "github.com/xxjwxc/public/mycache" + "github.com/xxjwxc/public/myhttp" + "github.com/xxjwxc/public/mylog" ) const ( - GETTICKETURL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=wx_card&access_token=" - GETJSURL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=" + _getTicket = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=wx_card&access_token=" + _getJsurl = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=" + _getToken = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + _getSubscribe = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + _cacheToken = "wx_access_token" + _cacheTicket = "weixin_card_ticket" ) -//获取微信accesstoken -func GetAccessToken() (access_token string, err error) { - //先从缓存中获取 - cache := mycache.OnGetCache("weixin_token") +// GetAccessToken 获取微信accesstoken + +//获取登录凭证 +func GetAccessToken() (accessToken string, err error) { + //先从缓存中获取 access_token + cache := mycache.OnGetCache(_cacheToken) var tp interface{} - tp, b := cache.Value("base") + var b bool + tp, b = cache.Value(_cacheToken) if b { - access_token = tp.(string) + accessToken = *(tp.(*string)) } else { - wc := wechat.NewWechat(&cfg) - access_token, err = wc.GetAccessToken() - //保存缓存 - cache.Add("base", access_token, 7000*time.Second) + var url = _getToken + wxInfo.AppID + "&secret=" + wxInfo.AppSecret + + resp, err := http.Get(url) + if err != nil { + return "", err + } + + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return "", err + } + + //注入client ip + js, err := simplejson.NewJson(body) + if err == nil { + accessToken, _ = js.Get("access_token").String() + //保存缓存 + cache.Add(_cacheToken, &accessToken, time.Duration(7000)*time.Second) + //------------------end + } + //----------------------end } + //---------------获取 access_token --------end + return } -//获取微信卡券ticket -func GetApiTicket() (ticket string, err error) { +// GetAPITicket 获取微信卡券ticket +func GetAPITicket() (ticket string, err error) { //先从缓存中获取 - cache := mycache.OnGetCache("weixin_card_ticket") + cache := mycache.OnGetCache(_cacheTicket) var tp interface{} - tp, b := cache.Value("base") + tp, b := cache.Value(_cacheTicket) if b { ticket = tp.(string) } else { - access_token, e := GetAccessToken() + accessToken, e := GetAccessToken() if e != nil { mylog.Error(e) err = e return } - var url = GETTICKETURL + access_token + var url = _getTicket + accessToken resp, e1 := http.Get(url) if e1 != nil { @@ -63,16 +91,16 @@ func GetApiTicket() (ticket string, err error) { err = e2 return } - var result ApiTicket + var result APITicket json.Unmarshal(body, &result) ticket = result.Ticket //保存缓存 - cache.Add("base", ticket, 7000*time.Second) + cache.Add(_cacheTicket, ticket, 7000*time.Second) } return } -//获取微信js ticket +// GetJsTicket 获取微信js ticket func GetJsTicket() (ticket string, err error) { //先从缓存中获取 cache := mycache.OnGetCache("weixin_js_ticket") @@ -81,13 +109,13 @@ func GetJsTicket() (ticket string, err error) { if b { ticket = tp.(string) } else { - access_token, e := GetAccessToken() + accessToken, e := GetAccessToken() if e != nil { mylog.Error(e) err = e return } - var url = GETJSURL + access_token + var url = _getJsurl + accessToken resp, e1 := http.Get(url) if e1 != nil { @@ -102,7 +130,7 @@ func GetJsTicket() (ticket string, err error) { err = e2 return } - var result ApiTicket + var result APITicket json.Unmarshal(body, &result) ticket = result.Ticket //保存缓存 @@ -110,3 +138,19 @@ func GetJsTicket() (ticket string, err error) { } return } + +// 发送订阅消息 +func SendTemplateMsg(msg TempMsg) bool { + accessToken, err := GetAccessToken() + if err != nil { + mylog.Error(err) + return false + } + + bo, _ := json.Marshal(msg) + resb := myhttp.OnPostJSON(_getSubscribe+accessToken, string(bo)) + + var res ResTempMsg + json.Unmarshal(resb, &res) + return res.Errcode == 0 +} diff --git a/weixin/cache.go b/weixin/cache.go index 836deef..84e1ceb 100644 --- a/weixin/cache.go +++ b/weixin/cache.go @@ -1,11 +1,12 @@ package weixin import ( - "public/mycache" "time" + + "github.com/xxjwxc/public/mycache" ) -//Memcache struct contains *memcache.Client +// Gocache Memcache struct contains *memcache.Client type Gocache struct { mc *mycache.MyCache } diff --git a/weixin/def.go b/weixin/def.go index ff65aec..8772732 100644 --- a/weixin/def.go +++ b/weixin/def.go @@ -11,9 +11,37 @@ type UserInfo struct { Privilege []string `json:"privilege"` Unionid string `json:"unionid"` } -type ApiTicket struct { - Errcode int `json:"errcode"` - Errmsg string `json:"errmsg"` - Ticket string `json:"ticket"` - Expires_in int `json:"expires_in"` -} \ No newline at end of file + +// APITicket ... +type APITicket struct { + Errcode int `json:"errcode"` + Errmsg string `json:"errmsg"` + Ticket string `json:"ticket"` + ExpiresIn int `json:"expires_in"` +} + +// WxInfo 微信配置信息 +type WxInfo struct { + AppID string // 微信公众平台应用ID + AppSecret string // 微信支付商户平台商户号 + APIKey string // 微信支付商户平台API密钥 + MchID string + NotifyURL string + ShearURL string + Token string + EncodingAESKey string +} + +// TempMsg 订阅消息头 +type TempMsg struct { + Touser string `json:"touser"` // 是 接收者(用户)的 openid + TemplateID string `json:"template_id"` // 是 所需下发的模板消息的id + Page string `json:"page"` // 否 点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转。 + Data map[string]map[string]string `json:"data"` //是 模板内容,不填则下发空模板 +} + +// ResTempMsg 模版消息返回值 +type ResTempMsg struct { + Errcode int `json:"errcode"` // + Errmsg string `json:"errmsg"` +} diff --git a/weixin/enterprisePay.go b/weixin/enterprisePay.go index c0c7f49..e02b86b 100644 --- a/weixin/enterprisePay.go +++ b/weixin/enterprisePay.go @@ -2,24 +2,26 @@ package weixin import ( "log" - "public/tools" + + "github.com/xxjwxc/public/tools" wxpay "gopkg.in/go-with/wxpay.v1" ) const ( - enterprisePayUrl = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers" // 查询企业付款接口请求URL + enterprisePayURL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers" // 查询企业付款接口请求URL ) +// WxEnterprisePay 企业付款 /* - 企业付款 - open_id:用户唯一标识 - trade_no : 商户订单号 - desc : 操作说明 - amount:付款金额 分 +企业付款 +open_id:用户唯一标识 +trade_no : 商户订单号 +desc : 操作说明 +amount:付款金额 分 */ -func WxEnterprisePay(open_id, trade_no, desc, ipAddr string, amount int) bool { - c := wxpay.NewClient(pay_appId, mchId, apiKey) +func WxEnterprisePay(openID, tradeNO, desc, ipAddr string, amount int) bool { + c := wxpay.NewClient(wxInfo.AppID, wxInfo.MchID, wxInfo.APIKey) // 附着商户证书 err := c.WithCert(certFile, keyFile, rootcaFile) @@ -28,22 +30,22 @@ func WxEnterprisePay(open_id, trade_no, desc, ipAddr string, amount int) bool { } params := make(wxpay.Params) - nonce_str := tools.GetRandomString(16) + nonceStr := tools.GetRandomString(16) // 查询企业付款接口请求参数 - params.SetString("mch_appid", c.AppId) //商户账号appid - params.SetString("mchid", c.MchId) //商户号 - params.SetString("nonce_str", nonce_str) // 随机字符串 - params.SetString("partner_trade_no", trade_no) // 商户订单号 - params.SetString("openid", open_id) //用户openid - params.SetString("check_name", "NO_CHECK") //校验用户姓名选项 - params.SetInt64("amount", int64(amount)) //企业付款金额,单位为分 - params.SetString("desc", desc) //企业付款操作说明信息。必填。 + params.SetString("mch_appid", c.AppId) //商户账号appid + params.SetString("mchid", c.MchId) //商户号 + params.SetString("nonce_str", nonceStr) // 随机字符串 + params.SetString("partner_trade_no", tradeNO) // 商户订单号 + params.SetString("openid", openID) //用户openid + params.SetString("check_name", "NO_CHECK") //校验用户姓名选项 + params.SetInt64("amount", int64(amount)) //企业付款金额,单位为分 + params.SetString("desc", desc) //企业付款操作说明信息。必填。 params.SetString("spbill_create_ip", ipAddr) params.SetString("sign", c.Sign(params)) // 签名 // 发送查询企业付款请求 - ret, err := c.Post(enterprisePayUrl, params, true) + ret, err := c.Post(enterprisePayURL, params, true) if err != nil { log.Fatal(err) } @@ -52,7 +54,7 @@ func WxEnterprisePay(open_id, trade_no, desc, ipAddr string, amount int) bool { resultCode := ret.GetString("result_code") if returnCode == "SUCCESS" && resultCode == "SUCCESS" { return true - } else { - return false } + + return false } diff --git a/weixin/init.go b/weixin/init.go index db7991c..e6af529 100644 --- a/weixin/init.go +++ b/weixin/init.go @@ -1,8 +1,7 @@ package weixin import ( - "data/config" - "public/tools" + "github.com/xxjwxc/public/tools" "github.com/silenceper/wechat" wxpay "gopkg.in/go-with/wxpay.v1" @@ -18,28 +17,15 @@ const ( var cfg wechat.Config var client *wxpay.Client -var pay_appId string // 微信公众平台应用ID -var mchId string // 微信支付商户平台商户号 -var apiKey string // 微信支付商户平台API密钥 -var secret string -var notify_url string -var token string -var encodingAESKey string +var wxInfo WxInfo var certFile string // 微信支付商户平台证书路径 var keyFile string var rootcaFile string -func init() { - wx_info := config.GetWxInfo() - //配置微信支付参数 - pay_appId = wx_info.AppID - mchId = wx_info.MchId - apiKey = wx_info.Key - secret = wx_info.AppSecret - notify_url = wx_info.NotifyUrl - token = wx_info.Token - encodingAESKey = wx_info.EncodingAESKey +// InitWxinfo 初始化配置信息 +func InitWxinfo(info WxInfo) { + wxInfo = info certFile = tools.GetModelPath() + certFileLoc keyFile = tools.GetModelPath() + keyFileLoc @@ -49,12 +35,12 @@ func init() { memCache := NewGocache("_winxin_access") //配置微信参数 cfg = wechat.Config{ - AppID: pay_appId, - AppSecret: secret, - Token: token, - EncodingAESKey: encodingAESKey, + AppID: wxInfo.APIKey, + AppSecret: wxInfo.AppSecret, + Token: wxInfo.Token, + EncodingAESKey: wxInfo.EncodingAESKey, Cache: memCache, } - client = wxpay.NewClient(pay_appId, mchId, apiKey) + client = wxpay.NewClient(wxInfo.AppID, wxInfo.MchID, wxInfo.APIKey) client.WithCert(certFile, keyFile, rootcaFile) } diff --git a/weixin/oauth.go b/weixin/oauth.go index c221ef0..2674319 100644 --- a/weixin/oauth.go +++ b/weixin/oauth.go @@ -3,15 +3,16 @@ package weixin import ( "io/ioutil" "net/http" - "public/mylog" + + "github.com/xxjwxc/public/mylog" ) /* 小程序授权 */ func SmallAppOauth(jscode string) string { - var url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + pay_appId + "&secret=" + - secret + "&js_code=" + jscode + "&grant_type=authorization_code&trade_type=JSAPI" + var url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + wxInfo.AppID + "&secret=" + + wxInfo.AppSecret + "&js_code=" + jscode + "&grant_type=authorization_code&trade_type=JSAPI" resp, e := http.Get(url) if e != nil { diff --git a/weixin/pay.go b/weixin/pay.go index df19a3a..a127992 100644 --- a/weixin/pay.go +++ b/weixin/pay.go @@ -4,20 +4,21 @@ import ( "crypto/md5" "fmt" "log" - "public/message" - "public/mylog" - "public/tools" "strconv" "strings" "time" + "github.com/xxjwxc/public/message" + "github.com/xxjwxc/public/mylog" + "github.com/xxjwxc/public/tools" + wxpay "gopkg.in/go-with/wxpay.v1" ) const ( - unifiedOrderUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder" // 统一下单请求URL - queryOrderUrl = "https://api.mch.weixin.qq.com/pay/orderquery" // 统一查询请求URL - refundUrl = "https://api.mch.weixin.qq.com/secapi/pay/refund" //退款请求URL + unifiedOrderURL = "https://api.mch.weixin.qq.com/pay/unifiedorder" // 统一下单请求URL + queryOrderURL = "https://api.mch.weixin.qq.com/pay/orderquery" // 统一查询请求URL + refundURL = "https://api.mch.weixin.qq.com/secapi/pay/refund" //退款请求URL ) const ( @@ -30,6 +31,7 @@ const ( PAY_ERROR = -1 //支付失败 ) +// SmallAppUnifiedorder 小程序统一下单接口 /* 小程序统一下单接口 open_id:用户唯一标识 @@ -37,8 +39,8 @@ const ( price_body : 支付描述 order_id : 商户订单号 */ -func SmallAppUnifiedorder(open_id string, price int, price_body, order_id, client_ip string) message.MessageBody { - if !tools.CheckParam(open_id, order_id) || price <= 0 { //参数检测 +func SmallAppUnifiedorder(openID string, price int, priceBody, orderID, clientIP string) message.MessageBody { + if !tools.CheckParam(openID, orderID) || price <= 0 { //参数检测 return message.GetErrorMsg(message.ParameterInvalid) } @@ -46,19 +48,19 @@ func SmallAppUnifiedorder(open_id string, price int, price_body, order_id, clien // 查询企业付款接口请求参数 params.SetString("appid", client.AppId) params.SetString("mch_id", client.MchId) - params.SetString("body", price_body) + params.SetString("body", priceBody) params.SetInt64("total_fee", int64(price*10)) - params.SetString("spbill_create_ip", client_ip) - params.SetString("notify_url", notify_url) + params.SetString("spbill_create_ip", clientIP) + params.SetString("notify_url", wxInfo.NotifyURL) params.SetString("trade_type", "JSAPI") - params.SetString("openid", open_id) + params.SetString("openid", openID) params.SetString("nonce_str", tools.GetRandomString(32)) // 随机字符串 - params.SetString("out_trade_no", order_id) // 商户订单号 + params.SetString("out_trade_no", orderID) // 商户订单号 params.SetString("sign", client.Sign(params)) // 签名 c.Sign(params) log.Println("paramsparams", params) // 发送查询企业付款请求 - ret, err := client.Post(unifiedOrderUrl, params, true) + ret, err := client.Post(unifiedOrderURL, params, true) if err != nil { mylog.Error(err) msg := message.GetErrorMsg(message.UnknownError) @@ -77,10 +79,10 @@ func SmallAppUnifiedorder(open_id string, price int, price_body, order_id, clien dd["signType"] = "MD5" dd["paySign"] = "MD5" //appId=wxd678efh567hg6787&nonceStr=5K8264ILTKCH16CQ2502SI8ZNMTM67VS&package=prepay_id=&signType=MD5&timeStamp=1490840662&key=qazwsxedcrfvtgbyhnujmikolp111111 - str := "appId=" + pay_appId + "&nonceStr=" + dd["nonceStr"] + "&package=" + dd["package"] + "&signType=MD5&timeStamp=" + dd["timeStamp"] + "&key=" + apiKey + str := "appId=" + wxInfo.AppID + "&nonceStr=" + dd["nonceStr"] + "&package=" + dd["package"] + "&signType=MD5&timeStamp=" + dd["timeStamp"] + "&key=" + wxInfo.APIKey by := md5.Sum([]byte(str)) dd["paySign"] = strings.ToUpper(fmt.Sprintf("%x", by)) - dd["order_id"] = order_id + dd["order_id"] = orderID msg := message.GetSuccessMsg() msg.Data = dd @@ -92,13 +94,14 @@ func SmallAppUnifiedorder(open_id string, price int, price_body, order_id, clien return msg } +// OnSelectData 统一查询接口 /* 统一查询接口 open_id:用户唯一标识 order_id : 商户订单号 */ -func OnSelectData(open_id, order_id string) (int, message.MessageBody) { - if !tools.CheckParam(open_id, order_id) { //参数检测 +func OnSelectData(openID, orderID string) (int, message.MessageBody) { + if !tools.CheckParam(openID, orderID) { //参数检测 return 0, message.GetErrorMsg(message.ParameterInvalid) } @@ -108,17 +111,17 @@ func OnSelectData(open_id, order_id string) (int, message.MessageBody) { // 查询企业付款接口请求参数 params.SetString("appid", client.AppId) params.SetString("mch_id", client.MchId) - params.SetString("out_trade_no", order_id) //商户订单号 + params.SetString("out_trade_no", orderID) //商户订单号 params.SetString("nonce_str", tools.GetRandomString(32)) // 随机字符串 params.SetString("sign", client.Sign(params)) // 签名 c.Sign(params) // 发送查询企业付款请求 ret := make(wxpay.Params) var err error - ret, err = client.Post(queryOrderUrl, params, true) + ret, err = client.Post(queryOrderURL, params, true) if err != nil { //做再次确认 time.Sleep(time.Second * 1) - ret, err = client.Post(queryOrderUrl, params, true) + ret, err = client.Post(queryOrderURL, params, true) if err != nil { mylog.Error(err) msg := message.GetSuccessMsg() @@ -164,6 +167,7 @@ func OnSelectData(open_id, order_id string) (int, message.MessageBody) { return code, msg } +// RefundPay 申请退款 /* 申请退款 open_id:用户唯一标识 @@ -172,8 +176,8 @@ func OnSelectData(open_id, order_id string) (int, message.MessageBody) { total_fee: 订单总金额 分 refund_fee: 退款总金额 分 */ -func RefundPay(open_id, order_id, refund_no string, total_fee, refund_fee int) (bool, message.MessageBody) { - if !tools.CheckParam(open_id, order_id) { //参数检测 +func RefundPay(openID, orderID, refundNO string, totalFee, refundFee int) (bool, message.MessageBody) { + if !tools.CheckParam(openID, orderID) { //参数检测 return false, message.GetErrorMsg(message.ParameterInvalid) } code := false @@ -181,15 +185,15 @@ func RefundPay(open_id, order_id, refund_no string, total_fee, refund_fee int) ( // 退款请求参数 params.SetString("appid", client.AppId) params.SetString("mch_id", client.MchId) - params.SetString("out_trade_no", order_id) //商户订单号 - params.SetString("out_refund_no", refund_no) //商户退款单号 - params.SetInt64("total_fee", int64(total_fee)) // 订单总金额(分) - params.SetInt64("refund_fee", int64(refund_fee)) // 退款金额(分) + params.SetString("out_trade_no", orderID) //商户订单号 + params.SetString("out_refund_no", refundNO) //商户退款单号 + params.SetInt64("total_fee", int64(totalFee)) // 订单总金额(分) + params.SetInt64("refund_fee", int64(refundFee)) // 退款金额(分) params.SetString("nonce_str", tools.GetRandomString(32)) // 随机字符串 params.SetString("sign", client.Sign(params)) // 签名 c.Sign(params) // 发送申请退款请求 - ret, err := client.Post(refundUrl, params, true) + ret, err := client.Post(refundURL, params, true) if err != nil { mylog.Error(err) msg := message.GetErrorMsg(message.UnknownError) diff --git a/workerpool/def.go b/workerpool/def.go new file mode 100644 index 0000000..33cc2b0 --- /dev/null +++ b/workerpool/def.go @@ -0,0 +1,28 @@ +package workerpool + +import ( + "sync" + "time" + + "github.com/xxjwxc/public/myqueue" +) + +// TaskHandler process .定义函数回调体 +type TaskHandler func() error + +// workerPool serves incoming connections via a pool of workers +// in FILO order, i.e. the most recently stopped worker will serve the next +// incoming connection. +// +// Such a scheme keeps CPU caches hot (in theory). +type WorkerPool struct { + //sync.Mutex + //maxWorkersCount int //最大的工作协程数 + //start sync.Once + closed int32 + errChan chan error //错误chan + timeout time.Duration //最大超时时间 + wg sync.WaitGroup + task chan TaskHandler + waitingQueue *myqueue.MyQueue +} diff --git a/workerpool/workerpool.go b/workerpool/workerpool.go new file mode 100644 index 0000000..fd2aea3 --- /dev/null +++ b/workerpool/workerpool.go @@ -0,0 +1,152 @@ +package workerpool + +import ( + "context" + "sync/atomic" + "time" + + "github.com/xxjwxc/public/myqueue" + + "github.com/xxjwxc/public/mylog" +) + +//New 注册工作池,并设置最大并发数 +//new workpool and set the max number of concurrencies +func New(max int) *WorkerPool { + if max < 1 { + max = 1 + } + + p := &WorkerPool{ + task: make(chan TaskHandler, 2*max), + errChan: make(chan error, 1), + waitingQueue: myqueue.New(), + } + + go p.loop(max) + return p +} + +//SetTimeout 设置超时时间 +func (p *WorkerPool) SetTimeout(timeout time.Duration) { + p.timeout = timeout +} + +//Add to the workpool and return immediately +//Do 添加到工作池,并立即返回 +func (p *WorkerPool) Do(fn TaskHandler) { + if p.IsClosed() { // 已关闭 + return + } + p.waitingQueue.Push(fn) + //p.task <- fn +} + +//Add to the workpool and wait for execution to complete before returning +//DoWait 添加到工作池,并等待执行完成之后再返回 +func (p *WorkerPool) DoWait(task TaskHandler) { + if p.IsClosed() { // closed + return + } + + doneChan := make(chan struct{}) + p.waitingQueue.Push(TaskHandler(func() error { + defer close(doneChan) + return task() + })) + <-doneChan +} + +//Waiting for the worker thread to finish executing +//Wait 等待工作线程执行结束 +func (p *WorkerPool) Wait() error { + p.waitingQueue.Wait() //等待队列结束 + close(p.task) + p.wg.Wait() //等待结束 + select { + case err := <-p.errChan: + return err + default: + return nil + } +} + +//Determine whether it is complete (non-blocking) +//IsDone 判断是否完成 (非阻塞) +func (p *WorkerPool) IsDone() bool { + if p == nil || p.task == nil { + return true + } + + return len(p.task) == 0 +} + +//Has it been closed? +//IsClosed 是否已经关闭 +func (p *WorkerPool) IsClosed() bool { + if atomic.LoadInt32(&p.closed) == 1 { // closed + return true + } + return false +} + +func (p *WorkerPool) startQueue() { + for { + fn := p.waitingQueue.Pop().(TaskHandler) + if p.IsClosed() { // closed + p.waitingQueue.Close() + break + } + + if fn != nil { + p.task <- fn + } + } +} + +func (p *WorkerPool) loop(maxWorkersCount int) { + go p.startQueue() //Startup queue , 启动队列 + + p.wg.Add(maxWorkersCount) // Maximum number of work cycles,最大的工作协程数 + //Start Max workers, 启动max个worker + for i := 0; i < maxWorkersCount; i++ { + go func() { + defer p.wg.Done() + // worker 开始干活 + for wt := range p.task { + if wt == nil || atomic.LoadInt32(&p.closed) == 1 { //returns immediately,有err 立即返回 + continue //It needs to be consumed before returning.需要先消费完了之后再返回, + } + + closed := make(chan struct{}, 1) + // Set timeout, priority task timeout.有设置超时,优先task 的超时 + if p.timeout > 0 { + ct, cancel := context.WithTimeout(context.Background(), p.timeout) + go func() { + select { + case <-ct.Done(): + p.errChan <- ct.Err() + //if atomic.LoadInt32(&p.closed) != 1 { + mylog.Error(ct.Err()) + atomic.StoreInt32(&p.closed, 1) + cancel() + case <-closed: + } + }() + } + + err := wt() //Points of Execution.真正执行的点 + close(closed) + if err != nil { + select { + case p.errChan <- err: + //if atomic.LoadInt32(&p.closed) != 1 { + mylog.Error(err) + atomic.StoreInt32(&p.closed, 1) + default: + } + } + } + }() + } +} diff --git a/workerpool/workerpool_test.go b/workerpool/workerpool_test.go new file mode 100644 index 0000000..947a25a --- /dev/null +++ b/workerpool/workerpool_test.go @@ -0,0 +1,105 @@ +package workerpool + +import ( + "fmt" + "testing" + "time" + + "github.com/xxjwxc/public/errors" +) + +//template +func TestWorkerPoolStart(t *testing.T) { + wp := New(10) //Set the maximum number of threads,设置最大线程数 + for i := 0; i < 20; i++ { //Open 20 requests 开启20个请求 + ii := i + wp.Do(func() error { + for j := 0; j < 10; j++ { + fmt.Println(fmt.Sprintf("%v->\t%v", ii, j)) + time.Sleep(1 * time.Second) + } + //time.Sleep(1 * time.Second) + return nil + }) + } + + wp.Wait() + fmt.Println("down") +} + +//Support for error return +//支持错误返回 +func TestWorkerPoolError(t *testing.T) { + wp := New(10) //Set the maximum number of threads,设置最大线程数 + for i := 0; i < 20; i++ { + ii := i + wp.Do(func() error { + for j := 0; j < 10; j++ { + fmt.Println(fmt.Sprintf("%v->\t%v", ii, j)) + if ii == 1 { + return errors.Cause(errors.New("my test err")) //有err 立即返回 + } + time.Sleep(1 * time.Second) + } + + return nil + //time.Sleep(1 * time.Second) + //return errors.New("my test err") + }) + } + + err := wp.Wait() + if err != nil { + fmt.Println(err) + } + fmt.Println("down") +} + +//Determine whether completion (non-blocking) is placed in the workpool and wait for execution results +//放到工作池里面 且等待执行结果 +func TestWorkerPoolDoWait(t *testing.T) { + wp := New(5) //Set the maximum number of threads,设置最大线程数 + for i := 0; i < 10; i++ { + ii := i + wp.DoWait(func() error { + for j := 0; j < 5; j++ { //每次打印0-10的值 + fmt.Println(fmt.Sprintf("%v->\t%v", ii, j)) + // if ii == 1 { + // return errors.New("my test err") + // } + time.Sleep(1 * time.Second) + } + + return nil + //time.Sleep(1 * time.Second) + //return errors.New("my test err") + }) + } + + err := wp.Wait() + if err != nil { + fmt.Println(err) + } + fmt.Println("down") +} + +//Determine whether it is complete (non-blocking) +//判断是否完成 (非阻塞) +func TestWorkerPoolIsDone(t *testing.T) { + wp := New(5) //Set the maximum number of threads,设置最大线程数 + for i := 0; i < 10; i++ { + // ii := i + wp.Do(func() error { + for j := 0; j < 5; j++ { + //fmt.Println(fmt.Sprintf("%v->\t%v", ii, j)) + time.Sleep(1 * time.Second) + } + return nil + }) + + fmt.Println(wp.IsDone()) + } + wp.Wait() + fmt.Println(wp.IsDone()) + fmt.Println("down") +}