|
|
|
|
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() {
|
|
|
|
|
var list []*common.WithdrawOrder
|
|
|
|
|
// 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{
|
|
|
|
|
OrderID: or.OrderID,
|
|
|
|
|
Amount: or.Amount / common.DecimalDigits,
|
|
|
|
|
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,
|
|
|
|
|
}
|
|
|
|
|
channel := call.GetConfigWithdrawChannelsByID(int(req.Channel), common.CurrencyINR)
|
|
|
|
|
if channel == nil || channel.WithdrawPer <= 0 {
|
|
|
|
|
con := call.GetConfigWithdrawChannelsBest(common.CurrencyINR)
|
|
|
|
|
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)
|
|
|
|
|
u := map[string]interface{}{"pay_channel": req.Channel}
|
|
|
|
|
if data.APIOrderID != "" {
|
|
|
|
|
u["apipayid"] = data.APIOrderID
|
|
|
|
|
}
|
|
|
|
|
db.Mysql().Update(&common.WithdrawOrder{ID: or.ID}, u)
|
|
|
|
|
}
|