// Code scaffolded by goctl. Safe to edit. // goctl 1.9.2 package wallet import ( "context" "errors" "fmt" "strings" "time" "juwan-backend/app/wallet/rpc/pb" "juwan-backend/common/utils/contextj" "juwan-backend/app/wallet/api/internal/svc" "juwan-backend/app/wallet/api/internal/types" "github.com/shopspring/decimal" "github.com/zeromicro/go-zero/core/logx" ) type TopupLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } // 充值 func NewTopupLogic(ctx context.Context, svcCtx *svc.ServiceContext) *TopupLogic { return &TopupLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *TopupLogic) Topup(req *types.TopupReq) (resp *types.EmptyResp, err error) { amount, err := decimal.NewFromString(req.Amount) if err != nil { return nil, errors.New("invalid amount") } if !amount.GreaterThan(decimal.Zero) { return nil, errors.New("amount must be greater than 0") } userID, err := contextj.UserIDFrom(l.ctx) if err != nil { return nil, err } walletResp, err := l.svcCtx.WalletRpc.GetWalletsById(l.ctx, &pb.GetWalletsByIdReq{Id: userID}) if err != nil { return nil, err } if walletResp.GetWallets() == nil { zeroAmount := "0" _, err = l.svcCtx.WalletRpc.AddWallets(l.ctx, &pb.AddWalletsReq{ UserId: userID, Balance: zeroAmount, FrozenBalance: zeroAmount, UpdatedAt: time.Now().Unix(), }) if err != nil { return nil, err } walletResp, err = l.svcCtx.WalletRpc.GetWalletsById(l.ctx, &pb.GetWalletsByIdReq{Id: userID}) if err != nil { return nil, err } if walletResp.GetWallets() == nil { return nil, errors.New("wallet not found") } } const maxRetries = 3 for i := 0; i < maxRetries; i++ { currentBalance, perr := decimal.NewFromString(walletResp.Wallets.Balance) if perr != nil { return nil, errors.New("invalid wallet balance") } frozenBalance, perr := decimal.NewFromString(walletResp.Wallets.FrozenBalance) if perr != nil { return nil, errors.New("invalid wallet frozen balance") } expectedVersion := walletResp.Wallets.Version newBalance := currentBalance.Add(amount) newBalanceStr := newBalance.String() frozenBalanceStr := frozenBalance.String() updatedAt := time.Now().Unix() _, uerr := l.svcCtx.WalletRpc.UpdateWallets(l.ctx, &pb.UpdateWalletsReq{ UserId: userID, Balance: &newBalanceStr, FrozenBalance: &frozenBalanceStr, UpdatedAt: &updatedAt, Version: &expectedVersion, }) if uerr == nil { desc := fmt.Sprintf("topup via %s", req.Method) _, err = l.svcCtx.WalletRpc.AddWalletTransactions(l.ctx, &pb.AddWalletTransactionsReq{ UserId: userID, Type: "topup", Amount: amount.String(), BalanceAfter: newBalanceStr, Description: desc, CreatedAt: updatedAt, }) if err != nil { return nil, err } return &types.EmptyResp{}, nil } if !strings.Contains(uerr.Error(), "conflict") { return nil, uerr } walletResp, err = l.svcCtx.WalletRpc.GetWalletsById(l.ctx, &pb.GetWalletsByIdReq{Id: userID}) if err != nil { return nil, err } } return nil, errors.New("wallet update conflict, please retry") }