Jianghan 3 年之前
父節點
當前提交
e6c07900aa

+ 17 - 0
.gitignore

@@ -0,0 +1,17 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+pkg
+bin
+*/pkg
+*.exe
+*.log
+*/src/src
+*.data
+*/bin
+tp

+ 696 - 0
common_utils/common.go

@@ -0,0 +1,696 @@
+package util
+
+import (
+	"crypto/md5"
+	cryptoRand "crypto/rand"
+	"encoding/hex"
+	"encoding/json"
+	"encoding/xml"
+	"fmt"
+	bson "gopkg.in/mgo.v2/bson"
+	"io"
+	"log"
+	"math"
+	"math/big"
+	mathRand "math/rand"
+	"net/url"
+	"reflect"
+	"regexp"
+	"runtime"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/dchest/captcha"
+)
+
+const (
+	tmp = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ12345678900"
+)
+
+//短地址加密
+func EncodeArticleId(keys ...string) string {
+	kstr := strings.Join(keys, ",")
+	return SE.EncodeString(kstr)
+}
+
+//短地址解密
+func DecodeArticleId(id string) []string {
+	return strings.Split(SE.DecodeString(id), ",")
+}
+
+//短地址加密,二次加密带校验和
+func EncodeArticleId2ByCheck(keys ...string) string {
+	kstr := strings.Join(keys, ",")
+	kstr = SE.EncodeStringByCheck(kstr)
+	return url.QueryEscape("ABC" + SE2.EncodeStringByCheck(kstr))
+}
+
+//短地址解密,二次解密带校验和
+func DecodeArticleId2ByCheck(id string) []string {
+	if !strings.Contains(id, "+") { //新加密算法解密
+		id, _ = url.QueryUnescape(id)
+	}
+	if id[:3] == "ABC" { //前三位为ABC是新加密数据
+		kstr := SE2.DecodeStringByCheck(id[3:])
+		return strings.Split(SE.DecodeStringByCheck(kstr), ",")
+	} else { //历史数据
+		rep := DecodeArticleId(id)
+		oldpushid := "58f87a9561a0721f157bc74d" //剑鱼1.9发版前最后一次推送信息id
+		if rep[0] > oldpushid {
+			return []string{""}
+		} else {
+			return rep
+		}
+	}
+}
+
+func Uuid(length int) string {
+	ret := []string{}
+	r := mathRand.New(mathRand.NewSource(time.Now().UnixNano()))
+	for i := 0; i < length; i++ {
+		index := r.Intn(62)
+		ret = append(ret, tmp[index:index+1])
+	}
+	return strings.Join(ret, "")
+}
+
+//计算字符串和值
+func Sumstring(code string) (sum int) {
+	tmp := []rune(code)
+	for _, v := range tmp {
+		sum = sum + int(v)
+	}
+	return
+}
+
+//获取随机数
+func GetRandom(n int) string {
+	var idChars = []byte("0123456789")
+	b := captcha.RandomDigits(n)
+	for i, c := range b {
+		b[i] = idChars[c]
+	}
+	return string(b)
+}
+
+//获取复杂的随机数
+func GetLetterRandom(length int, flag ...bool) string {
+	var idChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
+	var mod byte = 52
+	if len(flag) > 0 && flag[0] {
+		idChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
+		mod = 26
+	}
+	b := make([]byte, length)
+	maxrb := byte(256 - (256 % int(mod)))
+	i := 0
+EXIT:
+	for {
+		r := make([]byte, length+(length/4))
+		if _, err := io.ReadFull(cryptoRand.Reader, r); err != nil {
+			panic("captcha: error reading random source: " + err.Error())
+		}
+		for _, c := range r {
+			if c > maxrb {
+				continue
+			}
+			b[i] = c % mod
+			i++
+			if i == length {
+				break EXIT
+			}
+		}
+	}
+	for i, c := range b {
+		b[i] = idChars[c]
+	}
+	return string(b)
+}
+
+/*获取复杂的随机数,数字和字母的组合
+ * c > 2 数字的个数和字母的个数随机分配
+ * n 数字的个数
+ * l 字母的个数
+ */
+func GetComplexRandom(c, n, l int) string {
+	if c < 2 && (n < 1 || l < 1) {
+		return "--"
+	}
+	r := mathRand.New(mathRand.NewSource(time.Now().UnixNano()))
+	myCommonMethod := func(flag bool) int {
+		if flag {
+			return r.Intn(c-1) + 1
+		} else {
+			return r.Intn(c)
+		}
+	}
+	if c >= 2 {
+		n = myCommonMethod(true)
+		l = c - n
+	} else {
+		c = l + n
+	}
+	value := GetRandom(n) + GetLetterRandom(l)
+	var array = strings.Split(value, "")
+	for i := 0; i < c/2; i++ {
+		r1 := myCommonMethod(false)
+		r2 := myCommonMethod(false)
+		o := array[r1]
+		array[r1] = array[r2]
+		array[r2] = o
+	}
+	return strings.Join(array, "")
+}
+
+/*隐藏部分账号
+ *返回手机号:150...70765 邮箱:...shenjun@vip.qq.com
+ */
+func EncryCode(value string) string {
+	if len(value) == 0 {
+		return value
+	} else if strings.Contains(value, "@") {
+		start := strings.Index(value, "@") / 2
+		if start == 0 {
+			start++
+		}
+		value = "...." + string(value[start:])
+	} else {
+		value = string(value[0:3]) + "..." + string(value[len(value)-4:])
+	}
+	return value
+}
+
+//生成32位md5字串
+func GetMd5String(s string) string {
+	h := md5.New()
+	h.Write([]byte(s))
+	return hex.EncodeToString(h.Sum(nil))
+}
+
+//obj(string,M)转M,查询用到
+func ObjToMap(obj interface{}) *map[string]interface{} {
+	data := make(map[string]interface{})
+	if s, ok := obj.(string); ok {
+		json.Unmarshal([]byte(strings.Replace(s, "'", "\"", -1)), &data)
+	} else if s1, ok1 := obj.(map[string]interface{}); ok1 {
+		data = s1
+	} else if s1, ok1 := obj.(*map[string]interface{}); ok1 {
+		return s1
+	} else {
+		data = nil
+	}
+	return &data
+}
+
+/*UTC类型时间转字符串
+ *flag==true,日期格式yyyy-mm-dd hh:mm:ss
+ *flag==false,日期格式yyyy-mm-dd
+ */
+func LongToDate(date interface{}, flag bool) string {
+	var int64Date int64
+	if l1, ok1 := date.(float64); ok1 {
+		int64Date = int64(l1)
+	} else if l2, ok2 := date.(int64); ok2 {
+		int64Date = l2
+	} else if l3, ok3 := date.(int); ok3 {
+		int64Date = int64(l3)
+	}
+	t := time.Unix(int64Date, 0)
+	if flag {
+		return t.Format("2006-01-02 15:04:05")
+	} else {
+		return t.Format("2006-01-02")
+	}
+}
+
+func IntAll(num interface{}) int {
+	return IntAllDef(num, 0)
+}
+
+func Int64All(num interface{}) int64 {
+	if i, ok := num.(int64); ok {
+		return int64(i)
+	} else if i0, ok0 := num.(int32); ok0 {
+		return int64(i0)
+	} else if i1, ok1 := num.(float64); ok1 {
+		return int64(i1)
+	} else if i2, ok2 := num.(int); ok2 {
+		return int64(i2)
+	} else if i3, ok3 := num.(float32); ok3 {
+		return int64(i3)
+	} else if i4, ok4 := num.(string); ok4 {
+		i64, _ := strconv.ParseInt(i4, 10, 64)
+		//in, _ := strconv.Atoi(i4)
+		return i64
+	} else if i5, ok5 := num.(int16); ok5 {
+		return int64(i5)
+	} else if i6, ok6 := num.(int8); ok6 {
+		return int64(i6)
+	} else if i7, ok7 := num.(*big.Int); ok7 {
+		in, _ := strconv.ParseInt(fmt.Sprint(i7), 10, 64)
+		return int64(in)
+	} else if i8, ok8 := num.(*big.Float); ok8 {
+		in, _ := strconv.ParseInt(fmt.Sprint(i8), 10, 64)
+		return int64(in)
+	} else {
+		return 0
+	}
+}
+
+func Float64All(num interface{}) float64 {
+	if i, ok := num.(float64); ok {
+		return float64(i)
+	} else if i0, ok0 := num.(int32); ok0 {
+		return float64(i0)
+	} else if i1, ok1 := num.(int64); ok1 {
+		return float64(i1)
+	} else if i2, ok2 := num.(int); ok2 {
+		return float64(i2)
+	} else if i3, ok3 := num.(float32); ok3 {
+		return float64(i3)
+	} else if i4, ok4 := num.(string); ok4 {
+		in, _ := strconv.ParseFloat(i4, 64)
+		return in
+	} else if i5, ok5 := num.(int16); ok5 {
+		return float64(i5)
+	} else if i6, ok6 := num.(int8); ok6 {
+		return float64(i6)
+	} else if i6, ok6 := num.(uint); ok6 {
+		return float64(i6)
+	} else if i6, ok6 := num.(uint8); ok6 {
+		return float64(i6)
+	} else if i6, ok6 := num.(uint16); ok6 {
+		return float64(i6)
+	} else if i6, ok6 := num.(uint32); ok6 {
+		return float64(i6)
+	} else if i6, ok6 := num.(uint64); ok6 {
+		return float64(i6)
+	} else if i7, ok7 := num.(*big.Float); ok7 {
+		in, _ := strconv.ParseFloat(fmt.Sprint(i7), 64)
+		return float64(in)
+	} else if i8, ok8 := num.(*big.Int); ok8 {
+		in, _ := strconv.ParseFloat(fmt.Sprint(i8), 64)
+		return float64(in)
+	} else {
+		return 0
+	}
+}
+
+func IntAllDef(num interface{}, defaultNum int) int {
+	if i, ok := num.(int); ok {
+		return int(i)
+	} else if i0, ok0 := num.(int32); ok0 {
+		return int(i0)
+	} else if i1, ok1 := num.(float64); ok1 {
+		return int(i1)
+	} else if i2, ok2 := num.(int64); ok2 {
+		return int(i2)
+	} else if i3, ok3 := num.(float32); ok3 {
+		return int(i3)
+	} else if i4, ok4 := num.(string); ok4 {
+		in, _ := strconv.Atoi(i4)
+		return int(in)
+	} else if i5, ok5 := num.(int16); ok5 {
+		return int(i5)
+	} else if i6, ok6 := num.(int8); ok6 {
+		return int(i6)
+	} else if i7, ok7 := num.(*big.Int); ok7 {
+		in, _ := strconv.Atoi(fmt.Sprint(i7))
+		return int(in)
+	} else if i8, ok8 := num.(*big.Float); ok8 {
+		in, _ := strconv.Atoi(fmt.Sprint(i8))
+		return int(in)
+	} else {
+		return defaultNum
+	}
+}
+
+func ObjToString(old interface{}) string {
+	if nil == old {
+		return ""
+	} else {
+		r, _ := old.(string)
+		return r
+	}
+}
+
+func ObjToStringDef(old interface{}, defaultstr string) string {
+	if nil == old {
+		return defaultstr
+	} else {
+		r, _ := old.(string)
+		if r == "" {
+			return defaultstr
+		}
+		return r
+	}
+}
+
+//对象数组转成string数组
+func ObjArrToStringArr(old []interface{}) []string {
+	if old != nil {
+		new := make([]string, len(old))
+		for i, v := range old {
+			new[i] = v.(string)
+		}
+		return new
+	} else {
+		return nil
+	}
+}
+
+//对象数组转成map数组
+func ObjArrToMapArr(old []interface{}) []map[string]interface{} {
+	if old != nil {
+		new := make([]map[string]interface{}, len(old))
+		for i, v := range old {
+			new[i] = v.(map[string]interface{})
+		}
+		return new
+	} else {
+		return nil
+	}
+}
+
+//map数组转成对象数组
+func MapArrToObjArr(old []map[string]interface{}) []interface{} {
+	if old != nil {
+		new := make([]interface{}, len(old))
+		for i, v := range old {
+			new[i] = v
+		}
+		return new
+	} else {
+		return nil
+	}
+}
+
+func SubstrByByte(str string, length int) string {
+	bs := []byte(str)[:length]
+	bl := 0
+	for i := len(bs) - 1; i >= 0; i-- {
+		switch {
+		case bs[i] >= 0 && bs[i] <= 127:
+			return string(bs[:i+1])
+		case bs[i] >= 128 && bs[i] <= 191:
+			bl++
+		case bs[i] >= 192 && bs[i] <= 253:
+			cl := 0
+			switch {
+			case bs[i]&252 == 252:
+				cl = 6
+			case bs[i]&248 == 248:
+				cl = 5
+			case bs[i]&240 == 240:
+				cl = 4
+			case bs[i]&224 == 224:
+				cl = 3
+			default:
+				cl = 2
+			}
+			if bl+1 == cl {
+				return string(bs[:i+cl])
+			}
+			return string(bs[:i])
+		}
+	}
+	return ""
+}
+
+func SubString(str string, begin, length int) (substr string) {
+	// 将字符串的转换成[]rune
+	rs := []rune(str)
+	lth := len(rs)
+	// 简单的越界判断
+	if begin < 0 {
+		begin = 0
+	}
+	if begin >= lth {
+		begin = lth
+	}
+	end := begin + length
+	if end > lth {
+		end = lth
+	}
+
+	// 返回子串
+	return string(rs[begin:end])
+}
+
+//捕获异常
+func Try(fun func(), handler func(interface{})) {
+	defer func() {
+		if err := recover(); err != nil {
+			for skip := 1; ; skip++ {
+				_, file, line, ok := runtime.Caller(skip)
+				if !ok {
+					break
+				}
+				go log.Printf("%v,%v\n", file, line)
+			}
+			handler(err)
+		}
+	}()
+	fun()
+}
+
+//3目运算
+func If(b bool, to, fo interface{}) interface{} {
+	if b {
+		return to
+	} else {
+		return fo
+	}
+}
+
+//HashCode值
+func HashCode(uid string) int {
+	var h uint32 = 0
+	rs := []rune(uid)
+	for i := 0; i < len(rs); i++ {
+		h = 31*h + uint32(rs[i])
+	}
+	return int(h)
+}
+
+//获取离n天的秒差
+func GetDayStartSecond(n int) int64 {
+	now := time.Now()
+	tom := time.Date(now.Year(), now.Month(), now.Day()+n, 0, 0, 0, 0, time.Local)
+	return tom.Unix()
+}
+
+func InterfaceArrTointArr(arr []interface{}) []int {
+	tmp := make([]int, 0)
+	for _, v := range arr {
+		tmp = append(tmp, int(v.(float64)))
+	}
+	return tmp
+}
+func InterfaceArrToint64Arr(arr []interface{}) []int64 {
+	tmp := make([]int64, 0)
+	for _, v := range arr {
+		tmp = append(tmp, int64(v.(float64)))
+	}
+	return tmp
+}
+
+//根据bsonID转string
+func BsonIdToSId(uid interface{}) string {
+	if uid == nil {
+		return ""
+	} else if u, ok := uid.(string); ok {
+		return u
+	} else {
+		return fmt.Sprintf("%x", string(uid.(bson.ObjectId)))
+	}
+}
+
+func StringTOBsonId(id string) (bid bson.ObjectId) {
+	defer Catch()
+	if id != "" {
+		bid = bson.ObjectIdHex(id)
+	}
+	return
+}
+
+func GetSubDay(t1 int64) int {
+	tt1 := time.Unix(t1, 0)
+	tt2 := time.Now()
+	nt1 := time.Date(tt1.Year(), tt1.Month(), tt1.Day(), 0, 0, 0, 0, time.Local)
+	nt2 := time.Date(tt2.Year(), tt2.Month(), tt2.Day(), 0, 0, 0, 0, time.Local)
+	return int((nt1.Unix() - nt2.Unix()) / 86400)
+}
+func StartWith(value, str string) bool {
+	ok, _ := regexp.MatchString("^"+str, value)
+	return ok
+}
+func EndWith(value, str string) bool {
+	ok, _ := regexp.MatchString(str+"$", value)
+	return ok
+}
+
+//出错拦截
+func Catch() {
+	if r := recover(); r != nil {
+		log.Println(r)
+		for skip := 0; ; skip++ {
+			_, file, line, ok := runtime.Caller(skip)
+			if !ok {
+				break
+			}
+			go log.Printf("%v,%v\n", file, line)
+		}
+	}
+}
+
+func ConvertFileSize(s int) string {
+	size := float64(s)
+	var kb float64 = 1024
+	var mb float64 = kb * 1024
+	var gb float64 = mb * 1024
+	if size >= gb {
+		return fmt.Sprintf("%.1f GB", float64(size/gb))
+	} else if size >= mb {
+		f := float64(size / mb)
+		if f > 100 {
+			return fmt.Sprintf("%.0f MB", f)
+		}
+		return fmt.Sprintf("%.1f MB", f)
+	} else if size >= kb {
+		f := float64(size / kb)
+		if f > 100 {
+			return fmt.Sprintf("%.0f KB", f)
+		}
+		return fmt.Sprintf("%.1f KB", f)
+	}
+	return fmt.Sprintf("%d B", s)
+}
+
+//MD5签名
+func WxSign(format string, param ...interface{}) string {
+	data := fmt.Sprintf(format, param...)
+	h := md5.New()
+	h.Write([]byte(data))
+	sign := strings.ToUpper(hex.EncodeToString(h.Sum(nil)))
+	return sign
+}
+
+//计算时差
+func TimeDiff(date time.Time) string {
+	var date1 = date                        //开始时间
+	var date2 = time.Now()                  //结束时间
+	var date3 = date2.Unix() - date1.Unix() //时间差的毫秒数
+	//计算出相差天数
+	var days = math.Floor(float64(date3 / (24 * 3600)))
+	//计算出小时数
+	var leave1 = date3 % (24 * 3600) //计算天数后剩余的毫秒数
+	var hours = math.Floor(float64(leave1 / (3600)))
+	//计算相差分钟数
+	var leave2 = leave1 % (3600) //计算小时数后剩余的毫秒数
+	var minutes = math.Floor(float64(leave2 / (60)))
+	//计算相差秒数
+	var td = "30秒前"
+	if days > 0 {
+		if days > 10 {
+			if date1.Year() < date2.Year() {
+				td = FormatDate(&date, Date_Short_Layout)
+			} else {
+				td = FormatDate(&date, Date_Small_Layout)
+			}
+		} else {
+			td = fmt.Sprint(days) + "天前"
+		}
+	} else if hours > 0 {
+		td = fmt.Sprint(hours) + "小时前"
+	} else if minutes > 0 {
+		td = fmt.Sprint(minutes) + "分钟前"
+	}
+	return td
+}
+
+func FloatFormat(tmp float64, n int) float64 {
+	fs := fmt.Sprintf("%."+fmt.Sprint(n)+"f", tmp)
+	f, _ := strconv.ParseFloat(fs, 64)
+	return f
+}
+
+//生成微信支付的签名
+func CreateWxSign(afterStr string, obj interface{}, filter ...string) string {
+	filter = append(filter, "sign", "xml")
+	keys := []string{}
+	m := make(map[string]string)
+	t := reflect.TypeOf(obj)
+	v := reflect.ValueOf(obj)
+	k := t.Kind()
+	if t.Kind() == reflect.Ptr {
+		t = t.Elem()
+		k = t.Kind()
+		v = v.Elem()
+	}
+	if k == reflect.Map {
+		for _, key := range v.MapKeys() {
+			keys = append(keys, key.String())
+			m[key.String()] = fmt.Sprint(v.MapIndex(key).Interface())
+		}
+	} else if k == reflect.Struct {
+		for n := 0; n < t.NumField(); n++ {
+			tagName := t.Field(n).Tag.Get("xml")
+			if tagName == "" {
+				tagName = t.Field(n).Tag.Get("json")
+			}
+			if tagName == "" {
+				tagName = t.Field(n).Name
+			}
+			keys = append(keys, tagName)
+			m[tagName] = fmt.Sprint(v.Field(n))
+		}
+	}
+	sort.Strings(keys)
+	vs := []string{}
+L:
+	for _, v := range keys {
+		for _, f := range filter {
+			if f == v {
+				continue L
+			}
+		}
+		if strings.TrimSpace(m[v]) == "" {
+			continue
+		}
+		vs = append(vs, fmt.Sprintf("%s=%s", v, m[v]))
+	}
+	return WxSign(strings.Join(vs, "&") + afterStr)
+}
+
+//简单的xml转map,只有一个层级,没有多层嵌套
+func XmlToMap(input string) map[string]string {
+	var t xml.Token
+	var err error
+	inputReader := strings.NewReader(input)
+	decoder := xml.NewDecoder(inputReader)
+	isStart := false
+	nodeName := ""
+	m := make(map[string]string)
+	for t, err = decoder.Token(); err == nil; t, err = decoder.Token() {
+		switch token := t.(type) {
+		// 处理元素开始(标签)
+		case xml.StartElement:
+			isStart = true
+			nodeName = token.Name.Local
+		// 处理元素结束(标签)
+		case xml.EndElement:
+			isStart = false
+		// 处理字符数据(这里就是元素的文本)
+		case xml.CharData:
+			if isStart {
+				m[nodeName] = string([]byte(token))
+			}
+		default:
+			// ...
+		}
+	}
+	return m
+}

+ 99 - 0
common_utils/config.go

@@ -0,0 +1,99 @@
+package util
+
+import (
+	"encoding/json"
+	"errors"
+	"io/ioutil"
+
+	"os"
+	"strings"
+	"sync"
+)
+
+var lock sync.Mutex
+
+//读取配置文件,可以有2个参数,
+//第一个参数是配置文件路径,如果只有一个参数时,默认配置文件路径为./config.json
+//第二个参数是要注入的对象,可以是结构体,或者是interface对象
+func ReadConfig(config ...interface{}) {
+	var r *os.File
+	if len(config) > 1 {
+		filepath, _ := config[0].(string)
+		println(filepath)
+		r, _ = os.Open(filepath)
+		defer r.Close()
+		bs, _ := ioutil.ReadAll(r)
+		json.Unmarshal(bs, config[1])
+	} else {
+		r, _ = os.Open("./config.json")
+		defer r.Close()
+		bs, _ := ioutil.ReadAll(r)
+		json.Unmarshal(bs, config[0])
+	}
+}
+
+//程序修改SysConfig配置表后,调用写入配置文件
+func WriteSysConfig(config ...interface{}) {
+	var r *os.File
+	var configobj interface{}
+	if len(config) > 1 {
+		filepath, _ := config[0].(string)
+		r, _ = os.OpenFile(filepath, os.O_WRONLY|os.O_TRUNC, 0x644)
+		configobj = config[1]
+	} else {
+		r, _ = os.OpenFile("./config.json", os.O_WRONLY|os.O_TRUNC, 0x644)
+		configobj = config[0]
+	}
+	defer r.Close()
+	if s, ok := configobj.(string); ok {
+		r.Write([]byte(s))
+
+	} else {
+		bs, _ := json.Marshal(configobj)
+		r.Write(bs)
+	}
+}
+
+//按路径查map配置
+//TODO 暂未处理查询路径中是数组的情况
+func GetPropertie(qpath /*map的查询路径*/ string, config map[string]interface{}) (ret interface{}) {
+	//tmp := new(map[string]interface{})
+	tmp := config
+	qps := strings.Split(qpath, ".")
+	length := len(qps)
+	for i, v := range qps {
+		if v1, ok := tmp[v]; ok {
+			//log.Println("类型:", reflect.TypeOf(v1))
+			if v2, ok2 := v1.(map[string]interface{}); ok2 {
+				tmp = v2
+			} else if i == length-1 {
+				//map断了,没有下一层了
+				ret = v1
+			}
+		} else {
+			return nil
+		}
+	}
+	return
+}
+
+//设置值
+//TODO 未处理数组,仅处理map递归型
+func SetPropertie(qpath string, value interface{}, config map[string]interface{}) (err error) {
+	tmp := config
+	qps := strings.Split(qpath, ".")
+	length := len(qps)
+	for i := 0; i < length-1; i++ {
+		if v1, ok1 := (tmp[qps[i]]).(map[string]interface{}); ok1 {
+			tmp = v1
+		} else {
+			err = errors.New("路径查找失败")
+			break
+		}
+
+	}
+	if err == nil {
+		tmp[qps[length-1]] = value
+	}
+	return
+}

+ 79 - 0
common_utils/date.go

@@ -0,0 +1,79 @@
+//日期处理工具类
+package util
+
+import (
+	"fmt"
+	"time"
+)
+
+const (
+	Date_Full_Layout    = "2006-01-02 15:04:05"
+	Date_Short_Layout   = "2006-01-02"
+	Date_Small_Layout   = "01-02"
+	Date_Time_Layout    = "15:04"
+	Date_yyyyMMdd       = "20060102"
+	Date_yyyyMMdd_Point = "2006.01.02"
+)
+
+//当前日期格式化
+func NowFormat(layout string) string {
+	return time.Now().Local().Format(layout)
+}
+
+//日期格式化
+func FormatDate(src *time.Time, layout string) string {
+	return (*src).Local().Format(layout)
+}
+
+//兼容从Java转换过来的数据,java生成的时间戳是按微秒算的,Go中只能按毫秒或纳秒算
+func formatDateWithInt64(src *int64, layout string) string {
+	var tmp int64
+	if *src > 0 {
+		if len(fmt.Sprint(*src)) >= 12 {
+			tmp = (*src) / 1000
+		} else {
+			tmp = (*src)
+		}
+	} else {
+		if len(fmt.Sprint(*src)) >= 13 {
+			tmp = (*src) / 1000
+		} else {
+			tmp = (*src)
+		}
+	}
+	date := time.Unix(tmp, 0)
+	return FormatDate(&date, layout)
+}
+
+func FormatDateByInt64(src *int64, layout string) string {
+	var tmp int64
+	if *src > 0 {
+		if len(fmt.Sprint(*src)) >= 12 {
+			tmp = (*src) / 1000
+		} else {
+			tmp = (*src)
+		}
+	} else {
+		if len(fmt.Sprint(*src)) >= 13 {
+			tmp = (*src) / 1000
+		} else {
+			tmp = (*src)
+		}
+	}
+	date := time.Unix(tmp, 0)
+	return FormatDate(&date, layout)
+}
+
+//支持源端多种格式
+func FormatDateWithObj(src *interface{}, layout string) string {
+	if tmp, ok := (*src).(int64); ok {
+		return formatDateWithInt64(&tmp, layout)
+	} else if tmp2, ok2 := (*src).(float64); ok2 {
+		tmpe := int64(tmp2)
+		return formatDateWithInt64(&tmpe, layout)
+	} else if tmp3, ok3 := (*src).(*time.Time); ok3 {
+		return tmp3.Format(layout)
+	} else {
+		return ""
+	}
+}

File diff suppressed because it is too large
+ 0 - 0
common_utils/elastic/city.json


+ 357 - 0
common_utils/elastic/elasticSim.go

@@ -0,0 +1,357 @@
+package elastic
+
+import (
+	"encoding/json"
+	"fmt"
+	es "gopkg.in/olivere/elastic.v2"
+	"log"
+	"runtime"
+	"strings"
+	"sync"
+	"time"
+)
+
+type Elastic struct {
+	S_esurl      string
+	I_size       int
+	Addrs        []string
+	Pool         chan *es.Client
+	lastTime     int64
+	lastTimeLock sync.Mutex
+	ntimeout     int
+}
+
+func (e *Elastic) InitElasticSize() {
+	e.Pool = make(chan *es.Client, e.I_size)
+	for _, s := range strings.Split(e.S_esurl, ",") {
+		e.Addrs = append(e.Addrs, s)
+	}
+	for i := 0; i < e.I_size; i++ {
+		client, _ := es.NewClient(es.SetURL(e.Addrs...), es.SetMaxRetries(2), es.SetSniff(false))
+		e.Pool <- client
+	}
+}
+
+//关闭连接
+func (e *Elastic) DestoryEsConn(client *es.Client) {
+	select {
+	case e.Pool <- client:
+		break
+	case <-time.After(time.Second * 1):
+		if client != nil {
+			client.Stop()
+		}
+		client = nil
+	}
+}
+
+func (e *Elastic) GetEsConn() *es.Client {
+	select {
+	case c := <-e.Pool:
+		if c == nil || !c.IsRunning() {
+			log.Println("new esclient.", len(e.Pool))
+			client, err := es.NewClient(es.SetURL(e.Addrs...),
+				es.SetMaxRetries(2), es.SetSniff(false))
+			if err == nil && client.IsRunning() {
+				return client
+			}
+		}
+		return c
+	case <-time.After(time.Second * 4):
+		//超时
+		e.ntimeout++
+		e.lastTimeLock.Lock()
+		defer e.lastTimeLock.Unlock()
+		//12秒后允许创建链接
+		c := time.Now().Unix() - e.lastTime
+		if c > 12 {
+			e.lastTime = time.Now().Unix()
+			log.Println("add client..", len(e.Pool))
+			c, _ := es.NewClient(es.SetURL(e.Addrs...), es.SetMaxRetries(2), es.SetSniff(false))
+			go func() {
+				for i := 0; i < 2; i++ {
+					client, _ := es.NewClient(es.SetURL(e.Addrs...), es.SetMaxRetries(2), es.SetSniff(false))
+					e.Pool <- client
+				}
+			}()
+			return c
+		}
+		return nil
+	}
+}
+
+func (e *Elastic) Get(index, itype, query string) *[]map[string]interface{} {
+	client := e.GetEsConn()
+	defer func() {
+		go e.DestoryEsConn(client)
+	}()
+	var res []map[string]interface{}
+	if client != nil {
+		defer func() {
+			if r := recover(); r != nil {
+				log.Println("[E]", r)
+				for skip := 1; ; skip++ {
+					_, file, line, ok := runtime.Caller(skip)
+					if !ok {
+						break
+					}
+					go log.Printf("%v,%v\n", file, line)
+				}
+			}
+		}()
+		searchResult, err := client.Search().Index(index).Type(itype).Source(query).Do()
+		if err != nil {
+			log.Println("从ES查询出错", err.Error())
+			return nil
+		}
+		if searchResult.Hits != nil {
+			resNum := len(searchResult.Hits.Hits)
+			if resNum < 5000 {
+				res = make([]map[string]interface{}, resNum)
+				for i, hit := range searchResult.Hits.Hits {
+					parseErr := json.Unmarshal(*hit.Source, &res[i])
+					if parseErr == nil && hit.Highlight != nil && res[i] != nil {
+						res[i]["highlight"] = map[string][]string(hit.Highlight)
+					}
+				}
+			} else {
+				log.Println("查询结果太多,查询到:", resNum, "条")
+			}
+		}
+	}
+	return &res
+}
+
+//关闭elastic
+func (e *Elastic) Close() {
+	for i := 0; i < e.I_size; i++ {
+		cli := <-e.Pool
+		cli.Stop()
+		cli = nil
+	}
+	e.Pool = nil
+	e = nil
+}
+
+//获取连接
+//func (e *Elastic) GetEsConn() (c *es.Client) {
+//	defer util.Catch()
+//	select {
+//	case c = <-e.Pool:
+//		if c == nil || !c.IsRunning() {
+//			client, err := es.NewClient(es.SetURL(addrs...),
+//				es.SetMaxRetries(2), es.SetSniff(false))
+//			if err == nil && client.IsRunning() {
+//				return client
+//			}
+//			return nil
+//		}
+//		return
+//	case <-time.After(time.Second * 7):
+//		//超时
+//		ntimeout++
+//		log.Println("timeout times:", ntimeout)
+//		return nil
+//	}
+//}
+
+func (e *Elastic) BulkSave(index, itype string, obj *[]map[string]interface{}, isDelBefore bool) {
+	client := e.GetEsConn()
+	defer e.DestoryEsConn(client)
+	if client != nil {
+		req := client.Bulk()
+		for _, v := range *obj {
+			if isDelBefore {
+				req = req.Add(es.NewBulkDeleteRequest().Index(index).Type(itype).Id(fmt.Sprintf("%v", v["_id"])))
+			}
+			req = req.Add(es.NewBulkIndexRequest().Index(index).Type(itype).Doc(v))
+		}
+		_, err := req.Do()
+		if err != nil {
+			log.Println("批量保存到ES出错", err.Error())
+		}
+	}
+}
+
+//根据id删除索引对象
+func (e *Elastic) DelById(index, itype, id string) bool {
+	client := e.GetEsConn()
+	defer e.DestoryEsConn(client)
+	b := false
+	if client != nil {
+		var err error
+		_, err = client.Delete().Index(index).Type(itype).Id(id).Do()
+		if err != nil {
+			log.Println("更新检索出错:", err.Error())
+		} else {
+			b = true
+		}
+	}
+	return b
+}
+func (e *Elastic) GetNoLimit(index, itype, query string) *[]map[string]interface{} {
+	//log.Println("query  -- ", query)
+	client := e.GetEsConn()
+	defer e.DestoryEsConn(client)
+	var res []map[string]interface{}
+	if client != nil {
+		defer func() {
+			if r := recover(); r != nil {
+				log.Println("[E]", r)
+				for skip := 1; ; skip++ {
+					_, file, line, ok := runtime.Caller(skip)
+					if !ok {
+						break
+					}
+					go log.Printf("%v,%v\n", file, line)
+				}
+			}
+		}()
+		searchResult, err := client.Search().Index(index).Type(itype).Source(query).Do()
+		if err != nil {
+			log.Println("从ES查询出错", err.Error())
+			return nil
+		}
+
+		if searchResult.Hits != nil {
+			resNum := len(searchResult.Hits.Hits)
+			res = make([]map[string]interface{}, resNum)
+			for i, hit := range searchResult.Hits.Hits {
+				json.Unmarshal(*hit.Source, &res[i])
+			}
+		}
+	}
+	return &res
+}
+func (e *Elastic) GetByIdField(index, itype, id, fields string) *map[string]interface{} {
+	client := e.GetEsConn()
+	defer e.DestoryEsConn(client)
+	if client != nil {
+		defer func() {
+			if r := recover(); r != nil {
+				log.Println("[E]", r)
+				for skip := 1; ; skip++ {
+					_, file, line, ok := runtime.Caller(skip)
+					if !ok {
+						break
+					}
+					go log.Printf("%v,%v\n", file, line)
+				}
+			}
+		}()
+		query := `{"query":{"term":{"_id":"` + id + `"}}`
+		if len(fields) > 0 {
+			query = query + `,"_source":[` + fields + `]`
+		}
+		query = query + "}"
+		searchResult, err := client.Search().Index(index).Type(itype).Source(query).Do()
+		if err != nil {
+			log.Println("从ES查询出错", err.Error())
+			return nil
+		}
+		var res map[string]interface{}
+		if searchResult.Hits != nil {
+			resNum := len(searchResult.Hits.Hits)
+			if resNum == 1 {
+				res = make(map[string]interface{})
+				for _, hit := range searchResult.Hits.Hits {
+					json.Unmarshal(*hit.Source, &res)
+				}
+				return &res
+			}
+		}
+	}
+	return nil
+}
+
+func (e *Elastic) Count(index, itype string, query interface{}) int64 {
+	client := e.GetEsConn()
+	defer e.DestoryEsConn(client)
+	if client != nil {
+		defer func() {
+			if r := recover(); r != nil {
+				log.Println("[E]", r)
+				for skip := 1; ; skip++ {
+					_, file, line, ok := runtime.Caller(skip)
+					if !ok {
+						break
+					}
+					go log.Printf("%v,%v\n", file, line)
+				}
+			}
+		}()
+		var qq es.Query
+		if qi, ok2 := query.(es.Query); ok2 {
+			qq = qi
+		}
+		n, err := client.Count(index).Type(itype).Query(qq).Do()
+		if err != nil {
+			log.Println("统计出错", err.Error())
+		}
+
+		return n
+	}
+	return 0
+}
+
+//更新一个字段
+func (e *Elastic) BulkUpdateArr(index, itype string, update []map[string]string) {
+	client := e.GetEsConn()
+	defer e.DestoryEsConn(client)
+	if client != nil {
+		defer func() {
+			if r := recover(); r != nil {
+				log.Println("[E]", r)
+				for skip := 1; ; skip++ {
+					_, file, line, ok := runtime.Caller(skip)
+					if !ok {
+						break
+					}
+					go log.Printf("%v,%v\n", file, line)
+				}
+			}
+		}()
+		for _, data := range update {
+			id := data["id"]
+			updateStr := data["updateStr"]
+			if id != "" && updateStr != "" {
+				_, err := client.Update().Index(index).Type(itype).Id(id).Script(updateStr).ScriptLang("groovy").Do()
+				if err != nil {
+					log.Println("更新检索出错:", err.Error())
+				}
+			} else {
+				log.Println("数据错误")
+			}
+		}
+	}
+}
+
+//更新多个字段
+func (e *Elastic) BulkUpdateMultipleFields(index, itype string, arrs [][]map[string]interface{}) {
+	client := e.GetEsConn()
+	defer e.DestoryEsConn(client)
+	if client != nil {
+		defer func() {
+			if r := recover(); r != nil {
+				log.Println("[E]", r)
+				for skip := 1; ; skip++ {
+					_, file, line, ok := runtime.Caller(skip)
+					if !ok {
+						break
+					}
+					go log.Printf("%v,%v\n", file, line)
+				}
+			}
+		}()
+		for _, arr := range arrs {
+			id := arr[0]["id"].(string)
+			update := arr[1]["update"].([]string)
+			for _, str := range update {
+				_, err := client.Update().Index(index).Type(itype).Id(id).Script(str).ScriptLang("groovy").Do()
+				if err != nil {
+					log.Println("更新检索出错:", err.Error())
+				}
+			}
+		}
+	}
+}

+ 1169 - 0
common_utils/elastic/elasticutil.go

@@ -0,0 +1,1169 @@
+package elastic
+
+import (
+	"encoding/json"
+	"fmt"
+	es "gopkg.in/olivere/elastic.v2"
+	"log"
+	"net/url"
+	"reflect"
+	"runtime"
+	"strconv"
+	"strings"
+	"sync"
+	"time"
+)
+
+//检索库服务地址
+var addrs []string
+var LocCity = map[string]string{}
+var SIZE = 30
+
+const (
+	QStr = `{"query":{"bool":{"must":[$and],"must_not":[],
+	"should":[$or],"minimum_should_match" : 1}}}`
+)
+
+var pool chan *es.Client
+var ntimeout int
+
+var syncPool sync.Pool
+
+//自定义HttpClient
+/**
+var httpclient = &http.Client{Transport: &http.Transport{
+	Dial: func(netw, addr string) (net.Conn, error) {
+		deadline := time.Now().Add(5000 * time.Millisecond)
+		c, err := net.DialTimeout(netw, addr, 10000*time.Millisecond)
+		if err != nil {
+			return nil, err
+		}
+		tcp_conn := c.(*net.TCPConn)
+		tcp_conn.SetKeepAlive(false)
+		tcp_conn.SetDeadline(deadline)
+		return tcp_conn, nil
+	},
+	DisableKeepAlives: true, //不保持,这样才能释放
+}}
+**/
+//var op = es.SetHttpClient(httpclient)
+var poolsize = int32(20)
+
+//n倍的池
+func InitElasticSize(addr string, size int) {
+	poolsize = int32(3 * size)
+	pool = make(chan *es.Client, poolsize)
+	for _, s := range strings.Split(addr, ",") {
+		addrs = append(addrs, s)
+	}
+	for i := 0; i < size; i++ {
+		client, _ := es.NewClient(es.SetURL(addrs...), es.SetMaxRetries(2), es.SetSniff(false))
+		pool <- client
+	}
+}
+
+//关闭连接
+func DestoryEsConn(client *es.Client) {
+	select {
+	case pool <- client:
+		break
+	case <-time.After(time.Second * 1):
+		if client != nil {
+			client.Stop()
+		}
+		client = nil
+	}
+}
+
+var (
+	lastTime     = int64(0)
+	lastTimeLock = &sync.Mutex{}
+)
+
+//获取连接
+
+func GetEsConn() *es.Client {
+	select {
+	case c := <-pool:
+		if c == nil || !c.IsRunning() {
+			log.Println("new esclient.", len(pool))
+			client, err := es.NewClient(es.SetURL(addrs...),
+				es.SetMaxRetries(2), es.SetSniff(false))
+			if err == nil && client.IsRunning() {
+				return client
+			}
+		}
+		return c
+	case <-time.After(time.Second * 4):
+		//超时
+		ntimeout++
+		lastTimeLock.Lock()
+		defer lastTimeLock.Unlock()
+		//12秒后允许创建链接
+		c := time.Now().Unix() - lastTime
+		if c > 12 {
+			lastTime = time.Now().Unix()
+			log.Println("add client..", len(pool))
+			c, _ := es.NewClient(es.SetURL(addrs...), es.SetMaxRetries(2), es.SetSniff(false))
+			go func() {
+				for i := 0; i < 2; i++ {
+					client, _ := es.NewClient(es.SetURL(addrs...), es.SetMaxRetries(2), es.SetSniff(false))
+					pool <- client
+				}
+			}()
+			return c
+		}
+		return nil
+	}
+}
+
+//通用查询
+//{"query": {"bool":{"must":[{"query_string":{"default_field":"name","query":"*"}}]}}}
+//{"query":{"bool":{"must":{"match":{"content":{"query":"fulltextsearch","operator":"and"}}},"should":[{"match":{"content":{"query":"Elasticsearch","boost":3}}},{"match":{"content":{"query":"Lucene","boost":2}}}]}}}
+//prefix
+//{"query":{"match":{"title":{"query":"brownfox","operator":"and"}}}} //默认为or
+//{"query":{"multi_match":{"query":"PolandStreetW1V","type":"most_fields","fields":["*_street","city^2","country","postcode"]}}}
+//{"query":{"wildcard":{"postcode":"W?F*HW"}}}
+//{"query":{"regexp":{"postcode":"W[0-9].+"}}}
+//{"query":{"filtered":{"filter":{"range":{"price":{"gte":10000}}}}},"aggs":{"single_avg_price":{"avg":{"field":"price"}}}}
+//{"query":{"match":{"make":"ford"}},"aggs":{"colors":{"terms":{"field":"color"}}}}//查fork有几种颜色
+//过滤器不会计算相关度的得分,所以它们在计算上更快一些
+//{"query":{"filtered":{"query":{"match_all":{}},"filter":{"range":{"balance":{"gte":20000,"lte":30000}}}}}}
+//{"query":{"match_all":{}},"from":10,"size":10,"_source":["account_number","balance"],"sort":{"balance":{"order":"desc"}}}
+//{"query":{"match_phrase":{"address":"milllane"}}}和match不同会去匹配整个短语,相当于must[]
+func Get(index, itype, query string) *[]map[string]interface{} {
+	log.Println("query  -- ", query)
+	client := GetEsConn()
+	defer func() {
+		go DestoryEsConn(client)
+	}()
+	var res []map[string]interface{}
+	if client != nil {
+		defer func() {
+			if r := recover(); r != nil {
+				log.Println("[E]", r)
+				for skip := 1; ; skip++ {
+					_, file, line, ok := runtime.Caller(skip)
+					if !ok {
+						break
+					}
+					go log.Printf("%v,%v\n", file, line)
+				}
+			}
+		}()
+		searchResult, err := client.Search().Index(index).Type(itype).Source(query).Do()
+		if err != nil {
+			log.Println("从ES查询出错", err.Error())
+			return nil
+		}
+
+		if searchResult.Hits != nil {
+			resNum := len(searchResult.Hits.Hits)
+			if resNum < 5000 {
+				res = make([]map[string]interface{}, resNum)
+				for i, hit := range searchResult.Hits.Hits {
+					//d := json.NewDecoder(bytes.NewBuffer(*hit.Source))
+					//d.UseNumber()
+					//d.Decode(&res[i])
+					parseErr := json.Unmarshal(*hit.Source, &res[i])
+					if parseErr == nil && hit.Highlight != nil && res[i] != nil {
+						res[i]["highlight"] = map[string][]string(hit.Highlight)
+					}
+				}
+			} else {
+				log.Println("查询结果太多,查询到:", resNum, "条")
+			}
+
+		}
+	}
+	return &res
+}
+func GetOA(index, itype, query string) (*[]map[string]interface{}, int) {
+	//log.Println("query  -- ", query)
+	client := GetEsConn()
+	defer func() {
+		go DestoryEsConn(client)
+	}()
+	var res []map[string]interface{}
+	var resNum int
+	if client != nil {
+		defer func() {
+			if r := recover(); r != nil {
+				log.Println("[E]", r)
+				for skip := 1; ; skip++ {
+					_, file, line, ok := runtime.Caller(skip)
+					if !ok {
+						break
+					}
+					go log.Printf("%v,%v\n", file, line)
+				}
+			}
+		}()
+		searchResult, err := client.Search().Index(index).Type(itype).Source(query).Do()
+		if err != nil {
+			log.Println("从ES查询出错", err.Error())
+			return nil, 0
+		}
+
+		if searchResult.Hits != nil {
+			resNum = len(searchResult.Hits.Hits)
+			if resNum < 5000 {
+				res = make([]map[string]interface{}, resNum)
+				for i, hit := range searchResult.Hits.Hits {
+					//d := json.NewDecoder(bytes.NewBuffer(*hit.Source))
+					//d.UseNumber()
+					//d.Decode(&res[i])
+					parseErr := json.Unmarshal(*hit.Source, &res[i])
+					if parseErr == nil && hit.Highlight != nil && res[i] != nil {
+						res[i]["highlight"] = map[string][]string(hit.Highlight)
+					}
+				}
+			} else {
+				log.Println("查询结果太多,查询到:", resNum, "条")
+			}
+
+		}
+	}
+	return &res, resNum
+}
+
+func GetNoLimit(index, itype, query string) *[]map[string]interface{} {
+	//log.Println("query  -- ", query)
+	client := GetEsConn()
+	defer DestoryEsConn(client)
+	var res []map[string]interface{}
+	if client != nil {
+		defer func() {
+			if r := recover(); r != nil {
+				log.Println("[E]", r)
+				for skip := 1; ; skip++ {
+					_, file, line, ok := runtime.Caller(skip)
+					if !ok {
+						break
+					}
+					go log.Printf("%v,%v\n", file, line)
+				}
+			}
+		}()
+		searchResult, err := client.Search().Index(index).Type(itype).Source(query).Do()
+		if err != nil {
+			log.Println("从ES查询出错", err.Error())
+			return nil
+		}
+
+		if searchResult.Hits != nil {
+			resNum := len(searchResult.Hits.Hits)
+			res = make([]map[string]interface{}, resNum)
+			for i, hit := range searchResult.Hits.Hits {
+				json.Unmarshal(*hit.Source, &res[i])
+			}
+		}
+	}
+	return &res
+}
+
+//分页查询
+//{"name":"张三","$and":[{"age":{"$gt":10}},{"age":{"$lte":20}}]}
+//fields直接是 `"_id","title"`
+func GetPage(index, itype, query, order, field string, start, limit int) *[]map[string]interface{} {
+	return Get(index, itype, MakeQuery(query, order, field, start, limit))
+}
+
+//openapi
+func GetOAPage(index, itype, query, order, field string, start, limit int) (*[]map[string]interface{}, int) {
+	return GetOA(index, itype, MakeQuery(query, order, field, start, limit))
+}
+
+var SR = strings.Replace
+
+func MakeQuery(query, order, fileds string, start, limit int) string {
+	log.Println(query)
+	res := AnalyQuery(query, "", QStr)
+	log.Println(len(res), query)
+	if len(res) > 10 {
+		res = SR(SR(SR(SR(res, ",$and", "", -1), "$and", "", -1), ",$or", "", -1), "$or", "", -1)
+		log.Println("1", res)
+		if len(fileds) > 0 {
+			//"_source":["account_number","balance"]
+			res = res[:len(res)-1] + `,"_source":[` + fileds + "]}"
+		}
+		log.Println("2", res)
+		//{"name":-1,"age":1}
+		if len(order) > 0 {
+			res = res[:len(res)-1] + `,"sort":[` + SR(SR(SR(SR(order, ",", "},{", -1), " ", "", -1), ":-1", `:"desc"`, -1), ":1", `:"asc"`, -1) + `]}`
+		}
+		log.Println("3", res)
+		if start > -1 {
+			res = res[:len(res)-1] + `,"from":` + strconv.Itoa(start) + `,"size":` + strconv.Itoa(limit) + "}"
+		}
+		log.Println("4", res)
+		return res
+	}
+	return ""
+}
+
+//{"name":"aaa"}
+func AnalyQuery(query interface{}, parent string, result string) string {
+	m := make(map[string]interface{})
+	if q1, ok := query.(string); ok {
+		json.Unmarshal([]byte(q1), &m)
+	} else if q2, ok2 := query.(map[string]interface{}); ok2 {
+		m = q2
+	}
+	if len(parent) == 0 {
+		for k, v := range m {
+			if k == "$and" || k == "$or" {
+				temps := ""
+				if map1, ok := v.([]interface{}); ok {
+					for i := 0; i < len(map1); i++ {
+						temps += "," + AnalyQuery(map1[i], k, "")
+					}
+				}
+				if len(temps) > 0 {
+					temps = temps[1:]
+				}
+				result = SR(result, k, temps+","+k, 1)
+			} else {
+				switch reflect.TypeOf(v).String() {
+				case "string":
+					if strings.Index(k, "TERM_") == 0 {
+						result = SR(result, "$and", `{"term":{"`+SR(k, "TERM_", "", 1)+`":"`+fmt.Sprintf("%v", v)+`"}},$and`, 1)
+					} else {
+						result = SR(result, "$and", `{"query_string":{"default_field":"`+k+`","query":"`+fmt.Sprintf("%v", v)+`"}},$and`, 1)
+					}
+				case "int", "int8", "int32", "int64", "float32", "float64":
+					if strings.Index(k, "TERM_") == 0 {
+						result = SR(result, "$and", `{"term":{"`+SR(k, "TERM_", "", 1)+`":`+fmt.Sprintf("%v", v)+`}},$and`, 1)
+					} else {
+						result = SR(result, "$and", `{"query_string":{"default_field":"`+k+`","query":`+fmt.Sprintf("%v", v)+`}},$and`, 1)
+					}
+				default:
+					result = SR(result, "$and", AnalyQuery(v, k, "")+",$and", 1)
+				}
+			}
+		}
+		return result
+	} else {
+		for k, v := range m {
+			if k == "$in" {
+				s := ""
+				if map1, ok := v.([]interface{}); ok {
+					for i := 0; i < len(map1); i++ {
+						s += "," + `"` + fmt.Sprintf("%v", map1[i]) + `"`
+					}
+				}
+				if len(s) > 0 {
+					s = s[1:]
+				}
+				return `{"terms":{"` + parent + `":[` + s + `]}}`
+			} else if strings.Contains(k, "$lt") || strings.Contains(k, "$gt") {
+				return `{"range":{"` + parent + `":{"` + SR(k, "$", "", 1) + `":` + fmt.Sprintf("%v", v) + `}}}`
+			} else {
+				switch reflect.TypeOf(v).String() {
+				case "string":
+					if strings.Index(k, "TERM_") == 0 {
+						return `{"term":{"` + SR(k, "TERM_", "", 1) + `":"` + fmt.Sprintf("%v", v) + `"}}`
+					} else {
+						return `{"query_string":{"default_field":"` + k + `","query":"` + fmt.Sprintf("%v", v) + `"}}`
+					}
+				case "int", "int8", "int32", "int64", "float32", "float64":
+					if strings.Index(k, "TERM_") == 0 {
+						return `{"term":{"` + SR(k, "TERM_", "", 1) + `":` + fmt.Sprintf("%v", v) + `}}`
+					} else {
+						return `{"query_string":{"default_field":"` + k + `","query":` + fmt.Sprintf("%v", v) + `}}`
+					}
+				default:
+					return AnalyQuery(v, k, result)
+				}
+			}
+		}
+	}
+	return result
+}
+func GetByIdField(index, itype, id, fields string) *map[string]interface{} {
+	client := GetEsConn()
+	defer DestoryEsConn(client)
+	if client != nil {
+		defer func() {
+			if r := recover(); r != nil {
+				log.Println("[E]", r)
+				for skip := 1; ; skip++ {
+					_, file, line, ok := runtime.Caller(skip)
+					if !ok {
+						break
+					}
+					go log.Printf("%v,%v\n", file, line)
+				}
+			}
+		}()
+		query := `{"query":{"term":{"_id":"` + id + `"}}`
+		if len(fields) > 0 {
+			query = query + `,"_source":[` + fields + `]`
+		}
+		query = query + "}"
+		searchResult, err := client.Search().Index(index).Type(itype).Source(query).Do()
+		if err != nil {
+			log.Println("从ES查询出错", err.Error())
+			return nil
+		}
+		var res map[string]interface{}
+		if searchResult.Hits != nil {
+			resNum := len(searchResult.Hits.Hits)
+			if resNum == 1 {
+				res = make(map[string]interface{})
+				for _, hit := range searchResult.Hits.Hits {
+					json.Unmarshal(*hit.Source, &res)
+				}
+				return &res
+			}
+		}
+	}
+	return nil
+}
+
+//根据id来查询文档
+func GetById(index, itype string, ids ...string) *[]map[string]interface{} {
+	client := GetEsConn()
+	defer DestoryEsConn(client)
+	var res []map[string]interface{}
+	if client != nil {
+		defer func() {
+			if r := recover(); r != nil {
+				log.Println("[E]", r)
+				for skip := 1; ; skip++ {
+					_, file, line, ok := runtime.Caller(skip)
+					if !ok {
+						break
+					}
+					go log.Printf("%v,%v\n", file, line)
+				}
+			}
+		}()
+		query := es.NewIdsQuery().Ids(ids...)
+		searchResult, err := client.Search().Index(index).Type(itype).Query(&query).Do()
+		if err != nil {
+			log.Println("从ES查询出错", err.Error())
+			return nil
+		}
+
+		if searchResult.Hits != nil {
+			resNum := len(searchResult.Hits.Hits)
+			if resNum < 5000 {
+				res = make([]map[string]interface{}, resNum)
+				for i, hit := range searchResult.Hits.Hits {
+					json.Unmarshal(*hit.Source, &res[i])
+				}
+			} else {
+				log.Println("查询结果太多,查询到:", resNum, "条")
+			}
+
+		}
+	}
+	return &res
+}
+
+//删除某个索引,根据查询
+func Del(index, itype string, query interface{}) bool {
+	client := GetEsConn()
+	defer DestoryEsConn(client)
+	b := false
+	if client != nil {
+		defer func() {
+			if r := recover(); r != nil {
+				log.Println("[E]", r)
+				for skip := 1; ; skip++ {
+					_, file, line, ok := runtime.Caller(skip)
+					if !ok {
+						break
+					}
+					go log.Printf("%v,%v\n", file, line)
+				}
+			}
+		}()
+		var err error
+		if qi, ok2 := query.(es.Query); ok2 {
+			_, err = client.DeleteByQuery().Index(index).Type(itype).Query(qi).Do()
+		}
+		if err != nil {
+			log.Println("删除索引出错:", err.Error())
+		} else {
+			b = true
+		}
+	}
+	return b
+}
+
+//根据语句更新对象
+func Update(index, itype, id string, updateStr string) bool {
+	client := GetEsConn()
+	defer DestoryEsConn(client)
+	b := false
+	if client != nil {
+		defer func() {
+			if r := recover(); r != nil {
+				log.Println("[E]", r)
+				for skip := 1; ; skip++ {
+					_, file, line, ok := runtime.Caller(skip)
+					if !ok {
+						break
+					}
+					go log.Printf("%v,%v\n", file, line)
+				}
+			}
+		}()
+		var err error
+		_, err = client.Update().Index(index).Type(itype).Id(id).Script(updateStr).ScriptLang("groovy").Do()
+		if err != nil {
+			log.Println("更新检索出错:", err.Error())
+		} else {
+			b = true
+		}
+	}
+	return b
+}
+
+func BulkUpdate(index, itype string, ids []string, updateStr string) {
+	client := GetEsConn()
+	defer DestoryEsConn(client)
+	if client != nil {
+		defer func() {
+			if r := recover(); r != nil {
+				log.Println("[E]", r)
+				for skip := 1; ; skip++ {
+					_, file, line, ok := runtime.Caller(skip)
+					if !ok {
+						break
+					}
+					go log.Printf("%v,%v\n", file, line)
+				}
+			}
+		}()
+		for _, id := range ids {
+			_, err := client.Update().Index(index).Type(itype).Id(id).Script(updateStr).ScriptLang("groovy").Do()
+			if err != nil {
+				log.Println("更新检索出错:", err.Error())
+			}
+		}
+	}
+}
+
+func BulkUpdateArr(index, itype string, update []map[string]string) {
+	client := GetEsConn()
+	defer DestoryEsConn(client)
+	if client != nil {
+		defer func() {
+			if r := recover(); r != nil {
+				log.Println("[E]", r)
+				for skip := 1; ; skip++ {
+					_, file, line, ok := runtime.Caller(skip)
+					if !ok {
+						break
+					}
+					go log.Printf("%v,%v\n", file, line)
+				}
+			}
+		}()
+		for _, data := range update {
+			id := data["id"]
+			updateStr := data["updateStr"]
+			if id != "" && updateStr != "" {
+				_, err := client.Update().Index(index).Type(itype).Id(id).Script(updateStr).ScriptLang("groovy").Do()
+				if err != nil {
+					log.Println("更新检索出错:", err.Error())
+				}
+			} else {
+				log.Println("数据错误")
+			}
+		}
+
+	}
+}
+
+//根据id删除索引对象
+func DelById(index, itype, id string) bool {
+	client := GetEsConn()
+	defer DestoryEsConn(client)
+	b := false
+	if client != nil {
+		defer func() {
+			if r := recover(); r != nil {
+				log.Println("[E]", r)
+				for skip := 1; ; skip++ {
+					_, file, line, ok := runtime.Caller(skip)
+					if !ok {
+						break
+					}
+					go log.Printf("%v,%v\n", file, line)
+				}
+			}
+		}()
+		var err error
+		_, err = client.Delete().Index(index).Type(itype).Id(id).Do()
+		if err != nil {
+			log.Println("更新检索出错:", err.Error())
+		} else {
+			b = true
+		}
+	}
+	return b
+}
+
+//把地市代码转为地市
+func getLoc(code string, res *map[string]string) (loc string) {
+	switch len(code) {
+	case 6:
+		loc = (*res)[code[:2]] + " " + (*res)[code[:4]] + " " + (*res)[code]
+		break
+	case 4:
+		loc = (*res)[code[:2]] + " " + (*res)[code]
+		break
+	case 2:
+		loc = (*res)[code]
+		break
+	}
+	return
+}
+
+//把地市代码转为地市
+func Loop(m interface{}, res *map[string]string) {
+	m1, ok := m.([]interface{})
+	if !ok {
+		m2, _ := m.([]map[string]interface{})
+		for i := 0; i < len(m2); i++ {
+			ms := m2[i]
+			(*res)[fmt.Sprintf("%1.0f", ms["k"])] = fmt.Sprintf("%s", ms["n"])
+			s := ms["s"]
+			if nil != s {
+				mss, _ := s.([]interface{})
+				if nil != mss {
+					Loop(mss, res)
+				}
+			}
+		}
+	} else {
+		for i := 0; i < len(m1); i++ {
+			ms, _ := m1[i].(map[string]interface{})
+			(*res)[fmt.Sprintf("%1.0f", ms["k"])] = fmt.Sprintf("%s", ms["n"])
+			s := ms["s"]
+			if nil != s {
+				mss, _ := s.([]interface{})
+				if nil != mss {
+					Loop(mss, res)
+				}
+			}
+		}
+	}
+}
+
+func ConverData(ent *map[string]interface{}) map[string]interface{} {
+	tmp := *ent
+	id64, _ := tmp["ID"].(int64)
+	ids := fmt.Sprintf("%d", id64)
+	tmp2 := make(map[string]interface{})
+	tmp2["ID"] = ids
+	tmp2["_id"] = tmp["_id"]
+	tmp2["Area"] = tmp["Area"]
+	tmp2["LeRep"] = tmp["LeRep"]
+	tmp2["RegNo"] = tmp["RegNo"]
+	tmp2["EntType"] = tmp["EntType"]
+	tmp2["EntName"] = tmp["EntName"]
+	tmp2["EntTypeName"] = tmp["EntTypeName"]
+	tmp2["Dom"] = tmp["Dom"]
+	tmp2["EstDate"] = tmp["EstDate"]
+	tmp2["OpStateName"] = tmp["OpStateName"]
+	tmp2["OpScope"] = tmp["OpScope"]
+	tmp2["OpState"] = tmp["OpState"]
+	tmp2["s_submitid"] = tmp["s_submitid"]
+	tmp2["l_submittime"] = tmp["l_submittime"]
+	tmp2["s_submitname"] = tmp["s_submitname"]
+	tmp2["RegCapCurName"] = tmp["RegCapCurName"]
+	//增加营业状态排序
+	if tmp2["OpState"] == "06" {
+		tmp2["OpSint"] = true
+	} else {
+		tmp2["OpSint"] = false
+	}
+	tmp2["OpLocDistrict"] = tmp["OpLocDistrict"]
+	//增加代码转名称
+	tmpLoc, _ := tmp["OpLocDistrict"].(string)
+	tmp2["OpLocDistrictName"] = getLoc(tmpLoc, &LocCity)
+
+	tmp2["RecCap"] = tmp["RecCap"]
+	tmp2["RegCap"] = tmp["RegCap"]
+	tmp2["IndustryPhy"] = tmp["IndustryPhy"]
+	tmp2["IndustryPhyName"] = tmp["IndustryPhyName"]
+	tmp2["RegOrg"] = tmp["RegOrg"]
+	tmp2["RegOrgName"] = tmp["RegOrgName"]
+	tmp2["Tel"] = tmp["Tel"]
+	tmp2["CompForm"] = tmp["CompForm"]
+	tmp2["CompFormName"] = tmp["CompFormName"]
+	//增加异常名录标记 Ycml可能是bool也可能是string
+	Ycmlb, _ := tmp["Ycml"].(bool)
+	Ycmls, _ := tmp["Ycml"].(string)
+	if Ycmlb || Ycmls == "1" {
+		tmp2["Ycml"] = true
+	} else {
+		tmp2["Ycml"] = false
+	}
+	//增加年报联系信息
+	if tmp["Nb_email"] != nil {
+		tmp2["Nb_email"] = tmp["Nb_email"]
+	}
+	if tmp["Nb_tel"] != nil {
+		tmp2["Nb_tel"] = tmp["Nb_tel"]
+	}
+	if tmp["Nb_addr"] != nil {
+		tmp2["Nb_addr"] = tmp["Nb_addr"]
+	}
+
+	s_synopsis := tmp["s_synopsis"]
+	if s_synopsis == nil {
+		s_synopsis = ""
+	}
+	tmp2["s_synopsis"] = s_synopsis //企业简介
+
+	//股东
+	stock := getStock(tmp["investor"])
+	tmp2["stock"] = stock
+
+	tmp2["LegCerNO"] = tmp["LegCerNO"]
+	if tmp["s_microwebsite"] != nil {
+		tmp2["s_microwebsite"] = tmp["s_microwebsite"]
+	}
+
+	tmp2["SourceType"] = tmp["SourceType"] //数据来源
+	s_servicenames := tmp["s_servicenames"]
+	if s_servicenames == nil {
+		s_servicenames = ""
+	}
+	tmp2["s_servicenames"] = s_servicenames //服务名称
+	s_action := tmp["s_action"]
+	if s_action == nil {
+		s_action = "N"
+	}
+	tmp2["s_action"] = s_action
+	tmp2["s_persion"] = tmp["s_persion"]
+	tmp2["s_mobile"] = tmp["s_mobile"]
+	tmp2["s_enturl"] = tmp["s_enturl"]
+	tmp2["s_weixin"] = tmp["s_weixin"]
+	tmp2["s_avatar"] = tmp["s_avatar"]
+	return tmp2
+}
+
+func getStock(obj interface{}) string {
+	stock := ""
+	if ns, ok := obj.([]interface{}); ok {
+		stock = " "
+		for _, ns1 := range ns {
+			if nn, ok1 := ns1.(map[string]interface{}); ok1 {
+				tmp := fmt.Sprintf("%s", nn["Inv"])
+				if strings.Index(stock, tmp) < 0 {
+					stock += tmp + " "
+				}
+			}
+		}
+	}
+	return stock
+}
+
+func BulkSave(index, itype string, obj *[]map[string]interface{}, isDelBefore bool) {
+	client := GetEsConn()
+	defer DestoryEsConn(client)
+	if client != nil {
+		defer func() {
+			if r := recover(); r != nil {
+				log.Println("[E]", r)
+				for skip := 1; ; skip++ {
+					_, file, line, ok := runtime.Caller(skip)
+					if !ok {
+						break
+					}
+					go log.Printf("%v,%v\n", file, line)
+				}
+			}
+		}()
+		req := client.Bulk()
+		for _, v := range *obj {
+			if isDelBefore {
+				req = req.Add(es.NewBulkDeleteRequest().Index(index).Type(itype).Id(fmt.Sprintf("%v", v["_id"])))
+			}
+			req = req.Add(es.NewBulkIndexRequest().Index(index).Type(itype).Doc(v))
+		}
+		_, err := req.Do()
+		if err != nil {
+			log.Println("批量保存到ES出错", err.Error())
+		}
+	}
+}
+
+func Count(index, itype string, query interface{}) int64 {
+	client := GetEsConn()
+	defer DestoryEsConn(client)
+	if client != nil {
+		defer func() {
+			if r := recover(); r != nil {
+				log.Println("[E]", r)
+				for skip := 1; ; skip++ {
+					_, file, line, ok := runtime.Caller(skip)
+					if !ok {
+						break
+					}
+					go log.Printf("%v,%v\n", file, line)
+				}
+			}
+		}()
+		var qq es.Query
+		if qi, ok2 := query.(es.Query); ok2 {
+			qq = qi
+		}
+		n, err := client.Count(index).Type(itype).Query(qq).Do()
+		if err != nil {
+			log.Println("统计出错", err.Error())
+		}
+
+		return n
+	}
+	return 0
+}
+
+//ngram精确查询
+/*
+{
+  "query": {
+    "bool": {
+      "should": [
+        {
+	"bool":{
+	  "must":[
+	  {     "multi_match": {
+            "query": "智能",
+            "type": "phrase",
+            "fields": [
+              "title"
+            ],
+           "analyzer": "my_ngram"
+          }
+        },{
+          "multi_match": {
+            "query": "机器",
+            "type": "phrase",
+            "fields": [
+              "title"
+            ],
+           "analyzer": "my_ngram"
+          }
+        },{
+          "multi_match": {
+            "query": "2016",
+            "type": "phrase",
+            "fields": [
+              "title"
+            ],
+           "analyzer": "my_ngram"
+          }
+	  }
+	  ]
+	}
+        },
+
+{
+	"bool":{
+	  "must":[
+	  {          "multi_match": {
+            "query": "河南",
+            "type": "phrase",
+            "fields": [
+              "title"
+            ],
+           "analyzer": "my_ngram"
+          }
+        },{
+          "multi_match": {
+            "query": "工商",
+            "type": "phrase",
+            "fields": [
+              "title"
+            ],
+           "analyzer": "my_ngram"
+          }
+        },{
+          "multi_match": {
+            "query": "2016",
+            "type": "phrase",
+            "fields": [
+              "title"
+            ],
+           "analyzer": "my_ngram"
+          }
+	  }
+	  ]
+	}
+        }
+      ],"minimum_should_match": 1
+    }
+  },
+  "_source": [
+    "_id",
+    "title"
+  ],
+  "from": 0,
+  "size": 10,
+  "sort": [{
+      "publishtime": "desc"
+    }]
+}
+
+*/
+//"2016+智能+办公,"河南+工商"
+//["2016+智能+办公","河南+工商"]
+//QStr = `{"query":{"bool":{should":[$or],"minimum_should_match" : 1}}}`
+//{"bool":{"must":[]}}
+//{"multi_match": {"query": "$word","type": "phrase", "fields": [$field],"analyzer": "my_ngram"}}
+//"highlight": {"pre_tags": [""],"post_tags": [""],"fields": {"detail": {"fragment_size": 1,"number_of_fragments": 1},"title": {"fragment_size": 1,"number_of_fragments": 1}}}
+const (
+	//此处最后少一个},正好NgramStr取[1:]多一个}
+	FilterQuery     = `{"query": {"filtered": {"filter": {"bool": {"must": [%s]}},%s}}`
+	NgramStr        = `{"query":{"bool":{"must":[%s],"should":[%s],"minimum_should_match": 1}}}`
+	NgramMust       = `{"bool":{"must":[%s]}}`
+	NgramMustAndNot = `{"bool":{"must":[%s],"must_not":[%s]}}`
+	minq            = `{"multi_match": {"query": "%s","type": "phrase", "fields": [%s]}}`
+	HL              = `"highlight": {"pre_tags": [""],"post_tags": [""],"fields": {%s}}`
+	highlightStr    = `%s: {"fragment_size": %d,"number_of_fragments": 1}`
+
+	FilterQuery_New  = `{"query":{"bool":{"must": [%s%s%s],"should":[]}}}`
+	MatchQueryString = `{"match": {%s: { "query":"%s", "operator": "and"}}}`
+	HL_New           = `"highlight": {"pre_tags": ["<HL>"],"post_tags": ["<HL>"],"fields": {%s}}`
+
+	//数据查询高亮标记2019-07-10
+	HL_MP = `"highlight": {"pre_tags": ["<HL>"],"post_tags": ["</HL>"],"fields": {%s}}`
+
+	ik_highlightStr = `%s: {"fragment_size": %d,"number_of_fragments": 1,"require_field_match": true}`
+	IK_pre_tags     = `<font class=\"es-highlight\">`
+	IK_post_tags    = `</font>`
+	HL_IK           = `"highlight": {"pre_tags": ["` + IK_pre_tags + `"],"post_tags": ["` + IK_post_tags + `"],"fields": {%s}}`
+)
+
+type KeyConfig struct {
+	Keys      []string `json:"key"`
+	NotKeys   []string `json:"notkey"`
+	InfoTypes []string `json:"infotype"`
+	Areas     []string `json:"area"`
+}
+
+//替换了"号
+func GetResForJY(index, itype string, keys []KeyConfig, allquery, findfields, SortQuery, fields string, start, limit int) *[]map[string]interface{} {
+	if len(keys) > 0 {
+		qstr := ""
+		new_minq := fmt.Sprintf(minq, "%s", findfields)
+		not_new_minq := fmt.Sprintf(minq, "%s", findfields) //排除词只查询标题
+		musts := []string{}
+		for _, qs_words := range keys {
+			mq := []string{}
+			notmq := []string{}
+			for _, qs_word := range qs_words.Keys {
+				mq = append(mq, fmt.Sprintf(new_minq, ReplaceYH(qs_word)))
+				/*
+					qs := AnalyzerWord("bidding", qs_word)
+					for _, qw := range qs {
+						mq = append(mq, fmt.Sprintf(new_minq, ReplaceYH(qw)))
+					}
+				*/
+			}
+			for _, qs_word := range qs_words.NotKeys {
+				notmq = append(notmq, fmt.Sprintf(not_new_minq, ReplaceYH(qs_word)))
+			}
+			if len(qs_words.Areas) > 0 {
+				mq = append(mq, fmt.Sprintf(`{"terms":{"area":["%s"]}}`, strings.Join(qs_words.Areas, `","`)))
+			}
+			if len(qs_words.InfoTypes) > 0 {
+				mq = append(mq, fmt.Sprintf(`{"terms":{"toptype":["%s"]}}`, strings.Join(qs_words.InfoTypes, `","`)))
+			}
+			musts = append(musts, fmt.Sprintf(NgramMustAndNot, strings.Join(mq, ","), strings.Join(notmq, ",")))
+		}
+		qstr = fmt.Sprintf(NgramStr, "", strings.Join(musts, ","))
+
+		qstr = fmt.Sprintf(FilterQuery, allquery, qstr[1:])
+		ws := []string{}
+		for _, w := range strings.Split(findfields, ",") {
+			ws = append(ws, fmt.Sprintf(highlightStr, w, 1))
+		}
+		qstr = qstr[:len(qstr)-1] + `,` + fmt.Sprintf(HL, strings.Join(ws, ",")) + `}`
+		if len(fields) > 0 {
+			qstr = qstr[:len(qstr)-1] + `,"_source":[` + fields + "]}"
+		}
+		if len(SortQuery) > 0 {
+			qstr = qstr[:len(qstr)-1] + `,"sort":` + SortQuery + `}`
+		}
+		if start > -1 {
+			qstr = qstr[:len(qstr)-1] + `,"from":` + strconv.Itoa(start) + `,"size":` + strconv.Itoa(limit) + "}"
+		}
+		//log.Println("jy-ngram-find", qstr)
+		return Get(index, itype, qstr)
+	} else {
+		return nil
+	}
+}
+
+func ReplaceYH(src string) (rpl string) {
+	return strings.Replace(src, `"`, `\"`, -1)
+}
+
+//
+func GetAllByNgram(index, itype, qstr, findfields, order, fields string, start, limit, count int, highlight bool) *[]map[string]interface{} {
+	if qstr != "" {
+		if highlight {
+			ws := []string{}
+			for _, w := range strings.Split(findfields, ",") {
+				ws = append(ws, fmt.Sprintf(highlightStr, w, count))
+			}
+			qstr = qstr[:len(qstr)-1] + `,` + fmt.Sprintf(HL, strings.Join(ws, ",")) + `}`
+		}
+		if len(fields) > 0 {
+			qstr = qstr[:len(qstr)-1] + `,"_source":[` + fields + "]}"
+		}
+		if len(order) > 0 {
+			qstr = qstr[:len(qstr)-1] + `,"sort":[` + SR(SR(SR(SR(order, ",", "},{", -1), " ", "", -1), ":-1", `:"desc"`, -1), ":1", `:"asc"`, -1) + `]}`
+		}
+		if start > -1 {
+			qstr = qstr[:len(qstr)-1] + `,"from":` + strconv.Itoa(start) + `,"size":` + strconv.Itoa(limit) + "}"
+		}
+		log.Println("GetAllByNgram:", qstr)
+		return Get(index, itype, qstr)
+	} else {
+		return nil
+	}
+}
+
+//数据标记2019-07-10
+func GetAllByNgram_MP(index, itype, qstr, findfields, order, fields string, start, limit, count int, highlight bool) *[]map[string]interface{} {
+	if qstr != "" {
+		if highlight {
+			ws := []string{}
+			for _, w := range strings.Split(findfields, ",") {
+				ws = append(ws, fmt.Sprintf(highlightStr, w, count))
+			}
+			qstr = qstr[:len(qstr)-1] + `,` + fmt.Sprintf(HL_MP, strings.Join(ws, ",")) + `}`
+		}
+		if len(fields) > 0 {
+			qstr = qstr[:len(qstr)-1] + `,"_source":[` + fields + "]}"
+		}
+		if len(order) > 0 {
+			qstr = qstr[:len(qstr)-1] + `,"sort":[` + SR(SR(SR(SR(order, ",", "},{", -1), " ", "", -1), ":-1", `:"desc"`, -1), ":1", `:"asc"`, -1) + `]}`
+		}
+		if start > -1 {
+			qstr = qstr[:len(qstr)-1] + `,"from":` + strconv.Itoa(start) + `,"size":` + strconv.Itoa(limit) + "}"
+		}
+		//		log.Println("GetAllByNgram:", qstr)
+		return Get(index, itype, qstr)
+	} else {
+		return nil
+	}
+}
+
+//ik 分词
+func GetAllByIk(index, itype, qstr, findfields, order, fields string, start, limit, count int, highlight bool) *[]map[string]interface{} {
+	if qstr != "" {
+		if highlight {
+			ws := []string{}
+			for _, w := range strings.Split(findfields, ",") {
+				ws = append(ws, fmt.Sprintf(ik_highlightStr, w, count))
+			}
+			qstr = qstr[:len(qstr)-1] + `,` + fmt.Sprintf(HL_IK, strings.Join(ws, ",")) + `}`
+		}
+		if len(fields) > 0 {
+			qstr = qstr[:len(qstr)-1] + `,"_source":[` + fields + "]}"
+		}
+		if len(order) > 0 {
+			qstr = qstr[:len(qstr)-1] + `,"sort":[` + SR(SR(SR(SR(order, ",", "},{", -1), " ", "", -1), ":-1", `:"desc"`, -1), ":1", `:"asc"`, -1) + `]}`
+		}
+		if start > -1 {
+			qstr = qstr[:len(qstr)-1] + `,"from":` + strconv.Itoa(start) + `,"size":` + strconv.Itoa(limit) + "}"
+		}
+		//log.Println("GetAllByNgram:", qstr)
+		return Get(index, itype, qstr)
+	} else {
+		return nil
+	}
+}
+
+//分词
+func AnalyzerWord(index, word string) (result []string) {
+	client := GetEsConn()
+	defer DestoryEsConn(client)
+	result = []string{}
+	p := url.Values{}
+	p["text"] = []string{word}
+	p["analyzer"] = []string{"ik"}
+	by, err := client.PerformRequest("GET", "/"+index+"/_analyze", p, nil)
+	if err != nil {
+		log.Println("AnalyzerWord Error:", err)
+		return
+	}
+	b, err := by.Body.MarshalJSON()
+	if err != nil {
+		log.Println("AnalyzerWord MarshalJSON Error:", err)
+		return
+	}
+	var res map[string][]map[string]interface{}
+	err = json.Unmarshal(b, &res)
+	if err != nil {
+		log.Println("AnalyzerWord Unmarshal Error:", err)
+		return
+	}
+	if res == nil {
+		return
+	}
+	for _, v := range res["tokens"] {
+		token, _ := v["token"].(string)
+		if token != "" {
+			result = append(result, token)
+		}
+	}
+	return
+}
+
+func GetResForJYView(index, itype string, keys []KeyConfig, allquery, findfields, SortQuery, fields string, start, limit int) *[]map[string]interface{} {
+	if len(keys) > 0 {
+		qstr := ""
+		new_minq := fmt.Sprintf(minq, "%s", findfields)
+		not_new_minq := fmt.Sprintf(minq, "%s", findfields) //排除词只查询标题
+		musts := []string{}
+		for _, qs_words := range keys {
+			mq := []string{}
+			notmq := []string{}
+			for _, qs_word := range qs_words.Keys {
+				mq = append(mq, fmt.Sprintf(new_minq, ReplaceYH(qs_word)))
+			}
+			for _, qs_word := range qs_words.NotKeys {
+				notmq = append(notmq, fmt.Sprintf(not_new_minq, ReplaceYH(qs_word)))
+			}
+			if len(qs_words.Areas) > 0 {
+				mq = append(mq, fmt.Sprintf(`{"terms":{"area":["%s"]}}`, strings.Join(qs_words.Areas, `","`)))
+			}
+			if len(qs_words.InfoTypes) > 0 {
+				mq = append(mq, fmt.Sprintf(`{"terms":{"toptype":["%s"]}}`, strings.Join(qs_words.InfoTypes, `","`)))
+			}
+			musts = append(musts, fmt.Sprintf(NgramMustAndNot, strings.Join(mq, ","), strings.Join(notmq, ",")))
+		}
+		qstr = fmt.Sprintf(NgramStr, "", strings.Join(musts, ","))
+
+		qstr = fmt.Sprintf(FilterQuery, allquery, qstr[1:])
+		ws := []string{}
+		for _, w := range strings.Split(findfields, ",") {
+			ws = append(ws, fmt.Sprintf(highlightStr, w, 1))
+		}
+		qstr = qstr[:len(qstr)-1] + `,` + fmt.Sprintf(HL, strings.Join(ws, ",")) + `}`
+		if len(fields) > 0 {
+			qstr = qstr[:len(qstr)-1] + `,"_source":[` + fields + "]}"
+		}
+		if len(SortQuery) > 0 {
+			qstr = qstr[:len(qstr)-1] + `,"sort":` + SortQuery + `}`
+		}
+		if start > -1 {
+			qstr = qstr[:len(qstr)-1] + `,"from":` + strconv.Itoa(start) + `,"size":` + strconv.Itoa(limit) + "}"
+		}
+		return Get(index, itype, qstr)
+	} else {
+		return nil
+	}
+}

+ 60 - 0
common_utils/encryptarticle.go

@@ -0,0 +1,60 @@
+package util
+
+import (
+	"net/url"
+	"strings"
+)
+
+//正文
+var SE = &SimpleEncrypt{Key: "topnet2015topnet2015"}
+var SE2 = &SimpleEncrypt{Key: "2017jianyu"}
+
+//百度
+var BSE = &SimpleEncrypt{Key: "HNtopnet2017jy"}
+var BSE2 = &SimpleEncrypt{Key: "TOP2017jianyu"}
+
+//订阅推送邮件
+var ESE = &SimpleEncrypt{Key: "PEjy2018topnet"}
+var ESE2 = &SimpleEncrypt{Key: "TopnetJy2018Pe"}
+
+//通用加密
+func CommonEncodeArticle(stype string, keys ...string) (id string) {
+	switch stype {
+	case "content":
+		id = BEncodeArticleId2ByCheck("A", SE, SE2, keys...)
+	case "bdprivate":
+		id = BEncodeArticleId2ByCheck("B", BSE, BSE2, keys...)
+	case "mailprivate":
+		id = BEncodeArticleId2ByCheck("C", ESE, ESE2, keys...)
+	}
+	return
+}
+
+//通用解密
+func CommonDecodeArticle(stype string, id string) (res []string) {
+	switch stype {
+	case "content":
+		res = BDecodeArticleId2ByCheck(id, SE, SE2)
+	case "bdprivate":
+		res = BDecodeArticleId2ByCheck(id, BSE, BSE2)
+	case "mailprivate":
+		res = BDecodeArticleId2ByCheck(id, ESE, ESE2)
+	}
+	return
+}
+
+//短地址加密,二次加密带校验和
+func BEncodeArticleId2ByCheck(h string, s1, s2 *SimpleEncrypt, keys ...string) string {
+	kstr := strings.Join(keys, ",")
+	kstr = s1.EncodeStringByCheck(kstr)
+	return url.QueryEscape(h + GetLetterRandom(2) + s2.EncodeStringByCheck(kstr))
+}
+
+//短地址解密,二次解密带校验和
+func BDecodeArticleId2ByCheck(id string, s1, s2 *SimpleEncrypt) []string {
+	if !strings.Contains(id, "+") { //新加密算法解密
+		id, _ = url.QueryUnescape(id)
+	}
+	kstr := s2.DecodeStringByCheck(id[3:])
+	return strings.Split(s1.DecodeStringByCheck(kstr), ",")
+}

+ 12 - 0
common_utils/go.mod

@@ -0,0 +1,12 @@
+module common_utils
+
+go 1.14
+
+require (
+	github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
+	github.com/fortytw2/leaktest v1.3.0 // indirect
+	github.com/gomodule/redigo v1.8.2
+	go.mongodb.org/mongo-driver v1.4.1
+	gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22
+	gopkg.in/olivere/elastic.v2 v2.0.61
+)

+ 131 - 0
common_utils/go.sum

@@ -0,0 +1,131 @@
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/aws/aws-sdk-go v1.29.15 h1:0ms/213murpsujhsnxnNKNeVouW60aJqSd992Ks3mxs=
+github.com/aws/aws-sdk-go v1.29.15/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTgahTma5Xg=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f h1:q/DpyjJjZs94bziQ7YkBmIlpqbVP7yw179rnzoNVX1M=
+github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f/go.mod h1:QGrK8vMWWHQYQ3QU9bw9Y9OPNfxccGzfb41qjvVeXtY=
+github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
+github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
+github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
+github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
+github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs=
+github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
+github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
+github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk=
+github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28=
+github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo=
+github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk=
+github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw=
+github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360=
+github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg=
+github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE=
+github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8=
+github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
+github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
+github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
+github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
+github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
+github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
+github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
+github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k=
+github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
+github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
+github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
+github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
+github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
+github.com/klauspost/compress v1.9.5 h1:U+CaK85mrNNb4k8BNOfgJtJ/gr6kswUCFj6miSzVC6M=
+github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
+github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
+github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
+github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk=
+github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
+github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc h1:n+nNi93yXLkJvKwXNP9d55HC7lGK4H/SRcwB5IaUZLo=
+github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
+go.mongodb.org/mongo-driver v1.4.1 h1:38NSAyDPagwnFpUA/D5SFgbugUYR3NzYRNa4Qk9UxKs=
+go.mongodb.org/mongo-driver v1.4.1/go.mod h1:llVBH2pkj9HywK0Dtdt6lDikOjFLbceHVu/Rc0iMKLs=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
+golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 h1:8dUaAV7K4uHsF56JQWkprecIQKdPHtR9jCHF5nB8uzc=
+golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw=
+gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
+gopkg.in/olivere/elastic.v2 v2.0.61 h1:7cpl3MW8ysa4GYFBXklpo5mspe4NK0rpZTdyZ+QcD4U=
+gopkg.in/olivere/elastic.v2 v2.0.61/go.mod h1:CTVyl1gckiFw1aLZYxC00g3f9jnHmhoOKcWF7W3c6n4=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

+ 318 - 0
common_utils/logger.go

@@ -0,0 +1,318 @@
+package util
+
+import (
+	"fmt"
+	"log"
+	"os"
+	"runtime"
+	"strconv"
+	"sync"
+	"time"
+)
+
+const (
+	_VER string = "1.0.2"
+)
+
+type LEVEL int32
+
+var logLevel LEVEL = 1
+var maxFileSize int64
+var maxFileCount int32
+var dailyRolling bool = true
+var consoleAppender bool = true
+var RollingFile bool = false
+var logObj *_FILE
+
+const DATEFORMAT = "2006-01-02"
+
+type UNIT int64
+
+const (
+	_       = iota
+	KB UNIT = 1 << (iota * 10)
+	MB
+	GB
+	TB
+)
+
+const (
+	ALL LEVEL = iota
+	DEBUG
+	INFO
+	WARN
+	ERROR
+	FATAL
+	OFF
+)
+
+type _FILE struct {
+	dir      string
+	filename string
+	_suffix  int
+	isCover  bool
+	_date    *time.Time
+	mu       *sync.RWMutex
+	logfile  *os.File
+	lg       *log.Logger
+}
+
+func SetConsole(isConsole bool) {
+	consoleAppender = isConsole
+}
+
+func SetLevel(_level LEVEL) {
+	logLevel = _level
+}
+
+func SetRollingFile(fileDir, fileName string, maxNumber int32, maxSize int64, _unit UNIT) {
+	maxFileCount = maxNumber
+	maxFileSize = maxSize * int64(_unit)
+	RollingFile = true
+	dailyRolling = false
+	mkdirlog(fileDir)
+	logObj = &_FILE{dir: fileDir, filename: fileName, isCover: false, mu: new(sync.RWMutex)}
+	logObj.mu.Lock()
+	defer logObj.mu.Unlock()
+	for i := 1; i <= int(maxNumber); i++ {
+		if isExist(fileDir + "/" + fileName + "." + strconv.Itoa(i)) {
+			logObj._suffix = i
+		} else {
+			break
+		}
+	}
+	if !logObj.isMustRename() {
+		logObj.logfile, _ = os.OpenFile(fileDir+"/"+fileName, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
+		logObj.lg = log.New(logObj.logfile, "", log.Ldate|log.Ltime|log.Lshortfile)
+	} else {
+		logObj.rename()
+	}
+	go fileMonitor()
+}
+
+func SetRollingDaily(fileDir, fileName string) {
+	RollingFile = false
+	dailyRolling = true
+	t, _ := time.Parse(DATEFORMAT, time.Now().Format(DATEFORMAT))
+	mkdirlog(fileDir)
+	logObj = &_FILE{dir: fileDir, filename: fileName, _date: &t, isCover: false, mu: new(sync.RWMutex)}
+	logObj.mu.Lock()
+	defer logObj.mu.Unlock()
+
+	if !logObj.isMustRename() {
+		logObj.logfile, _ = os.OpenFile(fileDir+"/"+fileName, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
+		logObj.lg = log.New(logObj.logfile, "", log.Ldate|log.Ltime|log.Lshortfile)
+	} else {
+		logObj.rename()
+	}
+}
+
+func mkdirlog(dir string) (e error) {
+	_, er := os.Stat(dir)
+	b := er == nil || os.IsExist(er)
+	if !b {
+		if err := os.MkdirAll(dir, 0666); err != nil {
+			if os.IsPermission(err) {
+				fmt.Println("create dir error:", err.Error())
+				e = err
+			}
+		}
+	}
+	return
+}
+
+func console(s ...interface{}) {
+	if consoleAppender {
+		_, file, line, _ := runtime.Caller(2)
+		short := file
+		for i := len(file) - 1; i > 0; i-- {
+			if file[i] == '/' {
+				short = file[i+1:]
+				break
+			}
+		}
+		file = short
+		log.Println(file, strconv.Itoa(line), s)
+	}
+}
+
+func catchError() {
+	if err := recover(); err != nil {
+		log.Println("err", err)
+	}
+}
+
+func Debug(v ...interface{}) {
+	if dailyRolling {
+		fileCheck()
+	}
+	defer catchError()
+	if logObj != nil {
+		logObj.mu.RLock()
+		defer logObj.mu.RUnlock()
+	}
+
+	if logLevel <= DEBUG {
+		if logObj != nil {
+			logObj.lg.Output(2, fmt.Sprintln("debug", v))
+		}
+		console("debug", v)
+	}
+}
+func Info(v ...interface{}) {
+	if dailyRolling {
+		fileCheck()
+	}
+	defer catchError()
+	if logObj != nil {
+		logObj.mu.RLock()
+		defer logObj.mu.RUnlock()
+	}
+	if logLevel <= INFO {
+		if logObj != nil {
+			logObj.lg.Output(2, fmt.Sprintln("info", v))
+		}
+		console("info", v)
+	}
+}
+func Warn(v ...interface{}) {
+	if dailyRolling {
+		fileCheck()
+	}
+	defer catchError()
+	if logObj != nil {
+		logObj.mu.RLock()
+		defer logObj.mu.RUnlock()
+	}
+
+	if logLevel <= WARN {
+		if logObj != nil {
+			logObj.lg.Output(2, fmt.Sprintln("warn", v))
+		}
+		console("warn", v)
+	}
+}
+func Error(v ...interface{}) {
+	if dailyRolling {
+		fileCheck()
+	}
+	defer catchError()
+	if logObj != nil {
+		logObj.mu.RLock()
+		defer logObj.mu.RUnlock()
+	}
+	if logLevel <= ERROR {
+		if logObj != nil {
+			logObj.lg.Output(2, fmt.Sprintln("error", v))
+		}
+		console("error", v)
+	}
+}
+func Fatal(v ...interface{}) {
+	if dailyRolling {
+		fileCheck()
+	}
+	defer catchError()
+	if logObj != nil {
+		logObj.mu.RLock()
+		defer logObj.mu.RUnlock()
+	}
+	if logLevel <= FATAL {
+		if logObj != nil {
+			logObj.lg.Output(2, fmt.Sprintln("fatal", v))
+		}
+		console("fatal", v)
+	}
+}
+
+func (f *_FILE) isMustRename() bool {
+	if dailyRolling {
+		t, _ := time.Parse(DATEFORMAT, time.Now().Format(DATEFORMAT))
+		if t.After(*f._date) {
+			return true
+		}
+	} else {
+		if maxFileCount > 1 {
+			if fileSize(f.dir+"/"+f.filename) >= maxFileSize {
+				return true
+			}
+		}
+	}
+	return false
+}
+
+func (f *_FILE) rename() {
+	if dailyRolling {
+		fn := f.dir + "/" + f.filename + "." + f._date.Format(DATEFORMAT)
+		if !isExist(fn) && f.isMustRename() {
+			if f.logfile != nil {
+				f.logfile.Close()
+			}
+			err := os.Rename(f.dir+"/"+f.filename, fn)
+			if err != nil {
+				f.lg.Println("rename err", err.Error())
+			}
+			t, _ := time.Parse(DATEFORMAT, time.Now().Format(DATEFORMAT))
+			f._date = &t
+			f.logfile, _ = os.Create(f.dir + "/" + f.filename)
+			f.lg = log.New(logObj.logfile, "\n", log.Ldate|log.Ltime|log.Lshortfile)
+		}
+	} else {
+		f.coverNextOne()
+	}
+}
+
+func (f *_FILE) nextSuffix() int {
+	return int(f._suffix%int(maxFileCount) + 1)
+}
+
+func (f *_FILE) coverNextOne() {
+	f._suffix = f.nextSuffix()
+	if f.logfile != nil {
+		f.logfile.Close()
+	}
+	if isExist(f.dir + "/" + f.filename + "." + strconv.Itoa(int(f._suffix))) {
+		os.Remove(f.dir + "/" + f.filename + "." + strconv.Itoa(int(f._suffix)))
+	}
+	os.Rename(f.dir+"/"+f.filename, f.dir+"/"+f.filename+"."+strconv.Itoa(int(f._suffix)))
+	f.logfile, _ = os.Create(f.dir + "/" + f.filename)
+	f.lg = log.New(logObj.logfile, "\n", log.Ldate|log.Ltime|log.Lshortfile)
+}
+
+func fileSize(file string) int64 {
+	fmt.Println("fileSize", file)
+	f, e := os.Stat(file)
+	if e != nil {
+		fmt.Println(e.Error())
+		return 0
+	}
+	return f.Size()
+}
+
+func isExist(path string) bool {
+	_, err := os.Stat(path)
+	return err == nil || os.IsExist(err)
+}
+
+func fileMonitor() {
+	timer := time.NewTicker(1 * time.Second)
+	for {
+		select {
+		case <-timer.C:
+			fileCheck()
+		}
+	}
+}
+
+func fileCheck() {
+	defer func() {
+		if err := recover(); err != nil {
+			log.Println(err)
+		}
+	}()
+	if logObj != nil && logObj.isMustRename() {
+		logObj.mu.Lock()
+		defer logObj.mu.Unlock()
+		logObj.rename()
+	}
+}

+ 614 - 0
common_utils/mongodb/mongodb.go

@@ -0,0 +1,614 @@
+package mongodb
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+	"go.mongodb.org/mongo-driver/mongo"
+	"go.mongodb.org/mongo-driver/mongo/options"
+	"log"
+	"math/big"
+	"runtime"
+	"strconv"
+	"strings"
+	"time"
+)
+
+type MgoSess struct {
+	db     string
+	coll   string
+	query  interface{}
+	sorts  []string
+	fields interface{}
+	limit  int64
+	skip   int64
+	pipe   []map[string]interface{}
+	all    interface{}
+	M      *MongodbSim
+}
+
+type MgoIter struct {
+	Cursor *mongo.Cursor
+}
+
+func NewMgo(addr, db string, size int) *MongodbSim {
+	mgo := &MongodbSim{
+		MongodbAddr: addr,
+		Size:        size,
+		DbName:      db,
+	}
+	mgo.InitPool()
+	return mgo
+}
+
+func (mt *MgoIter) Next(result interface{}) bool {
+	if mt.Cursor != nil {
+		if mt.Cursor.Next(nil) {
+			err := mt.Cursor.Decode(result)
+			if err != nil {
+				log.Println("mgo cur err", err.Error())
+				mt.Cursor.Close(nil)
+				return false
+			}
+			return true
+		} else {
+			mt.Cursor.Close(nil)
+			return false
+		}
+	} else {
+		return false
+	}
+}
+
+func (ms *MgoSess) DB(name string) *MgoSess {
+	ms.db = name
+	return ms
+}
+
+func (ms *MgoSess) C(name string) *MgoSess {
+	ms.coll = name
+	return ms
+}
+
+func (ms *MgoSess) Find(q interface{}) *MgoSess {
+	if q == nil {
+		q = map[string]interface{}{}
+	}
+	ms.query = q
+	return ms
+}
+
+func (ms *MgoSess) Select(fields interface{}) *MgoSess {
+	ms.fields = fields
+	return ms
+}
+
+func (ms *MgoSess) Limit(limit int64) *MgoSess {
+	ms.limit = limit
+	return ms
+}
+func (ms *MgoSess) Skip(skip int64) *MgoSess {
+	ms.skip = skip
+	return ms
+}
+
+func (ms *MgoSess) Sort(sorts ...string) *MgoSess {
+	ms.sorts = sorts
+	return ms
+}
+func (ms *MgoSess) Pipe(p []map[string]interface{}) *MgoSess {
+	ms.pipe = p
+	return ms
+}
+func (ms *MgoSess) All(v *[]map[string]interface{}) {
+	cur, err := ms.M.C.Database(ms.db).Collection(ms.coll).Aggregate(ms.M.Ctx, ms.pipe)
+	if err == nil && cur.Err() == nil {
+		cur.All(ms.M.Ctx, v)
+	}
+}
+func (ms *MgoSess) Iter() *MgoIter {
+	it := &MgoIter{}
+	find := options.Find()
+	if ms.skip > 0 {
+		find.SetSkip(ms.skip)
+	}
+	if ms.limit > 0 {
+		find.SetLimit(ms.limit)
+	}
+	find.SetBatchSize(100)
+	if len(ms.sorts) > 0 {
+		sort := bson.M{}
+		for _, k := range ms.sorts {
+			switch k[:1] {
+			case "-":
+				sort[k[1:]] = -1
+			case "+":
+				sort[k[1:]] = 1
+			default:
+				sort[k] = 1
+			}
+		}
+		find.SetSort(sort)
+	}
+	if ms.fields != nil {
+		find.SetProjection(ms.fields)
+	}
+	cur, err := ms.M.C.Database(ms.db).Collection(ms.coll).Find(ms.M.Ctx, ms.query, find)
+	if err != nil {
+		log.Println("mgo find err", err.Error())
+	} else {
+		it.Cursor = cur
+	}
+	return it
+}
+
+func (ms *MgoSess) Count() (int64, error) {
+	return ms.M.C.Database(ms.db).Collection(ms.coll).CountDocuments(ms.M.Ctx, ms.query)
+}
+
+type MongodbSim struct {
+	MongodbAddr string
+	Size        int
+	//	MinSize     int
+	DbName   string
+	C        *mongo.Client
+	Ctx      context.Context
+	ShortCtx context.Context
+	pool     chan bool
+	UserName string
+	Password string
+	ReplSet  string
+}
+
+func (m *MongodbSim) GetMgoConn() *MgoSess {
+	//m.Open()
+	ms := &MgoSess{}
+	ms.M = m
+	return ms
+}
+
+func (m *MongodbSim) DestoryMongoConn(ms *MgoSess) {
+	//m.Close()
+	ms.M = nil
+	ms = nil
+}
+
+func (m *MongodbSim) Destroy() {
+	//m.Close()
+	m.C.Disconnect(nil)
+	m.C = nil
+}
+
+func (m *MongodbSim) InitPool() {
+	opts := options.Client()
+	opts.SetConnectTimeout(3 * time.Second)
+	opts.SetHosts(strings.Split(m.MongodbAddr, ","))
+	//opts.ApplyURI("mongodb://" + m.MongodbAddr)
+	opts.SetMaxPoolSize(uint64(m.Size))
+	if m.UserName != "" && m.Password != "" {
+		cre := options.Credential{
+			Username: m.UserName,
+			Password: m.Password,
+		}
+		opts.SetAuth(cre)
+	}
+	//ms := strings.Split(m.MongodbAddr, ",")
+	//if m.ReplSet == "" && len(ms) > 1 {
+	//	m.ReplSet = "qfws"
+	//}
+	if m.ReplSet != "" {
+		opts.SetReplicaSet(m.ReplSet)
+		opts.SetDirect(false)
+	}
+	m.pool = make(chan bool, m.Size)
+	opts.SetMaxConnIdleTime(2 * time.Hour)
+	m.Ctx, _ = context.WithTimeout(context.Background(), 99999*time.Hour)
+	m.ShortCtx, _ = context.WithTimeout(context.Background(), 1*time.Minute)
+	client, err := mongo.Connect(m.ShortCtx, opts)
+	if err != nil {
+		log.Println("mgo init error:", err.Error())
+	} else {
+		m.C = client
+	}
+}
+
+func (m *MongodbSim) Open() {
+	m.pool <- true
+}
+func (m *MongodbSim) Close() {
+	<-m.pool
+}
+
+func (m *MongodbSim) Save(c string, doc interface{}) string {
+	defer catch()
+	m.Open()
+	defer m.Close()
+	coll := m.C.Database(m.DbName).Collection(c)
+	obj := ObjToM(doc)
+	id := primitive.NewObjectID()
+	(*obj)["_id"] = id
+	_, err := coll.InsertOne(m.Ctx, obj)
+	if nil != err {
+		log.Println("SaveError", err)
+		return ""
+	}
+	return id.Hex()
+}
+
+//原_id不变
+func (m *MongodbSim) SaveByOriID(c string, doc interface{}) bool {
+	defer catch()
+	m.Open()
+	defer m.Close()
+	coll := m.C.Database(m.DbName).Collection(c)
+	_, err := coll.InsertOne(m.Ctx, ObjToM(doc))
+	if nil != err {
+		log.Println("SaveByOriIDError", err)
+		return false
+	}
+	return true
+}
+
+//批量插入
+func (m *MongodbSim) SaveBulk(c string, doc ...map[string]interface{}) bool {
+	defer catch()
+	m.Open()
+	defer m.Close()
+	coll := m.C.Database(m.DbName).Collection(c)
+	var writes []mongo.WriteModel
+	for _, d := range doc {
+		write := mongo.NewInsertOneModel()
+		write.SetDocument(d)
+		writes = append(writes, write)
+	}
+	br, e := coll.BulkWrite(m.Ctx, writes)
+	if e != nil {
+		b := strings.Index(e.Error(), "duplicate") > -1
+		log.Println("mgo savebulk error:", e.Error())
+		if br != nil {
+			log.Println("mgo savebulk size:", br.InsertedCount)
+		}
+		return b
+	}
+	return true
+}
+
+//批量插入
+func (m *MongodbSim) SaveBulkInterface(c string, doc ...interface{}) bool {
+	defer catch()
+	m.Open()
+	defer m.Close()
+	coll := m.C.Database(m.DbName).Collection(c)
+	var writes []mongo.WriteModel
+	for _, d := range doc {
+		write := mongo.NewInsertOneModel()
+		write.SetDocument(d)
+		writes = append(writes, write)
+	}
+	br, e := coll.BulkWrite(m.Ctx, writes)
+	if e != nil {
+		b := strings.Index(e.Error(), "duplicate") > -1
+		log.Println("mgo SaveBulkInterface error:", e.Error())
+		if br != nil {
+			log.Println("mgo SaveBulkInterface size:", br.InsertedCount)
+		}
+		return b
+	}
+	return true
+}
+
+//按条件统计
+func (m *MongodbSim) Count(c string, q interface{}) int {
+	r, _ := m.CountByErr(c, q)
+	return r
+}
+
+//统计
+func (m *MongodbSim) CountByErr(c string, q interface{}) (int, error) {
+	defer catch()
+	m.Open()
+	defer m.Close()
+	res, err := m.C.Database(m.DbName).Collection(c).CountDocuments(m.Ctx, ObjToM(q))
+	if err != nil {
+		log.Println("统计错误", err.Error())
+		return 0, err
+	} else {
+		return int(res), nil
+	}
+}
+
+//按条件删除
+func (m *MongodbSim) Delete(c string, q interface{}) int64 {
+	defer catch()
+	m.Open()
+	defer m.Close()
+	res, err := m.C.Database(m.DbName).Collection(c).DeleteMany(m.Ctx, ObjToM(q))
+	if err != nil && res == nil {
+		log.Println("删除错误", err.Error())
+	}
+	return res.DeletedCount
+}
+
+//删除对象
+func (m *MongodbSim) Del(c string, q interface{}) bool {
+	defer catch()
+	m.Open()
+	defer m.Close()
+	_, err := m.C.Database(m.DbName).Collection(c).DeleteMany(m.Ctx, ObjToM(q))
+	if err != nil {
+		log.Println("删除错误", err.Error())
+		return false
+	}
+	return true
+}
+
+//删除表
+func (m *MongodbSim) DelColl(c string) bool {
+	defer catch()
+	m.Open()
+	defer m.Close()
+	err := m.C.Database(m.DbName).Collection(c).Drop(m.Ctx)
+	if err != nil {
+		log.Println("删除错误", err.Error())
+		return false
+	}
+	return true
+}
+
+//按条件更新
+func (m *MongodbSim) Update(c string, q, u interface{}, upsert bool, multi bool) bool {
+	defer catch()
+	m.Open()
+	defer m.Close()
+	ct := options.Update()
+	if upsert {
+		ct.SetUpsert(true)
+	}
+	coll := m.C.Database(m.DbName).Collection(c)
+	var err error
+	if multi {
+		_, err = coll.UpdateMany(m.Ctx, ObjToM(q), ObjToM(u), ct)
+	} else {
+		_, err = coll.UpdateOne(m.Ctx, ObjToM(q), ObjToM(u), ct)
+	}
+	if err != nil {
+		log.Println("删除错误", err.Error())
+		return false
+	}
+	return true
+}
+func (m *MongodbSim) UpdateById(c string, id interface{}, set interface{}) bool {
+	defer catch()
+	m.Open()
+	defer m.Close()
+	q := make(map[string]interface{})
+	if sid, ok := id.(string); ok {
+		q["_id"], _ = primitive.ObjectIDFromHex(sid)
+	} else {
+		q["_id"] = id
+	}
+	_, err := m.C.Database(m.DbName).Collection(c).UpdateOne(m.Ctx, q, ObjToM(set))
+	if nil != err {
+		log.Println("UpdateByIdError", err)
+		return false
+	}
+	return true
+}
+
+//批量更新
+func (m *MongodbSim) UpdateBulkAll(db, c string, doc ...[]map[string]interface{}) bool {
+	return m.upSertBulk(db, c, false, doc...)
+}
+
+func (m *MongodbSim) UpdateBulk(c string, doc ...[]map[string]interface{}) bool {
+	return m.UpdateBulkAll(m.DbName, c, doc...)
+}
+
+//批量插入
+func (m *MongodbSim) UpSertBulk(c string, doc ...[]map[string]interface{}) bool {
+	return m.upSertBulk(m.DbName, c, true, doc...)
+}
+
+//批量插入
+func (m *MongodbSim) upSertBulk(db, c string, upsert bool, doc ...[]map[string]interface{}) bool {
+	defer catch()
+	m.Open()
+	defer m.Close()
+	coll := m.C.Database(db).Collection(c)
+	var writes []mongo.WriteModel
+	for _, d := range doc {
+		write := mongo.NewUpdateOneModel()
+		write.SetFilter(d[0])
+		write.SetUpdate(d[1])
+		write.SetUpsert(upsert)
+		writes = append(writes, write)
+	}
+	br, e := coll.BulkWrite(m.Ctx, writes)
+	if e != nil {
+		log.Println("mgo upsert error:", e.Error())
+		return br == nil || br.UpsertedCount == 0
+	}
+	//	else {
+	//		if r.UpsertedCount != int64(len(doc)) {
+	//			log.Println("mgo upsert uncomplete:uc/dc", r.UpsertedCount, len(doc))
+	//		}
+	//		return true
+	//	}
+	return true
+}
+
+//查询单条对象
+func (m *MongodbSim) FindOne(c string, query interface{}) (*map[string]interface{}, bool) {
+	return m.FindOneByField(c, query, nil)
+}
+
+//查询单条对象
+func (m *MongodbSim) FindOneByField(c string, query interface{}, fields interface{}) (*map[string]interface{}, bool) {
+	defer catch()
+	res, ok := m.Find(c, query, nil, fields, true, -1, -1)
+	if nil != res && len(*res) > 0 {
+		return &((*res)[0]), ok
+	}
+	return nil, ok
+}
+
+//查询单条对象
+func (m *MongodbSim) FindById(c string, query string, fields interface{}) (*map[string]interface{}, bool) {
+	defer catch()
+	m.Open()
+	defer m.Close()
+	of := options.FindOne()
+	of.SetProjection(ObjToOth(fields))
+	b := false
+	res := make(map[string]interface{})
+	_id, err := primitive.ObjectIDFromHex(query)
+	if err != nil {
+		log.Println("_id error", err)
+		return &res, b
+	}
+	sr := m.C.Database(m.DbName).Collection(c).FindOne(m.Ctx, map[string]interface{}{"_id": _id}, of)
+	if sr.Err() == nil {
+		b = true
+		sr.Decode(&res)
+	}
+	return &res, b
+}
+
+//底层查询方法
+func (m *MongodbSim) Find(c string, query interface{}, order interface{}, fields interface{}, single bool, start int, limit int) (*[]map[string]interface{}, bool) {
+	defer catch()
+	m.Open()
+	defer m.Close()
+	res := make([]map[string]interface{}, 1)
+	coll := m.C.Database(m.DbName).Collection(c)
+	if single {
+		of := options.FindOne()
+		of.SetProjection(ObjToOth(fields))
+		of.SetSort(ObjToM(order))
+		if sr := coll.FindOne(m.Ctx, ObjToM(query), of); sr.Err() == nil {
+			sr.Decode(&res[0])
+		}
+	} else {
+		of := options.Find()
+		of.SetProjection(ObjToOth(fields))
+		of.SetSort(ObjToM(order))
+		if start > -1 {
+			of.SetSkip(int64(start))
+			of.SetLimit(int64(limit))
+		}
+		cur, err := coll.Find(m.Ctx, ObjToM(query), of)
+		if err == nil && cur.Err() == nil {
+			cur.All(m.Ctx, &res)
+		}
+	}
+	return &res, true
+}
+
+func ObjToOth(query interface{}) *bson.M {
+	return ObjToMQ(query, false)
+}
+func ObjToM(query interface{}) *bson.M {
+	return ObjToMQ(query, true)
+}
+
+//obj(string,M)转M,查询用到
+func ObjToMQ(query interface{}, isQuery bool) *bson.M {
+	data := make(bson.M)
+	defer catch()
+	if s2, ok2 := query.(*map[string]interface{}); ok2 {
+		data = bson.M(*s2)
+	} else if s3, ok3 := query.(*bson.M); ok3 {
+		return s3
+	} else if s3, ok3 := query.(*primitive.M); ok3 {
+		return s3
+	} else if s, ok := query.(string); ok {
+		json.Unmarshal([]byte(strings.Replace(s, "'", "\"", -1)), &data)
+		if ss, oks := data["_id"]; oks && isQuery {
+			switch ss.(type) {
+			case string:
+				data["_id"], _ = primitive.ObjectIDFromHex(ss.(string))
+			case map[string]interface{}:
+				tmp := ss.(map[string]interface{})
+				for k, v := range tmp {
+					tmp[k], _ = primitive.ObjectIDFromHex(v.(string))
+				}
+				data["_id"] = tmp
+			}
+
+		}
+	} else if s1, ok1 := query.(map[string]interface{}); ok1 {
+		data = s1
+	} else if s4, ok4 := query.(bson.M); ok4 {
+		data = s4
+	} else if s4, ok4 := query.(primitive.M); ok4 {
+		data = s4
+	} else {
+		data = nil
+	}
+	return &data
+}
+func intAllDef(num interface{}, defaultNum int) int {
+	if i, ok := num.(int); ok {
+		return int(i)
+	} else if i0, ok0 := num.(int32); ok0 {
+		return int(i0)
+	} else if i1, ok1 := num.(float64); ok1 {
+		return int(i1)
+	} else if i2, ok2 := num.(int64); ok2 {
+		return int(i2)
+	} else if i3, ok3 := num.(float32); ok3 {
+		return int(i3)
+	} else if i4, ok4 := num.(string); ok4 {
+		in, _ := strconv.Atoi(i4)
+		return int(in)
+	} else if i5, ok5 := num.(int16); ok5 {
+		return int(i5)
+	} else if i6, ok6 := num.(int8); ok6 {
+		return int(i6)
+	} else if i7, ok7 := num.(*big.Int); ok7 {
+		in, _ := strconv.Atoi(fmt.Sprint(i7))
+		return int(in)
+	} else if i8, ok8 := num.(*big.Float); ok8 {
+		in, _ := strconv.Atoi(fmt.Sprint(i8))
+		return int(in)
+	} else {
+		return defaultNum
+	}
+}
+
+//出错拦截
+func catch() {
+	if r := recover(); r != nil {
+		log.Println(r)
+		for skip := 0; ; skip++ {
+			_, file, line, ok := runtime.Caller(skip)
+			if !ok {
+				break
+			}
+			go log.Printf("%v,%v\n", file, line)
+		}
+	}
+}
+
+//根据bsonID转string
+func BsonIdToSId(uid interface{}) string {
+	if uid == nil {
+		return ""
+	} else if u, ok := uid.(string); ok {
+		return u
+	} else if u, ok := uid.(primitive.ObjectID); ok {
+		return u.Hex()
+	} else {
+		return ""
+	}
+}
+
+func StringTOBsonId(id string) (bid primitive.ObjectID) {
+	defer catch()
+	if id != "" {
+		bid, _ = primitive.ObjectIDFromHex(id)
+	}
+	return
+}

+ 70 - 0
common_utils/redis/redisloginutil.go

@@ -0,0 +1,70 @@
+package redis
+
+import (
+	"log"
+	"strings"
+	"time"
+
+	redisLogin "github.com/gomodule/redigo/redis"
+)
+
+var RedisLoginPool *redisLogin.Pool
+
+func InitRedisLogin(addrs string) {
+	addr := strings.Split(addrs, ",")
+	for _, v := range addr {
+		saddr := strings.Split(v, "=")
+		if saddr[0] == "login" {
+			RedisLoginPool = &redisLogin.Pool{MaxActive: 10, MaxIdle: 5,
+				IdleTimeout: time.Duration(10) * time.Second, Dial: func() (redisLogin.Conn, error) {
+					c, err := redisLogin.Dial("tcp", saddr[1])
+					if err != nil {
+						return nil, err
+					}
+					return c, nil
+				}}
+		}
+	}
+
+}
+
+//
+func SetLoginVal(key, value string) {
+	conn := RedisLoginPool.Get()
+	defer conn.Close()
+	conn.Do("PUBLISH", key, value)
+}
+
+//
+func GetLoginVal(key string, wxFunc func(wxParams []string) bool) {
+	for {
+		defer catch()
+	L:
+		for {
+			conn := RedisLoginPool.Get()
+			defer conn.Close()
+			if conn.Err() == nil {
+				psc := redisLogin.PubSubConn{Conn: conn}
+				if err := psc.Subscribe(redisLogin.Args{}.AddFlat(key)...); err != nil {
+					log.Println(err)
+				}
+				for {
+					msg := psc.Receive()
+					//					go func(msg interface{}) {
+					switch n := msg.(type) {
+					case error:
+						log.Println("wxlogin err", msg)
+						break L
+					case redisLogin.Message:
+						res := string(n.Data)
+						param := strings.Split(res, ",")
+						go wxFunc(param)
+					}
+					//					}(msg)
+				}
+			}
+			time.Sleep(2 * time.Second)
+		}
+		time.Sleep(1 * time.Second)
+	}
+}

+ 517 - 0
common_utils/redis/redisutil.go

@@ -0,0 +1,517 @@
+package redis
+
+import (
+	"encoding/json"
+	"errors"
+	redigo "github.com/gomodule/redigo/redis"
+	"log"
+	"runtime"
+	"strings"
+	"time"
+)
+
+var RedisPool map[string]*redigo.Pool
+
+//初始化redis 1为多个连接池,2为共用一个连接池
+func InitRedis(addrs string) {
+	InitRedisBySize(addrs, 300, 30, 240)
+}
+
+func InitRedisBySize(addrs string, maxSize, maxIdle, timeout int) {
+	RedisPool = map[string]*redigo.Pool{}
+	addr := strings.Split(addrs, ",")
+	for _, v := range addr {
+		saddr := strings.Split(v, "=")
+		RedisPool[saddr[0]] = &redigo.Pool{MaxActive: maxSize, MaxIdle: maxIdle,
+			IdleTimeout: time.Duration(timeout) * time.Second, Dial: func() (redigo.Conn, error) {
+				c, err := redigo.Dial("tcp", saddr[1])
+				if err != nil {
+					return nil, err
+				}
+				return c, nil
+			}}
+	}
+}
+
+//分流redis`
+//并存入字符串缓存
+func PutKV(key string, obj interface{}) bool {
+	return Put("other", key, obj, -1)
+}
+func PutCKV(code, key string, obj interface{}) bool {
+	return Put(code, key, obj, -1)
+}
+func Put(code, key string, obj interface{}, timeout int) bool {
+	b := false
+	defer catch()
+	conn := RedisPool[code].Get()
+	defer conn.Close()
+	var err error
+	_obj, _err := json.Marshal(obj)
+	if _err != nil {
+		log.Println("redisutil-SET-序列化出错Error", _err)
+		return b
+	}
+	if timeout < 1 {
+		_, err = conn.Do("SET", key, _obj)
+	} else {
+		_, err = conn.Do("SET", key, _obj, "EX", timeout)
+	}
+	if nil != err {
+		log.Println("redisutil-SETError-put", err)
+	} else {
+		b = true
+	}
+	return b
+}
+
+func BulkPut(code string, timeout int, obj ...interface{}) bool {
+	b := false
+	defer catch()
+	conn := RedisPool[code].Get()
+	defer conn.Close()
+	var err error
+	for _, _tmp := range obj {
+		tmp, ok := _tmp.([]interface{})
+		if ok && len(tmp) == 2 {
+			key, kok := tmp[0].(string)
+			if kok && key != "" {
+				_obj, _err := json.Marshal(tmp[1])
+				if _err != nil {
+					log.Println("redisutil-SET-序列化出错Error", _err)
+					return b
+				}
+				if timeout < 1 {
+					_, err = conn.Do("SET", key, _obj)
+				} else {
+					_, err = conn.Do("SET", key, _obj, "EX", timeout)
+				}
+			}
+		}
+	}
+	if nil != err {
+		b = false
+		log.Println("redisutil-SETError-put", err)
+	} else {
+		b = b && true
+	}
+	return b
+}
+
+//直接存字节流
+func PutBytes(code, key string, data *[]byte, timeout int) (err error) {
+	defer catch()
+
+	conn := RedisPool[code].Get()
+	defer conn.Close()
+
+	if timeout < 1 {
+		_, err = conn.Do("SET", key, *data)
+	} else {
+		_, err = conn.Do("SET", key, *data, "EX", timeout)
+	}
+	if nil != err {
+		log.Println("redisutil-SETError", err)
+	}
+	return
+}
+
+//设置超时时间,单位秒
+func SetExpire(code, key string, expire int) error {
+	defer catch()
+
+	conn := RedisPool[code].Get()
+	defer conn.Close()
+	_, err := conn.Do("expire", key, expire)
+	return err
+}
+
+//判断一个key是否存在
+func Exists(code, key string) (bool, error) {
+	defer catch()
+
+	conn := RedisPool[code].Get()
+	defer conn.Close()
+	repl, err := conn.Do("exists", key)
+	ret, _ := redigo.Int(repl, err)
+	return ret == 1, err
+}
+
+//获取string
+func GetStr(code, key string) string {
+	res := Get(code, key)
+	str, _ := res.(string)
+	return str
+}
+
+//获取int
+func GetInt(code, key string) int {
+	result, _ := GetNewInt(code, key)
+	return result
+}
+
+func GetNewInt(code, key string) (int, error) {
+	var res interface{}
+	err := GetNewInterface(code, key, &res)
+	var result int
+	if str, ok := res.(float64); ok {
+		result = int(str)
+	}
+	return result, err
+}
+
+//取得字符串,支持变参,2个 (key,code),返回后自己断言
+func Get(code, key string) (result interface{}) {
+	GetInterface(code, key, &result)
+	return
+}
+
+func GetInterface(code, key string, result interface{}) {
+	GetNewInterface(code, key, result)
+}
+
+func GetNewInterface(code, key string, result interface{}) error {
+	defer catch()
+	conn := RedisPool[code].Get()
+	defer conn.Close()
+	ret, err := conn.Do("GET", key)
+	if nil != err {
+		log.Println("redisutil-GetError", err)
+	} else {
+		var ok bool
+		var res []byte
+		if res, ok = ret.([]byte); ok {
+			err = json.Unmarshal(res, result)
+			if err != nil {
+				log.Println("Get ERROR:", err.Error())
+			}
+		}
+	}
+	return err
+}
+
+//直接返回字节流
+func GetBytes(code, key string) (ret *[]byte, err error) {
+	defer catch()
+
+	conn := RedisPool[code].Get()
+	defer conn.Close()
+	var r interface{}
+	r, err = conn.Do("GET", key)
+	if err != nil {
+		log.Println("redisutil-GetBytesError", err)
+	} else {
+		if tmp, ok := r.([]byte); ok {
+			ret = &tmp
+		} else {
+			err = errors.New("redis返回数据格式不对")
+		}
+	}
+	return
+}
+func GetNewBytes(code, key string) (ret *[]byte, err error) {
+	defer catch()
+	redisPool := RedisPool[code]
+	if redisPool == nil {
+		err = errors.New("redis code " + code + " is nil")
+		log.Println("redisutil-GetNewBytesError", err)
+		return
+	}
+	conn := redisPool.Get()
+	defer conn.Close()
+	var r interface{}
+	r, err = conn.Do("GET", key)
+	if err != nil {
+		log.Println("redisutil-GetNewBytesError", err)
+	} else if r != nil {
+		if tmp, ok := r.([]byte); ok {
+			ret = &tmp
+		}
+	}
+	return
+}
+
+//删所有key
+func FlushDB(code string) bool {
+	b := false
+	defer catch()
+
+	conn := RedisPool[code].Get()
+	defer conn.Close()
+
+	var err error
+	_, err = conn.Do("FLUSHDB")
+	if nil != err {
+		log.Println("redisutil-FLUSHDBError", err)
+	} else {
+		b = true
+	}
+	return b
+}
+
+//支持删除多个key
+func Del(code string, key ...interface{}) bool {
+	defer catch()
+	b := false
+	conn := RedisPool[code].Get()
+	defer conn.Close()
+
+	var err error
+	_, err = conn.Do("DEL", key...)
+	if nil != err {
+		log.Println("redisutil-DELError", err)
+	} else {
+		b = true
+	}
+	return b
+}
+
+/**
+func DelKey(key ...interface{}) {
+	defer func() {
+		if r := recover(); r != nil {
+			log.Println("[E]", r)
+			for skip := 1; ; skip++ {
+				_, file, line, ok := runtime.Caller(skip)
+				if !ok {
+					break
+				}
+				go log.Printf("%v,%v\n", file, line)
+			}
+		}
+	}()
+	for i := 0; i < len(RedisPool); i++ {
+		delByNum(i, key...)
+	}
+
+}
+**/
+
+/**
+func delByNum(n int, key ...interface{}) {
+	defer func() {
+		if r := recover(); r != nil {
+			log.Println("[E]", r)
+			for skip := 1; ; skip++ {
+				_, file, line, ok := runtime.Caller(skip)
+				if !ok {
+					break
+				}
+				go log.Printf("%v,%v\n", file, line)
+			}
+		}
+	}()
+	i := 0
+	for _, v := range RedisPool {
+		if i == n {
+			conn := v.Get()
+			defer conn.Close()
+			conn.Do("DEL", key...)
+			break
+		}
+		i++
+	}
+}
+**/
+
+//根据代码和前辍key删除多个
+func DelByCodePattern(code, key string) {
+	defer catch()
+
+	conn := RedisPool[code].Get()
+	defer conn.Close()
+	ret, err := conn.Do("KEYS", key)
+	var result []interface{}
+	if nil != err {
+		log.Println("redisutil-GetError", err)
+	} else {
+		result = ret.([]interface{})
+		for k := 0; k < len(result); k++ {
+			conn.Do("DEL", string(result[k].([]uint8)))
+		}
+	}
+}
+
+/**
+func DelByPattern(key string) {
+	defer func() {
+		if r := recover(); r != nil {
+			log.Println("[E]", r)
+			for skip := 1; ; skip++ {
+				_, file, line, ok := runtime.Caller(skip)
+				if !ok {
+					break
+				}
+				go log.Printf("%v,%v\n", file, line)
+			}
+		}
+	}()
+	i := 0
+	for _, v := range RedisPool {
+		conn := v.Get()
+		defer conn.Close()
+		ret, err := conn.Do("KEYS", key)
+		var result []interface{}
+		if nil != err {
+			log.Println("redisutil-GetError", err)
+		} else {
+			result = ret.([]interface{})
+			for k := 0; k < len(result); k++ {
+				delByNum(i, string(result[k].([]uint8)))
+			}
+		}
+		i++
+	}
+
+}
+**/
+//自增计数器
+func Incr(code, key string) int64 {
+	defer catch()
+	conn := RedisPool[code].Get()
+	defer conn.Close()
+	ret, err := conn.Do("INCR", key)
+	if nil != err {
+		log.Println("redisutil-INCR-Error", err)
+	} else {
+		if res, ok := ret.(int64); ok {
+			return res
+		} else {
+			return 0
+		}
+	}
+	return 0
+}
+
+//自减
+func Decrby(code, key string, val int) int64 {
+	defer catch()
+	conn := RedisPool[code].Get()
+	defer conn.Close()
+	ret, err := conn.Do("DECRBY", key, val)
+	if nil != err {
+		log.Println("redisutil-DECR-Error", err)
+	} else {
+		if res, ok := ret.(int64); ok {
+			return res
+		} else {
+			return 0
+		}
+	}
+	return 0
+}
+
+//根据正则去取
+func GetKeysByPattern(code, key string) []interface{} {
+	defer catch()
+	conn := RedisPool[code].Get()
+	defer conn.Close()
+	ret, err := conn.Do("KEYS", key)
+	if nil != err {
+		log.Println("redisutil-GetKeysError", err)
+		return nil
+	} else {
+		res, _ := ret.([]interface{})
+		return res
+	}
+}
+
+//批量取多个key
+func Mget(code string, key []string) []interface{} {
+	defer catch()
+	conn := RedisPool[code].Get()
+	defer conn.Close()
+	interfaceKeys := make([]interface{}, len(key))
+	for n, k := range key {
+		interfaceKeys[n] = k
+	}
+	ret, err := conn.Do("MGET", interfaceKeys...)
+	if nil != err {
+		log.Println("redisutil-MgetError", err)
+		return nil
+	} else {
+		res, _ := ret.([]interface{})
+		return res
+	}
+}
+
+//取出并删除Key
+func Pop(code string, key string) (result interface{}) {
+	defer catch()
+	conn := RedisPool[code].Get()
+	defer conn.Close()
+	ret, err := conn.Do("GET", key)
+	if nil != err {
+		log.Println("redisutil-PopError", err)
+	} else {
+		var ok bool
+		var res []byte
+		if res, ok = ret.([]byte); ok {
+			err = json.Unmarshal(res, &result)
+			if err != nil {
+				log.Println("Poperr", err)
+			}
+		}
+		conn.Do("DEL", key)
+	}
+	return
+}
+
+//list操作
+func LPOP(code, list string) (result interface{}) {
+	defer catch()
+	conn := RedisPool[code].Get()
+	defer conn.Close()
+	ret, err := conn.Do("LPOP", list)
+	if nil != err {
+		log.Println("redisutil-LPopError", err)
+	} else {
+		if res, ok := ret.([]byte); ok {
+			err = json.Unmarshal(res, &result)
+			log.Println(err)
+		}
+	}
+	return
+}
+
+func RPUSH(code, list string, val interface{}) bool {
+	defer catch()
+	conn := RedisPool[code].Get()
+	defer conn.Close()
+	_obj, _ := json.Marshal(val)
+	_, err := conn.Do("RPUSH", list, _obj)
+	if nil != err {
+		log.Println("redisutil-RPUSHError", err)
+		return false
+	}
+	return true
+}
+
+func LLEN(code, list string) int64 {
+	defer catch()
+	conn := RedisPool[code].Get()
+	defer conn.Close()
+	ret, err := conn.Do("LLEN", list)
+	if nil != err {
+		log.Println("redisutil-LLENError", err)
+		return 0
+	}
+	if res, ok := ret.(int64); ok {
+		return res
+	} else {
+		return 0
+	}
+}
+
+func catch() {
+	if r := recover(); r != nil {
+		log.Println(r)
+		for skip := 0; ; skip++ {
+			_, file, line, ok := runtime.Caller(skip)
+			if !ok {
+				break
+			}
+			go log.Printf("%v,%v\n", file, line)
+		}
+	}
+}

+ 86 - 0
common_utils/redis/redisutil_test.go

@@ -0,0 +1,86 @@
+package redis
+
+import (
+	"fmt"
+	"log"
+	"testing"
+	"time"
+)
+
+func Test_redis(t *testing.T) {
+	InitRedis("192.168.3.142:6379,192.168.3.142:7379")
+
+	log.Println(PutKV("key-BBB", "v-oooo"))
+	log.Println(GetStr("", "key-BBB"))
+
+	log.Println(PutCKV("code-K", "key-BBB2", 123))
+	log.Println(GetInt("code-K", "key-BBB2"))
+
+	log.Println(Put("codeA", "BBB", "oooo", 0))
+	//log.Println(Del("codeA", "AAA"))
+	log.Println(GetStr("codeA", "AAA"))
+
+	log.Println(Put("code", "Key", &map[string]string{"name": "张三"}, 0))
+	log.Println(Get("code", "Key"))
+}
+
+func Test_redisMorePut(t *testing.T) {
+	//by := []byte("dddddddddddd555d")
+
+	//InitRedis("enterprise=192.168.3.14:1379,service=192.168.3.14:2379,other=192.168.3.14:3379")
+	InitRedis("sso=192.168.3.14:1379")
+	//PutBytes("enterprise", "A", &by, 0)
+	//Put("sso", "new_1070776706", "obEpLuMMDUYUM-zlzCbQ0MbuQOzc", 180)
+	Put("sso", "1903540616", "oJULtwzXo6EFV1Ah-XeyRBimXGM8", 180)
+	//log.Println(Get("enterprise", "A"))
+	//GetMoreKey("ent*")
+}
+
+func Test_other(t *testing.T) {
+	//s := `[{"S_remark":"kokopkopkop","s_pic":"/upload/2015/10/16/2015101611194101018681.jpg","o_extend":{"k1":"ioio","k7":"000"}}]`
+	//mo := []map[string]interface{}{}
+	//json.Unmarshal([]byte(s), &mo)
+	//log.Println(mo)
+
+	pools := make(chan bool, 1)
+	pool := make(chan bool, 2)
+	for i := 0; i < 1000; i++ {
+		go func(i int) {
+			pool <- true
+			defer func() {
+				<-pool
+			}()
+			log.Println("--", i)
+			time.Sleep(5 * time.Millisecond)
+
+		}(i)
+	}
+	<-pools
+}
+
+func TestIncr(t *testing.T) {
+	InitRedis("other=192.168.3.14:1379")
+	//log.Println(GetBytes("sso", "p_share_3200000006"))
+	Put("other", "test", "12212212", 50000)
+}
+
+func Test_Pop(t *testing.T) {
+	InitRedis("sso=192.168.3.207:1601")
+	m := "aaaa"
+	Put("sso", "aaa", m, 1000)
+	log.Println(Pop("sso", "aaa"))
+}
+
+func Test_MGET(t *testing.T) {
+	InitRedis("sso=192.168.3.207:3378")
+	InitRedis("sso=192.168.3.207:1711")
+	n := 0
+	for {
+		n++
+		Put("sso", fmt.Sprintf("%d_1", n), n, 3600)
+		time.Sleep(8 * time.Second)
+
+	}
+	res := Mget("sso", []string{"pn_金业街(西自由街-虎泉路)道路建设工程"})
+	log.Println(res)
+}

+ 135 - 0
common_utils/simple_encrypt.go

@@ -0,0 +1,135 @@
+package util
+
+import (
+	"bytes"
+	"encoding/base64"
+	"encoding/hex"
+)
+
+/**
+加解密
+	数据结构
+	密文+sha32校验
+*/
+//
+type SimpleEncrypt struct {
+	Key string //加解密用到的key(加密key索引)+
+}
+
+//计算检验和
+func (s *SimpleEncrypt) calaCheckCode(src []byte) []byte {
+	check := 0
+	for i := 0; i < len(src); i++ {
+		check += int(src[i])
+	}
+	return []byte{byte((check >> 8) & 0xff), byte(check & 0xff)}
+}
+
+//验证数据有效性
+func (s *SimpleEncrypt) verify(src []byte) bool {
+	v := s.calaCheckCode(src[:len(src)-2])
+	return bytes.Equal(v, src[len(src)-2:])
+}
+
+//加密String
+func (s *SimpleEncrypt) EncodeString(str string) string {
+	data := []byte(str)
+	s.doEncode(data)
+	return base64.StdEncoding.EncodeToString(data)
+}
+
+//加密String,ByCheck
+func (s *SimpleEncrypt) EncodeStringByCheck(str string) string {
+	data := []byte(str)
+	s.doEncode(data)
+	v := s.calaCheckCode(data)
+	data = append(data, v...)
+	return base64.StdEncoding.EncodeToString(data)
+}
+
+//
+func (s *SimpleEncrypt) Encode2Hex(str string) string {
+	data := []byte(str)
+	s.doEncode(data)
+	return hex.EncodeToString(data)
+}
+func (s *SimpleEncrypt) Encode2HexByCheck(str string) string {
+	data := []byte(str)
+	s.doEncode(data)
+	v := s.calaCheckCode(data)
+	data = append(data, v...)
+	return hex.EncodeToString(data)
+}
+
+//解密String
+func (s *SimpleEncrypt) DecodeString(str string) string {
+	data, _ := base64.StdEncoding.DecodeString(str)
+	s.doEncode(data)
+	return string(data)
+}
+
+//解密String,ByCheck
+func (s *SimpleEncrypt) DecodeStringByCheck(str string) string {
+	data, _ := base64.StdEncoding.DecodeString(str)
+	if len(data) < 2 || !s.verify(data) {
+		return ""
+	}
+	data = data[:len(data)-2]
+	s.doEncode(data)
+	return string(data)
+}
+
+//
+func (s *SimpleEncrypt) Decode4Hex(str string) string {
+	data, _ := hex.DecodeString(str)
+	s.doEncode(data)
+	return string(data)
+}
+func (s *SimpleEncrypt) Decode4HexByCheck(str string) string {
+	data, _ := hex.DecodeString(str)
+	if len(data) < 2 || !s.verify(data) {
+		return ""
+	}
+	data = data[:len(data)-2]
+	s.doEncode(data)
+	return string(data)
+}
+
+//加密
+func (s *SimpleEncrypt) Encode(data []byte) {
+	s.doEncode(data)
+
+}
+
+func (s *SimpleEncrypt) EncodeByCheck(data []byte) {
+	s.doEncode(data)
+	v := s.calaCheckCode(data)
+	data = append(data, v...)
+}
+
+//解密
+func (s *SimpleEncrypt) Decode(data []byte) {
+	s.doEncode(data)
+}
+
+//解密
+func (s *SimpleEncrypt) DecodeByCheck(data []byte) {
+	if len(data) < 2 || !s.verify(data) {
+		data = []byte{}
+		return
+	}
+	s.doEncode(data)
+}
+
+func (s *SimpleEncrypt) doEncode(bs []byte) {
+	tmp := []byte(s.Key)
+THEFOR:
+	for i := 0; i < len(bs); {
+		for j := 0; j < len(tmp); j, i = j+1, i+1 {
+			if i >= len(bs) {
+				break THEFOR
+			}
+			bs[i] = bs[i] ^ tmp[j]
+		}
+	}
+}

+ 64 - 0
common_utils/validator/converter.go

@@ -0,0 +1,64 @@
+package govalidator
+
+import (
+	"encoding/json"
+	"fmt"
+	"reflect"
+	"strconv"
+)
+
+// ToString convert the input to a string.
+func ToString(obj interface{}) string {
+	res := fmt.Sprintf("%v", obj)
+	return res
+}
+
+// ToJSON convert the input to a valid JSON string
+func ToJSON(obj interface{}) (string, error) {
+	res, err := json.Marshal(obj)
+	if err != nil {
+		res = []byte("")
+	}
+	return string(res), err
+}
+
+// ToFloat convert the input string to a float, or 0.0 if the input is not a float.
+func ToFloat(str string) (float64, error) {
+	res, err := strconv.ParseFloat(str, 64)
+	if err != nil {
+		res = 0.0
+	}
+	return res, err
+}
+
+// ToInt convert the input string or any int type to an integer type 64, or 0 if the input is not an integer.
+func ToInt(value interface{}) (res int64, err error) {
+	val := reflect.ValueOf(value)
+
+	switch value.(type) {
+	case int, int8, int16, int32, int64:
+		res = val.Int()
+	case uint, uint8, uint16, uint32, uint64:
+		res = int64(val.Uint())
+	case string:
+		if IsInt(val.String()) {
+			res, err = strconv.ParseInt(val.String(), 0, 64)
+			if err != nil {
+				res = 0
+			}
+		} else {
+			err = fmt.Errorf("ToInt: invalid numeric format %g", value)
+			res = 0
+		}
+	default:
+		err = fmt.Errorf("ToInt: unknown interface type %T", value)
+		res = 0
+	}
+
+	return
+}
+
+// ToBoolean convert the input string to a boolean.
+func ToBoolean(str string) (bool, error) {
+	return strconv.ParseBool(str)
+}

+ 47 - 0
common_utils/validator/error.go

@@ -0,0 +1,47 @@
+package govalidator
+
+import (
+	"sort"
+	"strings"
+)
+
+// Errors is an array of multiple errors and conforms to the error interface.
+type Errors []error
+
+// Errors returns itself.
+func (es Errors) Errors() []error {
+	return es
+}
+
+func (es Errors) Error() string {
+	var errs []string
+	for _, e := range es {
+		errs = append(errs, e.Error())
+	}
+	sort.Strings(errs)
+	return strings.Join(errs, ";")
+}
+
+// Error encapsulates a name, an error and whether there's a custom error message or not.
+type Error struct {
+	Name                     string
+	Err                      error
+	CustomErrorMessageExists bool
+
+	// Validator indicates the name of the validator that failed
+	Validator string
+	Path      []string
+}
+
+func (e Error) Error() string {
+	if e.CustomErrorMessageExists {
+		return e.Err.Error()
+	}
+
+	errName := e.Name
+	if len(e.Path) > 0 {
+		errName = strings.Join(append(e.Path, e.Name), ".")
+	}
+
+	return errName + ": " + e.Err.Error()
+}

+ 96 - 0
common_utils/validator/numerics.go

@@ -0,0 +1,96 @@
+package govalidator
+
+import (
+	"math"
+)
+
+// Abs returns absolute value of number
+func Abs(value float64) float64 {
+	return math.Abs(value)
+}
+
+// Sign returns signum of number: 1 in case of value > 0, -1 in case of value < 0, 0 otherwise
+func Sign(value float64) float64 {
+	if value > 0 {
+		return 1
+	} else if value < 0 {
+		return -1
+	} else {
+		return 0
+	}
+}
+
+// IsNegative returns true if value < 0
+func IsNegative(value float64) bool {
+	return value < 0
+}
+
+// IsPositive returns true if value > 0
+func IsPositive(value float64) bool {
+	return value > 0
+}
+
+// IsNonNegative returns true if value >= 0
+func IsNonNegative(value float64) bool {
+	return value >= 0
+}
+
+// IsNonPositive returns true if value <= 0
+func IsNonPositive(value float64) bool {
+	return value <= 0
+}
+
+// InRangeInt returns true if value lies between left and right border
+func InRangeInt(value, left, right interface{}) bool {
+	value64, _ := ToInt(value)
+	left64, _ := ToInt(left)
+	right64, _ := ToInt(right)
+	if left64 > right64 {
+		left64, right64 = right64, left64
+	}
+	return value64 >= left64 && value64 <= right64
+}
+
+// InRangeFloat32 returns true if value lies between left and right border
+func InRangeFloat32(value, left, right float32) bool {
+	if left > right {
+		left, right = right, left
+	}
+	return value >= left && value <= right
+}
+
+// InRangeFloat64 returns true if value lies between left and right border
+func InRangeFloat64(value, left, right float64) bool {
+	if left > right {
+		left, right = right, left
+	}
+	return value >= left && value <= right
+}
+
+// InRange returns true if value lies between left and right border, generic type to handle int, float32, float64 and string.
+// All types must the same type.
+// False if value doesn't lie in range or if it incompatible or not comparable
+func InRange(value interface{}, left interface{}, right interface{}) bool {
+	switch value.(type) {
+	case int:
+		return InRangeInt(value.(int), left.(int), right.(int))
+	case float32:
+		return InRangeFloat32(value.(float32), left.(float32), right.(float32))
+	case float64:
+		return InRangeFloat64(value.(float64), left.(float64), right.(float64))
+	case string:
+		return value.(string) >= left.(string) && value.(string) <= right.(string)
+	default:
+		return false
+	}
+}
+
+// IsWhole returns true if value is whole number
+func IsWhole(value float64) bool {
+	return math.Remainder(value, 1) == 0
+}
+
+// IsNatural returns true if value is natural number (positive and whole)
+func IsNatural(value float64) bool {
+	return IsWhole(value) && IsPositive(value)
+}

+ 107 - 0
common_utils/validator/patterns.go

@@ -0,0 +1,107 @@
+package govalidator
+
+import "regexp"
+
+// Basic regular expressions for validating strings
+const (
+	Email             string = "^(((([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$"
+	CreditCard        string = "^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11}|6[27][0-9]{14})$"
+	ISBN10            string = "^(?:[0-9]{9}X|[0-9]{10})$"
+	ISBN13            string = "^(?:[0-9]{13})$"
+	UUID3             string = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$"
+	UUID4             string = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
+	UUID5             string = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
+	UUID              string = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
+	Alpha             string = "^[a-zA-Z]+$"
+	Alphanumeric      string = "^[a-zA-Z0-9]+$"
+	Numeric           string = "^[0-9]+$"
+	Int               string = "^(?:[-+]?(?:0|[1-9][0-9]*))$"
+	Float             string = "^(?:[-+]?(?:[0-9]+))?(?:\\.[0-9]*)?(?:[eE][\\+\\-]?(?:[0-9]+))?$"
+	Hexadecimal       string = "^[0-9a-fA-F]+$"
+	Hexcolor          string = "^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
+	RGBcolor          string = "^rgb\\(\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*\\)$"
+	ASCII             string = "^[\x00-\x7F]+$"
+	Multibyte         string = "[^\x00-\x7F]"
+	FullWidth         string = "[^\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]"
+	HalfWidth         string = "[\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]"
+	Base64            string = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$"
+	PrintableASCII    string = "^[\x20-\x7E]+$"
+	DataURI           string = "^data:.+\\/(.+);base64$"
+	MagnetURI         string = "^magnet:\\?xt=urn:[a-zA-Z0-9]+:[a-zA-Z0-9]{32,40}&dn=.+&tr=.+$"
+	Latitude          string = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$"
+	Longitude         string = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$"
+	DNSName           string = `^([a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62}){1}(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*[\._]?$`
+	IP                string = `(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))`
+	URLSchema         string = `((ftp|tcp|udp|wss?|https?):\/\/)`
+	URLUsername       string = `(\S+(:\S*)?@)`
+	URLPath           string = `((\/|\?|#)[^\s]*)`
+	URLPort           string = `(:(\d{1,5}))`
+	URLIP             string = `([1-9]\d?|1\d\d|2[01]\d|22[0-3]|24\d|25[0-5])(\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-5]))`
+	URLSubdomain      string = `((www\.)|([a-zA-Z0-9]+([-_\.]?[a-zA-Z0-9])*[a-zA-Z0-9]\.[a-zA-Z0-9]+))`
+	URL                      = `^` + URLSchema + `?` + URLUsername + `?` + `((` + URLIP + `|(\[` + IP + `\])|(([a-zA-Z0-9]([a-zA-Z0-9-_]+)?[a-zA-Z0-9]([-\.][a-zA-Z0-9]+)*)|(` + URLSubdomain + `?))?(([a-zA-Z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-zA-Z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-zA-Z\x{00a1}-\x{ffff}]{1,}))?))\.?` + URLPort + `?` + URLPath + `?$`
+	SSN               string = `^\d{3}[- ]?\d{2}[- ]?\d{4}$`
+	WinPath           string = `^[a-zA-Z]:\\(?:[^\\/:*?"<>|\r\n]+\\)*[^\\/:*?"<>|\r\n]*$`
+	UnixPath          string = `^(/[^/\x00]*)+/?$`
+	Semver            string = "^v?(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)(-(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(\\.(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\\+[0-9a-zA-Z-]+(\\.[0-9a-zA-Z-]+)*)?$"
+	tagName           string = "valid"
+	hasLowerCase      string = ".*[[:lower:]]"
+	hasUpperCase      string = ".*[[:upper:]]"
+	hasWhitespace     string = ".*[[:space:]]"
+	hasWhitespaceOnly string = "^[[:space:]]+$"
+	IMEI              string = "^[0-9a-f]{14}$|^\\d{15}$|^\\d{18}$"
+	IMSI              string = "^\\d{14,15}$"
+)
+
+// Used by IsFilePath func
+const (
+	// Unknown is unresolved OS type
+	Unknown = iota
+	// Win is Windows type
+	Win
+	// Unix is *nix OS types
+	Unix
+)
+
+var (
+	userRegexp          = regexp.MustCompile("^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~.-]+$")
+	hostRegexp          = regexp.MustCompile("^[^\\s]+\\.[^\\s]+$")
+	userDotRegexp       = regexp.MustCompile("(^[.]{1})|([.]{1}$)|([.]{2,})")
+	rxEmail             = regexp.MustCompile(Email)
+	rxCreditCard        = regexp.MustCompile(CreditCard)
+	rxISBN10            = regexp.MustCompile(ISBN10)
+	rxISBN13            = regexp.MustCompile(ISBN13)
+	rxUUID3             = regexp.MustCompile(UUID3)
+	rxUUID4             = regexp.MustCompile(UUID4)
+	rxUUID5             = regexp.MustCompile(UUID5)
+	rxUUID              = regexp.MustCompile(UUID)
+	rxAlpha             = regexp.MustCompile(Alpha)
+	rxAlphanumeric      = regexp.MustCompile(Alphanumeric)
+	rxNumeric           = regexp.MustCompile(Numeric)
+	rxInt               = regexp.MustCompile(Int)
+	rxFloat             = regexp.MustCompile(Float)
+	rxHexadecimal       = regexp.MustCompile(Hexadecimal)
+	rxHexcolor          = regexp.MustCompile(Hexcolor)
+	rxRGBcolor          = regexp.MustCompile(RGBcolor)
+	rxASCII             = regexp.MustCompile(ASCII)
+	rxPrintableASCII    = regexp.MustCompile(PrintableASCII)
+	rxMultibyte         = regexp.MustCompile(Multibyte)
+	rxFullWidth         = regexp.MustCompile(FullWidth)
+	rxHalfWidth         = regexp.MustCompile(HalfWidth)
+	rxBase64            = regexp.MustCompile(Base64)
+	rxDataURI           = regexp.MustCompile(DataURI)
+	rxMagnetURI         = regexp.MustCompile(MagnetURI)
+	rxLatitude          = regexp.MustCompile(Latitude)
+	rxLongitude         = regexp.MustCompile(Longitude)
+	rxDNSName           = regexp.MustCompile(DNSName)
+	rxURL               = regexp.MustCompile(URL)
+	rxSSN               = regexp.MustCompile(SSN)
+	rxWinPath           = regexp.MustCompile(WinPath)
+	rxUnixPath          = regexp.MustCompile(UnixPath)
+	rxSemver            = regexp.MustCompile(Semver)
+	rxHasLowerCase      = regexp.MustCompile(hasLowerCase)
+	rxHasUpperCase      = regexp.MustCompile(hasUpperCase)
+	rxHasWhitespace     = regexp.MustCompile(hasWhitespace)
+	rxHasWhitespaceOnly = regexp.MustCompile(hasWhitespaceOnly)
+	rxIMEI              = regexp.MustCompile(IMEI)
+	rxIMSI              = regexp.MustCompile(IMSI)
+)

+ 655 - 0
common_utils/validator/types.go

@@ -0,0 +1,655 @@
+package govalidator
+
+import (
+	"reflect"
+	"regexp"
+	"sort"
+	"sync"
+)
+
+// Validator is a wrapper for a validator function that returns bool and accepts string.
+type Validator func(str string) bool
+
+// CustomTypeValidator is a wrapper for validator functions that returns bool and accepts any type.
+// The second parameter should be the context (in the case of validating a struct: the whole object being validated).
+type CustomTypeValidator func(i interface{}, o interface{}) bool
+
+// ParamValidator is a wrapper for validator functions that accept additional parameters.
+type ParamValidator func(str string, params ...string) bool
+
+// InterfaceParamValidator is a wrapper for functions that accept variants parameters for an interface value
+type InterfaceParamValidator func(in interface{}, params ...string) bool
+type tagOptionsMap map[string]tagOption
+
+func (t tagOptionsMap) orderedKeys() []string {
+	var keys []string
+	for k := range t {
+		keys = append(keys, k)
+	}
+
+	sort.Slice(keys, func(a, b int) bool {
+		return t[keys[a]].order < t[keys[b]].order
+	})
+
+	return keys
+}
+
+type tagOption struct {
+	name               string
+	customErrorMessage string
+	order              int
+}
+
+// UnsupportedTypeError is a wrapper for reflect.Type
+type UnsupportedTypeError struct {
+	Type reflect.Type
+}
+
+// stringValues is a slice of reflect.Value holding *reflect.StringValue.
+// It implements the methods to sort by string.
+type stringValues []reflect.Value
+
+// InterfaceParamTagMap is a map of functions accept variants parameters for an interface value
+var InterfaceParamTagMap = map[string]InterfaceParamValidator{
+	"type": IsType,
+}
+
+// InterfaceParamTagRegexMap maps interface param tags to their respective regexes.
+var InterfaceParamTagRegexMap = map[string]*regexp.Regexp{
+	"type": regexp.MustCompile(`^type\((.*)\)$`),
+}
+
+// ParamTagMap is a map of functions accept variants parameters
+var ParamTagMap = map[string]ParamValidator{
+	"length":          ByteLength,
+	"range":           Range,
+	"runelength":      RuneLength,
+	"stringlength":    StringLength,
+	"matches":         StringMatches,
+	"in":              IsInRaw,
+	"rsapub":          IsRsaPub,
+	"minstringlength": MinStringLength,
+	"maxstringlength": MaxStringLength,
+}
+
+// ParamTagRegexMap maps param tags to their respective regexes.
+var ParamTagRegexMap = map[string]*regexp.Regexp{
+	"range":           regexp.MustCompile("^range\\((\\d+)\\|(\\d+)\\)$"),
+	"length":          regexp.MustCompile("^length\\((\\d+)\\|(\\d+)\\)$"),
+	"runelength":      regexp.MustCompile("^runelength\\((\\d+)\\|(\\d+)\\)$"),
+	"stringlength":    regexp.MustCompile("^stringlength\\((\\d+)\\|(\\d+)\\)$"),
+	"in":              regexp.MustCompile(`^in\((.*)\)`),
+	"matches":         regexp.MustCompile(`^matches\((.+)\)$`),
+	"rsapub":          regexp.MustCompile("^rsapub\\((\\d+)\\)$"),
+	"minstringlength": regexp.MustCompile("^minstringlength\\((\\d+)\\)$"),
+	"maxstringlength": regexp.MustCompile("^maxstringlength\\((\\d+)\\)$"),
+}
+
+type customTypeTagMap struct {
+	validators map[string]CustomTypeValidator
+
+	sync.RWMutex
+}
+
+func (tm *customTypeTagMap) Get(name string) (CustomTypeValidator, bool) {
+	tm.RLock()
+	defer tm.RUnlock()
+	v, ok := tm.validators[name]
+	return v, ok
+}
+
+func (tm *customTypeTagMap) Set(name string, ctv CustomTypeValidator) {
+	tm.Lock()
+	defer tm.Unlock()
+	tm.validators[name] = ctv
+}
+
+// CustomTypeTagMap is a map of functions that can be used as tags for ValidateStruct function.
+// Use this to validate compound or custom types that need to be handled as a whole, e.g.
+// `type UUID [16]byte` (this would be handled as an array of bytes).
+var CustomTypeTagMap = &customTypeTagMap{validators: make(map[string]CustomTypeValidator)}
+
+// TagMap is a map of functions, that can be used as tags for ValidateStruct function.
+var TagMap = map[string]Validator{
+	"email":              IsEmail,
+	"url":                IsURL,
+	"dialstring":         IsDialString,
+	"requrl":             IsRequestURL,
+	"requri":             IsRequestURI,
+	"alpha":              IsAlpha,
+	"utfletter":          IsUTFLetter,
+	"alphanum":           IsAlphanumeric,
+	"utfletternum":       IsUTFLetterNumeric,
+	"numeric":            IsNumeric,
+	"utfnumeric":         IsUTFNumeric,
+	"utfdigit":           IsUTFDigit,
+	"hexadecimal":        IsHexadecimal,
+	"hexcolor":           IsHexcolor,
+	"rgbcolor":           IsRGBcolor,
+	"lowercase":          IsLowerCase,
+	"uppercase":          IsUpperCase,
+	"int":                IsInt,
+	"float":              IsFloat,
+	"null":               IsNull,
+	"notnull":            IsNotNull,
+	"uuid":               IsUUID,
+	"uuidv3":             IsUUIDv3,
+	"uuidv4":             IsUUIDv4,
+	"uuidv5":             IsUUIDv5,
+	"creditcard":         IsCreditCard,
+	"isbn10":             IsISBN10,
+	"isbn13":             IsISBN13,
+	"json":               IsJSON,
+	"multibyte":          IsMultibyte,
+	"ascii":              IsASCII,
+	"printableascii":     IsPrintableASCII,
+	"fullwidth":          IsFullWidth,
+	"halfwidth":          IsHalfWidth,
+	"variablewidth":      IsVariableWidth,
+	"base64":             IsBase64,
+	"datauri":            IsDataURI,
+	"ip":                 IsIP,
+	"port":               IsPort,
+	"ipv4":               IsIPv4,
+	"ipv6":               IsIPv6,
+	"dns":                IsDNSName,
+	"host":               IsHost,
+	"mac":                IsMAC,
+	"latitude":           IsLatitude,
+	"longitude":          IsLongitude,
+	"ssn":                IsSSN,
+	"semver":             IsSemver,
+	"rfc3339":            IsRFC3339,
+	"rfc3339WithoutZone": IsRFC3339WithoutZone,
+	"ISO3166Alpha2":      IsISO3166Alpha2,
+	"ISO3166Alpha3":      IsISO3166Alpha3,
+	"ISO4217":            IsISO4217,
+	"IMEI":               IsIMEI,
+}
+
+// ISO3166Entry stores country codes
+type ISO3166Entry struct {
+	EnglishShortName string
+	FrenchShortName  string
+	Alpha2Code       string
+	Alpha3Code       string
+	Numeric          string
+}
+
+//ISO3166List based on https://www.iso.org/obp/ui/#search/code/ Code Type "Officially Assigned Codes"
+var ISO3166List = []ISO3166Entry{
+	{"Afghanistan", "Afghanistan (l')", "AF", "AFG", "004"},
+	{"Albania", "Albanie (l')", "AL", "ALB", "008"},
+	{"Antarctica", "Antarctique (l')", "AQ", "ATA", "010"},
+	{"Algeria", "Algérie (l')", "DZ", "DZA", "012"},
+	{"American Samoa", "Samoa américaines (les)", "AS", "ASM", "016"},
+	{"Andorra", "Andorre (l')", "AD", "AND", "020"},
+	{"Angola", "Angola (l')", "AO", "AGO", "024"},
+	{"Antigua and Barbuda", "Antigua-et-Barbuda", "AG", "ATG", "028"},
+	{"Azerbaijan", "Azerbaïdjan (l')", "AZ", "AZE", "031"},
+	{"Argentina", "Argentine (l')", "AR", "ARG", "032"},
+	{"Australia", "Australie (l')", "AU", "AUS", "036"},
+	{"Austria", "Autriche (l')", "AT", "AUT", "040"},
+	{"Bahamas (the)", "Bahamas (les)", "BS", "BHS", "044"},
+	{"Bahrain", "Bahreïn", "BH", "BHR", "048"},
+	{"Bangladesh", "Bangladesh (le)", "BD", "BGD", "050"},
+	{"Armenia", "Arménie (l')", "AM", "ARM", "051"},
+	{"Barbados", "Barbade (la)", "BB", "BRB", "052"},
+	{"Belgium", "Belgique (la)", "BE", "BEL", "056"},
+	{"Bermuda", "Bermudes (les)", "BM", "BMU", "060"},
+	{"Bhutan", "Bhoutan (le)", "BT", "BTN", "064"},
+	{"Bolivia (Plurinational State of)", "Bolivie (État plurinational de)", "BO", "BOL", "068"},
+	{"Bosnia and Herzegovina", "Bosnie-Herzégovine (la)", "BA", "BIH", "070"},
+	{"Botswana", "Botswana (le)", "BW", "BWA", "072"},
+	{"Bouvet Island", "Bouvet (l'Île)", "BV", "BVT", "074"},
+	{"Brazil", "Brésil (le)", "BR", "BRA", "076"},
+	{"Belize", "Belize (le)", "BZ", "BLZ", "084"},
+	{"British Indian Ocean Territory (the)", "Indien (le Territoire britannique de l'océan)", "IO", "IOT", "086"},
+	{"Solomon Islands", "Salomon (Îles)", "SB", "SLB", "090"},
+	{"Virgin Islands (British)", "Vierges britanniques (les Îles)", "VG", "VGB", "092"},
+	{"Brunei Darussalam", "Brunéi Darussalam (le)", "BN", "BRN", "096"},
+	{"Bulgaria", "Bulgarie (la)", "BG", "BGR", "100"},
+	{"Myanmar", "Myanmar (le)", "MM", "MMR", "104"},
+	{"Burundi", "Burundi (le)", "BI", "BDI", "108"},
+	{"Belarus", "Bélarus (le)", "BY", "BLR", "112"},
+	{"Cambodia", "Cambodge (le)", "KH", "KHM", "116"},
+	{"Cameroon", "Cameroun (le)", "CM", "CMR", "120"},
+	{"Canada", "Canada (le)", "CA", "CAN", "124"},
+	{"Cabo Verde", "Cabo Verde", "CV", "CPV", "132"},
+	{"Cayman Islands (the)", "Caïmans (les Îles)", "KY", "CYM", "136"},
+	{"Central African Republic (the)", "République centrafricaine (la)", "CF", "CAF", "140"},
+	{"Sri Lanka", "Sri Lanka", "LK", "LKA", "144"},
+	{"Chad", "Tchad (le)", "TD", "TCD", "148"},
+	{"Chile", "Chili (le)", "CL", "CHL", "152"},
+	{"China", "Chine (la)", "CN", "CHN", "156"},
+	{"Taiwan (Province of China)", "Taïwan (Province de Chine)", "TW", "TWN", "158"},
+	{"Christmas Island", "Christmas (l'Île)", "CX", "CXR", "162"},
+	{"Cocos (Keeling) Islands (the)", "Cocos (les Îles)/ Keeling (les Îles)", "CC", "CCK", "166"},
+	{"Colombia", "Colombie (la)", "CO", "COL", "170"},
+	{"Comoros (the)", "Comores (les)", "KM", "COM", "174"},
+	{"Mayotte", "Mayotte", "YT", "MYT", "175"},
+	{"Congo (the)", "Congo (le)", "CG", "COG", "178"},
+	{"Congo (the Democratic Republic of the)", "Congo (la République démocratique du)", "CD", "COD", "180"},
+	{"Cook Islands (the)", "Cook (les Îles)", "CK", "COK", "184"},
+	{"Costa Rica", "Costa Rica (le)", "CR", "CRI", "188"},
+	{"Croatia", "Croatie (la)", "HR", "HRV", "191"},
+	{"Cuba", "Cuba", "CU", "CUB", "192"},
+	{"Cyprus", "Chypre", "CY", "CYP", "196"},
+	{"Czech Republic (the)", "tchèque (la République)", "CZ", "CZE", "203"},
+	{"Benin", "Bénin (le)", "BJ", "BEN", "204"},
+	{"Denmark", "Danemark (le)", "DK", "DNK", "208"},
+	{"Dominica", "Dominique (la)", "DM", "DMA", "212"},
+	{"Dominican Republic (the)", "dominicaine (la République)", "DO", "DOM", "214"},
+	{"Ecuador", "Équateur (l')", "EC", "ECU", "218"},
+	{"El Salvador", "El Salvador", "SV", "SLV", "222"},
+	{"Equatorial Guinea", "Guinée équatoriale (la)", "GQ", "GNQ", "226"},
+	{"Ethiopia", "Éthiopie (l')", "ET", "ETH", "231"},
+	{"Eritrea", "Érythrée (l')", "ER", "ERI", "232"},
+	{"Estonia", "Estonie (l')", "EE", "EST", "233"},
+	{"Faroe Islands (the)", "Féroé (les Îles)", "FO", "FRO", "234"},
+	{"Falkland Islands (the) [Malvinas]", "Falkland (les Îles)/Malouines (les Îles)", "FK", "FLK", "238"},
+	{"South Georgia and the South Sandwich Islands", "Géorgie du Sud-et-les Îles Sandwich du Sud (la)", "GS", "SGS", "239"},
+	{"Fiji", "Fidji (les)", "FJ", "FJI", "242"},
+	{"Finland", "Finlande (la)", "FI", "FIN", "246"},
+	{"Åland Islands", "Åland(les Îles)", "AX", "ALA", "248"},
+	{"France", "France (la)", "FR", "FRA", "250"},
+	{"French Guiana", "Guyane française (la )", "GF", "GUF", "254"},
+	{"French Polynesia", "Polynésie française (la)", "PF", "PYF", "258"},
+	{"French Southern Territories (the)", "Terres australes françaises (les)", "TF", "ATF", "260"},
+	{"Djibouti", "Djibouti", "DJ", "DJI", "262"},
+	{"Gabon", "Gabon (le)", "GA", "GAB", "266"},
+	{"Georgia", "Géorgie (la)", "GE", "GEO", "268"},
+	{"Gambia (the)", "Gambie (la)", "GM", "GMB", "270"},
+	{"Palestine, State of", "Palestine, État de", "PS", "PSE", "275"},
+	{"Germany", "Allemagne (l')", "DE", "DEU", "276"},
+	{"Ghana", "Ghana (le)", "GH", "GHA", "288"},
+	{"Gibraltar", "Gibraltar", "GI", "GIB", "292"},
+	{"Kiribati", "Kiribati", "KI", "KIR", "296"},
+	{"Greece", "Grèce (la)", "GR", "GRC", "300"},
+	{"Greenland", "Groenland (le)", "GL", "GRL", "304"},
+	{"Grenada", "Grenade (la)", "GD", "GRD", "308"},
+	{"Guadeloupe", "Guadeloupe (la)", "GP", "GLP", "312"},
+	{"Guam", "Guam", "GU", "GUM", "316"},
+	{"Guatemala", "Guatemala (le)", "GT", "GTM", "320"},
+	{"Guinea", "Guinée (la)", "GN", "GIN", "324"},
+	{"Guyana", "Guyana (le)", "GY", "GUY", "328"},
+	{"Haiti", "Haïti", "HT", "HTI", "332"},
+	{"Heard Island and McDonald Islands", "Heard-et-Îles MacDonald (l'Île)", "HM", "HMD", "334"},
+	{"Holy See (the)", "Saint-Siège (le)", "VA", "VAT", "336"},
+	{"Honduras", "Honduras (le)", "HN", "HND", "340"},
+	{"Hong Kong", "Hong Kong", "HK", "HKG", "344"},
+	{"Hungary", "Hongrie (la)", "HU", "HUN", "348"},
+	{"Iceland", "Islande (l')", "IS", "ISL", "352"},
+	{"India", "Inde (l')", "IN", "IND", "356"},
+	{"Indonesia", "Indonésie (l')", "ID", "IDN", "360"},
+	{"Iran (Islamic Republic of)", "Iran (République Islamique d')", "IR", "IRN", "364"},
+	{"Iraq", "Iraq (l')", "IQ", "IRQ", "368"},
+	{"Ireland", "Irlande (l')", "IE", "IRL", "372"},
+	{"Israel", "Israël", "IL", "ISR", "376"},
+	{"Italy", "Italie (l')", "IT", "ITA", "380"},
+	{"Côte d'Ivoire", "Côte d'Ivoire (la)", "CI", "CIV", "384"},
+	{"Jamaica", "Jamaïque (la)", "JM", "JAM", "388"},
+	{"Japan", "Japon (le)", "JP", "JPN", "392"},
+	{"Kazakhstan", "Kazakhstan (le)", "KZ", "KAZ", "398"},
+	{"Jordan", "Jordanie (la)", "JO", "JOR", "400"},
+	{"Kenya", "Kenya (le)", "KE", "KEN", "404"},
+	{"Korea (the Democratic People's Republic of)", "Corée (la République populaire démocratique de)", "KP", "PRK", "408"},
+	{"Korea (the Republic of)", "Corée (la République de)", "KR", "KOR", "410"},
+	{"Kuwait", "Koweït (le)", "KW", "KWT", "414"},
+	{"Kyrgyzstan", "Kirghizistan (le)", "KG", "KGZ", "417"},
+	{"Lao People's Democratic Republic (the)", "Lao, République démocratique populaire", "LA", "LAO", "418"},
+	{"Lebanon", "Liban (le)", "LB", "LBN", "422"},
+	{"Lesotho", "Lesotho (le)", "LS", "LSO", "426"},
+	{"Latvia", "Lettonie (la)", "LV", "LVA", "428"},
+	{"Liberia", "Libéria (le)", "LR", "LBR", "430"},
+	{"Libya", "Libye (la)", "LY", "LBY", "434"},
+	{"Liechtenstein", "Liechtenstein (le)", "LI", "LIE", "438"},
+	{"Lithuania", "Lituanie (la)", "LT", "LTU", "440"},
+	{"Luxembourg", "Luxembourg (le)", "LU", "LUX", "442"},
+	{"Macao", "Macao", "MO", "MAC", "446"},
+	{"Madagascar", "Madagascar", "MG", "MDG", "450"},
+	{"Malawi", "Malawi (le)", "MW", "MWI", "454"},
+	{"Malaysia", "Malaisie (la)", "MY", "MYS", "458"},
+	{"Maldives", "Maldives (les)", "MV", "MDV", "462"},
+	{"Mali", "Mali (le)", "ML", "MLI", "466"},
+	{"Malta", "Malte", "MT", "MLT", "470"},
+	{"Martinique", "Martinique (la)", "MQ", "MTQ", "474"},
+	{"Mauritania", "Mauritanie (la)", "MR", "MRT", "478"},
+	{"Mauritius", "Maurice", "MU", "MUS", "480"},
+	{"Mexico", "Mexique (le)", "MX", "MEX", "484"},
+	{"Monaco", "Monaco", "MC", "MCO", "492"},
+	{"Mongolia", "Mongolie (la)", "MN", "MNG", "496"},
+	{"Moldova (the Republic of)", "Moldova , République de", "MD", "MDA", "498"},
+	{"Montenegro", "Monténégro (le)", "ME", "MNE", "499"},
+	{"Montserrat", "Montserrat", "MS", "MSR", "500"},
+	{"Morocco", "Maroc (le)", "MA", "MAR", "504"},
+	{"Mozambique", "Mozambique (le)", "MZ", "MOZ", "508"},
+	{"Oman", "Oman", "OM", "OMN", "512"},
+	{"Namibia", "Namibie (la)", "NA", "NAM", "516"},
+	{"Nauru", "Nauru", "NR", "NRU", "520"},
+	{"Nepal", "Népal (le)", "NP", "NPL", "524"},
+	{"Netherlands (the)", "Pays-Bas (les)", "NL", "NLD", "528"},
+	{"Curaçao", "Curaçao", "CW", "CUW", "531"},
+	{"Aruba", "Aruba", "AW", "ABW", "533"},
+	{"Sint Maarten (Dutch part)", "Saint-Martin (partie néerlandaise)", "SX", "SXM", "534"},
+	{"Bonaire, Sint Eustatius and Saba", "Bonaire, Saint-Eustache et Saba", "BQ", "BES", "535"},
+	{"New Caledonia", "Nouvelle-Calédonie (la)", "NC", "NCL", "540"},
+	{"Vanuatu", "Vanuatu (le)", "VU", "VUT", "548"},
+	{"New Zealand", "Nouvelle-Zélande (la)", "NZ", "NZL", "554"},
+	{"Nicaragua", "Nicaragua (le)", "NI", "NIC", "558"},
+	{"Niger (the)", "Niger (le)", "NE", "NER", "562"},
+	{"Nigeria", "Nigéria (le)", "NG", "NGA", "566"},
+	{"Niue", "Niue", "NU", "NIU", "570"},
+	{"Norfolk Island", "Norfolk (l'Île)", "NF", "NFK", "574"},
+	{"Norway", "Norvège (la)", "NO", "NOR", "578"},
+	{"Northern Mariana Islands (the)", "Mariannes du Nord (les Îles)", "MP", "MNP", "580"},
+	{"United States Minor Outlying Islands (the)", "Îles mineures éloignées des États-Unis (les)", "UM", "UMI", "581"},
+	{"Micronesia (Federated States of)", "Micronésie (États fédérés de)", "FM", "FSM", "583"},
+	{"Marshall Islands (the)", "Marshall (Îles)", "MH", "MHL", "584"},
+	{"Palau", "Palaos (les)", "PW", "PLW", "585"},
+	{"Pakistan", "Pakistan (le)", "PK", "PAK", "586"},
+	{"Panama", "Panama (le)", "PA", "PAN", "591"},
+	{"Papua New Guinea", "Papouasie-Nouvelle-Guinée (la)", "PG", "PNG", "598"},
+	{"Paraguay", "Paraguay (le)", "PY", "PRY", "600"},
+	{"Peru", "Pérou (le)", "PE", "PER", "604"},
+	{"Philippines (the)", "Philippines (les)", "PH", "PHL", "608"},
+	{"Pitcairn", "Pitcairn", "PN", "PCN", "612"},
+	{"Poland", "Pologne (la)", "PL", "POL", "616"},
+	{"Portugal", "Portugal (le)", "PT", "PRT", "620"},
+	{"Guinea-Bissau", "Guinée-Bissau (la)", "GW", "GNB", "624"},
+	{"Timor-Leste", "Timor-Leste (le)", "TL", "TLS", "626"},
+	{"Puerto Rico", "Porto Rico", "PR", "PRI", "630"},
+	{"Qatar", "Qatar (le)", "QA", "QAT", "634"},
+	{"Réunion", "Réunion (La)", "RE", "REU", "638"},
+	{"Romania", "Roumanie (la)", "RO", "ROU", "642"},
+	{"Russian Federation (the)", "Russie (la Fédération de)", "RU", "RUS", "643"},
+	{"Rwanda", "Rwanda (le)", "RW", "RWA", "646"},
+	{"Saint Barthélemy", "Saint-Barthélemy", "BL", "BLM", "652"},
+	{"Saint Helena, Ascension and Tristan da Cunha", "Sainte-Hélène, Ascension et Tristan da Cunha", "SH", "SHN", "654"},
+	{"Saint Kitts and Nevis", "Saint-Kitts-et-Nevis", "KN", "KNA", "659"},
+	{"Anguilla", "Anguilla", "AI", "AIA", "660"},
+	{"Saint Lucia", "Sainte-Lucie", "LC", "LCA", "662"},
+	{"Saint Martin (French part)", "Saint-Martin (partie française)", "MF", "MAF", "663"},
+	{"Saint Pierre and Miquelon", "Saint-Pierre-et-Miquelon", "PM", "SPM", "666"},
+	{"Saint Vincent and the Grenadines", "Saint-Vincent-et-les Grenadines", "VC", "VCT", "670"},
+	{"San Marino", "Saint-Marin", "SM", "SMR", "674"},
+	{"Sao Tome and Principe", "Sao Tomé-et-Principe", "ST", "STP", "678"},
+	{"Saudi Arabia", "Arabie saoudite (l')", "SA", "SAU", "682"},
+	{"Senegal", "Sénégal (le)", "SN", "SEN", "686"},
+	{"Serbia", "Serbie (la)", "RS", "SRB", "688"},
+	{"Seychelles", "Seychelles (les)", "SC", "SYC", "690"},
+	{"Sierra Leone", "Sierra Leone (la)", "SL", "SLE", "694"},
+	{"Singapore", "Singapour", "SG", "SGP", "702"},
+	{"Slovakia", "Slovaquie (la)", "SK", "SVK", "703"},
+	{"Viet Nam", "Viet Nam (le)", "VN", "VNM", "704"},
+	{"Slovenia", "Slovénie (la)", "SI", "SVN", "705"},
+	{"Somalia", "Somalie (la)", "SO", "SOM", "706"},
+	{"South Africa", "Afrique du Sud (l')", "ZA", "ZAF", "710"},
+	{"Zimbabwe", "Zimbabwe (le)", "ZW", "ZWE", "716"},
+	{"Spain", "Espagne (l')", "ES", "ESP", "724"},
+	{"South Sudan", "Soudan du Sud (le)", "SS", "SSD", "728"},
+	{"Sudan (the)", "Soudan (le)", "SD", "SDN", "729"},
+	{"Western Sahara*", "Sahara occidental (le)*", "EH", "ESH", "732"},
+	{"Suriname", "Suriname (le)", "SR", "SUR", "740"},
+	{"Svalbard and Jan Mayen", "Svalbard et l'Île Jan Mayen (le)", "SJ", "SJM", "744"},
+	{"Swaziland", "Swaziland (le)", "SZ", "SWZ", "748"},
+	{"Sweden", "Suède (la)", "SE", "SWE", "752"},
+	{"Switzerland", "Suisse (la)", "CH", "CHE", "756"},
+	{"Syrian Arab Republic", "République arabe syrienne (la)", "SY", "SYR", "760"},
+	{"Tajikistan", "Tadjikistan (le)", "TJ", "TJK", "762"},
+	{"Thailand", "Thaïlande (la)", "TH", "THA", "764"},
+	{"Togo", "Togo (le)", "TG", "TGO", "768"},
+	{"Tokelau", "Tokelau (les)", "TK", "TKL", "772"},
+	{"Tonga", "Tonga (les)", "TO", "TON", "776"},
+	{"Trinidad and Tobago", "Trinité-et-Tobago (la)", "TT", "TTO", "780"},
+	{"United Arab Emirates (the)", "Émirats arabes unis (les)", "AE", "ARE", "784"},
+	{"Tunisia", "Tunisie (la)", "TN", "TUN", "788"},
+	{"Turkey", "Turquie (la)", "TR", "TUR", "792"},
+	{"Turkmenistan", "Turkménistan (le)", "TM", "TKM", "795"},
+	{"Turks and Caicos Islands (the)", "Turks-et-Caïcos (les Îles)", "TC", "TCA", "796"},
+	{"Tuvalu", "Tuvalu (les)", "TV", "TUV", "798"},
+	{"Uganda", "Ouganda (l')", "UG", "UGA", "800"},
+	{"Ukraine", "Ukraine (l')", "UA", "UKR", "804"},
+	{"Macedonia (the former Yugoslav Republic of)", "Macédoine (l'ex‑République yougoslave de)", "MK", "MKD", "807"},
+	{"Egypt", "Égypte (l')", "EG", "EGY", "818"},
+	{"United Kingdom of Great Britain and Northern Ireland (the)", "Royaume-Uni de Grande-Bretagne et d'Irlande du Nord (le)", "GB", "GBR", "826"},
+	{"Guernsey", "Guernesey", "GG", "GGY", "831"},
+	{"Jersey", "Jersey", "JE", "JEY", "832"},
+	{"Isle of Man", "Île de Man", "IM", "IMN", "833"},
+	{"Tanzania, United Republic of", "Tanzanie, République-Unie de", "TZ", "TZA", "834"},
+	{"United States of America (the)", "États-Unis d'Amérique (les)", "US", "USA", "840"},
+	{"Virgin Islands (U.S.)", "Vierges des États-Unis (les Îles)", "VI", "VIR", "850"},
+	{"Burkina Faso", "Burkina Faso (le)", "BF", "BFA", "854"},
+	{"Uruguay", "Uruguay (l')", "UY", "URY", "858"},
+	{"Uzbekistan", "Ouzbékistan (l')", "UZ", "UZB", "860"},
+	{"Venezuela (Bolivarian Republic of)", "Venezuela (République bolivarienne du)", "VE", "VEN", "862"},
+	{"Wallis and Futuna", "Wallis-et-Futuna", "WF", "WLF", "876"},
+	{"Samoa", "Samoa (le)", "WS", "WSM", "882"},
+	{"Yemen", "Yémen (le)", "YE", "YEM", "887"},
+	{"Zambia", "Zambie (la)", "ZM", "ZMB", "894"},
+}
+
+// ISO4217List is the list of ISO currency codes
+var ISO4217List = []string{
+	"AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN",
+	"BAM", "BBD", "BDT", "BGN", "BHD", "BIF", "BMD", "BND", "BOB", "BOV", "BRL", "BSD", "BTN", "BWP", "BYN", "BZD",
+	"CAD", "CDF", "CHE", "CHF", "CHW", "CLF", "CLP", "CNY", "COP", "COU", "CRC", "CUC", "CUP", "CVE", "CZK",
+	"DJF", "DKK", "DOP", "DZD",
+	"EGP", "ERN", "ETB", "EUR",
+	"FJD", "FKP",
+	"GBP", "GEL", "GHS", "GIP", "GMD", "GNF", "GTQ", "GYD",
+	"HKD", "HNL", "HRK", "HTG", "HUF",
+	"IDR", "ILS", "INR", "IQD", "IRR", "ISK",
+	"JMD", "JOD", "JPY",
+	"KES", "KGS", "KHR", "KMF", "KPW", "KRW", "KWD", "KYD", "KZT",
+	"LAK", "LBP", "LKR", "LRD", "LSL", "LYD",
+	"MAD", "MDL", "MGA", "MKD", "MMK", "MNT", "MOP", "MRO", "MUR", "MVR", "MWK", "MXN", "MXV", "MYR", "MZN",
+	"NAD", "NGN", "NIO", "NOK", "NPR", "NZD",
+	"OMR",
+	"PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "PYG",
+	"QAR",
+	"RON", "RSD", "RUB", "RWF",
+	"SAR", "SBD", "SCR", "SDG", "SEK", "SGD", "SHP", "SLL", "SOS", "SRD", "SSP", "STD", "STN", "SVC", "SYP", "SZL",
+	"THB", "TJS", "TMT", "TND", "TOP", "TRY", "TTD", "TWD", "TZS",
+	"UAH", "UGX", "USD", "USN", "UYI", "UYU", "UYW", "UZS",
+	"VEF", "VES", "VND", "VUV",
+	"WST",
+	"XAF", "XAG", "XAU", "XBA", "XBB", "XBC", "XBD", "XCD", "XDR", "XOF", "XPD", "XPF", "XPT", "XSU", "XTS", "XUA", "XXX",
+	"YER",
+	"ZAR", "ZMW", "ZWL",
+}
+
+// ISO693Entry stores ISO language codes
+type ISO693Entry struct {
+	Alpha3bCode string
+	Alpha2Code  string
+	English     string
+}
+
+//ISO693List based on http://data.okfn.org/data/core/language-codes/r/language-codes-3b2.json
+var ISO693List = []ISO693Entry{
+	{Alpha3bCode: "aar", Alpha2Code: "aa", English: "Afar"},
+	{Alpha3bCode: "abk", Alpha2Code: "ab", English: "Abkhazian"},
+	{Alpha3bCode: "afr", Alpha2Code: "af", English: "Afrikaans"},
+	{Alpha3bCode: "aka", Alpha2Code: "ak", English: "Akan"},
+	{Alpha3bCode: "alb", Alpha2Code: "sq", English: "Albanian"},
+	{Alpha3bCode: "amh", Alpha2Code: "am", English: "Amharic"},
+	{Alpha3bCode: "ara", Alpha2Code: "ar", English: "Arabic"},
+	{Alpha3bCode: "arg", Alpha2Code: "an", English: "Aragonese"},
+	{Alpha3bCode: "arm", Alpha2Code: "hy", English: "Armenian"},
+	{Alpha3bCode: "asm", Alpha2Code: "as", English: "Assamese"},
+	{Alpha3bCode: "ava", Alpha2Code: "av", English: "Avaric"},
+	{Alpha3bCode: "ave", Alpha2Code: "ae", English: "Avestan"},
+	{Alpha3bCode: "aym", Alpha2Code: "ay", English: "Aymara"},
+	{Alpha3bCode: "aze", Alpha2Code: "az", English: "Azerbaijani"},
+	{Alpha3bCode: "bak", Alpha2Code: "ba", English: "Bashkir"},
+	{Alpha3bCode: "bam", Alpha2Code: "bm", English: "Bambara"},
+	{Alpha3bCode: "baq", Alpha2Code: "eu", English: "Basque"},
+	{Alpha3bCode: "bel", Alpha2Code: "be", English: "Belarusian"},
+	{Alpha3bCode: "ben", Alpha2Code: "bn", English: "Bengali"},
+	{Alpha3bCode: "bih", Alpha2Code: "bh", English: "Bihari languages"},
+	{Alpha3bCode: "bis", Alpha2Code: "bi", English: "Bislama"},
+	{Alpha3bCode: "bos", Alpha2Code: "bs", English: "Bosnian"},
+	{Alpha3bCode: "bre", Alpha2Code: "br", English: "Breton"},
+	{Alpha3bCode: "bul", Alpha2Code: "bg", English: "Bulgarian"},
+	{Alpha3bCode: "bur", Alpha2Code: "my", English: "Burmese"},
+	{Alpha3bCode: "cat", Alpha2Code: "ca", English: "Catalan; Valencian"},
+	{Alpha3bCode: "cha", Alpha2Code: "ch", English: "Chamorro"},
+	{Alpha3bCode: "che", Alpha2Code: "ce", English: "Chechen"},
+	{Alpha3bCode: "chi", Alpha2Code: "zh", English: "Chinese"},
+	{Alpha3bCode: "chu", Alpha2Code: "cu", English: "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic"},
+	{Alpha3bCode: "chv", Alpha2Code: "cv", English: "Chuvash"},
+	{Alpha3bCode: "cor", Alpha2Code: "kw", English: "Cornish"},
+	{Alpha3bCode: "cos", Alpha2Code: "co", English: "Corsican"},
+	{Alpha3bCode: "cre", Alpha2Code: "cr", English: "Cree"},
+	{Alpha3bCode: "cze", Alpha2Code: "cs", English: "Czech"},
+	{Alpha3bCode: "dan", Alpha2Code: "da", English: "Danish"},
+	{Alpha3bCode: "div", Alpha2Code: "dv", English: "Divehi; Dhivehi; Maldivian"},
+	{Alpha3bCode: "dut", Alpha2Code: "nl", English: "Dutch; Flemish"},
+	{Alpha3bCode: "dzo", Alpha2Code: "dz", English: "Dzongkha"},
+	{Alpha3bCode: "eng", Alpha2Code: "en", English: "English"},
+	{Alpha3bCode: "epo", Alpha2Code: "eo", English: "Esperanto"},
+	{Alpha3bCode: "est", Alpha2Code: "et", English: "Estonian"},
+	{Alpha3bCode: "ewe", Alpha2Code: "ee", English: "Ewe"},
+	{Alpha3bCode: "fao", Alpha2Code: "fo", English: "Faroese"},
+	{Alpha3bCode: "fij", Alpha2Code: "fj", English: "Fijian"},
+	{Alpha3bCode: "fin", Alpha2Code: "fi", English: "Finnish"},
+	{Alpha3bCode: "fre", Alpha2Code: "fr", English: "French"},
+	{Alpha3bCode: "fry", Alpha2Code: "fy", English: "Western Frisian"},
+	{Alpha3bCode: "ful", Alpha2Code: "ff", English: "Fulah"},
+	{Alpha3bCode: "geo", Alpha2Code: "ka", English: "Georgian"},
+	{Alpha3bCode: "ger", Alpha2Code: "de", English: "German"},
+	{Alpha3bCode: "gla", Alpha2Code: "gd", English: "Gaelic; Scottish Gaelic"},
+	{Alpha3bCode: "gle", Alpha2Code: "ga", English: "Irish"},
+	{Alpha3bCode: "glg", Alpha2Code: "gl", English: "Galician"},
+	{Alpha3bCode: "glv", Alpha2Code: "gv", English: "Manx"},
+	{Alpha3bCode: "gre", Alpha2Code: "el", English: "Greek, Modern (1453-)"},
+	{Alpha3bCode: "grn", Alpha2Code: "gn", English: "Guarani"},
+	{Alpha3bCode: "guj", Alpha2Code: "gu", English: "Gujarati"},
+	{Alpha3bCode: "hat", Alpha2Code: "ht", English: "Haitian; Haitian Creole"},
+	{Alpha3bCode: "hau", Alpha2Code: "ha", English: "Hausa"},
+	{Alpha3bCode: "heb", Alpha2Code: "he", English: "Hebrew"},
+	{Alpha3bCode: "her", Alpha2Code: "hz", English: "Herero"},
+	{Alpha3bCode: "hin", Alpha2Code: "hi", English: "Hindi"},
+	{Alpha3bCode: "hmo", Alpha2Code: "ho", English: "Hiri Motu"},
+	{Alpha3bCode: "hrv", Alpha2Code: "hr", English: "Croatian"},
+	{Alpha3bCode: "hun", Alpha2Code: "hu", English: "Hungarian"},
+	{Alpha3bCode: "ibo", Alpha2Code: "ig", English: "Igbo"},
+	{Alpha3bCode: "ice", Alpha2Code: "is", English: "Icelandic"},
+	{Alpha3bCode: "ido", Alpha2Code: "io", English: "Ido"},
+	{Alpha3bCode: "iii", Alpha2Code: "ii", English: "Sichuan Yi; Nuosu"},
+	{Alpha3bCode: "iku", Alpha2Code: "iu", English: "Inuktitut"},
+	{Alpha3bCode: "ile", Alpha2Code: "ie", English: "Interlingue; Occidental"},
+	{Alpha3bCode: "ina", Alpha2Code: "ia", English: "Interlingua (International Auxiliary Language Association)"},
+	{Alpha3bCode: "ind", Alpha2Code: "id", English: "Indonesian"},
+	{Alpha3bCode: "ipk", Alpha2Code: "ik", English: "Inupiaq"},
+	{Alpha3bCode: "ita", Alpha2Code: "it", English: "Italian"},
+	{Alpha3bCode: "jav", Alpha2Code: "jv", English: "Javanese"},
+	{Alpha3bCode: "jpn", Alpha2Code: "ja", English: "Japanese"},
+	{Alpha3bCode: "kal", Alpha2Code: "kl", English: "Kalaallisut; Greenlandic"},
+	{Alpha3bCode: "kan", Alpha2Code: "kn", English: "Kannada"},
+	{Alpha3bCode: "kas", Alpha2Code: "ks", English: "Kashmiri"},
+	{Alpha3bCode: "kau", Alpha2Code: "kr", English: "Kanuri"},
+	{Alpha3bCode: "kaz", Alpha2Code: "kk", English: "Kazakh"},
+	{Alpha3bCode: "khm", Alpha2Code: "km", English: "Central Khmer"},
+	{Alpha3bCode: "kik", Alpha2Code: "ki", English: "Kikuyu; Gikuyu"},
+	{Alpha3bCode: "kin", Alpha2Code: "rw", English: "Kinyarwanda"},
+	{Alpha3bCode: "kir", Alpha2Code: "ky", English: "Kirghiz; Kyrgyz"},
+	{Alpha3bCode: "kom", Alpha2Code: "kv", English: "Komi"},
+	{Alpha3bCode: "kon", Alpha2Code: "kg", English: "Kongo"},
+	{Alpha3bCode: "kor", Alpha2Code: "ko", English: "Korean"},
+	{Alpha3bCode: "kua", Alpha2Code: "kj", English: "Kuanyama; Kwanyama"},
+	{Alpha3bCode: "kur", Alpha2Code: "ku", English: "Kurdish"},
+	{Alpha3bCode: "lao", Alpha2Code: "lo", English: "Lao"},
+	{Alpha3bCode: "lat", Alpha2Code: "la", English: "Latin"},
+	{Alpha3bCode: "lav", Alpha2Code: "lv", English: "Latvian"},
+	{Alpha3bCode: "lim", Alpha2Code: "li", English: "Limburgan; Limburger; Limburgish"},
+	{Alpha3bCode: "lin", Alpha2Code: "ln", English: "Lingala"},
+	{Alpha3bCode: "lit", Alpha2Code: "lt", English: "Lithuanian"},
+	{Alpha3bCode: "ltz", Alpha2Code: "lb", English: "Luxembourgish; Letzeburgesch"},
+	{Alpha3bCode: "lub", Alpha2Code: "lu", English: "Luba-Katanga"},
+	{Alpha3bCode: "lug", Alpha2Code: "lg", English: "Ganda"},
+	{Alpha3bCode: "mac", Alpha2Code: "mk", English: "Macedonian"},
+	{Alpha3bCode: "mah", Alpha2Code: "mh", English: "Marshallese"},
+	{Alpha3bCode: "mal", Alpha2Code: "ml", English: "Malayalam"},
+	{Alpha3bCode: "mao", Alpha2Code: "mi", English: "Maori"},
+	{Alpha3bCode: "mar", Alpha2Code: "mr", English: "Marathi"},
+	{Alpha3bCode: "may", Alpha2Code: "ms", English: "Malay"},
+	{Alpha3bCode: "mlg", Alpha2Code: "mg", English: "Malagasy"},
+	{Alpha3bCode: "mlt", Alpha2Code: "mt", English: "Maltese"},
+	{Alpha3bCode: "mon", Alpha2Code: "mn", English: "Mongolian"},
+	{Alpha3bCode: "nau", Alpha2Code: "na", English: "Nauru"},
+	{Alpha3bCode: "nav", Alpha2Code: "nv", English: "Navajo; Navaho"},
+	{Alpha3bCode: "nbl", Alpha2Code: "nr", English: "Ndebele, South; South Ndebele"},
+	{Alpha3bCode: "nde", Alpha2Code: "nd", English: "Ndebele, North; North Ndebele"},
+	{Alpha3bCode: "ndo", Alpha2Code: "ng", English: "Ndonga"},
+	{Alpha3bCode: "nep", Alpha2Code: "ne", English: "Nepali"},
+	{Alpha3bCode: "nno", Alpha2Code: "nn", English: "Norwegian Nynorsk; Nynorsk, Norwegian"},
+	{Alpha3bCode: "nob", Alpha2Code: "nb", English: "Bokmål, Norwegian; Norwegian Bokmål"},
+	{Alpha3bCode: "nor", Alpha2Code: "no", English: "Norwegian"},
+	{Alpha3bCode: "nya", Alpha2Code: "ny", English: "Chichewa; Chewa; Nyanja"},
+	{Alpha3bCode: "oci", Alpha2Code: "oc", English: "Occitan (post 1500); Provençal"},
+	{Alpha3bCode: "oji", Alpha2Code: "oj", English: "Ojibwa"},
+	{Alpha3bCode: "ori", Alpha2Code: "or", English: "Oriya"},
+	{Alpha3bCode: "orm", Alpha2Code: "om", English: "Oromo"},
+	{Alpha3bCode: "oss", Alpha2Code: "os", English: "Ossetian; Ossetic"},
+	{Alpha3bCode: "pan", Alpha2Code: "pa", English: "Panjabi; Punjabi"},
+	{Alpha3bCode: "per", Alpha2Code: "fa", English: "Persian"},
+	{Alpha3bCode: "pli", Alpha2Code: "pi", English: "Pali"},
+	{Alpha3bCode: "pol", Alpha2Code: "pl", English: "Polish"},
+	{Alpha3bCode: "por", Alpha2Code: "pt", English: "Portuguese"},
+	{Alpha3bCode: "pus", Alpha2Code: "ps", English: "Pushto; Pashto"},
+	{Alpha3bCode: "que", Alpha2Code: "qu", English: "Quechua"},
+	{Alpha3bCode: "roh", Alpha2Code: "rm", English: "Romansh"},
+	{Alpha3bCode: "rum", Alpha2Code: "ro", English: "Romanian; Moldavian; Moldovan"},
+	{Alpha3bCode: "run", Alpha2Code: "rn", English: "Rundi"},
+	{Alpha3bCode: "rus", Alpha2Code: "ru", English: "Russian"},
+	{Alpha3bCode: "sag", Alpha2Code: "sg", English: "Sango"},
+	{Alpha3bCode: "san", Alpha2Code: "sa", English: "Sanskrit"},
+	{Alpha3bCode: "sin", Alpha2Code: "si", English: "Sinhala; Sinhalese"},
+	{Alpha3bCode: "slo", Alpha2Code: "sk", English: "Slovak"},
+	{Alpha3bCode: "slv", Alpha2Code: "sl", English: "Slovenian"},
+	{Alpha3bCode: "sme", Alpha2Code: "se", English: "Northern Sami"},
+	{Alpha3bCode: "smo", Alpha2Code: "sm", English: "Samoan"},
+	{Alpha3bCode: "sna", Alpha2Code: "sn", English: "Shona"},
+	{Alpha3bCode: "snd", Alpha2Code: "sd", English: "Sindhi"},
+	{Alpha3bCode: "som", Alpha2Code: "so", English: "Somali"},
+	{Alpha3bCode: "sot", Alpha2Code: "st", English: "Sotho, Southern"},
+	{Alpha3bCode: "spa", Alpha2Code: "es", English: "Spanish; Castilian"},
+	{Alpha3bCode: "srd", Alpha2Code: "sc", English: "Sardinian"},
+	{Alpha3bCode: "srp", Alpha2Code: "sr", English: "Serbian"},
+	{Alpha3bCode: "ssw", Alpha2Code: "ss", English: "Swati"},
+	{Alpha3bCode: "sun", Alpha2Code: "su", English: "Sundanese"},
+	{Alpha3bCode: "swa", Alpha2Code: "sw", English: "Swahili"},
+	{Alpha3bCode: "swe", Alpha2Code: "sv", English: "Swedish"},
+	{Alpha3bCode: "tah", Alpha2Code: "ty", English: "Tahitian"},
+	{Alpha3bCode: "tam", Alpha2Code: "ta", English: "Tamil"},
+	{Alpha3bCode: "tat", Alpha2Code: "tt", English: "Tatar"},
+	{Alpha3bCode: "tel", Alpha2Code: "te", English: "Telugu"},
+	{Alpha3bCode: "tgk", Alpha2Code: "tg", English: "Tajik"},
+	{Alpha3bCode: "tgl", Alpha2Code: "tl", English: "Tagalog"},
+	{Alpha3bCode: "tha", Alpha2Code: "th", English: "Thai"},
+	{Alpha3bCode: "tib", Alpha2Code: "bo", English: "Tibetan"},
+	{Alpha3bCode: "tir", Alpha2Code: "ti", English: "Tigrinya"},
+	{Alpha3bCode: "ton", Alpha2Code: "to", English: "Tonga (Tonga Islands)"},
+	{Alpha3bCode: "tsn", Alpha2Code: "tn", English: "Tswana"},
+	{Alpha3bCode: "tso", Alpha2Code: "ts", English: "Tsonga"},
+	{Alpha3bCode: "tuk", Alpha2Code: "tk", English: "Turkmen"},
+	{Alpha3bCode: "tur", Alpha2Code: "tr", English: "Turkish"},
+	{Alpha3bCode: "twi", Alpha2Code: "tw", English: "Twi"},
+	{Alpha3bCode: "uig", Alpha2Code: "ug", English: "Uighur; Uyghur"},
+	{Alpha3bCode: "ukr", Alpha2Code: "uk", English: "Ukrainian"},
+	{Alpha3bCode: "urd", Alpha2Code: "ur", English: "Urdu"},
+	{Alpha3bCode: "uzb", Alpha2Code: "uz", English: "Uzbek"},
+	{Alpha3bCode: "ven", Alpha2Code: "ve", English: "Venda"},
+	{Alpha3bCode: "vie", Alpha2Code: "vi", English: "Vietnamese"},
+	{Alpha3bCode: "vol", Alpha2Code: "vo", English: "Volapük"},
+	{Alpha3bCode: "wel", Alpha2Code: "cy", English: "Welsh"},
+	{Alpha3bCode: "wln", Alpha2Code: "wa", English: "Walloon"},
+	{Alpha3bCode: "wol", Alpha2Code: "wo", English: "Wolof"},
+	{Alpha3bCode: "xho", Alpha2Code: "xh", English: "Xhosa"},
+	{Alpha3bCode: "yid", Alpha2Code: "yi", English: "Yiddish"},
+	{Alpha3bCode: "yor", Alpha2Code: "yo", English: "Yoruba"},
+	{Alpha3bCode: "zha", Alpha2Code: "za", English: "Zhuang; Chuang"},
+	{Alpha3bCode: "zul", Alpha2Code: "zu", English: "Zulu"},
+}

+ 270 - 0
common_utils/validator/utils.go

@@ -0,0 +1,270 @@
+package govalidator
+
+import (
+	"errors"
+	"fmt"
+	"html"
+	"math"
+	"path"
+	"regexp"
+	"strings"
+	"unicode"
+	"unicode/utf8"
+)
+
+// Contains checks if the string contains the substring.
+func Contains(str, substring string) bool {
+	return strings.Contains(str, substring)
+}
+
+// Matches checks if string matches the pattern (pattern is regular expression)
+// In case of error return false
+func Matches(str, pattern string) bool {
+	match, _ := regexp.MatchString(pattern, str)
+	return match
+}
+
+// LeftTrim trims characters from the left side of the input.
+// If second argument is empty, it will remove leading spaces.
+func LeftTrim(str, chars string) string {
+	if chars == "" {
+		return strings.TrimLeftFunc(str, unicode.IsSpace)
+	}
+	r, _ := regexp.Compile("^[" + chars + "]+")
+	return r.ReplaceAllString(str, "")
+}
+
+// RightTrim trims characters from the right side of the input.
+// If second argument is empty, it will remove trailing spaces.
+func RightTrim(str, chars string) string {
+	if chars == "" {
+		return strings.TrimRightFunc(str, unicode.IsSpace)
+	}
+	r, _ := regexp.Compile("[" + chars + "]+$")
+	return r.ReplaceAllString(str, "")
+}
+
+// Trim trims characters from both sides of the input.
+// If second argument is empty, it will remove spaces.
+func Trim(str, chars string) string {
+	return LeftTrim(RightTrim(str, chars), chars)
+}
+
+// WhiteList removes characters that do not appear in the whitelist.
+func WhiteList(str, chars string) string {
+	pattern := "[^" + chars + "]+"
+	r, _ := regexp.Compile(pattern)
+	return r.ReplaceAllString(str, "")
+}
+
+// BlackList removes characters that appear in the blacklist.
+func BlackList(str, chars string) string {
+	pattern := "[" + chars + "]+"
+	r, _ := regexp.Compile(pattern)
+	return r.ReplaceAllString(str, "")
+}
+
+// StripLow removes characters with a numerical value < 32 and 127, mostly control characters.
+// If keep_new_lines is true, newline characters are preserved (\n and \r, hex 0xA and 0xD).
+func StripLow(str string, keepNewLines bool) string {
+	chars := ""
+	if keepNewLines {
+		chars = "\x00-\x09\x0B\x0C\x0E-\x1F\x7F"
+	} else {
+		chars = "\x00-\x1F\x7F"
+	}
+	return BlackList(str, chars)
+}
+
+// ReplacePattern replaces regular expression pattern in string
+func ReplacePattern(str, pattern, replace string) string {
+	r, _ := regexp.Compile(pattern)
+	return r.ReplaceAllString(str, replace)
+}
+
+// Escape replaces <, >, & and " with HTML entities.
+var Escape = html.EscapeString
+
+func addSegment(inrune, segment []rune) []rune {
+	if len(segment) == 0 {
+		return inrune
+	}
+	if len(inrune) != 0 {
+		inrune = append(inrune, '_')
+	}
+	inrune = append(inrune, segment...)
+	return inrune
+}
+
+// UnderscoreToCamelCase converts from underscore separated form to camel case form.
+// Ex.: my_func => MyFunc
+func UnderscoreToCamelCase(s string) string {
+	return strings.Replace(strings.Title(strings.Replace(strings.ToLower(s), "_", " ", -1)), " ", "", -1)
+}
+
+// CamelCaseToUnderscore converts from camel case form to underscore separated form.
+// Ex.: MyFunc => my_func
+func CamelCaseToUnderscore(str string) string {
+	var output []rune
+	var segment []rune
+	for _, r := range str {
+
+		// not treat number as separate segment
+		if !unicode.IsLower(r) && string(r) != "_" && !unicode.IsNumber(r) {
+			output = addSegment(output, segment)
+			segment = nil
+		}
+		segment = append(segment, unicode.ToLower(r))
+	}
+	output = addSegment(output, segment)
+	return string(output)
+}
+
+// Reverse returns reversed string
+func Reverse(s string) string {
+	r := []rune(s)
+	for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 {
+		r[i], r[j] = r[j], r[i]
+	}
+	return string(r)
+}
+
+// GetLines splits string by "\n" and return array of lines
+func GetLines(s string) []string {
+	return strings.Split(s, "\n")
+}
+
+// GetLine returns specified line of multiline string
+func GetLine(s string, index int) (string, error) {
+	lines := GetLines(s)
+	if index < 0 || index >= len(lines) {
+		return "", errors.New("line index out of bounds")
+	}
+	return lines[index], nil
+}
+
+// RemoveTags removes all tags from HTML string
+func RemoveTags(s string) string {
+	return ReplacePattern(s, "<[^>]*>", "")
+}
+
+// SafeFileName returns safe string that can be used in file names
+func SafeFileName(str string) string {
+	name := strings.ToLower(str)
+	name = path.Clean(path.Base(name))
+	name = strings.Trim(name, " ")
+	separators, err := regexp.Compile(`[ &_=+:]`)
+	if err == nil {
+		name = separators.ReplaceAllString(name, "-")
+	}
+	legal, err := regexp.Compile(`[^[:alnum:]-.]`)
+	if err == nil {
+		name = legal.ReplaceAllString(name, "")
+	}
+	for strings.Contains(name, "--") {
+		name = strings.Replace(name, "--", "-", -1)
+	}
+	return name
+}
+
+// NormalizeEmail canonicalize an email address.
+// The local part of the email address is lowercased for all domains; the hostname is always lowercased and
+// the local part of the email address is always lowercased for hosts that are known to be case-insensitive (currently only GMail).
+// Normalization follows special rules for known providers: currently, GMail addresses have dots removed in the local part and
+// are stripped of tags (e.g. some.one+tag@gmail.com becomes someone@gmail.com) and all @googlemail.com addresses are
+// normalized to @gmail.com.
+func NormalizeEmail(str string) (string, error) {
+	if !IsEmail(str) {
+		return "", fmt.Errorf("%s is not an email", str)
+	}
+	parts := strings.Split(str, "@")
+	parts[0] = strings.ToLower(parts[0])
+	parts[1] = strings.ToLower(parts[1])
+	if parts[1] == "gmail.com" || parts[1] == "googlemail.com" {
+		parts[1] = "gmail.com"
+		parts[0] = strings.Split(ReplacePattern(parts[0], `\.`, ""), "+")[0]
+	}
+	return strings.Join(parts, "@"), nil
+}
+
+// Truncate a string to the closest length without breaking words.
+func Truncate(str string, length int, ending string) string {
+	var aftstr, befstr string
+	if len(str) > length {
+		words := strings.Fields(str)
+		before, present := 0, 0
+		for i := range words {
+			befstr = aftstr
+			before = present
+			aftstr = aftstr + words[i] + " "
+			present = len(aftstr)
+			if present > length && i != 0 {
+				if (length - before) < (present - length) {
+					return Trim(befstr, " /\\.,\"'#!?&@+-") + ending
+				}
+				return Trim(aftstr, " /\\.,\"'#!?&@+-") + ending
+			}
+		}
+	}
+
+	return str
+}
+
+// PadLeft pads left side of a string if size of string is less then indicated pad length
+func PadLeft(str string, padStr string, padLen int) string {
+	return buildPadStr(str, padStr, padLen, true, false)
+}
+
+// PadRight pads right side of a string if size of string is less then indicated pad length
+func PadRight(str string, padStr string, padLen int) string {
+	return buildPadStr(str, padStr, padLen, false, true)
+}
+
+// PadBoth pads both sides of a string if size of string is less then indicated pad length
+func PadBoth(str string, padStr string, padLen int) string {
+	return buildPadStr(str, padStr, padLen, true, true)
+}
+
+// PadString either left, right or both sides.
+// Note that padding string can be unicode and more then one character
+func buildPadStr(str string, padStr string, padLen int, padLeft bool, padRight bool) string {
+
+	// When padded length is less then the current string size
+	if padLen < utf8.RuneCountInString(str) {
+		return str
+	}
+
+	padLen -= utf8.RuneCountInString(str)
+
+	targetLen := padLen
+
+	targetLenLeft := targetLen
+	targetLenRight := targetLen
+	if padLeft && padRight {
+		targetLenLeft = padLen / 2
+		targetLenRight = padLen - targetLenLeft
+	}
+
+	strToRepeatLen := utf8.RuneCountInString(padStr)
+
+	repeatTimes := int(math.Ceil(float64(targetLen) / float64(strToRepeatLen)))
+	repeatedString := strings.Repeat(padStr, repeatTimes)
+
+	leftSide := ""
+	if padLeft {
+		leftSide = repeatedString[0:targetLenLeft]
+	}
+
+	rightSide := ""
+	if padRight {
+		rightSide = repeatedString[0:targetLenRight]
+	}
+
+	return leftSide + str + rightSide
+}
+
+// TruncatingErrorf removes extra args from fmt.Errorf if not formatted in the str object
+func TruncatingErrorf(str string, args ...interface{}) error {
+	n := strings.Count(str, "%s")
+	return fmt.Errorf(str, args[:n]...)
+}

+ 1622 - 0
common_utils/validator/validator.go

@@ -0,0 +1,1622 @@
+// Package govalidator is package of validators and sanitizers for strings, structs and collections.
+package govalidator
+
+import (
+	"bytes"
+	"crypto/rsa"
+	"crypto/x509"
+	"encoding/base64"
+	"encoding/json"
+	"encoding/pem"
+	"fmt"
+	"io/ioutil"
+	"net"
+	"net/url"
+	"reflect"
+	"regexp"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
+	"unicode"
+	"unicode/utf8"
+)
+
+var (
+	fieldsRequiredByDefault bool
+	nilPtrAllowedByRequired = false
+	notNumberRegexp         = regexp.MustCompile("[^0-9]+")
+	whiteSpacesAndMinus     = regexp.MustCompile(`[\s-]+`)
+	paramsRegexp            = regexp.MustCompile(`\(.*\)$`)
+)
+
+const maxURLRuneCount = 2083
+const minURLRuneCount = 3
+const rfc3339WithoutZone = "2006-01-02T15:04:05"
+
+// SetFieldsRequiredByDefault causes validation to fail when struct fields
+// do not include validations or are not explicitly marked as exempt (using `valid:"-"` or `valid:"email,optional"`).
+// This struct definition will fail govalidator.ValidateStruct() (and the field values do not matter):
+//     type exampleStruct struct {
+//         Name  string ``
+//         Email string `valid:"email"`
+// This, however, will only fail when Email is empty or an invalid email address:
+//     type exampleStruct2 struct {
+//         Name  string `valid:"-"`
+//         Email string `valid:"email"`
+// Lastly, this will only fail when Email is an invalid email address but not when it's empty:
+//     type exampleStruct2 struct {
+//         Name  string `valid:"-"`
+//         Email string `valid:"email,optional"`
+func SetFieldsRequiredByDefault(value bool) {
+	fieldsRequiredByDefault = value
+}
+
+// SetNilPtrAllowedByRequired causes validation to pass for nil ptrs when a field is set to required.
+// The validation will still reject ptr fields in their zero value state. Example with this enabled:
+//     type exampleStruct struct {
+//         Name  *string `valid:"required"`
+// With `Name` set to "", this will be considered invalid input and will cause a validation error.
+// With `Name` set to nil, this will be considered valid by validation.
+// By default this is disabled.
+func SetNilPtrAllowedByRequired(value bool) {
+	nilPtrAllowedByRequired = value
+}
+
+// IsEmail checks if the string is an email.
+func IsEmail(str string) bool {
+	// TODO uppercase letters are not supported
+	return rxEmail.MatchString(str)
+}
+
+// IsExistingEmail checks if the string is an email of existing domain
+func IsExistingEmail(email string) bool {
+
+	if len(email) < 6 || len(email) > 254 {
+		return false
+	}
+	at := strings.LastIndex(email, "@")
+	if at <= 0 || at > len(email)-3 {
+		return false
+	}
+	user := email[:at]
+	host := email[at+1:]
+	if len(user) > 64 {
+		return false
+	}
+	switch host {
+	case "localhost", "example.com":
+		return true
+	}
+	if userDotRegexp.MatchString(user) || !userRegexp.MatchString(user) || !hostRegexp.MatchString(host) {
+		return false
+	}
+	if _, err := net.LookupMX(host); err != nil {
+		if _, err := net.LookupIP(host); err != nil {
+			return false
+		}
+	}
+
+	return true
+}
+
+// IsURL checks if the string is an URL.
+func IsURL(str string) bool {
+	if str == "" || utf8.RuneCountInString(str) >= maxURLRuneCount || len(str) <= minURLRuneCount || strings.HasPrefix(str, ".") {
+		return false
+	}
+	strTemp := str
+	if strings.Contains(str, ":") && !strings.Contains(str, "://") {
+		// support no indicated urlscheme but with colon for port number
+		// http:// is appended so url.Parse will succeed, strTemp used so it does not impact rxURL.MatchString
+		strTemp = "http://" + str
+	}
+	u, err := url.Parse(strTemp)
+	if err != nil {
+		return false
+	}
+	if strings.HasPrefix(u.Host, ".") {
+		return false
+	}
+	if u.Host == "" && (u.Path != "" && !strings.Contains(u.Path, ".")) {
+		return false
+	}
+	return rxURL.MatchString(str)
+}
+
+// IsRequestURL checks if the string rawurl, assuming
+// it was received in an HTTP request, is a valid
+// URL confirm to RFC 3986
+func IsRequestURL(rawurl string) bool {
+	url, err := url.ParseRequestURI(rawurl)
+	if err != nil {
+		return false //Couldn't even parse the rawurl
+	}
+	if len(url.Scheme) == 0 {
+		return false //No Scheme found
+	}
+	return true
+}
+
+// IsRequestURI checks if the string rawurl, assuming
+// it was received in an HTTP request, is an
+// absolute URI or an absolute path.
+func IsRequestURI(rawurl string) bool {
+	_, err := url.ParseRequestURI(rawurl)
+	return err == nil
+}
+
+// IsAlpha checks if the string contains only letters (a-zA-Z). Empty string is valid.
+func IsAlpha(str string) bool {
+	if IsNull(str) {
+		return true
+	}
+	return rxAlpha.MatchString(str)
+}
+
+//IsUTFLetter checks if the string contains only unicode letter characters.
+//Similar to IsAlpha but for all languages. Empty string is valid.
+func IsUTFLetter(str string) bool {
+	if IsNull(str) {
+		return true
+	}
+
+	for _, c := range str {
+		if !unicode.IsLetter(c) {
+			return false
+		}
+	}
+	return true
+
+}
+
+// IsAlphanumeric checks if the string contains only letters and numbers. Empty string is valid.
+func IsAlphanumeric(str string) bool {
+	if IsNull(str) {
+		return true
+	}
+	return rxAlphanumeric.MatchString(str)
+}
+
+// IsUTFLetterNumeric checks if the string contains only unicode letters and numbers. Empty string is valid.
+func IsUTFLetterNumeric(str string) bool {
+	if IsNull(str) {
+		return true
+	}
+	for _, c := range str {
+		if !unicode.IsLetter(c) && !unicode.IsNumber(c) { //letters && numbers are ok
+			return false
+		}
+	}
+	return true
+
+}
+
+// IsNumeric checks if the string contains only numbers. Empty string is valid.
+func IsNumeric(str string) bool {
+	if IsNull(str) {
+		return true
+	}
+	return rxNumeric.MatchString(str)
+}
+
+// IsUTFNumeric checks if the string contains only unicode numbers of any kind.
+// Numbers can be 0-9 but also Fractions ¾,Roman Ⅸ and Hangzhou 〩. Empty string is valid.
+func IsUTFNumeric(str string) bool {
+	if IsNull(str) {
+		return true
+	}
+	if strings.IndexAny(str, "+-") > 0 {
+		return false
+	}
+	if len(str) > 1 {
+		str = strings.TrimPrefix(str, "-")
+		str = strings.TrimPrefix(str, "+")
+	}
+	for _, c := range str {
+		if !unicode.IsNumber(c) { //numbers && minus sign are ok
+			return false
+		}
+	}
+	return true
+
+}
+
+// IsUTFDigit checks if the string contains only unicode radix-10 decimal digits. Empty string is valid.
+func IsUTFDigit(str string) bool {
+	if IsNull(str) {
+		return true
+	}
+	if strings.IndexAny(str, "+-") > 0 {
+		return false
+	}
+	if len(str) > 1 {
+		str = strings.TrimPrefix(str, "-")
+		str = strings.TrimPrefix(str, "+")
+	}
+	for _, c := range str {
+		if !unicode.IsDigit(c) { //digits && minus sign are ok
+			return false
+		}
+	}
+	return true
+
+}
+
+// IsHexadecimal checks if the string is a hexadecimal number.
+func IsHexadecimal(str string) bool {
+	return rxHexadecimal.MatchString(str)
+}
+
+// IsHexcolor checks if the string is a hexadecimal color.
+func IsHexcolor(str string) bool {
+	return rxHexcolor.MatchString(str)
+}
+
+// IsRGBcolor checks if the string is a valid RGB color in form rgb(RRR, GGG, BBB).
+func IsRGBcolor(str string) bool {
+	return rxRGBcolor.MatchString(str)
+}
+
+// IsLowerCase checks if the string is lowercase. Empty string is valid.
+func IsLowerCase(str string) bool {
+	if IsNull(str) {
+		return true
+	}
+	return str == strings.ToLower(str)
+}
+
+// IsUpperCase checks if the string is uppercase. Empty string is valid.
+func IsUpperCase(str string) bool {
+	if IsNull(str) {
+		return true
+	}
+	return str == strings.ToUpper(str)
+}
+
+// HasLowerCase checks if the string contains at least 1 lowercase. Empty string is valid.
+func HasLowerCase(str string) bool {
+	if IsNull(str) {
+		return true
+	}
+	return rxHasLowerCase.MatchString(str)
+}
+
+// HasUpperCase checks if the string contains as least 1 uppercase. Empty string is valid.
+func HasUpperCase(str string) bool {
+	if IsNull(str) {
+		return true
+	}
+	return rxHasUpperCase.MatchString(str)
+}
+
+// IsInt checks if the string is an integer. Empty string is valid.
+func IsInt(str string) bool {
+	if IsNull(str) {
+		return true
+	}
+	return rxInt.MatchString(str)
+}
+
+// IsFloat checks if the string is a float.
+func IsFloat(str string) bool {
+	return str != "" && rxFloat.MatchString(str)
+}
+
+// IsDivisibleBy checks if the string is a number that's divisible by another.
+// If second argument is not valid integer or zero, it's return false.
+// Otherwise, if first argument is not valid integer or zero, it's return true (Invalid string converts to zero).
+func IsDivisibleBy(str, num string) bool {
+	f, _ := ToFloat(str)
+	p := int64(f)
+	q, _ := ToInt(num)
+	if q == 0 {
+		return false
+	}
+	return (p == 0) || (p%q == 0)
+}
+
+// IsNull checks if the string is null.
+func IsNull(str string) bool {
+	return len(str) == 0
+}
+
+// IsNotNull checks if the string is not null.
+func IsNotNull(str string) bool {
+	return !IsNull(str)
+}
+
+// HasWhitespaceOnly checks the string only contains whitespace
+func HasWhitespaceOnly(str string) bool {
+	return len(str) > 0 && rxHasWhitespaceOnly.MatchString(str)
+}
+
+// HasWhitespace checks if the string contains any whitespace
+func HasWhitespace(str string) bool {
+	return len(str) > 0 && rxHasWhitespace.MatchString(str)
+}
+
+// IsByteLength checks if the string's length (in bytes) falls in a range.
+func IsByteLength(str string, min, max int) bool {
+	return len(str) >= min && len(str) <= max
+}
+
+// IsUUIDv3 checks if the string is a UUID version 3.
+func IsUUIDv3(str string) bool {
+	return rxUUID3.MatchString(str)
+}
+
+// IsUUIDv4 checks if the string is a UUID version 4.
+func IsUUIDv4(str string) bool {
+	return rxUUID4.MatchString(str)
+}
+
+// IsUUIDv5 checks if the string is a UUID version 5.
+func IsUUIDv5(str string) bool {
+	return rxUUID5.MatchString(str)
+}
+
+// IsUUID checks if the string is a UUID (version 3, 4 or 5).
+func IsUUID(str string) bool {
+	return rxUUID.MatchString(str)
+}
+
+// IsCreditCard checks if the string is a credit card.
+func IsCreditCard(str string) bool {
+	sanitized := notNumberRegexp.ReplaceAllString(str, "")
+	if !rxCreditCard.MatchString(sanitized) {
+		return false
+	}
+	var sum int64
+	var digit string
+	var tmpNum int64
+	var shouldDouble bool
+	for i := len(sanitized) - 1; i >= 0; i-- {
+		digit = sanitized[i:(i + 1)]
+		tmpNum, _ = ToInt(digit)
+		if shouldDouble {
+			tmpNum *= 2
+			if tmpNum >= 10 {
+				sum += (tmpNum % 10) + 1
+			} else {
+				sum += tmpNum
+			}
+		} else {
+			sum += tmpNum
+		}
+		shouldDouble = !shouldDouble
+	}
+
+	return sum%10 == 0
+}
+
+// IsISBN10 checks if the string is an ISBN version 10.
+func IsISBN10(str string) bool {
+	return IsISBN(str, 10)
+}
+
+// IsISBN13 checks if the string is an ISBN version 13.
+func IsISBN13(str string) bool {
+	return IsISBN(str, 13)
+}
+
+// IsISBN checks if the string is an ISBN (version 10 or 13).
+// If version value is not equal to 10 or 13, it will be checks both variants.
+func IsISBN(str string, version int) bool {
+	sanitized := whiteSpacesAndMinus.ReplaceAllString(str, "")
+	var checksum int32
+	var i int32
+	if version == 10 {
+		if !rxISBN10.MatchString(sanitized) {
+			return false
+		}
+		for i = 0; i < 9; i++ {
+			checksum += (i + 1) * int32(sanitized[i]-'0')
+		}
+		if sanitized[9] == 'X' {
+			checksum += 10 * 10
+		} else {
+			checksum += 10 * int32(sanitized[9]-'0')
+		}
+		if checksum%11 == 0 {
+			return true
+		}
+		return false
+	} else if version == 13 {
+		if !rxISBN13.MatchString(sanitized) {
+			return false
+		}
+		factor := []int32{1, 3}
+		for i = 0; i < 12; i++ {
+			checksum += factor[i%2] * int32(sanitized[i]-'0')
+		}
+		return (int32(sanitized[12]-'0'))-((10-(checksum%10))%10) == 0
+	}
+	return IsISBN(str, 10) || IsISBN(str, 13)
+}
+
+// IsJSON checks if the string is valid JSON (note: uses json.Unmarshal).
+func IsJSON(str string) bool {
+	var js json.RawMessage
+	return json.Unmarshal([]byte(str), &js) == nil
+}
+
+// IsMultibyte checks if the string contains one or more multibyte chars. Empty string is valid.
+func IsMultibyte(str string) bool {
+	if IsNull(str) {
+		return true
+	}
+	return rxMultibyte.MatchString(str)
+}
+
+// IsASCII checks if the string contains ASCII chars only. Empty string is valid.
+func IsASCII(str string) bool {
+	if IsNull(str) {
+		return true
+	}
+	return rxASCII.MatchString(str)
+}
+
+// IsPrintableASCII checks if the string contains printable ASCII chars only. Empty string is valid.
+func IsPrintableASCII(str string) bool {
+	if IsNull(str) {
+		return true
+	}
+	return rxPrintableASCII.MatchString(str)
+}
+
+// IsFullWidth checks if the string contains any full-width chars. Empty string is valid.
+func IsFullWidth(str string) bool {
+	if IsNull(str) {
+		return true
+	}
+	return rxFullWidth.MatchString(str)
+}
+
+// IsHalfWidth checks if the string contains any half-width chars. Empty string is valid.
+func IsHalfWidth(str string) bool {
+	if IsNull(str) {
+		return true
+	}
+	return rxHalfWidth.MatchString(str)
+}
+
+// IsVariableWidth checks if the string contains a mixture of full and half-width chars. Empty string is valid.
+func IsVariableWidth(str string) bool {
+	if IsNull(str) {
+		return true
+	}
+	return rxHalfWidth.MatchString(str) && rxFullWidth.MatchString(str)
+}
+
+// IsBase64 checks if a string is base64 encoded.
+func IsBase64(str string) bool {
+	return rxBase64.MatchString(str)
+}
+
+// IsFilePath checks is a string is Win or Unix file path and returns it's type.
+func IsFilePath(str string) (bool, int) {
+	if rxWinPath.MatchString(str) {
+		//check windows path limit see:
+		//  http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath
+		if len(str[3:]) > 32767 {
+			return false, Win
+		}
+		return true, Win
+	} else if rxUnixPath.MatchString(str) {
+		return true, Unix
+	}
+	return false, Unknown
+}
+
+// IsDataURI checks if a string is base64 encoded data URI such as an image
+func IsDataURI(str string) bool {
+	dataURI := strings.Split(str, ",")
+	if !rxDataURI.MatchString(dataURI[0]) {
+		return false
+	}
+	return IsBase64(dataURI[1])
+}
+
+// IsMagnetURI checks if a string is valid magnet URI
+func IsMagnetURI(str string) bool {
+	return rxMagnetURI.MatchString(str)
+}
+
+// IsISO3166Alpha2 checks if a string is valid two-letter country code
+func IsISO3166Alpha2(str string) bool {
+	for _, entry := range ISO3166List {
+		if str == entry.Alpha2Code {
+			return true
+		}
+	}
+	return false
+}
+
+// IsISO3166Alpha3 checks if a string is valid three-letter country code
+func IsISO3166Alpha3(str string) bool {
+	for _, entry := range ISO3166List {
+		if str == entry.Alpha3Code {
+			return true
+		}
+	}
+	return false
+}
+
+// IsISO693Alpha2 checks if a string is valid two-letter language code
+func IsISO693Alpha2(str string) bool {
+	for _, entry := range ISO693List {
+		if str == entry.Alpha2Code {
+			return true
+		}
+	}
+	return false
+}
+
+// IsISO693Alpha3b checks if a string is valid three-letter language code
+func IsISO693Alpha3b(str string) bool {
+	for _, entry := range ISO693List {
+		if str == entry.Alpha3bCode {
+			return true
+		}
+	}
+	return false
+}
+
+// IsDNSName will validate the given string as a DNS name
+func IsDNSName(str string) bool {
+	if str == "" || len(strings.Replace(str, ".", "", -1)) > 255 {
+		// constraints already violated
+		return false
+	}
+	return !IsIP(str) && rxDNSName.MatchString(str)
+}
+
+// IsHash checks if a string is a hash of type algorithm.
+// Algorithm is one of ['md4', 'md5', 'sha1', 'sha256', 'sha384', 'sha512', 'ripemd128', 'ripemd160', 'tiger128', 'tiger160', 'tiger192', 'crc32', 'crc32b']
+func IsHash(str string, algorithm string) bool {
+	var len string
+	algo := strings.ToLower(algorithm)
+
+	if algo == "crc32" || algo == "crc32b" {
+		len = "8"
+	} else if algo == "md5" || algo == "md4" || algo == "ripemd128" || algo == "tiger128" {
+		len = "32"
+	} else if algo == "sha1" || algo == "ripemd160" || algo == "tiger160" {
+		len = "40"
+	} else if algo == "tiger192" {
+		len = "48"
+	} else if algo == "sha256" {
+		len = "64"
+	} else if algo == "sha384" {
+		len = "96"
+	} else if algo == "sha512" {
+		len = "128"
+	} else {
+		return false
+	}
+
+	return Matches(str, "^[a-f0-9]{"+len+"}$")
+}
+
+// IsSHA512 checks is a string is a SHA512 hash. Alias for `IsHash(str, "sha512")`
+func IsSHA512(str string) bool {
+	return IsHash(str, "sha512")
+}
+
+// IsSHA384 checks is a string is a SHA384 hash. Alias for `IsHash(str, "sha384")`
+func IsSHA384(str string) bool {
+	return IsHash(str, "sha384")
+}
+
+// IsSHA256 checks is a string is a SHA256 hash. Alias for `IsHash(str, "sha256")`
+func IsSHA256(str string) bool {
+	return IsHash(str, "sha256")
+}
+
+// IsTiger192 checks is a string is a Tiger192 hash. Alias for `IsHash(str, "tiger192")`
+func IsTiger192(str string) bool {
+	return IsHash(str, "tiger192")
+}
+
+// IsTiger160 checks is a string is a Tiger160 hash. Alias for `IsHash(str, "tiger160")`
+func IsTiger160(str string) bool {
+	return IsHash(str, "tiger160")
+}
+
+// IsRipeMD160 checks is a string is a RipeMD160 hash. Alias for `IsHash(str, "ripemd160")`
+func IsRipeMD160(str string) bool {
+	return IsHash(str, "ripemd160")
+}
+
+// IsSHA1 checks is a string is a SHA-1 hash. Alias for `IsHash(str, "sha1")`
+func IsSHA1(str string) bool {
+	return IsHash(str, "sha1")
+}
+
+// IsTiger128 checks is a string is a Tiger128 hash. Alias for `IsHash(str, "tiger128")`
+func IsTiger128(str string) bool {
+	return IsHash(str, "tiger128")
+}
+
+// IsRipeMD128 checks is a string is a RipeMD128 hash. Alias for `IsHash(str, "ripemd128")`
+func IsRipeMD128(str string) bool {
+	return IsHash(str, "ripemd128")
+}
+
+// IsCRC32 checks is a string is a CRC32 hash. Alias for `IsHash(str, "crc32")`
+func IsCRC32(str string) bool {
+	return IsHash(str, "crc32")
+}
+
+// IsCRC32b checks is a string is a CRC32b hash. Alias for `IsHash(str, "crc32b")`
+func IsCRC32b(str string) bool {
+	return IsHash(str, "crc32b")
+}
+
+// IsMD5 checks is a string is a MD5 hash. Alias for `IsHash(str, "md5")`
+func IsMD5(str string) bool {
+	return IsHash(str, "md5")
+}
+
+// IsMD4 checks is a string is a MD4 hash. Alias for `IsHash(str, "md4")`
+func IsMD4(str string) bool {
+	return IsHash(str, "md4")
+}
+
+// IsDialString validates the given string for usage with the various Dial() functions
+func IsDialString(str string) bool {
+	if h, p, err := net.SplitHostPort(str); err == nil && h != "" && p != "" && (IsDNSName(h) || IsIP(h)) && IsPort(p) {
+		return true
+	}
+
+	return false
+}
+
+// IsIP checks if a string is either IP version 4 or 6. Alias for `net.ParseIP`
+func IsIP(str string) bool {
+	return net.ParseIP(str) != nil
+}
+
+// IsPort checks if a string represents a valid port
+func IsPort(str string) bool {
+	if i, err := strconv.Atoi(str); err == nil && i > 0 && i < 65536 {
+		return true
+	}
+	return false
+}
+
+// IsIPv4 checks if the string is an IP version 4.
+func IsIPv4(str string) bool {
+	ip := net.ParseIP(str)
+	return ip != nil && strings.Contains(str, ".")
+}
+
+// IsIPv6 checks if the string is an IP version 6.
+func IsIPv6(str string) bool {
+	ip := net.ParseIP(str)
+	return ip != nil && strings.Contains(str, ":")
+}
+
+// IsCIDR checks if the string is an valid CIDR notiation (IPV4 & IPV6)
+func IsCIDR(str string) bool {
+	_, _, err := net.ParseCIDR(str)
+	return err == nil
+}
+
+// IsMAC checks if a string is valid MAC address.
+// Possible MAC formats:
+// 01:23:45:67:89:ab
+// 01:23:45:67:89:ab:cd:ef
+// 01-23-45-67-89-ab
+// 01-23-45-67-89-ab-cd-ef
+// 0123.4567.89ab
+// 0123.4567.89ab.cdef
+func IsMAC(str string) bool {
+	_, err := net.ParseMAC(str)
+	return err == nil
+}
+
+// IsHost checks if the string is a valid IP (both v4 and v6) or a valid DNS name
+func IsHost(str string) bool {
+	return IsIP(str) || IsDNSName(str)
+}
+
+// IsMongoID checks if the string is a valid hex-encoded representation of a MongoDB ObjectId.
+func IsMongoID(str string) bool {
+	return rxHexadecimal.MatchString(str) && (len(str) == 24)
+}
+
+// IsLatitude checks if a string is valid latitude.
+func IsLatitude(str string) bool {
+	return rxLatitude.MatchString(str)
+}
+
+// IsLongitude checks if a string is valid longitude.
+func IsLongitude(str string) bool {
+	return rxLongitude.MatchString(str)
+}
+
+// IsIMEI checks if a string is valid IMEI
+func IsIMEI(str string) bool {
+	return rxIMEI.MatchString(str)
+}
+
+// IsIMSI checks if a string is valid IMSI
+func IsIMSI(str string) bool {
+	if !rxIMSI.MatchString(str) {
+		return false
+	}
+
+	mcc, err := strconv.ParseInt(str[0:3], 10, 32)
+	if err != nil {
+		return false
+	}
+
+	switch mcc {
+	case 202, 204, 206, 208, 212, 213, 214, 216, 218, 219:
+	case 220, 221, 222, 226, 228, 230, 231, 232, 234, 235:
+	case 238, 240, 242, 244, 246, 247, 248, 250, 255, 257:
+	case 259, 260, 262, 266, 268, 270, 272, 274, 276, 278:
+	case 280, 282, 283, 284, 286, 288, 289, 290, 292, 293:
+	case 294, 295, 297, 302, 308, 310, 311, 312, 313, 314:
+	case 315, 316, 330, 332, 334, 338, 340, 342, 344, 346:
+	case 348, 350, 352, 354, 356, 358, 360, 362, 363, 364:
+	case 365, 366, 368, 370, 372, 374, 376, 400, 401, 402:
+	case 404, 405, 406, 410, 412, 413, 414, 415, 416, 417:
+	case 418, 419, 420, 421, 422, 424, 425, 426, 427, 428:
+	case 429, 430, 431, 432, 434, 436, 437, 438, 440, 441:
+	case 450, 452, 454, 455, 456, 457, 460, 461, 466, 467:
+	case 470, 472, 502, 505, 510, 514, 515, 520, 525, 528:
+	case 530, 536, 537, 539, 540, 541, 542, 543, 544, 545:
+	case 546, 547, 548, 549, 550, 551, 552, 553, 554, 555:
+	case 602, 603, 604, 605, 606, 607, 608, 609, 610, 611:
+	case 612, 613, 614, 615, 616, 617, 618, 619, 620, 621:
+	case 622, 623, 624, 625, 626, 627, 628, 629, 630, 631:
+	case 632, 633, 634, 635, 636, 637, 638, 639, 640, 641:
+	case 642, 643, 645, 646, 647, 648, 649, 650, 651, 652:
+	case 653, 654, 655, 657, 658, 659, 702, 704, 706, 708:
+	case 710, 712, 714, 716, 722, 724, 730, 732, 734, 736:
+	case 738, 740, 742, 744, 746, 748, 750, 995:
+		return true
+	default:
+		return false
+	}
+	return true
+}
+
+// IsRsaPublicKey checks if a string is valid public key with provided length
+func IsRsaPublicKey(str string, keylen int) bool {
+	bb := bytes.NewBufferString(str)
+	pemBytes, err := ioutil.ReadAll(bb)
+	if err != nil {
+		return false
+	}
+	block, _ := pem.Decode(pemBytes)
+	if block != nil && block.Type != "PUBLIC KEY" {
+		return false
+	}
+	var der []byte
+
+	if block != nil {
+		der = block.Bytes
+	} else {
+		der, err = base64.StdEncoding.DecodeString(str)
+		if err != nil {
+			return false
+		}
+	}
+
+	key, err := x509.ParsePKIXPublicKey(der)
+	if err != nil {
+		return false
+	}
+	pubkey, ok := key.(*rsa.PublicKey)
+	if !ok {
+		return false
+	}
+	bitlen := len(pubkey.N.Bytes()) * 8
+	return bitlen == int(keylen)
+}
+
+func toJSONName(tag string) string {
+	if tag == "" {
+		return ""
+	}
+
+	// JSON name always comes first. If there's no options then split[0] is
+	// JSON name, if JSON name is not set, then split[0] is an empty string.
+	split := strings.SplitN(tag, ",", 2)
+
+	name := split[0]
+
+	// However it is possible that the field is skipped when
+	// (de-)serializing from/to JSON, in which case assume that there is no
+	// tag name to use
+	if name == "-" {
+		return ""
+	}
+	return name
+}
+
+func prependPathToErrors(err error, path string) error {
+	switch err2 := err.(type) {
+	case Error:
+		err2.Path = append([]string{path}, err2.Path...)
+		return err2
+	case Errors:
+		errors := err2.Errors()
+		for i, err3 := range errors {
+			errors[i] = prependPathToErrors(err3, path)
+		}
+		return err2
+	}
+	return err
+}
+
+// ValidateMap use validation map for fields.
+// result will be equal to `false` if there are any errors.
+// s is the map containing the data to be validated.
+// m is the validation map in the form:
+//   map[string]interface{}{"name":"required,alpha","address":map[string]interface{}{"line1":"required,alphanum"}}
+func ValidateMap(s map[string]interface{}, m map[string]interface{}) (bool, error) {
+	if s == nil {
+		return true, nil
+	}
+	result := true
+	var err error
+	var errs Errors
+	var index int
+	val := reflect.ValueOf(s)
+	for key, value := range s {
+		presentResult := true
+		validator, ok := m[key]
+		if !ok {
+			presentResult = false
+			var err error
+			err = fmt.Errorf("all map keys has to be present in the validation map; got %s", key)
+			err = prependPathToErrors(err, key)
+			errs = append(errs, err)
+		}
+		valueField := reflect.ValueOf(value)
+		mapResult := true
+		typeResult := true
+		structResult := true
+		resultField := true
+		switch subValidator := validator.(type) {
+		case map[string]interface{}:
+			var err error
+			if v, ok := value.(map[string]interface{}); !ok {
+				mapResult = false
+				err = fmt.Errorf("map validator has to be for the map type only; got %s", valueField.Type().String())
+				err = prependPathToErrors(err, key)
+				errs = append(errs, err)
+			} else {
+				mapResult, err = ValidateMap(v, subValidator)
+				if err != nil {
+					mapResult = false
+					err = prependPathToErrors(err, key)
+					errs = append(errs, err)
+				}
+			}
+		case string:
+			if (valueField.Kind() == reflect.Struct ||
+				(valueField.Kind() == reflect.Ptr && valueField.Elem().Kind() == reflect.Struct)) &&
+				subValidator != "-" {
+				var err error
+				structResult, err = ValidateStruct(valueField.Interface())
+				if err != nil {
+					err = prependPathToErrors(err, key)
+					errs = append(errs, err)
+				}
+			}
+			resultField, err = typeCheck(valueField, reflect.StructField{
+				Name:      key,
+				PkgPath:   "",
+				Type:      val.Type(),
+				Tag:       reflect.StructTag(fmt.Sprintf("%s:%q", tagName, subValidator)),
+				Offset:    0,
+				Index:     []int{index},
+				Anonymous: false,
+			}, val, nil)
+			if err != nil {
+				errs = append(errs, err)
+			}
+		case nil:
+			// already handlerd when checked before
+		default:
+			typeResult = false
+			err = fmt.Errorf("map validator has to be either map[string]interface{} or string; got %s", valueField.Type().String())
+			err = prependPathToErrors(err, key)
+			errs = append(errs, err)
+		}
+		result = result && presentResult && typeResult && resultField && structResult && mapResult
+		index++
+	}
+	// checks required keys
+	requiredResult := true
+	for key, value := range m {
+		if schema, ok := value.(string); ok {
+			tags := parseTagIntoMap(schema)
+			if required, ok := tags["required"]; ok {
+				if _, ok := s[key]; !ok {
+					requiredResult = false
+					if required.customErrorMessage != "" {
+						err = Error{key, fmt.Errorf(required.customErrorMessage), true, "required", []string{}}
+					} else {
+						err = Error{key, fmt.Errorf("required field missing"), false, "required", []string{}}
+					}
+					errs = append(errs, err)
+				}
+			}
+		}
+	}
+
+	if len(errs) > 0 {
+		err = errs
+	}
+	return result && requiredResult, err
+}
+
+// ValidateStruct use tags for fields.
+// result will be equal to `false` if there are any errors.
+// todo currently there is no guarantee that errors will be returned in predictable order (tests may to fail)
+func ValidateStruct(s interface{}) (bool, error) {
+	if s == nil {
+		return true, nil
+	}
+	result := true
+	var err error
+	val := reflect.ValueOf(s)
+	if val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr {
+		val = val.Elem()
+	}
+	// we only accept structs
+	if val.Kind() != reflect.Struct {
+		return false, fmt.Errorf("function only accepts structs; got %s", val.Kind())
+	}
+	var errs Errors
+	for i := 0; i < val.NumField(); i++ {
+		valueField := val.Field(i)
+		typeField := val.Type().Field(i)
+		if typeField.PkgPath != "" {
+			continue // Private field
+		}
+		structResult := true
+		if valueField.Kind() == reflect.Interface {
+			valueField = valueField.Elem()
+		}
+		if (valueField.Kind() == reflect.Struct ||
+			(valueField.Kind() == reflect.Ptr && valueField.Elem().Kind() == reflect.Struct)) &&
+			typeField.Tag.Get(tagName) != "-" {
+			var err error
+			structResult, err = ValidateStruct(valueField.Interface())
+			if err != nil {
+				err = prependPathToErrors(err, typeField.Name)
+				errs = append(errs, err)
+			}
+		}
+		resultField, err2 := typeCheck(valueField, typeField, val, nil)
+		if err2 != nil {
+
+			// Replace structure name with JSON name if there is a tag on the variable
+			jsonTag := toJSONName(typeField.Tag.Get("json"))
+			if jsonTag != "" {
+				switch jsonError := err2.(type) {
+				case Error:
+					jsonError.Name = jsonTag
+					err2 = jsonError
+				case Errors:
+					for i2, err3 := range jsonError {
+						switch customErr := err3.(type) {
+						case Error:
+							customErr.Name = jsonTag
+							jsonError[i2] = customErr
+						}
+					}
+
+					err2 = jsonError
+				}
+			}
+
+			errs = append(errs, err2)
+		}
+		result = result && resultField && structResult
+	}
+	if len(errs) > 0 {
+		err = errs
+	}
+	return result, err
+}
+
+// ValidateStructAsync performs async validation of the struct and returns results through the channels
+func ValidateStructAsync(s interface{}) (<-chan bool, <-chan error) {
+	res := make(chan bool)
+	errors := make(chan error)
+
+	go func() {
+		defer close(res)
+		defer close(errors)
+
+		isValid, isFailed := ValidateStruct(s)
+
+		res <- isValid
+		errors <- isFailed
+	}()
+
+	return res, errors
+}
+
+// ValidateMapAsync performs async validation of the map and returns results through the channels
+func ValidateMapAsync(s map[string]interface{}, m map[string]interface{}) (<-chan bool, <-chan error) {
+	res := make(chan bool)
+	errors := make(chan error)
+
+	go func() {
+		defer close(res)
+		defer close(errors)
+
+		isValid, isFailed := ValidateMap(s, m)
+
+		res <- isValid
+		errors <- isFailed
+	}()
+
+	return res, errors
+}
+
+// parseTagIntoMap parses a struct tag `valid:required~Some error message,length(2|3)` into map[string]string{"required": "Some error message", "length(2|3)": ""}
+func parseTagIntoMap(tag string) tagOptionsMap {
+	optionsMap := make(tagOptionsMap)
+	options := strings.Split(tag, ",")
+
+	for i, option := range options {
+		option = strings.TrimSpace(option)
+
+		validationOptions := strings.Split(option, "~")
+		if !isValidTag(validationOptions[0]) {
+			continue
+		}
+		if len(validationOptions) == 2 {
+			optionsMap[validationOptions[0]] = tagOption{validationOptions[0], validationOptions[1], i}
+		} else {
+			optionsMap[validationOptions[0]] = tagOption{validationOptions[0], "", i}
+		}
+	}
+	return optionsMap
+}
+
+func isValidTag(s string) bool {
+	if s == "" {
+		return false
+	}
+	for _, c := range s {
+		switch {
+		case strings.ContainsRune("\\'\"!#$%&()*+-./:<=>?@[]^_{|}~ ", c):
+			// Backslash and quote chars are reserved, but
+			// otherwise any punctuation chars are allowed
+			// in a tag name.
+		default:
+			if !unicode.IsLetter(c) && !unicode.IsDigit(c) {
+				return false
+			}
+		}
+	}
+	return true
+}
+
+// IsSSN will validate the given string as a U.S. Social Security Number
+func IsSSN(str string) bool {
+	if str == "" || len(str) != 11 {
+		return false
+	}
+	return rxSSN.MatchString(str)
+}
+
+// IsSemver checks if string is valid semantic version
+func IsSemver(str string) bool {
+	return rxSemver.MatchString(str)
+}
+
+// IsType checks if interface is of some type
+func IsType(v interface{}, params ...string) bool {
+	if len(params) == 1 {
+		typ := params[0]
+		return strings.Replace(reflect.TypeOf(v).String(), " ", "", -1) == strings.Replace(typ, " ", "", -1)
+	}
+	return false
+}
+
+// IsTime checks if string is valid according to given format
+func IsTime(str string, format string) bool {
+	_, err := time.Parse(format, str)
+	return err == nil
+}
+
+// IsUnixTime checks if string is valid unix timestamp value
+func IsUnixTime(str string) bool {
+	if _, err := strconv.Atoi(str); err == nil {
+		return true
+	}
+	return false
+}
+
+// IsRFC3339 checks if string is valid timestamp value according to RFC3339
+func IsRFC3339(str string) bool {
+	return IsTime(str, time.RFC3339)
+}
+
+// IsRFC3339WithoutZone checks if string is valid timestamp value according to RFC3339 which excludes the timezone.
+func IsRFC3339WithoutZone(str string) bool {
+	return IsTime(str, rfc3339WithoutZone)
+}
+
+// IsISO4217 checks if string is valid ISO currency code
+func IsISO4217(str string) bool {
+	for _, currency := range ISO4217List {
+		if str == currency {
+			return true
+		}
+	}
+
+	return false
+}
+
+// ByteLength checks string's length
+func ByteLength(str string, params ...string) bool {
+	if len(params) == 2 {
+		min, _ := ToInt(params[0])
+		max, _ := ToInt(params[1])
+		return len(str) >= int(min) && len(str) <= int(max)
+	}
+
+	return false
+}
+
+// RuneLength checks string's length
+// Alias for StringLength
+func RuneLength(str string, params ...string) bool {
+	return StringLength(str, params...)
+}
+
+// IsRsaPub checks whether string is valid RSA key
+// Alias for IsRsaPublicKey
+func IsRsaPub(str string, params ...string) bool {
+	if len(params) == 1 {
+		len, _ := ToInt(params[0])
+		return IsRsaPublicKey(str, int(len))
+	}
+
+	return false
+}
+
+// StringMatches checks if a string matches a given pattern.
+func StringMatches(s string, params ...string) bool {
+	if len(params) == 1 {
+		pattern := params[0]
+		return Matches(s, pattern)
+	}
+	return false
+}
+
+// StringLength checks string's length (including multi byte strings)
+func StringLength(str string, params ...string) bool {
+
+	if len(params) == 2 {
+		strLength := utf8.RuneCountInString(str)
+		min, _ := ToInt(params[0])
+		max, _ := ToInt(params[1])
+		return strLength >= int(min) && strLength <= int(max)
+	}
+
+	return false
+}
+
+// MinStringLength checks string's minimum length (including multi byte strings)
+func MinStringLength(str string, params ...string) bool {
+
+	if len(params) == 1 {
+		strLength := utf8.RuneCountInString(str)
+		min, _ := ToInt(params[0])
+		return strLength >= int(min)
+	}
+
+	return false
+}
+
+// MaxStringLength checks string's maximum length (including multi byte strings)
+func MaxStringLength(str string, params ...string) bool {
+
+	if len(params) == 1 {
+		strLength := utf8.RuneCountInString(str)
+		max, _ := ToInt(params[0])
+		return strLength <= int(max)
+	}
+
+	return false
+}
+
+// Range checks string's length
+func Range(str string, params ...string) bool {
+	if len(params) == 2 {
+		value, _ := ToFloat(str)
+		min, _ := ToFloat(params[0])
+		max, _ := ToFloat(params[1])
+		return InRange(value, min, max)
+	}
+
+	return false
+}
+
+// IsInRaw checks if string is in list of allowed values
+func IsInRaw(str string, params ...string) bool {
+	if len(params) == 1 {
+		rawParams := params[0]
+
+		parsedParams := strings.Split(rawParams, "|")
+
+		return IsIn(str, parsedParams...)
+	}
+
+	return false
+}
+
+// IsIn checks if string str is a member of the set of strings params
+func IsIn(str string, params ...string) bool {
+	for _, param := range params {
+		if str == param {
+			return true
+		}
+	}
+
+	return false
+}
+
+func checkRequired(v reflect.Value, t reflect.StructField, options tagOptionsMap) (bool, error) {
+	if nilPtrAllowedByRequired {
+		k := v.Kind()
+		if (k == reflect.Ptr || k == reflect.Interface) && v.IsNil() {
+			return true, nil
+		}
+	}
+
+	if requiredOption, isRequired := options["required"]; isRequired {
+		if len(requiredOption.customErrorMessage) > 0 {
+			return false, Error{t.Name, fmt.Errorf(requiredOption.customErrorMessage), true, "required", []string{}}
+		}
+		return false, Error{t.Name, fmt.Errorf("non zero value required"), false, "required", []string{}}
+	} else if _, isOptional := options["optional"]; fieldsRequiredByDefault && !isOptional {
+		return false, Error{t.Name, fmt.Errorf("Missing required field"), false, "required", []string{}}
+	}
+	// not required and empty is valid
+	return true, nil
+}
+
+func typeCheck(v reflect.Value, t reflect.StructField, o reflect.Value, options tagOptionsMap) (isValid bool, resultErr error) {
+	if !v.IsValid() {
+		return false, nil
+	}
+
+	tag := t.Tag.Get(tagName)
+
+	// checks if the field should be ignored
+	switch tag {
+	case "":
+		if v.Kind() != reflect.Slice && v.Kind() != reflect.Map {
+			if !fieldsRequiredByDefault {
+				return true, nil
+			}
+			return false, Error{t.Name, fmt.Errorf("All fields are required to at least have one validation defined"), false, "required", []string{}}
+		}
+	case "-":
+		return true, nil
+	}
+
+	isRootType := false
+	if options == nil {
+		isRootType = true
+		options = parseTagIntoMap(tag)
+	}
+
+	if isEmptyValue(v) {
+		// an empty value is not validated, checks only required
+		isValid, resultErr = checkRequired(v, t, options)
+		for key := range options {
+			delete(options, key)
+		}
+		return isValid, resultErr
+	}
+
+	var customTypeErrors Errors
+	optionsOrder := options.orderedKeys()
+	for _, validatorName := range optionsOrder {
+		validatorStruct := options[validatorName]
+		if validatefunc, ok := CustomTypeTagMap.Get(validatorName); ok {
+			delete(options, validatorName)
+
+			if result := validatefunc(v.Interface(), o.Interface()); !result {
+				if len(validatorStruct.customErrorMessage) > 0 {
+					customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: TruncatingErrorf(validatorStruct.customErrorMessage, fmt.Sprint(v), validatorName), CustomErrorMessageExists: true, Validator: stripParams(validatorName)})
+					continue
+				}
+				customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: fmt.Errorf("%s does not validate as %s", fmt.Sprint(v), validatorName), CustomErrorMessageExists: false, Validator: stripParams(validatorName)})
+			}
+		}
+	}
+
+	if len(customTypeErrors.Errors()) > 0 {
+		return false, customTypeErrors
+	}
+
+	if isRootType {
+		// Ensure that we've checked the value by all specified validators before report that the value is valid
+		defer func() {
+			delete(options, "optional")
+			delete(options, "required")
+
+			if isValid && resultErr == nil && len(options) != 0 {
+				optionsOrder := options.orderedKeys()
+				for _, validator := range optionsOrder {
+					isValid = false
+					resultErr = Error{t.Name, fmt.Errorf(
+						"The following validator is invalid or can't be applied to the field: %q", validator), false, stripParams(validator), []string{}}
+					return
+				}
+			}
+		}()
+	}
+
+	for _, validatorSpec := range optionsOrder {
+		validatorStruct := options[validatorSpec]
+		var negate bool
+		validator := validatorSpec
+		customMsgExists := len(validatorStruct.customErrorMessage) > 0
+
+		// checks whether the tag looks like '!something' or 'something'
+		if validator[0] == '!' {
+			validator = validator[1:]
+			negate = true
+		}
+
+		// checks for interface param validators
+		for key, value := range InterfaceParamTagRegexMap {
+			ps := value.FindStringSubmatch(validator)
+			if len(ps) == 0 {
+				continue
+			}
+
+			validatefunc, ok := InterfaceParamTagMap[key]
+			if !ok {
+				continue
+			}
+
+			delete(options, validatorSpec)
+
+			field := fmt.Sprint(v)
+			if result := validatefunc(v.Interface(), ps[1:]...); (!result && !negate) || (result && negate) {
+				if customMsgExists {
+					return false, Error{t.Name, TruncatingErrorf(validatorStruct.customErrorMessage, field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
+				}
+				if negate {
+					return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
+				}
+				return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
+			}
+		}
+	}
+
+	switch v.Kind() {
+	case reflect.Bool,
+		reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
+		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
+		reflect.Float32, reflect.Float64,
+		reflect.String:
+		// for each tag option checks the map of validator functions
+		for _, validatorSpec := range optionsOrder {
+			validatorStruct := options[validatorSpec]
+			var negate bool
+			validator := validatorSpec
+			customMsgExists := len(validatorStruct.customErrorMessage) > 0
+
+			// checks whether the tag looks like '!something' or 'something'
+			if validator[0] == '!' {
+				validator = validator[1:]
+				negate = true
+			}
+
+			// checks for param validators
+			for key, value := range ParamTagRegexMap {
+				ps := value.FindStringSubmatch(validator)
+				if len(ps) == 0 {
+					continue
+				}
+
+				validatefunc, ok := ParamTagMap[key]
+				if !ok {
+					continue
+				}
+
+				delete(options, validatorSpec)
+
+				switch v.Kind() {
+				case reflect.String,
+					reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
+					reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
+					reflect.Float32, reflect.Float64:
+
+					field := fmt.Sprint(v) // make value into string, then validate with regex
+					if result := validatefunc(field, ps[1:]...); (!result && !negate) || (result && negate) {
+						if customMsgExists {
+							return false, Error{t.Name, TruncatingErrorf(validatorStruct.customErrorMessage, field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
+						}
+						if negate {
+							return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
+						}
+						return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
+					}
+				default:
+					// type not yet supported, fail
+					return false, Error{t.Name, fmt.Errorf("Validator %s doesn't support kind %s", validator, v.Kind()), false, stripParams(validatorSpec), []string{}}
+				}
+			}
+
+			if validatefunc, ok := TagMap[validator]; ok {
+				delete(options, validatorSpec)
+
+				switch v.Kind() {
+				case reflect.String,
+					reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
+					reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
+					reflect.Float32, reflect.Float64:
+					field := fmt.Sprint(v) // make value into string, then validate with regex
+					if result := validatefunc(field); !result && !negate || result && negate {
+						if customMsgExists {
+							return false, Error{t.Name, TruncatingErrorf(validatorStruct.customErrorMessage, field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
+						}
+						if negate {
+							return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
+						}
+						return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
+					}
+				default:
+					//Not Yet Supported Types (Fail here!)
+					err := fmt.Errorf("Validator %s doesn't support kind %s for value %v", validator, v.Kind(), v)
+					return false, Error{t.Name, err, false, stripParams(validatorSpec), []string{}}
+				}
+			}
+		}
+		return true, nil
+	case reflect.Map:
+		if v.Type().Key().Kind() != reflect.String {
+			return false, &UnsupportedTypeError{v.Type()}
+		}
+		var sv stringValues
+		sv = v.MapKeys()
+		sort.Sort(sv)
+		result := true
+		for i, k := range sv {
+			var resultItem bool
+			var err error
+			if v.MapIndex(k).Kind() != reflect.Struct {
+				resultItem, err = typeCheck(v.MapIndex(k), t, o, options)
+				if err != nil {
+					return false, err
+				}
+			} else {
+				resultItem, err = ValidateStruct(v.MapIndex(k).Interface())
+				if err != nil {
+					err = prependPathToErrors(err, t.Name+"."+sv[i].Interface().(string))
+					return false, err
+				}
+			}
+			result = result && resultItem
+		}
+		return result, nil
+	case reflect.Slice, reflect.Array:
+		result := true
+		for i := 0; i < v.Len(); i++ {
+			var resultItem bool
+			var err error
+			if v.Index(i).Kind() != reflect.Struct {
+				resultItem, err = typeCheck(v.Index(i), t, o, options)
+				if err != nil {
+					return false, err
+				}
+			} else {
+				resultItem, err = ValidateStruct(v.Index(i).Interface())
+				if err != nil {
+					err = prependPathToErrors(err, t.Name+"."+strconv.Itoa(i))
+					return false, err
+				}
+			}
+			result = result && resultItem
+		}
+		return result, nil
+	case reflect.Interface:
+		// If the value is an interface then encode its element
+		if v.IsNil() {
+			return true, nil
+		}
+		return ValidateStruct(v.Interface())
+	case reflect.Ptr:
+		// If the value is a pointer then checks its element
+		if v.IsNil() {
+			return true, nil
+		}
+		return typeCheck(v.Elem(), t, o, options)
+	case reflect.Struct:
+		return true, nil
+	default:
+		return false, &UnsupportedTypeError{v.Type()}
+	}
+}
+
+func stripParams(validatorString string) string {
+	return paramsRegexp.ReplaceAllString(validatorString, "")
+}
+
+// isEmptyValue checks whether value empty or not
+func isEmptyValue(v reflect.Value) bool {
+	switch v.Kind() {
+	case reflect.String, reflect.Array:
+		return v.Len() == 0
+	case reflect.Map, reflect.Slice:
+		return v.Len() == 0 || v.IsNil()
+	case reflect.Bool:
+		return !v.Bool()
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		return v.Int() == 0
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+		return v.Uint() == 0
+	case reflect.Float32, reflect.Float64:
+		return v.Float() == 0
+	case reflect.Interface, reflect.Ptr:
+		return v.IsNil()
+	}
+
+	return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())
+}
+
+// ErrorByField returns error for specified field of the struct
+// validated by ValidateStruct or empty string if there are no errors
+// or this field doesn't exists or doesn't have any errors.
+func ErrorByField(e error, field string) string {
+	if e == nil {
+		return ""
+	}
+	return ErrorsByField(e)[field]
+}
+
+// ErrorsByField returns map of errors of the struct validated
+// by ValidateStruct or empty map if there are no errors.
+func ErrorsByField(e error) map[string]string {
+	m := make(map[string]string)
+	if e == nil {
+		return m
+	}
+	// prototype for ValidateStruct
+
+	switch e := e.(type) {
+	case Error:
+		m[e.Name] = e.Err.Error()
+	case Errors:
+		for _, item := range e.Errors() {
+			n := ErrorsByField(item)
+			for k, v := range n {
+				m[k] = v
+			}
+		}
+	}
+
+	return m
+}
+
+// Error returns string equivalent for reflect.Type
+func (e *UnsupportedTypeError) Error() string {
+	return "validator: unsupported type: " + e.Type.String()
+}
+
+func (sv stringValues) Len() int           { return len(sv) }
+func (sv stringValues) Swap(i, j int)      { sv[i], sv[j] = sv[j], sv[i] }
+func (sv stringValues) Less(i, j int) bool { return sv.get(i) < sv.get(j) }
+func (sv stringValues) get(i int) string   { return sv[i].String() }

+ 133 - 0
jy_member/client/client.go

@@ -0,0 +1,133 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"log"
+	"net/rpc"
+)
+
+type FcRequest struct {
+	RedisFKey string
+	InfoId string
+	Id string
+	Pname string
+	Purchasing string
+	Buyer string
+	Area string
+	City string
+	Budget float64
+	BuyerContent []string
+
+}
+
+type JyResponse struct {
+	Rep []map[string]interface{}
+}
+
+type FcResponse struct {
+	Rep bool
+}
+
+//附件
+type AtRequest struct {
+	InfoId string
+}
+
+var id, pname, area, city, content string
+
+func main() {
+	//
+	//flag.StringVar(&id, "id", "", "项目id")
+	//flag.StringVar(&pname, "pname", "", "项目名称")
+	//flag.StringVar(&area, "area", "", "省份")
+	//flag.StringVar(&city, "city", "", "城市")
+	//flag.StringVar(&content, "content", "", "标的物,逗号隔开")
+	//flag.Parse()
+	//
+	//if id != "" && pname != "" && content != "" {
+	//	conn, err := rpc.DialHTTP("tcp", "172.17.145.178:10082")
+	//	if err != nil {
+	//		log.Fatalln("dailing error: ", err)
+	//	}
+	//	req := &FcRequest{
+	//		RedisFKey: "forcast_res_" + id,
+	//		Id: id,
+	//		Pname: pname,
+	//		Area: area,
+	//		City: city,
+	//		BuyerContent: strings.Split(content, ","),
+	//	}
+	//	var res FcResponse
+	//	err = conn.Call("JyService.Forecast", req, &res)
+	//	if err != nil {
+	//		log.Fatalln("JyService error: ", err)
+	//	}
+	//	fmt.Println("-------", res)
+	//}else {
+	//	flag.PrintDefaults()
+	//}
+
+	flag.StringVar(&id, "id", "", "公告id")
+	flag.Parse()
+	if id != "" {
+		conn, err := rpc.DialHTTP("tcp", "192.168.3.128:10082")
+		if err != nil {
+			log.Fatalln("dailing error: ", err)
+		}
+		req := AtRequest{
+			InfoId: id,
+		}
+		var res JyResponse
+		err = conn.Call("JyService.Download", req, &res)
+		if err != nil {
+			log.Fatalln("JyService error: ", err)
+		}
+		fmt.Println("-------", req.InfoId, res.Rep)
+	}else {
+		flag.PrintDefaults()
+	}
+}
+
+//服务地址配置
+//const (
+//	rdserver = "192.168.3.240:10021"
+//
+//	//TODO 目前实现前2中
+//	ERROR_ERTRY_TYPE_FIRST    = iota //1次,找到第一个服务提供者,出错即返回
+//	ERROR_ERTRY_TYPE_FIRST_5         //5次尝试,找到第一个服务提供者
+//	ERROR_ERTRY_TYPE_OVER_ALL        //尝试遍历所有服务,直至又一个提供正常返回
+//)
+//
+//func main() {
+//	conn, err := grpc.Dial(rdserver, grpc.WithInsecure())
+//	if err != nil {
+//		log.Println(err)
+//	}
+//	var client proto.ServiceClient
+//	client = proto.NewServiceClient(conn)
+//	repl, err := client.Apply(context.Background(), &proto.ApplyReqData{Name: "winner_predict", Balance: 0})
+//	if err != nil {
+//		log.Println(err)
+//	}
+//	log.Println("结果", repl.Ip, repl.Port)
+//	//2.业务调用
+//	addr := fmt.Sprintf("%s:%d", repl.Ip, repl.Port)
+//	conn_b, err := grpc.Dial(addr, grpc.WithInsecure())
+//	if err != nil {
+//		log.Println(err)
+//	}
+//	defer conn_b.Close()
+//	pc := proto.NewWinnerPredictServiceClient(conn_b)
+//	rep, err := pc.Predict(context.Background(), &proto.PredictReq{
+//		ProjectName: "北京拓普",
+//		Mount: 3500000.01,
+//		BidUnit: "河南中国",
+//		Area: "河南",
+//		City: "郑州",
+//		Goods: []string{"笔记本1", "笔记本2"},
+//		Winner: "经营部",
+//		WinnerOrder: []string{"胜利1", "胜利2", "胜利3"},
+//	})
+//	log.Println(rep.Item)
+//}

+ 19 - 0
jy_member/config.json

@@ -0,0 +1,19 @@
+{
+  "mongodbServers": "192.168.3.207:27092",
+  "mongodbPoolSize": 10,
+  "mongodbName": "qfw_data",
+  "mongodbColl": "bidding",
+  "mongodbColl1": "bidding_back",
+  "uname": "root",
+  "upwd": "root",
+  "es": {
+    "addr": "http://192.168.3.128:9800",
+    "index": "projectset",
+    "itype": "projectset",
+    "pool": 10
+  },
+  "file_type": ["pdf", "doc", "docx", "xlsx", "xls", "txt", "jpg", "JPG"],
+  "serverCenter": "192.168.3.240:10021",
+  "rpcServer": "192.168.20.65:10082",
+  "redis": "predict_pro=192.168.3.128:1712"
+}

+ 51 - 0
jy_member/es_util.go

@@ -0,0 +1,51 @@
+package main
+
+type QueryObject struct {
+	Must    []interface{} `json:"must,omitempty"`
+	MustNot []interface{} `json:"must_not,omitempty"`
+	Should  []interface{} `json:"should,omitempty"`
+}
+
+type NesEsObject struct {
+	Query QueryObject `json:"bool,omitempty"`
+}
+
+type PnameObj struct {
+	Match ProjectName `json:"match,omitempty"`
+}
+
+type ProjectName struct {
+	Pname string `json:"s_projectname,omitempty"`
+}
+
+type PurObj struct {
+	Match Purchasing `json:"terms,omitempty"`
+}
+
+type Purchasing struct {
+	Pur []string `json:"purchasing,omitempty"`
+}
+
+type BuyerObj struct {
+	Match Buyer `json:"match,omitempty"`
+}
+
+type Buyer struct {
+	Buyer string `json:"buyer,omitempty"`
+}
+
+type AreaObj struct {
+	Match Area `json:"match,omitempty"`
+}
+
+type Area struct {
+	Area string `json:"area,omitempty"`
+}
+
+type CityObj struct {
+	Match City `json:"match,omitempty"`
+}
+
+type City struct {
+	City string `json:"city,omitempty"`
+}

+ 13 - 0
jy_member/go.mod

@@ -0,0 +1,13 @@
+module jy_member
+
+go 1.14
+
+require (
+	app.yhyue.com/BP/servicerd v0.0.0-20200921011231-05967cb5f5cf
+	github.com/golang/protobuf v1.4.2
+	google.golang.org/grpc v1.31.0
+	google.golang.org/protobuf v1.25.0
+	utils v0.0.0
+)
+
+replace utils => ../common_utils

+ 217 - 0
jy_member/go.sum

@@ -0,0 +1,217 @@
+app.yhyue.com/BP/servicerd v0.0.0-20200921011231-05967cb5f5cf h1:+W66n+Nz32wF+yXE1F8Z79SbJyYIH+HT6Ws4Vu3vXLA=
+app.yhyue.com/BP/servicerd v0.0.0-20200921011231-05967cb5f5cf/go.mod h1:vDomUktO0sHs06GW9XSrNjYA8oOkrVDB+TvFKeKSsUU=
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
+github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
+github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
+github.com/aws/aws-sdk-go v1.29.15 h1:0ms/213murpsujhsnxnNKNeVouW60aJqSd992Ks3mxs=
+github.com/aws/aws-sdk-go v1.29.15/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTgahTma5Xg=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f h1:q/DpyjJjZs94bziQ7YkBmIlpqbVP7yw179rnzoNVX1M=
+github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f/go.mod h1:QGrK8vMWWHQYQ3QU9bw9Y9OPNfxccGzfb41qjvVeXtY=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
+github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
+github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
+github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
+github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
+github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs=
+github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
+github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
+github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk=
+github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28=
+github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo=
+github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk=
+github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw=
+github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360=
+github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg=
+github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE=
+github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8=
+github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
+github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
+github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
+github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
+github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
+github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
+github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k=
+github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/hashicorp/go-immutable-radix v1.2.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-memdb v1.2.1/go.mod h1:OSvLJ662Jim8hMM+gWGyhktyWk2xPCnWMc7DWIqtkGA=
+github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
+github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
+github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
+github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
+github.com/klauspost/compress v1.9.5 h1:U+CaK85mrNNb4k8BNOfgJtJ/gr6kswUCFj6miSzVC6M=
+github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
+github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
+github.com/mattn/go-sqlite3 v1.14.2/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
+github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/shirou/gopsutil v2.20.6+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
+github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
+github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk=
+github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
+github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc h1:n+nNi93yXLkJvKwXNP9d55HC7lGK4H/SRcwB5IaUZLo=
+github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
+go.mongodb.org/mongo-driver v1.4.1 h1:38NSAyDPagwnFpUA/D5SFgbugUYR3NzYRNa4Qk9UxKs=
+go.mongodb.org/mongo-driver v1.4.1/go.mod h1:llVBH2pkj9HywK0Dtdt6lDikOjFLbceHVu/Rc0iMKLs=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
+golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 h1:8dUaAV7K4uHsF56JQWkprecIQKdPHtR9jCHF5nB8uzc=
+golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
+golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2 h1:T5DasATyLQfmbTpfEXx/IOL9vfjzW6up+ZDkmHvIf2s=
+golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.31.0 h1:T7P4R73V3SSDPhH7WW7ATbfViLtmamH0DKrP3f9AuDI=
+google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw=
+gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
+gopkg.in/olivere/elastic.v2 v2.0.61 h1:7cpl3MW8ysa4GYFBXklpo5mspe4NK0rpZTdyZ+QcD4U=
+gopkg.in/olivere/elastic.v2 v2.0.61/go.mod h1:CTVyl1gckiFw1aLZYxC00g3f9jnHmhoOKcWF7W3c6n4=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

+ 76 - 0
jy_member/main.go

@@ -0,0 +1,76 @@
+package main
+
+import (
+	"fmt"
+	"net"
+	"net/http"
+	"net/rpc"
+	"os"
+	"regexp"
+	util "utils"
+	"utils/elastic"
+	"utils/mongodb"
+	"utils/redis"
+)
+
+var (
+	Sysconfig          map[string]interface{}
+	FileType		   []interface{}
+	MongoTool          *mongodb.MongodbSim
+	Dbname             string
+	MgoColl		   	   string
+	MgoColl1		   string
+	Es 			 	   *elastic.Elastic
+	Index        	   string
+	Itype        	   string
+	RpcServer		   string
+	ClientAddr		   string
+)
+
+var (
+	_ent = regexp.MustCompile(".{2,100}(集团|公司|学校|中心|家具城|门诊|\\\\[大中小\\\\]学|部|院|局|厂|店|所|队|社|室|厅|段|会|场|行)$")
+	down_url = "https://jy-datafile.oss-cn-beijing.aliyuncs.com/"
+)
+
+func init() {
+	util.ReadConfig(&Sysconfig)
+	FileType = Sysconfig["file_type"].([]interface {})
+	Dbname = Sysconfig["mongodbName"].(string)
+	MgoColl = Sysconfig["mongodbColl"].(string)
+	MgoColl1 = Sysconfig["mongodbColl1"].(string)
+	MongoTool = &mongodb.MongodbSim{
+		MongodbAddr: Sysconfig["mongodbServers"].(string),
+		Size:        util.IntAllDef(Sysconfig["mongodbPoolSize"], 5),
+		DbName:      Dbname,
+		UserName:	 Sysconfig["uname"].(string),
+		Password:	 Sysconfig["upwd"].(string),
+	}
+	MongoTool.InitPool()
+	es := Sysconfig["es"].(map[string]interface{})
+	Es = &elastic.Elastic{
+		S_esurl: util.ObjToString(es["addr"]),
+		I_size:  util.IntAllDef(es["pool"], 10),
+	}
+	Index = util.ObjToString(es["index"])
+	Itype = util.ObjToString(es["itype"])
+	Es.InitElasticSize()
+	ClientAddr = Sysconfig["serverCenter"].(string)
+	RpcServer = Sysconfig["rpcServer"].(string)
+	//初始化redis
+	redis.InitRedisBySize(util.ObjToString(Sysconfig["redis"]), 50, 30, 240)
+}
+
+func initRpc()  {
+	rpc.Register(new(JyService)) // 注册rpc服务
+	rpc.HandleHTTP()         // 采用http协议作为rpc载体
+	lis, err := net.Listen("tcp", RpcServer)
+	if err != nil {
+		util.Debug("fatal error: ", err)
+	}
+	_, _ = fmt.Fprintf(os.Stdout, "%s\n", "start connection")
+	_ = http.Serve(lis, nil)
+}
+
+func main()  {
+	initRpc()
+}

+ 455 - 0
jy_member/proto/winnerpredict.pb.go

@@ -0,0 +1,455 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.23.0
+// 	protoc        v3.12.4
+// source: winnerpredict.proto
+
+//声明 包名
+
+package proto
+
+import (
+	context "context"
+	proto "github.com/golang/protobuf/proto"
+	grpc "google.golang.org/grpc"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// This is a compile-time assertion that a sufficiently up-to-date version
+// of the legacy proto package is being used.
+const _ = proto.ProtoPackageIsVersion4
+
+//请求参数
+type PredictReq struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	ProjectName string   `protobuf:"bytes,1,opt,name=ProjectName,proto3" json:"ProjectName,omitempty"` //项目名称
+	Mount       float32  `protobuf:"fixed32,2,opt,name=Mount,proto3" json:"Mount,omitempty"`           //金额
+	BidUnit     string   `protobuf:"bytes,3,opt,name=BidUnit,proto3" json:"BidUnit,omitempty"`         //招标单位
+	Area        string   `protobuf:"bytes,4,opt,name=Area,proto3" json:"Area,omitempty"`               //地域
+	City        string   `protobuf:"bytes,5,opt,name=City,proto3" json:"City,omitempty"`               //城市
+	Goods       []string `protobuf:"bytes,6,rep,name=Goods,proto3" json:"Goods,omitempty"`             //物品清单
+	Winner      string   `protobuf:"bytes,7,opt,name=Winner,proto3" json:"Winner,omitempty"`           //中标人
+	WinnerOrder []string `protobuf:"bytes,8,rep,name=WinnerOrder,proto3" json:"WinnerOrder,omitempty"` //中标候选人
+}
+
+func (x *PredictReq) Reset() {
+	*x = PredictReq{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_winnerpredict_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *PredictReq) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*PredictReq) ProtoMessage() {}
+
+func (x *PredictReq) ProtoReflect() protoreflect.Message {
+	mi := &file_winnerpredict_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use PredictReq.ProtoReflect.Descriptor instead.
+func (*PredictReq) Descriptor() ([]byte, []int) {
+	return file_winnerpredict_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *PredictReq) GetProjectName() string {
+	if x != nil {
+		return x.ProjectName
+	}
+	return ""
+}
+
+func (x *PredictReq) GetMount() float32 {
+	if x != nil {
+		return x.Mount
+	}
+	return 0
+}
+
+func (x *PredictReq) GetBidUnit() string {
+	if x != nil {
+		return x.BidUnit
+	}
+	return ""
+}
+
+func (x *PredictReq) GetArea() string {
+	if x != nil {
+		return x.Area
+	}
+	return ""
+}
+
+func (x *PredictReq) GetCity() string {
+	if x != nil {
+		return x.City
+	}
+	return ""
+}
+
+func (x *PredictReq) GetGoods() []string {
+	if x != nil {
+		return x.Goods
+	}
+	return nil
+}
+
+func (x *PredictReq) GetWinner() string {
+	if x != nil {
+		return x.Winner
+	}
+	return ""
+}
+
+func (x *PredictReq) GetWinnerOrder() []string {
+	if x != nil {
+		return x.WinnerOrder
+	}
+	return nil
+}
+
+//返回结果
+type PredictResp struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Item []*ResultEntiry `protobuf:"bytes,1,rep,name=Item,proto3" json:"Item,omitempty"`
+}
+
+func (x *PredictResp) Reset() {
+	*x = PredictResp{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_winnerpredict_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *PredictResp) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*PredictResp) ProtoMessage() {}
+
+func (x *PredictResp) ProtoReflect() protoreflect.Message {
+	mi := &file_winnerpredict_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use PredictResp.ProtoReflect.Descriptor instead.
+func (*PredictResp) Descriptor() ([]byte, []int) {
+	return file_winnerpredict_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *PredictResp) GetItem() []*ResultEntiry {
+	if x != nil {
+		return x.Item
+	}
+	return nil
+}
+
+//
+type ResultEntiry struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	EntName string  `protobuf:"bytes,1,opt,name=EntName,proto3" json:"EntName,omitempty"` //
+	Score   float32 `protobuf:"fixed32,2,opt,name=Score,proto3" json:"Score,omitempty"`
+	Weight  float32 `protobuf:"fixed32,3,opt,name=Weight,proto3" json:"Weight,omitempty"`
+}
+
+func (x *ResultEntiry) Reset() {
+	*x = ResultEntiry{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_winnerpredict_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ResultEntiry) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ResultEntiry) ProtoMessage() {}
+
+func (x *ResultEntiry) ProtoReflect() protoreflect.Message {
+	mi := &file_winnerpredict_proto_msgTypes[2]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ResultEntiry.ProtoReflect.Descriptor instead.
+func (*ResultEntiry) Descriptor() ([]byte, []int) {
+	return file_winnerpredict_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *ResultEntiry) GetEntName() string {
+	if x != nil {
+		return x.EntName
+	}
+	return ""
+}
+
+func (x *ResultEntiry) GetScore() float32 {
+	if x != nil {
+		return x.Score
+	}
+	return 0
+}
+
+func (x *ResultEntiry) GetWeight() float32 {
+	if x != nil {
+		return x.Weight
+	}
+	return 0
+}
+
+var File_winnerpredict_proto protoreflect.FileDescriptor
+
+var file_winnerpredict_proto_rawDesc = []byte{
+	0x0a, 0x13, 0x77, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x70, 0x72, 0x65, 0x64, 0x69, 0x63, 0x74, 0x2e,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd6, 0x01, 0x0a,
+	0x0a, 0x50, 0x72, 0x65, 0x64, 0x69, 0x63, 0x74, 0x52, 0x65, 0x71, 0x12, 0x20, 0x0a, 0x0b, 0x50,
+	0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x0b, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a,
+	0x05, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x4d, 0x6f,
+	0x75, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x42, 0x69, 0x64, 0x55, 0x6e, 0x69, 0x74, 0x18, 0x03,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x42, 0x69, 0x64, 0x55, 0x6e, 0x69, 0x74, 0x12, 0x12, 0x0a,
+	0x04, 0x41, 0x72, 0x65, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x41, 0x72, 0x65,
+	0x61, 0x12, 0x12, 0x0a, 0x04, 0x43, 0x69, 0x74, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x04, 0x43, 0x69, 0x74, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x18, 0x06,
+	0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x57,
+	0x69, 0x6e, 0x6e, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x57, 0x69, 0x6e,
+	0x6e, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x57, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x4f, 0x72, 0x64,
+	0x65, 0x72, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x57, 0x69, 0x6e, 0x6e, 0x65, 0x72,
+	0x4f, 0x72, 0x64, 0x65, 0x72, 0x22, 0x36, 0x0a, 0x0b, 0x50, 0x72, 0x65, 0x64, 0x69, 0x63, 0x74,
+	0x52, 0x65, 0x73, 0x70, 0x12, 0x27, 0x0a, 0x04, 0x49, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x03,
+	0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c,
+	0x74, 0x45, 0x6e, 0x74, 0x69, 0x72, 0x79, 0x52, 0x04, 0x49, 0x74, 0x65, 0x6d, 0x22, 0x56, 0x0a,
+	0x0c, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x45, 0x6e, 0x74, 0x69, 0x72, 0x79, 0x12, 0x18, 0x0a,
+	0x07, 0x45, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
+	0x45, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x53, 0x63, 0x6f, 0x72, 0x65,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x16, 0x0a,
+	0x06, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x52, 0x06, 0x57,
+	0x65, 0x69, 0x67, 0x68, 0x74, 0x32, 0x4a, 0x0a, 0x14, 0x57, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x50,
+	0x72, 0x65, 0x64, 0x69, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x32, 0x0a,
+	0x07, 0x50, 0x72, 0x65, 0x64, 0x69, 0x63, 0x74, 0x12, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x2e, 0x50, 0x72, 0x65, 0x64, 0x69, 0x63, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x72, 0x65, 0x64, 0x69, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x22,
+	0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_winnerpredict_proto_rawDescOnce sync.Once
+	file_winnerpredict_proto_rawDescData = file_winnerpredict_proto_rawDesc
+)
+
+func file_winnerpredict_proto_rawDescGZIP() []byte {
+	file_winnerpredict_proto_rawDescOnce.Do(func() {
+		file_winnerpredict_proto_rawDescData = protoimpl.X.CompressGZIP(file_winnerpredict_proto_rawDescData)
+	})
+	return file_winnerpredict_proto_rawDescData
+}
+
+var file_winnerpredict_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
+var file_winnerpredict_proto_goTypes = []interface{}{
+	(*PredictReq)(nil),   // 0: proto.PredictReq
+	(*PredictResp)(nil),  // 1: proto.PredictResp
+	(*ResultEntiry)(nil), // 2: proto.ResultEntiry
+}
+var file_winnerpredict_proto_depIdxs = []int32{
+	2, // 0: proto.PredictResp.Item:type_name -> proto.ResultEntiry
+	0, // 1: proto.WinnerPredictService.Predict:input_type -> proto.PredictReq
+	1, // 2: proto.WinnerPredictService.Predict:output_type -> proto.PredictResp
+	2, // [2:3] is the sub-list for method output_type
+	1, // [1:2] is the sub-list for method input_type
+	1, // [1:1] is the sub-list for extension type_name
+	1, // [1:1] is the sub-list for extension extendee
+	0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_winnerpredict_proto_init() }
+func file_winnerpredict_proto_init() {
+	if File_winnerpredict_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_winnerpredict_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*PredictReq); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_winnerpredict_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*PredictResp); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_winnerpredict_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ResultEntiry); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_winnerpredict_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   3,
+			NumExtensions: 0,
+			NumServices:   1,
+		},
+		GoTypes:           file_winnerpredict_proto_goTypes,
+		DependencyIndexes: file_winnerpredict_proto_depIdxs,
+		MessageInfos:      file_winnerpredict_proto_msgTypes,
+	}.Build()
+	File_winnerpredict_proto = out.File
+	file_winnerpredict_proto_rawDesc = nil
+	file_winnerpredict_proto_goTypes = nil
+	file_winnerpredict_proto_depIdxs = nil
+}
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConnInterface
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+const _ = grpc.SupportPackageIsVersion6
+
+// WinnerPredictServiceClient is the client API for WinnerPredictService service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
+type WinnerPredictServiceClient interface {
+	Predict(ctx context.Context, in *PredictReq, opts ...grpc.CallOption) (*PredictResp, error)
+}
+
+type winnerPredictServiceClient struct {
+	cc grpc.ClientConnInterface
+}
+
+func NewWinnerPredictServiceClient(cc grpc.ClientConnInterface) WinnerPredictServiceClient {
+	return &winnerPredictServiceClient{cc}
+}
+
+func (c *winnerPredictServiceClient) Predict(ctx context.Context, in *PredictReq, opts ...grpc.CallOption) (*PredictResp, error) {
+	out := new(PredictResp)
+	err := c.cc.Invoke(ctx, "/proto.WinnerPredictService/Predict", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+// WinnerPredictServiceServer is the server API for WinnerPredictService service.
+type WinnerPredictServiceServer interface {
+	Predict(context.Context, *PredictReq) (*PredictResp, error)
+}
+
+// UnimplementedWinnerPredictServiceServer can be embedded to have forward compatible implementations.
+type UnimplementedWinnerPredictServiceServer struct {
+}
+
+func (*UnimplementedWinnerPredictServiceServer) Predict(context.Context, *PredictReq) (*PredictResp, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Predict not implemented")
+}
+
+func RegisterWinnerPredictServiceServer(s *grpc.Server, srv WinnerPredictServiceServer) {
+	s.RegisterService(&_WinnerPredictService_serviceDesc, srv)
+}
+
+func _WinnerPredictService_Predict_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(PredictReq)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(WinnerPredictServiceServer).Predict(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/proto.WinnerPredictService/Predict",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(WinnerPredictServiceServer).Predict(ctx, req.(*PredictReq))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+var _WinnerPredictService_serviceDesc = grpc.ServiceDesc{
+	ServiceName: "proto.WinnerPredictService",
+	HandlerType: (*WinnerPredictServiceServer)(nil),
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "Predict",
+			Handler:    _WinnerPredictService_Predict_Handler,
+		},
+	},
+	Streams:  []grpc.StreamDesc{},
+	Metadata: "winnerpredict.proto",
+}

+ 32 - 0
jy_member/proto_src/winnerpredict.proto

@@ -0,0 +1,32 @@
+syntax = "proto3";//声明proto的版本 只能 是3,才支持 grpc
+
+//声明 包名
+package proto;
+
+//服务管理
+service WinnerPredictService {
+  rpc Predict(PredictReq)returns(PredictResp){}
+}
+
+//请求参数
+message PredictReq {
+    string ProjectName =1 ; //项目名称
+    float Mount=2;//金额
+    string BidUnit=3;//招标单位
+    string Area = 4;//地域
+    string City =5;//城市
+    repeated string Goods = 6;//物品清单
+    string Winner =7;//中标人
+    repeated string WinnerOrder = 8;//中标候选人
+}
+
+//返回结果
+message PredictResp {
+ repeated ResultEntiry Item=1;
+}
+//
+message ResultEntiry{
+    string EntName =1; //
+    float Score=2;
+    float Weight=3;
+}

+ 364 - 0
jy_member/service.go

@@ -0,0 +1,364 @@
+package main
+
+import (
+	"app.yhyue.com/BP/servicerd/proto"
+	"context"
+	"encoding/json"
+	"fmt"
+	"google.golang.org/grpc"
+	jyProto "jy_member/proto"
+	"net/http"
+	"strings"
+	"utils"
+	"utils/redis"
+	"utils/validator"
+)
+
+type JyService struct {
+}
+
+//附件
+type AtRequest struct {
+	InfoId string
+}
+
+type AtResponse struct {
+	Rep []map[string]interface{}
+}
+
+//预测
+type FcRequest struct {
+	RedisFKey    string
+	Id           string
+	Pname        string
+	Buyer        string
+	Area         string
+	City         string
+	Budget       float64
+	BuyerContent []string
+}
+
+type FcResponse struct {
+	Rep bool
+}
+
+func (j *JyService) Download(req AtRequest, res *AtResponse) error {
+	//util.Debug("Download----infoid:", req.InfoId)
+	if req.InfoId == "" {
+		return fmt.Errorf(" _id error")
+	}
+	var err error
+	res.Rep, err = Method(req.InfoId)
+	return err
+}
+
+func (j *JyService) Forecast(req FcRequest, res *FcResponse) error {
+	util.Debug("Forecast----ProjectId:", req)
+	if req.RedisFKey == "" {
+		res.Rep = false
+		return fmt.Errorf("RedisFKey is nil")
+	}
+	if req.Id == "" {
+		res.Rep = false
+		return fmt.Errorf("ProjectId is nil")
+	}
+	if req.Pname == "" {
+		res.Rep = false
+		return fmt.Errorf("ProjectName is nil")
+	}
+	if len(req.BuyerContent) <= 0 {
+		res.Rep = false
+		return fmt.Errorf("BuyerContent is nil")
+	}
+	go ForecastMethod(&req)
+	res.Rep = true
+	return nil
+}
+
+func Method(infoid string) ([]map[string]interface{}, error) {
+	fields := map[string]interface{}{"projectinfo": 1, "site": 1}
+	info := &map[string]interface{}{}
+	var resp []map[string]interface{}
+	b := false
+	info, b = MongoTool.FindById(MgoColl, infoid, fields)
+	if !b {
+		info, b = MongoTool.FindById(MgoColl1, infoid, fields)
+		if !b {
+			util.Debug("Not found data.")
+			return resp, fmt.Errorf("Not found data.")
+		}
+	}
+	if att, ok := (*info)["projectinfo"].(map[string]interface{}); ok {
+		file := util.ObjToMap(att["attachments"])
+		for _, v := range *file {
+			attachment := map[string]interface{}{}
+			if v1, ok := v.(map[string]interface{}); ok {
+				if valiType(v1) {
+					if ossid, ok := v1["ossid"].(string); ok {
+						attachment["downurl"] = down_url + ossid
+						attachment["filename"] = util.ObjToString(v1["filename"])
+						attachment["org_url"] = util.ObjToString(v1["org_url"])
+						attachment["size"] = util.ObjToString(v1["size"])
+						resp = append(resp, attachment)
+					} else if v1["fid"] != nil {
+						if util.ObjToString((*info)["site"]) == "中国招标投标公共服务平台" && util.ObjToString(v1["size"]) == "8.4 KB" {
+							break
+						}
+						attachment["downurl"] = down_url + util.ObjToString(v1["fid"])
+						attachment["org_url"] = util.ObjToString(v1["org_url"])
+						attachment["filename"] = util.ObjToString(v1["filename"])
+						attachment["size"] = util.ObjToString(v1["size"])
+						resp = append(resp, attachment)
+					} else {
+						if govalidator.IsURL(util.ObjToString(v1["org_url"])) && util.ObjToString((*info)["site"]) != "中国招标投标公共服务平台" &&
+							util.ObjToString(v1["filename"]) != "文件下载.jpg" && util.ObjToString(v1["filename"]) != "文件下载.png" {
+							attachment["downurl"] = util.ObjToString(v1["org_url"])
+							attachment["org_url"] = util.ObjToString(v1["org_url"])
+							attachment["filename"] = util.ObjToString(v1["filename"])
+							attachment["size"] = util.ObjToString(v1["size"])
+							resp = append(resp, attachment)
+						}
+					}
+				}
+			}
+		}
+	}
+	return resp, nil
+}
+
+func FindProject(projectId string, query map[string]interface{}) map[string]interface{} {
+	client := Es.GetEsConn()
+	defer Es.DestoryEsConn(client)
+	esquery := `{"query": {"bool": {"must": [{"match": {"_id": "` + projectId + `"}}]}}}`
+	data := Es.Get(Index, Itype, esquery)
+	util.Debug(*data)
+	if len(*data) > 0 {
+		tmp := (*data)[0]
+		if CheckContain(util.ObjToString(query["projectname"]), util.ObjToString(tmp["projectname"])) != 3 &&
+			CheckContain(util.ObjToString(query["purchasing"]), util.ObjToString(tmp["purchasing"])) != 3 &&
+			CheckContain(util.ObjToString(query["buyer"]), util.ObjToString(tmp["buyer"])) != 3 &&
+			CheckContain(util.ObjToString(query["area"]), util.ObjToString(tmp["area"])) != 3 &&
+			CheckContain(util.ObjToString(query["city"]), util.ObjToString(tmp["city"])) != 3 {
+			return tmp
+		} else {
+			//项目名称或者标的物不相同,查询项目
+			findPro := EsFindPro(query)
+			if util.ObjToString(findPro["_id"]) == projectId {
+				return findPro
+			} else {
+				//用户填写的内容
+				findPro["projectname"] = query["projectname"]
+				findPro["purchasing"] = query["purchasing"]
+				if query["buyer"] != "" {
+					findPro["buyer"] = query["buyer"]
+				}
+				if query["area"] != "" {
+					findPro["area"] = query["area"]
+				}
+				if query["city"] != "" {
+					findPro["city"] = query["city"]
+				}
+				if query["budget"] != 0 {
+					findPro["budget"] = query["budget"]
+				}
+			}
+			return findPro
+		}
+	} else {
+		return EsFindPro(query)
+	}
+}
+
+func EsFindPro(q map[string]interface{}) map[string]interface{} {
+	esObj := NesEsObject{}
+	if util.ObjToString(q["projectname"]) != "" {
+		pobj := PnameObj{Match: ProjectName{
+			Pname: util.ObjToString(q["projectname"]),
+		}}
+		esObj.Query.Should = append(esObj.Query.Should, pobj)
+	}
+	if q["purchasing"] != nil {
+		purObj := PurObj{Match: Purchasing{
+			Pur: q["purchasing"].([]string),
+		}}
+		esObj.Query.Should = append(esObj.Query.Should, purObj)
+	}
+	if util.ObjToString(q["buyer"]) != "" {
+		bObj := BuyerObj{Match: Buyer{
+			Buyer: util.ObjToString(q["buyer"]),
+		}}
+		esObj.Query.Should = append(esObj.Query.Should, bObj)
+	}
+	if util.ObjToString(q["area"]) != "" {
+		areaObj := AreaObj{Match: Area{
+			Area: util.ObjToString(q["area"]),
+		}}
+		esObj.Query.Should = append(esObj.Query.Should, areaObj)
+	}
+	if util.ObjToString(q["city"]) != "" {
+		cityObj := CityObj{Match: City{
+			City: util.ObjToString(q["city"]),
+		}}
+		esObj.Query.Should = append(esObj.Query.Should, cityObj)
+	}
+	rdata := make(map[string]interface{})
+	rdata["query"] = esObj
+	util.Debug(esObj)
+	util.Debug(rdata)
+	esbytes, _ := json.Marshal(rdata)
+	util.Debug(string(esbytes))
+	data := Es.Get(Index, Itype, string(esbytes))
+	if len(*data) > 0 {
+		return (*data)[0]
+	} else {
+		return map[string]interface{}{}
+	}
+}
+
+func getSize(url string) int64 {
+	resp, err := http.Get(url)
+	if err != nil {
+		fmt.Printf("HEAD failed: %v", err)
+	}
+	fmt.Printf("resp: %s\n", resp.Status)
+	fmt.Printf("size: %d\n", resp.ContentLength)
+	return resp.ContentLength
+}
+
+func valiType(tmp map[string]interface{}) bool {
+	for _, v := range FileType {
+		if util.ObjToString(v) == "*" {
+			return true
+		}
+		if strings.Contains(util.ObjToString(tmp["filename"]), util.ObjToString(v)) {
+			return true
+		}
+		if strings.Contains(util.ObjToString(tmp["ftype"]), util.ObjToString(v)) {
+			return true
+		}
+	}
+	return false
+}
+
+func CheckContain(b1, b2 string) (res int) {
+	if b1 == b2 {
+		res = 1 //相等
+		return
+	}
+	bs1 := []rune(b1)
+	bs2 := []rune(b2)
+	tmp := ""
+	for i := 0; i < len(bs1); i++ {
+		for j := 0; j < len(bs2); j++ {
+			if bs1[i] == bs2[j] {
+				tmp += string(bs1[i])
+			} else if tmp != "" {
+				b1 = strings.Replace(b1, tmp, "", -1)
+				b2 = strings.Replace(b2, tmp, "", -1)
+				tmp = ""
+			}
+		}
+	}
+	if tmp != "" {
+		b1 = strings.Replace(b1, tmp, "", -1)
+		b2 = strings.Replace(b2, tmp, "", -1)
+	}
+	if b1 == b2 {
+		res = 1 //相等
+	} else if b1 == "" || b2 == "" {
+		res = 2 //包含
+	} else {
+		res = 3 //不相同
+	}
+	return
+}
+
+func ForecastMethod(req *FcRequest) {
+	query := map[string]interface{}{}
+	query["projectname"] = req.Pname
+	query["purchasing"] = req.BuyerContent
+	if req.Buyer != "" {
+		query["buyer"] = req.Buyer
+	}
+	if req.Area != "" {
+		query["area"] = req.Area
+	}
+	if req.City != "" {
+		query["city"] = req.City
+	}
+	query["budget"] = req.Budget
+	result := FindProject(req.Id, query)
+	wins := []string{}
+	budget := 0.0
+	if len(result) > 0 {
+		if result["winnerorder"] != nil {
+			wins = util.ObjArrToStringArr(result["winnerorder"].([]interface{}))
+		}
+		if result["buyer"] != nil && query["buyer"] == nil {
+			query["buyer"] = result["buyer"]
+		}
+		if result["area"] != nil && query["area"] == nil {
+			query["area"] = result["area"]
+		}
+		if result["city"] != nil && query["city"] == nil {
+			query["city"] = result["city"]
+		}
+		if result["budget"] != nil {
+			budget = util.Float64All(result["budget"])
+		}
+		if result["winner"] != nil && _ent.MatchString(util.ObjToString(result["winner"])) {
+			query["winner"] = result["winner"]
+		}
+	}
+
+	reqs := &jyProto.PredictReq{
+		ProjectName: req.Pname,
+		Mount:       float32(budget),
+		BidUnit:     util.ObjToString(query["buyer"]),
+		Area:        util.ObjToString(query["area"]),
+		City:        util.ObjToString(query["city"]),
+		Goods:       req.BuyerContent,
+		Winner:      util.ObjToString(query["winner"]),
+		WinnerOrder: wins}
+	// 调用gRPC接口
+	util.Debug(reqs)
+	grpcConn(reqs, req.RedisFKey)
+}
+
+func grpcConn(reqs *jyProto.PredictReq, risKey string) {
+	conn, err := grpc.Dial(ClientAddr, grpc.WithInsecure())
+	if err != nil {
+		redis.Put("predict_pro", risKey, "", -1)
+		util.Debug(err)
+	}
+	var client proto.ServiceClient
+	client = proto.NewServiceClient(conn)
+	repl, err := client.Apply(context.Background(), &proto.ApplyReqData{Name: "winner_predict", Balance: 0})
+	if err != nil {
+		util.Debug(err)
+		redis.Put("predict_pro", risKey, "", -1)
+		return
+	}
+	util.Debug("结果", repl.Ip, repl.Port)
+	//2.业务调用
+	addr := fmt.Sprintf("%s:%d", repl.Ip, repl.Port)
+	conn_b, err := grpc.Dial(addr, grpc.WithInsecure())
+	if err != nil {
+		util.Debug(err)
+		redis.Put("predict_pro", risKey, "", -1)
+		return
+	}
+	defer conn_b.Close()
+	pc := jyProto.NewWinnerPredictServiceClient(conn_b)
+	rep, err := pc.Predict(context.Background(), reqs)
+	if err != nil {
+		redis.Put("predict_pro", risKey, "", -1)
+		util.Debug(err)
+	} else {
+		util.Debug(rep.Item)
+		jsonStr, _ := json.Marshal(rep.Item)
+		bol := redis.Put("predict_pro", risKey, string(jsonStr), -1)
+		util.Debug("预测结果:", bol)
+	}
+
+}

Some files were not shown because too many files changed in this diff