印度包网
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

308 lines
8.3 KiB

1 year ago
package pay
import (
"encoding/json"
"errors"
"fmt"
"server/call"
"server/common"
"server/config"
"server/db"
"server/modules/pay/allpay"
"server/modules/pay/base"
"server/modules/pay/values"
"server/pb"
"server/util"
"sort"
"sync"
"time"
"github.com/gogo/protobuf/proto"
"github.com/liangdas/mqant/log"
)
var (
withdrawGroup sync.WaitGroup
)
func initTimer() {
// StartPayCheckTimer()
WithdrawTimer()
}
func StartPayCheckTimer() {
values.PayStatus, _ = db.Redis().GetInt(common.RedisKeyPayStatus)
if values.PayStatus != values.PayStatusAuto {
values.PayStatus = values.PayStatusAuto
util.Go(func() {
db.Redis().SetData(common.RedisKeyPayStatus, values.PayStatusAuto)
})
}
t := config.GetConfig().Pay.PayCheckTime
if t <= 0 {
t = 2
}
time.AfterFunc(time.Duration(t)*time.Minute, func() {
PayCheck()
StartPayCheckTimer()
})
}
var (
totalAll int64 = 20 // 当前时间段计算的总单数
)
// 每隔一段时间检测渠道成功率,切换渠道
func PayCheck() {
log.Debug("pay check start...... ")
defer func() {
log.Debug("pay check finish...... ")
log.Debug("status:%v", values.PayStatus)
for _, v := range call.ConfigPayChannels {
log.Debug("%+v", *v)
}
}()
log.Debug("limit:%v", config.GetConfig().Pay.CheckLimit)
// t := time.Now().Add(-5 * time.Minute).Format("2006-01-02 15:04:05")
// 当前成功率没有达到预设值,触发重置权重
// totalAll := getPayCount(t, -1, false)
// if totalAll == 0 {
// return
// }
// success := getPayCount(t, -1, true)
// if success*100/totalAll >= int64(config.GetConfig().Pay.CheckLimit) {
// return
// }
// 检查是否有渠道的成功率达标
pays := []*common.ConfigPayChannels{}
values.PayWeightLock.RLock()
for _, v := range call.ConfigPayChannels {
if v.PayPer > 0 {
pays = append(pays, v)
}
}
values.PayWeightLock.RUnlock()
// choosePay := -1
// var bestPer int64
successPer := map[int]int64{}
successTotalPer := map[int]int64{}
// var totalSuccess int64
for i, v := range pays {
per := getSuccessPer(v.ChannelID, i)
successPer[v.ChannelID] = per
// if per >= int64(config.GetConfig().Pay.CheckLimit) && per > bestPer {
// choosePay = v.ChannelID
// bestPer = per
// }
tPer := getSuccessPerTotal(v.ChannelID)
successTotalPer[v.ChannelID] = tPer
// totalSuccess += tPer
}
log.Debug("successPer:%v,successTotalPer:%v", successPer, successTotalPer)
// 当前有达标的渠道选择,直接切换渠道权重
values.PayWeightLock.Lock()
defer values.PayWeightLock.Unlock()
// 全部按成功率重置权重
for _, v := range pays {
st := successTotalPer[v.ChannelID]
if st == 0 {
st = int64(config.GetConfig().Pay.BaseSuccess)
}
per := successPer[v.ChannelID]*500 + st*100 + 100
// if totalSuccess > 0 {
// per += successTotalPer[v.ChannelID] * 1000 / totalSuccess
// }
for _, c := range call.ConfigPayChannels {
if c.ChannelID == v.ChannelID {
c.PayPer = int(per)
break
}
}
cid := v.ChannelID
util.Go(func() {
values.SetPayChannel(cid, int(per))
})
}
// if choosePay > -1 {
// // 切换为主渠道模式
// values.PayStatus = values.PayStatusChannel
// util.Go(func() {
// db.Redis().SetData(common.RedisKeyPayStatus, values.PayStatusChannel)
// })
// } else {
// values.PayStatus = values.PayStatusAuto
// util.Go(func() {
// db.Redis().SetData(common.RedisKeyPayStatus, values.PayStatusAuto)
// })
// 常驻渠道
// for _, v := range config.GetConfig().Pay.RootChannel {
// for _, c := range call.ConfigPayChannels {
// if v == c.ChannelID {
// if c.PayPer <= 0 {
// c.PayPer = 500
// cid := c.ChannelID
// util.Go(func() {
// values.SetPayChannel(cid, 500)
// })
// }
// break
// }
// }
// }
// }
sort.Slice(call.ConfigPayChannels, func(i, j int) bool {
return call.ConfigPayChannels[i].PayPer > call.ConfigPayChannels[j].PayPer
})
}
func getPayCount(start, end int64, channel int, success bool) int64 {
sql := fmt.Sprintf("event = %v and create_time >= %d and create_time < %d and upi >= 0", common.CurrencyEventReCharge, start, end)
if channel >= 0 {
sql += fmt.Sprintf(" and pay_channel = %v", channel)
}
if success {
sql += fmt.Sprintf(" and status = %v", common.StatusROrderPay)
}
return db.Mysql().Count(&common.RechargeOrder{}, sql)
}
func getSuccessPer(channel, index int) int64 {
now := time.Now()
start := now.Add(-6 * time.Minute).Unix()
end := now.Add(-1 * time.Minute).Unix()
total := getPayCount(start, end, channel, false)
success := getPayCount(start, end, channel, true)
if index == 0 { // 当前最高权重的渠道
if total < 20 { // 如果最高权重的渠道总单数少于20,降低最低单数判断标准
totalAll = 10
} else {
totalAll = 20
}
}
printTotal := total
if total < totalAll {
total = totalAll
}
per := success * 100 / total
log.Debug("channel:%v,total:%v,totalAll:%v,success:%v,per:%v", channel, printTotal, totalAll, success, per)
return per
}
func getSuccessPerTotal(channel int) int64 {
now := time.Now()
start := now.Unix()
end := now.AddDate(0, 0, 1).Unix()
total := getPayCount(start, end, channel, false)
if total == 0 {
return 0
}
success := getPayCount(start, end, channel, true)
if success == 0 && total > 50 {
return 1
}
per := success * 100 / total
log.Debug("total:channel:%v,total:%v,success:%v,per:%v", channel, total, success, per)
return per
}
func WithdrawTimer() {
1 year ago
var list []*common.WithdrawOrder
1 year ago
// startData := time.Now().AddDate(0, 0, -1).Unix()
db.Mysql().QueryAll(fmt.Sprintf("status = %d and pay_source = %d",
common.StatusROrderWaitting, common.PaySourceModulePay), "", &common.WithdrawOrder{}, &list)
l := len(list)
if l > 0 {
log.Debug("start scan orders:%v", l)
withdrawGroup.Add(l)
for _, v := range list {
// 提交订单
res, err := db.Mysql().UpdateRes(&common.WithdrawOrder{OrderID: v.OrderID, Status: common.StatusROrderWaitting,
PaySource: common.PaySourceModulePay},
map[string]interface{}{"status": common.StatusROrderPay})
if err != nil {
log.Error("err:%v", err)
withdrawGroup.Done()
continue
}
if res == 0 {
withdrawGroup.Done()
continue
}
or := v
log.Debug("trying:%v", or.OrderID)
send := new(common.WithdrawCommon)
if err := json.Unmarshal([]byte(or.PayAccount), &send); err != nil {
log.Error("withdraw unmarshal err %v", err)
call.ReturnBackWithdraw(or, common.StatusROrderPay, common.StatusROrderFail)
return
}
util.Go(func() {
TryWithdraw(or)
withdrawGroup.Done()
})
}
}
t := config.GetConfig().Pay.WithdrawScanTime
if t <= 0 {
t = 10
}
time.AfterFunc(time.Duration(t)*time.Second, WithdrawTimer)
}
func TryWithdraw(or *common.WithdrawOrder) {
send := new(common.WithdrawCommon)
if err := json.Unmarshal([]byte(or.PayAccount), &send); err != nil {
log.Error("withdraw unmarshal err %v", err)
call.ReturnBackWithdraw(or, common.StatusROrderPay, common.StatusROrderFail)
return
}
req := &pb.InnerWithdrawReq{
1 year ago
OrderID: or.OrderID,
2 months ago
Amount: or.Amount / common.DecimalDigits,
1 year ago
Phone: send.Mobile,
Name: send.AccountName,
CardNo: send.BankCardNo,
PayCode: send.BankCode,
PayType: int64(send.PayType),
UID: uint32(or.UID),
Channel: int64(or.UPI),
Email: send.Email,
BankName: send.BankName,
BankBranchName: send.BankBranchName,
DeviceID: send.DeviceNo,
IP: send.IP,
1 year ago
}
channel := call.GetConfigWithdrawChannelsByID(int(req.Channel), common.CurrencyINR)
1 year ago
if channel == nil || channel.WithdrawPer <= 0 {
con := call.GetConfigWithdrawChannelsBest(common.CurrencyINR)
1 year ago
if con == nil {
return
}
req.Channel = int64(con.ChannelID)
}
var ret []byte
var err error
base := base.NewWithdrawBase(req)
allpay.NewSub(base, int(req.Channel))
if base.Sub != nil {
ret, err = base.Req()
} else {
ret, err = nil, errors.New("inner error")
}
log.Debug("order:%v,err:%v", req.OrderID, err)
if err != nil {
log.Debug("order:%+v,err:%v", or, err)
call.ReturnBackWithdraw(or, common.StatusROrderPay, common.StatusROrderFail, int(req.Channel))
return
}
data := &pb.InnerWithdrawResp{}
proto.Unmarshal(ret, data)
log.Debug("withdraw resp:%+v", *data)
1 year ago
u := map[string]interface{}{"pay_channel": req.Channel}
1 year ago
if data.APIOrderID != "" {
1 year ago
u["apipayid"] = data.APIOrderID
1 year ago
}
1 year ago
db.Mysql().Update(&common.WithdrawOrder{ID: or.ID}, u)
1 year ago
}