marion 5 years ago
commit
c780a5d89e

+ 51 - 0
.gitignore

@@ -0,0 +1,51 @@
+# ---> Go
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
+
+# compiled output
+/dist
+/dist-server
+/tmp
+/out-tsc
+/bin
+/logs
+
+# IDEs and editors
+/.idea
+.project
+.classpath
+.c9/
+*.launch
+.settings/
+*.sublime-workspace
+vendor
+
+# IDE - VSCode
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+

+ 8 - 0
LICENSE

@@ -0,0 +1,8 @@
+MIT License
+Copyright (c) <year> <copyright holders>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 125 - 0
README.md

@@ -0,0 +1,125 @@
+# Golang 公共项目
+
+## Golang开发环境安装
+
+### 安装
+
+[此处](https://studygolang.com/dl) 下载安装
+
+### 环境变量配置
+
+**Windows**
+
+在我的电脑->属性->高级->环境变量->系统变量中添加以下环境变量:
+
+```
+GOROOT=C:\go
+GOPATH=%USERPROFILE%\go
+```
+
+PATH环境变量末尾添加 %GOROOT%\bin
+
+**MAC OS X**
+
+> cd
+
+> sudo vi .bash_profile
+
+修改以下项目
+
+```
+export GOROOT=/usr/local/go
+export GOPATH=$HOME/go
+export PATH="${GOROOT}/bin:${PATH}"
+```
+
+> source .bash_profile
+
+更新配置
+
+
+与Java、Python、NodeJS、PHP等均不同,只是开发环境需要安装用于代码编译,生产环境直接运行项目编译好的可执行文件即可,无需安装任何类似JRE、Python、v8、PHP的虚拟机或运行时环境!
+
+执行以下操作前请理解并确认:
+1. GOPATH 环境变量已配置,例如配置到用户目录下的go目录
+2. GOPATH 目录是用于存放golang项目和其相关依赖的目录,所有golang项目代码都应该位于GOPATH目录中的src子目录下的包括代码托管地址、组织名、项目名在内的多级子目录下
+
+## 获取项目
+   
+### 获取
+
+> go get -v -insecure -d git.haoqitour.com/haoqi/go-common
+
+参数:-v 显示详情,-insecure 非https版本仓库路径,-d 仅获取,不编译安装到GOPATH
+
+可能会卡住或者报错,不用管,只要项目本身源代码文件获取下来了即可,文件保存位置即在上述GOPATH目录中的子目录里
+
+**完成上述操作之后,以后日常开发都可以正常使用git命令来获取和提交此项目代码**
+
+### 建立软连接
+
+建立软连接的目的仅仅是为了方便找而已!
+
+使用GoLand等开发工具时,仍然打开GOPATH里面的项目目录,而不是打开软连接位置的!
+
+**Windows** 
+
+Power Shell 或 Windows CMD (需要用管理员身份运行):
+
+**注意,Windows Power Shell打开后首先输入cmd进入Windows CMD,下同不再累述**
+
+> mklink /D %USERPROFILE%\Documents\project\haoqitour\go-common %GOPATH%\src\git.haoqitour.com\haoqi\go-common
+
+**MAC OS X**
+
+> sudo ln -s $GOPATH/src/git.haoqitour.com/haoqi/go-common/ ~/Documents/project/haoqitour/go-common
+
+## 相关依赖
+
+### Golang官方包
+
+Golang运行时 >= 1.10.3
+
+其他常用golang官方包,因为golang.org被墙,所以用以下步骤获取:
+
+**Windows**
+
+```cmd
+git clone https://github.com/golang/net.git %GOPATH%\src\golang.org\x\net
+git clone https://github.com/golang/text.git %GOPATH%\src\golang.org\x\text
+git clone https://github.com/golang/tools.git %GOPATH%\src\golang.org\x\tools
+git clone https://github.com/golang/sys.git %GOPATH%\src\golang.org\x\sys
+git clone https://github.com/golang/crypto.git %GOPATH%\src\golang.org\x\crypto
+cd %GOPATH%\src
+go install golang.org\x\text
+```
+
+**MAC OS X**
+
+```bash
+git clone https://github.com/golang/net.git $GOPATH/src/golang.org/x/net \
+&& git clone https://github.com/golang/text.git $GOPATH/src/golang.org/x/text \
+&& git clone https://github.com/golang/tools.git $GOPATH/src/golang.org/x/tools \
+&& git clone https://github.com/golang/sys.git $GOPATH/src/golang.org/x/sys \
+&& git clone https://github.com/golang/crypto.git $GOPATH/src/golang.org/x/crypto \
+&& cd $GOPATH/src/ \
+&& go install golang.org/x/text
+```
+
+### 第三方包
+
+如果有未下载的依赖包,进入项目目录,执行以下命令即可
+
+> go get -v
+
+本项目主要依赖包如下
+
+[结构化日志logrus](https://github.com/Sirupsen/logrus)
+
+[结构化日志logrus输出到graylog](https://gopkg.in/gemnasium/logrus-graylog-hook.v2)
+
+[配置文件读取viper](https://github.com/spf13/viper)
+
+[数据库ORM框架xorm](https://github.com/go-xorm/xorm)
+
+默认的golang依赖库是全局的,如果希望各个项目各自管理自己的依赖,可以使用 [godep](https://github.com/tools/godep)

+ 102 - 0
main.go

@@ -0,0 +1,102 @@
+package main
+
+import (
+	"fmt"
+	"git.haoqitour.com/haoqi/go-common/utils"
+	"strconv"
+)
+
+func main() {
+	newId, _ := utils.SingletonSnowflakeKeyGen().NextId()
+	fmt.Printf("%v\n", utils.Decompose(newId))
+
+	println(len("oXnePxBaI4qUWq-X0HIU-ce6vgak"))
+	//numCPU := runtime.NumCPU()
+	//runtime.GOMAXPROCS(numCPU)
+	//fmt.Println("number of cpu:", numCPU)
+	//
+	//consumer := make(chan utils.Long)
+	//
+	//const numID = 10000
+	//generate := func() {
+	//	for i := 0; i < numID; i++ {
+	//		consumer <- utils.NextId()
+	//	}
+	//}
+	//
+	//const numGenerator = 10
+	//for i := 0; i < numGenerator; i++ {
+	//	go generate()
+	//}
+	//
+	//set := mapset.NewSet()
+	//for i := 0; i < numID*numGenerator; i++ {
+	//	id := <-consumer
+	//	if set.Contains(id) {
+	//		log.Fatal("duplicated id")
+	//	} else {
+	//		set.Add(id)
+	//	}
+	//}
+	//fmt.Println("number of id:", set.Cardinality())
+
+	// 临时测试代码
+	//c, err := jws.NewJWT("hard to guess string").
+	//	ParseToken("eyJhbGciOiJIUzI1NiIsImlhdCI6MTUzNzg0Njc4MywiZXhwIjoxNTM5MDU2MzgzfQ.eyJpZCI6MzI0NTEzNH0.RqRoad8H5oAGY6L3qMLcxCUYE5Fl7-MXtpSyfSU4aqU")
+	//if nil != err {
+	//	println(err)
+	//}
+	//if nil != c {
+	//	fmt.Printf("%v\n", c)
+	//	fmt.Printf(strconv.FormatFloat(c.ID, 'f', 0, 64))
+	//}
+
+	//n := 3
+	//for i := 0; i < 500; i++ {
+	//	newId, _ := utils.SingletonSnowflakeKeyGen().NextID()
+	//
+	//	idStr := strconv.FormatUint(newId, 10)
+	//	sLen := len(idStr)
+	//	subLen:= sLen / n
+	//	if sLen % n != 0 {
+	//		subLen++
+	//	}
+	//
+	//	var partArr []string
+	//	var codeArr []string
+	//	for j := 0; j < n; j++ {
+	//		start := subLen * j
+	//		end := subLen * (j + 1)
+	//		if end >= sLen {
+	//			end = sLen
+	//		}
+	//		part := idStr[start:end]
+	//		partArr = append(partArr, part)
+	//
+	//		partNum, _ := strconv.ParseInt(part, 10, 32)
+	//		code := decimalToAny(int(partNum), 35)
+	//		codeArr = append(codeArr, code)
+	//	}
+	//
+	//	println(newId, " : ", strings.Join(partArr, "-"), " : ", strings.Join(codeArr, "-"))
+	//}
+}
+
+var tenToAny = map[int]string{0: "0", 1: "1", 2: "2", 3: "3", 4: "4", 5: "5", 6: "6", 7: "7", 8: "8", 9: "9", 10: "a", 11: "b", 12: "c", 13: "d", 14: "e", 15: "f", 16: "g", 17: "h", 18: "i", 19: "j", 20: "k", 21: "l", 22: "m", 23: "n", 24: "o", 25: "p", 26: "q", 27: "r", 28: "s", 29: "t", 30: "u", 31: "v", 32: "w", 33: "x", 34: "y", 35: "z", 36: ":", 37: ";", 38: "<", 39: "=", 40: ">", 41: "?", 42: "@", 43: "[", 44: "]", 45: "^", 46: "_", 47: "{", 48: "|", 49: "}", 50: "A", 51: "B", 52: "C", 53: "D", 54: "E", 55: "F", 56: "G", 57: "H", 58: "I", 59: "J", 60: "K", 61: "L", 62: "M", 63: "N", 64: "O", 65: "P", 66: "Q", 67: "R", 68: "S", 69: "T", 70: "U", 71: "V", 72: "W", 73: "X", 74: "Y", 75: "Z"}
+
+func decimalToAny(num int, n int) string {
+	new_num_str := ""
+	var remainder int
+	var remainder_string string
+	for num != 0 {
+		remainder = num % n
+		if 76 > remainder && remainder > 9 {
+			remainder_string = tenToAny[remainder]
+		} else {
+			remainder_string = strconv.Itoa(remainder)
+		}
+		new_num_str = remainder_string + new_num_str
+		num = num / n
+	}
+	return new_num_str
+}

+ 115 - 0
utils/common_utils.go

@@ -0,0 +1,115 @@
+package utils
+
+import (
+	"bytes"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"reflect"
+	"runtime"
+	"strings"
+	"time"
+)
+
+// 获取当前执行目录
+func GetCurrentDir() (string, error) {
+	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
+	if err != nil {
+		return "", err
+	}
+	return strings.Replace(dir, "\\", "/", -1), nil
+}
+
+// 检查对象是否包含在指定集合内
+func Contains(set interface{}, obj interface{}) bool {
+	targetValue := reflect.ValueOf(set)
+	switch reflect.TypeOf(set).Kind() {
+	case reflect.Slice, reflect.Array:
+		for i := 0; i < targetValue.Len(); i++ {
+			if targetValue.Index(i).Interface() == obj {
+				return true
+			}
+		}
+	case reflect.Map:
+		if targetValue.MapIndex(reflect.ValueOf(obj)).IsValid() {
+			return true
+		}
+	}
+
+	return false
+}
+
+// 获取Unix时间戳
+func GetUTCTicks(datetime time.Time) int64 {
+	return datetime.UnixNano() / int64(time.Millisecond)
+}
+
+
+var (
+	dunno     = []byte("???")
+	centerDot = []byte("·")
+	dot       = []byte(".")
+	slash     = []byte("/")
+)
+
+// 获取调用堆栈信息
+func GetStack(skip int) []byte {
+	buf := new(bytes.Buffer) // the returned data
+	// As we loop, we open files and read them. These variables record the currently
+	// loaded file.
+	var lines [][]byte
+	var lastFile string
+	for i := skip; ; i++ { // Skip the expected number of frames
+		pc, file, line, ok := runtime.Caller(i)
+		if !ok {
+			break
+		}
+		// Print this much at least.  If we can't find the source, it won't show.
+		_, _ = fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc)
+		if file != lastFile {
+			data, err := ioutil.ReadFile(file)
+			if err != nil {
+				continue
+			}
+			lines = bytes.Split(data, []byte{'\n'})
+			lastFile = file
+		}
+		_, _ = fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line))
+	}
+	return buf.Bytes()
+}
+
+// source returns a space-trimmed slice of the n'th line.
+func source(lines [][]byte, n int) []byte {
+	n-- // in stack trace, lines are 1-indexed but our array is 0-indexed
+	if n < 0 || n >= len(lines) {
+		return dunno
+	}
+	return bytes.TrimSpace(lines[n])
+}
+
+// function returns, if possible, the name of the function containing the PC.
+func function(pc uintptr) []byte {
+	fn := runtime.FuncForPC(pc)
+	if fn == nil {
+		return dunno
+	}
+	name := []byte(fn.Name())
+	// The name includes the path name to the package, which is unnecessary
+	// since the file name is already included.  Plus, it has center dots.
+	// That is, we see
+	//	runtime/debug.*T·ptrmethod
+	// and want
+	//	*T.ptrmethod
+	// Also the package path might contains dot (e.g. code.google.com/...),
+	// so first eliminate the path prefix
+	if lastslash := bytes.LastIndex(name, slash); lastslash >= 0 {
+		name = name[lastslash+1:]
+	}
+	if period := bytes.Index(name, dot); period >= 0 {
+		name = name[period+1:]
+	}
+	name = bytes.Replace(name, centerDot, dot, -1)
+	return name
+}

+ 370 - 0
utils/date/date.go

@@ -0,0 +1,370 @@
+package date
+
+import (
+	"time"
+	"errors"
+	"strings"
+	"database/sql/driver"
+)
+
+type Date time.Time
+
+const NormalDateFormat = "2006-01-02"
+
+func (t Date) MarshalJSON() ([]byte, error) {
+	if y := t.Year(); y < 0 || y >= 10000 {
+		return nil, errors.New("Time.MarshalJSON: year outside of range [0,9999]")
+	}
+	if t.IsZero() {
+		b := []byte(`""`)
+		return b, nil
+	}
+	b := make([]byte, 0, len(NormalDateFormat)+2)
+	b = append(b, '"')
+	b = t.AppendFormat(b, NormalDateFormat)
+	b = append(b, '"')
+	return b, nil
+}
+
+func (t *Date) UnmarshalJSON(value []byte) error {
+	var v = strings.TrimSpace(strings.Trim(string(value), "\""))
+	if v == "" {
+		return nil
+	}
+	tm, err := time.ParseInLocation(NormalDateFormat, v, time.Local)
+	if err != nil {
+		return err
+	}
+	*t = Date(Date(tm).OfTime(0,0,0).OfNanosecond(0))
+	return nil
+}
+
+func (t Date) MarshalText() ([]byte, error) {
+	if y := t.Year(); y < 0 || y >= 10000 {
+		return nil, errors.New("Time.MarshalText: year outside of range [0,9999]")
+	}
+
+	b := make([]byte, 0, len(NormalDateFormat))
+	return t.AppendFormat(b, NormalDateFormat), nil
+}
+
+func (t *Date) UnmarshalText(data []byte) error {
+	*t = t.FromString(string(data))
+	return nil
+}
+
+func (t Date) FromString(str string) Date {
+	tm, err := time.Parse(NormalDateFormat, str)
+	if nil != err {
+		return Unix(0, 0).ToDate()
+	}
+	return Date(Date(tm).OfTime(0,0,0).OfNanosecond(0))
+}
+
+func (t Date) String() string {
+	return t.Format(NormalDateFormat)
+}
+
+func (t Date) Value() (driver.Value, error) {
+	if t.IsZero() {
+		return nil, nil
+	}
+
+	return t.Format(NormalDateFormat), nil
+}
+
+func (t *Date) Scan(value interface{}) error {
+	if value == nil {
+		return nil
+	}
+
+	*t = Date(value.(time.Time))
+	return nil
+}
+
+func (t Date) Format(layout string) string {
+	return t.T().Format(layout)
+}
+
+func (t Date) AppendFormat(b []byte, layout string) []byte {
+	return t.T().AppendFormat(b, layout)
+}
+
+// 当前时间
+func Today() Date {
+	return Date(Date(time.Now()).OfTime(0,0,0).OfNanosecond(0))
+}
+
+// 构造date.Time
+func NewDate(year int, month time.Month, day int, loc *time.Location) Date {
+	return Date(time.Date(year, month, day, 0, 0, 0, 0, loc))
+}
+
+// 转date.Datetime
+func (t Date) ToDatetime() Datetime {
+	return Datetime(t)
+}
+
+// 转date.Date
+func AsDate(tm time.Time) Date {
+	return Date(Date(tm).OfTime(0,0,0).OfNanosecond(0))
+}
+
+// 转date.Time
+func (t Date) ToTime() Time {
+	return Time(t)
+}
+
+// 转time.Time
+func (t Date) T() time.Time {
+	return time.Time(t)
+}
+
+// 是否晚于
+func (t Date) After(u Date) bool {
+	return t.T().After(u.T())
+}
+
+// 是否早于
+func (t Date) Before(u Date) bool {
+	return t.T().Before(u.T())
+}
+
+// 是否等于
+func (t Date) Equal(u Date) bool {
+	return t.T().Equal(u.T())
+}
+
+// 是否零值
+func (t Date) IsZero() bool {
+	return t.T().IsZero()
+}
+
+// 获取年、月、日
+func (t Date) Date() (year int, month time.Month, day int) {
+	return t.T().Date()
+}
+
+// 获取年
+func (t Date) Year() int {
+	return t.T().Year()
+}
+
+// 获取月
+func (t Date) Month() time.Month {
+	return t.T().Month()
+}
+
+// 获取日
+func (t Date) Day() int {
+	return t.T().Day()
+}
+
+// 获取星期几
+func (t Date) Weekday() time.Weekday {
+	return t.T().Weekday()
+}
+
+// 获取年、第几周
+func (t Date) ISOWeek() (year, week int) {
+	return t.T().ISOWeek()
+}
+
+// 获取时、分、秒
+func (t Date) Clock() (hour, min, sec int) {
+	return t.T().Clock()
+}
+
+// 获取小时
+func (t Date) Hour() int {
+	return t.T().Hour()
+}
+
+// 获取分钟
+func (t Date) Minute() int {
+	return t.T().Minute()
+}
+
+// 获取秒
+func (t Date) Second() int {
+	return t.T().Second()
+}
+
+// 获取毫秒
+func (t Date) Millisecond() int {
+	return int(time.Duration(t.T().Nanosecond()) / time.Millisecond)
+}
+
+// 获取微秒
+func (t Date) Microsecond() int {
+	return int(time.Duration(t.T().Nanosecond()) / time.Microsecond)
+}
+
+// 获取纳秒
+func (t Date) Nanosecond() int {
+	return t.T().Nanosecond()
+}
+
+// 获取是一年中第几天
+func (t Date) YearDay() int {
+	return t.T().YearDay()
+}
+
+// 获取该时间 - 参数时间 的时间差
+func (t Date) Sub(u Date) time.Duration {
+	return t.T().Sub(u.T())
+}
+
+// 加一个时间差
+func (t Date) Add(d time.Duration) Datetime {
+	return Datetime(t.T().Add(d))
+}
+
+// 加年、月、日
+func (t Date) AddDate(years int, months int, days int) Date {
+	return Date(t.T().AddDate(years, months, days))
+}
+
+// 加时、分、秒
+func (t Date) AddTime(hours int, minutes int, seconds int) Datetime {
+	d := time.Duration(hours) * time.Hour + time.Duration(minutes) * time.Minute + time.Duration(seconds) * time.Second
+	return t.Add(d)
+}
+
+// 加年
+func (t Date) AddYears(years int) Date {
+	return t.AddDate(years, 0, 0)
+}
+
+// 加月
+func (t Date) AddMonths(months int) Date {
+	return t.AddDate(0, months, 0)
+}
+
+// 加日
+func (t Date) AddDays(days int) Date {
+	return t.AddDate(0, 0, days)
+}
+
+// 加小时
+func (t Date) AddHours(hours int) Datetime {
+	d := time.Duration(hours) * time.Hour
+	return t.Add(d)
+}
+
+// 加分钟
+func (t Date) AddMinutes(minutes int) Datetime {
+	d := time.Duration(minutes) * time.Minute
+	return t.Add(d)
+}
+
+// 加秒
+func (t Date) AddSeconds(seconds int) Datetime {
+	d := time.Duration(seconds) * time.Second
+	return t.Add(d)
+}
+
+// 加纳秒
+func (t Date) AddNanoseconds(nanoseconds int) Datetime {
+	d := time.Duration(nanoseconds) * time.Nanosecond
+	return t.Add(d)
+}
+
+// 指定时间
+func (t Date) Of(year int, month time.Month, day int, hour int, minute int, second int, nanosecond int) Datetime {
+	return Datetime(time.Date(year, month, day, hour, minute, second, nanosecond, t.Location()))
+}
+
+// 指定年、月、日
+func (t Date) OfDate(year int, month time.Month, day int) Date {
+	hour, minute, second := t.Clock()
+	return Date(t.Of(year, month, day, hour, minute, second, t.Nanosecond()))
+}
+
+// 指定时、分、秒
+func (t Date) OfTime(hour int, minute int, second int) Datetime {
+	year, month, day := t.Date()
+	return t.Of(year, month, day, hour, minute, second, t.Nanosecond())
+}
+
+// 指定年
+func (t Date) OfYear(year int) Date {
+	return t.OfDate(year, t.Month(), t.Day())
+}
+
+// 指定月
+func (t Date) OfMonth(month time.Month) Date {
+	return t.OfDate(t.Year(), month, t.Day())
+}
+
+// 指定日
+func (t Date) OfDay(day int) Date {
+	return t.OfDate(t.Year(), t.Month(), day)
+}
+
+// 指定小时
+func (t Date) OfHour(hour int) Datetime {
+	return t.OfTime(hour, t.Minute(), t.Second())
+}
+
+// 指定分钟
+func (t Date) OfMinute(minute int) Datetime {
+	return t.OfTime(t.Hour(), minute, t.Second())
+}
+
+// 指定秒
+func (t Date) OfSecond(second int) Datetime {
+	return t.OfTime(t.Hour(), t.Minute(), second)
+}
+
+// 指定纳秒
+func (t Date) OfNanosecond(nanosecond int) Datetime {
+	year, month, day := t.Date()
+	hour, minute, second := t.Clock()
+	return t.Of(year, month, day, hour, minute, second, nanosecond)
+}
+
+// 转UTC时间
+func (t Date) UTC() Datetime {
+	return Datetime(t.T().UTC())
+}
+
+// 转本地时间
+func (t Date) Local() Datetime {
+	return Datetime(t.T().Local())
+}
+
+// 转指定时区时间
+func (t Date) In(loc *time.Location) Datetime {
+	return Datetime(t.T().In(loc))
+}
+
+// 获取时区
+func (t Date) Location() *time.Location {
+	return t.T().Location()
+}
+
+// 获取时区
+func (t Date) Zone() (name string, offset int) {
+	return t.T().Zone()
+}
+
+// 获取UTC时间戳
+func (t Date) Unix() int64 {
+	return t.T().Unix()
+}
+
+// 获取UTC纳秒数
+func (t Date) UnixNano() int64 {
+	return t.T().Unix()
+}
+
+// 从目标日期开始到现在的时间差
+func SinceDate(t Date) time.Duration {
+	return Today().Sub(t)
+}
+
+// 现在到目标日期的时间差
+func UntilDate(t Date) time.Duration {
+	return t.Sub(Today())
+}

+ 375 - 0
utils/date/datetime.go

@@ -0,0 +1,375 @@
+package date
+
+import (
+	"time"
+	"errors"
+	"strings"
+	"database/sql/driver"
+)
+
+type Datetime time.Time
+
+const NormalDatetimeFormat = "2006-01-02 15:04:05"
+
+func (t Datetime) MarshalJSON() ([]byte, error) {
+	if y := t.Year(); y < 0 || y >= 10000 {
+		return nil, errors.New("Time.MarshalJSON: year outside of range [0,9999]")
+	}
+	if t.IsZero() {
+		b := []byte(`""`)
+		return b, nil
+	}
+	b := make([]byte, 0, len(NormalDatetimeFormat)+2)
+	b = append(b, '"')
+	b = t.AppendFormat(b, NormalDatetimeFormat)
+	b = append(b, '"')
+	return b, nil
+}
+
+func (t *Datetime) UnmarshalJSON(value []byte) error {
+	var v = strings.TrimSpace(strings.Trim(string(value), "\""))
+	if v == "" {
+		return nil
+	}
+	tm, err := time.ParseInLocation(NormalDatetimeFormat, v, time.Local)
+	if err != nil {
+		return err
+	}
+	*t = Datetime(tm)
+	return nil
+}
+
+func (t Datetime) MarshalText() ([]byte, error) {
+	if y := t.Year(); y < 0 || y >= 10000 {
+		return nil, errors.New("Time.MarshalText: year outside of range [0,9999]")
+	}
+
+	b := make([]byte, 0, len(NormalDatetimeFormat))
+	return t.AppendFormat(b, NormalDatetimeFormat), nil
+}
+
+func (t *Datetime) UnmarshalText(data []byte) error {
+	*t = t.FromString(string(data))
+	return nil
+}
+
+func (t Datetime) FromString(str string) Datetime {
+	tm, err := time.Parse(NormalDatetimeFormat, str)
+	if nil != err {
+		return Unix(0, 0)
+	}
+	return Datetime(tm)
+}
+
+func (t Datetime) String() string {
+	return t.Format(NormalDatetimeFormat)
+}
+
+func (t Datetime) Value() (driver.Value, error) {
+	if t.IsZero() {
+		return nil, nil
+	}
+
+	return t.Format(NormalDatetimeFormat), nil
+}
+
+func (t *Datetime) Scan(value interface{}) error {
+	if value == nil {
+		return nil
+	}
+
+	*t = Datetime(value.(time.Time))
+	return nil
+}
+
+func (t Datetime) Format(layout string) string {
+	return t.T().Format(layout)
+}
+
+func (t Datetime) AppendFormat(b []byte, layout string) []byte {
+	return t.T().AppendFormat(b, layout)
+}
+
+// 当前时间
+func Now() Datetime {
+	return Datetime(time.Now())
+}
+
+// 构造date.Datetime
+func NewDatetime(year int, month time.Month, day, hour, min, sec, nsec int, loc *time.Location) Datetime {
+	return Datetime(time.Date(year, month, day, hour, min, sec, nsec, loc))
+}
+
+// 转date.Time
+func AsDatetime(tm time.Time) Datetime {
+	return Datetime(tm)
+}
+
+// 转date.Date
+func (t Datetime) ToDate() Date {
+	return Date(t.OfTime(0,0,0).OfNanosecond(0))
+}
+
+// 转date.Time
+func (t Datetime) ToTime() Time {
+	return Time(t.OfDate(1,time.January,1))
+}
+
+// 转time.Time
+func (t Datetime) T() time.Time {
+	return time.Time(t)
+}
+
+// 是否晚于
+func (t Datetime) After(u Datetime) bool {
+	return t.T().After(u.T())
+}
+
+// 是否早于
+func (t Datetime) Before(u Datetime) bool {
+	return t.T().Before(u.T())
+}
+
+// 是否等于
+func (t Datetime) Equal(u Datetime) bool {
+	return t.T().Equal(u.T())
+}
+
+// 是否零值
+func (t Datetime) IsZero() bool {
+	return t.T().IsZero()
+}
+
+// 获取年、月、日
+func (t Datetime) Date() (year int, month time.Month, day int) {
+	return t.T().Date()
+}
+
+// 获取年
+func (t Datetime) Year() int {
+	return t.T().Year()
+}
+
+// 获取月
+func (t Datetime) Month() time.Month {
+	return t.T().Month()
+}
+
+// 获取日
+func (t Datetime) Day() int {
+	return t.T().Day()
+}
+
+// 获取星期几
+func (t Datetime) Weekday() time.Weekday {
+	return t.T().Weekday()
+}
+
+// 获取年、第几周
+func (t Datetime) ISOWeek() (year, week int) {
+	return t.T().ISOWeek()
+}
+
+// 获取时、分、秒
+func (t Datetime) Clock() (hour, min, sec int) {
+	return t.T().Clock()
+}
+
+// 获取小时
+func (t Datetime) Hour() int {
+	return t.T().Hour()
+}
+
+// 获取分钟
+func (t Datetime) Minute() int {
+	return t.T().Minute()
+}
+
+// 获取秒
+func (t Datetime) Second() int {
+	return t.T().Second()
+}
+
+// 获取毫秒
+func (t Datetime) Millisecond() int {
+	return int(time.Duration(t.T().Nanosecond()) / time.Millisecond)
+}
+
+// 获取微秒
+func (t Datetime) Microsecond() int {
+	return int(time.Duration(t.T().Nanosecond()) / time.Microsecond)
+}
+
+// 获取纳秒
+func (t Datetime) Nanosecond() int {
+	return t.T().Nanosecond()
+}
+
+// 获取是一年中第几天
+func (t Datetime) YearDay() int {
+	return t.T().YearDay()
+}
+
+// 获取该时间 - 参数时间 的时间差
+func (t Datetime) Sub(u Datetime) time.Duration {
+	return t.T().Sub(u.T())
+}
+
+// 加一个时间差
+func (t Datetime) Add(d time.Duration) Datetime {
+	return Datetime(t.T().Add(d))
+}
+
+// 加年、月、日
+func (t Datetime) AddDate(years int, months int, days int) Datetime {
+	return Datetime(t.T().AddDate(years, months, days))
+}
+
+// 加时、分、秒
+func (t Datetime) AddTime(hours int, minutes int, seconds int) Datetime {
+	d := time.Duration(hours) * time.Hour + time.Duration(minutes) * time.Minute + time.Duration(seconds) * time.Second
+	return t.Add(d)
+}
+
+// 加年
+func (t Datetime) AddYears(years int) Datetime {
+	return t.AddDate(years, 0, 0)
+}
+
+// 加月
+func (t Datetime) AddMonths(months int) Datetime {
+	return t.AddDate(0, months, 0)
+}
+
+// 加日
+func (t Datetime) AddDays(days int) Datetime {
+	return t.AddDate(0, 0, days)
+}
+
+// 加小时
+func (t Datetime) AddHours(hours int) Datetime {
+	d := time.Duration(hours) * time.Hour
+	return t.Add(d)
+}
+
+// 加分钟
+func (t Datetime) AddMinutes(minutes int) Datetime {
+	d := time.Duration(minutes) * time.Minute
+	return t.Add(d)
+}
+
+// 加秒
+func (t Datetime) AddSeconds(seconds int) Datetime {
+	d := time.Duration(seconds) * time.Second
+	return t.Add(d)
+}
+
+// 加纳秒
+func (t Datetime) AddNanoseconds(nanoseconds int) Datetime {
+	d := time.Duration(nanoseconds) * time.Nanosecond
+	return t.Add(d)
+}
+
+// 指定时间
+func (t Datetime) Of(year int, month time.Month, day int, hour int, minute int, second int, nanosecond int) Datetime {
+	return Datetime(time.Date(year, month, day, hour, minute, second, nanosecond, t.Location()))
+}
+
+// 指定年、月、日
+func (t Datetime) OfDate(year int, month time.Month, day int) Datetime {
+	hour, minute, second := t.Clock()
+	return t.Of(year, month, day, hour, minute, second, t.Nanosecond())
+}
+
+// 指定时、分、秒
+func (t Datetime) OfTime(hour int, minute int, second int) Datetime {
+	year, month, day := t.Date()
+	return t.Of(year, month, day, hour, minute, second, t.Nanosecond())
+}
+
+// 指定年
+func (t Datetime) OfYear(year int) Datetime {
+	return t.OfDate(year, t.Month(), t.Day())
+}
+
+// 指定月
+func (t Datetime) OfMonth(month time.Month) Datetime {
+	return t.OfDate(t.Year(), month, t.Day())
+}
+
+// 指定日
+func (t Datetime) OfDay(day int) Datetime {
+	return t.OfDate(t.Year(), t.Month(), day)
+}
+
+// 指定小时
+func (t Datetime) OfHour(hour int) Datetime {
+	return t.OfTime(hour, t.Minute(), t.Second())
+}
+
+// 指定分钟
+func (t Datetime) OfMinute(minute int) Datetime {
+	return t.OfTime(t.Hour(), minute, t.Second())
+}
+
+// 指定秒
+func (t Datetime) OfSecond(second int) Datetime {
+	return t.OfTime(t.Hour(), t.Minute(), second)
+}
+
+// 指定纳秒
+func (t Datetime) OfNanosecond(nanosecond int) Datetime {
+	year, month, day := t.Date()
+	hour, minute, second := t.Clock()
+	return t.Of(year, month, day, hour, minute, second, nanosecond)
+}
+
+// 转UTC时间
+func (t Datetime) UTC() Datetime {
+	return Datetime(t.T().UTC())
+}
+
+// 转本地时间
+func (t Datetime) Local() Datetime {
+	return Datetime(t.T().Local())
+}
+
+// 转指定时区时间
+func (t Datetime) In(loc *time.Location) Datetime {
+	return Datetime(t.T().In(loc))
+}
+
+// 获取时区
+func (t Datetime) Location() *time.Location {
+	return t.T().Location()
+}
+
+// 获取时区
+func (t Datetime) Zone() (name string, offset int) {
+	return t.T().Zone()
+}
+
+// 获取UTC时间戳
+func (t Datetime) Unix() int64 {
+	return t.T().Unix()
+}
+
+// 获取UTC纳秒数
+func (t Datetime) UnixNano() int64 {
+	return t.T().Unix()
+}
+
+// 从目标时间开始到现在的时间差
+func Since(t Datetime) time.Duration {
+	return Now().Sub(t)
+}
+
+// 现在到目标时间的时间差
+func Until(t Datetime) time.Duration {
+	return t.Sub(Now())
+}
+
+// UTC时间戳和纳秒数转时间
+func Unix(sec int64, nsec int64) Datetime {
+	return Datetime(time.Unix(sec, nsec))
+}

+ 370 - 0
utils/date/time.go

@@ -0,0 +1,370 @@
+package date
+
+import (
+	"time"
+	"errors"
+	"strings"
+	"database/sql/driver"
+)
+
+type Time time.Time
+
+const NormalTimeFormat = "15:04:05"
+
+func (t Time) MarshalJSON() ([]byte, error) {
+	if y := t.Year(); y < 0 || y >= 10000 {
+		return nil, errors.New("Time.MarshalJSON: year outside of range [0,9999]")
+	}
+	if t.IsZero() {
+		b := []byte(`""`)
+		return b, nil
+	}
+	b := make([]byte, 0, len(NormalTimeFormat)+2)
+	b = append(b, '"')
+	b = t.AppendFormat(b, NormalTimeFormat)
+	b = append(b, '"')
+	return b, nil
+}
+
+func (t *Time) UnmarshalJSON(value []byte) error {
+	var v = strings.TrimSpace(strings.Trim(string(value), "\""))
+	if v == "" {
+		return nil
+	}
+	tm, err := time.ParseInLocation(NormalTimeFormat, v, time.Local)
+	if err != nil {
+		return err
+	}
+	*t = Time(Time(tm).OfDate(1,time.January,1))
+	return nil
+}
+
+func (t Time) MarshalText() ([]byte, error) {
+	if y := t.Year(); y < 0 || y >= 10000 {
+		return nil, errors.New("Time.MarshalText: year outside of range [0,9999]")
+	}
+
+	b := make([]byte, 0, len(NormalTimeFormat))
+	return t.AppendFormat(b, NormalTimeFormat), nil
+}
+
+func (t *Time) UnmarshalText(data []byte) error {
+	*t = t.FromString(string(data))
+	return nil
+}
+
+func (t Time) FromString(str string) Time {
+	tm, err := time.Parse(NormalTimeFormat, str)
+	if nil != err {
+		return Unix(0, 0).ToTime()
+	}
+	return Time(Time(tm).OfDate(1,time.January,1))
+}
+
+func (t Time) String() string {
+	return t.Format(NormalTimeFormat)
+}
+
+func (t Time) Value() (driver.Value, error) {
+	if t.IsZero() {
+		return nil, nil
+	}
+
+	return t.Format(NormalTimeFormat), nil
+}
+
+func (t *Time) Scan(value interface{}) error {
+	if value == nil {
+		return nil
+	}
+
+	*t = Time(value.(time.Time))
+	return nil
+}
+
+func (t Time) Format(layout string) string {
+	return t.T().Format(layout)
+}
+
+func (t Time) AppendFormat(b []byte, layout string) []byte {
+	return t.T().AppendFormat(b, layout)
+}
+
+// 当前时间
+func CurrentTime() Time {
+	return Time(Time(time.Now()).OfDate(1,time.January,1))
+}
+
+// 构造date.Time
+func NewTime(hour, min, sec, nsec int, loc *time.Location) Time {
+	return Time(time.Date(1, time.January, 1, hour, min, sec, nsec, loc))
+}
+
+// 转date.Datetime
+func (t Time) ToDatetime() Datetime {
+	return Datetime(t)
+}
+
+// 转date.Date
+func (t Time) ToDate() Date {
+	return Date(t)
+}
+
+// 转date.Time
+func AsTime(tm time.Time) Time {
+	return Time(Time(tm).OfDate(1,time.January,1))
+}
+
+// 转time.Time
+func (t Time) T() time.Time {
+	return time.Time(t)
+}
+
+// 是否晚于
+func (t Time) After(u Time) bool {
+	return t.T().After(u.T())
+}
+
+// 是否早于
+func (t Time) Before(u Time) bool {
+	return t.T().Before(u.T())
+}
+
+// 是否等于
+func (t Time) Equal(u Time) bool {
+	return t.T().Equal(u.T())
+}
+
+// 是否零值
+func (t Time) IsZero() bool {
+	return t.T().IsZero()
+}
+
+// 获取年、月、日
+func (t Time) Date() (year int, month time.Month, day int) {
+	return t.T().Date()
+}
+
+// 获取年
+func (t Time) Year() int {
+	return t.T().Year()
+}
+
+// 获取月
+func (t Time) Month() time.Month {
+	return t.T().Month()
+}
+
+// 获取日
+func (t Time) Day() int {
+	return t.T().Day()
+}
+
+// 获取星期几
+func (t Time) Weekday() time.Weekday {
+	return t.T().Weekday()
+}
+
+// 获取年、第几周
+func (t Time) ISOWeek() (year, week int) {
+	return t.T().ISOWeek()
+}
+
+// 获取时、分、秒
+func (t Time) Clock() (hour, min, sec int) {
+	return t.T().Clock()
+}
+
+// 获取小时
+func (t Time) Hour() int {
+	return t.T().Hour()
+}
+
+// 获取分钟
+func (t Time) Minute() int {
+	return t.T().Minute()
+}
+
+// 获取秒
+func (t Time) Second() int {
+	return t.T().Second()
+}
+
+// 获取毫秒
+func (t Time) Millisecond() int {
+	return int(time.Duration(t.T().Nanosecond()) / time.Millisecond)
+}
+
+// 获取微秒
+func (t Time) Microsecond() int {
+	return int(time.Duration(t.T().Nanosecond()) / time.Microsecond)
+}
+
+// 获取纳秒
+func (t Time) Nanosecond() int {
+	return t.T().Nanosecond()
+}
+
+// 获取是一年中第几天
+func (t Time) YearDay() int {
+	return t.T().YearDay()
+}
+
+// 获取该时间 - 参数时间 的时间差
+func (t Time) Sub(u Time) time.Duration {
+	return t.T().Sub(u.T())
+}
+
+// 加一个时间差
+func (t Time) Add(d time.Duration) Datetime {
+	return Datetime(t.T().Add(d))
+}
+
+// 加年、月、日
+func (t Time) AddDate(years int, months int, days int) Datetime {
+	return Datetime(t.T().AddDate(years, months, days))
+}
+
+// 加时、分、秒
+func (t Time) AddTime(hours int, minutes int, seconds int) Datetime {
+	d := time.Duration(hours) * time.Hour + time.Duration(minutes) * time.Minute + time.Duration(seconds) * time.Second
+	return t.Add(d)
+}
+
+// 加年
+func (t Time) AddYears(years int) Datetime {
+	return t.AddDate(years, 0, 0)
+}
+
+// 加月
+func (t Time) AddMonths(months int) Datetime {
+	return t.AddDate(0, months, 0)
+}
+
+// 加日
+func (t Time) AddDays(days int) Datetime {
+	return t.AddDate(0, 0, days)
+}
+
+// 加小时
+func (t Time) AddHours(hours int) Datetime {
+	d := time.Duration(hours) * time.Hour
+	return t.Add(d)
+}
+
+// 加分钟
+func (t Time) AddMinutes(minutes int) Datetime {
+	d := time.Duration(minutes) * time.Minute
+	return t.Add(d)
+}
+
+// 加秒
+func (t Time) AddSeconds(seconds int) Datetime {
+	d := time.Duration(seconds) * time.Second
+	return t.Add(d)
+}
+
+// 加纳秒
+func (t Time) AddNanoseconds(nanoseconds int) Datetime {
+	d := time.Duration(nanoseconds) * time.Nanosecond
+	return t.Add(d)
+}
+
+// 指定时间
+func (t Time) Of(year int, month time.Month, day int, hour int, minute int, second int, nanosecond int) Datetime {
+	return Datetime(time.Date(year, month, day, hour, minute, second, nanosecond, t.Location()))
+}
+
+// 指定年、月、日
+func (t Time) OfDate(year int, month time.Month, day int) Datetime {
+	hour, minute, second := t.Clock()
+	return t.Of(year, month, day, hour, minute, second, t.Nanosecond())
+}
+
+// 指定时、分、秒
+func (t Time) OfTime(hour int, minute int, second int) Time {
+	year, month, day := t.Date()
+	return Time(t.Of(year, month, day, hour, minute, second, t.Nanosecond()))
+}
+
+// 指定年
+func (t Time) OfYear(year int) Datetime {
+	return t.OfDate(year, t.Month(), t.Day())
+}
+
+// 指定月
+func (t Time) OfMonth(month time.Month) Datetime {
+	return t.OfDate(t.Year(), month, t.Day())
+}
+
+// 指定日
+func (t Time) OfDay(day int) Datetime {
+	return t.OfDate(t.Year(), t.Month(), day)
+}
+
+// 指定小时
+func (t Time) OfHour(hour int) Time {
+	return t.OfTime(hour, t.Minute(), t.Second())
+}
+
+// 指定分钟
+func (t Time) OfMinute(minute int) Time {
+	return t.OfTime(t.Hour(), minute, t.Second())
+}
+
+// 指定秒
+func (t Time) OfSecond(second int) Time {
+	return t.OfTime(t.Hour(), t.Minute(), second)
+}
+
+// 指定纳秒
+func (t Time) OfNanosecond(nanosecond int) Time {
+	year, month, day := t.Date()
+	hour, minute, second := t.Clock()
+	return Time(t.Of(year, month, day, hour, minute, second, nanosecond))
+}
+
+// 转UTC时间
+func (t Time) UTC() Datetime {
+	return Datetime(t.T().UTC())
+}
+
+// 转本地时间
+func (t Time) Local() Datetime {
+	return Datetime(t.T().Local())
+}
+
+// 转指定时区时间
+func (t Time) In(loc *time.Location) Datetime {
+	return Datetime(t.T().In(loc))
+}
+
+// 获取时区
+func (t Time) Location() *time.Location {
+	return t.T().Location()
+}
+
+// 获取时区
+func (t Time) Zone() (name string, offset int) {
+	return t.T().Zone()
+}
+
+// 获取UTC时间戳
+func (t Time) Unix() int64 {
+	return t.T().Unix()
+}
+
+// 获取UTC纳秒数
+func (t Time) UnixNano() int64 {
+	return t.T().Unix()
+}
+
+// 从目标时间开始到现在的时间差
+func SinceTime(t Time) time.Duration {
+	return CurrentTime().Sub(t)
+}
+
+// 现在到目标时间的时间差
+func UntilTime(t Time) time.Duration {
+	return t.Sub(CurrentTime())
+}

+ 44 - 0
utils/http_middleware/gin_core.go

@@ -0,0 +1,44 @@
+package http_middleware
+
+import (
+	"fmt"
+	"git.haoqitour.com/haoqi/go-common/utils/jws"
+	"github.com/gin-gonic/gin"
+	"regexp"
+	"strings"
+)
+
+var tsPostfixPattern = regexp.MustCompile(`[?&](t=\d+&?)`) // 处理防重放防缓存的时间戳参数的正则
+
+// 日志和错误处理需要记录日志中的请求信息处理
+func splitUri(hostPrefix string, ctxt *gin.Context) (string, map[string]interface{}) {
+	path := ctxt.Request.URL.Path
+	method := ctxt.Request.Method
+	raw := ctxt.Request.URL.RawQuery
+
+	fields := make(map[string]interface{})
+	uri := path
+	if len(strings.TrimSpace(hostPrefix)) > 0 && !getHostPrefixPattern(hostPrefix).MatchString(path) {
+		uri = strings.TrimSpace(hostPrefix) + "/" + strings.TrimLeft(path, "/")
+	}
+	query := string(raw)
+	if sub := tsPostfixPattern.FindStringSubmatch(query); nil != sub && len(sub) > 1 {
+		query = strings.Replace(query, sub[1], "", -1)
+	}
+	if raw != "" {
+		path = uri + "?" + raw
+	}
+	if token := jws.GetToken(ctxt); len(token) > 0 {
+		fields["token"] = token
+	}
+	fields["endpoint"] = fmt.Sprintf("%s %s", method, uri) // GET /service/test/test
+	fields["uri"] = uri                                    // /service/test/test
+	fields["query"] = query                                // name=test&sex=1
+	fields["method"] = method                              // GET
+	req := fmt.Sprintf("%s %s", method, path)              // GET /service/test/test?name=test&sex=1&t=32523535323
+	return req, fields
+}
+
+func getHostPrefixPattern(hostPrefix string) *regexp.Regexp {
+	return regexp.MustCompile("^" + strings.TrimSpace(hostPrefix)) // 处理子系统路径前缀的正则
+}

+ 21 - 0
utils/http_middleware/gin_cros.go

@@ -0,0 +1,21 @@
+package http_middleware
+
+import "github.com/gin-gonic/gin"
+
+// 允许跨域响应头中间件
+func AddCrossOriginHeaders() gin.HandlerFunc {
+	return func(ctxt *gin.Context) {
+		ctxt.Header("Access-Control-Allow-Credentials", "true")
+		var origin string
+		if ctxt.Request.Header.Get("Origin") != "" {
+			origin = ctxt.Request.Header.Get("Origin")
+		} else {
+			origin = "*"
+		}
+		ctxt.Header("Access-Control-Allow-Origin", origin)
+		ctxt.Header("Access-Control-Max-Age", "3600")
+		ctxt.Header("Access-Control-Allow-Methods", "POST,GET,OPTIONS,DELETE,PUT")
+		ctxt.Header("Access-Control-Allow-Headers", "Origin,x-requested-with,Content-Type,Accept,Authorization,Auth")
+		ctxt.Next()
+	}
+}

+ 53 - 0
utils/http_middleware/gin_favicon.go

@@ -0,0 +1,53 @@
+package http_middleware
+
+import (
+	"bytes"
+	"github.com/gin-gonic/gin"
+	"io/ioutil"
+	"net/http"
+	"os"
+	"path/filepath"
+)
+
+func Favicon(path string) gin.HandlerFunc {
+	path = filepath.FromSlash(path)
+	if len(path) > 0 && !os.IsPathSeparator(path[0]) {
+		wd, err := os.Getwd()
+		if err != nil {
+			panic(err)
+		}
+		path = filepath.Join(wd, path)
+	}
+
+	info, err := os.Stat(path)
+	if err != nil || info == nil || info.IsDir() {
+		panic("Invalid favicon path: " + path)
+	}
+
+	file, err := ioutil.ReadFile(path)
+	if err != nil {
+		panic(err)
+	}
+
+	reader := bytes.NewReader(file)
+
+	return func(ctxt *gin.Context) {
+		if ctxt.Request.RequestURI != "/favicon.ico" {
+			ctxt.Next()
+			return
+		}
+		if ctxt.Request.Method != "GET" && ctxt.Request.Method != "HEAD" {
+			status := http.StatusOK
+			if ctxt.Request.Method != "OPTIONS" {
+				status = http.StatusMethodNotAllowed
+			}
+			ctxt.Header("Allow", "GET,HEAD,OPTIONS")
+			ctxt.AbortWithStatus(status)
+			ctxt.Abort()
+			return
+		}
+		ctxt.Header("Content-Type", "image/x-icon")
+		http.ServeContent(ctxt.Writer, ctxt.Request, "favicon.ico", info.ModTime(), reader)
+		ctxt.Abort()
+	}
+}

+ 117 - 0
utils/http_middleware/gin_logger.go

@@ -0,0 +1,117 @@
+package http_middleware
+
+import (
+	"bytes"
+	"fmt"
+	"git.haoqitour.com/haoqi/go-common/utils/date"
+	"git.haoqitour.com/haoqi/go-common/utils/logger"
+	"github.com/gin-gonic/gin"
+	"io"
+	"io/ioutil"
+	"net/http"
+	"strings"
+)
+
+type bodyBuffer struct {
+	bytes.Buffer
+}
+
+func (b *bodyBuffer) String() string {
+	str := b.Buffer.String()
+	return strings.Replace(strings.Replace(str, "\n", "", -1), "\t", "", -1)
+}
+
+type reqBodyLogReader struct {
+	io.ReadCloser
+	buffer *bodyBuffer
+}
+
+func (r *reqBodyLogReader) Read(p []byte) (n int, err error) {
+	b, err := ioutil.ReadAll(r.ReadCloser)
+	if nil != err {
+		return 0, err
+	}
+	oldReader := r.ReadCloser
+	defer func(oldReader io.ReadCloser) {
+		_ = oldReader.Close()
+	}(oldReader)
+	_, _ = r.buffer.Write(b)
+	r.ReadCloser = ioutil.NopCloser(bytes.NewReader(b))
+	return r.ReadCloser.Read(p)
+}
+
+func (r *reqBodyLogReader) Close() error {
+	return r.ReadCloser.Close()
+}
+
+type respBodyLogWriter struct {
+	gin.ResponseWriter
+	buffer *bodyBuffer
+}
+
+func (w *respBodyLogWriter) Write(b []byte) (int, error) {
+	_, _ = w.buffer.Write(b)
+	return w.ResponseWriter.Write(b)
+}
+
+const HTTPRequestStartTime = "httpRequestStartTime"
+
+// 日志中间件
+func Logger(hostPrefix string, logger *logger.Logger, notLogged ...string) gin.HandlerFunc {
+	var skip map[string]struct{}
+	if length := len(notLogged); length > 0 {
+		skip = make(map[string]struct{}, length)
+
+		for _, path := range notLogged {
+			skip[path] = struct{}{}
+		}
+	}
+
+	return func(ctxt *gin.Context) {
+		start := date.Now()
+		ctxt.Set(HTTPRequestStartTime, start)
+		reqReader := &reqBodyLogReader{buffer: &bodyBuffer{Buffer: *bytes.NewBufferString("")}, ReadCloser: ctxt.Request.Body}
+		ctxt.Request.Body = reqReader
+		respWriter := &respBodyLogWriter{buffer: &bodyBuffer{Buffer: *bytes.NewBufferString("")}, ResponseWriter: ctxt.Writer}
+		ctxt.Writer = respWriter
+
+		ctxt.Next()
+
+		if ctxt.Writer.Status() < http.StatusBadRequest { // httpStatus大于等于400的不应在此记录,而应该panic抛给下面的Recovery方法处理
+			path := ctxt.Request.URL.Path
+			if _, ok := skip[path]; !ok {
+				end := date.Now()
+				latency := end.Sub(start)
+
+				httpStatus := ctxt.Writer.Status()
+				clientIP := ctxt.ClientIP()
+				req, fields := splitUri(hostPrefix, ctxt)
+				comment := ctxt.Errors.ByType(gin.ErrorTypePrivate).String()
+				logHttpRequest := true
+				if i, ok := ctxt.Get(IsNoLogHTTPRequest); ok {
+					if isNoLogHttpRequest, ok := i.(bool); ok && isNoLogHttpRequest {
+						logHttpRequest = false
+					}
+				}
+				if logHttpRequest {
+					logger = logger.WithField("requestBody", reqReader.buffer.String())
+				}
+				if i, ok := ctxt.Get(IsLogHTTPResponse); ok {
+					if isLogHttpResponse, ok := i.(bool); ok && isLogHttpResponse {
+						logger = logger.WithField("responseBody", respWriter.buffer.String())
+					}
+				}
+				logger.
+					WithCaller(7).
+					WithField("tag", "API").
+					WithField("lib", "gin").
+					WithField("httpStatus", httpStatus).
+					WithField("latency", fmt.Sprintf("%v", latency)).
+					WithField("clientIP", clientIP).
+					WithField("comment", comment).
+					WithFields(fields).
+					Info(req)
+			}
+		}
+	}
+}

+ 16 - 0
utils/http_middleware/gin_no_req_logger.go

@@ -0,0 +1,16 @@
+package http_middleware
+
+import (
+	"github.com/gin-gonic/gin"
+)
+
+const IsNoLogHTTPRequest = "isNoLogHttpRequest"
+
+// 记录http响应结果
+func NoReqLogger() gin.HandlerFunc {
+	return func(ctxt *gin.Context) {
+		ctxt.Set(IsNoLogHTTPRequest, true)
+
+		ctxt.Next()
+	}
+}

+ 18 - 0
utils/http_middleware/gin_options.go

@@ -0,0 +1,18 @@
+package http_middleware
+
+import (
+	"github.com/gin-gonic/gin"
+	"net/http"
+	"strings"
+)
+
+// HTTP OPTIONS、HEAD 方法直接响应成功中间件
+func HandleOptionsMethod() gin.HandlerFunc {
+	return func(ctxt *gin.Context) {
+		if strings.ToUpper(ctxt.Request.Method) == http.MethodOptions || strings.ToUpper(ctxt.Request.Method) == http.MethodHead {
+			ctxt.String(http.StatusOK, "")
+			//ctxt.Header("Content-Type","text/html; charset=utf-8")
+			ctxt.Abort()
+		}
+	}
+}

+ 108 - 0
utils/http_middleware/gin_recovery.go

@@ -0,0 +1,108 @@
+package http_middleware
+
+import (
+	"encoding/json"
+	"fmt"
+	"git.haoqitour.com/haoqi/go-common/utils"
+	"git.haoqitour.com/haoqi/go-common/utils/date"
+	"git.haoqitour.com/haoqi/go-common/utils/logger"
+	"github.com/gin-gonic/gin"
+	"net/http"
+	"strings"
+)
+
+// Recovery中间件(统一错误处理)
+func Recovery(hostPrefix string, logger *logger.Logger) gin.HandlerFunc {
+	return func(ctxt *gin.Context) {
+		defer func() {
+			if err := recover(); err != nil && logger != nil {
+				stack := string(utils.GetStack(4))
+				if i, ok := ctxt.Get(HTTPRequestStartTime); ok {
+					if start, ok := i.(date.Datetime); ok {
+						end := date.Now()
+						latency := end.Sub(start)
+						logger = logger.WithField("latency", fmt.Sprintf("%v", latency))
+					}
+				}
+				clientIP := ctxt.ClientIP()
+				comment := ctxt.Errors.ByType(gin.ErrorTypePrivate).String()
+				req, fields := splitUri(hostPrefix, ctxt)
+				logHttpRequest := true
+				if i, ok := ctxt.Get(IsNoLogHTTPRequest); ok {
+					if isNoLogHttpRequest, ok := i.(bool); ok && isNoLogHttpRequest {
+						logHttpRequest = false
+					}
+				}
+				if logHttpRequest {
+					if reader, ok := ctxt.Request.Body.(*reqBodyLogReader); ok {
+						logger = logger.WithField("requestBody", reader.buffer.String())
+					}
+				}
+				logger = logger.
+					WithField("clientIP", clientIP).
+					WithField("comment", comment).
+					WithFields(fields)
+
+				var res *utils.Res
+				if r, ok := err.(*utils.Res); ok {
+					res = r
+				} else if r, ok := err.(utils.Res); ok {
+					res = &r
+				}
+				if res != nil {
+					httpStatus := http.StatusBadRequest
+					var s1, s2 string
+					if nil != &res.Head {
+						if res.Head.HttpStatus > 0 {
+							httpStatus = res.Head.HttpStatus
+						}
+						s1 = res.Head.ErrMsg
+						s2 = res.Head.Detail
+						if strings.Contains(s2, s1) {
+							s1 = ""
+						}
+						if strings.Contains(s1, s2) {
+							s2 = ""
+						}
+					} else {
+						res.Head = utils.ResHead{ErrCode: -1, ErrMsg: "未定义"}
+					}
+					if i, ok := ctxt.Get(IsLogHTTPResponse); ok {
+						if isLogHttpResponse, ok := i.(bool); ok && isLogHttpResponse {
+							respB, _ := json.Marshal(res)
+							logger = logger.WithField("responseBody", string(respB))
+						}
+					}
+					logger.
+						WithCaller(4).
+						WithField("tag", "Custom warn").
+						WithField("httpStatus", httpStatus).
+						WithField("errCode", res.Head.ErrCode).
+						WithField("errMsg", strings.TrimSpace(fmt.Sprintf("%d %s %s", res.Head.ErrCode, s1, s2))).
+						Warn(req)
+					ctxt.JSON(httpStatus, res)
+					ctxt.Abort()
+				} else {
+					res := utils.E(9999, "未定义", nil)
+					if i, ok := ctxt.Get(IsLogHTTPResponse); ok {
+						if isLogHttpResponse, ok := i.(bool); ok && isLogHttpResponse {
+							respB, _ := json.Marshal(res)
+							logger = logger.WithField("responseBody", string(respB))
+						}
+					}
+					logger.
+						WithCaller(5).
+						WithField("tag", "Catch Exception").
+						WithField("httpStatus", http.StatusBadRequest).
+						WithField("errCode", 9999).
+						WithField("errMsg", fmt.Sprintf("9999 %s", err)).
+						Error(req)
+					println(stack)
+					ctxt.JSON(http.StatusBadRequest, res)
+					ctxt.Abort()
+				}
+			}
+		}()
+		ctxt.Next()
+	}
+}

+ 16 - 0
utils/http_middleware/gin_resp_logger.go

@@ -0,0 +1,16 @@
+package http_middleware
+
+import (
+	"github.com/gin-gonic/gin"
+)
+
+const IsLogHTTPResponse = "isLogHttpResponse"
+
+// 记录http响应结果
+func RespLogger() gin.HandlerFunc {
+	return func(ctxt *gin.Context) {
+		ctxt.Set(IsLogHTTPResponse, true)
+
+		ctxt.Next()
+	}
+}

+ 23 - 0
utils/http_middleware/prefix_cut.go

@@ -0,0 +1,23 @@
+package http_middleware
+
+import (
+	"net/http"
+	"strings"
+)
+
+// 请求前缀剪裁中间件(底层http.Handler接口织入,非Gin中间件)
+type PrefixCut struct {
+	Handler http.Handler
+	HostPrefix string
+}
+
+func (cut *PrefixCut) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
+	if len(strings.TrimSpace(cut.HostPrefix)) > 0 {
+		hostPrefixPattern := getHostPrefixPattern(cut.HostPrefix)
+		if hostPrefixPattern.MatchString(request.URL.Path) {
+			request.URL.Path = hostPrefixPattern.ReplaceAllString(request.URL.Path, "")
+			request.RequestURI = hostPrefixPattern.ReplaceAllString(request.RequestURI, "")
+		}
+	}
+	cut.Handler.ServeHTTP(writer, request)
+}

+ 310 - 0
utils/http_utils.go

@@ -0,0 +1,310 @@
+package utils
+
+import (
+	"compress/flate"
+	"compress/gzip"
+	"compress/zlib"
+	"context"
+	"fmt"
+	"golang.org/x/net/proxy"
+	"io"
+	"io/ioutil"
+	"net"
+	"net/http"
+	"net/url"
+	"strings"
+	"time"
+)
+
+// Error Code接口
+type IErrorCode interface {
+	// 转换为int
+	ToInt() int
+	// 方便与int比较
+	Equals(errCode int) bool
+	// 获取错误信息
+	ErrMsg(args ...interface{}) string
+}
+
+type Res struct {
+	Head ResHead     `json:"head"`
+	Data interface{} `json:"data,omitempty"`
+}
+
+type ResHead struct {
+	ErrCode    int    `json:"errcode"`
+	ErrMsg     string `json:"errmsg,omitempty"`
+	Detail     string `json:"-"`
+	HttpStatus int    `json:"-"`
+}
+
+func E2(errDetail interface{}, errCode IErrorCode, args ...interface{}) *Res {
+	errMsg := errCode.ErrMsg(args...)
+	return E(errCode.ToInt(), errMsg, errDetail)
+}
+
+func E(status int, errMsg string, errDetail interface{}) *Res {
+	var msg, detail string
+	if nil != errDetail {
+		detail = strings.TrimSpace(fmt.Sprintf("%s", errDetail))
+	}
+	msg = strings.TrimSpace(errMsg)
+	return &Res{
+		Head: ResHead{
+			ErrCode:    status,
+			ErrMsg:     msg,
+			Detail:     detail,
+			HttpStatus: http.StatusInternalServerError,
+		},
+	}
+}
+
+func R(data interface{}) *Res {
+	return &Res{
+		Head: ResHead{
+			ErrCode:    0,
+			ErrMsg:     "",
+			HttpStatus: http.StatusOK,
+		},
+		Data: data,
+	}
+}
+
+// http请求相关默认配置
+const (
+	DefaultHttpDialTimeout         = 20 * time.Second
+	DefaultHttpKeepAlive           = 120 * time.Second
+	DefaultHttpMaxIdleConns        = 1000
+	DefaultHttpMaxIdleConnsPerHost = 1000
+	DefaultHttpIdleConnTimeout     = 90 * time.Second
+	DefaultHttpTimeout             = 30 * time.Second
+)
+
+// http请求配置
+type RequestPromise struct {
+	headers     http.Header
+	encoding    Charset
+	timeout     time.Duration
+	proxy       func(*http.Request) (*url.URL, error)
+	dialContext func(ctx context.Context, network, addr string) (net.Conn, error)
+	client      *http.Client
+}
+
+// 返回一个http请求配置对象,默认带上压缩头
+func NewRequest() *RequestPromise {
+	return (&RequestPromise{}).
+		SetHeader("Accept-Encoding", "gzip, deflate, zlib")
+}
+
+// 返回一个http请求配置对象,默认带上压缩头和Content-Type = application/json; charset=utf-8
+func JSONRequest() *RequestPromise {
+	return (&RequestPromise{}).
+		SetHeader("Accept-Encoding", "gzip, deflate, zlib").
+		SetHeader("Content-Type", "application/json; charset=utf-8")
+}
+
+// 返回一个http请求配置对象,默认带上压缩头和Content-Type = application/xml; charset=utf-8
+func XMLRequest() *RequestPromise {
+	return (&RequestPromise{}).
+		SetHeader("Accept-Encoding", "gzip, deflate, zlib").
+		SetHeader("Content-Type", "application/xml; charset=utf-8")
+}
+
+// 返回一个采用了连接池和Keepalive配置的http client,可以配合RequestPromise的SetClient函数使用
+// 默认不使用它,而是每次请求新建连接
+func NewPoolingHttpClient() *http.Client {
+	return &http.Client{
+		Transport: &http.Transport{
+			DialContext: (&net.Dialer{
+				Timeout:   DefaultHttpDialTimeout,
+				KeepAlive: DefaultHttpKeepAlive,
+			}).DialContext,
+			MaxIdleConns:        DefaultHttpMaxIdleConns,
+			MaxIdleConnsPerHost: DefaultHttpMaxIdleConnsPerHost,
+			IdleConnTimeout:     DefaultHttpIdleConnTimeout,
+		},
+		Timeout: DefaultHttpTimeout, // 此处设置小于等于零的值,意为不超时
+	}
+}
+
+// 设置http header
+func (r *RequestPromise) SetHeader(key string, value string) *RequestPromise {
+	if len(strings.TrimSpace(key)) == 0 {
+		return r
+	}
+	key = strings.TrimSpace(key)
+	if nil == r.headers {
+		r.headers = make(http.Header)
+	}
+	r.headers.Set(key, value)
+	return r
+}
+
+// 设置http响应的编码,默认utf8
+func (r *RequestPromise) SetEncoding(encoding Charset) *RequestPromise {
+	if encoding == UTF8 {
+		return r
+	}
+	r.encoding = encoding
+	return r
+}
+
+// 设置超时时间,从连接到接收到响应的总时间
+// 如果此处不设置则采用http client中设置的超时时间,默认http client超时时间30秒
+// 如果此处设置不等于零的值,则覆盖http client中设置的超时时间
+// 如果此处设置小于零的值,意为不超时
+func (r *RequestPromise) SetTimeout(timeout time.Duration) *RequestPromise {
+	if timeout == 0 {
+		return r
+	}
+	r.timeout = timeout
+	return r
+}
+
+// 设置http或https代理,默认无代理
+func (r *RequestPromise) SetHttpProxy(proxyUri string) *RequestPromise {
+	if len(strings.TrimSpace(proxyUri)) == 0 {
+		return r
+	}
+	proxyUri = strings.TrimSpace(proxyUri)
+	uri, err := (&url.URL{}).Parse(proxyUri)
+	if nil != err {
+		return r
+	}
+	r.proxy = http.ProxyURL(uri)
+	return r
+}
+
+// 设置socket5代理,默认无代理
+func (r *RequestPromise) SetSocket5Proxy(proxyUri string) *RequestPromise {
+	if len(strings.TrimSpace(proxyUri)) == 0 {
+		return r
+	}
+	proxyUri = strings.TrimSpace(proxyUri)
+	dialer, err := proxy.SOCKS5("tcp", proxyUri, nil, proxy.Direct)
+	if nil != err {
+		return r
+	}
+	r.dialContext = func(_ context.Context, network string, address string) (net.Conn, error) {
+		return dialer.Dial(network, address)
+	}
+	return r
+}
+
+// 设置事先实例化好的http client,默认每次请求会新建一个http client
+func (r *RequestPromise) SetClient(client *http.Client) *RequestPromise {
+	if nil == client {
+		return r
+	}
+	r.client = client
+	return r
+}
+
+// 发起请求并返回响应内容
+// FORM方式提交数据请设置Content-Type=application/x-www-form-urlencoded请求头,且io.Reader传url.Values.Encode得到的字符串的reader
+func (r *RequestPromise) Call(httpMethod string, targetUri string, data io.Reader) ([]byte, error) {
+	targetUri = strings.TrimSpace(targetUri)
+	if len(targetUri) == 0 {
+		return nil, nil
+	}
+
+	// http request handle
+	if len(strings.TrimSpace(httpMethod)) == 0 {
+		httpMethod = http.MethodGet
+	} else {
+		httpMethod = strings.ToUpper(strings.TrimSpace(httpMethod))
+	}
+	req, err := http.NewRequest(httpMethod, targetUri, data)
+	if err != nil {
+		return nil, err
+	}
+	if nil != r.headers {
+		req.Header = r.headers
+	}
+
+	r.initClient()
+
+	// send http request & get http response
+	resp, err := r.client.Do(req)
+	if err != nil {
+		return nil, err
+	}
+
+	return r.readResponseBody(resp)
+}
+
+func (r *RequestPromise) initClient() {
+	// http client handle
+	if nil == r.client { // create new http client instance
+		r.client = &http.Client{Timeout: DefaultHttpTimeout} // default timeout
+	}
+	if r.timeout != 0 {
+		r.client.Timeout = r.timeout
+	}
+	if r.client.Timeout < 0 {
+		r.client.Timeout = 0
+	}
+	if nil != r.proxy || nil != r.dialContext {
+		if nil == r.client.Transport {
+			r.client.Transport = &http.Transport{}
+		}
+		transport := (r.client.Transport).(*http.Transport)
+		if nil != r.proxy {
+			transport.Proxy = r.proxy
+		}
+		if nil != r.dialContext {
+			transport.DialContext = r.dialContext
+		}
+	}
+}
+
+func (r *RequestPromise) readResponseBody(resp *http.Response) ([]byte, error) {
+	defer func(body io.ReadCloser) {
+		_ = body.Close()
+	}(resp.Body)
+
+	var reader io.ReadCloser
+	switch resp.Header.Get("Content-Encoding") {
+	case "gzip":
+		reader, _ = gzip.NewReader(resp.Body)
+		defer func(reader io.Closer) {
+			_ = reader.Close()
+		}(reader)
+	case "deflate":
+		reader = flate.NewReader(resp.Body)
+		defer func(reader io.Closer) {
+			_ = reader.Close()
+		}(reader)
+	case "zlib":
+		reader, _ = zlib.NewReader(resp.Body)
+		defer func(reader io.Closer) {
+			_ = reader.Close()
+		}(reader)
+	default:
+		reader = resp.Body
+	}
+
+	body, err := ioutil.ReadAll(reader)
+	if err != nil {
+		return nil, err
+	}
+	if r.encoding != "" {
+		body = ConvertToEncodingBytes(body, r.encoding)
+	}
+	return body, nil
+}
+
+// 获取客户端IP地址
+func GetRemoteIP(req *http.Request) string {
+	remoteAddr := req.RemoteAddr
+	if ip := req.Header.Get("Remote_addr"); ip != "" {
+		remoteAddr = ip
+	} else {
+		remoteAddr, _, _ = net.SplitHostPort(remoteAddr)
+	}
+
+	if remoteAddr == "::1" {
+		remoteAddr = "127.0.0.1"
+	}
+	return remoteAddr
+}

+ 54 - 0
utils/jws/gin_jwt.go

@@ -0,0 +1,54 @@
+package jws
+
+import (
+	"git.haoqitour.com/haoqi/go-common/utils"
+	"github.com/gin-gonic/gin"
+	"net/http"
+)
+
+// JWT中间件,检查token,校验不通过报错
+func NeedLogin() gin.HandlerFunc {
+	return func(ctxt *gin.Context) {
+		token := GetToken(ctxt)
+		if token == "" {
+			res := utils.E(http.StatusUnauthorized, "无权限访问", nil)
+			res.Head.HttpStatus = http.StatusUnauthorized
+			panic(res)
+			return
+		}
+
+		j := NewJWT()
+		// parseToken 解析token包含的信息
+		claims, err := j.ParseToken(token)
+		if err != nil {
+			res := utils.E(http.StatusUnauthorized, err.Error(), nil)
+			res.Head.HttpStatus = http.StatusUnauthorized
+			panic(res)
+			return
+		}
+		claims.Token = token
+		// 继续交由下一个路由处理,并将解析出的信息传递下去
+		ctxt.Set(Claims, claims)
+		ctxt.Next()
+	}
+}
+
+// JWT中间件,仅通过token获取载荷信息,校验不通过也不报错
+func GetClaims() gin.HandlerFunc {
+	return func(ctxt *gin.Context) {
+		token := GetToken(ctxt)
+		if token != "" {
+			j := NewJWT()
+			// parseToken 解析token包含的信息
+			claims, err := j.ParseToken(token)
+			if err == nil {
+				claims.Token = token
+				// 继续交由下一个路由处理,并将解析出的信息传递下去
+				ctxt.Set(Claims, claims)
+			} else {
+				claims = &TokenClaims{Token: token}
+			}
+		}
+		ctxt.Next()
+	}
+}

+ 118 - 0
utils/jws/jwt.go

@@ -0,0 +1,118 @@
+package jws
+
+import (
+	"errors"
+	"github.com/dgrijalva/jwt-go"
+	"github.com/gin-gonic/gin"
+	"github.com/spf13/viper"
+	"strings"
+	"time"
+)
+
+// JWT 签名结构
+type JWT struct {
+	SigningKey []byte
+}
+
+// 一些常量
+var (
+	TokenExpired     = errors.New("授权已过期")
+	TokenNotValidYet = errors.New("授权未生效")
+	TokenMalformed   = errors.New("无权限访问")
+	TokenInvalid     = errors.New("无权限访问")
+	TokenHeaderName  = "Authorization"
+	Claims           = "claims"
+	DefaultSignKey   = "defaultSignKey"
+	TokenTimeout     = 2592000 // 60 * 60 * 24 * 30
+)
+
+// 载荷,可以加一些自己需要的信息
+type TokenClaims struct {
+	ID    int64  `json:"id"`
+	Token string `json:"-"`
+	jwt.StandardClaims
+}
+
+// 新建一个jwt实例
+func NewJWT(args ...string) *JWT {
+	var signKey string
+	if key := viper.GetString("jws.sign_key"); len(strings.TrimSpace(key)) > 0 {
+		signKey = strings.TrimSpace(key)
+	} else if len(args) > 0 {
+		signKey = strings.TrimSpace(args[0])
+	} else {
+		signKey = DefaultSignKey
+	}
+	return &JWT{SigningKey: []byte(signKey)}
+}
+
+func GetToken(ctxt *gin.Context) string {
+	if nil == ctxt || nil == ctxt.Request ||
+		nil == ctxt.Request.Header || len(ctxt.Request.Header) == 0 {
+		return ""
+	}
+	token := ctxt.Request.Header.Get(TokenHeaderName)
+	if token == "" {
+		token = ctxt.Request.Header.Get(strings.ToLower(TokenHeaderName))
+	}
+	return strings.TrimSpace(token)
+}
+
+// CreateToken 生成一个token
+func (j *JWT) CreateToken(claims TokenClaims) (string, error) {
+	jwt.TimeFunc = time.Now
+	claims.StandardClaims.ExpiresAt = time.Now().Add(time.Duration(TokenTimeout) * time.Second).Unix()
+	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
+	token.Header["exp"] = claims.StandardClaims.ExpiresAt
+	return token.SignedString(j.SigningKey)
+}
+
+// 解析Tokne
+func (j *JWT) ParseToken(tokenString string) (*TokenClaims, error) {
+	token, err := jwt.ParseWithClaims(tokenString, &TokenClaims{}, func(token *jwt.Token) (interface{}, error) {
+		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
+			return nil, TokenInvalid
+		}
+
+		return j.SigningKey, nil
+	})
+	if err != nil {
+		if ve, ok := err.(*jwt.ValidationError); ok {
+			if ve.Errors&jwt.ValidationErrorMalformed != 0 {
+				return nil, TokenMalformed
+			} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
+				// Token is expired
+				return nil, TokenExpired
+			} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
+				return nil, TokenNotValidYet
+			} else {
+				return nil, TokenInvalid
+			}
+		}
+	}
+	if claims, ok := token.Claims.(*TokenClaims); ok && token.Valid {
+		return claims, nil
+	}
+	return nil, TokenInvalid
+}
+
+// 更新token
+func (j *JWT) RefreshToken(tokenString string) (string, error) {
+	jwt.TimeFunc = func() time.Time {
+		return time.Unix(0, 0)
+	}
+	token, err := jwt.ParseWithClaims(tokenString, &TokenClaims{}, func(token *jwt.Token) (interface{}, error) {
+		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
+			return nil, TokenInvalid
+		}
+
+		return j.SigningKey, nil
+	})
+	if err != nil {
+		return "", err
+	}
+	if claims, ok := token.Claims.(*TokenClaims); ok && token.Valid {
+		return j.CreateToken(*claims)
+	}
+	return "", TokenInvalid
+}

+ 194 - 0
utils/keygen.go

@@ -0,0 +1,194 @@
+package utils
+
+import (
+	"errors"
+	"github.com/spf13/viper"
+	"net"
+	"sync"
+	"time"
+)
+
+// 获取新的程序生成的编号
+func NextId() Long {
+	id, e := SingletonSnowflakeKeyGen().NextId()
+	if nil != e {
+		println("get next id failed", e)
+		return 0
+	}
+	return Long(id)
+}
+
+/*------------------------------------------------------Singleton----------------------------------------------------*/
+
+var snowflakeKeyGen *Snowflake
+var once sync.Once
+
+func SingletonSnowflakeKeyGen() *Snowflake {
+	once.Do(func() {
+		snowflakeKeyGen = NewSnowflake(SnowflakeSettings{})
+	})
+	return snowflakeKeyGen
+}
+
+/*----------------------------------------------------SnowflakeKeyGen------------------------------------------------*/
+
+const (
+	SnowflakeTimeUnit  = 1e7 // 时间单位,一纳秒的多少倍,1e6 = 一毫秒,1e7 = 百分之一秒,1e8 = 十分之一秒
+	BitLenSequence     = 8   // 序列号的个数最多256个(0-255),即每单位时间并发数,如时间单位是1e7,则单实例qps = 25600
+	BitLenDataCenterId = 3   // 数据中心个数最多8个(0-7),即同一个环境(生产、预发布、测试等)的数据中心(假设一个机房相同数据域的应用服务器集群只有一个,则数据中心数等于机房数)最多有8个
+	BitLenMachineId    = 16  // 同一个数据中心下最多65536个应用实例(0-65535),默认是根据实例ip后两段算实例id(k8s环境动态创建Pod,也建议用此方式),所以需要预留255 * 255这么多
+	BitLenTime         = 36  // 时间戳之差最大 = 2的36次方 * 时间单位 / 1e9 秒,目前的设计最多可以用21.79年就需要更新开始时间(随之还需要归档旧数据和更新次新数据id)
+	// 总共63位,不超过bit64
+)
+
+type SnowflakeSettings struct {
+	StartTime      time.Time
+	DataCenterId   func() (uint16, error)
+	MachineId      func() (uint16, error)
+	CheckMachineId func(uint16) bool
+}
+
+type Snowflake struct {
+	mutex        *sync.Mutex
+	startTime    int64
+	elapsedTime  int64
+	sequence     uint16
+	dataCenterId uint16
+	machineId    uint16
+}
+
+func NewSnowflake(st SnowflakeSettings) *Snowflake {
+	sf := new(Snowflake)
+	sf.mutex = new(sync.Mutex)
+	sf.sequence = uint16(1<<BitLenSequence - 1)
+
+	if st.StartTime.After(time.Now()) {
+		return nil
+	}
+	if st.StartTime.IsZero() {
+		sf.startTime = toSnowflakeTime(time.Date(2018, 9, 26, 0, 0, 0, 0, time.UTC)) //没有配置默认使用此时间
+	} else {
+		sf.startTime = toSnowflakeTime(st.StartTime)
+	}
+
+	var err error
+	if st.MachineId == nil {
+		sf.machineId, err = GetPrivateIPv4Id() // 没有配置会读机器内网ip后两段,然后计算出一个值
+	} else {
+		sf.machineId, err = st.MachineId()
+	}
+	if nil != err {
+		err = nil
+		sf.machineId = uint16(0)
+	}
+	if st.DataCenterId == nil {
+		if id := viper.GetInt("data_center_id"); id > 0 { // 没有配置会尝试从配置文件读取数据中心id
+			sf.dataCenterId = uint16(id)
+		} else { // 如果配置文件也没有,默认数据中心id为0
+			sf.dataCenterId = uint16(0)
+		}
+	} else {
+		sf.dataCenterId, err = st.DataCenterId()
+		if nil != err {
+			sf.dataCenterId = uint16(0)
+		}
+	}
+	if st.CheckMachineId != nil && !st.CheckMachineId(sf.machineId) {
+		return nil
+	}
+
+	return sf
+}
+
+func (sf *Snowflake) NextId() (uint64, error) {
+	const maskSequence = uint16(1<<BitLenSequence - 1)
+
+	sf.mutex.Lock()
+	defer sf.mutex.Unlock()
+
+	current := currentElapsedTime(sf.startTime)
+	if sf.elapsedTime < current {
+		sf.elapsedTime = current
+		sf.sequence = 0
+	} else { // sf.elapsedTime >= current
+		sf.sequence = (sf.sequence + 1) & maskSequence
+		if sf.sequence == 0 {
+			sf.elapsedTime++
+			overtime := sf.elapsedTime - current
+			time.Sleep(sleepTime(overtime))
+		}
+	}
+
+	return sf.toId()
+}
+
+func toSnowflakeTime(t time.Time) int64 {
+	return t.UTC().UnixNano() / SnowflakeTimeUnit
+}
+
+func currentElapsedTime(startTime int64) int64 {
+	return toSnowflakeTime(time.Now()) - startTime
+}
+
+func sleepTime(overtime int64) time.Duration {
+	return time.Duration(overtime)*10*time.Millisecond -
+		time.Duration(time.Now().UTC().UnixNano()%SnowflakeTimeUnit)*time.Nanosecond
+}
+
+func (sf *Snowflake) toId() (uint64, error) {
+	if sf.elapsedTime >= 1<<BitLenTime {
+		return 0, errors.New("over the time limit")
+	}
+
+	return uint64(sf.elapsedTime)<<(BitLenSequence+BitLenDataCenterId+BitLenMachineId) |
+		uint64(sf.sequence)<<(BitLenDataCenterId+BitLenMachineId) |
+		uint64(sf.dataCenterId)<<BitLenMachineId |
+		uint64(sf.machineId), nil
+}
+
+func privateIPv4() (net.IP, error) {
+	as, err := net.InterfaceAddrs()
+	if err != nil {
+		return nil, err
+	}
+
+	for _, a := range as {
+		ipnet, ok := a.(*net.IPNet)
+		if !ok || ipnet.IP.IsLoopback() {
+			continue
+		}
+
+		ip := ipnet.IP.To4()
+		if isPrivateIPv4(ip) {
+			return ip, nil
+		}
+	}
+	return nil, errors.New("no private ip address")
+}
+
+func isPrivateIPv4(ip net.IP) bool {
+	return ip != nil &&
+		(ip[0] == 10 || ip[0] == 172 && (ip[1] >= 16 && ip[1] < 32) || ip[0] == 192 && ip[1] == 168)
+}
+
+func GetPrivateIPv4Id() (uint16, error) {
+	ip, err := privateIPv4()
+	if err != nil {
+		return 0, err
+	}
+
+	return uint16(ip[2])<<8 + uint16(ip[3]), nil
+}
+
+func Decompose(id uint64) map[string]uint64 {
+	const maskDataCenterId = uint64((1<<BitLenDataCenterId - 1) << BitLenMachineId)
+	const maskMachineId = uint64(1<<BitLenMachineId - 1)
+
+	dataCenterId := id & maskDataCenterId >> BitLenMachineId
+	machineId := id & maskMachineId
+	return map[string]uint64{
+		"id":           id,
+		"dataCenterId": dataCenterId,
+		"machineId":    machineId,
+	}
+}

+ 308 - 0
utils/logger/logger.go

@@ -0,0 +1,308 @@
+package logger
+
+import (
+	"database/sql/driver"
+	"fmt"
+	"github.com/sirupsen/logrus"
+	"github.com/spf13/viper"
+	"gopkg.in/gemnasium/logrus-graylog-hook.v2"
+	"os"
+	"reflect"
+	"regexp"
+	"runtime"
+	"strings"
+	"time"
+	"unicode"
+)
+
+var MaxInt64 = ^int64(0)
+
+type Logger struct {
+	logrus.Entry
+	Level logrus.Level
+}
+
+func New() *Logger {
+	timeFormat := "2006/01/02 15:04:05.000 -0700"
+	if tmFormat, ok := viper.Get("logger.time_format").(string); ok && len(tmFormat) > 0 {
+		timeFormat = strings.TrimSpace(tmFormat)
+	}
+	level := logrus.InfoLevel
+	if lvStr, ok := viper.Get("logger.level").(string); ok {
+		lvStr = strings.TrimSpace(strings.ToLower(lvStr))
+		if lvStr == "warn" {
+			level = logrus.WarnLevel
+		} else if lvStr == "debug" {
+			level = logrus.DebugLevel
+		} else if lvStr == "error" {
+			level = logrus.ErrorLevel
+		} else if lvStr == "fatal" {
+			level = logrus.FatalLevel
+		} else if lvStr == "panic" {
+			level = logrus.PanicLevel
+		}
+	}
+	log := &logrus.Logger{
+		Out:       os.Stderr,
+		Formatter: &logrus.TextFormatter{TimestampFormat: timeFormat},
+		Hooks:     make(logrus.LevelHooks),
+		Level:     level,
+	}
+	if grayAddr, ok := viper.Get("logger.graylog.addr").(string); ok && len(grayAddr) > 0 {
+		grayHook := graylog.NewGraylogHook(grayAddr, nil)
+		log.AddHook(grayHook)
+	}
+	entry := logrus.NewEntry(log)
+	extra := viper.GetStringMap("logger.extra")
+	if nil != extra && len(extra) > 0 {
+		entry = entry.WithFields(extra)
+	}
+	return &Logger{Entry: *entry, Level: level}
+}
+
+func (logger *Logger) Print(args ...interface{}) {
+	if args == nil || len(args) == 0 {
+		return
+	}
+	if tp, ok := args[0].(string); ok {
+		tp = strings.ToLower(strings.TrimSpace(tp))
+		if "sql" == tp && len(args) == 6 {
+			logger.printSql(args...)
+		} else {
+			logger.WithCaller(2).Entry.Print(args...)
+		}
+	} else {
+		logger.WithCaller(2).Entry.Print(args...)
+	}
+}
+
+func (logger *Logger) WithField(key string, value interface{}) *Logger {
+	return &Logger{Entry: *logger.Entry.WithField(key, value)}
+}
+
+func (logger *Logger) WithFields(fields map[string]interface{}) *Logger {
+ 	return &Logger{Entry: *logger.Entry.WithFields(fields)}
+}
+
+func (logger *Logger) WithError(err error) *Logger {
+	return &Logger{Entry: *logger.Entry.WithError(err)}
+}
+
+func (logger *Logger) WithCaller(skip int) *Logger {
+	if _, ok := logger.Data["codeline"]; ok {
+		return logger
+	}
+	for i := 0; i < 100; i++ {
+		if _, file, line, ok := runtime.Caller(i); ok {
+			if strings.Contains(file, "git.haoqitour.com") &&
+				!strings.Contains(file, "git.haoqitour.com/haoqi/go-common/utils/logger") {
+				return logger.
+					WithField("codeline", fmt.Sprintf("%s:%d", file, line))
+				//WithField("func", runtime.FuncForPC(pc).Name())
+			}
+		}
+	}
+	if _, file, line, ok := runtime.Caller(skip); ok {
+		return logger.
+			WithField("codeline", fmt.Sprintf("%s:%d", file, line))
+			//WithField("func", runtime.FuncForPC(pc).Name())
+	}
+	return logger
+}
+
+func (logger *Logger) Debugf(format string, args ...interface{}) {
+	logger.WithCaller(2).Entry.Debugf(format, args...)
+}
+
+func (logger *Logger) Infof(format string, args ...interface{}) {
+	logger.WithCaller(2).Entry.Infof(format, args...)
+}
+
+func (logger *Logger) Printf(format string, args ...interface{}) {
+	logger.WithCaller(2).Entry.Printf(format, args...)
+}
+
+func (logger *Logger) Warnf(format string, args ...interface{}) {
+	logger.WithCaller(2).Entry.Warnf(format, args...)
+}
+
+func (logger *Logger) Warningf(format string, args ...interface{}) {
+	logger.WithCaller(2).Entry.Warningf(format, args...)
+}
+
+func (logger *Logger) Errorf(format string, args ...interface{}) {
+	logger.WithCaller(2).Entry.Errorf(format, args...)
+}
+
+func (logger *Logger) Fatalf(format string, args ...interface{}) {
+	logger.WithCaller(2).Entry.Fatalf(format, args...)
+}
+
+func (logger *Logger) Panicf(format string, args ...interface{}) {
+	logger.WithCaller(2).Entry.Panicf(format, args...)
+}
+
+func (logger *Logger) Debug(args ...interface{}) {
+	logger.WithCaller(2).Entry.Debug(args...)
+}
+
+func (logger *Logger) Info(args ...interface{}) {
+	logger.WithCaller(2).Entry.Info(args...)
+}
+
+func (logger *Logger) Warn(args ...interface{}) {
+	logger.WithCaller(2).Entry.Warn(args...)
+}
+
+func (logger *Logger) Warning(args ...interface{}) {
+	logger.WithCaller(2).Entry.Warning(args...)
+}
+
+func (logger *Logger) Error(args ...interface{}) {
+	logger.WithCaller(2).Entry.Error(args...)
+}
+
+func (logger *Logger) Fatal(args ...interface{}) {
+	logger.WithCaller(2).Entry.Fatal(args...)
+}
+
+func (logger *Logger) Panic(args ...interface{}) {
+	logger.WithCaller(2).Entry.Panic(args...)
+}
+
+func (logger *Logger) Debugln(args ...interface{}) {
+	logger.WithCaller(2).Entry.Debugln(args...)
+}
+
+func (logger *Logger) Infoln(args ...interface{}) {
+	logger.WithCaller(2).Entry.Infoln(args...)
+}
+
+func (logger *Logger) Println(args ...interface{}) {
+	logger.WithCaller(2).Entry.Println(args...)
+}
+
+func (logger *Logger) Warnln(args ...interface{}) {
+	logger.WithCaller(2).Entry.Warnln(args...)
+}
+
+func (logger *Logger) Warningln(args ...interface{}) {
+	logger.WithCaller(2).Entry.Warningln(args...)
+}
+
+func (logger *Logger) Errorln(args ...interface{}) {
+	logger.WithCaller(2).Entry.Errorln(args...)
+}
+
+func (logger *Logger) Fatalln(args ...interface{}) {
+	logger.WithCaller(2).Entry.Fatalln(args...)
+}
+
+func (logger *Logger) Panicln(args ...interface{}) {
+	logger.WithCaller(2).Entry.Panicln(args...)
+}
+
+var (
+	sqlRegexp                = regexp.MustCompile(`\?`)
+	numericPlaceHolderRegexp = regexp.MustCompile(`\$\d+`)
+)
+
+func (logger *Logger) printSql(args ...interface{}) {
+	length := len(args)
+	var (
+		codeLine, sql string
+		params        []interface{}
+		latency       time.Duration
+		rows          int64
+		ok            bool
+	)
+	if length > 1 {
+		codeLine, _ = args[1].(string)
+	}
+	if length > 2 {
+		latency, _ = args[2].(time.Duration)
+	}
+	if length > 3 {
+		sql, ok = args[3].(string)
+		if ok {
+			sql = strings.TrimSpace(strings.Replace(strings.Replace(strings.Replace(sql, "\r\n", " ", -1), "\n", " ", -1), "\t", " ", -1))
+		}
+	}
+	if length > 4 {
+		params, _ = args[4].([]interface{})
+	}
+	if length > 5 {
+		rows, _ = args[5].(int64)
+	}
+	lg := logger.
+		WithField("tag", "SQL").
+		WithField("sql", logger.getSql(sql, params))
+	if len(codeLine) > 0 {
+		lg = lg.WithField("codeline", strings.TrimSpace(codeLine))
+	} else {
+		lg = lg.WithCaller(9)
+	}
+	if latency > 0 {
+		lg = lg.WithField("latency", fmt.Sprintf("%v", latency))
+	}
+	if rows != MaxInt64 {
+		lg = lg.WithField("rows", fmt.Sprintf("%d rows affected or returned", rows))
+	}
+	lg.Info(fmt.Sprintf("%s %v", sql, params))
+}
+
+func (logger *Logger) getSql(originSql string, params []interface{}) string {
+	var formattedValues []string
+	for _, value := range params {
+		indirectValue := reflect.Indirect(reflect.ValueOf(value))
+		if indirectValue.IsValid() {
+			value = indirectValue.Interface()
+			if t, ok := value.(time.Time); ok {
+				formattedValues = append(formattedValues, fmt.Sprintf("'%v'", t.Format("2006-01-02 15:04:05")))
+			} else if b, ok := value.([]byte); ok {
+				if str := string(b); logger.isPrintable(str) {
+					formattedValues = append(formattedValues, fmt.Sprintf("'%v'", str))
+				} else {
+					formattedValues = append(formattedValues, "'<binary>'")
+				}
+			} else if r, ok := value.(driver.Valuer); ok {
+				if value, err := r.Value(); err == nil && value != nil {
+					formattedValues = append(formattedValues, fmt.Sprintf("'%v'", value))
+				} else {
+					formattedValues = append(formattedValues, "NULL")
+				}
+			} else {
+				formattedValues = append(formattedValues, fmt.Sprintf("'%v'", value))
+			}
+		} else {
+			formattedValues = append(formattedValues, "NULL")
+		}
+	}
+
+	var sql string
+	// differentiate between $n placeholders or else treat like ?
+	if numericPlaceHolderRegexp.MatchString(originSql) {
+		for index, value := range formattedValues {
+			placeholder := fmt.Sprintf(`\$%d([^\d]|$)`, index+1)
+			sql = regexp.MustCompile(placeholder).ReplaceAllString(originSql, value+"$1")
+		}
+	} else {
+		formattedValuesLength := len(formattedValues)
+		for index, value := range sqlRegexp.Split(originSql, -1) {
+			sql += value
+			if index < formattedValuesLength {
+				sql += formattedValues[index]
+			}
+		}
+	}
+	return sql
+}
+
+func (logger *Logger) isPrintable(s string) bool {
+	for _, r := range s {
+		if !unicode.IsPrint(r) {
+			return false
+		}
+	}
+	return true
+}

+ 135 - 0
utils/logger/xorm_logger.go

@@ -0,0 +1,135 @@
+package logger
+
+import (
+	"github.com/go-xorm/core"
+	"github.com/sirupsen/logrus"
+)
+
+type XORMLogger struct {
+	logger  *Logger
+	level   core.LogLevel
+	showSQL bool
+}
+
+func NewXORMLogger(logger *Logger) core.ILogger {
+	lg := &XORMLogger{logger: logger.WithField("lib", "xorm"), showSQL: true}
+	level := core.LOG_INFO
+	if logger.Level == logrus.WarnLevel {
+		level = core.LOG_WARNING
+	} else if logger.Level == logrus.DebugLevel {
+		level = core.LOG_DEBUG
+	} else if logger.Level == logrus.ErrorLevel {
+		level = core.LOG_ERR
+	} else if logger.Level == logrus.FatalLevel {
+		level = core.LOG_OFF
+	} else if logger.Level == logrus.PanicLevel {
+		level = core.LOG_UNKNOWN
+	}
+	lg.level = level
+	return lg
+}
+
+func (s *XORMLogger) printSql(v ...interface{})  {
+	var sql, params interface{}
+	if len(v) > 0 {
+		sql = v[0]
+	} else {
+		sql = ""
+	}
+	if len(v) > 1 {
+		params = v[1]
+	} else {
+		params = nil
+	}
+	args := []interface{} {"sql", "", 0, sql, params, MaxInt64}
+	s.logger.Print(args...)
+}
+
+// Error implement core.ILogger
+func (s *XORMLogger) Error(v ...interface{}) {
+	if s.level <= core.LOG_ERR {
+		s.printSql(v...)
+	}
+	return
+}
+
+// Errorf implement core.ILogger
+func (s *XORMLogger) Errorf(format string, v ...interface{}) {
+	if s.level <= core.LOG_ERR {
+		s.printSql(v...)
+	}
+	return
+}
+
+// Debug implement core.ILogger
+func (s *XORMLogger) Debug(v ...interface{}) {
+	if s.level <= core.LOG_DEBUG {
+		s.printSql(v...)
+	}
+	return
+}
+
+// Debugf implement core.ILogger
+func (s *XORMLogger) Debugf(format string, v ...interface{}) {
+	if s.level <= core.LOG_DEBUG {
+		s.printSql(v...)
+	}
+	return
+}
+
+// Info implement core.ILogger
+func (s *XORMLogger) Info(v ...interface{}) {
+	if s.level <= core.LOG_INFO {
+		s.printSql(v...)
+	}
+	return
+}
+
+// Infof implement core.ILogger
+func (s *XORMLogger) Infof(format string, v ...interface{}) {
+	if s.level <= core.LOG_INFO {
+		s.printSql(v...)
+	}
+	return
+}
+
+// Warn implement core.ILogger
+func (s *XORMLogger) Warn(v ...interface{}) {
+	if s.level <= core.LOG_WARNING {
+		s.printSql(v...)
+	}
+	return
+}
+
+// Warnf implement core.ILogger
+func (s *XORMLogger) Warnf(format string, v ...interface{}) {
+	if s.level <= core.LOG_WARNING {
+		s.printSql(v...)
+	}
+	return
+}
+
+// Level implement core.ILogger
+func (s *XORMLogger) Level() core.LogLevel {
+	return s.level
+}
+
+// SetLevel implement core.ILogger
+func (s *XORMLogger) SetLevel(l core.LogLevel) {
+	s.level = l
+	return
+}
+
+// ShowSQL implement core.ILogger
+func (s *XORMLogger) ShowSQL(show ...bool) {
+	if len(show) == 0 {
+		s.showSQL = true
+		return
+	}
+	s.showSQL = show[0]
+}
+
+// IsShowSQL implement core.ILogger
+func (s *XORMLogger) IsShowSQL() bool {
+	return s.showSQL
+}

+ 78 - 0
utils/long.go

@@ -0,0 +1,78 @@
+package utils
+
+import (
+	"database/sql/driver"
+	"fmt"
+	"strconv"
+	"strings"
+)
+
+type Long int64
+
+func (t Long) MarshalJSON() ([]byte, error) {
+	b := fmt.Sprintf(`"%d"`, t)
+	return []byte(b), nil
+}
+
+func (t *Long) UnmarshalJSON(value []byte) error {
+	var v = strings.TrimSpace(strings.Trim(string(value), "\""))
+	if v == "" {
+		*t = Long(0)
+		return nil
+	}
+	num, err := strconv.ParseInt(v, 10, 64)
+	if err != nil {
+		return err
+	}
+	*t = Long(num)
+	return nil
+}
+
+func (t Long) MarshalText() ([]byte, error) {
+	return []byte(t.String()), nil
+}
+
+func (t *Long) UnmarshalText(data []byte) error {
+	*t = t.FromString(string(data))
+	return nil
+}
+
+func (t Long) FromString(str string) Long {
+	num, err := strconv.ParseInt(str, 10, 64)
+	if nil != err {
+		return 0
+	}
+	return Long(num)
+}
+
+func (t Long) String() string {
+	return strconv.FormatInt(int64(t), 10)
+}
+
+func (t Long) Value() (driver.Value, error) {
+	if t.IsZero() {
+		return int64(0), nil
+	}
+
+	return int64(t), nil
+}
+
+func (t *Long) Scan(value interface{}) error {
+	if value == nil {
+		return nil
+	}
+
+	if v, ok := value.(int64); ok {
+		*t = Long(v)
+	} else if v, ok := value.([]uint8); ok {
+		num, err := strconv.ParseInt(string(v), 10, 64)
+		if err == nil {
+			*t = Long(num)
+		}
+	}
+	return nil
+}
+
+func (t Long) IsZero() bool {
+	return t == 0
+}

+ 9 - 0
utils/paging_result.go

@@ -0,0 +1,9 @@
+package utils
+
+// 分页数据对象
+type PagingResult struct {
+	PageSize   int64       `json:"pageSize" xml:"PageSize"`     // 分页大小
+	PageIndex  int64       `json:"pageIndex" xml:"PageIndex"`   // 页面索引
+	Data       interface{} `json:"data" xml:"Result"`           // 分页结果
+	TotalCount int64       `json:"totalCount" xml:"TotalCount"` // 总大小
+}

+ 195 - 0
utils/string_utils.go

@@ -0,0 +1,195 @@
+package utils
+
+// 字符串处理工具类
+
+import (
+	"golang.org/x/text/encoding/simplifiedchinese"
+	"regexp"
+	"strings"
+)
+
+type Charset string
+
+const (
+	UTF8    = Charset("UTF-8")
+	GB18030 = Charset("GB18030")
+	GBK     = Charset("GBK")
+)
+
+func ConvertToEncodingBytes(bytes []byte, charset Charset) []byte {
+	switch charset {
+	case GB18030:
+		var decodeBytes, _ = simplifiedchinese.GB18030.NewDecoder().Bytes(bytes)
+		return decodeBytes
+	case GBK:
+		var decodeBytes, _ = simplifiedchinese.GBK.NewDecoder().Bytes(bytes)
+		return decodeBytes
+	case UTF8:
+		fallthrough
+	default:
+		return bytes
+	}
+}
+
+func SplitChineseName(name string) (string, string) {
+	if len(strings.TrimSpace(name)) == 0 {
+		return "", ""
+	}
+
+	name = strings.TrimSpace(name)
+	for _, str := range surnames {
+		if strings.Index(name, str) == 0 {
+			return str, strings.TrimLeft(name, str)
+		}
+	}
+
+	return SubStr(name, 0, 1), SubStr(name, 1, -1)
+}
+
+// 支持中文的字符串Index
+func IndexOfStr(str, substr string) int {
+	// 子串在字符串的字节位置
+	result := strings.Index(str, substr)
+	if result >= 0 {
+		// 获得子串之前的字符串并转换成[]byte
+		prefix := []byte(str)[0:result]
+		// 将子串之前的字符串转换成[]rune
+		rs := []rune(string(prefix))
+		// 获得子串之前的字符串的长度,便是子串在字符串的字符位置
+		result = len(rs)
+	}
+	return result
+}
+
+// 支持中文的字符串截取, end传-1为无限大
+func SubStr(input string, begin, end int) string {
+	if begin < 0 {
+		begin = 0
+	}
+	if end >= 0 && end <= begin {
+		return ""
+	}
+	rs := []rune(input)
+	ls := len(rs)
+	if ls <= begin {
+		return ""
+	} else if ls <= end || end < 0 {
+		return string(rs[begin:])
+	} else {
+		return string(rs[begin:end])
+	}
+}
+
+var SurNames = []string{
+	"赵", "钱", "孙", "李", "周", "吴", "郑", "王",
+	"冯", "陈", "卫", "蒋", "沈", "韩", "杨",
+	"朱", "秦", "尤", "许", "何", "吕", "施", "张",
+	"孔", "曹", "严", "金", "魏", "陶", "姜",
+	"戚", "谢", "邹", "喻", "章",
+	"云", "苏", "潘", "葛", "范", "彭", "郎",
+	"鲁", "马", "花", "方",
+	"俞", "任", "袁", "柳", "史", "唐",
+}
+
+var surnames = []string{
+	"赵", "钱", "孙", "李", "周", "吴", "郑", "王",
+	"冯", "陈", "楮", "卫", "蒋", "沈", "韩", "杨",
+	"朱", "秦", "尤", "许", "何", "吕", "施", "张",
+	"孔", "曹", "严", "华", "金", "魏", "陶", "姜",
+	"戚", "谢", "邹", "喻", "柏", "水", "窦", "章",
+	"云", "苏", "潘", "葛", "奚", "范", "彭", "郎",
+	"鲁", "韦", "昌", "马", "苗", "凤", "花", "方",
+	"俞", "任", "袁", "柳", "酆", "鲍", "史", "唐",
+	"费", "廉", "岑", "薛", "雷", "贺", "倪", "汤",
+	"滕", "殷", "罗", "毕", "郝", "邬", "安", "常",
+	"乐", "于", "时", "傅", "皮", "卞", "齐", "康",
+	"伍", "余", "元", "卜", "顾", "孟", "平", "黄",
+	"和", "穆", "萧", "尹", "姚", "邵", "湛", "汪",
+	"祁", "毛", "禹", "狄", "米", "贝", "明", "臧",
+	"计", "伏", "成", "戴", "谈", "宋", "茅", "庞",
+	"熊", "纪", "舒", "屈", "项", "祝", "董", "梁",
+	"杜", "阮", "蓝", "闽", "席", "季", "麻", "强",
+	"贾", "路", "娄", "危", "江", "童", "颜", "郭",
+	"梅", "盛", "林", "刁", "锺", "徐", "丘", "骆",
+	"高", "夏", "蔡", "田", "樊", "胡", "凌", "霍",
+	"虞", "万", "支", "柯", "昝", "管", "卢", "莫",
+	"经", "房", "裘", "缪", "干", "解", "应", "宗",
+	"丁", "宣", "贲", "邓", "郁", "单", "杭", "洪",
+	"包", "诸", "左", "石", "崔", "吉", "钮", "龚",
+	"程", "嵇", "邢", "滑", "裴", "陆", "荣", "翁",
+	"荀", "羊", "於", "惠", "甄", "麹", "家", "封",
+	"芮", "羿", "储", "靳", "汲", "邴", "糜", "松",
+	"井", "段", "富", "巫", "乌", "焦", "巴", "弓",
+	"牧", "隗", "山", "谷", "车", "侯", "宓", "蓬",
+	"全", "郗", "班", "仰", "秋", "仲", "伊", "宫",
+	"宁", "仇", "栾", "暴", "甘", "斜", "厉", "戎",
+	"祖", "武", "符", "刘", "景", "詹", "束", "龙",
+	"叶", "幸", "司", "韶", "郜", "黎", "蓟", "薄",
+	"印", "宿", "白", "怀", "蒲", "邰", "从", "鄂",
+	"索", "咸", "籍", "赖", "卓", "蔺", "屠", "蒙",
+	"池", "乔", "阴", "郁", "胥", "能", "苍", "双",
+	"闻", "莘", "党", "翟", "谭", "贡", "劳", "逄",
+	"姬", "申", "扶", "堵", "冉", "宰", "郦", "雍",
+	"郤", "璩", "桑", "桂", "濮", "牛", "寿", "通",
+	"边", "扈", "燕", "冀", "郏", "浦", "尚", "农",
+	"温", "别", "庄", "晏", "柴", "瞿", "阎", "充",
+	"慕", "连", "茹", "习", "宦", "艾", "鱼", "容",
+	"向", "古", "易", "慎", "戈", "廖", "庾", "终",
+	"暨", "居", "衡", "步", "都", "耿", "满", "弘",
+	"匡", "国", "文", "寇", "广", "禄", "阙", "东",
+	"欧", "殳", "沃", "利", "蔚", "越", "夔", "隆",
+	"师", "巩", "厍", "聂", "晁", "勾", "敖", "融",
+	"冷", "訾", "辛", "阚", "那", "简", "饶", "空",
+	"曾", "毋", "沙", "乜", "养", "鞠", "须", "丰",
+	"巢", "关", "蒯", "相", "查", "后", "荆", "红",
+	"游", "竺", "权", "逑", "盖", "益", "桓", "公",
+	"旷", "肖", "付", "钟",
+	"万俟", "司马", "上官", "欧阳",
+	"夏侯", "诸葛", "闻人", "东方",
+	"赫连", "皇甫", "尉迟", "公羊",
+	"澹台", "公冶", "宗政", "濮阳",
+	"淳于", "单于", "太叔", "申屠",
+	"公孙", "仲孙", "轩辕", "令狐",
+	"锺离", "宇文", "长孙", "慕容",
+	"鲜于", "闾丘", "司徒", "司空",
+	"丌官", "司寇", "仉", "督", "子车",
+	"颛孙", "端木", "巫马", "公西",
+	"漆雕", "乐正", "壤驷", "公良",
+	"拓拔", "夹谷", "宰父", "谷梁",
+	"晋", "楚", "阎", "法", "汝", "鄢", "涂", "钦",
+	"段干", "百里", "东郭", "南门",
+	"呼延", "归", "海", "羊舌", "微生",
+	"岳", "帅", "缑", "亢", "况", "后", "有", "琴",
+	"梁丘", "左丘", "东门", "西门",
+	"商", "牟", "佘", "佴", "伯", "赏", "南宫",
+	"墨", "哈", "谯", "笪", "年", "爱", "阳", "佟",
+}
+
+var (
+	locationPostfixPattern *regexp.Regexp
+	nationPattern          *regexp.Regexp
+	emptyBytes             []byte
+)
+
+func init() {
+	locationPostfixPattern, _ = regexp.Compile(`(省|市|特别行政区|特区|自治区|自治州|自治县|自治乡|自治旗|盟|地区|县|乡|镇|旗|街道办|街道)*$`)
+	nationPattern, _ = regexp.Compile(`(布朗族|布朗|佤族|拉祜族|拉祜|水族|怒族|独龙族|普米族|普米|保安族|纳西族|纳西|哈尼族|哈尼|达斡尔族|阿昌族|阿昌|达斡尔|乌孜别克族|乌孜别克|僳僳族|僳僳|俄罗斯族|俄罗斯|京族|塔塔尔族|塔塔尔|鄂伦春族|鄂伦春|赫哲族|赫哲|基诺族|基诺|门巴族|门巴|东乡族|高山族|珞巴族|珞巴|鄂温克族|鄂温克|撒拉族|德昂族|焉耆族|焉耆|锡伯族|锡伯|撒拉|裕固族|裕固|布依族|布依|毛南族|毛南|各族|仫佬族|仫佬|侗族|羌族|朝鲜族|鲜族|朝鲜|壮族|蒙古族|蒙古|蒙族|苗族|畲族|瑶族|彝族|白族|土家族|土族|傣族|景颇族|黎族|维吾尔族|维吾尔|吉尔吉斯|吉尔吉斯族|柯尔克孜|柯尔克孜族|塔吉克族|塔吉克|哈萨克族|哈萨克|藏族|回族|满族)$`)
+	emptyBytes = []byte("")
+}
+
+// 获取干净的中国省份、城市、区域名称
+func GetCleanLocationName(name string) string {
+	name = strings.TrimSpace(name)
+	if name == "" {
+		return ""
+	}
+
+	b := []byte(name)
+	b = locationPostfixPattern.ReplaceAll(b, emptyBytes)
+	if string(b) != "内蒙古" {
+		for nationPattern.Match(b) {
+			b = nationPattern.ReplaceAll(b, emptyBytes)
+		}
+	}
+	return string(b)
+}