123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471 |
- package service
- import (
- "context"
- "fmt"
- "math/rand"
- "time"
- "go-common/app/job/main/ugcpay/dao"
- "go-common/app/job/main/ugcpay/model"
- "go-common/app/job/main/ugcpay/service/pay"
- xsql "go-common/library/database/sql"
- "go-common/library/log"
- "github.com/pkg/errors"
- )
- type taskBillDaily struct {
- dao *dao.Dao
- pay *pay.Pay
- rnd *rand.Rand
- dayOffset int
- namePrefix string
- tl *taskLog
- }
- func (s *taskBillDaily) Run() (err error) {
- var (
- ctx = context.Background()
- finished bool
- expectFN = func(ctx context.Context) (expect int64, err error) {
- var (
- beginTime, endTime = dayRange(s.dayOffset)
- expectPaid, expectRefunded int64
- )
- if expectPaid, err = s.dao.CountPaidOrderUser(ctx, beginTime, endTime); err != nil {
- return
- }
- if expectRefunded, err = s.dao.CountRefundedOrderUser(ctx, beginTime, endTime); err != nil {
- return
- }
- expect = expectPaid + expectRefunded
- return
- }
- )
- if finished, err = checkOrCreateTaskFromLog(ctx, s, s.tl, expectFN); err != nil || finished {
- return
- }
- return s.run(ctx)
- }
- func (s *taskBillDaily) TTL() int32 {
- return 3600 * 2
- }
- func (s *taskBillDaily) Name() string {
- return fmt.Sprintf("%s_%d", s.namePrefix, dailyBillVer(time.Now()))
- }
- // 日账单生成
- func (s *taskBillDaily) run(ctx context.Context) (err error) {
- // 已支付成功订单入账
- paidLL := &orderPaidLL{
- limit: 1000,
- dao: s.dao,
- }
- paidLL.beginTime, paidLL.endTime = dayRange(s.dayOffset)
- if err = runLimitedList(ctx, paidLL, time.Millisecond*5, s.runPaidOrder); err != nil {
- return
- }
- // 已退款订单入账
- refundLL := &orderRefundedLL{
- limit: 1000,
- dao: s.dao,
- }
- refundLL.beginTime, refundLL.endTime = dayRange(s.dayOffset)
- return runLimitedList(ctx, refundLL, time.Millisecond*5, s.runRefundedOrder)
- }
- // 处理退款order
- func (s *taskBillDaily) runRefundedOrder(ctx context.Context, ele interface{}) (err error) {
- order, ok := ele.(*model.Order)
- if !ok {
- return errors.Errorf("refundedOrderHandler convert ele: %+v failed", order)
- }
- log.Info("runRefundedOrder handle order: %+v", order)
- logOrder := &model.LogOrder{
- OrderID: order.OrderID,
- FromState: order.State,
- ToState: model.OrderStateRefundFinished,
- }
- order.State = model.OrderStateRefundFinished
- fn := func(ctx context.Context, tx *xsql.Tx) (affected bool, err error) {
- var (
- bill *model.DailyBill
- asset *model.Asset
- ver = dailyBillVer(order.RefundTime)
- monthVer = monthlyBillVer(order.RefundTime)
- billDailyLog *model.LogBillDaily
- userIncome, _ = calcAssetIncome(order.RealFee) // 收入计算结果
- )
- affected = true
- // 获得订单对应的asset
- if asset, err = s.dao.Asset(ctx, order.OID, order.OType, order.Currency); err != nil {
- return
- }
- if asset == nil {
- err = errors.Errorf("dailyBillHander find invalid asset order, order: %+v", order)
- return
- }
- // 获得该mid对应的日账单
- if bill, err = s.dao.DailyBill(ctx, asset.MID, model.BizAsset, model.CurrencyBP, ver); err != nil {
- return
- }
- if bill == nil {
- if bill, err = s.initDailyBill(ctx, asset.MID, model.BizAsset, model.CurrencyBP, ver, monthVer); err != nil {
- return
- }
- }
- // 计算日账单
- billDailyLog = &model.LogBillDaily{
- BillID: bill.BillID,
- FromIn: bill.In,
- ToIn: bill.In,
- FromOut: bill.Out + userIncome,
- ToOut: bill.Out,
- OrderID: order.OrderID + "_r",
- }
- bill.Out += userIncome
- // 更新order
- rowAffected, err := s.dao.TXUpdateOrder(ctx, tx, order)
- if err != nil {
- tx.Rollback()
- return
- }
- if rowAffected <= 0 {
- tx.Rollback()
- log.Error("UpdateOrder no affected from order: %+v", order)
- affected = false
- return
- }
- // 添加 order log
- _, err = s.dao.TXInsertOrderUserLog(ctx, tx, logOrder)
- if err != nil {
- tx.Rollback()
- return
- }
- // 更新daily_bill
- rowAffected, err = s.dao.TXUpdateDailyBill(ctx, tx, bill)
- if err != nil {
- tx.Rollback()
- return
- }
- if rowAffected <= 0 {
- log.Error("TXUpdateDailyBill no affected bill: %+v", bill)
- tx.Rollback()
- affected = false
- return
- }
- // 添加 daily bill log , uk order_id
- _, err = s.dao.TXInsertLogDailyBill(ctx, tx, billDailyLog)
- if err != nil {
- tx.Rollback()
- return
- }
- // 更新 aggr
- aggrMonthlyAsset := &model.AggrIncomeUserAsset{
- MID: bill.MID,
- Currency: bill.Currency,
- Ver: monthVer,
- OID: order.OID,
- OType: order.OType,
- }
- aggrAllAsset := &model.AggrIncomeUserAsset{
- MID: bill.MID,
- Currency: bill.Currency,
- Ver: 0,
- OID: order.OID,
- OType: order.OType,
- }
- aggrUser := &model.AggrIncomeUser{
- MID: bill.MID,
- Currency: bill.Currency,
- }
- _, err = s.dao.TXUpsertDeltaAggrIncomeUserAsset(ctx, tx, aggrAllAsset, 0, 1, 0, userIncome)
- if err != nil {
- tx.Rollback()
- return
- }
- _, err = s.dao.TXUpsertDeltaAggrIncomeUserAsset(ctx, tx, aggrMonthlyAsset, 0, 1, 0, userIncome)
- if err != nil {
- tx.Rollback()
- return
- }
- _, err = s.dao.TXUpsertDeltaAggrIncomeUser(ctx, tx, aggrUser, 0, 1, 0, userIncome)
- if err != nil {
- tx.Rollback()
- return
- }
- log.Info("Settle daily bill: %+v, aggrAllAsset: %+v, aggrMonthlyAsset: %+v, aggrUser: %+v, from refunded order: %+v", bill, aggrAllAsset, aggrMonthlyAsset, aggrUser, order)
- return
- }
- return runTXCASTaskWithLog(ctx, s, s.tl, fn)
- }
- func (s *taskBillDaily) runPaidOrder(ctx context.Context, ele interface{}) (err error) {
- order, ok := ele.(*model.Order)
- if !ok {
- return errors.Errorf("runPaidOrder convert ele: %+v failed", order)
- }
- log.Info("runPaidOrder handle order: %+v", order)
- checkOK, payDesc, err := s.checkOrder(ctx, order) // 对支付订单对账
- if err != nil {
- return err
- }
- logOrder := &model.LogOrder{
- OrderID: order.OrderID,
- FromState: order.State,
- Desc: payDesc,
- }
- var fn func(context.Context, *xsql.Tx) (affected bool, err error)
- if checkOK { // 对账成功
- logOrder.ToState = model.OrderStateSettled
- order.State = model.OrderStateSettled
- fn = func(ctx context.Context, tx *xsql.Tx) (affected bool, err error) {
- var (
- bill *model.DailyBill
- asset *model.Asset
- ver = dailyBillVer(order.PayTime)
- monthVer = monthlyBillVer(order.PayTime)
- billDailyLog *model.LogBillDaily
- userIncome, _ = calcAssetIncome(order.RealFee) // 收入计算结果
- )
- affected = true
- // 获得订单对应的asset
- if asset, err = s.dao.Asset(ctx, order.OID, order.OType, order.Currency); err != nil {
- return
- }
- if asset == nil {
- log.Error("runPaidOrder find invalid asset order, order: %+v", order)
- return
- }
- // 获得该mid对应的日账单
- if bill, err = s.dao.DailyBill(ctx, asset.MID, model.BizAsset, model.CurrencyBP, ver); err != nil {
- return
- }
- if bill == nil {
- if bill, err = s.initDailyBill(ctx, asset.MID, model.BizAsset, model.CurrencyBP, ver, monthVer); err != nil {
- return
- }
- }
- // 计算日账单
- billDailyLog = &model.LogBillDaily{
- BillID: bill.BillID,
- FromIn: bill.In,
- ToIn: bill.In + userIncome,
- FromOut: bill.Out,
- ToOut: bill.Out,
- OrderID: order.OrderID,
- }
- bill.In += userIncome
- // 更新order
- rowAffected, err := s.dao.TXUpdateOrder(ctx, tx, order)
- if err != nil {
- tx.Rollback()
- return
- }
- if rowAffected <= 0 {
- tx.Rollback()
- log.Error("UpdateOrder no affected from order: %+v", order)
- affected = false
- return
- }
- // 添加 order log
- _, err = s.dao.TXInsertOrderUserLog(ctx, tx, logOrder)
- if err != nil {
- tx.Rollback()
- return
- }
- // 更新daily_bill
- rowAffected, err = s.dao.TXUpdateDailyBill(ctx, tx, bill)
- if err != nil {
- tx.Rollback()
- return
- }
- if rowAffected <= 0 {
- log.Error("TXUpsertDeltaDailyBill no affected bill: %+v", bill)
- tx.Rollback()
- affected = false
- return
- }
- // 添加 daily bill log , uk order_id
- _, err = s.dao.TXInsertLogDailyBill(ctx, tx, billDailyLog)
- if err != nil {
- tx.Rollback()
- return
- }
- // 更新 aggr
- aggrMonthlyAsset := &model.AggrIncomeUserAsset{
- MID: bill.MID,
- Currency: bill.Currency,
- Ver: monthVer,
- OID: order.OID,
- OType: order.OType,
- }
- aggrAllAsset := &model.AggrIncomeUserAsset{
- MID: bill.MID,
- Currency: bill.Currency,
- Ver: 0,
- OID: order.OID,
- OType: order.OType,
- }
- aggrUser := &model.AggrIncomeUser{
- MID: bill.MID,
- Currency: bill.Currency,
- }
- _, err = s.dao.TXUpsertDeltaAggrIncomeUserAsset(ctx, tx, aggrAllAsset, 1, 0, userIncome, 0)
- if err != nil {
- tx.Rollback()
- return
- }
- _, err = s.dao.TXUpsertDeltaAggrIncomeUserAsset(ctx, tx, aggrMonthlyAsset, 1, 0, userIncome, 0)
- if err != nil {
- tx.Rollback()
- return
- }
- _, err = s.dao.TXUpsertDeltaAggrIncomeUser(ctx, tx, aggrUser, 1, 0, userIncome, 0)
- if err != nil {
- tx.Rollback()
- return
- }
- log.Info("taskBillDaily: %+v, aggrAllAsset: %+v, aggrMonthlyAsset: %+v, aggrUser: %+v, from paid order: %+v", bill, aggrAllAsset, aggrMonthlyAsset, aggrUser, order)
- return
- }
- } else { // 对账失败
- logOrder.ToState = model.OrderStateBadDebt
- order.State = model.OrderStateBadDebt
- fn = func(ctx context.Context, tx *xsql.Tx) (affected bool, err error) {
- var (
- orderBadDebt *model.OrderBadDebt
- )
- affected = true
- if orderBadDebt, err = s.dao.OrderBadDebt(ctx, order.OrderID); err != nil {
- return
- }
- if orderBadDebt == nil {
- if orderBadDebt, err = s.initBadDebt(ctx, order.OrderID); err != nil {
- return
- }
- }
- orderBadDebt.Type = "unknown"
- orderBadDebt.State = "failed"
- // 更新order
- rowAffected, theErr := s.dao.TXUpdateOrder(ctx, tx, order)
- if theErr != nil {
- tx.Rollback()
- return
- }
- if rowAffected <= 0 {
- tx.Rollback()
- log.Error("UpdateOrder no affected from order: %+v", order)
- affected = false
- return
- }
- // 添加order log
- _, theErr = s.dao.TXInsertOrderUserLog(ctx, tx, logOrder)
- if theErr != nil {
- tx.Rollback()
- return
- }
- // 添加坏账表
- _, err = s.dao.TXUpdateOrderBadDebt(ctx, tx, orderBadDebt)
- if err != nil {
- tx.Rollback()
- return
- }
- log.Info("Add bad debt: %+v", orderBadDebt)
- return
- }
- }
- return runTXCASTaskWithLog(ctx, s, s.tl, fn)
- }
- func (s *taskBillDaily) checkOrder(ctx context.Context, order *model.Order) (ok bool, payDesc string, err error) {
- ok = false
- if order == nil {
- return
- }
- if order.PayID == "" {
- log.Error("Check order found baddebt order: %+v", order)
- return
- }
- payParam := s.pay.CheckOrder(order.PayID)
- s.pay.Sign(payParam)
- payJSON, err := s.pay.ToJSON(payParam)
- if err != nil {
- return
- }
- orders, err := s.dao.PayCheckOrder(ctx, payJSON)
- if err != nil {
- return
- }
- result, ok := orders[order.PayID]
- if !ok {
- return
- }
- payDesc = result.RecoStatusDesc
- switch result.RecoStatusDesc {
- case model.PayCheckOrderStateSuccess:
- ok = true
- default:
- ok = false
- }
- return
- }
- func (s *taskBillDaily) initDailyBill(ctx context.Context, mid int64, biz, currency string, ver, monthVer int64) (bill *model.DailyBill, err error) {
- bill = &model.DailyBill{}
- bill.BillID = orderID(s.rnd)
- bill.MID = mid
- bill.Biz = model.BizAsset
- bill.Currency = model.CurrencyBP
- bill.In = 0
- bill.Out = 0
- bill.Ver = ver
- bill.MonthVer = monthVer
- bill.Version = 1
- if bill.ID, err = s.dao.InsertDailyBill(ctx, bill); err != nil {
- return
- }
- return
- }
- func (s *taskBillDaily) initBadDebt(ctx context.Context, orderID string) (data *model.OrderBadDebt, err error) {
- data = &model.OrderBadDebt{
- OrderID: orderID,
- Type: "",
- State: "",
- }
- if data.ID, err = s.dao.InsertOrderBadDebt(ctx, data); err != nil {
- return
- }
- return
- }
|