@@ -0,0 +1,44 @@ | |||||
# Binaries for programs and plugins | |||||
*.exe | |||||
*.exe~ | |||||
*.dll | |||||
*.so | |||||
*.dylib | |||||
# Test binary, built with `go test -c` | |||||
*.test | |||||
# Output of the go coverage tool, specifically when used with LiteIDE | |||||
*.out | |||||
.idea | |||||
.vscode | |||||
.DS_Store | |||||
Thumbs.db | |||||
*.swp | |||||
*.swn | |||||
*.swo | |||||
*.swm | |||||
*.7z | |||||
*.zip | |||||
*.rar | |||||
*.tar | |||||
*.tar.gz | |||||
go.sum | |||||
/etc/cfg.yaml | |||||
images | |||||
test/test.json | |||||
etc/cfg.yml | |||||
t.json | |||||
t1.json | |||||
t2.json | |||||
t3.json | |||||
t.go | |||||
wait-for-it.sh | |||||
test.go | |||||
xorm | |||||
test.csv | |||||
nginx.conf | |||||
.devcontainer | |||||
.devcontainer/Dockerfile | |||||
.devcontainer/sources.list | |||||
/t1.go | |||||
.idea/* | |||||
/.idea/modules.xml |
@@ -0,0 +1,84 @@ | |||||
package db | |||||
import ( | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/md" | |||||
"database/sql" | |||||
"fmt" | |||||
_ "github.com/go-sql-driver/mysql" //必须导入mysql驱动,否则会panic | |||||
"os" | |||||
"time" | |||||
"xorm.io/xorm" | |||||
"xorm.io/xorm/log" | |||||
) | |||||
var Db *xorm.Engine | |||||
//根据DB配置文件初始化数据库 | |||||
func InitDB(c *md.DBCfg) error { | |||||
var ( | |||||
err error | |||||
f *os.File | |||||
) | |||||
//创建Orm引擎 | |||||
if Db, err = xorm.NewEngine("mysql", fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4", c.User, c.Psw, c.Host, c.Name)); err != nil { | |||||
return err | |||||
} | |||||
Db.SetConnMaxLifetime(c.MaxLifetime * time.Second) //设置最长连接时间 | |||||
Db.SetMaxOpenConns(c.MaxOpenConns) //设置最大打开连接数 | |||||
Db.SetMaxIdleConns(c.MaxIdleConns) //设置连接池的空闲数大小 | |||||
if err = Db.Ping(); err != nil { //尝试ping数据库 | |||||
return err | |||||
} | |||||
if c.ShowLog { //根据配置文件设置日志 | |||||
Db.ShowSQL(true) //设置是否打印sql | |||||
Db.Logger().SetLevel(0) //设置日志等级 | |||||
//修改日志文件存放路径文件名是%s.logs | |||||
path := fmt.Sprintf(c.Path, c.Name) | |||||
f, err = os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0777) | |||||
if err != nil { | |||||
os.RemoveAll(c.Path) | |||||
if f, err = os.OpenFile(c.Path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0777); err != nil { | |||||
return err | |||||
} | |||||
} | |||||
logger := log.NewSimpleLogger(f) | |||||
logger.ShowSQL(true) | |||||
Db.SetLogger(logger) | |||||
} | |||||
return nil | |||||
} | |||||
/********************************************* 公用方法 *********************************************/ | |||||
// QueryNativeString 查询原生sql | |||||
func QueryNativeString(Db *xorm.Engine, sql string, args ...interface{}) ([]map[string]string, error) { | |||||
results, err := Db.SQL(sql, args...).QueryString() | |||||
return results, err | |||||
} | |||||
// UpdateComm common update | |||||
func UpdateComm(Db *xorm.Engine, id interface{}, model interface{}) (int64, error) { | |||||
row, err := Db.ID(id).Update(model) | |||||
return row, err | |||||
} | |||||
// InsertComm common insert | |||||
func InsertComm(Db *xorm.Engine, model interface{}) (int64, error) { | |||||
row, err := Db.InsertOne(model) | |||||
return row, err | |||||
} | |||||
// InsertCommWithSession common insert | |||||
func InsertCommWithSession(session *xorm.Session, model interface{}) (int64, error) { | |||||
row, err := session.InsertOne(model) | |||||
return row, err | |||||
} | |||||
// ExecuteOriginalSql 执行原生sql | |||||
func ExecuteOriginalSql(session *xorm.Session, sql string) (sql.Result, error) { | |||||
result, err := session.Exec(sql) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return result, nil | |||||
} |
@@ -0,0 +1,18 @@ | |||||
package model | |||||
import "time" | |||||
type PayChannel struct { | |||||
Id int `json:"id" xorm:"not null pk autoincr INT(11)"` | |||||
Name string `json:"name" xorm:"not null comment('渠道名称') VARCHAR(255)"` | |||||
IsUse int `json:"is_use" xorm:"not null default 1 comment('是否开启;1:开启;0:关闭') TINYINT(1)"` | |||||
ThirdPartyId string `json:"third_party_id" xorm:"not null comment('第三方聚合支付的渠道id') VARCHAR(20)"` | |||||
Kind string `json:"kind" xorm:"not null comment('渠道种类(self:自有支付 zhiYing:智莺支付 thirdParty 第三方聚合支付 )') VARCHAR(255)"` | |||||
ModuleIdentifier string `json:"module_identifier" xorm:"not null comment('模块标识符') VARCHAR(255)"` | |||||
ModuleName string `json:"module_name" xorm:"not null comment('模块名称') VARCHAR(255)"` | |||||
CommissionRate string `json:"commission_rate" xorm:"not null comment('抽成比例(千分之)') VARCHAR(255)"` | |||||
Memo string `json:"memo" xorm:"not null comment('备注') VARCHAR(255)"` | |||||
Ext string `json:"ext" xorm:"comment('拓展字段(json存储)') TEXT"` | |||||
CreateAt time.Time `json:"create_at" xorm:"default 'CURRENT_TIMESTAMP' comment('创建时间') TIMESTAMP"` | |||||
UpdateAt time.Time `json:"update_at" xorm:"default 'CURRENT_TIMESTAMP' comment('更新时间') TIMESTAMP"` | |||||
} |
@@ -0,0 +1,12 @@ | |||||
package model | |||||
type PayChannelMaster struct { | |||||
Id int `json:"id" xorm:"not null pk autoincr INT(11)"` | |||||
ChannelId int `json:"channel_id" xorm:"not null default 1 comment('支付通道id') INT(11)"` | |||||
Identifier string `json:"identifier" xorm:"not null comment('场景标识符') VARCHAR(255)"` | |||||
MasterId int `json:"master_id" xorm:"not null default 1 comment('站长id') INT(11)"` | |||||
MasterNickname string `json:"master_nickname" xorm:"not null comment('站长名称') VARCHAR(255)"` | |||||
ThirdPartyId string `json:"third_party_id" xorm:"not null comment('第三方聚合支付的渠道id') VARCHAR(50)"` | |||||
MasterAppName string `json:"master_app_name" xorm:"not null comment('站长应用名称') VARCHAR(50)"` | |||||
AppId int `json:"app_id" xorm:"not null default 1 comment('应用id(mid)') INT(20)"` | |||||
} |
@@ -0,0 +1,7 @@ | |||||
package model | |||||
type SysCfg struct { | |||||
Key string `json:"key" xorm:"not null pk comment('键') VARCHAR(127)"` | |||||
Val string `json:"val" xorm:"comment('值') TEXT"` | |||||
Memo string `json:"memo" xorm:"not null default '' comment('备注') VARCHAR(255)"` | |||||
} |
@@ -0,0 +1,19 @@ | |||||
module code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git | |||||
go 1.15 | |||||
require ( | |||||
github.com/forgoer/openssl v1.2.1 | |||||
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 v1.8.8 | |||||
github.com/iGoogle-ink/gopay v1.5.36 | |||||
github.com/pkg/errors v0.9.1 | |||||
github.com/syyongx/php2go v0.9.7 | |||||
go.uber.org/zap v1.16.0 | |||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 | |||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 | |||||
xorm.io/builder v0.3.10 // indirect | |||||
xorm.io/xorm v1.3.0 | |||||
) |
@@ -0,0 +1,151 @@ | |||||
package icbc | |||||
import ( | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/md" | |||||
zhios_pay_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/utils" | |||||
"crypto" | |||||
"crypto/rand" | |||||
"crypto/rsa" | |||||
"encoding/base64" | |||||
"encoding/json" | |||||
"errors" | |||||
"fmt" | |||||
"golang.org/x/crypto/ssh" | |||||
"sort" | |||||
"strings" | |||||
"time" | |||||
) | |||||
func ShowIntegral(appId, privateKeyStr string, param map[string]string) (int, error) { | |||||
bizContent := map[string]string{ | |||||
"name": param["name"], //客户姓名 | |||||
"mobile": param["mobile"], //手机号码 | |||||
"mobileArea": param["mobileArea"], //手机国际代码 853、852、86 | |||||
"optFlag": param["optFlag"], //客户姓名 | |||||
"cardNo": param["cardNo"], | |||||
"cardType": param["cardType"], | |||||
} | |||||
if param["optFlag"] == "1" { | |||||
bizContent["otp"] = param["otp"] | |||||
} | |||||
marshal, _ := json.Marshal(bizContent) | |||||
param["biz_content"] = string(marshal) | |||||
send := Send("/macaufh/mybank/account/cuscredit/cuscreditquery/V1", appId, privateKeyStr, param) | |||||
var data = md.IcbcRes{} | |||||
err := json.Unmarshal([]byte(send), &data) | |||||
fmt.Println("icbc===============", send) | |||||
fmt.Println("icbc===============", err) | |||||
var cusCredit = 0 | |||||
if err != nil { | |||||
return cusCredit, err | |||||
} | |||||
if data.ResponseBizContent.Success { | |||||
return data.ResponseBizContent.CusCredit, nil | |||||
} | |||||
return cusCredit, errors.New(data.ResponseBizContent.ReturnMsg) | |||||
} | |||||
//积分扣除 | |||||
func DeductIntegral(appId, privateKeyStr string, param map[string]string) error { | |||||
bizContent := map[string]string{ | |||||
"name": param["name"], //客户姓名 | |||||
"mobile": param["mobile"], //手机号码 | |||||
"mobileArea": param["mobileArea"], //手机国际代码 853、852、86 | |||||
"optFlag": param["optFlag"], //客户姓名 | |||||
"cardNo": param["cardNo"], | |||||
"cardType": param["cardType"], | |||||
"cusCredit": param["cusCredit"], | |||||
} | |||||
if param["optFlag"] == "1" { | |||||
bizContent["otp"] = param["otp"] | |||||
} | |||||
marshal, _ := json.Marshal(bizContent) | |||||
param["biz_content"] = string(marshal) | |||||
send := Send("/macaufh/mybank/account/cuscredit/cuscreditconsume/V1", appId, privateKeyStr, param) | |||||
var data = md.IcbcRes{} | |||||
err := json.Unmarshal([]byte(send), &data) | |||||
fmt.Println("icbc===============", send) | |||||
fmt.Println("icbc===============", err) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
if data.ResponseBizContent.Success { | |||||
return nil | |||||
} | |||||
return errors.New(data.ResponseBizContent.ReturnMsg) | |||||
} | |||||
func DefaultParam(method, appId, privateKeyStr string, param map[string]string) map[string]interface{} { | |||||
now := time.Now() | |||||
arg := map[string]interface{}{ | |||||
"app_id": appId, | |||||
"msg_id": zhios_pay_utils.Int64ToStr(now.Unix()), | |||||
"format": "json", | |||||
"charset": "utf-8", | |||||
"sign_type": "RSA2", | |||||
"timestamp": now.Format("2006-01-02 15:04:05"), | |||||
} | |||||
if param["biz_content"] != "" { | |||||
arg["biz_content"] = param["biz_content"] | |||||
} | |||||
getSign, err2 := GetSign(method, privateKeyStr, arg) | |||||
if err2 != nil { | |||||
return nil | |||||
} | |||||
fmt.Println(getSign) | |||||
arg["sign"] = getSign | |||||
return arg | |||||
} | |||||
func Send(method string, appId, privateKeyStr string, param map[string]string) string { | |||||
req := DefaultParam(method, appId, privateKeyStr, param) | |||||
url := "https://apipcs.icbcmo.site/api" + method | |||||
headers := map[string]string{ | |||||
"Content-Type": "application/x-www-form-urlencoded", | |||||
} | |||||
fmt.Println(req) | |||||
//for k, v := range req { | |||||
// if strings.Contains(url, "?") == false { | |||||
// url += "?" + k + "=" + php2go.URLEncode(v.(string)) | |||||
// } else { | |||||
// url += "&" + k + "=" + php2go.URLEncode(v.(string)) | |||||
// } | |||||
//} | |||||
fmt.Println(url) | |||||
res, err := zhios_pay_utils.CurlPost(url, req, headers) | |||||
if err != nil { | |||||
fmt.Println(err) | |||||
return "" | |||||
} | |||||
fmt.Println(string(res)) | |||||
return string(res) | |||||
} | |||||
func GetSign(method, privateKeyStr string, param map[string]interface{}) (string, error) { | |||||
privateKey, err := ssh.ParseRawPrivateKey([]byte(privateKeyStr)) | |||||
if err != nil { | |||||
return "", err | |||||
} | |||||
keys := make([]string, 0, len(param)) | |||||
for k := range param { | |||||
keys = append(keys, k) | |||||
} | |||||
sort.Strings(keys) | |||||
data := "/api" + method | |||||
for _, v := range keys { | |||||
if strings.Contains(data, "?") == false { | |||||
data += "?" + v + "=" + param[v].(string) | |||||
} else { | |||||
data += "&" + v + "=" + param[v].(string) | |||||
} | |||||
} | |||||
fmt.Println(privateKey.(*rsa.PrivateKey)) | |||||
fmt.Println(data) | |||||
h := crypto.Hash.New(crypto.SHA256) | |||||
h.Write([]byte(data)) | |||||
hashed := h.Sum(nil) | |||||
sign, err := rsa.SignPKCS1v15(rand.Reader, privateKey.(*rsa.PrivateKey), crypto.SHA256, hashed) | |||||
if err != nil { | |||||
return "", nil | |||||
} | |||||
return base64.StdEncoding.EncodeToString(sign), nil | |||||
} |
@@ -0,0 +1,176 @@ | |||||
package local_alipay | |||||
import ( | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/md" | |||||
"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" { | |||||
client.SetAppCertSN(paySet.PayAppCertSn) | |||||
aliPayRootCertSN := "687b59193f3f462dd5336e5abf83c5d8_02941eef3187dddf3d3b83462e1dfcf6" | |||||
client.SetAliPayRootCertSN(aliPayRootCertSN) | |||||
client.SetAliPayPublicCertSN(paySet.PayAlipayrsaPublicKey) | |||||
} | |||||
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 "", 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" { | |||||
client.SetAppCertSN(paySet.PayAppCertSn) | |||||
aliPayRootCertSN := "687b59193f3f462dd5336e5abf83c5d8_02941eef3187dddf3d3b83462e1dfcf6" | |||||
client.SetAliPayRootCertSN(aliPayRootCertSN) | |||||
client.SetAliPayPublicCertSN(paySet.PayAlipayrsaPublicKey) | |||||
} | |||||
//请求参数 | |||||
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 "", 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" { | |||||
client.SetAppCertSN(paySet.PayAppCertSn) | |||||
aliPayRootCertSN := "687b59193f3f462dd5336e5abf83c5d8_02941eef3187dddf3d3b83462e1dfcf6" | |||||
client.SetAliPayRootCertSN(aliPayRootCertSN) | |||||
client.SetAliPayPublicCertSN(paySet.PayAlipayrsaPublicKey) | |||||
} | |||||
//请求参数 | |||||
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, 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, err | |||||
} | |||||
return aliRsp, nil | |||||
} |
@@ -0,0 +1,303 @@ | |||||
package local_wxpay | |||||
import ( | |||||
zhios_pay_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/utils" | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/utils/logx" | |||||
"fmt" | |||||
"github.com/iGoogle-ink/gopay" | |||||
"github.com/iGoogle-ink/gopay/pkg/util" | |||||
"github.com/iGoogle-ink/gopay/wechat" | |||||
v3 "github.com/iGoogle-ink/gopay/wechat/v3" | |||||
"strconv" | |||||
"time" | |||||
) | |||||
func NewClient(appId, mchId, apiKey string, isProd bool) *wechat.Client { | |||||
// 初始化微信客户端 | |||||
// appId:应用ID | |||||
// mchId:商户ID | |||||
// apiKey:API秘钥值 | |||||
// isProd:是否是正式环境 | |||||
client := wechat.NewClient(appId, mchId, apiKey, isProd) | |||||
// 打开Debug开关,输出请求日志,默认关闭 | |||||
client.DebugSwitch = gopay.DebugOn | |||||
// 设置国家:不设置默认 中国国内 | |||||
// wechat.China:中国国内 | |||||
// wechat.China2:中国国内备用 | |||||
// wechat.SoutheastAsia:东南亚 | |||||
// wechat.Other:其他国家 | |||||
client.SetCountry(wechat.China) | |||||
// 添加微信证书 Path 路径 | |||||
// certFilePath:apiclient_cert.pem 路径 | |||||
// keyFilePath:apiclient_key.pem 路径 | |||||
// pkcs12FilePath:apiclient_cert.p12 路径 | |||||
// 返回err | |||||
//client.AddCertFilePath() | |||||
// 添加微信证书内容 Content | |||||
// certFileContent:apiclient_cert.pem 内容 | |||||
// keyFileContent:apiclient_key.pem 内容 | |||||
// pkcs12FileContent:apiclient_cert.p12 内容 | |||||
// 返回err | |||||
//client.AddCertFileContent() | |||||
return client | |||||
} | |||||
// TradeAppPay is 微信APP支付 | |||||
func TradeAppPay(client *wechat.Client, subject, orderID, amount, notifyUrl string) (map[string]string, error) { | |||||
// 初始化 BodyMap | |||||
bm := make(gopay.BodyMap) | |||||
bm.Set("nonce_str", util.GetRandomString(32)). | |||||
Set("body", subject). | |||||
Set("out_trade_no", orderID). | |||||
Set("total_fee", amount). | |||||
Set("spbill_create_ip", "127.0.0.1"). | |||||
Set("notify_url", notifyUrl). | |||||
Set("trade_type", wechat.TradeType_App). | |||||
Set("sign_type", wechat.SignType_MD5) | |||||
/*.Set("openid", "o0Df70H2Q0fY8JXh1aFPIRyOBgu8")*/ | |||||
// 预下单 | |||||
wxRsp, err := client.UnifiedOrder(bm) | |||||
if err != nil { | |||||
_ = zhios_pay_logx.Warn(err) | |||||
return nil, err | |||||
} | |||||
_, err = wechat.VerifySign(client.ApiKey, wechat.SignType_MD5, wxRsp) | |||||
if err != nil { | |||||
_ = zhios_pay_logx.Warn(err) | |||||
return nil, err | |||||
} | |||||
//if !ok { | |||||
// return nil, errors.New("验签失败") | |||||
//} | |||||
timeStamp := strconv.FormatInt(time.Now().Unix(), 10) | |||||
paySign := wechat.GetAppPaySign(client.AppId, client.MchId, wxRsp.NonceStr, wxRsp.PrepayId, wechat.SignType_MD5, timeStamp, client.ApiKey) | |||||
res := map[string]string{ | |||||
"appid": client.AppId, | |||||
"partnerid": client.MchId, | |||||
"prepayid": wxRsp.PrepayId, | |||||
"sign": paySign, | |||||
"package": "Sign=WXPay", | |||||
"noncestr": wxRsp.NonceStr, | |||||
"timestamp": timeStamp, | |||||
} | |||||
return res, nil | |||||
} | |||||
// TradeAppPay is 微信H5支付 | |||||
func TradeH5Pay(client *wechat.Client, subject, orderID, amount, notifyUrl string) (map[string]string, error) { | |||||
// 初始化 BodyMap | |||||
bm := make(gopay.BodyMap) | |||||
bm.Set("nonce_str", util.GetRandomString(32)). | |||||
Set("body", subject). | |||||
Set("out_trade_no", orderID). | |||||
Set("total_fee", amount). | |||||
Set("spbill_create_ip", "121.196.29.49"). | |||||
Set("notify_url", notifyUrl). | |||||
Set("trade_type", wechat.TradeType_H5). | |||||
Set("sign_type", wechat.SignType_MD5). | |||||
SetBodyMap("scene_info", func(bm gopay.BodyMap) { | |||||
bm.SetBodyMap("h5_info", func(bm gopay.BodyMap) { | |||||
bm.Set("type", "Wap") | |||||
bm.Set("wap_url", "https://www.fumm.cc") | |||||
bm.Set("wap_name", "zyos") | |||||
}) | |||||
}) | |||||
/*.Set("openid", "o0Df70H2Q0fY8JXh1aFPIRyOBgu8")*/ | |||||
// 预下单 | |||||
wxRsp, err := client.UnifiedOrder(bm) | |||||
if err != nil { | |||||
_ = zhios_pay_logx.Warn(err) | |||||
return nil, err | |||||
} | |||||
_, err = wechat.VerifySign(client.ApiKey, wechat.SignType_MD5, wxRsp) | |||||
if err != nil { | |||||
_ = zhios_pay_logx.Warn(err) | |||||
return nil, err | |||||
} | |||||
timeStamp := strconv.FormatInt(time.Now().Unix(), 10) | |||||
packages := "prepay_id=" + wxRsp.PrepayId | |||||
paySign := wechat.GetH5PaySign(client.AppId, wxRsp.NonceStr, packages, wechat.SignType_MD5, timeStamp, client.ApiKey) | |||||
fmt.Println("paySign===", paySign) | |||||
r := map[string]string{ | |||||
"redirect_url": wxRsp.MwebUrl, | |||||
} | |||||
return r, nil | |||||
} | |||||
// TradeMiniProgPay is 微信小程序支付 ☑️ | |||||
func TradeMiniProgPay(client *wechat.Client, subject, orderID, amount, notifyUrl, openid string) (map[string]string, error) { | |||||
// 初始化 BodyMap | |||||
bm := make(gopay.BodyMap) | |||||
bm.Set("nonce_str", util.GetRandomString(32)). | |||||
Set("body", subject). | |||||
Set("openid", openid). | |||||
Set("out_trade_no", orderID). | |||||
Set("total_fee", amount). | |||||
Set("spbill_create_ip", "127.0.0.1"). | |||||
Set("notify_url", notifyUrl). | |||||
Set("trade_type", wechat.TradeType_Mini). | |||||
Set("sign_type", wechat.SignType_MD5) | |||||
// 预下单 | |||||
wxRsp, err := client.UnifiedOrder(bm) | |||||
if err != nil { | |||||
_ = zhios_pay_logx.Warn(err) | |||||
return nil, err | |||||
} | |||||
fmt.Println(wxRsp) | |||||
timeStamp := strconv.FormatInt(time.Now().Unix(), 10) | |||||
packages := "prepay_id=" + wxRsp.PrepayId | |||||
paySign := wechat.GetMiniPaySign(client.AppId, wxRsp.NonceStr, packages, wechat.SignType_MD5, timeStamp, client.ApiKey) | |||||
res := map[string]string{ | |||||
"appId": client.AppId, | |||||
"paySign": paySign, | |||||
"signType": wechat.SignType_MD5, | |||||
"package": packages, | |||||
"nonceStr": wxRsp.NonceStr, | |||||
"timeStamp": timeStamp, | |||||
} | |||||
return res, nil | |||||
} | |||||
// TradeAppPayV3 is 微信APP支付v3 | |||||
func TradeAppPayV3(client *v3.ClientV3, subject, orderID, amount, notifyUrl string) (map[string]string, error) { | |||||
// 初始化 BodyMap | |||||
amountNew := zhios_pay_utils.AnyToFloat64(amount) * 100 | |||||
bm := make(gopay.BodyMap) | |||||
bm.Set("nonce_str", util.GetRandomString(32)). | |||||
Set("body", subject). | |||||
Set("out_trade_no", orderID). | |||||
Set("total_fee", amountNew). | |||||
Set("spbill_create_ip", "127.0.0.1"). | |||||
Set("notify_url", notifyUrl). | |||||
Set("trade_type", wechat.TradeType_App). | |||||
Set("sign_type", wechat.SignType_MD5) | |||||
/*.Set("openid", "o0Df70H2Q0fY8JXh1aFPIRyOBgu8")*/ | |||||
//// 预下单 | |||||
//wxRsp, err := v3.UnifiedOrder(bm) | |||||
//if err != nil { | |||||
// _ = zhios_pay_logx.Warn(err) | |||||
// return nil, err | |||||
//} | |||||
//_, err = wechat.VerifySign(client.ApiKey, wechat.SignType_MD5, wxRsp) | |||||
//if err != nil { | |||||
// _ = zhios_pay_logx.Warn(err) | |||||
// return nil, err | |||||
//} | |||||
////if !ok { | |||||
//// return nil, errors.New("验签失败") | |||||
////} | |||||
//timeStamp := strconv.FormatInt(time.Now().Unix(), 10) | |||||
//paySign := wechat.GetAppPaySign(client.AppId, client.MchId, wxRsp.NonceStr, wxRsp.PrepayId, wechat.SignType_MD5, timeStamp, client.ApiKey) | |||||
//res := map[string]string{ | |||||
// "appid": client.AppId, | |||||
// "partnerid": client.MchId, | |||||
// "prepayid": wxRsp.PrepayId, | |||||
// "sign": paySign, | |||||
// "package": "Sign=WXPay", | |||||
// "noncestr": wxRsp.NonceStr, | |||||
// "timestamp": timeStamp, | |||||
//} | |||||
//return res, nil | |||||
return nil, nil | |||||
} | |||||
//// TradeJSAPIPay is 微信JSAPI支付 | |||||
func TradeJSAPIPay(client *wechat.Client, subject, orderID, amount, notifyUrl, openid string) (map[string]string, error) { | |||||
// 初始化 BodyMap | |||||
bm := make(gopay.BodyMap) | |||||
bm.Set("nonce_str", util.GetRandomString(32)). | |||||
Set("body", subject). | |||||
Set("out_trade_no", orderID). | |||||
Set("total_fee", amount). | |||||
Set("spbill_create_ip", "121.196.29.49"). | |||||
Set("notify_url", notifyUrl). | |||||
Set("trade_type", wechat.TradeType_JsApi). | |||||
Set("sign_type", wechat.SignType_MD5). | |||||
Set("openid", openid). | |||||
SetBodyMap("scene_info", func(bm gopay.BodyMap) { | |||||
bm.SetBodyMap("h5_info", func(bm gopay.BodyMap) { | |||||
bm.Set("type", "Wap") | |||||
bm.Set("wap_url", "https://www.fumm.cc") | |||||
bm.Set("wap_name", "zyos") | |||||
}) | |||||
}) | |||||
// 预下单 | |||||
wxRsp, err := client.UnifiedOrder(bm) | |||||
if err != nil { | |||||
_ = zhios_pay_logx.Warn(err) | |||||
return nil, err | |||||
} | |||||
_, err = wechat.VerifySign(client.ApiKey, wechat.SignType_MD5, wxRsp) | |||||
if err != nil { | |||||
_ = zhios_pay_logx.Warn(err) | |||||
return nil, err | |||||
} | |||||
//if !ok { | |||||
// return nil, errors.New("验签失败") | |||||
//} | |||||
timeStamp := strconv.FormatInt(time.Now().Unix(), 10) | |||||
//paySign := wechat.GetAppPaySign(client.AppId, client.MchId, wxRsp.NonceStr, wxRsp.PrepayId, wechat.SignType_MD5, timeStamp, client.ApiKey) | |||||
packages := "prepay_id=" + wxRsp.PrepayId | |||||
paySign := wechat.GetJsapiPaySign(client.AppId, wxRsp.NonceStr, packages, wechat.SignType_MD5, timeStamp, client.ApiKey) | |||||
zhios_pay_logx.Info("wxRsp.PrepayId:" + wxRsp.PrepayId) | |||||
zhios_pay_logx.Info("wxRsp.PrepayId:" + wxRsp.PrepayId) | |||||
zhios_pay_logx.Info("wxRsp.PrepayId:" + openid) | |||||
res := map[string]string{ | |||||
"appid": client.AppId, | |||||
"partnerid": client.MchId, | |||||
"prepayid": wxRsp.PrepayId, | |||||
"sign": paySign, | |||||
"package": "prepay_id=" + wxRsp.PrepayId, | |||||
"noncestr": wxRsp.NonceStr, | |||||
"timestamp": timeStamp, | |||||
} | |||||
return res, nil | |||||
} | |||||
// TradeH5PayV3 is 微信H5支付v3 | |||||
func TradeH5PayV3(client *wechat.Client, subject, orderID, amount, notifyUrl string) (string, error) { | |||||
// 初始化 BodyMap | |||||
bm := make(gopay.BodyMap) | |||||
bm.Set("nonce_str", util.GetRandomString(32)). | |||||
Set("body", subject). | |||||
Set("out_trade_no", orderID). | |||||
Set("total_fee", amount). | |||||
Set("spbill_create_ip", "127.0.0.1"). | |||||
Set("notify_url", notifyUrl). | |||||
Set("trade_type", wechat.TradeType_App). | |||||
Set("device_info", "WEB"). | |||||
Set("sign_type", wechat.SignType_MD5). | |||||
SetBodyMap("scene_info", func(bm gopay.BodyMap) { | |||||
bm.SetBodyMap("h5_info", func(bm gopay.BodyMap) { | |||||
bm.Set("type", "Wap") | |||||
bm.Set("wap_url", "https://www.fumm.cc") | |||||
bm.Set("wap_name", "H5测试支付") | |||||
}) | |||||
}) /*.Set("openid", "o0Df70H2Q0fY8JXh1aFPIRyOBgu8")*/ | |||||
// 预下单 | |||||
wxRsp, err := client.UnifiedOrder(bm) | |||||
if err != nil { | |||||
_ = zhios_pay_logx.Warn(err) | |||||
return "", err | |||||
} | |||||
// ====APP支付 paySign==== | |||||
timeStamp := strconv.FormatInt(time.Now().Unix(), 10) | |||||
// 获取APP支付的 paySign | |||||
// 注意:package 参数因为是固定值,无需开发者再传入 | |||||
// appId:AppID | |||||
// partnerid:partnerid | |||||
// nonceStr:随机字符串 | |||||
// prepayId:统一下单成功后得到的值 | |||||
// signType:签名方式,务必与统一下单时用的签名方式一致 | |||||
// timeStamp:时间 | |||||
// apiKey:API秘钥值 | |||||
paySign := wechat.GetAppPaySign(client.AppId, client.MchId, wxRsp.NonceStr, wxRsp.PrepayId, wechat.SignType_MD5, timeStamp, client.ApiKey) | |||||
return paySign, nil | |||||
} | |||||
// TradeMiniProgPayV3 is 微信小程序支付v3 | |||||
func TradeMiniProgPayV3(client *v3.ClientV3, subject, orderID, amount, notifyUrl string) (string, error) { | |||||
return "", nil | |||||
} |
@@ -0,0 +1,17 @@ | |||||
package md | |||||
type AliPayPayParams struct { | |||||
Subject string `json:"subject" binding:"required"` | |||||
Amount string `json:"amount" binding:"required"` | |||||
OrderType string `json:"order_type" binding:"required"` | |||||
OrdId string `json:"ord_id"` | |||||
} | |||||
type PayData struct { | |||||
PayAppCertSn string `json:"pay_app_cert_sn"` | |||||
PayAlipayRootCertSn string `json:"pay_alipay_root_cert_sn"` | |||||
PayAlipayrsaPublicKey string `json:"pay_alipayrsa_public_key"` | |||||
PayAliUseType string `json:"pay_ali_use_type"` | |||||
PriKey string `json:"pay_ali_new_private_key"` | |||||
WxAppletFilepathUrl string `json:"wx_applet_filepath_url"` | |||||
} |
@@ -0,0 +1,19 @@ | |||||
package md | |||||
// 缓存key统一管理, %s格式化为masterId | |||||
const ( | |||||
AppCfgCacheKey = "%s:cfg_cache:%s" // 占位符: masterId, key的第一个字母 | |||||
VirtualCoinCfgCacheKey = "%s:virtual_coin_cfg" | |||||
PlanRewardCfgCacheKey = "%s:plan_reward_cfg" | |||||
UnionSetCacheCfg = "%s:union_set_cfg:%s" // 联盟设置缓存key | |||||
UserFinValidUpdateLock = "%s:user_fin_valid_update_lock:%s" // 用户余额更新锁(能拿到锁才能更新余额) | |||||
WithdrawApplyQueueListKey = "withdraw_apply_queue" // 提现队列 | |||||
TplBottomNavRedisKey = "%s:tpl_nav_bottom_key:%s" // master_id platform | |||||
SysModByIdRedisKey = "%s:sys_mod_tpl_by_id:%s" | |||||
CfgCacheTime = 86400 | |||||
) |
@@ -0,0 +1,6 @@ | |||||
package md | |||||
const ( | |||||
ZHIOS_PAY_URL_PRD = "http://pay.zhios.cn" | |||||
ZHIOS_PAY_URL_DEV = "http://pay.izhyin.com" | |||||
) |
@@ -0,0 +1,16 @@ | |||||
package md | |||||
import "time" | |||||
//数据库配置结构体 | |||||
type DBCfg struct { | |||||
Host string `yaml:"host"` //ip及端口 | |||||
Name string `yaml:"name"` //库名 | |||||
User string `yaml:"user"` //用户 | |||||
Psw string `yaml:"psw"` //密码 | |||||
ShowLog bool `yaml:"show_log"` //是否显示SQL语句 | |||||
MaxLifetime time.Duration `yaml:"max_lifetime"` | |||||
MaxOpenConns int `yaml:"max_open_conns"` | |||||
MaxIdleConns int `yaml:"max_idle_conns"` | |||||
Path string `yaml:"path"` //日志文件存放路径 | |||||
} |
@@ -0,0 +1,12 @@ | |||||
package md | |||||
type IcbcRes struct { | |||||
ResponseBizContent struct { | |||||
CusCredit int `json:"cusCredit"` | |||||
MsgID string `json:"msg_id"` | |||||
ReturnCode int64 `json:"return_code"` | |||||
ReturnMsg string `json:"return_msg"` | |||||
Success bool `json:"success"` | |||||
} `json:"response_biz_content"` | |||||
Sign string `json:"sign"` | |||||
} |
@@ -0,0 +1,102 @@ | |||||
package md | |||||
const ( | |||||
JoinPayUrl = "https://www.joinpay.com/trade/uniPayApi.action" | |||||
PayTestAccount = "888118100005250" | |||||
PayTestSecretKey = "a12ceace4cc5423eae32b4626adf56c9" | |||||
Q1FrpCodeALIPAY = "ALIPAY_H5" //支付宝固定 | |||||
Q1FrpCodeWEIXINPAYAPP = "WEIXIN_APP3" //微信固定(应该) | |||||
//Q1FrpCodeWEIXINPAYH5 = "WEIXIN_H5_PLUS" | |||||
//Q1FrpCodeWEIXINPAYGZH = "WEIXIN_GZH" | |||||
//Q1FrpCodeWEIXINPAYXCX = "WEIXIN_XCX" | |||||
PayTestAliAccount = "777146200391877" | |||||
PayTestWeChatAccount = "777196100391173" | |||||
WEIXINPATH = "/pages/payIndex/payIndex?rc_result=" | |||||
) | |||||
// | |||||
// JoinPayReq | |||||
// @Description: 汇聚支付-聚合支付-支付接口-请求参数信息体 | |||||
// @Url: https://www.joinpay.com/open-platform/pages/document.html?apiName=%E8%81%9A%E5%90%88%E6%94%AF%E4%BB%98&id=6 | |||||
// | |||||
type JoinPayReq struct { | |||||
P0Version string `json:"p0_Version" label:"版本号;目前版本号为:2.1;是"` | |||||
P1MerchantNo string `json:"p1_MerchantNo" label:"商户编号;商户在支付平台系统的唯一身份标识;是"` | |||||
P2OrderNo string `json:"p2_OrderNo" label:"商户订单号;商户系统提交的唯一订单号。仅支持由英文字母、阿拉伯数字、下划线(英文符号“_”)组成的订单号传输;是"` | |||||
P3Amount string `json:"p3_Amount" label:"订单金额;单位:元,精确到分,保留两位小数。例如:10.23。;是"` | |||||
P4Cur string `json:"p4_Cur" label:"交易币种;默认设置为1(代表人民币);是"` | |||||
P5ProductName string `json:"p5_ProductName" label:"商品名称;用于支付时显示在支付平台网关上的订单产品信息;是"` | |||||
P6ProductDesc string `json:"p6_ProductDesc" label:"商品描述;对商品信息进行描述;否"` | |||||
P7Mp string `json:"p7_Mp" label:"公用回传参数;如果商户请求时传递了该参数,则返回给商户时会原值传回;否"` | |||||
P9NotifyUrl string `json:"p9_NotifyUrl" label:"服务器异步通知地址;汇聚支付系统主动通知商户网站里指定的 http 地址。 | |||||
异步通知地址需按: | |||||
1.AB|notifyUrl 的规则上送才会返回用户标识和平台优惠金额; | |||||
2.云闪付 CD|notifyUrl 的规则上才会返回用户支付的卡类型;是"` | |||||
Q1FrpCode string `json:"q1_FrpCode" label:"交易类型;可见md模型里提供的;是"` | |||||
Q2MerchantBankCode string `json:"q2_MerchantBankCode" label:"银行商户编码;非必填;否"` | |||||
Q4IsShowPic string `json:"q4_IsShowPic" label:"是否展示图片;默认为空,不输出图片;填1表示输出图片,仅交易类型为主扫时可用(即当q1_FrpCode=ALIPAY_NATIVE、WEIXIN_NATIVE、JD_NATIVE、QQ_NATIVE、UNIONPAY_NATIVE、BAIDU_NATIVE、SUNING_NATIVE);否"` | |||||
Q5OpenId string `json:"q5_OpenId" label:"微信Openid; | |||||
公众号获取用户Openid,公众号支付商户及微信小程序商户必填(即当q1_FrpCode=WEIXIN_GZH、WEIXIN_XCX);否"` | |||||
Q6AuthCode string `json:"q6_AuthCode" label:" | |||||
付款码数字;付款码数字被扫支付必填(即当q1_FrpCode= ALIPAY_CARD、WEIXIN_CARD、JD_CARD、QQ_CARD、UNIONPAY_CARD;否"` | |||||
Q7AppId string `json:"q7_AppId" label:"APPID;微信公众号、微信小程序、微信App、微信app+支付必填(即当q1_FrpCode= WEIXIN_GZH、WEIXIN_XCX、WEIXIN_APP3、WEIXIN_H5_PLUS【对应报备小程序appid】);否"` | |||||
Q8TerminalNo string `json:"q8_TerminalNo" label:" | |||||
终端设备号; | |||||
通过终端信息采集接口获取,由汇聚生成。微信/支付宝被扫支付必填(即当q1_FrpCode= ALIPAY_CARD、WEIXIN_CARD);否"` | |||||
Q9TransactionModel string `json:"q9_TransactionModel" label:"支付宝H5模式;支付宝H5选填,默认为空 | |||||
1.模式一:当 q9_TransactionModel = MODEL1或为空时,有应答参数返回,rc_Result中返回html,需进行重定向跳转;2.模式二:当 q9_TransactionModel = MODEL2时,直接跳转链接,不返回应答参数。;否"` | |||||
QATradeMerchantNo string `json:"qa_TradeMerchantNo" label:"报备商户号;请填写报备产品后返回的报备商户号;是"` | |||||
QBbuyerId string `json:"qb_buyerId" label:"买家的支付宝唯一用户号;支付宝服务窗支付必填(即当q1_FrpCode=ALIPAY_FWC )(买家的支付宝唯一用户号(2088开头16位纯数字));否"` | |||||
QJDJPlan string `json:"qj_DJPlan" label:"点金计划;该字段参数值为JSON串;否"` | |||||
QKDisablePayModel string `json:"qk_DisablePayModel" label:"禁用支付方式;目前仅支持使用微信和支付宝支付时可禁用某些方式,详细枚举值见附录;否"` | |||||
QLTerminalIp string `json:"ql_TerminalIp" label:"终端设备IP; | |||||
商户端终端设备 IP 地址。微信/支付宝被扫支付必填(即当q1_FrpCode= ALIPAY_CARD、WEIXIN_CARD);否"` | |||||
Hmac string `json:"hmac" label:"签名数据;参见签名机制;是"` | |||||
} | |||||
type JoinPayResponse struct { | |||||
R7TrxNo string `json:"r7_TrxNo"` | |||||
RbCodeMsg string `json:"rb_CodeMsg"` | |||||
R2OrderNo string `json:"r2_OrderNo"` | |||||
R3Amount string `json:"r3_Amount"` | |||||
R6FrpCode string `json:"r6_FrpCode"` | |||||
RcResult string `json:"rc_Result"` | |||||
RaCode int `json:"ra_Code"` | |||||
Hmac string `json:"hmac"` | |||||
RdPic string `json:"rd_Pic"` | |||||
R4Cur string `json:"r4_Cur"` | |||||
R5Mp string `json:"r5_Mp"` | |||||
R8MerchantBankCode string `json:"r8_MerchantBankCode"` | |||||
R0Version string `json:"r0_Version"` | |||||
R1MerchantNo string `json:"r1_MerchantNo"` | |||||
} | |||||
type JoinPayResponseWeChatRcResult struct { | |||||
OrderNo string `json:"order_no"` | |||||
TrxNo string `json:"trx_no"` | |||||
PayeeName string `json:"payee_name"` | |||||
OriginalID string `json:"original_id"` | |||||
AppID string `json:"app_id"` | |||||
ProductName string `json:"product_name"` | |||||
OrderAmout string `json:"order_amout"` | |||||
} | |||||
type JoinPayCallBack struct { | |||||
R1MerchantNo string `json:"r1_MerchantNo" form:"r1_MerchantNo"` | |||||
R2OrderNo string `json:"r2_OrderNo" form:"r2_OrderNo"` | |||||
R3Amount string `json:"r3_Amount" form:"r3_Amount"` | |||||
R4Cur string `json:"r4_Cur" form:"r4_Cur"` | |||||
R5Mp string `json:"r5_Mp" form:"r5_Mp"` | |||||
R6Status int `json:"r6_Status" form:"r6_Status"` | |||||
R7TrxNo string `json:"r7_TrxNo" form:"r7_TrxNo"` | |||||
R8BankOrderNo string `json:"r8_BankOrderNo" form:"r8_BankOrderNo"` | |||||
R9BankTrxNo string `json:"r9_BankTrxNo" form:"r9_BankTrxNo"` | |||||
RaPayTime string `json:"ra_PayTime" form:"ra_PayTime"` | |||||
RbDealTime string `json:"rb_DealTime" form:"rb_DealTime"` | |||||
RcBankCode string `json:"rc_BankCode" form:"rc_BankCode"` | |||||
RdOpenId string `json:"rd_OpenId" form:"rd_OpenId"` | |||||
ReDiscountAmount string `json:"re_DiscountAmount" form:"re_DiscountAmount"` | |||||
RhcardType string `json:"rh_cardType" form:"rh_cardType"` | |||||
Hmac string `json:"hmac" form:"hmac"` | |||||
} |
@@ -0,0 +1,11 @@ | |||||
package md | |||||
type JoinBankCardPayCallBack struct { | |||||
BizCode string `json:"biz_code"` | |||||
BizMsg string `json:"biz_msg"` | |||||
Data string `json:"data"` | |||||
MchNo string `json:"mch_no"` | |||||
RandStr string `json:"rand_str"` | |||||
Sign string `json:"sign"` | |||||
SignType string `json:"sign_type"` | |||||
} |
@@ -0,0 +1,69 @@ | |||||
package md | |||||
type KuDianWxPayParams struct { | |||||
AppId string `json:"app_id"` | |||||
OpenId string `json:"open_id"` | |||||
OutTradeNo string `json:"out_trade_no"` | |||||
Description string `json:"description"` | |||||
NotifyUrl string `json:"notify_url"` | |||||
TotalAmount string `json:"total_amount" binding:"required"` | |||||
Attach string `json:"attach" binding:"required"` | |||||
MustParams struct { | |||||
ShopId string `json:"shop_id"` | |||||
KuDianMchId string `json:"ku_dian_mch_id"` | |||||
KuDianSecretKey string `json:"ku_dian_secret_key"` | |||||
} `json:"must_params"` | |||||
} | |||||
type KuDianAliAppPayParams struct { | |||||
OutTradeNo string `json:"out_trade_no"` | |||||
Description string `json:"description"` | |||||
NotifyUrl string `json:"notify_url"` | |||||
UserId string `json:"user_id"` | |||||
TotalAmount string `json:"total_amount" binding:"required"` | |||||
Attach string `json:"attach" binding:"required"` | |||||
TradeType string `json:"trade_type"` | |||||
SenceInfo struct { | |||||
WapUrl string `json:"wap_url"` | |||||
WapName string `json:"wap_name"` | |||||
ReturnUrl string `json:"return_url"` | |||||
} `json:"sence_info"` | |||||
MustParams struct { | |||||
ShopId string `json:"shop_id"` | |||||
KuDianMchId string `json:"ku_dian_mch_id"` | |||||
KuDianSecretKey string `json:"ku_dian_secret_key"` | |||||
} `json:"must_params"` | |||||
} | |||||
type MakePayParamsByKuDian struct { | |||||
RequestUrl string `json:"request_url"` | |||||
Params interface{} `json:"params"` | |||||
} | |||||
const ( | |||||
WX_APPLET_PAY_URL = "zy-base-widget-sub/pages/cashier/cashier?unique_identifier=" | |||||
WX_APP_TO_APPLET_PAY_UNIQUE_IDENTIFIER = "%s:wx_app_to_applet_pay_unique_identifier:%s:%s" // 酷点支付 - App跳转小程序支付 (mid + store_id + 纳秒时间戳) | |||||
WX_APP_TO_APPLET_PAY_UNIQUE_IDENTIFIER_CACHE_TIME = 60 * 10 //缓存10分钟 | |||||
) | |||||
// ########################################### 酷点支付 - 回调结构体 ############################################### | |||||
type KuDianPayCallback struct { | |||||
Code int `json:"code"` | |||||
Msg string `json:"msg"` | |||||
Result string `json:"result"` | |||||
} | |||||
type KuDianPayCallbackResp struct { | |||||
MchId string `json:"mch_id"` //商户id | |||||
PayNo string `json:"pay_no"` //聚合支付订单号 | |||||
OutTradeNo string `json:"out_trade_no"` //业务系统订单号 | |||||
TransactionId string `json:"transaction_id"` //支付平台交易流水号。微信、支付宝、云闪付等用户支付使用的平台返回流水号 | |||||
Fee int `json:"fee"` //交易总额,单位:分 | |||||
ReceivedFee int `json:"received_fee"` //商户实收金额,单位:分 | |||||
PayTime string `json:"pay_time"` //支付时间,格式"yyyy-MM-dd HH:mm:ss" | |||||
Payment string `json:"payment"` //支付方式 | |||||
Body string `json:"body"` //支付简要描述 | |||||
Status int `json:"status"` //支付状态 | |||||
Attach string `json:"attach"` //附加信息 | |||||
} |
@@ -0,0 +1,40 @@ | |||||
package md | |||||
const ( | |||||
/*********** DEVICE ***********/ | |||||
PLATFORM_WX_APPLET = "wx_applet" // 小程序 | |||||
PLATFORM_TOUTIAO_APPLET = "toutiao_applet" | |||||
PLATFORM_TIKTOK_APPLET = "tiktok_applet" | |||||
PLATFORM_BAIDU_APPLET = "baidu_applet" | |||||
PLATFORM_ALIPAY_APPLET = "alipay_applet" | |||||
PLATFORM_WAP = "wap" //h5 | |||||
PLATFORM_ANDROID = "android" | |||||
PLATFORM_IOS = "ios" | |||||
PLATFORM_PC = "pc" | |||||
PLATFORM_JSAPI = "jsapi" // 公众号 | |||||
) | |||||
const WX_PAY_BROWSER = "wx_pay_browser" // 用于判断显示支付方式 | |||||
var PlatformList = map[string]struct{}{ | |||||
PLATFORM_WX_APPLET: {}, | |||||
PLATFORM_TOUTIAO_APPLET: {}, | |||||
PLATFORM_TIKTOK_APPLET: {}, | |||||
PLATFORM_BAIDU_APPLET: {}, | |||||
PLATFORM_ALIPAY_APPLET: {}, | |||||
PLATFORM_WAP: {}, | |||||
PLATFORM_ANDROID: {}, | |||||
PLATFORM_IOS: {}, | |||||
PLATFORM_PC: {}, | |||||
} | |||||
var PlatformMap = map[string]string{ | |||||
"android": "2", | |||||
"ios": "2", | |||||
"wap": "4", // 和小程序公用模板 | |||||
"wx_applet": "4", //微信小程序 | |||||
"tiktok_applet": "4", | |||||
"baidu_applet": "4", | |||||
"alipay_applet": "4", | |||||
"toutiao_applet": "4", | |||||
} |
@@ -0,0 +1,40 @@ | |||||
package md | |||||
type WxPayParams struct { | |||||
Subject string `json:"subject" binding:"required"` | |||||
Amount string `json:"amount" binding:"required"` | |||||
OrderType string `json:"order_type" binding:"required"` | |||||
OrdId string `json:"ord_id"` | |||||
PayWxAppid string `json:"pay_wx_appid"` | |||||
PayWxMchId string `json:"pay_wx_mch_id"` | |||||
PayWxApiKey string `json:"pay_wx_api_key"` | |||||
NotifyUrl string `json:"notify_url"` | |||||
ThirdPartyWechatOpenid string `json:"third_party_wechat_openid"` | |||||
Platform string `json:"platform"` | |||||
PayType string `json:"pay_type"` | |||||
PayThirdPartyId string `json:"pay_third_party_id"` | |||||
KuDianNotifyUrl string `json:"ku_dian_notify_url"` | |||||
} | |||||
type WxPayCallback struct { | |||||
AppId string `json:"appid"` | |||||
BankType string `json:"bank_type"` | |||||
CashFee string `json:"cash_fee"` | |||||
FeeType string `json:"fee_type"` | |||||
IsSubscribe string `json:"is_subscribe"` | |||||
MasterID string `json:"master_id"` | |||||
MchID string `json:"mch_id"` | |||||
NonceStr string `json:"nonce_str"` | |||||
Openid string `json:"openid"` | |||||
OrderType string `json:"order_type"` | |||||
OutTradeNo string `json:"out_trade_no"` | |||||
PayMethod string `json:"pay_method"` | |||||
ResultCode string `json:"result_code"` | |||||
ReturnCode string `json:"return_code"` | |||||
Sign string `json:"sign"` | |||||
TimeEnd string `json:"time_end"` | |||||
TotalFee string `json:"total_fee"` | |||||
TradeType string `json:"trade_type"` | |||||
TransactionID string `json:"transaction_id"` | |||||
} |
@@ -0,0 +1,34 @@ | |||||
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" | |||||
) | |||||
var cfg = md.DBCfg{ | |||||
Host: "", | |||||
Name: "", | |||||
User: "", | |||||
Psw: "", | |||||
ShowLog: false, | |||||
MaxLifetime: 0, | |||||
MaxOpenConns: 0, | |||||
MaxIdleConns: 0, | |||||
Path: "", | |||||
} | |||||
func Init(host, name, user, psw string) error { | |||||
cfg.Host = host | |||||
cfg.Name = name | |||||
cfg.User = user | |||||
cfg.Psw = psw | |||||
cfg.ShowLog = true | |||||
cfg.MaxLifetime = 30 | |||||
cfg.MaxOpenConns = 100 | |||||
cfg.MaxIdleConns = 100 | |||||
cfg.Path = "./tmp/%s.logs" | |||||
if err := db.InitDB(&cfg); err != nil { | |||||
return err | |||||
} | |||||
return nil | |||||
} |
@@ -0,0 +1,2 @@ | |||||
[xorm] [info] 2022/05/31 10:40:31.829104 [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] - 28.9565ms | |||||
[xorm] [info] 2022/05/31 10:41:41.231871 [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] - 24.9118ms |
@@ -0,0 +1,26 @@ | |||||
package pay | |||||
import "code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/lib/icbc" | |||||
//澳门工行积分 | |||||
func ShowIntegral(appId, privateKeyStr string, param map[string]string) (int, error) { | |||||
cusCredit, err := icbc.ShowIntegral(appId, privateKeyStr, param) | |||||
return cusCredit, err | |||||
} | |||||
func DeductIntegral(appId, privateKeyStr string, param map[string]string) error { | |||||
err := icbc.DeductIntegral(appId, privateKeyStr, param) | |||||
return err | |||||
} | |||||
//澳门工行积分短信 | |||||
func ShowIntegralSms(appId, privateKeyStr string, param map[string]string) (int, error) { | |||||
cusCredit, err := icbc.ShowIntegral(appId, privateKeyStr, param) | |||||
return cusCredit, err | |||||
} | |||||
//澳门工行积分短信 | |||||
func DeductIntegralSms(appId, privateKeyStr string, param map[string]string) error { | |||||
err := icbc.DeductIntegral(appId, privateKeyStr, param) | |||||
return err | |||||
} |
@@ -0,0 +1,146 @@ | |||||
package pay | |||||
import ( | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/md" | |||||
zhios_pay_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/utils" | |||||
"crypto/tls" | |||||
"io/ioutil" | |||||
"net/http" | |||||
"net/url" | |||||
"strings" | |||||
) | |||||
func UniPayByJoin(payParams *md.JoinPayReq, paySecretKey string, isPrd bool) (*md.JoinPayResponse, error) { | |||||
payParams.P0Version = "2.1" | |||||
payParams.P4Cur = "1" | |||||
//可注释行: | |||||
//payParams.P1MerchantNo = md.PayTestAccount | |||||
//payParams.P2OrderNo = "95271" | |||||
//payParams.P3Amount = "0.1" | |||||
//payParams.P5ProductName = "测试商品" | |||||
//payParams.P9NotifyUrl = "AB|https://www.baidu.com" | |||||
//--交易类型 | |||||
//payParams.Q1FrpCode = md.Q1FrpCodeWEIXINPAYAPP | |||||
//payParams.Q1FrpCode = md.Q1FrpCodeALIPAY | |||||
//原始微信AppId | |||||
//payParams.Q7AppId = "gh_5a236de9850a" | |||||
//微信AppId | |||||
//payParams.Q7AppId = "wx5bb10bb79cb5da1c" | |||||
//商家报备编号(分微信和支付宝的);支付宝的: | |||||
//payParams.QATradeMerchantNo = md.PayTestAliAccount | |||||
//商家报备编号(分微信和支付宝的);微信的: | |||||
//payParams.QATradeMerchantNo = md.PayTestWeChatAccount | |||||
//不可注释行: | |||||
postData := url.Values{} | |||||
creatHmac(payParams, paySecretKey, &postData) | |||||
zhios_pay_utils.FilePutContents("UniPayByJoin", zhios_pay_utils.SerializeStr(map[string]interface{}{ | |||||
"data": payParams, | |||||
})) | |||||
tr := &http.Transport{ | |||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, | |||||
} | |||||
client := &http.Client{Transport: tr} | |||||
response, err := client.Post(md.JoinPayUrl, "application/x-www-form-urlencoded", strings.NewReader(postData.Encode())) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
defer response.Body.Close() | |||||
body, err := ioutil.ReadAll(response.Body) | |||||
resp := md.JoinPayResponse{} | |||||
zhios_pay_utils.Unserialize(body, &resp) | |||||
return &resp, err | |||||
} | |||||
func creatHmac(payParams *md.JoinPayReq, paySecretKey string, postData *url.Values) { | |||||
hmac := "" | |||||
if payParams.P0Version != "" { | |||||
postData.Add("p0_Version", payParams.P0Version) | |||||
hmac = hmac + payParams.P0Version | |||||
} | |||||
if payParams.P1MerchantNo != "" { | |||||
postData.Add("p1_MerchantNo", payParams.P1MerchantNo) | |||||
hmac = hmac + payParams.P1MerchantNo | |||||
} | |||||
if payParams.P2OrderNo != "" { | |||||
postData.Add("p2_OrderNo", payParams.P2OrderNo) | |||||
hmac = hmac + payParams.P2OrderNo | |||||
} | |||||
if payParams.P3Amount != "" { | |||||
postData.Add("p3_Amount", payParams.P3Amount) | |||||
hmac = hmac + payParams.P3Amount | |||||
} | |||||
if payParams.P4Cur != "" { | |||||
postData.Add("p4_Cur", payParams.P4Cur) | |||||
hmac = hmac + payParams.P4Cur | |||||
} | |||||
if payParams.P5ProductName != "" { | |||||
postData.Add("p5_ProductName", payParams.P5ProductName) | |||||
hmac = hmac + payParams.P5ProductName | |||||
} | |||||
if payParams.P6ProductDesc != "" { | |||||
postData.Add("p6_ProductDesc", payParams.P6ProductDesc) | |||||
hmac = hmac + payParams.P6ProductDesc | |||||
} | |||||
if payParams.P7Mp != "" { | |||||
postData.Add("p7_Mp", payParams.P7Mp) | |||||
hmac = hmac + payParams.P7Mp | |||||
} | |||||
if payParams.P9NotifyUrl != "" { | |||||
postData.Add("p9_NotifyUrl", payParams.P9NotifyUrl) | |||||
hmac = hmac + payParams.P9NotifyUrl | |||||
} | |||||
if payParams.Q1FrpCode != "" { | |||||
postData.Add("q1_FrpCode", payParams.Q1FrpCode) | |||||
hmac = hmac + payParams.Q1FrpCode | |||||
} | |||||
if payParams.Q2MerchantBankCode != "" { | |||||
postData.Add("q2_MerchantBankCode", payParams.Q2MerchantBankCode) | |||||
hmac = hmac + payParams.Q2MerchantBankCode | |||||
} | |||||
if payParams.Q4IsShowPic != "" { | |||||
postData.Add("q4_IsShowPic", payParams.Q4IsShowPic) | |||||
hmac = hmac + payParams.Q4IsShowPic | |||||
} | |||||
if payParams.Q5OpenId != "" { | |||||
postData.Add("q5_OpenId", payParams.Q5OpenId) | |||||
hmac = hmac + payParams.Q5OpenId | |||||
} | |||||
if payParams.Q6AuthCode != "" { | |||||
postData.Add("q6_AuthCode", payParams.Q6AuthCode) | |||||
hmac = hmac + payParams.Q6AuthCode | |||||
} | |||||
if payParams.Q7AppId != "" { | |||||
postData.Add("q7_AppId", payParams.Q7AppId) | |||||
hmac = hmac + payParams.Q7AppId | |||||
} | |||||
if payParams.Q8TerminalNo != "" { | |||||
postData.Add("q8_TerminalNo", payParams.Q8TerminalNo) | |||||
hmac = hmac + payParams.Q8TerminalNo | |||||
} | |||||
if payParams.Q9TransactionModel != "" { | |||||
postData.Add("q9_TransactionModel", payParams.Q9TransactionModel) | |||||
hmac = hmac + payParams.Q9TransactionModel | |||||
} | |||||
if payParams.QATradeMerchantNo != "" { | |||||
postData.Add("qa_TradeMerchantNo", payParams.QATradeMerchantNo) | |||||
hmac = hmac + payParams.QATradeMerchantNo | |||||
} | |||||
if payParams.QBbuyerId != "" { | |||||
postData.Add("qb_buyerId", payParams.QBbuyerId) | |||||
hmac = hmac + payParams.QBbuyerId | |||||
} | |||||
if payParams.QJDJPlan != "" { | |||||
postData.Add("qj_DJPlan", payParams.QJDJPlan) | |||||
hmac = hmac + payParams.QJDJPlan | |||||
} | |||||
if payParams.QKDisablePayModel != "" { | |||||
postData.Add("qk_DisablePayModel", payParams.QKDisablePayModel) | |||||
hmac = hmac + payParams.QKDisablePayModel | |||||
} | |||||
if payParams.QLTerminalIp != "" { | |||||
postData.Add("ql_TerminalIp", payParams.QLTerminalIp) | |||||
hmac = hmac + payParams.QLTerminalIp | |||||
} | |||||
payParams.Hmac = zhios_pay_utils.Md5(hmac + paySecretKey) | |||||
postData.Add("hmac", payParams.Hmac) | |||||
} |
@@ -0,0 +1,203 @@ | |||||
package pay | |||||
import ( | |||||
zhios_pay_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/utils" | |||||
"crypto" | |||||
"crypto/md5" | |||||
"crypto/rand" | |||||
"crypto/rsa" | |||||
"crypto/x509" | |||||
"encoding/base64" | |||||
"encoding/json" | |||||
"encoding/pem" | |||||
"errors" | |||||
"fmt" | |||||
"github.com/forgoer/openssl" | |||||
"github.com/syyongx/php2go" | |||||
"golang.org/x/crypto/ssh" | |||||
mathRand "math/rand" | |||||
"sort" | |||||
"time" | |||||
) | |||||
/*** | |||||
method fastPay.agreement.signSms | |||||
data 请求交易的数据,具体参数见下表 | |||||
mch_no 商户在支付平台系统的唯一身份标识。(注:商户编号和商户密钥在汇聚商户后台获取) | |||||
pub_key 公钥 | |||||
pri_key 私钥 | |||||
*/ | |||||
func GetSms(pubKey, platformKey, priKey, mch_no string, args map[string]string) (string, error) { | |||||
param := map[string]string{ | |||||
"mch_no": mch_no, | |||||
} | |||||
param["sec_key"] = randomString(16) | |||||
args["payer_name"] = AesEncryptECB([]byte(args["payer_name"]), []byte(param["sec_key"])) | |||||
args["id_no"] = AesEncryptECB([]byte(args["id_no"]), []byte(param["sec_key"])) | |||||
args["bank_card_no"] = AesEncryptECB([]byte(args["bank_card_no"]), []byte(param["sec_key"])) | |||||
args["mobile_no"] = AesEncryptECB([]byte(args["mobile_no"]), []byte(param["sec_key"])) | |||||
args["expire_date"] = AesEncryptECB([]byte(args["expire_date"]), []byte(param["sec_key"])) | |||||
data, _ := json.Marshal(args) | |||||
param["data"] = string(data) | |||||
send, err := Send("fastPay.agreement.signSms", platformKey, priKey, param) | |||||
return send, err | |||||
} | |||||
func SignContract(pubKey, platformKey, priKey, mch_no string, args map[string]string) (string, error) { | |||||
param := map[string]string{ | |||||
"mch_no": mch_no, | |||||
} | |||||
param["sec_key"] = randomString(16) | |||||
data, _ := json.Marshal(args) | |||||
param["data"] = string(data) | |||||
send, err := Send("fastPay.agreement.smsSign", platformKey, priKey, param) | |||||
return send, err | |||||
} | |||||
func UnSignContract(pubKey, platformKey, priKey, mch_no string, args map[string]string) (string, error) { | |||||
param := map[string]string{ | |||||
"mch_no": mch_no, | |||||
} | |||||
param["sec_key"] = randomString(16) | |||||
args["sign_no"] = AesEncryptECB([]byte(args["sign_no"]), []byte(param["sec_key"])) | |||||
data, _ := json.Marshal(args) | |||||
param["data"] = string(data) | |||||
send, err := Send("fastPay.agreement.unSign", platformKey, priKey, param) | |||||
return send, err | |||||
} | |||||
func GetPaySms(pubKey, platformKey, priKey, mchNo string, args map[string]string) (string, error) { | |||||
param := map[string]string{ | |||||
"mch_no": mchNo, | |||||
} | |||||
param["sec_key"] = randomString(16) | |||||
args["sign_no"] = AesEncryptECB([]byte(args["sign_no"]), []byte(param["sec_key"])) | |||||
data, _ := json.Marshal(args) | |||||
param["data"] = string(data) | |||||
send, err := Send("fastPay.agreement.paySms", platformKey, priKey, param) | |||||
return send, err | |||||
} | |||||
func PayDoing(pubKey, platformKey, priKey, mchNo string, args map[string]string) (string, error) { | |||||
param := map[string]string{ | |||||
"mch_no": mchNo, | |||||
} | |||||
param["sec_key"] = randomString(16) | |||||
data, _ := json.Marshal(args) | |||||
param["data"] = string(data) | |||||
send, err := Send("fastPay.agreement.smsPay", platformKey, priKey, param) | |||||
return send, err | |||||
} | |||||
func Send(method, platformKey, priKey string, param map[string]string) (string, error) { | |||||
//priKey = strings.ReplaceAll(priKey, "+", "\n") | |||||
//priKey = "-----BEGIN PRIVATE KEY-----\n" + priKey + "\n-----END PRIVATE KEY-----" | |||||
router := "https://api.joinpay.com/fastpay" | |||||
param["method"] = method | |||||
param["version"] = "1.0" | |||||
param["rand_str"] = randomString(32) | |||||
param["sign_type"] = "2" | |||||
sign, err := GetSign(priKey, param) | |||||
if err != nil { | |||||
return "", err | |||||
} | |||||
param["sign"] = sign | |||||
if param["sec_key"] != "" { //rsa加密 | |||||
encrypts, err := RsaEncrypts([]byte(param["sec_key"]), []byte(platformKey)) | |||||
if err != nil { | |||||
return "", err | |||||
} | |||||
param["sec_key"] = string(encrypts) | |||||
} | |||||
post, err := zhios_pay_utils.CurlPost(router, param, nil) | |||||
fmt.Println(string(post)) | |||||
fmt.Println(err) | |||||
return string(post), nil | |||||
} | |||||
func GetSign(privateKeyStr string, param map[string]string) (string, error) { | |||||
privateKey, err := ssh.ParseRawPrivateKey([]byte(privateKeyStr)) | |||||
if err != nil { | |||||
return "", err | |||||
} | |||||
keys := make([]string, 0, len(param)) | |||||
for k := range param { | |||||
keys = append(keys, k) | |||||
} | |||||
sort.Strings(keys) | |||||
data := "" | |||||
for _, v := range keys { | |||||
if v == "sign" || v == "sec_key" { | |||||
continue | |||||
} | |||||
if data != "" { | |||||
data += "&" + v + "=" + param[v] | |||||
} else { | |||||
data += v + "=" + param[v] | |||||
} | |||||
} | |||||
fmt.Println("md5 before2:>>>>", data) | |||||
hashMd5 := md5.Sum([]byte(data)) | |||||
fmt.Println("md5 before2:>>>>", hashMd5) | |||||
hashed := hashMd5[:] | |||||
fmt.Println("hashed:>>>>", hashed) | |||||
fmt.Println("hashed_string:>>>>", string(hashed)) | |||||
sign, err := rsa.SignPKCS1v15(rand.Reader, privateKey.(*rsa.PrivateKey), crypto.MD5, hashed) | |||||
if err != nil { | |||||
return "", err | |||||
} | |||||
return base64.StdEncoding.EncodeToString(sign), nil | |||||
} | |||||
// 公钥加密 | |||||
func RsaEncrypts(data, keyBytes []byte) (string, error) { | |||||
var ciphertext string | |||||
//解密pem格式的公钥 | |||||
block, _ := pem.Decode(keyBytes) | |||||
if block == nil { | |||||
return ciphertext, errors.New("公钥错误") | |||||
} | |||||
// 解析公钥 | |||||
pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes) | |||||
if err != nil { | |||||
return ciphertext, err | |||||
} | |||||
// 类型断言 | |||||
pub := pubInterface.(*rsa.PublicKey) | |||||
//加密 | |||||
ciphertext1, err := rsa.EncryptPKCS1v15(rand.Reader, pub, data) | |||||
if err != nil { | |||||
return ciphertext, err | |||||
} | |||||
return base64.StdEncoding.EncodeToString(ciphertext1), nil | |||||
} | |||||
func randomString(ln int) string { | |||||
letters := []rune("1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") | |||||
b := make([]rune, ln) | |||||
r := mathRand.New(mathRand.NewSource(time.Now().UnixNano())) | |||||
for i := range b { | |||||
b[i] = letters[r.Intn(len(letters))] | |||||
} | |||||
return string(b) | |||||
} | |||||
func AesEncryptECB(origData []byte, key []byte) string { | |||||
str, _ := openssl.AesECBEncrypt(origData, key, openssl.PKCS7_PADDING) | |||||
value := php2go.Base64Encode(string(str)) | |||||
return value | |||||
} | |||||
func generateKey(key []byte) (genKey []byte) { | |||||
genKey = make([]byte, 16) | |||||
copy(genKey, key) | |||||
for i := 16; i < len(key); { | |||||
for j := 0; j < 16 && i < len(key); j, i = j+1, i+1 { | |||||
genKey[j] ^= key[i] | |||||
} | |||||
} | |||||
return genKey | |||||
} |
@@ -0,0 +1,208 @@ | |||||
package pay | |||||
import ( | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/md" | |||||
zhios_pay_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/utils" | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/utils/cache" | |||||
"encoding/json" | |||||
"fmt" | |||||
"github.com/gin-gonic/gin" | |||||
"github.com/pkg/errors" | |||||
"time" | |||||
) | |||||
//酷点 - 支付宝APP支付 | |||||
func AliAppPayByKuDian(payParams *md.KuDianAliAppPayParams, isPrd bool) (string, error) { | |||||
var url string | |||||
if isPrd == true { | |||||
url = md.ZHIOS_PAY_URL_PRD + "/kuDianPay/Pay/aliAppPay" | |||||
} else { | |||||
url = md.ZHIOS_PAY_URL_DEV + "/kuDianPay/Pay/aliAppPay" | |||||
} | |||||
zhios_pay_utils.FilePutContents("AliAppPayByKuDian", zhios_pay_utils.SerializeStr(map[string]interface{}{ | |||||
"data": payParams, | |||||
})) | |||||
bytes, err := zhios_pay_utils.CurlPost(url, zhios_pay_utils.Serialize(payParams), nil) | |||||
if err != nil { | |||||
return "", err | |||||
} | |||||
var result struct { | |||||
Code int `json:"code"` | |||||
Msg string `json:"msg"` | |||||
Data map[string]string `json:"data"` | |||||
} | |||||
zhios_pay_utils.FilePutContents("AliAppPayByKuDian", zhios_pay_utils.SerializeStr(result)) | |||||
err = json.Unmarshal(bytes, &result) | |||||
if err != nil { | |||||
return "", err | |||||
} | |||||
if result.Code != 0 { | |||||
return "", errors.New(result.Msg) | |||||
} | |||||
zhios_pay_utils.FilePutContents("AliAppPayByKuDian", zhios_pay_utils.SerializeStr(result.Data["data"])) | |||||
return result.Data["url"], nil | |||||
} | |||||
// 酷点 - 支付宝JsAPi支付 | |||||
func AliJsApiPayByKuDian(payParams *md.KuDianAliAppPayParams, isPrd bool) (string, error) { | |||||
var url string | |||||
if isPrd == true { | |||||
url = md.ZHIOS_PAY_URL_PRD + "/kuDianPay/Pay/aliJsApiPay" | |||||
} else { | |||||
url = md.ZHIOS_PAY_URL_DEV + "/kuDianPay/Pay/aliJsApiPay" | |||||
} | |||||
zhios_pay_utils.FilePutContents("AliAppPayByKuDian", zhios_pay_utils.SerializeStr(map[string]interface{}{ | |||||
"data": payParams, | |||||
})) | |||||
bytes, err := zhios_pay_utils.CurlPost(url, zhios_pay_utils.Serialize(payParams), nil) | |||||
if err != nil { | |||||
return "", err | |||||
} | |||||
var result struct { | |||||
Code int `json:"code"` | |||||
Msg string `json:"msg"` | |||||
Data map[string]string `json:"data"` | |||||
} | |||||
zhios_pay_utils.FilePutContents("AliAppPayByKuDian", zhios_pay_utils.SerializeStr(result)) | |||||
err = json.Unmarshal(bytes, &result) | |||||
if err != nil { | |||||
return "", err | |||||
} | |||||
if result.Code != 0 { | |||||
return "", errors.New(result.Msg) | |||||
} | |||||
zhios_pay_utils.FilePutContents("AliAppPayByKuDian", zhios_pay_utils.SerializeStr(result.Data["data"])) | |||||
return result.Data["trade_no"], nil | |||||
} | |||||
// 酷点 - 微信小程序支付 | |||||
func WxAppletPayByKuDian(payParams *md.KuDianWxPayParams, isPrd bool) (interface{}, error) { | |||||
var url string | |||||
if isPrd == true { | |||||
url = md.ZHIOS_PAY_URL_PRD + "/kuDianPay/Pay/wxJsAPi" | |||||
} else { | |||||
url = md.ZHIOS_PAY_URL_DEV + "/kuDianPay/Pay/wxJsAPi" | |||||
} | |||||
zhios_pay_utils.FilePutContents("WxAppletPayByKuDian", zhios_pay_utils.SerializeStr(map[string]interface{}{ | |||||
"data": payParams, | |||||
})) | |||||
bytes, err := zhios_pay_utils.CurlPost(url, zhios_pay_utils.Serialize(payParams), nil) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
var result struct { | |||||
Code int `json:"code"` | |||||
Msg string `json:"msg"` | |||||
Data map[string]interface{} `json:"data"` | |||||
} | |||||
zhios_pay_utils.FilePutContents("WxAppletPayByKuDian", zhios_pay_utils.SerializeStr(result)) | |||||
err = json.Unmarshal(bytes, &result) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
if result.Code != 0 { | |||||
return nil, errors.New(result.Msg) | |||||
} | |||||
zhios_pay_utils.FilePutContents("WxAppletPayByKuDian", zhios_pay_utils.SerializeStr(result.Data["data"])) | |||||
return result.Data["data"], nil | |||||
} | |||||
// 酷点 - 检查返回参数 | |||||
func CheckAllCallbackParamsByKuDian(c *gin.Context) (md.KuDianPayCallbackResp, error) { | |||||
var dataWx md.KuDianPayCallback | |||||
var dataPay md.KuDianPayCallbackResp | |||||
if err := c.ShouldBindJSON(&dataWx); err != nil { | |||||
fmt.Println(">>>>>>>>>CheckAllCallbackParamsByKuDian>>>>>>>>>>>>>>", err) | |||||
return dataPay, err | |||||
} | |||||
if err := json.Unmarshal([]byte(dataWx.Result), &dataPay); err != nil { | |||||
fmt.Println(">>>>>>>>>CheckAllCallbackParamsByKuDian>>>>>>>>>>>>>>", err) | |||||
return dataPay, err | |||||
} | |||||
zhios_pay_utils.FilePutContents("CheckAllCallbackParamsByKuDian", zhios_pay_utils.SerializeStr(dataPay)) | |||||
return dataPay, nil | |||||
} | |||||
/** | |||||
* MakePayParamsByKuDian(构造酷点支付参数[APP跳转小程序支付]) | |||||
* @param url string 请求URL | |||||
* @param uniqueIdentifierOne string 标识占位符-第一个 (示例: 如 masterId) | |||||
* @param uniqueIdentifierTwo string 标识占位符-第二个 (示例: 如 uid) | |||||
* @param appletOriginalId string 小程序原始id | |||||
* @param queryParams string url上参数(示例: "&ord_id=123131313434343&state_zh=待支付&total=6.66") | |||||
* @param params map 请求参数 | |||||
*/ | |||||
func MakePayParamsByKuDian(url, uniqueIdentifierOne, uniqueIdentifierTwo, appletOriginalId, queryParams string, params map[string]interface{}) (map[string]string, error) { | |||||
if params == nil { | |||||
return nil, errors.New("参数解析错误") | |||||
} | |||||
data := md.MakePayParamsByKuDian{ | |||||
RequestUrl: url, | |||||
Params: params, | |||||
} | |||||
redisKey := fmt.Sprintf(md.WX_APP_TO_APPLET_PAY_UNIQUE_IDENTIFIER, uniqueIdentifierOne, uniqueIdentifierTwo, zhios_pay_utils.AnyToString(time.Now().UnixNano())) | |||||
cache.SetJson(redisKey, data, md.WX_APP_TO_APPLET_PAY_UNIQUE_IDENTIFIER_CACHE_TIME) | |||||
var r = map[string]string{} | |||||
r["use_pay_method"] = "third_party" | |||||
r["applet_original_id"] = appletOriginalId | |||||
r["wx_url"] = md.WX_APPLET_PAY_URL + redisKey + "&" + queryParams | |||||
return r, nil | |||||
} | |||||
func KuDianAlipayConfig(req, payConfig map[string]string) (*md.KuDianAliAppPayParams, error) { | |||||
var payParams = &md.KuDianAliAppPayParams{ | |||||
OutTradeNo: payConfig["ord_id"], | |||||
NotifyUrl: payConfig["notify_url"], | |||||
Description: payConfig["subject"], | |||||
TotalAmount: payConfig["amount"], | |||||
Attach: req["mid"], | |||||
MustParams: struct { | |||||
ShopId string `json:"shop_id"` | |||||
KuDianMchId string `json:"ku_dian_mch_id"` | |||||
KuDianSecretKey string `json:"ku_dian_secret_key"` | |||||
}{ | |||||
ShopId: payConfig["pay_third_party_id"], | |||||
KuDianMchId: req["ku_dian_mch_id"], | |||||
KuDianSecretKey: req["ku_dian_secret_key"], | |||||
}, | |||||
} | |||||
if payConfig["trade_type"] != "" { | |||||
payParams.TradeType = payConfig["trade_type"] | |||||
} | |||||
if payConfig["wap_mame"] != "" { | |||||
payParams.SenceInfo.WapName = payConfig["wap_mame"] | |||||
} | |||||
if payConfig["return_url"] != "" { | |||||
payParams.SenceInfo.ReturnUrl = payConfig["return_url"] | |||||
} | |||||
if payConfig["wap_url"] != "" { | |||||
payParams.SenceInfo.WapUrl = payConfig["wap_url"] | |||||
} | |||||
if payConfig["trade_type"] != "" { | |||||
payParams.TradeType = payConfig["trade_type"] | |||||
} | |||||
return payParams, nil | |||||
} | |||||
func KuDianWxConfig(req, payConfig map[string]string) (*md.KuDianWxPayParams, error) { | |||||
var payParams = &md.KuDianWxPayParams{ | |||||
OutTradeNo: payConfig["ord_id"], | |||||
NotifyUrl: payConfig["notify_url"], | |||||
Description: payConfig["subject"], | |||||
TotalAmount: payConfig["amount"], | |||||
AppId: req["wx_app_id"], | |||||
OpenId: req["wx_open_id"], | |||||
Attach: req["mid"], | |||||
MustParams: struct { | |||||
ShopId string `json:"shop_id"` | |||||
KuDianMchId string `json:"ku_dian_mch_id"` | |||||
KuDianSecretKey string `json:"ku_dian_secret_key"` | |||||
}{ | |||||
ShopId: payConfig["pay_third_party_id"], | |||||
KuDianMchId: req["ku_dian_mch_id"], | |||||
KuDianSecretKey: req["ku_dian_secret_key"], | |||||
}, | |||||
} | |||||
return payParams, nil | |||||
} |
@@ -0,0 +1,117 @@ | |||||
package pay | |||||
import ( | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/lib/local_alipay" | |||||
local_wxpay "code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/lib/local_wechat" | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/md" | |||||
zhios_pay_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/utils" | |||||
"fmt" | |||||
"github.com/pkg/errors" | |||||
) | |||||
/** | |||||
自有支付 支付宝 | |||||
req 传参 | |||||
ord_id //订单号 | |||||
amount //总金额 | |||||
subject //描述 | |||||
order_type //订单类型 | |||||
notify_url //自有支付回调地址 | |||||
platform //平台 由前端header传来的 | |||||
pay_app_cert_sn //应用公钥证书 值 | |||||
pay_alipayrsa_public_key //支付宝公钥证书 值 | |||||
pay_ali_use_type //支付宝选择版本 0旧版 1新版 | |||||
private_key //支付宝商户私钥 数据库字段 旧版 pay_ali_private_key 新版 pay_ali_new_private_key | |||||
app_id //支付宝appId 数据库字段 旧版 pay_ali_app_id 新版 pay_ali_new_app_id | |||||
rsa //支付宝密钥长度 旧版使用 数据库字段 pay_ali_key_len_type | |||||
pkcs //支付宝密钥格式 旧版使用 数据库字段 pay_ali_key_format_type | |||||
page_url //支付宝wap支付成功跳转链接 | |||||
*/ | |||||
func AlipayCofing(args map[string]string) *md.PayData { | |||||
var paySet = &md.PayData{ | |||||
PayAppCertSn: args["pay_app_cert_sn"], | |||||
PayAlipayrsaPublicKey: args["pay_alipayrsa_public_key"], | |||||
PayAliUseType: args["pay_ali_use_type"], | |||||
} | |||||
return paySet | |||||
} | |||||
func AlipayApp(args map[string]string) (string, error) { | |||||
paySet := AlipayCofing(args) | |||||
if args["private_key"] == "" || args["app_id"] == "" { | |||||
return "", errors.New("请在后台正确配置支付宝") | |||||
} | |||||
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) | |||||
if err != nil { | |||||
fmt.Println("支付宝错误日志") | |||||
fmt.Println(param) | |||||
fmt.Println(err) | |||||
return "", errors.New("支付宝订单创建失败") | |||||
} | |||||
return zhios_pay_utils.AnyToString(param), nil | |||||
} | |||||
func AlipayWap(args map[string]string) (string, error) { | |||||
paySet := AlipayCofing(args) | |||||
if args["private_key"] == "" || args["app_id"] == "" { | |||||
return "", errors.New("请在后台正确配置支付宝") | |||||
} | |||||
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) | |||||
if err != nil { | |||||
fmt.Println("支付宝错误日志") | |||||
fmt.Println(param) | |||||
fmt.Println(err) | |||||
return "", errors.New("支付宝订单创建失败") | |||||
} | |||||
return zhios_pay_utils.AnyToString(param), nil | |||||
} | |||||
func AlipayApplet(args map[string]string) (string, error) { | |||||
paySet := AlipayCofing(args) | |||||
if args["private_key"] == "" || args["app_id"] == "" { | |||||
return "", errors.New("请在后台正确配置支付宝") | |||||
} | |||||
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) | |||||
if err != nil { | |||||
fmt.Println("支付宝错误日志") | |||||
fmt.Println(param) | |||||
fmt.Println(err) | |||||
return "", errors.New("支付宝订单创建失败") | |||||
} | |||||
return zhios_pay_utils.AnyToString(param), nil | |||||
} | |||||
// 微信app支付v2 | |||||
func WxAppPay(params map[string]string) (map[string]string, error) { | |||||
client := local_wxpay.NewClient(params["pay_wx_appid"], params["pay_wx_mch_id"], params["pay_wx_api_key"], true) | |||||
r, err := local_wxpay.TradeAppPay(client, params["subject"], params["ord_id"], params["amount"], params["notify_url"]) | |||||
return r, err | |||||
} | |||||
// 微信H5支付v2 | |||||
func WxH5Pay(params map[string]string) (map[string]string, error) { | |||||
client := local_wxpay.NewClient(params["pay_wx_appid"], params["pay_wx_mch_id"], params["pay_wx_api_key"], true) | |||||
r, err := local_wxpay.TradeH5Pay(client, params["subject"], params["ord_id"], params["amount"], params["notify_url"]) | |||||
return r, err | |||||
} | |||||
// 微信小程序v2 | |||||
func WxMiniProgPay(params map[string]string) (map[string]string, error) { | |||||
client := local_wxpay.NewClient(params["pay_wx_appid"], params["pay_wx_mch_id"], params["pay_wx_api_key"], true) | |||||
if params["third_party_wechat_openid"] == "" { | |||||
return nil, errors.New("请先授权微信") | |||||
} | |||||
r, err := local_wxpay.TradeMiniProgPay(client, params["subject"], params["ord_id"], params["amount"], params["notify_url"], params["third_party_wechat_openid"]) | |||||
return r, err | |||||
} | |||||
// 微信JSAPI支付 | |||||
func WxAppJSAPIPay(params map[string]string) (map[string]string, error) { | |||||
client := local_wxpay.NewClient(params["pay_wx_appid"], params["pay_wx_mch_id"], params["pay_wx_api_key"], true) | |||||
if params["third_party_wechat_openid"] == "" { | |||||
return nil, errors.New("请先授权微信") | |||||
} | |||||
r, err := local_wxpay.TradeJSAPIPay(client, params["subject"], params["ord_id"], params["amount"], params["notify_url"], params["third_party_wechat_openid"]) | |||||
return r, err | |||||
} |
@@ -0,0 +1,36 @@ | |||||
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" | |||||
"errors" | |||||
) | |||||
//支付渠道获取 | |||||
func JudgePayChannel(mid, paySceneIdentifier string) (model.PayChannelMaster, error) { | |||||
var payChannelMaster = model.PayChannelMaster{} | |||||
res, err := db.Db.Where("app_id = ?", mid).And("identifier = ?", paySceneIdentifier).Get(&payChannelMaster) | |||||
if err != nil { | |||||
return payChannelMaster, err | |||||
} | |||||
if !res { | |||||
return payChannelMaster, errors.New("查询数据失败") | |||||
} | |||||
if payChannelMaster.ChannelId == 0 { | |||||
return payChannelMaster, errors.New("请联系服务商绑定支付渠道") | |||||
} | |||||
return payChannelMaster, nil | |||||
} | |||||
//支付方式获取 | |||||
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 !res { | |||||
return payChannel, errors.New("查询数据失败") | |||||
} | |||||
return payChannel, nil | |||||
} |
@@ -0,0 +1,41 @@ | |||||
package pay | |||||
import ( | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/lib/icbc" | |||||
"fmt" | |||||
"testing" | |||||
"time" | |||||
) | |||||
func TestIcbc(t1 *testing.T) { | |||||
t := time.Now() | |||||
stime := time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), 0, 0, 0, t.Location()).Format("2006-1-02 15:04:05") | |||||
fmt.Println(stime) | |||||
} | |||||
func TestIcbcShow(t *testing.T) { | |||||
param := map[string]string{ | |||||
"name": "象揪巒", //客户姓名 | |||||
"mobile": "66732397", //手机号码 | |||||
"mobileArea": "853", //手机国际代码 853、852、86 *- | |||||
"optFlag": "1", // | |||||
"otp": "921507", | |||||
"cardNo": "5305068200048153", | |||||
"cardType": "C", | |||||
} | |||||
integral, err := icbc.ShowIntegral("011904180095001", "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDFaVbTHQF1WNlz\n0ZFgioxApDFBtwlVsoJ8Ov6WdfKLO5aikZjZJNcR2dhtgFSCvpLx/L6p/v+Jv6kl\nnJ6ZGdH1e8Yy7zcrOZd20KNawfsVYfbMFZ0yqygAfif96Uw0k2qwpXLZ6KSlV/PT\nA900571+yv2o4mu7fyHTimojwcrfVIKu+6kgidlHd7usciEvvcplV7xafZmzpG+h\n+qlF8iQ9hxou85dvF9HBox8ZkFay0xMy/QqXRYxzIaZgfAF0io/tuthol+6WUZ/M\nLzzkAwn9E4/YM1GE3qzXXE5TZmyEOV/pE+VKzfhj5sMrO1EnzLAsWGWXNd4ISKsV\noeGmtnFpAgMBAAECggEBAL7fOQnqc6yIecgYCt4zV6RwnQeS+D2Z2bxQNjQ4Wojw\n1pbc+9KIYkJqRW6qtAuuQ9ohrCAl4+HGJL+vDVGuu2kFgf9heBAdISvKUwe/owtr\nqvpss494GbdQQ06nsfzrxC6D6eZzoC4WOjP++nTa1Eex8UzHj668H3Kv1Cw6hfMj\nymxsjvV6X6a0Dp2IXiqzDlWRYPzrj3TLxz1AOLyNJP1VaVHaizTL45AT5+9dtkNc\n0kF4KTCfYLMZK9BjlgtgL4dTVun/MVuVeYb2xYUUdJ/lZ501xnwc/OG1zRPmmnIe\nPC8d6kr1VqvODL2xsPZFjha+oIhkVQPvOyjqfNLtBRkCgYEA49TsSdj8wC8iGXs7\nhXTCylH28Fomx1DITujYabhYtfmDD0IIBUU3drOtl8VHDby7wX7pDgCT8JEvbPLQ\n3FlxEgw6YWfhkOo/64MMK6hE9f0VQh2vnWL3S/5zpll5r2ZHkib4SkjWSC2q9qNA\n30lsdCAIJk3xuv6plv+9Q/0M6WsCgYEA3dGWk/o2MU589j4UspWjPSBAMyI/thzX\nYGn73jvk+RAPOxwXkAA0A9azqwvE8YfoQSNe8128OpSvcnmreXc4HphLCeR/Fbls\nDxI/f8C3Qn/LmGK8P795kQaZRDzHw1gBRpshGjEuQy7m7psR14o8gM7cof73d71W\nMzvGEPeioXsCgYEAmQpzOtv2VEaU+lbsUaZwHqXWYfe86tBZ7Z+9l66McJjNr9ck\nOh4XtrXVmwiaxSCNYtoURSKgPc3Y9K9bG1k9eeka4kl8nPKtw08BQuZChJbfy8zT\nJb7P7Y1cNFcQRYFH/GYPDR9GSa/bktdxAUx5/Ltznf9nrLvaQaNCiMQYA0cCgYA0\nomoHnQTE+GngiGSi56z/cSqqt4LEtN1KbHuuhSnVC8GBdi4FJncxPf4OJVHLOkOb\nSUhbmS9z+xoSoNpWjTqMz/hmVYjuX7xLazoUF2ek6Ownsa4XtlijWByAO6fvK2/K\nED9vlqHyoSt9DODT82Jd8jgaXITeLdhJefKKzigSBwKBgH7miTS77XP1n5nMMlbp\nNVPWF40CB7bt4VBslrJA/u0dmEfEKAhRBh6Cg1+l88Q4o/eWR/RjzqTR1Q3XaS3d\nQcwMzAiA+4aqtywStD8v6iRsA/p9A5QYjPsKze3B6urNgzGz8hMqsRLL3NyeYS+3\nCtK4zuKHZKPLivbxALqfJKkH\n-----END PRIVATE KEY-----", param) | |||||
fmt.Println(integral) | |||||
fmt.Println(err) | |||||
} | |||||
func TestIcbcDeduct(t *testing.T) { | |||||
param := map[string]string{ | |||||
"name": "象揪巒", //客户姓名 | |||||
"mobile": "66732397", //手机号码 | |||||
"mobileArea": "853", //手机国际代码 853、852、86 *- | |||||
"optFlag": "1", // | |||||
"otp": "179305", | |||||
"cardNo": "5305068200048153", | |||||
"cardType": "C", | |||||
"cusCredit": "1", | |||||
} | |||||
icbc.DeductIntegral("10000000000000203505", "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCZBYj5SOjewCZ+\nGx2dAj/Lvp/TlDeVseoUl7V1X4K0jzAyo8kwYzMip5SPKj64zbkydyxqUOdEI5vk\nhfUtnZcb8PI7+lhgoBc6nv7Po+pZuP+0OkxhRvgbL+b0O/e2I3dQJOmfafQNLU1x\n3Nf3uqHtLbFuzrGKmG7zrP6to+tqY7+a+mq/o59ZSkDTWFH094jsrLtngu85hKSf\n0N7moxak+LWJF0Czqd4xI8kXfIW5O3t/jaWJPomm1fZSjlbf4VvPXVahkLOV4+Tk\naTlbMrc7ouGPVTGQo0UtWX4CVy30/QvDMfUuhTSw7S/xMO+u7r4wWrNxkEjxIu/m\nzwDFC3Q1AgMBAAECggEBAI2M4Tg4ibIEbZcZ3jrvabQ/kflWklSXT8Hc1K7Ou0nr\natptSB7KpPLZskL9AShSpY5ZT2zJiiUuC3dJs5OOw8nFCU8SNc6sSNChY3UQXCjD\nSUae5yjVTY1YsnwbPPnvtN8eRj6A9JF75rf9Yop2ynNsYUXvTay3dWtUDAL0L0Ep\nsyiU7tcbQ2NjpgVQhTpoP5cPF1ao7kwD2Ky5KmicLSjU7eLUqSvvV+O6Dw8Y5HaA\n96b57zW0J7WL7Jdo4XrfPqJ8zpQP+o7hjAulekNfOtep33TsR4MO8f9nphSUgCb0\nvlJw94nwfzvKfYGaGxRulAr4FXNBSab+H2cYsP6v88ECgYEA4cyBV+XYqEDWOWQS\nxn+zArrfUpk4J4jRnDFyAUdohhC1IKvGjhACmCg3+n8g857+HPixjGdGg0ukh2da\npHJ9FbmEmz+te/fnA0IkQTweUHQP9pJGR3DLn0WQzqBK2Ifp1cQAGWOyFRChqPb4\nemIeL80YubYW8Mgl9+EWF5Tw4G8CgYEArX0Xetk1rAmgg5RWMESh2v2Dbv9XqfDx\nk9AyCmjP21nyizzXCCw7W5MmGl0Av+nXSa78b2nXvXsz/d6/PyW0BtaO8pQX/ZWg\nVxJrUhRMA6JTg0ho/g793jF0nMBSpdivDwy1529PId8WpyeGGm7CMmSOy0SZuHiu\nTB4UJ9sr/5sCgYEAwjOPszeeReet2CJ3+31tSxXKaVIad/Y2wa3NLchq35OTFQxo\nc1rVwGQm8S1R+/Zs/CHxIp7YiKgQU9zSmslVR9ANezEjVpH6gFTstaatM+j+qcVY\n6KzclJnSV8SJWv5aYV53715Nlcx+HcJ8J1YoPFibwW4ZAPJDpZRT2ehh28UCgYBv\nBeKEx4RFeFO0YLVXZlR+JEZ8PgQHyR2QDmg3qSN7qHBDEsg5frTtwZlcva/BIRCZ\nItaBJibeJ336NRYGf0ZaKeyI6KUBr0NQ9YSgnkSW/saa+TfXfhiQnO2XcqxxXd2J\nsASo+t6VOoLwraZwO/ki7p9DDb+iAfjo1UboyqW5iQKBgQCzXyhRfacrW0kgmEp6\nCWR0oQgD54aXj1JjUkEe+gE6E7LY9Cl+yAdHBYmyCzS014jNX93+Bpg1XhoR1knO\n78A8Q7sPCrP908ycKul668daDXHp55CGpScxsx3z9jJs5ori+ZncSDAnHf0T0OjX\n+nDJPGN/gp9/WqApT1L4SLlDxg==\n-----END PRIVATE KEY-----", param) | |||||
} |
@@ -0,0 +1 @@ | |||||
[xorm] [info] 2022/07/07 10:46:11.171900 [SQL] SELECT `id`, `channel_id`, `identifier`, `master_id`, `master_nickname`, `third_party_id`, `master_app_name`, `app_id` FROM `pay_channel_master` WHERE (app_id = ?) AND (identifier = ?) LIMIT 1 [35618318 mall] - 26.2547ms |
@@ -0,0 +1,11 @@ | |||||
package test | |||||
import ( | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/md" | |||||
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/pay" | |||||
"testing" | |||||
) | |||||
func TestUniPayByJoin(t *testing.T) { | |||||
pay.UniPayByJoin(&md.JoinPayReq{},"a12ceace4cc5423eae32b4626adf56c9" ,false) | |||||
} |
@@ -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) | |||||
} |
@@ -0,0 +1,403 @@ | |||||
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), | |||||
) | |||||
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 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)) | |||||
} | |||||
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 | |||||
} |
@@ -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 | |||||
} |
@@ -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 | |||||
} |
@@ -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 | |||||
} |
@@ -0,0 +1,366 @@ | |||||
package zhios_pay_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 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 | |||||
} |
@@ -0,0 +1,209 @@ | |||||
package zhios_pay_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) | |||||
} |
@@ -0,0 +1,22 @@ | |||||
package zhios_pay_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() | |||||
} |
@@ -0,0 +1,245 @@ | |||||
package zhios_pay_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 | |||||
} |
@@ -0,0 +1,105 @@ | |||||
package zhios_pay_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 | |||||
} |
@@ -0,0 +1,192 @@ | |||||
package zhios_pay_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) | |||||
} |
@@ -0,0 +1,12 @@ | |||||
package zhios_pay_utils | |||||
import ( | |||||
"crypto/md5" | |||||
"encoding/hex" | |||||
) | |||||
func Md5(str string) string { | |||||
h := md5.New() | |||||
h.Write([]byte(str)) | |||||
return hex.EncodeToString(h.Sum(nil)) | |||||
} |
@@ -0,0 +1,23 @@ | |||||
package zhios_pay_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)) | |||||
} |