com_handler_asset_callback.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. package services
  2. import (
  3. "app/commons/constant"
  4. "app/commons/core"
  5. "app/commons/core/exchange"
  6. "app/commons/model/entity"
  7. "fmt"
  8. "github.com/shopspring/decimal"
  9. )
  10. func (s *CommonService) RwCallback(req *exchange.TransferCallbackReq) error {
  11. var count int64
  12. if err := s.DB().Model(&entity.AssetRwCallbackLog{}).
  13. Where("order_id", req.OrderId).
  14. Count(&count).Error; err != nil {
  15. return err
  16. }
  17. if count > 0 {
  18. return nil
  19. }
  20. newLog := entity.AssetRwCallbackLog{
  21. Currency: req.Currency,
  22. Amount: req.Amount,
  23. OrderId: req.OrderId,
  24. OpenId: req.OpenId,
  25. CreateTime: req.CreateTime,
  26. Type: req.Type,
  27. AppId: req.AppId,
  28. CustomParam: req.CustomParam,
  29. Sign: req.Sign,
  30. Status: req.Status, // 交易所返回状态码 0 1 成功 2 失败
  31. State: constant.RwCallbackStateWaiting, // 0 待处理 1 处理成功 2 处理失败
  32. }
  33. err := s.DB().Create(&newLog).Error
  34. if err != nil {
  35. return err
  36. }
  37. // 触发订单处理
  38. go s.callbackHandlerCheck()
  39. return nil
  40. }
  41. // 回调处理失败检查
  42. var callbackHandler bool = false
  43. func (s *CommonService) callbackHandlerCheck() {
  44. if callbackHandler {
  45. core.Log.Infof("回调任务处理中")
  46. return
  47. }
  48. callbackHandler = true
  49. defer func() {
  50. callbackHandler = false
  51. }()
  52. db := s.DB().Where("state", constant.RwCallbackStateWaiting)
  53. var count int64
  54. if err := db.Model(&entity.AssetRwCallbackLog{}).Count(&count).Error; err != nil {
  55. core.Log.Error(err.Error())
  56. return
  57. }
  58. if count == 0 {
  59. return
  60. }
  61. items, err := s.BatchAssetRwCallbackLog(db)
  62. if err != nil {
  63. core.Log.Error(err.Error())
  64. }
  65. for _, item := range items {
  66. switch item.Type {
  67. case exchange.TransferUserToApp:
  68. s.handleRecharge(item)
  69. break
  70. case exchange.TransferAppToUser:
  71. s.handleWithdraw(item)
  72. break
  73. }
  74. }
  75. return
  76. }
  77. // 转出回调处理逻辑
  78. // 收到转出成功回调
  79. // 1 修改回调记录状态 2 扣除用户冻结资产 3 产出流水信息
  80. // status 1 成功 2 失败
  81. func (s *CommonService) handleWithdraw(callbackLog *entity.AssetRwCallbackLog) {
  82. if callbackLog.State != constant.RwCallbackStateWaiting {
  83. return
  84. }
  85. var err error
  86. defer func() {
  87. callbackLog.State = constant.RwCallbackStateFinish
  88. callbackLog.ErrDesc = "success"
  89. if err != nil {
  90. callbackLog.State = constant.RwCallbackStateFail
  91. callbackLog.ErrDesc = err.Error()
  92. }
  93. if err = s.DB().Model(&entity.AssetRwCallbackLog{}).
  94. Where("id", callbackLog.Id).Updates(map[string]interface{}{
  95. "state": callbackLog.State,
  96. "err_desc": callbackLog.ErrDesc,
  97. }).Error; err != nil {
  98. core.Log.Error(err.Error())
  99. }
  100. }()
  101. order, err := s.FirstAssetRwRecord(s.DB().Where("order_id", callbackLog.OrderId))
  102. if err != nil {
  103. return
  104. }
  105. // 查找订单信息
  106. amount, err := decimal.NewFromString(callbackLog.Amount)
  107. if err != nil {
  108. return
  109. }
  110. // 订单状态检查 提现状态检查
  111. if order.Status != constant.RwStatePass && order.WithdrawState != constant.WithdrawStateFinish {
  112. err = fmt.Errorf("order.Status:%d WithdrawState:%d", order.Status, order.WithdrawState)
  113. return
  114. }
  115. // 金额检查 -- 这里是否改为大于则不处理?
  116. if !order.RealAmount.Equal(amount) {
  117. err = fmt.Errorf("order amount :%s app to user amount :%s", order.Amount, amount)
  118. return
  119. }
  120. // 提现状态 非1 即失败
  121. if callbackLog.Status == exchange.ExchangeRwStatusWithdrawSuccess {
  122. err = s.withdrawSuccessHandler(order)
  123. return
  124. }
  125. err = s.DB().Model(&entity.AssetRwRecord{}).Where("id", order.Id).
  126. Update("status", constant.RwStateSystemFail).Error
  127. return
  128. }
  129. // 提现成功扣除冻结处理
  130. func (s *CommonService) withdrawSuccessHandler(order *entity.AssetRwRecord) error {
  131. var err error
  132. if order.Status != constant.RwStatePass && order.WithdrawState != constant.WithdrawStateFinish {
  133. return nil
  134. }
  135. // order.Amount
  136. // 订单状态等于1 表示这个订单是审核后发起提现
  137. // 扣除冻结金额 并产生资产流水
  138. // 重新获取资产信息 -- 这个方法已经加了分布式锁 理论上是不需要再重新获取
  139. userAsset, err := s.GetAssetBySymbol(order.UserId, order.Symbol)
  140. if err != nil {
  141. return fmt.Errorf("not found asset")
  142. }
  143. dbTx := s.DB().Begin()
  144. bs := constant.BsById(constant.BsAssetWithdrawSuccess)
  145. bs.ContextName = order.TableName()
  146. bs.ContextValue = order.OrderId
  147. err = s.GenBillAndActionAsset(dbTx,
  148. userAsset.UserId,
  149. userAsset.Symbol,
  150. decimal.Zero,
  151. decimal.Zero.Sub(order.Amount),
  152. bs)
  153. if err != nil {
  154. dbTx.Rollback()
  155. return err
  156. }
  157. err = dbTx.Model(&entity.AssetRwRecord{}).
  158. Where("id", order.Id).
  159. Update("status", constant.RwStateFinish).Error
  160. if err != nil {
  161. dbTx.Rollback()
  162. return err
  163. }
  164. dbTx.Commit()
  165. return nil
  166. }
  167. // 构建充提记录
  168. // feeRatio,
  169. // feeAmount,
  170. // fee,
  171. // realAmount,
  172. func (s *CommonService) buildRwRecord(symbol string, userId int64, openId, orderId string, rwDirection int, amount, feeRatio, feeBaseAmount, fee, realAmount decimal.Decimal) *entity.AssetRwRecord {
  173. describe := fmt.Sprintf("用户%s 数量%s", constant.GetRwDirectionName(rwDirection), amount.String())
  174. return &entity.AssetRwRecord{
  175. OrderId: orderId,
  176. OpenId: openId,
  177. UserId: userId,
  178. Symbol: symbol,
  179. Amount: amount,
  180. Ratio: feeRatio,
  181. BaseFee: feeBaseAmount,
  182. Fee: fee,
  183. RealAmount: realAmount,
  184. Status: constant.RwStateWaiting,
  185. Direction: rwDirection,
  186. DirectionName: constant.GetRwDirectionName(rwDirection),
  187. Describe: describe,
  188. }
  189. }