Explorar o código

feature:library公共方法

ZZH-wl %!s(int64=3) %!d(string=hai) anos
pai
achega
aab56aff92

+ 44 - 0
opms_libary/db/db.go

@@ -0,0 +1,44 @@
+package db
+
+import (
+	"github.com/gogf/gf/database/gdb"
+	"github.com/gogf/gf/frame/g"
+
+	myerrors "dashoo.cn/opms_libary/myerrors"
+)
+
+type ServiceBase struct {
+	Tenant    string
+	SafeModel *gdb.Model
+	DB        gdb.DB
+	TableName string
+}
+
+func (s *ServiceBase) Init(tenant string, tableName string) error {
+	//db, err := gdb.Instance(tenant)
+	s.Tenant = tenant
+	db := g.DB(tenant)
+	if db == nil {
+		return myerrors.NewSysError(nil, "数据库初始化异常")
+	}
+	//if err != nil {
+	//	return gerror.New("获取数据库连接失败")
+	//}
+	s.DB = db
+	s.TableName = tableName
+	//s.Model = db.Table(tableName)
+	s.SafeModel = s.DB.Table(tableName).Safe()
+	return nil
+}
+
+// Exists 判断数据是否在数据表里已存在
+func (s *ServiceBase) Exists(tableName string, where ...interface{}) (bool, error) {
+	exist, err := s.DB.Table(tableName).Fields("1 as id").FindOne(where...)
+	if err != nil {
+		return false, myerrors.NewDbError(err)
+	}
+	if len(exist) > 0 {
+		return true, nil
+	}
+	return false, nil
+}

+ 115 - 0
opms_libary/db/db_test.go

@@ -0,0 +1,115 @@
+package db
+
+import (
+	"reflect"
+	"testing"
+
+	"github.com/gogf/gf/database/gdb"
+)
+
+func NewDB() (gdb.DB, error) {
+	configNode := gdb.ConfigNode{
+		Host:             "192.168.0.252",
+		Port:             "3306",
+		User:             "root",
+		Pass:             "Dashoo#190801@ali",
+		Name:             "lims_dev",
+		Type:             "mysql",
+		Role:             "master",
+		Charset:          "utf8",
+		Weight:           1,
+		MaxIdleConnCount: 10,
+		MaxOpenConnCount: 10,
+		MaxConnLifeTime:  600,
+	}
+	gdb.AddConfigNode("test", configNode)
+	return gdb.New()
+}
+
+func NewModel(table string) ServiceBase {
+	db, _ := NewDB()
+	sv := ServiceBase{
+		Tenant:    "test",
+		SafeModel: db.Model(table).Safe(),
+		DB:        db,
+		TableName: table,
+	}
+	return sv
+}
+
+func TestServiceBase_Exists(t *testing.T) {
+	type args struct {
+		tableName string
+		where     []interface{}
+	}
+	wheres := make([]interface{}, 0)
+	wheres = append(wheres, "name = ?", "dongdong")
+	tests := []struct {
+		name    string
+		fields  ServiceBase
+		args    args
+		want    bool
+		wantErr bool
+	}{
+		{"db exists", NewModel("test"), args{
+			tableName: "",
+			where:     wheres,
+		}, false, false},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			s := &ServiceBase{
+				Tenant:    tt.fields.Tenant,
+				SafeModel: tt.fields.SafeModel,
+				DB:        tt.fields.DB,
+				TableName: tt.fields.TableName,
+			}
+			got, err := s.Exists(tt.args.tableName, tt.args.where...)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("Exists() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if got != tt.want {
+				t.Errorf("Exists() got = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+//
+func TestServiceBase_GetDictListForWhere(t *testing.T) {
+	type fields struct {
+		Tenant    string
+		SafeModel *gdb.Model
+		DB        gdb.DB
+		TableName string
+	}
+	type args struct {
+		req DictReq
+	}
+	tests := []struct {
+		name    string
+		fields  ServiceBase
+		args    args
+		want    []Dict
+		wantErr bool
+	}{}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			s := &ServiceBase{
+				Tenant:    tt.fields.Tenant,
+				SafeModel: tt.fields.SafeModel,
+				DB:        tt.fields.DB,
+				TableName: tt.fields.TableName,
+			}
+			got, err := s.GetDictListForWhere(tt.args.req)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("GetDictListForWhere() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("GetDictListForWhere() got = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}

+ 95 - 0
opms_libary/db/dict.go

@@ -0,0 +1,95 @@
+package db
+
+import (
+	"dashoo.cn/opms_libary/myerrors"
+	"errors"
+)
+
+// 新增页面请求参数
+type DictEntity struct {
+	Id   string `json:"id"`
+	Name string `json:"name"`
+}
+
+// DictReq 字典返回项的请求参数
+// v:"name     @required|length:6,30#请输入用户名称|用户名称长度非法"`
+type DictReq struct {
+	TableName    string        `json:"table_name" v:"table_name@required#表名称为必填项"`      // 表名称
+	DisplayField []string      `json:"display_field" v:"display_field@required#显示字段必填"` // 显示字段
+	SortFields   string        `json:"sort_fields"`                                     // 排序字段
+	Where        interface{}   `json:"where"`                                           // 条件
+	Args         []interface{} `json:"args"`                                            // 参数
+}
+
+// Dict 字典项
+type Dict struct {
+	Label string `json:"label"`
+	Val   string `json:"val"`
+}
+
+// GetDictList 获取字典项列表(有效的) FileldParams可选,第一个为IdField,第二个为SortField,第三个为EnabledField
+func (s *ServiceBase) GetDictList(tableName string, NameField string, FileldParams ...string) ([]DictEntity, error) {
+	idField := "Id"
+	sortField := "SortCode"
+	enabledField := "Enabled"
+
+	if len(FileldParams) > 0 {
+		for index, param := range FileldParams {
+			if param == "" {
+				continue
+			}
+
+			if index == 0 {
+				idField = param
+			} else if index == 1 {
+				sortField = param
+			} else if index == 2 {
+				enabledField = param
+			}
+
+		}
+	}
+
+	var entity []DictEntity
+	err := s.DB.Model(tableName).
+		Fields(idField+" as Id", NameField+" as Name").
+		Where(enabledField, 1).
+		Order(sortField + " ASC").
+		Structs(&entity)
+	if err != nil {
+		return nil, myerrors.NewDbError(err)
+	}
+	return entity, nil
+}
+
+// GetDictListForWhere 根据条件返回字典项 (tableName:表名称, displayField 显示字段, where, args 条件参数)
+// 如果 displayField 字段只有一个,默认加 Id是key
+func (s *ServiceBase) GetDictListForWhere(req DictReq) ([]Dict, error) {
+	if len(req.DisplayField) == 0 {
+		return nil, errors.New("显示字段参数不能为空")
+	}
+	defaultField := "Id"
+	model := s.DB.Model(req.TableName)
+	if len(req.DisplayField) == 1 {
+		model = model.Fields(defaultField+" AS Label", req.DisplayField[0]+" AS Val")
+	} else {
+		model = model.Fields(req.DisplayField[0]+" AS Label", req.DisplayField[1]+" AS Val")
+	}
+	// 排序
+	if req.SortFields != "" {
+		model = model.OrderAsc(req.SortFields)
+	}
+	dictList := make([]Dict, 0)
+	if req.Where != nil {
+		if len(req.Args) > 0 {
+			model = model.Where(req.Where, req.Args)
+		} else {
+			model = model.Where(req.Where)
+		}
+	}
+	err := model.Scan(&dictList)
+	if err != nil {
+		return nil, err
+	}
+	return dictList, nil
+}

+ 1 - 0
opms_libary/go.mod

@@ -5,6 +5,7 @@ go 1.16
 require (
 	dashoo.cn/common_definition v0.0.0
 	github.com/gogf/gf v1.16.4
+	github.com/mojocn/base64Captcha v1.3.5
 	github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0
 	github.com/smallnest/rpcx v1.6.10
 )

+ 163 - 0
opms_libary/myerrors/error.go

@@ -0,0 +1,163 @@
+package myerrors
+
+import (
+	"github.com/gogf/gf/errors/gerror"
+	"github.com/gogf/gf/frame/g"
+)
+
+const (
+	SysErr  = "系统异常,请重新尝试"
+	ConvErr = "数据转换异常,请重新尝试"
+	DbErr   = "数据库操作异常,请重新尝试"
+	AuthErr = "认证异常,请重新尝试"
+)
+
+type apiCode interface {
+	Error() string
+	Code() int
+	TopError() error
+}
+
+// 自定义异常,方便输出标准异常提示,同时在日志中能记录原始异常信息
+type Error struct {
+	topError error // 原始异常
+	error    error
+}
+
+func (err *Error) Error() string {
+	if err == nil {
+		return ""
+	}
+	if err.error != nil {
+		return err.error.Error()
+	}
+
+	return ""
+}
+
+// 返回原始异常信息
+func (err *Error) TopError() error {
+	if err == nil {
+		return nil
+	}
+
+	if err.topError != nil {
+		return err.topError
+	} else {
+		return err.error
+	}
+}
+
+func (err *Error) Code() int {
+	if err == nil {
+		return -1
+	}
+	return gerror.Code(err.error)
+}
+
+func New(text string, topErr error) error {
+	return &Error{
+		topError: topErr,
+		error:    gerror.New(text),
+	}
+}
+
+func NewCode(code int, text string, topErr error) error {
+	return &Error{
+		topError: topErr,
+		error:    gerror.NewCode(code, text),
+	}
+}
+
+func Code(err error) int {
+	if err != nil {
+		if e, ok := err.(apiCode); ok {
+			return e.Code()
+		}
+	}
+	return -1
+}
+
+// 输出原始异常
+func TopErr(err error) error {
+	if err != nil {
+		if e, ok := err.(apiCode); ok {
+			return e.TopError()
+		}
+	}
+	return nil
+}
+
+// 异常检查  返回值:是否异常、error实例、异常码、异常信息
+func CheckError(err error, msg ...string) (bool, error, int32, string) {
+	message := ""
+	if err != nil {
+		theErr := err
+		if len(msg) > 0 {
+			message = msg[0]
+		}
+		if Code(err) == 210 { // 提示性异常,不返回Error
+			return false, nil, 210, err.Error()
+		} else if Code(err) == 900 { // 系统级异常
+			if TopErr(err) != nil {
+				theErr = TopErr(err)
+			}
+			g.Log().Error(theErr) // 记录原始异常
+			if message != "" {
+				return true, gerror.New(message), 900, message
+			}
+			return true, err, 900, err.Error()
+		} else { // 常规异常
+			if TopErr(err) != nil {
+				theErr = TopErr(err)
+			}
+			g.Log().Error(theErr) // 记录原始异常
+			if message != "" {
+				return true, gerror.New(message), 500, message
+			}
+			return true, err, 500, err.Error()
+		}
+	}
+	return false, nil, 200, ""
+}
+
+// 提示性异常,不进行通常错误处理(不输出日志)
+func NewMsgError(topErr error, msg string) error {
+	return NewCode(210, msg, topErr)
+}
+
+// 系统级别异常
+func NewSysError(topErr error, msg ...string) error {
+	message := SysErr
+	if len(msg) > 0 {
+		message = msg[0]
+	}
+	return NewCode(900, message, topErr)
+}
+
+// 数据库操作异常
+func NewDbError(topErr error, msg ...string) error {
+	message := DbErr
+	if len(msg) > 0 {
+		message = msg[0]
+	}
+	return NewCode(500, message, topErr)
+}
+
+// 类型转换异常
+func NewConvError(topErr error, msg ...string) error {
+	message := ConvErr
+	if len(msg) > 0 {
+		message = msg[0]
+	}
+	return NewCode(500, message, topErr)
+}
+
+// 认证异常
+func NewAuthError(topErr error, msg ...string) error {
+	message := AuthErr
+	if len(msg) > 0 {
+		message = msg[0]
+	}
+	return NewCode(401, message, topErr)
+}

+ 248 - 0
opms_libary/utils/slice_tree.go

@@ -0,0 +1,248 @@
+package utils
+
+import (
+	"fmt"
+	"github.com/gogf/gf/container/garray"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/util/gconv"
+	"reflect"
+)
+
+// 有层级关系的数组,父级-》子级 排序
+func ParentSonSort(list g.List, params ...interface{}) g.List {
+	args := make([]interface{}, 8)
+	for k, v := range params {
+		if k == 8 {
+			break
+		}
+		args[k] = v
+	}
+	var (
+		pid       int    //父级id
+		level     int    //层级数
+		fieldName string //父级id键名
+		id        string //id键名
+		levelName string //层级名称
+		title     string //标题名称
+		breaks    int    //中断层级
+		prefixStr string //字符串前缀
+	)
+	pid = gconv.Int(GetSliceByKey(args, 0, 0))
+	level = gconv.Int(GetSliceByKey(args, 1, 0))
+	fieldName = gconv.String(GetSliceByKey(args, 2, "pid"))
+	id = gconv.String(GetSliceByKey(args, 3, "id"))
+	levelName = gconv.String(GetSliceByKey(args, 4, "flg"))
+	title = gconv.String(GetSliceByKey(args, 5, "title"))
+	breaks = gconv.Int(GetSliceByKey(args, 6, -1))
+	prefixStr = gconv.String(GetSliceByKey(args, 7, "─"))
+	//定义一个新slice用于返回
+	var returnSlice g.List
+	for _, v := range list {
+		if pid == gconv.Int(v[fieldName]) {
+			v[levelName] = level
+			levelClone := level
+			titlePrefix := ""
+			for {
+				if levelClone < 0 {
+					break
+				}
+				titlePrefix += prefixStr
+				levelClone--
+			}
+			titlePrefix = "├" + titlePrefix
+			if level == 0 {
+				v["title_prefix"] = ""
+			} else {
+				v["title_prefix"] = titlePrefix
+			}
+			v["title_show"] = fmt.Sprintf("%s%s", v["title_prefix"], v[title])
+			returnSlice = append(returnSlice, v)
+			if breaks != -1 && breaks == level {
+				continue
+			}
+			args[0] = v[id]
+			args[1] = level + 1
+			newSlice2 := ParentSonSort(list, args...)
+			if len(newSlice2) > 0 {
+				returnSlice = append(returnSlice, newSlice2...)
+			}
+		}
+	}
+	return returnSlice
+}
+
+// 有层级关系的数组 ,将子级压入到父级(树形结构)
+func PushSonToParent(list g.List, params ...interface{}) g.List {
+	args := make([]interface{}, 7)
+	for k, v := range params {
+		if k == 7 {
+			break
+		}
+		args[k] = v
+	}
+	var (
+		pid         string      //父级id
+		fieldName   string      //父级id键名
+		id          string      //id键名
+		key         string      //子级数组键名
+		filter      string      //过滤键名
+		filterVal   interface{} //过滤的值
+		showNoChild bool        //是否显示不存在的子级健
+	)
+	pid = gconv.String(GetSliceByKey(args, 0, 0))
+	fieldName = gconv.String(GetSliceByKey(args, 1, "pid"))
+	id = gconv.String(GetSliceByKey(args, 2, "id"))
+	key = gconv.String(GetSliceByKey(args, 3, "children"))
+	filter = gconv.String(GetSliceByKey(args, 4, ""))
+	filterVal = GetSliceByKey(args, 5, nil)
+	showNoChild = gconv.Bool(GetSliceByKey(args, 6, true))
+	var returnList g.List
+	for _, v := range list {
+		if gconv.String(v[fieldName]) == pid {
+			if filter != "" {
+				if reflect.DeepEqual(v[filter], filterVal) {
+					args[0] = v[id]
+					child := PushSonToParent(list, args...)
+					if child != nil || showNoChild {
+						v[key] = child
+					}
+					returnList = append(returnList, v)
+				}
+			} else {
+				args[0] = v[id]
+				child := PushSonToParent(list, args...)
+				if child != nil || showNoChild {
+					v[key] = child
+				}
+				returnList = append(returnList, v)
+			}
+		}
+	}
+	return returnList
+}
+
+// 获取切片里的值 若为nil 可设置默认值val
+func GetSliceByKey(args []interface{}, key int, val interface{}) interface{} {
+	var value interface{}
+	if args[key] != nil {
+		value = args[key]
+	} else {
+		value = val
+	}
+	return value
+}
+
+// 有层级关系的切片,通过父级id查找所有子级id数组
+// parentId 父级id
+// parentIndex 父级索引名称
+// idIndex id索引名称
+func FindSonByParentId(list g.List, parentId int, parentIndex, idIndex string) g.List {
+	newList := make(g.List, 0, len(list))
+	for _, v := range list {
+		if gconv.Int(v[parentIndex]) == parentId {
+			newList = append(newList, v)
+			fList := FindSonByParentId(list, gconv.Int(v[idIndex]), parentIndex, idIndex)
+			newList = append(newList, fList...)
+		}
+	}
+	return newList
+}
+
+// 获取最顶层 parent Id
+func GetTopPidList(list g.List, parentIndex, idIndex string) *garray.Array {
+	arr := garray.NewArray()
+	for _, v1 := range list {
+		tag := true
+		for _, v2 := range list {
+			if v1[parentIndex] == v2[idIndex] {
+				tag = false
+				break
+			}
+		}
+		if tag {
+			arr.PushRight(v1[parentIndex])
+		}
+	}
+	return arr.Unique()
+}
+
+// 有层级关系的数组,通过子级fid查找所有父级数组
+func FindParentBySonPid(list g.List, id int, params ...interface{}) g.List {
+	args := make([]interface{}, 4)
+	for k, v := range params {
+		if k == 4 {
+			break
+		}
+		args[k] = v
+	}
+	var (
+		filter      = gconv.String(GetSliceByKey(args, 0, "filter")) //过滤键名
+		fPid        = gconv.String(GetSliceByKey(args, 1, "pid"))    //父级id字段键名
+		filterValue = GetSliceByKey(args, 2, nil)                    //过滤键值
+		fid         = gconv.String(GetSliceByKey(args, 3, "id"))     //id字段键名
+	)
+	rList := make(g.List, 0, len(list))
+	for _, v := range list {
+		if gconv.Int(v[fid]) == id {
+			if fv, ok := v[filter]; ok {
+				if reflect.DeepEqual(fv, filterValue) {
+					rList = append(rList, v)
+				}
+			} else {
+				rList = append(rList, v)
+			}
+			r := FindParentBySonPid(list, gconv.Int(v[fPid]), filter, fPid, filterValue, fid)
+			rList = append(rList, r...)
+		}
+	}
+	return rList
+}
+
+/**
+ * 根据id查询最顶层父级信息
+ * @param list 有层级关系的数组
+ * @param id 查找的id
+ * @param string fpid 父级id键名
+ * @param string fid 当前id键名
+ * @return g.Map
+ */
+func FindTopParent(list g.List, id int64, params ...interface{}) g.Map {
+	if len(list) == 0 {
+		return g.Map{}
+	}
+	args := make([]interface{}, 2)
+	for k, v := range params {
+		if k == 2 {
+			break
+		}
+		args[k] = v
+	}
+	var (
+		fPid = gconv.String(GetSliceByKey(args, 0, "pid")) //父级id字段键名
+		fid  = gconv.String(GetSliceByKey(args, 1, "id"))  //id字段键名
+	)
+	hasParent := true
+	top := g.Map{}
+	//找到要查找id值的数组
+	for _, v := range list {
+		if gconv.Int64(v[fid]) == gconv.Int64(id) {
+			top = v
+			break
+		}
+	}
+	for {
+		if !hasParent {
+			break
+		}
+		//查询最顶层
+		for _, v := range list {
+			if gconv.Int64(top[fPid]) == gconv.Int64(v[fid]) {
+				top = v
+				hasParent = true
+				break
+			}
+			hasParent = false
+		}
+	}
+	return top
+}

+ 198 - 0
opms_libary/utils/utils.go

@@ -0,0 +1,198 @@
+package utils
+
+import (
+	"bytes"
+	"crypto/cipher"
+	"crypto/des"
+	"crypto/md5"
+	"crypto/rand"
+	"encoding/base64"
+	"encoding/hex"
+	"github.com/gogf/gf/crypto/gmd5"
+	"github.com/gogf/gf/encoding/gcharset"
+	"github.com/gogf/gf/encoding/gjson"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/net/ghttp"
+	"github.com/gogf/gf/os/glog"
+	"github.com/gogf/gf/os/gtime"
+	"github.com/gogf/gf/text/gstr"
+	"github.com/mojocn/base64Captcha"
+	"io"
+	"time"
+)
+
+var (
+	Letters      = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" // 52
+	Symbols      = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"                   // 32
+	Digits       = "0123456789"                                           // 10
+	Characters   = Letters + Digits + Symbols                             // 94
+	LetterDigits = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+)
+
+// 密码加密
+func EncryptPassword(password, salt string) string {
+	return gmd5.MustEncryptString(gmd5.MustEncryptString(password) + gmd5.MustEncryptString(salt))
+}
+
+// 随机生成字符串
+func GetRandomString(n int) string {
+	const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+	var bytes = make([]byte, n)
+	rand.Read(bytes)
+	for i, b := range bytes {
+		bytes[i] = alphanum[b%byte(len(alphanum))]
+	}
+	return string(bytes)
+}
+
+// TripleDesEncrypt 3DES加密
+func TripleDesEncrypt(p string) (string, string, error) {
+	//k := "squR7K1XZapjhQgEjOPFyEPp"
+	k := GetRandomString(24)
+	origData := []byte(p)
+	key := []byte(k)
+	block, err := des.NewTripleDESCipher(key)
+	if err != nil {
+		return "", k, err
+	}
+	origData = PKCS5Padding(origData, block.BlockSize())
+	// origData = ZeroPadding(origData, block.BlockSize())
+	blockMode := cipher.NewCBCEncrypter(block, key[:8])
+	crypted := make([]byte, len(origData))
+	blockMode.CryptBlocks(crypted, origData)
+
+	return base64.StdEncoding.EncodeToString(crypted), k, nil
+}
+
+// TripleDesDecrypt 3DES解密
+func TripleDesDecrypt(p, k string) (string, error) {
+	crypted, _ := base64.StdEncoding.DecodeString(p)
+	key := []byte(k)
+	block, err := des.NewTripleDESCipher(key)
+	if err != nil {
+		return "", err
+	}
+	blockMode := cipher.NewCBCDecrypter(block, key[:8])
+	origData := make([]byte, len(crypted))
+	blockMode.CryptBlocks(origData, crypted)
+	origData = PKCS5UnPadding(origData)
+	return string(origData), nil
+}
+
+func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
+	padding := blockSize - len(ciphertext)%blockSize
+	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
+	return append(ciphertext, padtext...)
+}
+
+func PKCS5UnPadding(origData []byte) []byte {
+	length := len(origData)
+	// 去掉最后一个字节 unpadding 次
+	unpadding := int(origData[length-1])
+	return origData[:(length - unpadding)]
+}
+
+func Md5(str string) string {
+	h := md5.New()
+	h.Write([]byte(str))
+	return hex.EncodeToString(h.Sum(nil))
+}
+
+func GetGuid() string {
+	b := make([]byte, 48)
+	if _, err := io.ReadFull(rand.Reader, b); err != nil {
+		return ""
+	}
+	return Md5(base64.URLEncoding.EncodeToString(b))
+}
+
+// 获取字母数字混合验证码(用于Captcha验证)
+func GetVerifyImgString() (idKeyC string, base64stringC string) {
+	driver := &base64Captcha.DriverString{
+		Height:          80,
+		Width:           240,
+		NoiseCount:      50,
+		ShowLineOptions: 20,
+		Length:          4,
+		Source:          "abcdefghjkmnpqrstuvwxyz23456789",
+		Fonts:           []string{"chromohv.ttf"},
+	}
+	driver = driver.ConvertFonts()
+	store := base64Captcha.DefaultMemStore
+	c := base64Captcha.NewCaptcha(driver, store)
+	idKeyC, base64stringC, err := c.Generate()
+	if err != nil {
+		glog.Error(err)
+	}
+	return
+}
+
+// 验证输入的验证码是否正确(用于Captcha验证)
+func VerifyString(id, answer string) bool {
+	driver := new(base64Captcha.DriverString)
+	store := base64Captcha.DefaultMemStore
+	c := base64Captcha.NewCaptcha(driver, store)
+	answer = gstr.ToLower(answer)
+	return c.Verify(id, answer, true)
+}
+
+// 获取ip所属城市
+func GetCityByIp(ip string) string {
+	if ip == "" {
+		return ""
+	}
+	if ip == "[::1]" || ip == "127.0.0.1" {
+		return "内网IP"
+	}
+	url := "http://whois.pconline.com.cn/ipJson.jsp?json=true&ip=" + ip
+	bytes := ghttp.GetBytes(url)
+	src := string(bytes)
+	srcCharset := "GBK"
+	tmp, _ := gcharset.ToUTF8(srcCharset, src)
+	json, err := gjson.DecodeToJson(tmp)
+	if err != nil {
+		return ""
+	}
+	if json.GetInt("code") == 0 {
+		city := json.GetString("city")
+		return city
+	} else {
+		return ""
+	}
+}
+
+// GetHourDiffer 获取相差时间
+func GetHourDiffer(startTime, endTime string) int64 {
+	var hour int64
+	t1, err := time.ParseInLocation("2006-01-02 15:04:05", startTime, time.Local)
+	t2, err := time.ParseInLocation("2006-01-02 15:04:05", endTime, time.Local)
+	if err == nil && t1.Before(t2) {
+		diff := t2.Unix() - t1.Unix() //
+		hour = diff / 3600
+		return hour
+	} else {
+		return hour
+	}
+}
+
+// StrToTimestamp 日期字符串转时间戳(秒)
+func StrToTimestamp(dateStr string) int64 {
+	tm, err := gtime.StrToTime(dateStr)
+	if err != nil {
+		g.Log().Error(err)
+		return 0
+	}
+	return tm.Timestamp()
+}
+
+// TimeStampToDateTimeStr 时间戳转 yyyy-MM-dd HH:mm:ss
+func TimeStampToDateTimeStr(timeStamp int64) string {
+	tm := gtime.NewFromTimeStamp(timeStamp)
+	return tm.Format("Y-m-d H:i:s")
+}
+
+// TimeStampToDateStr 时间戳转 yyyy-MM-dd
+func TimeStampToDateStr(timeStamp int64) string {
+	tm := gtime.NewFromTimeStamp(timeStamp)
+	return tm.Format("Y-m-d")
+}