DengBiao преди 1 година
родител
ревизия
cf3771e3e8
променени са 22 файла, в които са добавени 3889 реда и са изтрити 334 реда
  1. +3
    -3
      db/db_sys_cfg.go
  2. +8
    -6
      go.mod
  3. +0
    -256
      lib/local_alipay/api.go
  4. +2
    -2
      pay/init.go
  5. +0
    -1
      pay/logs/zyos_website.log
  6. +0
    -43
      pay/pay_by_own.go
  7. +14
    -23
      pay/pay_config.go
  8. +421
    -0
      utils/cache/base.go
  9. +413
    -0
      utils/cache/redis.go
  10. +622
    -0
      utils/cache/redis_cluster.go
  11. +324
    -0
      utils/cache/redis_pool.go
  12. +617
    -0
      utils/cache/redis_pool_cluster.go
  13. +378
    -0
      utils/convert.go
  14. +209
    -0
      utils/curl.go
  15. +22
    -0
      utils/file.go
  16. +59
    -0
      utils/format.go
  17. +245
    -0
      utils/logx/log.go
  18. +105
    -0
      utils/logx/output.go
  19. +192
    -0
      utils/logx/sugar.go
  20. +23
    -0
      utils/serialize.go
  21. +203
    -0
      utils/string.go
  22. +29
    -0
      utils/time2s.go

+ 3
- 3
db/db_sys_cfg.go Целия файл

@@ -1,9 +1,9 @@
package db

import (
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/db/model"
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/md"
cache "code.fnuoos.com/go_rely_warehouse/zyos_go_tools.git/utils/cache"
"code.fnuoos.com/go_rely_warehouse/zyos_go_es.git/db/model"
"code.fnuoos.com/go_rely_warehouse/zyos_go_es.git/md"
"code.fnuoos.com/go_rely_warehouse/zyos_go_es.git/utils/cache"
"fmt"
"xorm.io/xorm"
)


+ 8
- 6
go.mod Целия файл

@@ -1,15 +1,17 @@
module code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git
module code.fnuoos.com/go_rely_warehouse/zyos_go_es.git

go 1.16
go 1.18

require (
code.fnuoos.com/go_rely_warehouse/zyos_go_tools.git v1.0.3 // indirect
github.com/go-pay/gopay v1.5.78
github.com/gin-gonic/gin v1.8.0
github.com/go-redis/redis v6.15.9+incompatible
github.com/go-sql-driver/mysql v1.6.0
github.com/gomodule/redigo/redis v0.0.1
github.com/gomodule/redigo v1.8.8
github.com/iGoogle-ink/gopay v1.5.36
go.uber.org/zap v1.13.0
github.com/iGoogle-ink/gotil v1.0.20
github.com/pkg/errors v0.9.1
github.com/syyongx/php2go v0.9.6
go.uber.org/zap v1.16.0
gopkg.in/natefinch/lumberjack.v2 v2.0.0
xorm.io/builder v0.3.10 // indirect
xorm.io/xorm v1.3.0


+ 0
- 256
lib/local_alipay/api.go Целия файл

@@ -1,256 +0,0 @@
package local_alipay

import (
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/md"
"code.fnuoos.com/go_rely_warehouse/zyos_go_tools.git/utils/logx"
"fmt"
"github.com/iGoogle-ink/gopay"
"github.com/iGoogle-ink/gopay/alipay"
)

// TradeAppPay is 支付宝APP支付
// 抖音头条小程序使用APP调起
func TradeAppPay(appID, priKey, subject, orderID, amount, notiURL, RSA, PKCS string, paySet *md.PayData) (string, error) {
//初始化支付宝客户端
// appID 是在支付宝申请的APPID
// priKey 是支付宝私钥
// subject 是支付订单的主题
// orderID 是智莺这边生成的订单id
// amount 是付费金额
// notiURL 通知地址url
// passback_params 回调通知参数

client := alipay.NewClient(appID, priKey, true)
client.DebugSwitch = gopay.DebugOn
//判断密钥的类型
rsa_type := alipay.RSA2
pkcs_type := alipay.PKCS1
if RSA == "1" {
rsa_type = alipay.RSA
}
if PKCS == "1" {
pkcs_type = alipay.PKCS8
}
if paySet.PayAliUseType == "1" {
rsa_type = alipay.RSA2
pkcs_type = alipay.PKCS8
}
//配置公共参数
client.SetCharset("utf-8").
SetSignType(rsa_type).
SetPrivateKeyType(pkcs_type)
if notiURL != "" {
client.SetNotifyUrl(notiURL)
}
//新支付宝支付
if paySet.PayAliUseType == "1" {
appCertSN, err := alipay.GetCertSN(paySet.WxAppletFilepathUrl + "/" + paySet.PayAppCertSn)
fmt.Println("-应用-")
fmt.Println(appCertSN)
if err != nil {
fmt.Println(err)
return "", err
}
if appCertSN == "" {
fmt.Println(err)
return "", err
}
client.SetAppCertSN(appCertSN)
//aliPayRootCertSN, err := local_alipay.GetCertSN(cfg.WxappletFilepath.URL + "/" + paySet.PayAlipayRootCertSn)
aliPayRootCertSN := "687b59193f3f462dd5336e5abf83c5d8_02941eef3187dddf3d3b83462e1dfcf6"
client.SetAliPayRootCertSN(aliPayRootCertSN)
aliPayPublicCertSN, err := alipay.GetCertSN(paySet.WxAppletFilepathUrl + "/" + paySet.PayAlipayrsaPublicKey)
fmt.Println("-公钥-")
fmt.Println(aliPayPublicCertSN)

if err != nil {
fmt.Println(err)
return "", err
}
if aliPayPublicCertSN == "" {
fmt.Println(err)
return "", err
}
client.SetAliPayPublicCertSN(aliPayPublicCertSN)
}
fmt.Println(client)
//请求参数
body := make(gopay.BodyMap)
body.Set("subject", subject)
body.Set("body", subject)
body.Set("out_trade_no", orderID)
body.Set("total_amount", amount)
body.Set("timeout_express", "30m")

// body.Set("passback_params", orderID)
//手机APP支付参数请求
payParam, err := client.TradeAppPay(body)
if err != nil {
return "", logx.Warn(err)
}
return payParam, nil
}

// TradeAppPay is 支付宝H5支付
func TradeWapPay(appID, priKey, subject, orderID, amount, notiURL, RSA, PKCS, page_url string, paySet *md.PayData) (string, error) {
//aliPayPublicKey := "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1wn1sU/8Q0rYLlZ6sq3enrPZw2ptp6FecHR2bBFLjJ+sKzepROd0bKddgj+Mr1ffr3Ej78mLdWV8IzLfpXUi945DkrQcOUWLY0MHhYVG2jSs/qzFfpzmtut2Cl2TozYpE84zom9ei06u2AXLMBkU6VpznZl+R4qIgnUfByt3Ix5b3h4Cl6gzXMAB1hJrrrCkq+WvWb3Fy0vmk/DUbJEz8i8mQPff2gsHBE1nMPvHVAMw1GMk9ImB4PxucVek4ZbUzVqxZXphaAgUXFK2FSFU+Q+q1SPvHbUsjtIyL+cLA6H/6ybFF9Ffp27Y14AHPw29+243/SpMisbGcj2KD+evBwIDAQAB"
privateKey := priKey
//判断密钥的类型
rsa_type := alipay.RSA2
pkcs_type := alipay.PKCS1
if RSA == "1" {
rsa_type = alipay.RSA
}
if PKCS == "1" {
pkcs_type = alipay.PKCS8
}
if paySet.PayAliUseType == "1" {
rsa_type = alipay.RSA2
pkcs_type = alipay.PKCS8
}
//初始化支付宝客户端
// appId:应用ID
// privateKey:应用秘钥
// isProd:是否是正式环境
client := alipay.NewClient(appID, privateKey, true)
//配置公共参数
client.SetCharset("utf-8").
SetSignType(rsa_type).
SetPrivateKeyType(pkcs_type).
SetReturnUrl(page_url).
SetNotifyUrl(notiURL)
//新支付宝支付
if paySet.PayAliUseType == "1" {
appCertSN, err := alipay.GetCertSN(paySet.WxAppletFilepathUrl + "/" + paySet.PayAppCertSn)
if err != nil {
fmt.Println(err)
return "", err
}
if appCertSN == "" {
fmt.Println(err)
return "", err
}
client.SetAppCertSN(appCertSN)
//aliPayRootCertSN, err := local_alipay.GetCertSN(paySet.WxAppletFilepathUrl + "/" + paySet.PayAlipayRootCertSn)
//if err != nil {
// fmt.Println(err)
// return "", err
//}
//if aliPayRootCertSN == "" {
// fmt.Println(err)
// return "", err
//}
aliPayRootCertSN := "687b59193f3f462dd5336e5abf83c5d8_02941eef3187dddf3d3b83462e1dfcf6"
client.SetAliPayRootCertSN(aliPayRootCertSN)
aliPayPublicCertSN, err := alipay.GetCertSN(paySet.WxAppletFilepathUrl + "/" + paySet.PayAlipayrsaPublicKey)
if err != nil {
fmt.Println(err)
return "", err
}
if aliPayPublicCertSN == "" {
fmt.Println(err)
return "", err
}
client.SetAliPayPublicCertSN(aliPayPublicCertSN)
}
//请求参数
body := make(gopay.BodyMap)
body.Set("subject", subject)
body.Set("out_trade_no", orderID)
// quit_url is 用户付款中途退出返回商户网站的地址
body.Set("quit_url", notiURL)
body.Set("total_amount", amount)
// product_code is 销售产品码,商家和支付宝签约的产品码
body.Set("product_code", "QUICK_WAP_WAY")
//手机网站支付请求
payUrl, err := client.TradeWapPay(body)
if err != nil {
return "", logx.Warn(err)

}
return payUrl, nil
}

// TradeAppPay is 支付宝小程序本身支付
func TradeCreate(appID, priKey, subject, orderID, amount, notiURL, RSA, PKCS string, paySet *md.PayData) (*alipay.TradeCreateResponse, error) {
//aliPayPublicKey := "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1wn1sU/8Q0rYLlZ6sq3enrPZw2ptp6FecHR2bBFLjJ+sKzepROd0bKddgj+Mr1ffr3Ej78mLdWV8IzLfpXUi945DkrQcOUWLY0MHhYVG2jSs/qzFfpzmtut2Cl2TozYpE84zom9ei06u2AXLMBkU6VpznZl+R4qIgnUfByt3Ix5b3h4Cl6gzXMAB1hJrrrCkq+WvWb3Fy0vmk/DUbJEz8i8mQPff2gsHBE1nMPvHVAMw1GMk9ImB4PxucVek4ZbUzVqxZXphaAgUXFK2FSFU+Q+q1SPvHbUsjtIyL+cLA6H/6ybFF9Ffp27Y14AHPw29+243/SpMisbGcj2KD+evBwIDAQAB"
privateKey := priKey
rsa_type := alipay.RSA2
pkcs_type := alipay.PKCS1
if RSA == "1" {
rsa_type = alipay.RSA
}
if PKCS == "1" {
pkcs_type = alipay.PKCS8
}
if paySet.PayAliUseType == "1" {
rsa_type = alipay.RSA2
pkcs_type = alipay.PKCS8
}
//初始化支付宝客户端
// appId:应用ID
// privateKey:应用私钥,支持PKCS1和PKCS8
// isProd:是否是正式环境
client := alipay.NewClient(appID, privateKey, true)
//配置公共参数
client.SetCharset("utf-8").
SetSignType(rsa_type).
SetPrivateKeyType(pkcs_type).
SetNotifyUrl(notiURL)
if paySet.PayAliUseType == "1" {
appCertSN, err := alipay.GetCertSN(paySet.WxAppletFilepathUrl + "/" + paySet.PayAppCertSn)
if err != nil {
fmt.Println(err)
return nil, err
}
if appCertSN == "" {
fmt.Println(err)
return nil, err
}
client.SetAppCertSN(appCertSN)
//aliPayRootCertSN, err := local_alipay.GetCertSN(paySet.WxAppletFilepathUrl + "/" + paySet.PayAlipayRootCertSn)
//if err != nil {
// fmt.Println(err)
// return nil, err
//}
//if aliPayRootCertSN == "" {
// fmt.Println(err)
// return nil, err
//}
aliPayRootCertSN := "687b59193f3f462dd5336e5abf83c5d8_02941eef3187dddf3d3b83462e1dfcf6"
client.SetAliPayRootCertSN(aliPayRootCertSN)
aliPayPublicCertSN, err := alipay.GetCertSN(paySet.WxAppletFilepathUrl + "/" + paySet.PayAlipayrsaPublicKey)
if err != nil {
fmt.Println(err)
return nil, err
}
if aliPayPublicCertSN == "" {
fmt.Println(err)
return nil, err
}
client.SetAliPayPublicCertSN(aliPayPublicCertSN)
}
//请求参数
body := make(gopay.BodyMap)
body.Set("subject", subject)
// 支付宝小程序支付时 buyer_id 为必传参数,需要提前获取,获取方法如下两种
// 1、local_alipay.SystemOauthToken() 返回取值:rsp.SystemOauthTokenResponse.UserId
// 2、client.SystemOauthToken() 返回取值:aliRsp.SystemOauthTokenResponse.UserId
buyer_id, err := client.SystemOauthToken(body)
if err != nil {
return nil, logx.Warn(err)
}
body.Set("buyer_id", buyer_id)
body.Set("out_trade_no", orderID)
body.Set("total_amount", amount)
//创建订单
aliRsp, err := client.TradeCreate(body)

if err != nil {
return nil, logx.Warn(err)
}
logx.Warn("aliRsp:", *aliRsp)
logx.Warn("aliRsp.TradeNo:", aliRsp.Response.TradeNo)
return aliRsp, nil

}

+ 2
- 2
pay/init.go Целия файл

@@ -1,8 +1,8 @@
package pay

import (
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/db"
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/md"
"code.fnuoos.com/go_rely_warehouse/zyos_go_es.git/db"
"code.fnuoos.com/go_rely_warehouse/zyos_go_es.git/md"
)

var cfg = md.DBCfg{


+ 0
- 1
pay/logs/zyos_website.log Целия файл

@@ -1 +0,0 @@
[xorm] [info] 2022/04/27 15:58:13.777967 [SQL] SELECT `id`, `channel_id`, `identifier`, `master_id`, `master_nickname`, `app_id` FROM `pay_channel_master` WHERE (app_id = ?) AND (identifier = ?) LIMIT 1 [35618318 mall] - 27.4838ms

+ 0
- 43
pay/pay_by_own.go Целия файл

@@ -1,43 +0,0 @@
package pay

import (
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/lib/local_alipay"
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/md"
"code.fnuoos.com/go_rely_warehouse/zyos_go_tools.git/e"
"code.fnuoos.com/go_rely_warehouse/zyos_go_tools.git/utils"
"fmt"
)

//自有支付 支付宝
func Alipay(args map[string]string) (string, error) {
var paySet = &md.PayData{
PayAppCertSn: args["pay_app_cert_sn"],
PayAlipayRootCertSn: args["pay_alipay_root_cert_sn"],
PayAlipayrsaPublicKey: args["pay_alipayrsa_public_key"],
PayAliUseType: args["pay_ali_use_type"],
WxAppletFilepathUrl: args["wx_applet_filepath_url"],
}
if args["private_key"] == "" || args["app_id"] == "" {
return "", e.NewErr(400, "请在后台正确配置支付宝")
}
var param interface{}
var err error
switch args["platform"] {
case md.PLATFORM_ALIPAY_APPLET:
param, err = local_alipay.TradeCreate(args["app_id"], args["private_key"], args["subject"], args["ord_id"], args["amount"], args["notify_url"], args["rsa"], args["pkcs"], paySet)
case md.PLATFORM_WAP:
param, err = local_alipay.TradeWapPay(args["app_id"], args["private_key"], args["subject"], args["ord_id"], args["amount"], args["notify_url"], args["rsa"], args["pkcs"], args["page_url"], paySet)
case md.PLATFORM_ANDROID, md.PLATFORM_IOS:
param, err = local_alipay.TradeAppPay(args["app_id"], args["private_key"], args["subject"], args["ord_id"], args["amount"], args["notify_url"], args["rsa"], args["pkcs"], paySet)
default:
return "", e.NewErr(400, "支付平台不存在")
}
if err != nil {
fmt.Println("支付宝错误日志")
fmt.Println(param)
fmt.Println(err)
return "", e.NewErr(400, "支付宝订单创建失败")
}
return utils.AnyToString(param), nil

}

+ 14
- 23
pay/pay_config.go Целия файл

@@ -1,11 +1,9 @@
package pay

import (
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/db"
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/db/model"
"code.fnuoos.com/go_rely_warehouse/zyos_go_tools.git/e"
"code.fnuoos.com/go_rely_warehouse/zyos_go_tools.git/utils/cache"
"xorm.io/xorm"
"code.fnuoos.com/go_rely_warehouse/zyos_go_es.git/db"
"code.fnuoos.com/go_rely_warehouse/zyos_go_es.git/db/model"
"errors"
)

//支付渠道获取
@@ -16,30 +14,23 @@ func JudgePayChannel(mid, paySceneIdentifier string) (model.PayChannelMaster, er
return payChannelMaster, err
}
if !res {
return payChannelMaster, e.NewErr(400, "查询数据失败")
return payChannelMaster, errors.New("查询数据失败")
}
if payChannelMaster.ChannelId == 0 {
return payChannelMaster, e.NewErr(400, "请联系服务商绑定支付渠道")
return payChannelMaster, errors.New("请联系服务商绑定支付渠道")
}
return payChannelMaster, nil
}

//客户端支付宝配置
func OwnCustomAlipayConfig(eg *xorm.Engine, args map[string]string) map[string]string {
cache.NewRedis(args["redis_addr"])
var paySet = map[string]string{
"pay_app_cert_sn": db.SysCfgGetWithDb(eg, args["master_id"], "pay_app_cert_sn"),
"pay_alipay_root_cert_sn": db.SysCfgGetWithDb(eg, args["master_id"], "pay_alipay_root_cert_sn"),
"pay_alipayrsa_public_key": db.SysCfgGetWithDb(eg, args["master_id"], "pay_alipayrsa_public_key"),
"pay_ali_use_type": db.SysCfgGetWithDb(eg, args["master_id"], "pay_ali_use_type"),
"private_key": db.SysCfgGetWithDb(eg, args["master_id"], "pay_ali_private_key"),
"app_id": db.SysCfgGetWithDb(eg, args["master_id"], "pay_ali_app_id"),
"rsa": db.SysCfgGetWithDb(eg, args["master_id"], "pay_ali_key_len_type"),
"pkcs": db.SysCfgGetWithDb(eg, args["master_id"], "pay_ali_key_format_type"),
//支付方式获取
func JudgePayChannelPayType(channelId string) (model.PayChannel, error) {
var payChannel = model.PayChannel{}
res, err := db.Db.Where("id = ? and is_use=?", channelId, 1).Get(&payChannel)
if err != nil {
return payChannel, err
}
if paySet["pay_ali_use_type"] == "1" {
paySet["private_key"] = db.SysCfgGetWithDb(eg, args["master_id"], "pay_ali_new_private_key")
paySet["app_id"] = db.SysCfgGetWithDb(eg, args["master_id"], "pay_ali_new_app_id")
if !res {
return payChannel, errors.New("查询数据失败")
}
return paySet
return payChannel, nil
}

+ 421
- 0
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)
}

+ 413
- 0
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)
}

+ 622
- 0
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
}

+ 324
- 0
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
}

+ 617
- 0
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
}

+ 378
- 0
utils/convert.go Целия файл

@@ -0,0 +1,378 @@
package zhios_order_relate_utils

import (
"encoding/binary"
"encoding/json"
"fmt"
"math"
"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 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
}

+ 209
- 0
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)
}

+ 22
- 0
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()
}

+ 59
- 0
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
}

+ 245
- 0
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
}

+ 105
- 0
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
}

+ 192
- 0
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)
}

+ 23
- 0
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))
}

+ 203
- 0
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
}

+ 29
- 0
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<t2
func GetDiffDays(t1, t2 time.Time) int {
t1 = time.Date(t1.Year(), t1.Month(), t1.Day(), 0, 0, 0, 0, time.Local)
t2 = time.Date(t2.Year(), t2.Month(), t2.Day(), 0, 0, 0, 0, time.Local)

return int(t1.Sub(t2).Hours() / 24)
}

Зареждане…
Отказ
Запис