commit f1af811be5459e0b64843b28f9ce6ad799223cb2
Author: huangjiajun <582604932@qq.com>
Date: Wed Dec 7 17:01:38 2022 +0800
test
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..35410ca
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..42f0fc7
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/zyos_go_day_luck_draw.iml b/.idea/zyos_go_day_luck_draw.iml
new file mode 100644
index 0000000..5e764c4
--- /dev/null
+++ b/.idea/zyos_go_day_luck_draw.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/db/db_day_luck_draw_setting.go b/db/db_day_luck_draw_setting.go
new file mode 100644
index 0000000..d9f6c78
--- /dev/null
+++ b/db/db_day_luck_draw_setting.go
@@ -0,0 +1,36 @@
+package db
+
+import (
+ "code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git/db/model"
+ "xorm.io/xorm"
+)
+
+func GetDayLuckDrawSetting(eg *xorm.Engine) *model.DayLuckDrawSetting {
+ var data model.DayLuckDrawSetting
+ get, err := eg.Get(&data)
+ if err != nil {
+ return nil
+ }
+ if get == false {
+ one, err := eg.InsertOne(&data)
+ if one == 0 || err != nil {
+ return nil
+ }
+ }
+ return &data
+}
+
+func InsertDayLuckDrawSetting(eg *xorm.Engine, data *model.DayLuckDrawSetting) bool {
+ if data.Id == 0 {
+ one, err := eg.InsertOne(data)
+ if one == 0 || err != nil {
+ return false
+ }
+ } else {
+ update, err := eg.Where("id=?", data.Id).AllCols().Update(data)
+ if update == 0 || err != nil {
+ return false
+ }
+ }
+ return true
+}
diff --git a/db/model/day_luck_draw_order_jackpot.go b/db/model/day_luck_draw_order_jackpot.go
new file mode 100644
index 0000000..9c2b28e
--- /dev/null
+++ b/db/model/day_luck_draw_order_jackpot.go
@@ -0,0 +1,17 @@
+package model
+
+import (
+ "time"
+)
+
+type DayLuckDrawOrderJackpot struct {
+ Id int `json:"id" xorm:"not null pk autoincr INT(11)"`
+ Oid string `json:"oid" xorm:"index unique(oid_pvd) VARCHAR(255)"`
+ Pvd string `json:"pvd" xorm:"unique(oid_pvd) index VARCHAR(255)"`
+ Amount string `json:"amount" xorm:"DECIMAL(11,6)"`
+ Commission string `json:"commission" xorm:"DECIMAL(11,6)"`
+ Reward string `json:"reward" xorm:"DECIMAL(11,6)"`
+ Uid int `json:"uid" xorm:"index INT(11)"`
+ CreateTime time.Time `json:"create_time" xorm:"DATETIME"`
+ Period int `json:"period" xorm:"default 0 INT(11)"`
+}
diff --git a/db/model/day_luck_draw_period.go b/db/model/day_luck_draw_period.go
new file mode 100644
index 0000000..af0710d
--- /dev/null
+++ b/db/model/day_luck_draw_period.go
@@ -0,0 +1,12 @@
+package model
+
+import (
+ "time"
+)
+
+type DayLuckDrawPeriod struct {
+ Id int `json:"id" xorm:"not null pk autoincr INT(11)"`
+ Reward string `json:"reward" xorm:"VARCHAR(255)"`
+ CreateTime time.Time `json:"create_time" xorm:"DATETIME"`
+ Period int `json:"period" xorm:"default 0 INT(11)"`
+}
diff --git a/db/model/day_luck_draw_period_reward.go b/db/model/day_luck_draw_period_reward.go
new file mode 100644
index 0000000..5b2f249
--- /dev/null
+++ b/db/model/day_luck_draw_period_reward.go
@@ -0,0 +1,14 @@
+package model
+
+import (
+ "time"
+)
+
+type DayLuckDrawPeriodReward struct {
+ Id int `json:"id" xorm:"not null pk autoincr INT(11)"`
+ Reward string `json:"reward" xorm:"VARCHAR(255)"`
+ CreateTime time.Time `json:"create_time" xorm:"DATETIME"`
+ Period int `json:"period" xorm:"default 0 INT(11)"`
+ Uid int `json:"uid" xorm:"default 0 INT(11)"`
+ Lv int `json:"lv" xorm:"default 0 INT(11)"`
+}
diff --git a/db/model/day_luck_draw_setting.go b/db/model/day_luck_draw_setting.go
new file mode 100644
index 0000000..9e6aac1
--- /dev/null
+++ b/db/model/day_luck_draw_setting.go
@@ -0,0 +1,20 @@
+package model
+
+import (
+ "time"
+)
+
+type DayLuckDrawSetting struct {
+ Id int `json:"id" xorm:"not null pk autoincr INT(11)"`
+ IsUse int `json:"is_use" xorm:"default 0 INT(255)"`
+ StartTime time.Time `json:"start_time" xorm:"DATETIME"`
+ EndTime time.Time `json:"end_time" xorm:"DATETIME"`
+ Source string `json:"source" xorm:"comment('奖池来源 commission佣金 amount金额') VARCHAR(255)"`
+ CommissionBili string `json:"commission_bili" xorm:"comment('奖池为订单佣金时 存入订单佣金比例') VARCHAR(255)"`
+ OrderSoruce string `json:"order_soruce" xorm:"comment('订单来源 all导购+自营 mall 自营') VARCHAR(255)"`
+ OpenTime time.Time `json:"open_time" xorm:"DATETIME"`
+ CycleDay int `json:"cycle_day" xorm:"default 0 comment('周期') INT(11)"`
+ AdvUse int `json:"adv_use" xorm:"INT(11)"`
+ AdvType string `json:"adv_type" xorm:"VARCHAR(255)"`
+ RewardData string `json:"reward_data" xorm:"TEXT"`
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..834649e
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,15 @@
+module code.fnuoos.com/go_rely_warehouse/zyos_go_order_relate_rule.git
+
+go 1.15
+
+require (
+ github.com/go-redis/redis v6.15.9+incompatible
+ github.com/gomodule/redigo v1.8.9
+ github.com/jinzhu/copier v0.3.5
+ github.com/shopspring/decimal v1.3.1
+ github.com/syyongx/php2go v0.9.6
+ go.uber.org/zap v1.13.0
+ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f
+ gopkg.in/natefinch/lumberjack.v2 v2.0.0
+ xorm.io/xorm v1.3.1
+)
diff --git a/utils/cache/base.go b/utils/cache/base.go
new file mode 100644
index 0000000..64648dd
--- /dev/null
+++ b/utils/cache/base.go
@@ -0,0 +1,421 @@
+package cache
+
+import (
+ "errors"
+ "fmt"
+ "strconv"
+ "time"
+)
+
+const (
+ redisDialTTL = 10 * time.Second
+ redisReadTTL = 3 * time.Second
+ redisWriteTTL = 3 * time.Second
+ redisIdleTTL = 10 * time.Second
+ redisPoolTTL = 10 * time.Second
+ redisPoolSize int = 512
+ redisMaxIdleConn int = 64
+ redisMaxActive int = 512
+)
+
+var (
+ ErrNil = errors.New("nil return")
+ ErrWrongArgsNum = errors.New("args num error")
+ ErrNegativeInt = errors.New("redis cluster: unexpected value for Uint64")
+)
+
+// 以下为提供类型转换
+
+func Int(reply interface{}, err error) (int, error) {
+ if err != nil {
+ return 0, err
+ }
+ switch reply := reply.(type) {
+ case int:
+ return reply, nil
+ case int8:
+ return int(reply), nil
+ case int16:
+ return int(reply), nil
+ case int32:
+ return int(reply), nil
+ case int64:
+ x := int(reply)
+ if int64(x) != reply {
+ return 0, strconv.ErrRange
+ }
+ return x, nil
+ case uint:
+ n := int(reply)
+ if n < 0 {
+ return 0, strconv.ErrRange
+ }
+ return n, nil
+ case uint8:
+ return int(reply), nil
+ case uint16:
+ return int(reply), nil
+ case uint32:
+ n := int(reply)
+ if n < 0 {
+ return 0, strconv.ErrRange
+ }
+ return n, nil
+ case uint64:
+ n := int(reply)
+ if n < 0 {
+ return 0, strconv.ErrRange
+ }
+ return n, nil
+ case []byte:
+ data := string(reply)
+ if len(data) == 0 {
+ return 0, ErrNil
+ }
+
+ n, err := strconv.ParseInt(data, 10, 0)
+ return int(n), err
+ case string:
+ if len(reply) == 0 {
+ return 0, ErrNil
+ }
+
+ n, err := strconv.ParseInt(reply, 10, 0)
+ return int(n), err
+ case nil:
+ return 0, ErrNil
+ case error:
+ return 0, reply
+ }
+ return 0, fmt.Errorf("redis cluster: unexpected type for Int, got type %T", reply)
+}
+
+func Int64(reply interface{}, err error) (int64, error) {
+ if err != nil {
+ return 0, err
+ }
+ switch reply := reply.(type) {
+ case int:
+ return int64(reply), nil
+ case int8:
+ return int64(reply), nil
+ case int16:
+ return int64(reply), nil
+ case int32:
+ return int64(reply), nil
+ case int64:
+ return reply, nil
+ case uint:
+ n := int64(reply)
+ if n < 0 {
+ return 0, strconv.ErrRange
+ }
+ return n, nil
+ case uint8:
+ return int64(reply), nil
+ case uint16:
+ return int64(reply), nil
+ case uint32:
+ return int64(reply), nil
+ case uint64:
+ n := int64(reply)
+ if n < 0 {
+ return 0, strconv.ErrRange
+ }
+ return n, nil
+ case []byte:
+ data := string(reply)
+ if len(data) == 0 {
+ return 0, ErrNil
+ }
+
+ n, err := strconv.ParseInt(data, 10, 64)
+ return n, err
+ case string:
+ if len(reply) == 0 {
+ return 0, ErrNil
+ }
+
+ n, err := strconv.ParseInt(reply, 10, 64)
+ return n, err
+ case nil:
+ return 0, ErrNil
+ case error:
+ return 0, reply
+ }
+ return 0, fmt.Errorf("redis cluster: unexpected type for Int64, got type %T", reply)
+}
+
+func Uint64(reply interface{}, err error) (uint64, error) {
+ if err != nil {
+ return 0, err
+ }
+ switch reply := reply.(type) {
+ case uint:
+ return uint64(reply), nil
+ case uint8:
+ return uint64(reply), nil
+ case uint16:
+ return uint64(reply), nil
+ case uint32:
+ return uint64(reply), nil
+ case uint64:
+ return reply, nil
+ case int:
+ if reply < 0 {
+ return 0, ErrNegativeInt
+ }
+ return uint64(reply), nil
+ case int8:
+ if reply < 0 {
+ return 0, ErrNegativeInt
+ }
+ return uint64(reply), nil
+ case int16:
+ if reply < 0 {
+ return 0, ErrNegativeInt
+ }
+ return uint64(reply), nil
+ case int32:
+ if reply < 0 {
+ return 0, ErrNegativeInt
+ }
+ return uint64(reply), nil
+ case int64:
+ if reply < 0 {
+ return 0, ErrNegativeInt
+ }
+ return uint64(reply), nil
+ case []byte:
+ data := string(reply)
+ if len(data) == 0 {
+ return 0, ErrNil
+ }
+
+ n, err := strconv.ParseUint(data, 10, 64)
+ return n, err
+ case string:
+ if len(reply) == 0 {
+ return 0, ErrNil
+ }
+
+ n, err := strconv.ParseUint(reply, 10, 64)
+ return n, err
+ case nil:
+ return 0, ErrNil
+ case error:
+ return 0, reply
+ }
+ return 0, fmt.Errorf("redis cluster: unexpected type for Uint64, got type %T", reply)
+}
+
+func Float64(reply interface{}, err error) (float64, error) {
+ if err != nil {
+ return 0, err
+ }
+
+ var value float64
+ err = nil
+ switch v := reply.(type) {
+ case float32:
+ value = float64(v)
+ case float64:
+ value = v
+ case int:
+ value = float64(v)
+ case int8:
+ value = float64(v)
+ case int16:
+ value = float64(v)
+ case int32:
+ value = float64(v)
+ case int64:
+ value = float64(v)
+ case uint:
+ value = float64(v)
+ case uint8:
+ value = float64(v)
+ case uint16:
+ value = float64(v)
+ case uint32:
+ value = float64(v)
+ case uint64:
+ value = float64(v)
+ case []byte:
+ data := string(v)
+ if len(data) == 0 {
+ return 0, ErrNil
+ }
+ value, err = strconv.ParseFloat(string(v), 64)
+ case string:
+ if len(v) == 0 {
+ return 0, ErrNil
+ }
+ value, err = strconv.ParseFloat(v, 64)
+ case nil:
+ err = ErrNil
+ case error:
+ err = v
+ default:
+ err = fmt.Errorf("redis cluster: unexpected type for Float64, got type %T", v)
+ }
+
+ return value, err
+}
+
+func Bool(reply interface{}, err error) (bool, error) {
+ if err != nil {
+ return false, err
+ }
+ switch reply := reply.(type) {
+ case bool:
+ return reply, nil
+ case int64:
+ return reply != 0, nil
+ case []byte:
+ data := string(reply)
+ if len(data) == 0 {
+ return false, ErrNil
+ }
+
+ return strconv.ParseBool(data)
+ case string:
+ if len(reply) == 0 {
+ return false, ErrNil
+ }
+
+ return strconv.ParseBool(reply)
+ case nil:
+ return false, ErrNil
+ case error:
+ return false, reply
+ }
+ return false, fmt.Errorf("redis cluster: unexpected type for Bool, got type %T", reply)
+}
+
+func Bytes(reply interface{}, err error) ([]byte, error) {
+ if err != nil {
+ return nil, err
+ }
+ switch reply := reply.(type) {
+ case []byte:
+ if len(reply) == 0 {
+ return nil, ErrNil
+ }
+ return reply, nil
+ case string:
+ data := []byte(reply)
+ if len(data) == 0 {
+ return nil, ErrNil
+ }
+ return data, nil
+ case nil:
+ return nil, ErrNil
+ case error:
+ return nil, reply
+ }
+ return nil, fmt.Errorf("redis cluster: unexpected type for Bytes, got type %T", reply)
+}
+
+func String(reply interface{}, err error) (string, error) {
+ if err != nil {
+ return "", err
+ }
+
+ value := ""
+ err = nil
+ switch v := reply.(type) {
+ case string:
+ if len(v) == 0 {
+ return "", ErrNil
+ }
+
+ value = v
+ case []byte:
+ if len(v) == 0 {
+ return "", ErrNil
+ }
+
+ value = string(v)
+ case int:
+ value = strconv.FormatInt(int64(v), 10)
+ case int8:
+ value = strconv.FormatInt(int64(v), 10)
+ case int16:
+ value = strconv.FormatInt(int64(v), 10)
+ case int32:
+ value = strconv.FormatInt(int64(v), 10)
+ case int64:
+ value = strconv.FormatInt(v, 10)
+ case uint:
+ value = strconv.FormatUint(uint64(v), 10)
+ case uint8:
+ value = strconv.FormatUint(uint64(v), 10)
+ case uint16:
+ value = strconv.FormatUint(uint64(v), 10)
+ case uint32:
+ value = strconv.FormatUint(uint64(v), 10)
+ case uint64:
+ value = strconv.FormatUint(v, 10)
+ case float32:
+ value = strconv.FormatFloat(float64(v), 'f', -1, 32)
+ case float64:
+ value = strconv.FormatFloat(v, 'f', -1, 64)
+ case bool:
+ value = strconv.FormatBool(v)
+ case nil:
+ err = ErrNil
+ case error:
+ err = v
+ default:
+ err = fmt.Errorf("redis cluster: unexpected type for String, got type %T", v)
+ }
+
+ return value, err
+}
+
+func Strings(reply interface{}, err error) ([]string, error) {
+ if err != nil {
+ return nil, err
+ }
+ switch reply := reply.(type) {
+ case []interface{}:
+ result := make([]string, len(reply))
+ for i := range reply {
+ if reply[i] == nil {
+ continue
+ }
+ switch subReply := reply[i].(type) {
+ case string:
+ result[i] = subReply
+ case []byte:
+ result[i] = string(subReply)
+ default:
+ return nil, fmt.Errorf("redis cluster: unexpected element type for String, got type %T", reply[i])
+ }
+ }
+ return result, nil
+ case []string:
+ return reply, nil
+ case nil:
+ return nil, ErrNil
+ case error:
+ return nil, reply
+ }
+ return nil, fmt.Errorf("redis cluster: unexpected type for Strings, got type %T", reply)
+}
+
+func Values(reply interface{}, err error) ([]interface{}, error) {
+ if err != nil {
+ return nil, err
+ }
+ switch reply := reply.(type) {
+ case []interface{}:
+ return reply, nil
+ case nil:
+ return nil, ErrNil
+ case error:
+ return nil, reply
+ }
+ return nil, fmt.Errorf("redis cluster: unexpected type for Values, got type %T", reply)
+}
diff --git a/utils/cache/redis.go b/utils/cache/redis.go
new file mode 100644
index 0000000..55d60a5
--- /dev/null
+++ b/utils/cache/redis.go
@@ -0,0 +1,413 @@
+package cache
+
+import (
+ "encoding/json"
+ "errors"
+ "log"
+ "strings"
+ "time"
+
+ redigo "github.com/gomodule/redigo/redis"
+)
+
+// configuration
+type Config struct {
+ Server string
+ Password string
+ MaxIdle int // Maximum number of idle connections in the pool.
+
+ // Maximum number of connections allocated by the pool at a given time.
+ // When zero, there is no limit on the number of connections in the pool.
+ MaxActive int
+
+ // Close connections after remaining idle for this duration. If the value
+ // is zero, then idle connections are not closed. Applications should set
+ // the timeout to a value less than the server's timeout.
+ IdleTimeout time.Duration
+
+ // If Wait is true and the pool is at the MaxActive limit, then Get() waits
+ // for a connection to be returned to the pool before returning.
+ Wait bool
+ KeyPrefix string // prefix to all keys; example is "dev environment name"
+ KeyDelimiter string // delimiter to be used while appending keys; example is ":"
+ KeyPlaceholder string // placeholder to be parsed using given arguments to obtain a final key; example is "?"
+}
+
+var pool *redigo.Pool
+var conf *Config
+
+func NewRedis(addr string) {
+ if addr == "" {
+ panic("\nredis connect string cannot be empty\n")
+ }
+ pool = &redigo.Pool{
+ MaxIdle: redisMaxIdleConn,
+ IdleTimeout: redisIdleTTL,
+ MaxActive: redisMaxActive,
+ // MaxConnLifetime: redisDialTTL,
+ Wait: true,
+ Dial: func() (redigo.Conn, error) {
+ c, err := redigo.Dial("tcp", addr,
+ redigo.DialConnectTimeout(redisDialTTL),
+ redigo.DialReadTimeout(redisReadTTL),
+ redigo.DialWriteTimeout(redisWriteTTL),
+ //redigo.DialDatabase(RedisDataBase),
+ )
+ if err != nil {
+ log.Println("Redis Dial failed: ", err)
+ return nil, err
+ }
+ return c, err
+ },
+ TestOnBorrow: func(c redigo.Conn, t time.Time) error {
+ _, err := c.Do("PING")
+ if err != nil {
+ log.Println("Unable to ping to redis server:", err)
+ }
+ return err
+ },
+ }
+ conn := pool.Get()
+ defer conn.Close()
+ if conn.Err() != nil {
+ println("\nredis connect " + addr + " error: " + conn.Err().Error())
+ } else {
+ println("\nredis connect " + addr + " success!\n")
+ }
+}
+
+func Do(cmd string, args ...interface{}) (reply interface{}, err error) {
+ conn := pool.Get()
+ defer conn.Close()
+ return conn.Do(cmd, args...)
+}
+
+func SelectDb(db int) (interface{}, error) {
+ return Do("SELECT", db)
+}
+
+func GetPool() *redigo.Pool {
+ return pool
+}
+
+func ParseKey(key string, vars []string) (string, error) {
+ arr := strings.Split(key, conf.KeyPlaceholder)
+ actualKey := ""
+ if len(arr) != len(vars)+1 {
+ return "", errors.New("redis/connection.go: Insufficient arguments to parse key")
+ } else {
+ for index, val := range arr {
+ if index == 0 {
+ actualKey = arr[index]
+ } else {
+ actualKey += vars[index-1] + val
+ }
+ }
+ }
+ return getPrefixedKey(actualKey), nil
+}
+
+func getPrefixedKey(key string) string {
+ return conf.KeyPrefix + conf.KeyDelimiter + key
+}
+func StripEnvKey(key string) string {
+ return strings.TrimLeft(key, conf.KeyPrefix+conf.KeyDelimiter)
+}
+func SplitKey(key string) []string {
+ return strings.Split(key, conf.KeyDelimiter)
+}
+func Expire(key string, ttl int) (interface{}, error) {
+ return Do("EXPIRE", key, ttl)
+}
+func Persist(key string) (interface{}, error) {
+ return Do("PERSIST", key)
+}
+
+func Del(key string) (interface{}, error) {
+ return Do("DEL", key)
+}
+func Set(key string, data interface{}) (interface{}, error) {
+ // set
+ return Do("SET", key, data)
+}
+func SetNX(key string, data interface{}) (interface{}, error) {
+ return Do("SETNX", key, data)
+}
+func SetEx(key string, data interface{}, ttl int) (interface{}, error) {
+ return Do("SETEX", key, ttl, data)
+}
+
+func SetJson(key string, data interface{}, ttl int) bool {
+ c, err := json.Marshal(data)
+ if err != nil {
+ return false
+ }
+ if ttl < 1 {
+ _, err = Set(key, c)
+ } else {
+ _, err = SetEx(key, c, ttl)
+ }
+ if err != nil {
+ return false
+ }
+ return true
+}
+
+func GetJson(key string, dst interface{}) error {
+ b, err := GetBytes(key)
+ if err != nil {
+ return err
+ }
+ if err = json.Unmarshal(b, dst); err != nil {
+ return err
+ }
+ return nil
+}
+
+func Get(key string) (interface{}, error) {
+ // get
+ return Do("GET", key)
+}
+func GetTTL(key string) (time.Duration, error) {
+ ttl, err := redigo.Int64(Do("TTL", key))
+ return time.Duration(ttl) * time.Second, err
+}
+func GetBytes(key string) ([]byte, error) {
+ return redigo.Bytes(Do("GET", key))
+}
+func GetString(key string) (string, error) {
+ return redigo.String(Do("GET", key))
+}
+func GetStringMap(key string) (map[string]string, error) {
+ return redigo.StringMap(Do("GET", key))
+}
+func GetInt(key string) (int, error) {
+ return redigo.Int(Do("GET", key))
+}
+func GetInt64(key string) (int64, error) {
+ return redigo.Int64(Do("GET", key))
+}
+func GetStringLength(key string) (int, error) {
+ return redigo.Int(Do("STRLEN", key))
+}
+func ZAdd(key string, score float64, data interface{}) (interface{}, error) {
+ return Do("ZADD", key, score, data)
+}
+func ZAddNX(key string, score float64, data interface{}) (interface{}, error) {
+ return Do("ZADD", key, "NX", score, data)
+}
+func ZRem(key string, data interface{}) (interface{}, error) {
+ return Do("ZREM", key, data)
+}
+func ZRange(key string, start int, end int, withScores bool) ([]interface{}, error) {
+ if withScores {
+ return redigo.Values(Do("ZRANGE", key, start, end, "WITHSCORES"))
+ }
+ return redigo.Values(Do("ZRANGE", key, start, end))
+}
+func ZRemRangeByScore(key string, start int64, end int64) ([]interface{}, error) {
+ return redigo.Values(Do("ZREMRANGEBYSCORE", key, start, end))
+}
+func ZCard(setName string) (int64, error) {
+ return redigo.Int64(Do("ZCARD", setName))
+}
+func ZScan(setName string) (int64, error) {
+ return redigo.Int64(Do("ZCARD", setName))
+}
+func SAdd(setName string, data interface{}) (interface{}, error) {
+ return Do("SADD", setName, data)
+}
+func SCard(setName string) (int64, error) {
+ return redigo.Int64(Do("SCARD", setName))
+}
+func SIsMember(setName string, data interface{}) (bool, error) {
+ return redigo.Bool(Do("SISMEMBER", setName, data))
+}
+func SMembers(setName string) ([]string, error) {
+ return redigo.Strings(Do("SMEMBERS", setName))
+}
+func SRem(setName string, data interface{}) (interface{}, error) {
+ return Do("SREM", setName, data)
+}
+func HSet(key string, HKey string, data interface{}) (interface{}, error) {
+ return Do("HSET", key, HKey, data)
+}
+
+func HGet(key string, HKey string) (interface{}, error) {
+ return Do("HGET", key, HKey)
+}
+
+func HMGet(key string, hashKeys ...string) ([]interface{}, error) {
+ ret, err := Do("HMGET", key, hashKeys)
+ if err != nil {
+ return nil, err
+ }
+ reta, ok := ret.([]interface{})
+ if !ok {
+ return nil, errors.New("result not an array")
+ }
+ return reta, nil
+}
+
+func HMSet(key string, hashKeys []string, vals []interface{}) (interface{}, error) {
+ if len(hashKeys) == 0 || len(hashKeys) != len(vals) {
+ var ret interface{}
+ return ret, errors.New("bad length")
+ }
+ input := []interface{}{key}
+ for i, v := range hashKeys {
+ input = append(input, v, vals[i])
+ }
+ return Do("HMSET", input...)
+}
+
+func HGetString(key string, HKey string) (string, error) {
+ return redigo.String(Do("HGET", key, HKey))
+}
+func HGetFloat(key string, HKey string) (float64, error) {
+ f, err := redigo.Float64(Do("HGET", key, HKey))
+ return f, err
+}
+func HGetInt(key string, HKey string) (int, error) {
+ return redigo.Int(Do("HGET", key, HKey))
+}
+func HGetInt64(key string, HKey string) (int64, error) {
+ return redigo.Int64(Do("HGET", key, HKey))
+}
+func HGetBool(key string, HKey string) (bool, error) {
+ return redigo.Bool(Do("HGET", key, HKey))
+}
+func HDel(key string, HKey string) (interface{}, error) {
+ return Do("HDEL", key, HKey)
+}
+
+func HGetAll(key string) (map[string]interface{}, error) {
+ vals, err := redigo.Values(Do("HGETALL", key))
+ if err != nil {
+ return nil, err
+ }
+ num := len(vals) / 2
+ result := make(map[string]interface{}, num)
+ for i := 0; i < num; i++ {
+ key, _ := redigo.String(vals[2*i], nil)
+ result[key] = vals[2*i+1]
+ }
+ return result, nil
+}
+
+func FlushAll() bool {
+ res, _ := redigo.String(Do("FLUSHALL"))
+ if res == "" {
+ return false
+ }
+ return true
+}
+
+// NOTE: Use this in production environment with extreme care.
+// Read more here:https://redigo.io/commands/keys
+func Keys(pattern string) ([]string, error) {
+ return redigo.Strings(Do("KEYS", pattern))
+}
+
+func HKeys(key string) ([]string, error) {
+ return redigo.Strings(Do("HKEYS", key))
+}
+
+func Exists(key string) bool {
+ count, err := redigo.Int(Do("EXISTS", key))
+ if count == 0 || err != nil {
+ return false
+ }
+ return true
+}
+
+func Incr(key string) (int64, error) {
+ return redigo.Int64(Do("INCR", key))
+}
+
+func Decr(key string) (int64, error) {
+ return redigo.Int64(Do("DECR", key))
+}
+
+func IncrBy(key string, incBy int64) (int64, error) {
+ return redigo.Int64(Do("INCRBY", key, incBy))
+}
+
+func DecrBy(key string, decrBy int64) (int64, error) {
+ return redigo.Int64(Do("DECRBY", key, decrBy))
+}
+
+func IncrByFloat(key string, incBy float64) (float64, error) {
+ return redigo.Float64(Do("INCRBYFLOAT", key, incBy))
+}
+
+func DecrByFloat(key string, decrBy float64) (float64, error) {
+ return redigo.Float64(Do("DECRBYFLOAT", key, decrBy))
+}
+
+// use for message queue
+func LPush(key string, data interface{}) (interface{}, error) {
+ // set
+ return Do("LPUSH", key, data)
+}
+
+func LPop(key string) (interface{}, error) {
+ return Do("LPOP", key)
+}
+
+func LPopString(key string) (string, error) {
+ return redigo.String(Do("LPOP", key))
+}
+func LPopFloat(key string) (float64, error) {
+ f, err := redigo.Float64(Do("LPOP", key))
+ return f, err
+}
+func LPopInt(key string) (int, error) {
+ return redigo.Int(Do("LPOP", key))
+}
+func LPopInt64(key string) (int64, error) {
+ return redigo.Int64(Do("LPOP", key))
+}
+
+func RPush(key string, data interface{}) (interface{}, error) {
+ // set
+ return Do("RPUSH", key, data)
+}
+
+func RPop(key string) (interface{}, error) {
+ return Do("RPOP", key)
+}
+
+func RPopString(key string) (string, error) {
+ return redigo.String(Do("RPOP", key))
+}
+func RPopFloat(key string) (float64, error) {
+ f, err := redigo.Float64(Do("RPOP", key))
+ return f, err
+}
+func RPopInt(key string) (int, error) {
+ return redigo.Int(Do("RPOP", key))
+}
+func RPopInt64(key string) (int64, error) {
+ return redigo.Int64(Do("RPOP", key))
+}
+
+func Scan(cursor int64, pattern string, count int64) (int64, []string, error) {
+ var items []string
+ var newCursor int64
+
+ values, err := redigo.Values(Do("SCAN", cursor, "MATCH", pattern, "COUNT", count))
+ if err != nil {
+ return 0, nil, err
+ }
+ values, err = redigo.Scan(values, &newCursor, &items)
+ if err != nil {
+ return 0, nil, err
+ }
+ return newCursor, items, nil
+}
+
+func LPushMax(key string, data ...interface{}) (interface{}, error) {
+ // set
+ return Do("LPUSH", key, data)
+}
diff --git a/utils/cache/redis_cluster.go b/utils/cache/redis_cluster.go
new file mode 100644
index 0000000..901f30c
--- /dev/null
+++ b/utils/cache/redis_cluster.go
@@ -0,0 +1,622 @@
+package cache
+
+import (
+ "strconv"
+ "time"
+
+ "github.com/go-redis/redis"
+)
+
+var pools *redis.ClusterClient
+
+func NewRedisCluster(addrs []string) error {
+ opt := &redis.ClusterOptions{
+ Addrs: addrs,
+ PoolSize: redisPoolSize,
+ PoolTimeout: redisPoolTTL,
+ IdleTimeout: redisIdleTTL,
+ DialTimeout: redisDialTTL,
+ ReadTimeout: redisReadTTL,
+ WriteTimeout: redisWriteTTL,
+ }
+ pools = redis.NewClusterClient(opt)
+ if err := pools.Ping().Err(); err != nil {
+ return err
+ }
+ return nil
+}
+
+func RCGet(key string) (interface{}, error) {
+ res, err := pools.Get(key).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return []byte(res), nil
+}
+func RCSet(key string, value interface{}) error {
+ err := pools.Set(key, value, 0).Err()
+ return convertError(err)
+}
+func RCGetSet(key string, value interface{}) (interface{}, error) {
+ res, err := pools.GetSet(key, value).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return []byte(res), nil
+}
+func RCSetNx(key string, value interface{}) (int64, error) {
+ res, err := pools.SetNX(key, value, 0).Result()
+ if err != nil {
+ return 0, convertError(err)
+ }
+ if res {
+ return 1, nil
+ }
+ return 0, nil
+}
+func RCSetEx(key string, value interface{}, timeout int64) error {
+ _, err := pools.Set(key, value, time.Duration(timeout)*time.Second).Result()
+ if err != nil {
+ return convertError(err)
+ }
+ return nil
+}
+
+// nil表示成功,ErrNil表示数据库内已经存在这个key,其他表示数据库发生错误
+func RCSetNxEx(key string, value interface{}, timeout int64) error {
+ res, err := pools.SetNX(key, value, time.Duration(timeout)*time.Second).Result()
+ if err != nil {
+ return convertError(err)
+ }
+ if res {
+ return nil
+ }
+ return ErrNil
+}
+func RCMGet(keys ...string) ([]interface{}, error) {
+ res, err := pools.MGet(keys...).Result()
+ return res, convertError(err)
+}
+
+// 为确保多个key映射到同一个slot,每个key最好加上hash tag,如:{test}
+func RCMSet(kvs map[string]interface{}) error {
+ pairs := make([]string, 0, len(kvs)*2)
+ for k, v := range kvs {
+ val, err := String(v, nil)
+ if err != nil {
+ return err
+ }
+ pairs = append(pairs, k, val)
+ }
+ return convertError(pools.MSet(pairs).Err())
+}
+
+// 为确保多个key映射到同一个slot,每个key最好加上hash tag,如:{test}
+func RCMSetNX(kvs map[string]interface{}) (bool, error) {
+ pairs := make([]string, 0, len(kvs)*2)
+ for k, v := range kvs {
+ val, err := String(v, nil)
+ if err != nil {
+ return false, err
+ }
+ pairs = append(pairs, k, val)
+ }
+ res, err := pools.MSetNX(pairs).Result()
+ return res, convertError(err)
+}
+func RCExpireAt(key string, timestamp int64) (int64, error) {
+ res, err := pools.ExpireAt(key, time.Unix(timestamp, 0)).Result()
+ if err != nil {
+ return 0, convertError(err)
+ }
+ if res {
+ return 1, nil
+ }
+ return 0, nil
+}
+func RCDel(keys ...string) (int64, error) {
+ args := make([]interface{}, 0, len(keys))
+ for _, key := range keys {
+ args = append(args, key)
+ }
+ res, err := pools.Del(keys...).Result()
+ if err != nil {
+ return res, convertError(err)
+ }
+ return res, nil
+}
+func RCIncr(key string) (int64, error) {
+ res, err := pools.Incr(key).Result()
+ if err != nil {
+ return res, convertError(err)
+ }
+ return res, nil
+}
+func RCIncrBy(key string, delta int64) (int64, error) {
+ res, err := pools.IncrBy(key, delta).Result()
+ if err != nil {
+ return res, convertError(err)
+ }
+ return res, nil
+}
+func RCExpire(key string, duration int64) (int64, error) {
+ res, err := pools.Expire(key, time.Duration(duration)*time.Second).Result()
+ if err != nil {
+ return 0, convertError(err)
+ }
+ if res {
+ return 1, nil
+ }
+ return 0, nil
+}
+func RCExists(key string) (bool, error) {
+ res, err := pools.Exists(key).Result()
+ if err != nil {
+ return false, convertError(err)
+ }
+ if res > 0 {
+ return true, nil
+ }
+ return false, nil
+}
+func RCHGet(key string, field string) (interface{}, error) {
+ res, err := pools.HGet(key, field).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return []byte(res), nil
+}
+func RCHLen(key string) (int64, error) {
+ res, err := pools.HLen(key).Result()
+ if err != nil {
+ return res, convertError(err)
+ }
+ return res, nil
+}
+func RCHSet(key string, field string, val interface{}) error {
+ value, err := String(val, nil)
+ if err != nil && err != ErrNil {
+ return err
+ }
+ _, err = pools.HSet(key, field, value).Result()
+ if err != nil {
+ return convertError(err)
+ }
+ return nil
+}
+func RCHDel(key string, fields ...string) (int64, error) {
+ args := make([]interface{}, 0, len(fields)+1)
+ args = append(args, key)
+ for _, field := range fields {
+ args = append(args, field)
+ }
+ res, err := pools.HDel(key, fields...).Result()
+ if err != nil {
+ return 0, convertError(err)
+ }
+ return res, nil
+}
+
+func RCHMGet(key string, fields ...string) (interface{}, error) {
+ args := make([]interface{}, 0, len(fields)+1)
+ args = append(args, key)
+ for _, field := range fields {
+ args = append(args, field)
+ }
+ if len(fields) == 0 {
+ return nil, ErrNil
+ }
+ res, err := pools.HMGet(key, fields...).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return res, nil
+}
+func RCHMSet(key string, kvs ...interface{}) error {
+ if len(kvs) == 0 {
+ return nil
+ }
+ if len(kvs)%2 != 0 {
+ return ErrWrongArgsNum
+ }
+ var err error
+ v := map[string]interface{}{} // todo change
+ v["field"], err = String(kvs[0], nil)
+ if err != nil && err != ErrNil {
+ return err
+ }
+ v["value"], err = String(kvs[1], nil)
+ if err != nil && err != ErrNil {
+ return err
+ }
+ pairs := make([]string, 0, len(kvs)-2)
+ if len(kvs) > 2 {
+ for _, kv := range kvs[2:] {
+ kvString, err := String(kv, nil)
+ if err != nil && err != ErrNil {
+ return err
+ }
+ pairs = append(pairs, kvString)
+ }
+ }
+ v["paris"] = pairs
+ _, err = pools.HMSet(key, v).Result()
+ if err != nil {
+ return convertError(err)
+ }
+ return nil
+}
+
+func RCHKeys(key string) ([]string, error) {
+ res, err := pools.HKeys(key).Result()
+ if err != nil {
+ return res, convertError(err)
+ }
+ return res, nil
+}
+func RCHVals(key string) ([]interface{}, error) {
+ res, err := pools.HVals(key).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ rs := make([]interface{}, 0, len(res))
+ for _, res := range res {
+ rs = append(rs, res)
+ }
+ return rs, nil
+}
+func RCHGetAll(key string) (map[string]string, error) {
+ vals, err := pools.HGetAll(key).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return vals, nil
+}
+func RCHIncrBy(key, field string, delta int64) (int64, error) {
+ res, err := pools.HIncrBy(key, field, delta).Result()
+ if err != nil {
+ return res, convertError(err)
+ }
+ return res, nil
+}
+func RCZAdd(key string, kvs ...interface{}) (int64, error) {
+ args := make([]interface{}, 0, len(kvs)+1)
+ args = append(args, key)
+ args = append(args, kvs...)
+ if len(kvs) == 0 {
+ return 0, nil
+ }
+ if len(kvs)%2 != 0 {
+ return 0, ErrWrongArgsNum
+ }
+ zs := make([]redis.Z, len(kvs)/2)
+ for i := 0; i < len(kvs); i += 2 {
+ idx := i / 2
+ score, err := Float64(kvs[i], nil)
+ if err != nil && err != ErrNil {
+ return 0, err
+ }
+ zs[idx].Score = score
+ zs[idx].Member = kvs[i+1]
+ }
+ res, err := pools.ZAdd(key, zs...).Result()
+ if err != nil {
+ return res, convertError(err)
+ }
+ return res, nil
+}
+func RCZRem(key string, members ...string) (int64, error) {
+ args := make([]interface{}, 0, len(members))
+ args = append(args, key)
+ for _, member := range members {
+ args = append(args, member)
+ }
+ res, err := pools.ZRem(key, members).Result()
+ if err != nil {
+ return res, convertError(err)
+ }
+ return res, err
+}
+
+func RCZRange(key string, min, max int64, withScores bool) (interface{}, error) {
+ res := make([]interface{}, 0)
+ if withScores {
+ zs, err := pools.ZRangeWithScores(key, min, max).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ for _, z := range zs {
+ res = append(res, z.Member, strconv.FormatFloat(z.Score, 'f', -1, 64))
+ }
+ } else {
+ ms, err := pools.ZRange(key, min, max).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ for _, m := range ms {
+ res = append(res, m)
+ }
+ }
+ return res, nil
+}
+func RCZRangeByScoreWithScore(key string, min, max int64) (map[string]int64, error) {
+ opt := new(redis.ZRangeBy)
+ opt.Min = strconv.FormatInt(int64(min), 10)
+ opt.Max = strconv.FormatInt(int64(max), 10)
+ opt.Count = -1
+ opt.Offset = 0
+ vals, err := pools.ZRangeByScoreWithScores(key, *opt).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ res := make(map[string]int64, len(vals))
+ for _, val := range vals {
+ key, err := String(val.Member, nil)
+ if err != nil && err != ErrNil {
+ return nil, err
+ }
+ res[key] = int64(val.Score)
+ }
+ return res, nil
+}
+func RCLRange(key string, start, stop int64) (interface{}, error) {
+ res, err := pools.LRange(key, start, stop).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return res, nil
+}
+func RCLSet(key string, index int, value interface{}) error {
+ err := pools.LSet(key, int64(index), value).Err()
+ return convertError(err)
+}
+func RCLLen(key string) (int64, error) {
+ res, err := pools.LLen(key).Result()
+ if err != nil {
+ return res, convertError(err)
+ }
+ return res, nil
+}
+func RCLRem(key string, count int, value interface{}) (int, error) {
+ val, _ := value.(string)
+ res, err := pools.LRem(key, int64(count), val).Result()
+ if err != nil {
+ return int(res), convertError(err)
+ }
+ return int(res), nil
+}
+func RCTTl(key string) (int64, error) {
+ duration, err := pools.TTL(key).Result()
+ if err != nil {
+ return int64(duration.Seconds()), convertError(err)
+ }
+ return int64(duration.Seconds()), nil
+}
+func RCLPop(key string) (interface{}, error) {
+ res, err := pools.LPop(key).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return res, nil
+}
+func RCRPop(key string) (interface{}, error) {
+ res, err := pools.RPop(key).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return res, nil
+}
+func RCBLPop(key string, timeout int) (interface{}, error) {
+ res, err := pools.BLPop(time.Duration(timeout)*time.Second, key).Result()
+ if err != nil {
+ // 兼容redis 2.x
+ if err == redis.Nil {
+ return nil, ErrNil
+ }
+ return nil, err
+ }
+ return res[1], nil
+}
+func RCBRPop(key string, timeout int) (interface{}, error) {
+ res, err := pools.BRPop(time.Duration(timeout)*time.Second, key).Result()
+ if err != nil {
+ // 兼容redis 2.x
+ if err == redis.Nil {
+ return nil, ErrNil
+ }
+ return nil, convertError(err)
+ }
+ return res[1], nil
+}
+func RCLPush(key string, value ...interface{}) error {
+ args := make([]interface{}, 0, len(value)+1)
+ args = append(args, key)
+ args = append(args, value...)
+ vals := make([]string, 0, len(value))
+ for _, v := range value {
+ val, err := String(v, nil)
+ if err != nil && err != ErrNil {
+ return err
+ }
+ vals = append(vals, val)
+ }
+ _, err := pools.LPush(key, vals).Result() // todo ...
+ if err != nil {
+ return convertError(err)
+ }
+ return nil
+}
+func RCRPush(key string, value ...interface{}) error {
+ args := make([]interface{}, 0, len(value)+1)
+ args = append(args, key)
+ args = append(args, value...)
+ vals := make([]string, 0, len(value))
+ for _, v := range value {
+ val, err := String(v, nil)
+ if err != nil && err != ErrNil {
+ if err == ErrNil {
+ continue
+ }
+ return err
+ }
+ if val == "" {
+ continue
+ }
+ vals = append(vals, val)
+ }
+ _, err := pools.RPush(key, vals).Result() // todo ...
+ if err != nil {
+ return convertError(err)
+ }
+ return nil
+}
+
+// 为确保srcKey跟destKey映射到同一个slot,srcKey和destKey需要加上hash tag,如:{test}
+func RCBRPopLPush(srcKey string, destKey string, timeout int) (interface{}, error) {
+ res, err := pools.BRPopLPush(srcKey, destKey, time.Duration(timeout)*time.Second).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return res, nil
+}
+
+// 为确保srcKey跟destKey映射到同一个slot,srcKey和destKey需要加上hash tag,如:{test}
+func RCRPopLPush(srcKey string, destKey string) (interface{}, error) {
+ res, err := pools.RPopLPush(srcKey, destKey).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return res, nil
+}
+func RCSAdd(key string, members ...interface{}) (int64, error) {
+ args := make([]interface{}, 0, len(members)+1)
+ args = append(args, key)
+ args = append(args, members...)
+ ms := make([]string, 0, len(members))
+ for _, member := range members {
+ m, err := String(member, nil)
+ if err != nil && err != ErrNil {
+ return 0, err
+ }
+ ms = append(ms, m)
+ }
+ res, err := pools.SAdd(key, ms).Result() // todo ...
+ if err != nil {
+ return res, convertError(err)
+ }
+ return res, nil
+}
+func RCSPop(key string) ([]byte, error) {
+ res, err := pools.SPop(key).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return []byte(res), nil
+}
+func RCSIsMember(key string, member interface{}) (bool, error) {
+ m, _ := member.(string)
+ res, err := pools.SIsMember(key, m).Result()
+ if err != nil {
+ return res, convertError(err)
+ }
+ return res, nil
+}
+func RCSRem(key string, members ...interface{}) (int64, error) {
+ args := make([]interface{}, 0, len(members)+1)
+ args = append(args, key)
+ args = append(args, members...)
+ ms := make([]string, 0, len(members))
+ for _, member := range members {
+ m, err := String(member, nil)
+ if err != nil && err != ErrNil {
+ return 0, err
+ }
+ ms = append(ms, m)
+ }
+ res, err := pools.SRem(key, ms).Result() // todo ...
+ if err != nil {
+ return res, convertError(err)
+ }
+ return res, nil
+}
+func RCSMembers(key string) ([]string, error) {
+ res, err := pools.SMembers(key).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return res, nil
+}
+func RCScriptLoad(luaScript string) (interface{}, error) {
+ res, err := pools.ScriptLoad(luaScript).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return res, nil
+}
+func RCEvalSha(sha1 string, numberKeys int, keysArgs ...interface{}) (interface{}, error) {
+ vals := make([]interface{}, 0, len(keysArgs)+2)
+ vals = append(vals, sha1, numberKeys)
+ vals = append(vals, keysArgs...)
+ keys := make([]string, 0, numberKeys)
+ args := make([]string, 0, len(keysArgs)-numberKeys)
+ for i, value := range keysArgs {
+ val, err := String(value, nil)
+ if err != nil && err != ErrNil {
+ return nil, err
+ }
+ if i < numberKeys {
+ keys = append(keys, val)
+ } else {
+ args = append(args, val)
+ }
+ }
+ res, err := pools.EvalSha(sha1, keys, args).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return res, nil
+}
+func RCEval(luaScript string, numberKeys int, keysArgs ...interface{}) (interface{}, error) {
+ vals := make([]interface{}, 0, len(keysArgs)+2)
+ vals = append(vals, luaScript, numberKeys)
+ vals = append(vals, keysArgs...)
+ keys := make([]string, 0, numberKeys)
+ args := make([]string, 0, len(keysArgs)-numberKeys)
+ for i, value := range keysArgs {
+ val, err := String(value, nil)
+ if err != nil && err != ErrNil {
+ return nil, err
+ }
+ if i < numberKeys {
+ keys = append(keys, val)
+ } else {
+ args = append(args, val)
+ }
+ }
+ res, err := pools.Eval(luaScript, keys, args).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return res, nil
+}
+func RCGetBit(key string, offset int64) (int64, error) {
+ res, err := pools.GetBit(key, offset).Result()
+ if err != nil {
+ return res, convertError(err)
+ }
+ return res, nil
+}
+func RCSetBit(key string, offset uint32, value int) (int, error) {
+ res, err := pools.SetBit(key, int64(offset), value).Result()
+ return int(res), convertError(err)
+}
+func RCGetClient() *redis.ClusterClient {
+ return pools
+}
+func convertError(err error) error {
+ if err == redis.Nil {
+ // 为了兼容redis 2.x,这里不返回 ErrNil,ErrNil在调用redis_cluster_reply函数时才返回
+ return nil
+ }
+ return err
+}
diff --git a/utils/cache/redis_pool.go b/utils/cache/redis_pool.go
new file mode 100644
index 0000000..ca38b3f
--- /dev/null
+++ b/utils/cache/redis_pool.go
@@ -0,0 +1,324 @@
+package cache
+
+import (
+ "errors"
+ "log"
+ "strings"
+ "time"
+
+ redigo "github.com/gomodule/redigo/redis"
+)
+
+type RedisPool struct {
+ *redigo.Pool
+}
+
+func NewRedisPool(cfg *Config) *RedisPool {
+ return &RedisPool{&redigo.Pool{
+ MaxIdle: cfg.MaxIdle,
+ IdleTimeout: cfg.IdleTimeout,
+ MaxActive: cfg.MaxActive,
+ Wait: cfg.Wait,
+ Dial: func() (redigo.Conn, error) {
+ c, err := redigo.Dial("tcp", cfg.Server)
+ if err != nil {
+ log.Println("Redis Dial failed: ", err)
+ return nil, err
+ }
+ if cfg.Password != "" {
+ if _, err := c.Do("AUTH", cfg.Password); err != nil {
+ c.Close()
+ log.Println("Redis AUTH failed: ", err)
+ return nil, err
+ }
+ }
+ return c, err
+ },
+ TestOnBorrow: func(c redigo.Conn, t time.Time) error {
+ _, err := c.Do("PING")
+ if err != nil {
+ log.Println("Unable to ping to redis server:", err)
+ }
+ return err
+ },
+ }}
+}
+
+func (p *RedisPool) Do(cmd string, args ...interface{}) (reply interface{}, err error) {
+ conn := pool.Get()
+ defer conn.Close()
+ return conn.Do(cmd, args...)
+}
+
+func (p *RedisPool) GetPool() *redigo.Pool {
+ return pool
+}
+
+func (p *RedisPool) ParseKey(key string, vars []string) (string, error) {
+ arr := strings.Split(key, conf.KeyPlaceholder)
+ actualKey := ""
+ if len(arr) != len(vars)+1 {
+ return "", errors.New("redis/connection.go: Insufficient arguments to parse key")
+ } else {
+ for index, val := range arr {
+ if index == 0 {
+ actualKey = arr[index]
+ } else {
+ actualKey += vars[index-1] + val
+ }
+ }
+ }
+ return getPrefixedKey(actualKey), nil
+}
+
+func (p *RedisPool) getPrefixedKey(key string) string {
+ return conf.KeyPrefix + conf.KeyDelimiter + key
+}
+func (p *RedisPool) StripEnvKey(key string) string {
+ return strings.TrimLeft(key, conf.KeyPrefix+conf.KeyDelimiter)
+}
+func (p *RedisPool) SplitKey(key string) []string {
+ return strings.Split(key, conf.KeyDelimiter)
+}
+func (p *RedisPool) Expire(key string, ttl int) (interface{}, error) {
+ return Do("EXPIRE", key, ttl)
+}
+func (p *RedisPool) Persist(key string) (interface{}, error) {
+ return Do("PERSIST", key)
+}
+
+func (p *RedisPool) Del(key string) (interface{}, error) {
+ return Do("DEL", key)
+}
+func (p *RedisPool) Set(key string, data interface{}) (interface{}, error) {
+ // set
+ return Do("SET", key, data)
+}
+func (p *RedisPool) SetNX(key string, data interface{}) (interface{}, error) {
+ return Do("SETNX", key, data)
+}
+func (p *RedisPool) SetEx(key string, data interface{}, ttl int) (interface{}, error) {
+ return Do("SETEX", key, ttl, data)
+}
+func (p *RedisPool) Get(key string) (interface{}, error) {
+ // get
+ return Do("GET", key)
+}
+func (p *RedisPool) GetStringMap(key string) (map[string]string, error) {
+ // get
+ return redigo.StringMap(Do("GET", key))
+}
+
+func (p *RedisPool) GetTTL(key string) (time.Duration, error) {
+ ttl, err := redigo.Int64(Do("TTL", key))
+ return time.Duration(ttl) * time.Second, err
+}
+func (p *RedisPool) GetBytes(key string) ([]byte, error) {
+ return redigo.Bytes(Do("GET", key))
+}
+func (p *RedisPool) GetString(key string) (string, error) {
+ return redigo.String(Do("GET", key))
+}
+func (p *RedisPool) GetInt(key string) (int, error) {
+ return redigo.Int(Do("GET", key))
+}
+func (p *RedisPool) GetStringLength(key string) (int, error) {
+ return redigo.Int(Do("STRLEN", key))
+}
+func (p *RedisPool) ZAdd(key string, score float64, data interface{}) (interface{}, error) {
+ return Do("ZADD", key, score, data)
+}
+func (p *RedisPool) ZRem(key string, data interface{}) (interface{}, error) {
+ return Do("ZREM", key, data)
+}
+func (p *RedisPool) ZRange(key string, start int, end int, withScores bool) ([]interface{}, error) {
+ if withScores {
+ return redigo.Values(Do("ZRANGE", key, start, end, "WITHSCORES"))
+ }
+ return redigo.Values(Do("ZRANGE", key, start, end))
+}
+func (p *RedisPool) SAdd(setName string, data interface{}) (interface{}, error) {
+ return Do("SADD", setName, data)
+}
+func (p *RedisPool) SCard(setName string) (int64, error) {
+ return redigo.Int64(Do("SCARD", setName))
+}
+func (p *RedisPool) SIsMember(setName string, data interface{}) (bool, error) {
+ return redigo.Bool(Do("SISMEMBER", setName, data))
+}
+func (p *RedisPool) SMembers(setName string) ([]string, error) {
+ return redigo.Strings(Do("SMEMBERS", setName))
+}
+func (p *RedisPool) SRem(setName string, data interface{}) (interface{}, error) {
+ return Do("SREM", setName, data)
+}
+func (p *RedisPool) HSet(key string, HKey string, data interface{}) (interface{}, error) {
+ return Do("HSET", key, HKey, data)
+}
+
+func (p *RedisPool) HGet(key string, HKey string) (interface{}, error) {
+ return Do("HGET", key, HKey)
+}
+
+func (p *RedisPool) HMGet(key string, hashKeys ...string) ([]interface{}, error) {
+ ret, err := Do("HMGET", key, hashKeys)
+ if err != nil {
+ return nil, err
+ }
+ reta, ok := ret.([]interface{})
+ if !ok {
+ return nil, errors.New("result not an array")
+ }
+ return reta, nil
+}
+
+func (p *RedisPool) HMSet(key string, hashKeys []string, vals []interface{}) (interface{}, error) {
+ if len(hashKeys) == 0 || len(hashKeys) != len(vals) {
+ var ret interface{}
+ return ret, errors.New("bad length")
+ }
+ input := []interface{}{key}
+ for i, v := range hashKeys {
+ input = append(input, v, vals[i])
+ }
+ return Do("HMSET", input...)
+}
+
+func (p *RedisPool) HGetString(key string, HKey string) (string, error) {
+ return redigo.String(Do("HGET", key, HKey))
+}
+func (p *RedisPool) HGetFloat(key string, HKey string) (float64, error) {
+ f, err := redigo.Float64(Do("HGET", key, HKey))
+ return float64(f), err
+}
+func (p *RedisPool) HGetInt(key string, HKey string) (int, error) {
+ return redigo.Int(Do("HGET", key, HKey))
+}
+func (p *RedisPool) HGetInt64(key string, HKey string) (int64, error) {
+ return redigo.Int64(Do("HGET", key, HKey))
+}
+func (p *RedisPool) HGetBool(key string, HKey string) (bool, error) {
+ return redigo.Bool(Do("HGET", key, HKey))
+}
+func (p *RedisPool) HDel(key string, HKey string) (interface{}, error) {
+ return Do("HDEL", key, HKey)
+}
+func (p *RedisPool) HGetAll(key string) (map[string]interface{}, error) {
+ vals, err := redigo.Values(Do("HGETALL", key))
+ if err != nil {
+ return nil, err
+ }
+ num := len(vals) / 2
+ result := make(map[string]interface{}, num)
+ for i := 0; i < num; i++ {
+ key, _ := redigo.String(vals[2*i], nil)
+ result[key] = vals[2*i+1]
+ }
+ return result, nil
+}
+
+// NOTE: Use this in production environment with extreme care.
+// Read more here:https://redigo.io/commands/keys
+func (p *RedisPool) Keys(pattern string) ([]string, error) {
+ return redigo.Strings(Do("KEYS", pattern))
+}
+
+func (p *RedisPool) HKeys(key string) ([]string, error) {
+ return redigo.Strings(Do("HKEYS", key))
+}
+
+func (p *RedisPool) Exists(key string) (bool, error) {
+ count, err := redigo.Int(Do("EXISTS", key))
+ if count == 0 {
+ return false, err
+ } else {
+ return true, err
+ }
+}
+
+func (p *RedisPool) Incr(key string) (int64, error) {
+ return redigo.Int64(Do("INCR", key))
+}
+
+func (p *RedisPool) Decr(key string) (int64, error) {
+ return redigo.Int64(Do("DECR", key))
+}
+
+func (p *RedisPool) IncrBy(key string, incBy int64) (int64, error) {
+ return redigo.Int64(Do("INCRBY", key, incBy))
+}
+
+func (p *RedisPool) DecrBy(key string, decrBy int64) (int64, error) {
+ return redigo.Int64(Do("DECRBY", key))
+}
+
+func (p *RedisPool) IncrByFloat(key string, incBy float64) (float64, error) {
+ return redigo.Float64(Do("INCRBYFLOAT", key, incBy))
+}
+
+func (p *RedisPool) DecrByFloat(key string, decrBy float64) (float64, error) {
+ return redigo.Float64(Do("DECRBYFLOAT", key, decrBy))
+}
+
+// use for message queue
+func (p *RedisPool) LPush(key string, data interface{}) (interface{}, error) {
+ // set
+ return Do("LPUSH", key, data)
+}
+
+func (p *RedisPool) LPop(key string) (interface{}, error) {
+ return Do("LPOP", key)
+}
+
+func (p *RedisPool) LPopString(key string) (string, error) {
+ return redigo.String(Do("LPOP", key))
+}
+func (p *RedisPool) LPopFloat(key string) (float64, error) {
+ f, err := redigo.Float64(Do("LPOP", key))
+ return float64(f), err
+}
+func (p *RedisPool) LPopInt(key string) (int, error) {
+ return redigo.Int(Do("LPOP", key))
+}
+func (p *RedisPool) LPopInt64(key string) (int64, error) {
+ return redigo.Int64(Do("LPOP", key))
+}
+
+func (p *RedisPool) RPush(key string, data interface{}) (interface{}, error) {
+ // set
+ return Do("RPUSH", key, data)
+}
+
+func (p *RedisPool) RPop(key string) (interface{}, error) {
+ return Do("RPOP", key)
+}
+
+func (p *RedisPool) RPopString(key string) (string, error) {
+ return redigo.String(Do("RPOP", key))
+}
+func (p *RedisPool) RPopFloat(key string) (float64, error) {
+ f, err := redigo.Float64(Do("RPOP", key))
+ return float64(f), err
+}
+func (p *RedisPool) RPopInt(key string) (int, error) {
+ return redigo.Int(Do("RPOP", key))
+}
+func (p *RedisPool) RPopInt64(key string) (int64, error) {
+ return redigo.Int64(Do("RPOP", key))
+}
+
+func (p *RedisPool) Scan(cursor int64, pattern string, count int64) (int64, []string, error) {
+ var items []string
+ var newCursor int64
+
+ values, err := redigo.Values(Do("SCAN", cursor, "MATCH", pattern, "COUNT", count))
+ if err != nil {
+ return 0, nil, err
+ }
+ values, err = redigo.Scan(values, &newCursor, &items)
+ if err != nil {
+ return 0, nil, err
+ }
+
+ return newCursor, items, nil
+}
diff --git a/utils/cache/redis_pool_cluster.go b/utils/cache/redis_pool_cluster.go
new file mode 100644
index 0000000..cd1911b
--- /dev/null
+++ b/utils/cache/redis_pool_cluster.go
@@ -0,0 +1,617 @@
+package cache
+
+import (
+ "strconv"
+ "time"
+
+ "github.com/go-redis/redis"
+)
+
+type RedisClusterPool struct {
+ client *redis.ClusterClient
+}
+
+func NewRedisClusterPool(addrs []string) (*RedisClusterPool, error) {
+ opt := &redis.ClusterOptions{
+ Addrs: addrs,
+ PoolSize: 512,
+ PoolTimeout: 10 * time.Second,
+ IdleTimeout: 10 * time.Second,
+ DialTimeout: 10 * time.Second,
+ ReadTimeout: 3 * time.Second,
+ WriteTimeout: 3 * time.Second,
+ }
+ c := redis.NewClusterClient(opt)
+ if err := c.Ping().Err(); err != nil {
+ return nil, err
+ }
+ return &RedisClusterPool{client: c}, nil
+}
+
+func (p *RedisClusterPool) Get(key string) (interface{}, error) {
+ res, err := p.client.Get(key).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return []byte(res), nil
+}
+func (p *RedisClusterPool) Set(key string, value interface{}) error {
+ err := p.client.Set(key, value, 0).Err()
+ return convertError(err)
+}
+func (p *RedisClusterPool) GetSet(key string, value interface{}) (interface{}, error) {
+ res, err := p.client.GetSet(key, value).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return []byte(res), nil
+}
+func (p *RedisClusterPool) SetNx(key string, value interface{}) (int64, error) {
+ res, err := p.client.SetNX(key, value, 0).Result()
+ if err != nil {
+ return 0, convertError(err)
+ }
+ if res {
+ return 1, nil
+ }
+ return 0, nil
+}
+func (p *RedisClusterPool) SetEx(key string, value interface{}, timeout int64) error {
+ _, err := p.client.Set(key, value, time.Duration(timeout)*time.Second).Result()
+ if err != nil {
+ return convertError(err)
+ }
+ return nil
+}
+
+// nil表示成功,ErrNil表示数据库内已经存在这个key,其他表示数据库发生错误
+func (p *RedisClusterPool) SetNxEx(key string, value interface{}, timeout int64) error {
+ res, err := p.client.SetNX(key, value, time.Duration(timeout)*time.Second).Result()
+ if err != nil {
+ return convertError(err)
+ }
+ if res {
+ return nil
+ }
+ return ErrNil
+}
+func (p *RedisClusterPool) MGet(keys ...string) ([]interface{}, error) {
+ res, err := p.client.MGet(keys...).Result()
+ return res, convertError(err)
+}
+
+// 为确保多个key映射到同一个slot,每个key最好加上hash tag,如:{test}
+func (p *RedisClusterPool) MSet(kvs map[string]interface{}) error {
+ pairs := make([]string, 0, len(kvs)*2)
+ for k, v := range kvs {
+ val, err := String(v, nil)
+ if err != nil {
+ return err
+ }
+ pairs = append(pairs, k, val)
+ }
+ return convertError(p.client.MSet(pairs).Err())
+}
+
+// 为确保多个key映射到同一个slot,每个key最好加上hash tag,如:{test}
+func (p *RedisClusterPool) MSetNX(kvs map[string]interface{}) (bool, error) {
+ pairs := make([]string, 0, len(kvs)*2)
+ for k, v := range kvs {
+ val, err := String(v, nil)
+ if err != nil {
+ return false, err
+ }
+ pairs = append(pairs, k, val)
+ }
+ res, err := p.client.MSetNX(pairs).Result()
+ return res, convertError(err)
+}
+func (p *RedisClusterPool) ExpireAt(key string, timestamp int64) (int64, error) {
+ res, err := p.client.ExpireAt(key, time.Unix(timestamp, 0)).Result()
+ if err != nil {
+ return 0, convertError(err)
+ }
+ if res {
+ return 1, nil
+ }
+ return 0, nil
+}
+func (p *RedisClusterPool) Del(keys ...string) (int64, error) {
+ args := make([]interface{}, 0, len(keys))
+ for _, key := range keys {
+ args = append(args, key)
+ }
+ res, err := p.client.Del(keys...).Result()
+ if err != nil {
+ return res, convertError(err)
+ }
+ return res, nil
+}
+func (p *RedisClusterPool) Incr(key string) (int64, error) {
+ res, err := p.client.Incr(key).Result()
+ if err != nil {
+ return res, convertError(err)
+ }
+ return res, nil
+}
+func (p *RedisClusterPool) IncrBy(key string, delta int64) (int64, error) {
+ res, err := p.client.IncrBy(key, delta).Result()
+ if err != nil {
+ return res, convertError(err)
+ }
+ return res, nil
+}
+func (p *RedisClusterPool) Expire(key string, duration int64) (int64, error) {
+ res, err := p.client.Expire(key, time.Duration(duration)*time.Second).Result()
+ if err != nil {
+ return 0, convertError(err)
+ }
+ if res {
+ return 1, nil
+ }
+ return 0, nil
+}
+func (p *RedisClusterPool) Exists(key string) (bool, error) { // todo (bool, error)
+ res, err := p.client.Exists(key).Result()
+ if err != nil {
+ return false, convertError(err)
+ }
+ if res > 0 {
+ return true, nil
+ }
+ return false, nil
+}
+func (p *RedisClusterPool) HGet(key string, field string) (interface{}, error) {
+ res, err := p.client.HGet(key, field).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return []byte(res), nil
+}
+func (p *RedisClusterPool) HLen(key string) (int64, error) {
+ res, err := p.client.HLen(key).Result()
+ if err != nil {
+ return res, convertError(err)
+ }
+ return res, nil
+}
+func (p *RedisClusterPool) HSet(key string, field string, val interface{}) error {
+ value, err := String(val, nil)
+ if err != nil && err != ErrNil {
+ return err
+ }
+ _, err = p.client.HSet(key, field, value).Result()
+ if err != nil {
+ return convertError(err)
+ }
+ return nil
+}
+func (p *RedisClusterPool) HDel(key string, fields ...string) (int64, error) {
+ args := make([]interface{}, 0, len(fields)+1)
+ args = append(args, key)
+ for _, field := range fields {
+ args = append(args, field)
+ }
+ res, err := p.client.HDel(key, fields...).Result()
+ if err != nil {
+ return 0, convertError(err)
+ }
+ return res, nil
+}
+
+func (p *RedisClusterPool) HMGet(key string, fields ...string) (interface{}, error) {
+ args := make([]interface{}, 0, len(fields)+1)
+ args = append(args, key)
+ for _, field := range fields {
+ args = append(args, field)
+ }
+ if len(fields) == 0 {
+ return nil, ErrNil
+ }
+ res, err := p.client.HMGet(key, fields...).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return res, nil
+}
+func (p *RedisClusterPool) HMSet(key string, kvs ...interface{}) error {
+ if len(kvs) == 0 {
+ return nil
+ }
+ if len(kvs)%2 != 0 {
+ return ErrWrongArgsNum
+ }
+ var err error
+ v := map[string]interface{}{} // todo change
+ v["field"], err = String(kvs[0], nil)
+ if err != nil && err != ErrNil {
+ return err
+ }
+ v["value"], err = String(kvs[1], nil)
+ if err != nil && err != ErrNil {
+ return err
+ }
+ pairs := make([]string, 0, len(kvs)-2)
+ if len(kvs) > 2 {
+ for _, kv := range kvs[2:] {
+ kvString, err := String(kv, nil)
+ if err != nil && err != ErrNil {
+ return err
+ }
+ pairs = append(pairs, kvString)
+ }
+ }
+ v["paris"] = pairs
+ _, err = p.client.HMSet(key, v).Result()
+ if err != nil {
+ return convertError(err)
+ }
+ return nil
+}
+
+func (p *RedisClusterPool) HKeys(key string) ([]string, error) {
+ res, err := p.client.HKeys(key).Result()
+ if err != nil {
+ return res, convertError(err)
+ }
+ return res, nil
+}
+func (p *RedisClusterPool) HVals(key string) ([]interface{}, error) {
+ res, err := p.client.HVals(key).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ rs := make([]interface{}, 0, len(res))
+ for _, res := range res {
+ rs = append(rs, res)
+ }
+ return rs, nil
+}
+func (p *RedisClusterPool) HGetAll(key string) (map[string]string, error) {
+ vals, err := p.client.HGetAll(key).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return vals, nil
+}
+func (p *RedisClusterPool) HIncrBy(key, field string, delta int64) (int64, error) {
+ res, err := p.client.HIncrBy(key, field, delta).Result()
+ if err != nil {
+ return res, convertError(err)
+ }
+ return res, nil
+}
+func (p *RedisClusterPool) ZAdd(key string, kvs ...interface{}) (int64, error) {
+ args := make([]interface{}, 0, len(kvs)+1)
+ args = append(args, key)
+ args = append(args, kvs...)
+ if len(kvs) == 0 {
+ return 0, nil
+ }
+ if len(kvs)%2 != 0 {
+ return 0, ErrWrongArgsNum
+ }
+ zs := make([]redis.Z, len(kvs)/2)
+ for i := 0; i < len(kvs); i += 2 {
+ idx := i / 2
+ score, err := Float64(kvs[i], nil)
+ if err != nil && err != ErrNil {
+ return 0, err
+ }
+ zs[idx].Score = score
+ zs[idx].Member = kvs[i+1]
+ }
+ res, err := p.client.ZAdd(key, zs...).Result()
+ if err != nil {
+ return res, convertError(err)
+ }
+ return res, nil
+}
+func (p *RedisClusterPool) ZRem(key string, members ...string) (int64, error) {
+ args := make([]interface{}, 0, len(members))
+ args = append(args, key)
+ for _, member := range members {
+ args = append(args, member)
+ }
+ res, err := p.client.ZRem(key, members).Result()
+ if err != nil {
+ return res, convertError(err)
+ }
+ return res, err
+}
+
+func (p *RedisClusterPool) ZRange(key string, min, max int64, withScores bool) (interface{}, error) {
+ res := make([]interface{}, 0)
+ if withScores {
+ zs, err := p.client.ZRangeWithScores(key, min, max).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ for _, z := range zs {
+ res = append(res, z.Member, strconv.FormatFloat(z.Score, 'f', -1, 64))
+ }
+ } else {
+ ms, err := p.client.ZRange(key, min, max).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ for _, m := range ms {
+ res = append(res, m)
+ }
+ }
+ return res, nil
+}
+func (p *RedisClusterPool) ZRangeByScoreWithScore(key string, min, max int64) (map[string]int64, error) {
+ opt := new(redis.ZRangeBy)
+ opt.Min = strconv.FormatInt(int64(min), 10)
+ opt.Max = strconv.FormatInt(int64(max), 10)
+ opt.Count = -1
+ opt.Offset = 0
+ vals, err := p.client.ZRangeByScoreWithScores(key, *opt).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ res := make(map[string]int64, len(vals))
+ for _, val := range vals {
+ key, err := String(val.Member, nil)
+ if err != nil && err != ErrNil {
+ return nil, err
+ }
+ res[key] = int64(val.Score)
+ }
+ return res, nil
+}
+func (p *RedisClusterPool) LRange(key string, start, stop int64) (interface{}, error) {
+ res, err := p.client.LRange(key, start, stop).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return res, nil
+}
+func (p *RedisClusterPool) LSet(key string, index int, value interface{}) error {
+ err := p.client.LSet(key, int64(index), value).Err()
+ return convertError(err)
+}
+func (p *RedisClusterPool) LLen(key string) (int64, error) {
+ res, err := p.client.LLen(key).Result()
+ if err != nil {
+ return res, convertError(err)
+ }
+ return res, nil
+}
+func (p *RedisClusterPool) LRem(key string, count int, value interface{}) (int, error) {
+ val, _ := value.(string)
+ res, err := p.client.LRem(key, int64(count), val).Result()
+ if err != nil {
+ return int(res), convertError(err)
+ }
+ return int(res), nil
+}
+func (p *RedisClusterPool) TTl(key string) (int64, error) {
+ duration, err := p.client.TTL(key).Result()
+ if err != nil {
+ return int64(duration.Seconds()), convertError(err)
+ }
+ return int64(duration.Seconds()), nil
+}
+func (p *RedisClusterPool) LPop(key string) (interface{}, error) {
+ res, err := p.client.LPop(key).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return res, nil
+}
+func (p *RedisClusterPool) RPop(key string) (interface{}, error) {
+ res, err := p.client.RPop(key).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return res, nil
+}
+func (p *RedisClusterPool) BLPop(key string, timeout int) (interface{}, error) {
+ res, err := p.client.BLPop(time.Duration(timeout)*time.Second, key).Result()
+ if err != nil {
+ // 兼容redis 2.x
+ if err == redis.Nil {
+ return nil, ErrNil
+ }
+ return nil, err
+ }
+ return res[1], nil
+}
+func (p *RedisClusterPool) BRPop(key string, timeout int) (interface{}, error) {
+ res, err := p.client.BRPop(time.Duration(timeout)*time.Second, key).Result()
+ if err != nil {
+ // 兼容redis 2.x
+ if err == redis.Nil {
+ return nil, ErrNil
+ }
+ return nil, convertError(err)
+ }
+ return res[1], nil
+}
+func (p *RedisClusterPool) LPush(key string, value ...interface{}) error {
+ args := make([]interface{}, 0, len(value)+1)
+ args = append(args, key)
+ args = append(args, value...)
+ vals := make([]string, 0, len(value))
+ for _, v := range value {
+ val, err := String(v, nil)
+ if err != nil && err != ErrNil {
+ return err
+ }
+ vals = append(vals, val)
+ }
+ _, err := p.client.LPush(key, vals).Result() // todo ...
+ if err != nil {
+ return convertError(err)
+ }
+ return nil
+}
+func (p *RedisClusterPool) RPush(key string, value ...interface{}) error {
+ args := make([]interface{}, 0, len(value)+1)
+ args = append(args, key)
+ args = append(args, value...)
+ vals := make([]string, 0, len(value))
+ for _, v := range value {
+ val, err := String(v, nil)
+ if err != nil && err != ErrNil {
+ if err == ErrNil {
+ continue
+ }
+ return err
+ }
+ if val == "" {
+ continue
+ }
+ vals = append(vals, val)
+ }
+ _, err := p.client.RPush(key, vals).Result() // todo ...
+ if err != nil {
+ return convertError(err)
+ }
+ return nil
+}
+
+// 为确保srcKey跟destKey映射到同一个slot,srcKey和destKey需要加上hash tag,如:{test}
+func (p *RedisClusterPool) BRPopLPush(srcKey string, destKey string, timeout int) (interface{}, error) {
+ res, err := p.client.BRPopLPush(srcKey, destKey, time.Duration(timeout)*time.Second).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return res, nil
+}
+
+// 为确保srcKey跟destKey映射到同一个slot,srcKey和destKey需要加上hash tag,如:{test}
+func (p *RedisClusterPool) RPopLPush(srcKey string, destKey string) (interface{}, error) {
+ res, err := p.client.RPopLPush(srcKey, destKey).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return res, nil
+}
+func (p *RedisClusterPool) SAdd(key string, members ...interface{}) (int64, error) {
+ args := make([]interface{}, 0, len(members)+1)
+ args = append(args, key)
+ args = append(args, members...)
+ ms := make([]string, 0, len(members))
+ for _, member := range members {
+ m, err := String(member, nil)
+ if err != nil && err != ErrNil {
+ return 0, err
+ }
+ ms = append(ms, m)
+ }
+ res, err := p.client.SAdd(key, ms).Result() // todo ...
+ if err != nil {
+ return res, convertError(err)
+ }
+ return res, nil
+}
+func (p *RedisClusterPool) SPop(key string) ([]byte, error) {
+ res, err := p.client.SPop(key).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return []byte(res), nil
+}
+func (p *RedisClusterPool) SIsMember(key string, member interface{}) (bool, error) {
+ m, _ := member.(string)
+ res, err := p.client.SIsMember(key, m).Result()
+ if err != nil {
+ return res, convertError(err)
+ }
+ return res, nil
+}
+func (p *RedisClusterPool) SRem(key string, members ...interface{}) (int64, error) {
+ args := make([]interface{}, 0, len(members)+1)
+ args = append(args, key)
+ args = append(args, members...)
+ ms := make([]string, 0, len(members))
+ for _, member := range members {
+ m, err := String(member, nil)
+ if err != nil && err != ErrNil {
+ return 0, err
+ }
+ ms = append(ms, m)
+ }
+ res, err := p.client.SRem(key, ms).Result() // todo ...
+ if err != nil {
+ return res, convertError(err)
+ }
+ return res, nil
+}
+func (p *RedisClusterPool) SMembers(key string) ([]string, error) {
+ res, err := p.client.SMembers(key).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return res, nil
+}
+func (p *RedisClusterPool) ScriptLoad(luaScript string) (interface{}, error) {
+ res, err := p.client.ScriptLoad(luaScript).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return res, nil
+}
+func (p *RedisClusterPool) EvalSha(sha1 string, numberKeys int, keysArgs ...interface{}) (interface{}, error) {
+ vals := make([]interface{}, 0, len(keysArgs)+2)
+ vals = append(vals, sha1, numberKeys)
+ vals = append(vals, keysArgs...)
+ keys := make([]string, 0, numberKeys)
+ args := make([]string, 0, len(keysArgs)-numberKeys)
+ for i, value := range keysArgs {
+ val, err := String(value, nil)
+ if err != nil && err != ErrNil {
+ return nil, err
+ }
+ if i < numberKeys {
+ keys = append(keys, val)
+ } else {
+ args = append(args, val)
+ }
+ }
+ res, err := p.client.EvalSha(sha1, keys, args).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return res, nil
+}
+func (p *RedisClusterPool) Eval(luaScript string, numberKeys int, keysArgs ...interface{}) (interface{}, error) {
+ vals := make([]interface{}, 0, len(keysArgs)+2)
+ vals = append(vals, luaScript, numberKeys)
+ vals = append(vals, keysArgs...)
+ keys := make([]string, 0, numberKeys)
+ args := make([]string, 0, len(keysArgs)-numberKeys)
+ for i, value := range keysArgs {
+ val, err := String(value, nil)
+ if err != nil && err != ErrNil {
+ return nil, err
+ }
+ if i < numberKeys {
+ keys = append(keys, val)
+ } else {
+ args = append(args, val)
+ }
+ }
+ res, err := p.client.Eval(luaScript, keys, args).Result()
+ if err != nil {
+ return nil, convertError(err)
+ }
+ return res, nil
+}
+func (p *RedisClusterPool) GetBit(key string, offset int64) (int64, error) {
+ res, err := p.client.GetBit(key, offset).Result()
+ if err != nil {
+ return res, convertError(err)
+ }
+ return res, nil
+}
+func (p *RedisClusterPool) SetBit(key string, offset uint32, value int) (int, error) {
+ res, err := p.client.SetBit(key, int64(offset), value).Result()
+ return int(res), convertError(err)
+}
+func (p *RedisClusterPool) GetClient() *redis.ClusterClient {
+ return pools
+}
diff --git a/utils/convert.go b/utils/convert.go
new file mode 100644
index 0000000..3645f12
--- /dev/null
+++ b/utils/convert.go
@@ -0,0 +1,385 @@
+package zhios_order_relate_utils
+
+import (
+ "encoding/binary"
+ "encoding/json"
+ "fmt"
+ "math"
+ "reflect"
+ "strconv"
+ "strings"
+)
+
+func ToString(raw interface{}, e error) (res string) {
+ if e != nil {
+ return ""
+ }
+ return AnyToString(raw)
+}
+
+func ToInt64(raw interface{}, e error) int64 {
+ if e != nil {
+ return 0
+ }
+ return AnyToInt64(raw)
+}
+func IsNil(i interface{}) bool {
+ vi := reflect.ValueOf(i)
+ if vi.Kind() == reflect.Ptr {
+ return vi.IsNil()
+ }
+ return false
+}
+func AnyToBool(raw interface{}) bool {
+ switch i := raw.(type) {
+ case float32, float64, int, int64, uint, uint8, uint16, uint32, uint64, int8, int16, int32:
+ return i != 0
+ case []byte:
+ return i != nil
+ case string:
+ if i == "false" {
+ return false
+ }
+ return i != ""
+ case error:
+ return false
+ case nil:
+ return true
+ }
+ val := fmt.Sprint(raw)
+ val = strings.TrimLeft(val, "&")
+ if strings.TrimLeft(val, "{}") == "" {
+ return false
+ }
+ if strings.TrimLeft(val, "[]") == "" {
+ return false
+ }
+ // ptr type
+ b, err := json.Marshal(raw)
+ if err != nil {
+ return false
+ }
+ if strings.TrimLeft(string(b), "\"\"") == "" {
+ return false
+ }
+ if strings.TrimLeft(string(b), "{}") == "" {
+ return false
+ }
+ return true
+}
+
+func AnyToInt64(raw interface{}) int64 {
+ switch i := raw.(type) {
+ case string:
+ res, _ := strconv.ParseInt(i, 10, 64)
+ return res
+ case []byte:
+ return BytesToInt64(i)
+ case int:
+ return int64(i)
+ case int64:
+ return i
+ case uint:
+ return int64(i)
+ case uint8:
+ return int64(i)
+ case uint16:
+ return int64(i)
+ case uint32:
+ return int64(i)
+ case uint64:
+ return int64(i)
+ case int8:
+ return int64(i)
+ case int16:
+ return int64(i)
+ case int32:
+ return int64(i)
+ case float32:
+ return int64(i)
+ case float64:
+ return int64(i)
+ case error:
+ return 0
+ case bool:
+ if i {
+ return 1
+ }
+ return 0
+ }
+ return 0
+}
+
+func AnyToString(raw interface{}) string {
+ switch i := raw.(type) {
+ case []byte:
+ return string(i)
+ case int:
+ return strconv.FormatInt(int64(i), 10)
+ case int64:
+ return strconv.FormatInt(i, 10)
+ case float32:
+ return Float64ToStr(float64(i))
+ case float64:
+ return Float64ToStr(i)
+ case uint:
+ return strconv.FormatInt(int64(i), 10)
+ case uint8:
+ return strconv.FormatInt(int64(i), 10)
+ case uint16:
+ return strconv.FormatInt(int64(i), 10)
+ case uint32:
+ return strconv.FormatInt(int64(i), 10)
+ case uint64:
+ return strconv.FormatInt(int64(i), 10)
+ case int8:
+ return strconv.FormatInt(int64(i), 10)
+ case int16:
+ return strconv.FormatInt(int64(i), 10)
+ case int32:
+ return strconv.FormatInt(int64(i), 10)
+ case string:
+ return i
+ case error:
+ return i.Error()
+ case bool:
+ return strconv.FormatBool(i)
+ }
+ return fmt.Sprintf("%#v", raw)
+}
+
+func AnyToFloat64(raw interface{}) float64 {
+ switch i := raw.(type) {
+ case []byte:
+ f, _ := strconv.ParseFloat(string(i), 64)
+ return f
+ case int:
+ return float64(i)
+ case int64:
+ return float64(i)
+ case float32:
+ return float64(i)
+ case float64:
+ return i
+ case uint:
+ return float64(i)
+ case uint8:
+ return float64(i)
+ case uint16:
+ return float64(i)
+ case uint32:
+ return float64(i)
+ case uint64:
+ return float64(i)
+ case int8:
+ return float64(i)
+ case int16:
+ return float64(i)
+ case int32:
+ return float64(i)
+ case string:
+ f, _ := strconv.ParseFloat(i, 64)
+ return f
+ case bool:
+ if i {
+ return 1
+ }
+ }
+ return 0
+}
+
+func ToByte(raw interface{}, e error) []byte {
+ if e != nil {
+ return []byte{}
+ }
+ switch i := raw.(type) {
+ case string:
+ return []byte(i)
+ case int:
+ return Int64ToBytes(int64(i))
+ case int64:
+ return Int64ToBytes(i)
+ case float32:
+ return Float32ToByte(i)
+ case float64:
+ return Float64ToByte(i)
+ case uint:
+ return Int64ToBytes(int64(i))
+ case uint8:
+ return Int64ToBytes(int64(i))
+ case uint16:
+ return Int64ToBytes(int64(i))
+ case uint32:
+ return Int64ToBytes(int64(i))
+ case uint64:
+ return Int64ToBytes(int64(i))
+ case int8:
+ return Int64ToBytes(int64(i))
+ case int16:
+ return Int64ToBytes(int64(i))
+ case int32:
+ return Int64ToBytes(int64(i))
+ case []byte:
+ return i
+ case error:
+ return []byte(i.Error())
+ case bool:
+ if i {
+ return []byte("true")
+ }
+ return []byte("false")
+ }
+ return []byte(fmt.Sprintf("%#v", raw))
+}
+
+func Int64ToBytes(i int64) []byte {
+ var buf = make([]byte, 8)
+ binary.BigEndian.PutUint64(buf, uint64(i))
+ return buf
+}
+
+func BytesToInt64(buf []byte) int64 {
+ return int64(binary.BigEndian.Uint64(buf))
+}
+
+func StrToInt(s string) int {
+ res, _ := strconv.Atoi(s)
+ return res
+}
+
+func StrToInt64(s string) int64 {
+ res, _ := strconv.ParseInt(s, 10, 64)
+ return res
+}
+
+func Float32ToByte(float float32) []byte {
+ bits := math.Float32bits(float)
+ bytes := make([]byte, 4)
+ binary.LittleEndian.PutUint32(bytes, bits)
+
+ return bytes
+}
+
+func ByteToFloat32(bytes []byte) float32 {
+ bits := binary.LittleEndian.Uint32(bytes)
+ return math.Float32frombits(bits)
+}
+
+func Float64ToByte(float float64) []byte {
+ bits := math.Float64bits(float)
+ bytes := make([]byte, 8)
+ binary.LittleEndian.PutUint64(bytes, bits)
+ return bytes
+}
+
+func ByteToFloat64(bytes []byte) float64 {
+ bits := binary.LittleEndian.Uint64(bytes)
+ return math.Float64frombits(bits)
+}
+
+func Float64ToStr(f float64) string {
+ return strconv.FormatFloat(f, 'f', 2, 64)
+}
+func Float64ToStrPrec1(f float64) string {
+ return strconv.FormatFloat(f, 'f', 1, 64)
+}
+
+func Float64ToStrPrec4(f float64) string {
+ return strconv.FormatFloat(f, 'f', 4, 64)
+}
+func Float64ToStrPrec10(f float64) string {
+ return strconv.FormatFloat(f, 'f', 10, 64)
+}
+
+func Float64ToStrPrec6(f float64) string {
+ return strconv.FormatFloat(f, 'f', 6, 64)
+}
+
+func Float64ToStrByPrec(f float64, prec int) string {
+ return strconv.FormatFloat(f, 'f', prec, 64)
+}
+
+func Float32ToStr(f float32) string {
+ return Float64ToStr(float64(f))
+}
+
+func StrToFloat64(s string) float64 {
+ res, err := strconv.ParseFloat(s, 64)
+ if err != nil {
+ return 0
+ }
+ return res
+}
+func StrToFormat(s string, prec int) string {
+ ex := strings.Split(s, ".")
+ if len(ex) == 2 {
+ if StrToFloat64(ex[1]) == 0 { //小数点后面为空就是不要小数点了
+ return ex[0]
+ }
+ //看取多少位
+ str := ex[1]
+ str1 := str
+ if prec < len(str) {
+ str1 = str[0:prec]
+ } else {
+ for i := 0; i < prec-len(str); i++ {
+ str1 += "0"
+ }
+ }
+ if prec > 0 {
+ return ex[0] + "." + str1
+ } else {
+ return ex[0]
+ }
+ }
+ return s
+}
+
+func StrToFloat32(s string) float32 {
+ res, err := strconv.ParseFloat(s, 32)
+ if err != nil {
+ return 0
+ }
+ return float32(res)
+}
+
+func StrToBool(s string) bool {
+ b, _ := strconv.ParseBool(s)
+ return b
+}
+
+func BoolToStr(b bool) string {
+ if b {
+ return "true"
+ }
+ return "false"
+}
+
+func FloatToInt64(f float64) int64 {
+ return int64(f)
+}
+
+func IntToStr(i int) string {
+ return strconv.Itoa(i)
+}
+
+func Int64ToStr(i int64) string {
+ return strconv.FormatInt(i, 10)
+}
+
+func IntToFloat64(i int) float64 {
+ s := strconv.Itoa(i)
+ res, err := strconv.ParseFloat(s, 64)
+ if err != nil {
+ return 0
+ }
+ return res
+}
+func Int64ToFloat64(i int64) float64 {
+ s := strconv.FormatInt(i, 10)
+ res, err := strconv.ParseFloat(s, 64)
+ if err != nil {
+ return 0
+ }
+ return res
+}
diff --git a/utils/curl.go b/utils/curl.go
new file mode 100644
index 0000000..cfaffde
--- /dev/null
+++ b/utils/curl.go
@@ -0,0 +1,209 @@
+package zhios_order_relate_utils
+
+import (
+ "bytes"
+ "crypto/tls"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "sort"
+ "strings"
+ "time"
+)
+
+var CurlDebug bool
+
+func CurlGet(router string, header map[string]string) ([]byte, error) {
+ return curl(http.MethodGet, router, nil, header)
+}
+func CurlGetJson(router string, body interface{}, header map[string]string) ([]byte, error) {
+ return curl_new(http.MethodGet, router, body, header)
+}
+
+// 只支持form 与json 提交, 请留意body的类型, 支持string, []byte, map[string]string
+func CurlPost(router string, body interface{}, header map[string]string) ([]byte, error) {
+ return curl(http.MethodPost, router, body, header)
+}
+
+func CurlPut(router string, body interface{}, header map[string]string) ([]byte, error) {
+ return curl(http.MethodPut, router, body, header)
+}
+
+// 只支持form 与json 提交, 请留意body的类型, 支持string, []byte, map[string]string
+func CurlPatch(router string, body interface{}, header map[string]string) ([]byte, error) {
+ return curl(http.MethodPatch, router, body, header)
+}
+
+// CurlDelete is curl delete
+func CurlDelete(router string, body interface{}, header map[string]string) ([]byte, error) {
+ return curl(http.MethodDelete, router, body, header)
+}
+
+func curl(method, router string, body interface{}, header map[string]string) ([]byte, error) {
+ var reqBody io.Reader
+ contentType := "application/json"
+ switch v := body.(type) {
+ case string:
+ reqBody = strings.NewReader(v)
+ case []byte:
+ reqBody = bytes.NewReader(v)
+ case map[string]string:
+ val := url.Values{}
+ for k, v := range v {
+ val.Set(k, v)
+ }
+ reqBody = strings.NewReader(val.Encode())
+ contentType = "application/x-www-form-urlencoded"
+ case map[string]interface{}:
+ val := url.Values{}
+ for k, v := range v {
+ val.Set(k, v.(string))
+ }
+ reqBody = strings.NewReader(val.Encode())
+ contentType = "application/x-www-form-urlencoded"
+ }
+ if header == nil {
+ header = map[string]string{"Content-Type": contentType}
+ }
+ if _, ok := header["Content-Type"]; !ok {
+ header["Content-Type"] = contentType
+ }
+ resp, er := CurlReq(method, router, reqBody, header)
+ if er != nil {
+ return nil, er
+ }
+ res, err := ioutil.ReadAll(resp.Body)
+ if CurlDebug {
+ blob := SerializeStr(body)
+ if contentType != "application/json" {
+ blob = HttpBuild(body)
+ }
+ fmt.Printf("\n\n=====================\n[url]: %s\n[time]: %s\n[method]: %s\n[content-type]: %v\n[req_header]: %s\n[req_body]: %#v\n[resp_err]: %v\n[resp_header]: %v\n[resp_body]: %v\n=====================\n\n",
+ router,
+ time.Now().Format("2006-01-02 15:04:05.000"),
+ method,
+ contentType,
+ HttpBuildQuery(header),
+ blob,
+ err,
+ SerializeStr(resp.Header),
+ string(res),
+ )
+ }
+ resp.Body.Close()
+ return res, err
+}
+
+func curl_new(method, router string, body interface{}, header map[string]string) ([]byte, error) {
+ var reqBody io.Reader
+ contentType := "application/json"
+
+ if header == nil {
+ header = map[string]string{"Content-Type": contentType}
+ }
+ if _, ok := header["Content-Type"]; !ok {
+ header["Content-Type"] = contentType
+ }
+ resp, er := CurlReq(method, router, reqBody, header)
+ if er != nil {
+ return nil, er
+ }
+ res, err := ioutil.ReadAll(resp.Body)
+ if CurlDebug {
+ blob := SerializeStr(body)
+ if contentType != "application/json" {
+ blob = HttpBuild(body)
+ }
+ fmt.Printf("\n\n=====================\n[url]: %s\n[time]: %s\n[method]: %s\n[content-type]: %v\n[req_header]: %s\n[req_body]: %#v\n[resp_err]: %v\n[resp_header]: %v\n[resp_body]: %v\n=====================\n\n",
+ router,
+ time.Now().Format("2006-01-02 15:04:05.000"),
+ method,
+ contentType,
+ HttpBuildQuery(header),
+ blob,
+ err,
+ SerializeStr(resp.Header),
+ string(res),
+ )
+ }
+ resp.Body.Close()
+ return res, err
+}
+
+func CurlReq(method, router string, reqBody io.Reader, header map[string]string) (*http.Response, error) {
+ req, _ := http.NewRequest(method, router, reqBody)
+ if header != nil {
+ for k, v := range header {
+ req.Header.Set(k, v)
+ }
+ }
+ // 绕过github等可能因为特征码返回503问题
+ // https://www.imwzk.com/posts/2021-03-14-why-i-always-get-503-with-golang/
+ defaultCipherSuites := []uint16{0xc02f, 0xc030, 0xc02b, 0xc02c, 0xcca8, 0xcca9, 0xc013, 0xc009,
+ 0xc014, 0xc00a, 0x009c, 0x009d, 0x002f, 0x0035, 0xc012, 0x000a}
+ client := &http.Client{
+ Transport: &http.Transport{
+ TLSClientConfig: &tls.Config{
+ InsecureSkipVerify: true,
+ CipherSuites: append(defaultCipherSuites[8:], defaultCipherSuites[:8]...),
+ },
+ },
+ // 获取301重定向
+ CheckRedirect: func(req *http.Request, via []*http.Request) error {
+ return http.ErrUseLastResponse
+ },
+ }
+ return client.Do(req)
+}
+
+// 组建get请求参数,sortAsc true为小到大,false为大到小,nil不排序 a=123&b=321
+func HttpBuildQuery(args map[string]string, sortAsc ...bool) string {
+ str := ""
+ if len(args) == 0 {
+ return str
+ }
+ if len(sortAsc) > 0 {
+ keys := make([]string, 0, len(args))
+ for k := range args {
+ keys = append(keys, k)
+ }
+ if sortAsc[0] {
+ sort.Strings(keys)
+ } else {
+ sort.Sort(sort.Reverse(sort.StringSlice(keys)))
+ }
+ for _, k := range keys {
+ str += "&" + k + "=" + args[k]
+ }
+ } else {
+ for k, v := range args {
+ str += "&" + k + "=" + v
+ }
+ }
+ return str[1:]
+}
+
+func HttpBuild(body interface{}, sortAsc ...bool) string {
+ params := map[string]string{}
+ if args, ok := body.(map[string]interface{}); ok {
+ for k, v := range args {
+ params[k] = AnyToString(v)
+ }
+ return HttpBuildQuery(params, sortAsc...)
+ }
+ if args, ok := body.(map[string]string); ok {
+ for k, v := range args {
+ params[k] = AnyToString(v)
+ }
+ return HttpBuildQuery(params, sortAsc...)
+ }
+ if args, ok := body.(map[string]int); ok {
+ for k, v := range args {
+ params[k] = AnyToString(v)
+ }
+ return HttpBuildQuery(params, sortAsc...)
+ }
+ return AnyToString(body)
+}
diff --git a/utils/file.go b/utils/file.go
new file mode 100644
index 0000000..f5b4c92
--- /dev/null
+++ b/utils/file.go
@@ -0,0 +1,22 @@
+package zhios_order_relate_utils
+
+import (
+ "os"
+ "path"
+ "strings"
+ "time"
+)
+
+// 获取文件后缀
+func FileExt(fname string) string {
+ return strings.ToLower(strings.TrimLeft(path.Ext(fname), "."))
+}
+
+func FilePutContents(fileName string, content string) {
+ fd, _ := os.OpenFile("./tmp/"+fileName+".logs", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
+ fd_time := time.Now().Format("2006-01-02 15:04:05")
+ fd_content := strings.Join([]string{"[", fd_time, "] ", content, "\n"}, "")
+ buf := []byte(fd_content)
+ fd.Write(buf)
+ fd.Close()
+}
diff --git a/utils/format.go b/utils/format.go
new file mode 100644
index 0000000..dbe8d4f
--- /dev/null
+++ b/utils/format.go
@@ -0,0 +1,59 @@
+package zhios_order_relate_utils
+
+import (
+ "math"
+)
+
+func CouponFormat(data string) string {
+ switch data {
+ case "0.00", "0", "":
+ return ""
+ default:
+ return Int64ToStr(FloatToInt64(StrToFloat64(data)))
+ }
+}
+func CommissionFormat(data string) string {
+ if data != "" && data != "0" {
+ return data
+ }
+
+ return ""
+}
+
+func HideString(src string, hLen int) string {
+ str := []rune(src)
+ if hLen == 0 {
+ hLen = 4
+ }
+ hideStr := ""
+ for i := 0; i < hLen; i++ {
+ hideStr += "*"
+ }
+ hideLen := len(str) / 2
+ showLen := len(str) - hideLen
+ if hideLen == 0 || showLen == 0 {
+ return hideStr
+ }
+ subLen := showLen / 2
+ if subLen == 0 {
+ return string(str[:showLen]) + hideStr
+ }
+ s := string(str[:subLen])
+ s += hideStr
+ s += string(str[len(str)-subLen:])
+ return s
+}
+
+//SaleCountFormat is 格式化销量
+func SaleCountFormat(s string) string {
+ return s + "已售"
+}
+
+// 小数格式化
+func FloatFormat(f float64, i int) float64 {
+ if i > 14 {
+ return f
+ }
+ p := math.Pow10(i)
+ return float64(int64((f+0.000000000000009)*p)) / p
+}
diff --git a/utils/logx/log.go b/utils/logx/log.go
new file mode 100644
index 0000000..586e4ab
--- /dev/null
+++ b/utils/logx/log.go
@@ -0,0 +1,245 @@
+package zhios_order_relate_logx
+
+import (
+ "os"
+ "strings"
+ "time"
+
+ "go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
+)
+
+type LogConfig struct {
+ AppName string `yaml:"app_name" json:"app_name" toml:"app_name"`
+ Level string `yaml:"level" json:"level" toml:"level"`
+ StacktraceLevel string `yaml:"stacktrace_level" json:"stacktrace_level" toml:"stacktrace_level"`
+ IsStdOut bool `yaml:"is_stdout" json:"is_stdout" toml:"is_stdout"`
+ TimeFormat string `yaml:"time_format" json:"time_format" toml:"time_format"` // second, milli, nano, standard, iso,
+ Encoding string `yaml:"encoding" json:"encoding" toml:"encoding"` // console, json
+ Skip int `yaml:"skip" json:"skip" toml:"skip"`
+
+ IsFileOut bool `yaml:"is_file_out" json:"is_file_out" toml:"is_file_out"`
+ FileDir string `yaml:"file_dir" json:"file_dir" toml:"file_dir"`
+ FileName string `yaml:"file_name" json:"file_name" toml:"file_name"`
+ FileMaxSize int `yaml:"file_max_size" json:"file_max_size" toml:"file_max_size"`
+ FileMaxAge int `yaml:"file_max_age" json:"file_max_age" toml:"file_max_age"`
+}
+
+var (
+ l *LogX = defaultLogger()
+ conf *LogConfig
+)
+
+// default logger setting
+func defaultLogger() *LogX {
+ conf = &LogConfig{
+ Level: "debug",
+ StacktraceLevel: "error",
+ IsStdOut: true,
+ TimeFormat: "standard",
+ Encoding: "console",
+ Skip: 2,
+ }
+ writers := []zapcore.WriteSyncer{os.Stdout}
+ lg, lv := newZapLogger(setLogLevel(conf.Level), setLogLevel(conf.StacktraceLevel), conf.Encoding, conf.TimeFormat, conf.Skip, zapcore.NewMultiWriteSyncer(writers...))
+ zap.RedirectStdLog(lg)
+ return &LogX{logger: lg, atomLevel: lv}
+}
+
+// initial standard log, if you don't init, it will use default logger setting
+func InitDefaultLogger(cfg *LogConfig) {
+ var writers []zapcore.WriteSyncer
+ if cfg.IsStdOut || (!cfg.IsStdOut && !cfg.IsFileOut) {
+ writers = append(writers, os.Stdout)
+ }
+ if cfg.IsFileOut {
+ writers = append(writers, NewRollingFile(cfg.FileDir, cfg.FileName, cfg.FileMaxSize, cfg.FileMaxAge))
+ }
+
+ lg, lv := newZapLogger(setLogLevel(cfg.Level), setLogLevel(cfg.StacktraceLevel), cfg.Encoding, cfg.TimeFormat, cfg.Skip, zapcore.NewMultiWriteSyncer(writers...))
+ zap.RedirectStdLog(lg)
+ if cfg.AppName != "" {
+ lg = lg.With(zap.String("app", cfg.AppName)) // 加上应用名称
+ }
+ l = &LogX{logger: lg, atomLevel: lv}
+}
+
+// create a new logger
+func NewLogger(cfg *LogConfig) *LogX {
+ var writers []zapcore.WriteSyncer
+ if cfg.IsStdOut || (!cfg.IsStdOut && !cfg.IsFileOut) {
+ writers = append(writers, os.Stdout)
+ }
+ if cfg.IsFileOut {
+ writers = append(writers, NewRollingFile(cfg.FileDir, cfg.FileName, cfg.FileMaxSize, cfg.FileMaxAge))
+ }
+
+ lg, lv := newZapLogger(setLogLevel(cfg.Level), setLogLevel(cfg.StacktraceLevel), cfg.Encoding, cfg.TimeFormat, cfg.Skip, zapcore.NewMultiWriteSyncer(writers...))
+ zap.RedirectStdLog(lg)
+ if cfg.AppName != "" {
+ lg = lg.With(zap.String("app", cfg.AppName)) // 加上应用名称
+ }
+ return &LogX{logger: lg, atomLevel: lv}
+}
+
+// create a new zaplog logger
+func newZapLogger(level, stacktrace zapcore.Level, encoding, timeType string, skip int, output zapcore.WriteSyncer) (*zap.Logger, *zap.AtomicLevel) {
+ encCfg := zapcore.EncoderConfig{
+ TimeKey: "T",
+ LevelKey: "L",
+ NameKey: "N",
+ CallerKey: "C",
+ MessageKey: "M",
+ StacktraceKey: "S",
+ LineEnding: zapcore.DefaultLineEnding,
+ EncodeCaller: zapcore.ShortCallerEncoder,
+ EncodeDuration: zapcore.NanosDurationEncoder,
+ EncodeLevel: zapcore.LowercaseLevelEncoder,
+ }
+ setTimeFormat(timeType, &encCfg) // set time type
+ atmLvl := zap.NewAtomicLevel() // set level
+ atmLvl.SetLevel(level)
+ encoder := zapcore.NewJSONEncoder(encCfg) // 确定encoder格式
+ if encoding == "console" {
+ encoder = zapcore.NewConsoleEncoder(encCfg)
+ }
+ return zap.New(zapcore.NewCore(encoder, output, atmLvl), zap.AddCaller(), zap.AddStacktrace(stacktrace), zap.AddCallerSkip(skip)), &atmLvl
+}
+
+// set log level
+func setLogLevel(lvl string) zapcore.Level {
+ switch strings.ToLower(lvl) {
+ case "panic":
+ return zapcore.PanicLevel
+ case "fatal":
+ return zapcore.FatalLevel
+ case "error":
+ return zapcore.ErrorLevel
+ case "warn", "warning":
+ return zapcore.WarnLevel
+ case "info":
+ return zapcore.InfoLevel
+ default:
+ return zapcore.DebugLevel
+ }
+}
+
+// set time format
+func setTimeFormat(timeType string, z *zapcore.EncoderConfig) {
+ switch strings.ToLower(timeType) {
+ case "iso": // iso8601 standard
+ z.EncodeTime = zapcore.ISO8601TimeEncoder
+ case "sec": // only for unix second, without millisecond
+ z.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
+ enc.AppendInt64(t.Unix())
+ }
+ case "second": // unix second, with millisecond
+ z.EncodeTime = zapcore.EpochTimeEncoder
+ case "milli", "millisecond": // millisecond
+ z.EncodeTime = zapcore.EpochMillisTimeEncoder
+ case "nano", "nanosecond": // nanosecond
+ z.EncodeTime = zapcore.EpochNanosTimeEncoder
+ default: // standard format
+ z.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
+ enc.AppendString(t.Format("2006-01-02 15:04:05.000"))
+ }
+ }
+}
+
+func GetLevel() string {
+ switch l.atomLevel.Level() {
+ case zapcore.PanicLevel:
+ return "panic"
+ case zapcore.FatalLevel:
+ return "fatal"
+ case zapcore.ErrorLevel:
+ return "error"
+ case zapcore.WarnLevel:
+ return "warn"
+ case zapcore.InfoLevel:
+ return "info"
+ default:
+ return "debug"
+ }
+}
+
+func SetLevel(lvl string) {
+ l.atomLevel.SetLevel(setLogLevel(lvl))
+}
+
+// temporary add call skip
+func AddCallerSkip(skip int) *LogX {
+ l.logger.WithOptions(zap.AddCallerSkip(skip))
+ return l
+}
+
+// permanent add call skip
+func AddDepth(skip int) *LogX {
+ l.logger = l.logger.WithOptions(zap.AddCallerSkip(skip))
+ return l
+}
+
+// permanent add options
+func AddOptions(opts ...zap.Option) *LogX {
+ l.logger = l.logger.WithOptions(opts...)
+ return l
+}
+
+func AddField(k string, v interface{}) {
+ l.logger.With(zap.Any(k, v))
+}
+
+func AddFields(fields map[string]interface{}) *LogX {
+ for k, v := range fields {
+ l.logger.With(zap.Any(k, v))
+ }
+ return l
+}
+
+// Normal log
+func Debug(e interface{}, args ...interface{}) error {
+ return l.Debug(e, args...)
+}
+func Info(e interface{}, args ...interface{}) error {
+ return l.Info(e, args...)
+}
+func Warn(e interface{}, args ...interface{}) error {
+ return l.Warn(e, args...)
+}
+func Error(e interface{}, args ...interface{}) error {
+ return l.Error(e, args...)
+}
+func Panic(e interface{}, args ...interface{}) error {
+ return l.Panic(e, args...)
+}
+func Fatal(e interface{}, args ...interface{}) error {
+ return l.Fatal(e, args...)
+}
+
+// Format logs
+func Debugf(format string, args ...interface{}) error {
+ return l.Debugf(format, args...)
+}
+func Infof(format string, args ...interface{}) error {
+ return l.Infof(format, args...)
+}
+func Warnf(format string, args ...interface{}) error {
+ return l.Warnf(format, args...)
+}
+func Errorf(format string, args ...interface{}) error {
+ return l.Errorf(format, args...)
+}
+func Panicf(format string, args ...interface{}) error {
+ return l.Panicf(format, args...)
+}
+func Fatalf(format string, args ...interface{}) error {
+ return l.Fatalf(format, args...)
+}
+
+func formatFieldMap(m FieldMap) []Field {
+ var res []Field
+ for k, v := range m {
+ res = append(res, zap.Any(k, v))
+ }
+ return res
+}
diff --git a/utils/logx/output.go b/utils/logx/output.go
new file mode 100644
index 0000000..1f41c04
--- /dev/null
+++ b/utils/logx/output.go
@@ -0,0 +1,105 @@
+package zhios_order_relate_logx
+
+import (
+ "bytes"
+ "io"
+ "os"
+ "path/filepath"
+ "time"
+
+ "gopkg.in/natefinch/lumberjack.v2"
+)
+
+// output interface
+type WriteSyncer interface {
+ io.Writer
+ Sync() error
+}
+
+// split writer
+func NewRollingFile(dir, filename string, maxSize, MaxAge int) WriteSyncer {
+ s, err := os.Stat(dir)
+ if err != nil || !s.IsDir() {
+ os.RemoveAll(dir)
+ if err := os.MkdirAll(dir, 0766); err != nil {
+ panic(err)
+ }
+ }
+ return newLumberjackWriteSyncer(&lumberjack.Logger{
+ Filename: filepath.Join(dir, filename),
+ MaxSize: maxSize, // megabytes, MB
+ MaxAge: MaxAge, // days
+ LocalTime: true,
+ Compress: false,
+ })
+}
+
+type lumberjackWriteSyncer struct {
+ *lumberjack.Logger
+ buf *bytes.Buffer
+ logChan chan []byte
+ closeChan chan interface{}
+ maxSize int
+}
+
+func newLumberjackWriteSyncer(l *lumberjack.Logger) *lumberjackWriteSyncer {
+ ws := &lumberjackWriteSyncer{
+ Logger: l,
+ buf: bytes.NewBuffer([]byte{}),
+ logChan: make(chan []byte, 5000),
+ closeChan: make(chan interface{}),
+ maxSize: 1024,
+ }
+ go ws.run()
+ return ws
+}
+
+func (l *lumberjackWriteSyncer) run() {
+ ticker := time.NewTicker(1 * time.Second)
+
+ for {
+ select {
+ case <-ticker.C:
+ if l.buf.Len() > 0 {
+ l.sync()
+ }
+ case bs := <-l.logChan:
+ _, err := l.buf.Write(bs)
+ if err != nil {
+ continue
+ }
+ if l.buf.Len() > l.maxSize {
+ l.sync()
+ }
+ case <-l.closeChan:
+ l.sync()
+ return
+ }
+ }
+}
+
+func (l *lumberjackWriteSyncer) Stop() {
+ close(l.closeChan)
+}
+
+func (l *lumberjackWriteSyncer) Write(bs []byte) (int, error) {
+ b := make([]byte, len(bs))
+ for i, c := range bs {
+ b[i] = c
+ }
+ l.logChan <- b
+ return 0, nil
+}
+
+func (l *lumberjackWriteSyncer) Sync() error {
+ return nil
+}
+
+func (l *lumberjackWriteSyncer) sync() error {
+ defer l.buf.Reset()
+ _, err := l.Logger.Write(l.buf.Bytes())
+ if err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/utils/logx/sugar.go b/utils/logx/sugar.go
new file mode 100644
index 0000000..5beae07
--- /dev/null
+++ b/utils/logx/sugar.go
@@ -0,0 +1,192 @@
+package zhios_order_relate_logx
+
+import (
+ "errors"
+ "fmt"
+ "strconv"
+
+ "go.uber.org/zap"
+)
+
+type LogX struct {
+ logger *zap.Logger
+ atomLevel *zap.AtomicLevel
+}
+
+type Field = zap.Field
+type FieldMap map[string]interface{}
+
+// 判断其他类型--start
+func getFields(msg string, format bool, args ...interface{}) (string, []Field) {
+ var str []interface{}
+ var fields []zap.Field
+ if len(args) > 0 {
+ for _, v := range args {
+ if f, ok := v.(Field); ok {
+ fields = append(fields, f)
+ } else if f, ok := v.(FieldMap); ok {
+ fields = append(fields, formatFieldMap(f)...)
+ } else {
+ str = append(str, AnyToString(v))
+ }
+ }
+ if format {
+ return fmt.Sprintf(msg, str...), fields
+ }
+ str = append([]interface{}{msg}, str...)
+ return fmt.Sprintln(str...), fields
+ }
+ return msg, []Field{}
+}
+
+func (l *LogX) Debug(s interface{}, args ...interface{}) error {
+ es, e := checkErr(s)
+ if es != "" {
+ msg, field := getFields(es, false, args...)
+ l.logger.Debug(msg, field...)
+ }
+ return e
+}
+func (l *LogX) Info(s interface{}, args ...interface{}) error {
+ es, e := checkErr(s)
+ if es != "" {
+ msg, field := getFields(es, false, args...)
+ l.logger.Info(msg, field...)
+ }
+ return e
+}
+func (l *LogX) Warn(s interface{}, args ...interface{}) error {
+ es, e := checkErr(s)
+ if es != "" {
+ msg, field := getFields(es, false, args...)
+ l.logger.Warn(msg, field...)
+ }
+ return e
+}
+func (l *LogX) Error(s interface{}, args ...interface{}) error {
+ es, e := checkErr(s)
+ if es != "" {
+ msg, field := getFields(es, false, args...)
+ l.logger.Error(msg, field...)
+ }
+ return e
+}
+func (l *LogX) DPanic(s interface{}, args ...interface{}) error {
+ es, e := checkErr(s)
+ if es != "" {
+ msg, field := getFields(es, false, args...)
+ l.logger.DPanic(msg, field...)
+ }
+ return e
+}
+func (l *LogX) Panic(s interface{}, args ...interface{}) error {
+ es, e := checkErr(s)
+ if es != "" {
+ msg, field := getFields(es, false, args...)
+ l.logger.Panic(msg, field...)
+ }
+ return e
+}
+func (l *LogX) Fatal(s interface{}, args ...interface{}) error {
+ es, e := checkErr(s)
+ if es != "" {
+ msg, field := getFields(es, false, args...)
+ l.logger.Fatal(msg, field...)
+ }
+ return e
+}
+
+func checkErr(s interface{}) (string, error) {
+ switch e := s.(type) {
+ case error:
+ return e.Error(), e
+ case string:
+ return e, errors.New(e)
+ case []byte:
+ return string(e), nil
+ default:
+ return "", nil
+ }
+}
+
+func (l *LogX) LogError(err error) error {
+ return l.Error(err.Error())
+}
+
+func (l *LogX) Debugf(msg string, args ...interface{}) error {
+ s, f := getFields(msg, true, args...)
+ l.logger.Debug(s, f...)
+ return errors.New(s)
+}
+
+func (l *LogX) Infof(msg string, args ...interface{}) error {
+ s, f := getFields(msg, true, args...)
+ l.logger.Info(s, f...)
+ return errors.New(s)
+}
+
+func (l *LogX) Warnf(msg string, args ...interface{}) error {
+ s, f := getFields(msg, true, args...)
+ l.logger.Warn(s, f...)
+ return errors.New(s)
+}
+
+func (l *LogX) Errorf(msg string, args ...interface{}) error {
+ s, f := getFields(msg, true, args...)
+ l.logger.Error(s, f...)
+ return errors.New(s)
+}
+
+func (l *LogX) DPanicf(msg string, args ...interface{}) error {
+ s, f := getFields(msg, true, args...)
+ l.logger.DPanic(s, f...)
+ return errors.New(s)
+}
+
+func (l *LogX) Panicf(msg string, args ...interface{}) error {
+ s, f := getFields(msg, true, args...)
+ l.logger.Panic(s, f...)
+ return errors.New(s)
+}
+
+func (l *LogX) Fatalf(msg string, args ...interface{}) error {
+ s, f := getFields(msg, true, args...)
+ l.logger.Fatal(s, f...)
+ return errors.New(s)
+}
+
+func AnyToString(raw interface{}) string {
+ switch i := raw.(type) {
+ case []byte:
+ return string(i)
+ case int:
+ return strconv.FormatInt(int64(i), 10)
+ case int64:
+ return strconv.FormatInt(i, 10)
+ case float32:
+ return strconv.FormatFloat(float64(i), 'f', 2, 64)
+ case float64:
+ return strconv.FormatFloat(i, 'f', 2, 64)
+ case uint:
+ return strconv.FormatInt(int64(i), 10)
+ case uint8:
+ return strconv.FormatInt(int64(i), 10)
+ case uint16:
+ return strconv.FormatInt(int64(i), 10)
+ case uint32:
+ return strconv.FormatInt(int64(i), 10)
+ case uint64:
+ return strconv.FormatInt(int64(i), 10)
+ case int8:
+ return strconv.FormatInt(int64(i), 10)
+ case int16:
+ return strconv.FormatInt(int64(i), 10)
+ case int32:
+ return strconv.FormatInt(int64(i), 10)
+ case string:
+ return i
+ case error:
+ return i.Error()
+ }
+ return fmt.Sprintf("%#v", raw)
+}
diff --git a/utils/serialize.go b/utils/serialize.go
new file mode 100644
index 0000000..dd0150b
--- /dev/null
+++ b/utils/serialize.go
@@ -0,0 +1,23 @@
+package zhios_order_relate_utils
+
+import (
+ "encoding/json"
+)
+
+func Serialize(data interface{}) []byte {
+ res, err := json.Marshal(data)
+ if err != nil {
+ return []byte{}
+ }
+ return res
+}
+
+func Unserialize(b []byte, dst interface{}) {
+ if err := json.Unmarshal(b, dst); err != nil {
+ dst = nil
+ }
+}
+
+func SerializeStr(data interface{}, arg ...interface{}) string {
+ return string(Serialize(data))
+}
diff --git a/utils/string.go b/utils/string.go
new file mode 100644
index 0000000..e68c51c
--- /dev/null
+++ b/utils/string.go
@@ -0,0 +1,203 @@
+package zhios_order_relate_utils
+
+import (
+ "fmt"
+ "github.com/syyongx/php2go"
+ "math/rand"
+ "reflect"
+ "sort"
+ "strings"
+ "time"
+)
+
+func Implode(glue string, args ...interface{}) string {
+ data := make([]string, len(args))
+ for i, s := range args {
+ data[i] = fmt.Sprint(s)
+ }
+ return strings.Join(data, glue)
+}
+
+//字符串是否在数组里
+func InArr(target string, str_array []string) bool {
+ for _, element := range str_array {
+ if target == element {
+ return true
+ }
+ }
+ return false
+}
+func InArrToInt(target int, str_array []int) bool {
+ for _, element := range str_array {
+ if target == element {
+ return true
+ }
+ }
+ return false
+}
+
+//把数组的值放到key里
+func ArrayColumn(array interface{}, key string) (result map[string]interface{}, err error) {
+ result = make(map[string]interface{})
+ t := reflect.TypeOf(array)
+ v := reflect.ValueOf(array)
+ if t.Kind() != reflect.Slice {
+ return nil, nil
+ }
+ if v.Len() == 0 {
+ return nil, nil
+ }
+ for i := 0; i < v.Len(); i++ {
+ indexv := v.Index(i)
+ if indexv.Type().Kind() != reflect.Struct {
+ return nil, nil
+ }
+ mapKeyInterface := indexv.FieldByName(key)
+ if mapKeyInterface.Kind() == reflect.Invalid {
+ return nil, nil
+ }
+ mapKeyString, err := InterfaceToString(mapKeyInterface.Interface())
+ if err != nil {
+ return nil, err
+ }
+ result[mapKeyString] = indexv.Interface()
+ }
+ return result, err
+}
+
+//转string
+func InterfaceToString(v interface{}) (result string, err error) {
+ switch reflect.TypeOf(v).Kind() {
+ case reflect.Int64, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32:
+ result = fmt.Sprintf("%v", v)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ result = fmt.Sprintf("%v", v)
+ case reflect.String:
+ result = v.(string)
+ default:
+ err = nil
+ }
+ return result, err
+}
+
+func HideTrueName(name string) string {
+ res := "**"
+ if name != "" {
+ runs := []rune(name)
+ leng := len(runs)
+ if leng <= 3 {
+ res = string(runs[0:1]) + res
+ } else if leng < 5 {
+ res = string(runs[0:2]) + res
+ } else if leng < 10 {
+ res = string(runs[0:2]) + "***" + string(runs[leng-2:leng])
+ } else if leng < 16 {
+ res = string(runs[0:3]) + "****" + string(runs[leng-3:leng])
+ } else {
+ res = string(runs[0:4]) + "*****" + string(runs[leng-4:leng])
+ }
+ }
+ return res
+}
+
+func GetQueryParam(uri string) map[string]string {
+ //根据问号分割路由还是query参数
+ uriList := strings.Split(uri, "?")
+ var query = make(map[string]string, 0)
+ //有参数才处理
+ if len(uriList) == 2 {
+ //分割query参数
+ var queryList = strings.Split(uriList[1], "&")
+ if len(queryList) > 0 {
+ //key value 分别赋值
+ for _, v := range queryList {
+ var valueList = strings.Split(v, "=")
+ if len(valueList) == 2 {
+ value, _ := php2go.URLDecode(valueList[1])
+ if value == "" {
+ value = valueList[1]
+ }
+ query[valueList[0]] = value
+ }
+ }
+ }
+ }
+ return query
+}
+
+//JoinStringsInASCII 按照规则,参数名ASCII码从小到大排序后拼接
+//data 待拼接的数据
+//sep 连接符
+//onlyValues 是否只包含参数值,true则不包含参数名,否则参数名和参数值均有
+//includeEmpty 是否包含空值,true则包含空值,否则不包含,注意此参数不影响参数名的存在
+//exceptKeys 被排除的参数名,不参与排序及拼接
+func JoinStringsInASCII(data map[string]string, sep string, onlyValues, includeEmpty bool, exceptKeys ...string) string {
+ var list []string
+ var keyList []string
+ m := make(map[string]int)
+ if len(exceptKeys) > 0 {
+ for _, except := range exceptKeys {
+ m[except] = 1
+ }
+ }
+ for k := range data {
+ if _, ok := m[k]; ok {
+ continue
+ }
+ value := data[k]
+ if !includeEmpty && value == "" {
+ continue
+ }
+ if onlyValues {
+ keyList = append(keyList, k)
+ } else {
+ list = append(list, fmt.Sprintf("%s=%s", k, value))
+ }
+ }
+ if onlyValues {
+ sort.Strings(keyList)
+ for _, v := range keyList {
+ list = append(list, AnyToString(data[v]))
+ }
+ } else {
+ sort.Strings(list)
+ }
+ return strings.Join(list, sep)
+}
+
+//x的y次方
+func RandPow(l int) string {
+ var i = "1"
+ for j := 0; j < l; j++ {
+ i += "0"
+ }
+ k := StrToInt64(i)
+ n := rand.New(rand.NewSource(time.Now().UnixNano())).Int63n(k)
+ ls := "%0" + IntToStr(l) + "v"
+ str := fmt.Sprintf(ls, n)
+ //min := int(math.Pow10(l - 1))
+ //max := int(math.Pow10(l) - 1)
+ return str
+}
+
+//根据显示长度截取字符串
+func ShowSubstr(s string, l int) string {
+ if len(s) <= l {
+ return s
+ }
+ ss, sl, rl, rs := "", 0, 0, []rune(s)
+ for _, r := range rs {
+ rint := int(r)
+ if rint < 128 {
+ rl = 1
+ } else {
+ rl = 2
+ }
+ if sl+rl > l {
+ break
+ }
+ sl += rl
+ ss += string(r)
+ }
+ return ss
+}
diff --git a/utils/time2s.go b/utils/time2s.go
new file mode 100644
index 0000000..6d3b1a9
--- /dev/null
+++ b/utils/time2s.go
@@ -0,0 +1,29 @@
+package zhios_order_relate_utils
+
+import "time"
+
+func Time2String(date time.Time, format string) string {
+ if format == "" {
+ format = "2006-01-02 15:04:05"
+ }
+ timeS := date.Format(format)
+ if timeS == "0001-01-01 00:00:00" {
+ return ""
+ }
+ return timeS
+}
+func String2Time(timeStr string) time.Time {
+ toTime, err := time.ParseInLocation("2006-01-02 15:04:05", timeStr, time.Local)
+ if err != nil {
+ return time.Now()
+ }
+ return toTime
+}
+
+//GetDiffDays 获取两个时间相差的天数,0表同一天,正数表t1>t2,负数表t1