package withdrawal import ( "context" "errors" "hfb_sys/backend/internal/model" "hfb_sys/backend/internal/modules/wallet" "gorm.io/gorm" ) // 用户创建提现申请 func (r *Repository) Create(ctx context.Context, userID uint64, req CreateWithdrawalRequest) (*WithdrawalDTO, error) { db := r.db.WithContext(ctx) // 验证收款账号 var paymentAccount model.UserPaymentAccount if err := db.Where("id = ? AND user_id = ? AND status = ?", req.PaymentAccountID, userID, "active"). First(&paymentAccount).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, errors.New("payment account not found") } return nil, err } // 计算手续费和实际到账金额(分) feeCent := int64(float64(req.AmountCent) * WithdrawalFeeRate) actualAmountCent := req.AmountCent - feeCent // 生成提现单号 withdrawNo, err := generateWithdrawNo() if err != nil { return nil, err } // 解密账号(用于存储快照) decryptedNo, err := r.decryptAccountNo(paymentAccount.AccountNo) if err != nil { decryptedNo = paymentAccount.AccountNo } withdrawal := model.WithdrawalRequest{ WithdrawNo: withdrawNo, UserID: userID, AmountCent: req.AmountCent, FeeCent: feeCent, ActualAmountCent: actualAmountCent, PaymentAccountID: &req.PaymentAccountID, AccountType: paymentAccount.AccountType, AccountName: paymentAccount.AccountName, AccountNo: maskAccountNo(decryptedNo, paymentAccount.AccountType), BankName: paymentAccount.BankName, BankBranch: paymentAccount.BankBranch, Status: "pending", } // 事务处理 err = db.Transaction(func(tx *gorm.DB) error { // 创建提现申请 if err := tx.Create(&withdrawal).Error; err != nil { return err } // 冻结用户余额 if err := wallet.AppendEntries(tx, wallet.Entry{ UserID: userID, Direction: "out", AmountCent: req.AmountCent, BalanceType: "available", BizType: "withdraw_freeze", BizNo: withdrawNo, Remark: "提现冻结", }, wallet.Entry{ UserID: userID, Direction: "in", AmountCent: req.AmountCent, BalanceType: "frozen", BizType: "withdraw_freeze", BizNo: withdrawNo, Remark: "提现冻结", }); err != nil { return err } return nil }) if err != nil { return nil, err } return r.FindByID(ctx, userID, withdrawal.ID) } // 用户查询提现列表 // 用户查询提现列表 func (r *Repository) List(ctx context.Context, userID uint64, page, pageSize int) (*PaginatedResult, error) { db := r.db.WithContext(ctx) var total int64 if err := db.Model(&model.WithdrawalRequest{}). Where("user_id = ?", userID). Count(&total).Error; err != nil { return nil, err } offset := (page - 1) * pageSize var withdrawals []model.WithdrawalRequest if err := db.Where("user_id = ?", userID). Order("created_at DESC"). Offset(offset).Limit(pageSize). Find(&withdrawals).Error; err != nil { return nil, err } items := make([]WithdrawalDTO, 0, len(withdrawals)) for _, w := range withdrawals { items = append(items, toDTO(w)) } return &PaginatedResult{ Items: items, Total: total, Page: page, PageSize: pageSize, }, nil } // 用户查询提现详情 // 用户查询提现详情 func (r *Repository) FindByID(ctx context.Context, userID, id uint64) (*WithdrawalDTO, error) { var withdrawal model.WithdrawalRequest if err := r.db.WithContext(ctx).Where("id = ? AND user_id = ?", id, userID). First(&withdrawal).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, ErrWithdrawalNotFound } return nil, err } dto := toDTO(withdrawal) return &dto, nil } // 用户取消提现 // 用户取消提现 func (r *Repository) Cancel(ctx context.Context, userID, id uint64) error { db := r.db.WithContext(ctx) var withdrawal model.WithdrawalRequest if err := db.Where("id = ? AND user_id = ?", id, userID). First(&withdrawal).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return ErrWithdrawalNotFound } return err } // 只有待审核状态可以取消 if withdrawal.Status != "pending" { return ErrWithdrawalLocked } return db.Transaction(func(tx *gorm.DB) error { // 更新状态 if err := tx.Model(&withdrawal).Update("status", "cancelled").Error; err != nil { return err } // 解冻余额 if err := wallet.AppendEntries(tx, wallet.Entry{ UserID: userID, Direction: "out", AmountCent: withdrawal.AmountCent, BalanceType: "frozen", BizType: "withdraw_cancel", BizNo: withdrawal.WithdrawNo, Remark: "用户取消提现", }, wallet.Entry{ UserID: userID, Direction: "in", AmountCent: withdrawal.AmountCent, BalanceType: "available", BizType: "withdraw_cancel", BizNo: withdrawal.WithdrawNo, Remark: "用户取消提现", }); err != nil { return err } return nil }) } // 管理员查询提现列表