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