Kaynağa Gözat

add Reverse:for v1.2.1 自有微信支付

tags/v1.2.1
huangjuajun 2 yıl önce
ebeveyn
işleme
51a5e056db
9 değiştirilmiş dosya ile 1031 ekleme ve 6 silme
  1. +3
    -1
      go.mod
  2. +303
    -0
      lib/local_wechat/api.go
  3. +35
    -4
      md/platform.go
  4. +35
    -0
      md/wxpay.go
  5. +1
    -1
      pay/init.go
  6. +112
    -0
      pay/pay_by_own.go
  7. +245
    -0
      utils/logx/log.go
  8. +105
    -0
      utils/logx/output.go
  9. +192
    -0
      utils/logx/sugar.go

+ 3
- 1
go.mod Dosyayı Görüntüle

@@ -6,7 +6,9 @@ require (
github.com/gin-gonic/gin v1.8.0
github.com/go-sql-driver/mysql v1.6.0
github.com/iGoogle-ink/gopay v1.5.36
github.com/pkg/errors v0.8.1
github.com/iGoogle-ink/gotil v1.0.20
github.com/pkg/errors v0.9.1
github.com/shopspring/decimal v1.2.0
xorm.io/builder v0.3.10 // indirect
xorm.io/xorm v1.3.0
)

+ 303
- 0
lib/local_wechat/api.go Dosyayı Görüntüle

@@ -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
}

+ 35
- 4
md/platform.go Dosyayı Görüntüle

@@ -2,8 +2,39 @@ package md

const (
/*********** DEVICE ***********/
PLATFORM_ALIPAY_APPLET = "alipay_applet"
PLATFORM_WAP = "wap" //h5
PLATFORM_ANDROID = "android"
PLATFORM_IOS = "ios"
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",
}

+ 35
- 0
md/wxpay.go Dosyayı Görüntüle

@@ -0,0 +1,35 @@
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"`
}

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"`
}

+ 1
- 1
pay/init.go Dosyayı Görüntüle

@@ -26,7 +26,7 @@ func Init(host, name, user, psw string) error {
cfg.MaxLifetime = 30
cfg.MaxOpenConns = 100
cfg.MaxIdleConns = 100
cfg.Path = "./logs/%s.logs"
cfg.Path = "./tmp/%s.logs"
if err := db.InitDB(&cfg); err != nil {
return err
}


+ 112
- 0
pay/pay_by_own.go Dosyayı Görüntüle

@@ -2,10 +2,15 @@ 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/iGoogle-ink/gopay"
v3 "github.com/iGoogle-ink/gopay/wechat/v3"
"github.com/iGoogle-ink/gotil/xlog"
"github.com/pkg/errors"
"github.com/shopspring/decimal"
)

//自有支付 支付宝
@@ -41,3 +46,110 @@ func Alipay(args map[string]string) (string, error) {
}
return zhios_pay_utils.AnyToString(param), nil
}

//自有支付 微信支付

// app支付v2
func WxAppPay(params *md.WxPayParams) (map[string]string, error) {
appId := params.PayWxAppid
mchId := params.PayWxMchId
apiKey := params.PayWxApiKey
notifyUrl := params.NotifyUrl
client := local_wxpay.NewClient(appId, mchId, apiKey, true)
r, err := local_wxpay.TradeAppPay(client, params.Subject, params.OrdId, params.Amount, notifyUrl)
return r, err
}

// H5支付v2
func WxH5Pay(params *md.WxPayParams) (map[string]string, error) {
appId := params.PayWxAppid
mchId := params.PayWxMchId
apiKey := params.PayWxApiKey
notifyUrl := params.NotifyUrl
client := local_wxpay.NewClient(appId, mchId, apiKey, true)
r, err := local_wxpay.TradeH5Pay(client, params.Subject, params.OrdId, params.Amount, notifyUrl)
return r, err
}

// 小程序v2
func WxMiniProgPay(params *md.WxPayParams) (map[string]string, error) {
appId := params.PayWxAppid
mchId := params.PayWxMchId
apiKey := params.PayWxApiKey
notifyUrl := params.NotifyUrl
client := local_wxpay.NewClient(appId, mchId, apiKey, true)
if params.ThirdPartyWechatOpenid == "" {
return nil, errors.New("请先授权微信")
}
r, err := local_wxpay.TradeMiniProgPay(client, params.Subject, params.OrdId, params.Amount, notifyUrl, params.ThirdPartyWechatOpenid)
return r, err
}

// app支付V3
func WxAppPayV3(params *md.WxPayParams) (map[string]string, error) {
appId := params.PayWxAppid
mchId := params.PayWxMchId
notifyUrl := params.NotifyUrl
SerialNo := params.PayWxApiKey
ApiV3Key := params.PayWxApiKey
PKContent := params.PayWxApiKey
client, err := v3.NewClientV3(appId, mchId, SerialNo, ApiV3Key, PKContent)
if err != nil {
xlog.Error(err)
return nil, err
}
client.DebugSwitch = gopay.DebugOff
r, err := local_wxpay.TradeAppPayV3(client, params.Subject, params.OrdId, params.Amount, notifyUrl)
return r, err
}

// 微信JSAPI支付
func WxAppJSAPIPay(params *md.WxPayParams) (map[string]string, error) {
appId := params.PayWxAppid
mchId := params.PayWxMchId
apiKey := params.PayWxApiKey
notifyUrl := params.NotifyUrl

client := local_wxpay.NewClient(appId, mchId, apiKey, true)
if params.ThirdPartyWechatOpenid == "" {
return nil, errors.New("请先授权微信")
}
r, err := local_wxpay.TradeJSAPIPay(client, params.Subject, params.OrdId, params.Amount, notifyUrl, params.ThirdPartyWechatOpenid)
return r, err
}

// H5支付V3
func WxH5PayV3(params *md.WxPayParams) (string, error) {
appId := params.PayWxAppid
mchId := params.PayWxMchId
apiKey := params.PayWxApiKey
notifyUrl := params.NotifyUrl
client := local_wxpay.NewClient(appId, mchId, apiKey, false)
_, err := local_wxpay.TradeH5Pay(client, params.Subject, params.OrdId, params.Amount, notifyUrl)
return "", err
}

// 小程序V3
func WxMiniProgPayV3(params *md.WxPayParams) (string, error) {
appId := params.PayWxAppid
mchId := params.PayWxMchId
notifyUrl := params.NotifyUrl
SerialNo := params.PayWxApiKey
ApiV3Key := params.PayWxApiKey
PKContent := params.PayWxApiKey
client, err := v3.NewClientV3(appId, mchId, SerialNo, ApiV3Key, PKContent)
if err != nil {
xlog.Error(err)
return "", err
}
client.DebugSwitch = gopay.DebugOff
r, err := local_wxpay.TradeMiniProgPayV3(client, params.Subject, params.OrdId, params.Amount, notifyUrl)
return r, err
}

// 微信金额乘100
func wxMoneyMulHundred(m string) string {
amount, _ := decimal.NewFromString(m)
newM := amount.Mul(decimal.NewFromInt(100))
return newM.String()
}

+ 245
- 0
utils/logx/log.go Dosyayı Görüntüle

@@ -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
}

+ 105
- 0
utils/logx/output.go Dosyayı Görüntüle

@@ -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
}

+ 192
- 0
utils/logx/sugar.go Dosyayı Görüntüle

@@ -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)
}

Yükleniyor…
İptal
Kaydet