package services import ( "app/commons/constant" "app/commons/core" "app/commons/core/exchange" "app/commons/model/entity" "fmt" "github.com/shopspring/decimal" ) func (s *CommonService) RwCallback(req *exchange.TransferCallbackReq) error { var count int64 if err := s.DB().Model(&entity.AssetRwCallbackLog{}). Where("order_id", req.OrderId). Count(&count).Error; err != nil { return err } if count > 0 { return nil } newLog := entity.AssetRwCallbackLog{ Currency: req.Currency, Amount: req.Amount, OrderId: req.OrderId, OpenId: req.OpenId, CreateTime: req.CreateTime, Type: req.Type, AppId: req.AppId, CustomParam: req.CustomParam, Sign: req.Sign, Status: req.Status, // 交易所返回状态码 0 1 成功 2 失败 State: constant.RwCallbackStateWaiting, // 0 待处理 1 处理成功 2 处理失败 } err := s.DB().Create(&newLog).Error if err != nil { return err } // 触发订单处理 go s.callbackHandlerCheck() return nil } // 回调处理失败检查 var callbackHandler bool = false func (s *CommonService) callbackHandlerCheck() { if callbackHandler { core.Log.Infof("回调任务处理中") return } callbackHandler = true defer func() { callbackHandler = false }() db := s.DB().Where("state", constant.RwCallbackStateWaiting) var count int64 if err := db.Model(&entity.AssetRwCallbackLog{}).Count(&count).Error; err != nil { core.Log.Error(err.Error()) return } if count == 0 { return } items, err := s.BatchAssetRwCallbackLog(db) if err != nil { core.Log.Error(err.Error()) } for _, item := range items { switch item.Type { case exchange.TransferUserToApp: s.handleRecharge(item) break case exchange.TransferAppToUser: s.handleWithdraw(item) break } } return } // 转出回调处理逻辑 // 收到转出成功回调 // 1 修改回调记录状态 2 扣除用户冻结资产 3 产出流水信息 // status 1 成功 2 失败 func (s *CommonService) handleWithdraw(callbackLog *entity.AssetRwCallbackLog) { if callbackLog.State != constant.RwCallbackStateWaiting { return } var err error defer func() { callbackLog.State = constant.RwCallbackStateFinish callbackLog.ErrDesc = "success" if err != nil { callbackLog.State = constant.RwCallbackStateFail callbackLog.ErrDesc = err.Error() } if err = s.DB().Model(&entity.AssetRwCallbackLog{}). Where("id", callbackLog.Id).Updates(map[string]interface{}{ "state": callbackLog.State, "err_desc": callbackLog.ErrDesc, }).Error; err != nil { core.Log.Error(err.Error()) } }() order, err := s.FirstAssetRwRecord(s.DB().Where("order_id", callbackLog.OrderId)) if err != nil { return } // 查找订单信息 amount, err := decimal.NewFromString(callbackLog.Amount) if err != nil { return } // 订单状态检查 提现状态检查 if order.Status != constant.RwStatePass && order.WithdrawState != constant.WithdrawStateFinish { err = fmt.Errorf("order.Status:%d WithdrawState:%d", order.Status, order.WithdrawState) return } // 金额检查 -- 这里是否改为大于则不处理? if !order.RealAmount.Equal(amount) { err = fmt.Errorf("order amount :%s app to user amount :%s", order.Amount, amount) return } // 提现状态 非1 即失败 if callbackLog.Status == exchange.ExchangeRwStatusWithdrawSuccess { err = s.withdrawSuccessHandler(order) return } err = s.DB().Model(&entity.AssetRwRecord{}).Where("id", order.Id). Update("status", constant.RwStateSystemFail).Error return } // 提现成功扣除冻结处理 func (s *CommonService) withdrawSuccessHandler(order *entity.AssetRwRecord) error { var err error if order.Status != constant.RwStatePass && order.WithdrawState != constant.WithdrawStateFinish { return nil } // order.Amount // 订单状态等于1 表示这个订单是审核后发起提现 // 扣除冻结金额 并产生资产流水 // 重新获取资产信息 -- 这个方法已经加了分布式锁 理论上是不需要再重新获取 userAsset, err := s.GetAssetBySymbol(order.UserId, order.Symbol) if err != nil { return fmt.Errorf("not found asset") } dbTx := s.DB().Begin() bs := constant.BsById(constant.BsAssetWithdrawSuccess) bs.ContextName = order.TableName() bs.ContextValue = order.OrderId err = s.GenBillAndActionAsset(dbTx, userAsset.UserId, userAsset.Symbol, decimal.Zero, decimal.Zero.Sub(order.Amount), bs) if err != nil { dbTx.Rollback() return err } err = dbTx.Model(&entity.AssetRwRecord{}). Where("id", order.Id). Update("status", constant.RwStateFinish).Error if err != nil { dbTx.Rollback() return err } dbTx.Commit() return nil } // 构建充提记录 // feeRatio, // feeAmount, // fee, // realAmount, func (s *CommonService) buildRwRecord(symbol string, userId int64, openId, orderId string, rwDirection int, amount, feeRatio, feeBaseAmount, fee, realAmount decimal.Decimal) *entity.AssetRwRecord { describe := fmt.Sprintf("用户%s 数量%s", constant.GetRwDirectionName(rwDirection), amount.String()) return &entity.AssetRwRecord{ OrderId: orderId, OpenId: openId, UserId: userId, Symbol: symbol, Amount: amount, Ratio: feeRatio, BaseFee: feeBaseAmount, Fee: fee, RealAmount: realAmount, Status: constant.RwStateWaiting, Direction: rwDirection, DirectionName: constant.GetRwDirectionName(rwDirection), Describe: describe, } }