Files
hfb_sys/backend/internal/modules/withdrawal/user.go
T

196 lines
4.9 KiB
Go

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
})
}
// 管理员查询提现列表