472 lines
13 KiB
Go
472 lines
13 KiB
Go
package withdrawser
|
|
|
|
import (
|
|
"91porn-server/app/service/messageser"
|
|
"91porn-server/common/stderr"
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
"time"
|
|
|
|
"91porn-server/app/appg"
|
|
"91porn-server/common"
|
|
"91porn-server/common/constant/redisconst"
|
|
"91porn-server/common/db"
|
|
"91porn-server/common/log"
|
|
"91porn-server/common/rchgutil"
|
|
"91porn-server/common/redis"
|
|
"91porn-server/models/v/txnactmod"
|
|
"91porn-server/models/v/txnmod"
|
|
"91porn-server/models/v/walletmod"
|
|
"91porn-server/models/v/wdchannmod"
|
|
"91porn-server/models/v/wdordmod"
|
|
"91porn-server/models/v/wdtaxmod"
|
|
"91porn-server/skd/skdg"
|
|
"91porn-server/web/webg"
|
|
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
"go.mongodb.org/mongo-driver/mongo/options"
|
|
)
|
|
|
|
// ExchangeAllow 提现通过
|
|
func ExchangeAllow(id string, usdtRate float64, operator string) (err error) {
|
|
// 查询订单信息
|
|
withdrawOrder, ec := wdordmod.FindWithdrawOrderByID(id)
|
|
if ec != nil {
|
|
return ec
|
|
}
|
|
now := time.Now()
|
|
set := wdordmod.EditSelector{
|
|
CheckedAt: &now,
|
|
ProgressAt: &now,
|
|
Status: wdordmod.PROCESSING,
|
|
StatusDesc: wdordmod.PROCESSINGError,
|
|
Operator: &operator,
|
|
}
|
|
content := "提现审核已通过,预计1个工作日内到账。"
|
|
// 特殊处理usdt提现审核
|
|
if usdtRate > 0 {
|
|
if withdrawOrder.PayType != wdordmod.TypeUsdt || withdrawOrder.Status != wdordmod.DEFAULT {
|
|
return fmt.Errorf("订单异常")
|
|
}
|
|
// 更新订单状态
|
|
if ec = wdordmod.UpdateOne(nil, bson.M{"_id": withdrawOrder.ID}, bson.M{
|
|
"checkedAt": now,
|
|
"progressAt": now,
|
|
"successAt": now,
|
|
"updatedAt": now,
|
|
"receivedAt": now,
|
|
"status": wdordmod.SUCCESS,
|
|
"statusDesc": wdordmod.SUCCESSStatus,
|
|
"usdtRate": usdtRate,
|
|
"operator": operator,
|
|
}); ec != nil {
|
|
return ec
|
|
}
|
|
go func() {
|
|
content = "USDT" + content
|
|
// 处理消息
|
|
log.Info(fmt.Sprintf("HandleOfficialMsg pass withdraw start content:%v,uid:%v", content, withdrawOrder.UID))
|
|
code := messageser.HandleOfficialMsg(withdrawOrder.UID, content, 2)
|
|
if code != stderr.Success {
|
|
log.Error(fmt.Sprintf("HandleOfficialMsg pass withdraw error:%+v:", err), log.Any("uid", withdrawOrder.UID))
|
|
}
|
|
}()
|
|
return nil
|
|
}
|
|
//获取银行卡编码
|
|
if withdrawOrder.PayType == wdordmod.TypeBankCard && withdrawOrder.BankCode == "" {
|
|
withdrawOrder.BankCode = GetBankInfo(withdrawOrder.Act)
|
|
if withdrawOrder.BankCode == "" {
|
|
log.Warn("GetBankInfo BankCode == ''", log.Any("uid", withdrawOrder.UID), log.Any("bankAct", withdrawOrder.Act))
|
|
return errors.New("获取银行编码异常,请重试")
|
|
}
|
|
}
|
|
w, err := wdordmod.FindOneAndUpate(id, wdordmod.DEFAULT, set)
|
|
if err != nil {
|
|
if w.ID.IsZero() || w.ActName == "" {
|
|
return err
|
|
}
|
|
return err
|
|
}
|
|
var msg rchgutil.ExchgMsg
|
|
var tradeNo string
|
|
if w.ProductType == 0 {
|
|
tradeNo = rchgutil.EChgIDAssemble(w.ID.Hex())
|
|
}
|
|
if w.ProductType == 1 {
|
|
tradeNo = rchgutil.EChgWLIDAssemble(w.ID.Hex())
|
|
}
|
|
if w.PayType == wdordmod.TypeBankCard && w.BankCode == "" {
|
|
typeAct := txnactmod.Bank
|
|
act, err := txnactmod.FindOne(txnactmod.Query{
|
|
Act: &(w.Act),
|
|
AType: &typeAct,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
w.BankCode = act.BankCode
|
|
}
|
|
req := &rchgutil.Exchg{
|
|
TransNo: tradeNo,
|
|
UID: strconv.FormatUint(w.UID, 10),
|
|
Name: w.Name,
|
|
PayMoney: w.PayMoney,
|
|
PayType: w.PayType,
|
|
ActName: w.ActName,
|
|
Act: w.Act,
|
|
UserIP: w.UserIP,
|
|
DeviceType: w.DeviceType,
|
|
DevID: w.DevID,
|
|
CheckedAt: now,
|
|
ProductType: w.ProductType,
|
|
BankCode: w.BankCode,
|
|
}
|
|
if w.ProductType == 1 {
|
|
req.Rate = 3
|
|
}
|
|
msg = req.ToPay()
|
|
set.OID = &(msg.OID)
|
|
if msg.Code != 200 {
|
|
if msg.Code == rchgutil.UNKNOWError {
|
|
set.Status = wdordmod.UNKNOWN
|
|
set.StatusDesc = wdordmod.UNKNOWNError
|
|
set.FailureAt = &now
|
|
if err = wdordmod.Update(nil, id, set); err != nil {
|
|
return err
|
|
}
|
|
return errors.New(set.StatusDesc)
|
|
}
|
|
//发生错误,执行退款程序
|
|
set.FailureAt = &now
|
|
set.Status = wdordmod.FAILURE
|
|
set.StatusDesc = wdordmod.ServerError + "[第三方返回: " + msg.Err + "]"
|
|
if err = refusedWirhdraw(w, set, "服务器请求平台失败"); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
common.Go(func() {
|
|
if err := CacheProcessingOrder(w.UID); err != nil {
|
|
log.Error("CacheProcessingUser", log.Any("order id", id), log.E(err))
|
|
}
|
|
})
|
|
return nil
|
|
}
|
|
|
|
// ExchangeRefuseByHand 手动对特定状态的提现订单进行退款
|
|
func ExchangeRefuseByHand(id string, status int, desc string) error {
|
|
w, err := wdordmod.FindWithdrawOrderByIDAndStatus(id, status) //防重入
|
|
if err != nil {
|
|
return err
|
|
}
|
|
//发生错误,执行退款程序
|
|
now := time.Now()
|
|
set := wdordmod.EditSelector{
|
|
Status: wdordmod.REFUSE,
|
|
StatusDesc: desc,
|
|
CheckedAt: &now,
|
|
}
|
|
if err = refusedWirhdraw(w, set, desc); err != nil {
|
|
return err
|
|
}
|
|
//棋牌usdt兑换拒绝增加usdt可提现额度
|
|
if w.ProductType == 1 && w.PayType == wdordmod.TypeUsdt {
|
|
if _, err = walletmod.IncUsdtAmount(w.UID, w.Money); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ExchangeRefuse 提现拒绝
|
|
func ExchangeRefuse(id string, statusDesc, operator string) error {
|
|
w, err := wdordmod.FindWithdrawOrderByIDAndStatus(id, wdordmod.DEFAULT) //防重入
|
|
if err != nil {
|
|
return err
|
|
}
|
|
//发生错误,执行退款程序
|
|
now := time.Now()
|
|
set := wdordmod.EditSelector{
|
|
Status: wdordmod.REFUSE,
|
|
StatusDesc: statusDesc,
|
|
Operator: &operator,
|
|
CheckedAt: &now,
|
|
}
|
|
if err = refusedWirhdraw(w, set, statusDesc); err != nil {
|
|
return err
|
|
}
|
|
//棋牌usdt兑换拒绝增加usdt可提现额度
|
|
if w.ProductType == 1 && w.PayType == wdordmod.TypeUsdt {
|
|
if _, err = walletmod.IncUsdtAmount(w.UID, w.Money); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
go func() {
|
|
content := "提现审核未通过," + statusDesc + "。"
|
|
if w.PayType == wdordmod.TypeUsdt {
|
|
content = "USDT" + content
|
|
}
|
|
if w.PayType == wdordmod.TypeAlipay {
|
|
content = "支付宝" + content
|
|
}
|
|
if w.PayType == wdordmod.TypeBankCard {
|
|
content = "银行卡" + content
|
|
}
|
|
// 处理消息
|
|
log.Info(fmt.Sprintf("HandleOfficialMsg refused withdraw start content:%v,uid:%v", content, w.UID))
|
|
code := messageser.HandleOfficialMsg(w.UID, content, 2)
|
|
if code != stderr.Success {
|
|
log.Error(fmt.Sprintf("HandleOfficialMsg refused withdraw error:%+v:", err), log.Any("uid", w.UID))
|
|
}
|
|
}()
|
|
return nil
|
|
}
|
|
|
|
// GetAllOrders 分页获取订单,
|
|
func GetAllOrders(cond primitive.M, opt *options.FindOptions) (total int64,
|
|
data []*wdordmod.WithdrawOrder, err error) {
|
|
if _, ok := cond["uid"]; !ok {
|
|
if b, ok := cond["filterChecked"].(bool); ok && b {
|
|
uids, err := GetAllProcessingOrders()
|
|
if err != nil {
|
|
log.Error("GetAllProcessingUser", log.E(err))
|
|
}
|
|
if len(uids) > 0 {
|
|
cond["uid"] = bson.M{"$nin": uids}
|
|
}
|
|
}
|
|
}
|
|
delete(cond, "filterChecked")
|
|
return wdordmod.FindOrders(cond, opt)
|
|
}
|
|
|
|
// GetAllProcessingOrders 获取过去15分钟内缓存的所有审核过的订单
|
|
func GetAllProcessingOrders() (uids []uint64, err error) {
|
|
redisc := getRedis()
|
|
if redisc != nil {
|
|
now := time.Now()
|
|
start := fmt.Sprintf("%d", now.Add(-redisconst.WithdrawOrderDuration).Unix())
|
|
end := fmt.Sprintf("%d", now.Unix())
|
|
uidStrs, err := redisc.ZRangeByScore(redisconst.WithdrawOrderKey, start, end)
|
|
if err != nil {
|
|
return uids, err
|
|
}
|
|
uids = make([]uint64, 0, len(uidStrs))
|
|
for _, str := range uidStrs {
|
|
uid, err := strconv.ParseUint(str, 0, 64)
|
|
if err != nil {
|
|
log.Error("strconv.ParseUint", log.Any("str", str), log.E(err))
|
|
continue
|
|
}
|
|
uids = append(uids, uid)
|
|
}
|
|
common.Go(func() {
|
|
DelCacheProcessingOrders()
|
|
})
|
|
}
|
|
return
|
|
}
|
|
|
|
// DelCacheProcessingOrders 删除过期数据(超过配置时间窗口的数据:-inf~前15分钟)
|
|
func DelCacheProcessingOrders() {
|
|
now := time.Now()
|
|
start := "-inf"
|
|
end := fmt.Sprintf("(%d", now.Add(-redisconst.WithdrawOrderDuration).Unix())
|
|
removed, err := getRedis().ZRemRangeByScore(redisconst.WithdrawOrderKey, start, end)
|
|
if err != nil {
|
|
log.Error("redisc.ZRemRangeByScore", log.Any("start", start), log.Any("end", end), log.E(err))
|
|
}
|
|
log.Info("redisc.ZRemRangeByScore removed success", log.Any("start", start), log.Any("end", end),
|
|
log.Any("number", removed))
|
|
}
|
|
|
|
// CacheProcessingOrder 缓存正在审核中的订单
|
|
func CacheProcessingOrder(uid uint64) error {
|
|
redisc := getRedis()
|
|
if redisc != nil {
|
|
_, err := redisc.ZAdd(redisconst.WithdrawOrderKey, redis.Member{Score: float64(time.Now().Unix()), Member: uid})
|
|
return err
|
|
}
|
|
return errors.New("nil redis client")
|
|
}
|
|
|
|
func getRedis() *redis.Client {
|
|
if appg.Redis != nil {
|
|
return appg.Redis
|
|
}
|
|
if webg.Redis != nil {
|
|
return webg.Redis
|
|
}
|
|
if skdg.Redis != nil {
|
|
return skdg.Redis
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ExchgCallBack 提现回调
|
|
func ExchgCallBack(tradeNo string, oid string, money int64, status string, mark string) (err error) {
|
|
w, err := wdordmod.FindWithdrawOrderByID(tradeNo)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if w.Status == wdordmod.SUCCESS || w.Status == wdordmod.FAILURE {
|
|
return
|
|
}
|
|
if w.Status != wdordmod.PROCESSING && w.Status != wdordmod.UNKNOWN {
|
|
err = errors.New("Wtihdraw status error")
|
|
log.Error(fmt.Sprintf("Wtihdraw status error ,order:%+v:", w))
|
|
return
|
|
}
|
|
now := time.Now()
|
|
set := wdordmod.EditSelector{
|
|
ReceivedAt: &now,
|
|
OID: &oid,
|
|
}
|
|
switch status {
|
|
case "paid": //已经打款
|
|
//修改订单状态
|
|
set.PayMoney = &money
|
|
set.SuccessAt = &now
|
|
set.Status = wdordmod.SUCCESS
|
|
set.StatusDesc = wdordmod.SUCCESSStatus
|
|
set.SuccessAt = &now
|
|
if err = wdordmod.Update(nil, tradeNo, set); err != nil {
|
|
return
|
|
}
|
|
case "refused": //拒绝
|
|
set.Status = wdordmod.FAILURE
|
|
set.StatusDesc = mark
|
|
set.FailureAt = &now
|
|
if err = refusedWirhdraw(w, set, "提现被平台拒绝"); err != nil {
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func refusedWirhdraw(w wdordmod.WithdrawOrder, set wdordmod.EditSelector, mark string) error {
|
|
if w.ID.IsZero() {
|
|
return errors.New("id is invalid")
|
|
}
|
|
if w.ProductType == 1 {
|
|
return errors.New("瓦力游戏已下架")
|
|
}
|
|
return webg.VideoDB.Trans(func(t *db.MongoTool) error {
|
|
switch w.WithdrawType {
|
|
case wdordmod.GoldWithdraw:
|
|
wallet, err := walletmod.CreditIncome(t, w.Amount, w.UID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
//插入收支流水
|
|
tk := txnmod.TransactionLog{
|
|
TransNo: w.ID,
|
|
UID: w.UID,
|
|
Amount: w.Amount,
|
|
TranType: txnmod.WithdrawRefund.Key(),
|
|
TranTypeInt: int64(txnmod.WithdrawRefund),
|
|
ChannelType: w.PayType,
|
|
Desc: "用户提现失败,退款" + strconv.FormatInt(w.Amount, 10),
|
|
DiscDoc: w.DiscDoc,
|
|
SysType: w.DeviceType,
|
|
ActualAmount: float64(w.Amount),
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
}
|
|
//插入一条支出流水
|
|
if err = txnmod.InsertTransactionLog(t, &tk); err != nil {
|
|
return err
|
|
}
|
|
case wdordmod.AgentWithdraw:
|
|
if err := walletmod.CreditMoney(t, w.Money, w.UID); err != nil {
|
|
return err
|
|
}
|
|
case wdordmod.DiscWithdraw:
|
|
// err = discWirhdrawRefund(t, w, mark)
|
|
// if err != nil {
|
|
// log.Error("discWirhdrawRefund failed", log.E(err), log.Any("witOrd", w))
|
|
// return err
|
|
// }
|
|
default:
|
|
return errors.New("invalid type")
|
|
}
|
|
return wdordmod.Update(t, w.ID.Hex(), set)
|
|
})
|
|
}
|
|
|
|
// WithdrawType 获取提现方式
|
|
func WithdrawType() (res []*wdchannmod.WithdrawChannelRes, err error) {
|
|
wt, err := wdtaxmod.GetWithDrawTariff()
|
|
if err != nil {
|
|
return
|
|
}
|
|
wcs, err := wdchannmod.GetWithdrawChannels()
|
|
if err != nil {
|
|
return
|
|
}
|
|
res = make([]*wdchannmod.WithdrawChannelRes, len(wcs))
|
|
for i, v := range wcs {
|
|
res[i] = &wdchannmod.WithdrawChannelRes{
|
|
WithdrawChannel: *v,
|
|
WithdrawTariff: wt,
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
type WithdrawMethod struct {
|
|
ChannelName string //提现渠道名字
|
|
CID string //渠道id
|
|
PayType string //支付方式
|
|
MinMoney int //支持最小支付金额
|
|
QpMinMoney int //支持棋牌最小支付金额
|
|
MaxMoney int //支持最大支付金额
|
|
Weight int //权重
|
|
Active bool //是否激活
|
|
CashTax int //代理提现比率
|
|
CoinTax int //金币提现比率
|
|
DiscCashTax int //商区提现比率
|
|
}
|
|
|
|
func (w WithdrawMethod) IsValid() bool {
|
|
return w.ChannelName != "" && w.CID != ""
|
|
}
|
|
|
|
// GetWithdrawMethod 获取提现方法
|
|
func GetWithdrawMethod(paymentMethod string) (WithdrawMethod, error) {
|
|
active := true
|
|
mats := []wdchannmod.Matcher{
|
|
(&wdchannmod.ActiveMatch{Active: &active}).New(),
|
|
(&wdchannmod.PayTypeMatch{PayType: &paymentMethod}).New(),
|
|
}
|
|
wcList, err := wdchannmod.List(nil, nil, nil, mats...)
|
|
if err != nil {
|
|
log.Error("GetWithdrawChannel failed", log.E(err))
|
|
return WithdrawMethod{}, err
|
|
}
|
|
wc := wcList.WeightedRandom()
|
|
if wc.ID.IsZero() {
|
|
return WithdrawMethod{}, nil
|
|
}
|
|
var method WithdrawMethod
|
|
method.ChannelName = wc.ChannelName
|
|
method.CID = wc.CID
|
|
method.PayType = wc.PayType
|
|
method.MinMoney = wc.MinMoney
|
|
method.QpMinMoney = wc.QpMinMoney
|
|
method.MaxMoney = wc.MaxMoney
|
|
method.Weight = wc.Weight
|
|
method.Active = wc.Active
|
|
wt, err := wdtaxmod.GetWithDrawTariff()
|
|
if err != nil {
|
|
log.Error("GetWithDrawTariff failed", log.E(err))
|
|
return WithdrawMethod{}, err
|
|
}
|
|
method.CashTax = wt.CashTax
|
|
method.CoinTax = wt.CoinTax
|
|
method.DiscCashTax = wt.DiscCashTax
|
|
return method, nil
|
|
}
|