package daichongser import ( "91jd-server/app/service/reportser" "91jd-server/models/v/advanceordermod" "context" "errors" "fmt" "strconv" "time" "go.mongodb.org/mongo-driver/bson" "91jd-server/app/appg" "91jd-server/app/service" "91jd-server/app/service/rechargeser" "91jd-server/common/crypt" "91jd-server/common/daichong" "91jd-server/common/db" "91jd-server/common/log" "91jd-server/models/e/daichongmod" "91jd-server/models/v/goldcfgmod" "91jd-server/models/v/productmod" "91jd-server/models/v/rchgordmod" "91jd-server/models/v/txnmod" "91jd-server/models/v/usermod" "91jd-server/models/v/walletmod" "github.com/shopspring/decimal" "go.mongodb.org/mongo-driver/bson/primitive" ) const ( rate_yinse_coin = 10 //金额转换成金币的单位 1元=10音色币 rate_yuan_feng = 100 //将元转换为分 ) func SeekScoreBaseOnAmount(customerId uint64, amount string) error { amt, err := strconv.Atoi(amount) if err != nil { return err } localAmount := covert2Amount(int64(amt), rate_yinse_coin) if err = appg.VideoDB.Trans(func(tool *db.MongoTool) error { //转换成本地项目中金币单位 wallet, err := walletmod.DebitAmount(tool, localAmount, customerId) if err != nil { return err } tl := txnmod.TransactionLog{ TransNo: primitive.NewObjectID(), UID: customerId, Amount: -localAmount, ActualAmount: float64(-amt), TranType: txnmod.ChaseScore.Key(), TranTypeInt: int64(txnmod.ChaseScore), Desc: fmt.Sprintf("官方代充追分-%d", amt), RealAmount: walletmod.GetRealAmount(wallet), } return txnmod.InsertTransactionLog(tool, &tl) }); err != nil { return err } return nil } // HandlerNewOrder 处理代充平台回掉调新订单通知 func HandlerNewOrder(ctx context.Context, newOrderSign daichongmod.NewOrderSign, user usermod.User) (int, error) { log.Info("HandlerNewOrder start", log.Any("parameter", newOrderSign)) //兼容时间为空的情况 if newOrderSign.SuccessAt.IsZero() { newOrderSign.SuccessAt = time.Now() } //更新订单信息 var rechargeType = string(rchgordmod.Rech_DaiChong) //转换成本地项目中金币单位 localAmount := covert2Amount(newOrderSign.Amount, rate_yinse_coin) localMoney := covert2Amount(newOrderSign.Amount, rate_yuan_feng) upsert := rchgordmod.UpserSelector{ UID: &user.UID, DevID: &user.DevID, DevType: &user.DevType, OID: &newOrderSign.OrderId, TraderId: &newOrderSign.FromId, Name: &user.Name, Tel: &user.Mobile, Amount: localAmount, NotifyTime: &newOrderSign.Time, PayMoney: &localMoney, Money: &localMoney, RechargeType: &rechargeType, Channel: &rechargeType, DiscDoc: user.DiscDoc, } processing := rchgordmod.Processing upsert.Status = &processing statusDesc := rchgordmod.Status(processing).Desc() upsert.StatusDesc = &statusDesc lock := rchgordmod.Lock upsert.LockStatus = &lock if _, err := rchgordmod.Upsert(nil, newOrderSign.OrderId, upsert); err != nil { log.ErrorX(ctx, "daichong order inset into local db failed", log.Any("param", newOrderSign), log.E(err)) return daichong.Failed, err } order, err := rchgordmod.FindRechargeOrderByThirdOrderId(newOrderSign.OrderId) if err != nil { log.ErrorX(ctx, "daichong rechargeCoin FindRechargeOrderByThirdOrderId failed", log.Any("orderId", newOrderSign.OrderId), log.E(err)) return daichong.Failed, err } var ( fn func(t *db.MongoTool) error csm int // 分成模式 vipExpireTime time.Time gold int64 ) // 商品信息解析 res := selectProduct(newOrderSign.ProductInfo, user.SysType) switch res.Type { case Gold: // 新版充值金币 if res.Version == newVersion { csm = rchgordmod.ChanShareCoin order.ProductID = res.ID if fn, gold, err = service.BuyGold(ctx, order, newOrderSign.Amount*100); err != nil { log.ErrorX(ctx, fmt.Sprintf("官方充值-订单号[%s] 购买金币异常[%v]", order.ID, err)) } } else { err = rechargeCoin(ctx, localAmount, localMoney, user.SysType, newOrderSign.OrderId, newOrderSign.SuccessAt) } case VIP: // 新版购买会员卡 if res.Version == newVersion { order.ProductID = res.ID if !res.Product.ID.IsZero() && res.Product.ProductType == productmod.AdvanceCard { err = handleAdvanceDCOrder(order, res.Product, user) if err != nil { log.ErrorX(ctx, fmt.Sprintf("官方充值-订单号[%s] 购买预售卡异常[%v]", order.ID, err)) } } if fn, csm, vipExpireTime, err = service.BuyProduct(ctx, order, newOrderSign.Amount*100); err != nil { log.ErrorX(ctx, fmt.Sprintf("官方充值-订单号[%s] 购买商品异常[%v]", order.ID, err)) } } else { log.Info("官方代充购买会员卡[旧版]") err = buyProduct(ctx, localAmount, newOrderSign.OrderId, res.Product) } case FruitCoin: order.ProductID = res.ID if fn, err = service.BuyFruitCoin(ctx, order, newOrderSign.Amount*100); err != nil { log.ErrorX(ctx, fmt.Sprintf("官方充值-订单号[%s] 购买果币异常[%v]", order.ID, err)) } } if err != nil { log.ErrorX(ctx, "HandlerNewOrder trans error", log.E(err), log.Any("orderId", newOrderSign.OrderId)) selector := rchgordmod.ReplaceEditSelector{} failure := rchgordmod.FAILURE selector.Status = &failure statusDesc := rchgordmod.Status(failure).Desc() selector.StatusDesc = &statusDesc failureAt := time.Now() selector.FailureAt = &failureAt unlock := rchgordmod.UnLock selector.LockStatus = &unlock _ = rchgordmod.UpdateBaseOnOidAndStatus(nil, newOrderSign.OrderId, rchgordmod.Processing, selector) } if err = appg.VideoDB.Trans(func(t *db.MongoTool) error { selector := rchgordmod.ReplaceEditSelector{} if fn != nil && res.Version == newVersion { if err = fn(t); err != nil { log.ErrorX(ctx, "充值金币新版执行事务异常", log.Any("order id", order.ID), log.Any("uid", order.UID), log.E(err)) return err } selector.ChanShareMod = &csm } success := rchgordmod.SUCCESS selector.Status = &success statusDesc := rchgordmod.Status(success).Desc() selector.StatusDesc = &statusDesc successAt := newOrderSign.SuccessAt selector.SuccessAt = &successAt unlock := rchgordmod.UnLock selector.LockStatus = &unlock selector.PaymentAt = &successAt selector.ProductID = res.ID selector.ServiceID = res.ServiceID //更新订单状态 if err = rchgordmod.UpdateBaseOnOidAndStatus(t, newOrderSign.OrderId, rchgordmod.Processing, selector); err != nil { log.ErrorX(ctx, "HandlerNewOrder UpdateBaseOnOidAndStatus error", log.Any("orderId", newOrderSign.OrderId), log.E(err)) return err } err = reportser.ReportOrderPaidEvent(t, order, newOrderSign.OrderId, localMoney, gold, vipExpireTime, newOrderSign.SuccessAt) if err != nil { log.ErrorX(ctx, "ReportOrderPaidEvent fail", log.E(err)) return err } return nil }); err != nil { log.ErrorX(ctx, "HandlerNewOrder trans error", log.E(err), log.Any("orderId", newOrderSign.OrderId)) err = reportser.ReportOrderPaidEvent(nil, order, newOrderSign.OrderId, localMoney, gold, vipExpireTime, newOrderSign.SuccessAt) if err != nil { log.ErrorX(ctx, "ReportOrderPaidEvent fail", log.E(err)) return daichong.Failed, err } selector := rchgordmod.ReplaceEditSelector{} failure := rchgordmod.FAILURE selector.Status = &failure statusDesc := rchgordmod.Status(failure).Desc() selector.StatusDesc = &statusDesc failureAt := time.Now() selector.FailureAt = &failureAt unlock := rchgordmod.UnLock selector.LockStatus = &unlock err = rchgordmod.UpdateBaseOnOidAndStatus(nil, newOrderSign.OrderId, rchgordmod.Processing, selector) if err != nil { log.ErrorX(ctx, "UpdateBaseOnOidAndStatus failed", log.Any("order id", newOrderSign.OrderId), log.Any("selector", selector), log.E(err)) } return daichong.Failed, err } //消息通知 充值到账成功 log.InfoX(ctx, "HandlerNewOrder success") // common.Go(func() { // //代充扣量 // executionDaiChongDeduction(ctx, newOrderSign.OrderId) // }) return daichong.Success, nil } func handleAdvanceDCOrder(order rchgordmod.RechargeOrder, product productmod.Product, u usermod.User) error { data, err := advanceordermod.IsExist(bson.M{"productID": product.ID, "uid": u.UID}) if err != nil { return errors.New("advance order query IsExist err") } // 预付订单 var advanceOrder advanceordermod.AdvanceOrder var advanceStatus = advanceordermod.DEFAULT if order.PayMoney == product.AdvanceAmount*10 { advanceStatus = advanceordermod.AdvanceProcessing } if order.PayMoney == product.BalanceAmount*10 { advanceStatus = advanceordermod.BalanceProcessing } if advanceStatus == advanceordermod.BalanceProcessing && (data == nil || data.ID.IsZero()) { return errors.New("advance order is null") } if advanceStatus == advanceordermod.DEFAULT { return errors.New("advance order status is err") } if data == nil || data.ID.IsZero() { // 创建预售订单 now := time.Now() advanceOrder.UID = u.UID advanceOrder.CreatedAt = now advanceOrder.Status = advanceordermod.AdvanceProcessing advanceOrder.AdvanceAmount = product.AdvanceAmount * 10 advanceOrder.BalanceAmount = product.BalanceAmount * 10 advanceOrder.StartTime = product.StartTime advanceOrder.EndTime = product.EndTime advanceOrder.ProductID = product.ID advanceOrder.TotalAmount = (product.AdvanceAmount + product.BalanceAmount) * 10 advanceOrder.AdvOid = order.ID.Hex() _, err = advanceordermod.CreditOrder(nil, &advanceOrder) if err != nil { return err } } else { if (advanceStatus == advanceordermod.AdvanceProcessing && data.Status != advanceordermod.AdvanceProcessing) || (advanceStatus == advanceordermod.BalanceProcessing && (data.Status != advanceordermod.BalanceProcessing && data.Status != advanceordermod.AdvanceSUCCESS)) { return errors.New("advance order status is err") } if data.Status == advanceordermod.AdvanceProcessing { // 修改订单号 err := advanceordermod.Update(nil, data.ID, bson.M{"advOid": order.ID.Hex()}) if err != nil { return err } } if data.Status == advanceordermod.BalanceProcessing || data.Status == advanceordermod.AdvanceSUCCESS { // 尾款订单 err := advanceordermod.Update(nil, data.ID, bson.M{"status": advanceordermod.BalanceProcessing, "balOid": order.ID.Hex()}) if err != nil { return err } } } return nil } // 产品选择 func selectProduct(productInfo string, sys string) (e ExtraInfo) { if productInfo == "" { return } if err := crypt.JSON2Struct(productInfo, &e); err != nil { log.Error(fmt.Sprintf("商品信息[%s]解析异常[%v]", productInfo, err)) return } switch e.Type { case VIP: // 查询产品信息 p, err := productmod.FindProduct(e.ID, sys) if err != nil { log.Error(fmt.Sprintf("商品信息[%s]查询产品信息异常[%v]", productInfo, err)) e.Type = Gold } e.Product = *p } return } // 根据商人回调过来的amount 转换本项目用户的分数 // 商人的数量是10 单位为元 本地项目用户的金额 单位为角 func covert2Amount(amount, rate int64) int64 { d1 := decimal.NewFromInt(amount) d2 := decimal.NewFromInt(rate) return d1.Mul(d2).IntPart() } // 充值金币 func rechargeCoin(ctx context.Context, localAmount, localMoney int64, sysType, orderId string, successAt time.Time) error { order, err := rchgordmod.FindRechargeOrderByThirdOrderId(orderId) if err != nil { log.Error("daichong rechargeCoin FindRechargeOrderByThirdOrderId failed", log.Any("orderId", orderId), log.E(err)) return err } csm := rchgordmod.ChanShareCoin err = appg.VideoDB.Trans(func(trans *db.MongoTool) error { //查询金币优惠 disCount, err := goldcfgmod.GetGoldCgfByAmountAndType(ctx, localAmount, string(rchgordmod.Rech_DaiChong)) if disCount != nil && err == nil { if disCount.IncrAmount != 0 { localAmount = localAmount + disCount.IncrAmount } else if disCount.IncTax != 0.0 { incr := decimal.NewFromFloat(disCount.IncTax) local := decimal.NewFromInt(localAmount) localAmount = localAmount + local.Mul(incr).IntPart() } } //用户增加货币 wallet, err := walletmod.Credit(trans, walletmod.CreditPlan{Amount: &localAmount, Consumption: &localMoney}, order.UID) if err != nil { log.Error("HandlerNewOrder CreditAmount error", log.E(err), log.Any("orderId", order.OID)) return err } selector := rchgordmod.ReplaceEditSelector{} success := rchgordmod.SUCCESS selector.Status = &success selector.ChanShareMod = &csm statusDesc := rchgordmod.Status(success).Desc() selector.StatusDesc = &statusDesc selector.SuccessAt = &successAt unlock := rchgordmod.UnLock selector.LockStatus = &unlock selector.PaymentAt = &successAt //更新订单状态 if err = rchgordmod.UpdateBaseOnOidAndStatus(trans, order.OID, rchgordmod.Processing, selector); err != nil { log.Error("HandlerNewOrder UpdateBaseOnOidAndStatus error", log.E(err), log.Any("orderId", order.OID)) return err } tl := txnmod.TransactionLog{ TransNo: order.ID, UID: order.UID, Amount: localAmount, ActualAmount: float64(localAmount), TranType: txnmod.OfficialRech.Key(), TranTypeInt: int64(txnmod.OfficialRech), Desc: txnmod.OfficialRech.Key() + "-" + fmt.Sprintf("到账%d金币", localAmount), SysType: sysType, RealAmount: walletmod.GetRealAmount(wallet), } if err = txnmod.InsertTransactionLog(trans, &tl); err != nil { log.Error("HandlerNewOrder InsertTransactionLog error", log.E(err), log.Any("orderId", order.ID)) return err } return nil }) return err } // 购买会员卡 func buyProduct(ctx context.Context, localAmount int64, orderId string, p productmod.Product) error { order, err := rchgordmod.FindRechargeOrderByThirdOrderId(orderId) if err != nil { log.Error("daichong buyProduct FindRechargeOrderByThirdOrderId failed", log.Any("orderId", orderId), log.E(err)) return err } disCount, err := goldcfgmod.GetGoldCgfByAmountAndType(ctx, localAmount, string(rchgordmod.Rech_DaiChong)) if disCount != nil && err == nil { if disCount.IncrAmount != 0 { localAmount = localAmount + disCount.IncrAmount } else if disCount.IncTax != 0.0 { incr := decimal.NewFromFloat(disCount.IncTax) local := decimal.NewFromInt(localAmount) localAmount = localAmount + local.Mul(incr).IntPart() } } disMoney := localAmount - p.DiscountedPrice if disMoney < -10 { tdesc := "购买「" + p.Name + "」实际支付" + strconv.Itoa(int(localAmount)) + ",全额转为「" + strconv.Itoa(int(localAmount)) + "」金币" return appg.VideoDB.Trans(func(t *db.MongoTool) error { set := rchgordmod.EditSelector{} csm := rchgordmod.ChanShareCoin set.ChanShareMod = &csm now := time.Now() set.SuccessAt = &now set.Status = rchgordmod.SUCCESS desc := rchgordmod.Status(rchgordmod.SUCCESS).Desc() set.StatusDesc = &desc set.PaymentAt = &now if err = rchgordmod.CallBackModifyWithProcessing(ctx, t, order.ID, set); err != nil { return err } return rechargeser.CallbackAddCoins(ctx, t, localAmount, localAmount, tdesc, order) }) } cms := 0 if p.ChanSplitMod == 1 { cms = rchgordmod.ChanShareModVIP } return appg.VideoDB.Trans(func(t *db.MongoTool) error { selector := rchgordmod.ReplaceEditSelector{} selector.ChanShareMod = &cms success := rchgordmod.SUCCESS selector.Status = &success statusDesc := rchgordmod.Status(success).Desc() selector.StatusDesc = &statusDesc successAt := time.Now() selector.SuccessAt = &successAt selector.VipID = p.ID unlock := rchgordmod.UnLock selector.LockStatus = &unlock selector.PaymentAt = &successAt if err = rchgordmod.UpdateBaseOnOidAndStatus(t, order.OID, rchgordmod.Processing, selector); err != nil { return err } creditPlan := walletmod.CreditPlan{ Consumption: &localAmount, } if _, err = walletmod.Credit(t, creditPlan, order.UID); err != nil { return err } fPayMoney := float32(localAmount) if disMoney > 0 { tdesc := "购买「" + p.Name + "」实际支付" + strconv.FormatFloat(float64(fPayMoney), 'f', 2, 64) + ",超额部分转为「" + strconv.FormatInt(disMoney, 10) + "」金币" if err = rechargeser.CallbackAddCoins(ctx, t, disMoney, 0, tdesc, order); err != nil { return err } } if err = rechargeser.RechargeBuy_p(t, order.UID, p, 0); err != nil { return err } return nil }) }