233 lines
9.4 KiB
Go
233 lines
9.4 KiB
Go
package service
|
||
|
||
import (
|
||
"context"
|
||
"errors"
|
||
"fmt"
|
||
"gateway/internal/config"
|
||
"gateway/internal/models/accounts"
|
||
"gateway/internal/models/merchant"
|
||
"gateway/internal/models/merchant_deploy"
|
||
"gateway/internal/models/order"
|
||
"gateway/internal/otelTrace"
|
||
|
||
"gateway/internal/utils"
|
||
"time"
|
||
|
||
"github.com/beego/beego/v2/client/orm"
|
||
"go.uber.org/zap"
|
||
)
|
||
|
||
const (
|
||
Interval = 2 // 隔多少分钟进行结算
|
||
Minutes = 1 // 每隔15分钟,进行扫码,看有没有隔天押款金额
|
||
)
|
||
|
||
// OrderSettle 订单结算,将那些支付成功的订单金额加入到商户账户的结算金额中
|
||
func OrderSettle(ctx context.Context) {
|
||
params := make(map[string]string)
|
||
params["is_allow_settle"] = config.YES
|
||
params["is_complete_settle"] = config.NO
|
||
orderSettleList := order.GetOrderSettleListByParams(ctx, params)
|
||
for _, orderSettle := range orderSettleList {
|
||
orderProfitInfo := order.GetOrderProfitByBankOrderId(ctx, orderSettle.BankOrderId)
|
||
if !settle(ctx, orderSettle, orderProfitInfo) {
|
||
otelTrace.Logger.WithContext(ctx).Error(fmt.Sprintf("结算订单bankOrderId = #{orderSettle.BankOrderId}, 执行失败"))
|
||
} else {
|
||
otelTrace.Logger.WithContext(ctx).Info(fmt.Sprintf("结算订单bankOrderId= #{orderSettle.BankOrderId},执行成功"))
|
||
}
|
||
}
|
||
}
|
||
|
||
func settle(ctx context.Context, orderSettle order.OrderSettleInfo, orderProfit order.OrderProfitInfo) bool {
|
||
o := orm.NewOrm()
|
||
|
||
if err := o.DoTxWithCtx(ctx, func(ctx context.Context, txOrm orm.TxOrmer) error {
|
||
tmpSettle := new(order.OrderSettleInfo)
|
||
if err := txOrm.RawWithCtx(ctx, "select * from order_settle_info where bank_order_id=? for update", orderSettle.BankOrderId).QueryRow(tmpSettle); err != nil || tmpSettle.BankOrderId == "" {
|
||
otelTrace.Logger.WithContext(ctx).Error(fmt.Sprintf("获取tmpSettle失败,bankOrderId=%s", orderSettle.BankOrderId), zap.String("BankOrderId", orderSettle.BankOrderId))
|
||
return err
|
||
}
|
||
|
||
tmpSettle.UpdateTime = time.Now()
|
||
tmpSettle.IsCompleteSettle = config.YES
|
||
if _, err := txOrm.UpdateWithCtx(ctx, tmpSettle); err != nil {
|
||
otelTrace.Logger.WithContext(ctx).Error("更新tmpSettle失败,错误:", zap.Error(err))
|
||
return err
|
||
}
|
||
|
||
accountInfo := new(accounts.AccountInfo)
|
||
if err := txOrm.RawWithCtx(ctx, "select * from account_info where account_uid= ? for update", orderSettle.MerchantUid).
|
||
QueryRow(accountInfo); err != nil || accountInfo.UpdateTime.IsZero() {
|
||
otelTrace.Logger.WithContext(ctx).Error("结算select account info失败,错误信息:", zap.Error(err))
|
||
return err
|
||
}
|
||
accountInfo.UpdateTime = time.Now()
|
||
|
||
// 商户有押款操作
|
||
loadAmount := 0.0
|
||
merchantDeployInfo := merchant_deploy.GetMerchantDeployByUidAndRoadUid(
|
||
ctx,
|
||
accountInfo.AccountUid,
|
||
orderSettle.RoadUid,
|
||
)
|
||
if merchantDeployInfo.IsLoan == config.YES {
|
||
loadAmount = merchantDeployInfo.LoanRate * 0.01 * orderProfit.FactAmount
|
||
date := utils.GetDate()
|
||
params := make(map[string]string)
|
||
params["merchant_uid"] = tmpSettle.MerchantUid
|
||
params["road_uid"] = tmpSettle.RoadUid
|
||
params["load_date"] = date
|
||
if !merchant.IsExistMerchantLoadByParams(params) {
|
||
tmp := merchant.MerchantLoadInfo{
|
||
Status: config.NO,
|
||
MerchantUid: orderSettle.MerchantUid,
|
||
RoadUid: orderSettle.RoadUid,
|
||
LoadDate: utils.GetDateAfterDays(merchantDeployInfo.LoanDays),
|
||
LoadAmount: loadAmount,
|
||
CreateTime: time.Now(),
|
||
UpdateTime: time.Now(),
|
||
}
|
||
|
||
if _, err := txOrm.Insert(&tmp); err != nil {
|
||
otelTrace.Logger.WithContext(ctx).Error("結算插入merchantLoad失敗,失败信息:", zap.Error(err))
|
||
return err
|
||
} else {
|
||
otelTrace.Logger.WithContext(ctx).Info("结算插入新的merchantLoad信息成功")
|
||
}
|
||
} else {
|
||
merchantLoad := new(merchant.MerchantLoadInfo)
|
||
if err := txOrm.RawWithCtx(ctx, "select * from merchant_load_info where merchant_uid=? and road_uid=? and load_date=? for update", orderSettle.MerchantUid, orderSettle.RoadUid, date).
|
||
QueryRow(merchantLoad); err != nil || merchantLoad.UpdateTime.IsZero() {
|
||
otelTrace.Logger.WithContext(ctx).Error(fmt.Sprintf("结算过程,select merchant load info失败,错误信息:%s", zap.Error(err)))
|
||
return err
|
||
} else {
|
||
merchantLoad.UpdateTime = time.Now()
|
||
merchantLoad.LoadAmount += loadAmount
|
||
if _, err := txOrm.UpdateWithCtx(ctx, merchantLoad); err != nil {
|
||
otelTrace.Logger.WithContext(ctx).Error(fmt.Sprintf("结算过程,update merchant load info失败,失败信息:%s", zap.Error(err)))
|
||
return err
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
otelTrace.Logger.WithContext(ctx).Info(fmt.Sprintf("结算过程中,该商户不需要押款,全款结算"))
|
||
}
|
||
|
||
if accountInfo.WaitAmount < orderProfit.UserInAmount {
|
||
otelTrace.Logger.WithContext(ctx).Error("系统出现严重故障,账户的带结算金额小于订单结算金额")
|
||
return errors.New("系统出现严重故障,账户的带结算金额小于订单结算金额, 账户 = " + accountInfo.AccountName + "订单id = " + orderProfit.BankOrderId)
|
||
}
|
||
|
||
needAmount := orderProfit.UserInAmount - loadAmount
|
||
|
||
accountInfo.SettleAmount = accountInfo.SettleAmount + needAmount
|
||
accountInfo.WaitAmount = accountInfo.WaitAmount - orderProfit.UserInAmount
|
||
accountInfo.LoanAmount = accountInfo.LoanAmount + loadAmount
|
||
|
||
if _, err := txOrm.UpdateWithCtx(ctx, accountInfo); err != nil {
|
||
otelTrace.Logger.WithContext(ctx).Error("结算update account 失败,错误信息:", zap.Error(err))
|
||
return err
|
||
}
|
||
|
||
return nil
|
||
}); err != nil {
|
||
return false
|
||
}
|
||
return true
|
||
}
|
||
|
||
// MerchantLoadSolve 商户的押款释放处理,根据商户的押款时间进行处理
|
||
func MerchantLoadSolve(ctx context.Context) {
|
||
hour := time.Now().Hour()
|
||
merchantDeployList := merchant_deploy.GetMerchantDeployByHour(ctx, hour)
|
||
for _, merchantDeploy := range merchantDeployList {
|
||
otelTrace.Logger.WithContext(ctx).Info(fmt.Sprintf("开始执行商户uid=%s,进行解款操作", merchantDeploy.MerchantUid))
|
||
|
||
loadDate := utils.GetDateBeforeDays(merchantDeploy.LoanDays)
|
||
params := make(map[string]string)
|
||
params["status"] = config.NO
|
||
params["merchant_uid"] = merchantDeploy.MerchantUid
|
||
params["load_date__lte"] = loadDate
|
||
|
||
merchantLoadList := merchant.GetMerchantLoadInfoByMap(ctx, params)
|
||
for _, merchantLoad := range merchantLoadList {
|
||
if MerchantAbleAmount(ctx, merchantLoad) {
|
||
otelTrace.Logger.WithContext(ctx).Info(fmt.Sprintf("商户uid=%s,押款金额=%f,押款通道= %s, 解款成功",
|
||
merchantLoad.MerchantUid, merchantLoad.LoadAmount, merchantLoad.RoadUid))
|
||
} else {
|
||
otelTrace.Logger.WithContext(ctx).Error(fmt.Sprintf("商户uid=%s,押款金额=%f,押款通道=%s, 解款失败",
|
||
merchantLoad.MerchantUid, merchantLoad.LoadAmount, merchantLoad.RoadUid))
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// MerchantAbleAmount 对应的商户的账户可用金额进行调整操作
|
||
func MerchantAbleAmount(ctx context.Context, merchantLoad merchant.MerchantLoadInfo) bool {
|
||
o := orm.NewOrm()
|
||
|
||
if err := o.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
|
||
tmpLoad := new(merchant.MerchantLoadInfo)
|
||
if err := txOrm.Raw("select * from merchant_load_info where merchant_uid=? and road_uid=? and load_date=? for update",
|
||
merchantLoad.MerchantUid, merchantLoad.RoadUid, merchantLoad.LoadDate).
|
||
QueryRow(tmpLoad); err != nil || tmpLoad.MerchantUid == "" {
|
||
otelTrace.Logger.WithContext(ctx).Error(fmt.Sprintf("解款操作获取商户押款信息失败,fail: %s", zap.Error(err)))
|
||
return err
|
||
}
|
||
if tmpLoad.Status != config.NO {
|
||
otelTrace.Logger.WithContext(ctx).Error(fmt.Sprintf("押款信息merchantuid=%s,通道uid=%s, 押款日期=%s,已经解款过,不需要再进行处理了", tmpLoad.MerchantUid, tmpLoad.RoadUid, tmpLoad.LoadDate))
|
||
return errors.New("已经解款过,不需要再进行处理了")
|
||
}
|
||
|
||
tmpLoad.UpdateTime = time.Now()
|
||
tmpLoad.Status = config.YES
|
||
if _, err := txOrm.Update(tmpLoad); err != nil {
|
||
otelTrace.Logger.WithContext(ctx).Error("解款操作更新merchant load info 失败", zap.Error(err))
|
||
return err
|
||
}
|
||
|
||
accountInfo := new(accounts.AccountInfo)
|
||
if err := txOrm.Raw("select * from account_info where account_uid = ? for update", merchantLoad.MerchantUid).
|
||
QueryRow(accountInfo); err != nil || accountInfo.AccountUid == "" {
|
||
otelTrace.Logger.WithContext(ctx).Error("结款操作获取账户信息失败:", zap.Error(err))
|
||
return err
|
||
}
|
||
accountInfo.UpdateTime = time.Now()
|
||
if accountInfo.LoanAmount >= tmpLoad.LoadAmount {
|
||
accountInfo.LoanAmount = accountInfo.LoanAmount - tmpLoad.LoadAmount
|
||
accountInfo.SettleAmount = accountInfo.SettleAmount + tmpLoad.LoadAmount
|
||
} else {
|
||
accountInfo.LoanAmount = config.ZERO
|
||
}
|
||
|
||
if _, err := txOrm.Update(accountInfo); err != nil {
|
||
otelTrace.Logger.WithContext(ctx).Error(fmt.Sprintf("解款操作更新account info 失败:%s,账户uid=%s", err, accountInfo.AccountUid))
|
||
return err
|
||
}
|
||
|
||
return nil
|
||
}); err != nil {
|
||
return false
|
||
}
|
||
return true
|
||
}
|
||
|
||
func OrderSettleInit(ctx context.Context) {
|
||
ctx, cancel := otelTrace.Span(ctx, "OrderSettleInit", "OrderSettleInit")
|
||
defer cancel()
|
||
// 每隔5分钟,巡查有没有可以进行结算的订单
|
||
settleTimer := time.NewTimer(time.Duration(Interval) * time.Minute)
|
||
oneMinuteTimer := time.NewTimer(time.Duration(Minutes) * time.Minute)
|
||
for {
|
||
select {
|
||
case <-settleTimer.C:
|
||
settleTimer = time.NewTimer(time.Duration(Interval) * time.Minute)
|
||
OrderSettle(ctx)
|
||
case <-oneMinuteTimer.C:
|
||
oneMinuteTimer = time.NewTimer(time.Duration(Minutes) * time.Minute)
|
||
MerchantLoadSolve(ctx)
|
||
}
|
||
}
|
||
}
|