Files
hm-server/app/main/api/internal/logic/agent/bankcardwithdrawallogic.go

225 lines
7.7 KiB
Go
Raw Normal View History

2025-12-24 17:38:08 +08:00
package agent
import (
"context"
"database/sql"
"regexp"
"time"
"tydata-server/app/main/model"
"tydata-server/common/ctxdata"
"tydata-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
// 提现类型常量
const (
WithdrawTypeAlipay = 1 // 支付宝提现
WithdrawTypeBankCard = 2 // 银行卡提现
)
type BankCardWithdrawalLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewBankCardWithdrawalLogic(ctx context.Context, svcCtx *svc.ServiceContext) *BankCardWithdrawalLogic {
return &BankCardWithdrawalLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *BankCardWithdrawalLogic) BankCardWithdrawal(req *types.BankCardWithdrawalReq) (resp *types.WithdrawalResp, err error) {
var (
outBizNo string
withdrawRes = &types.WithdrawalResp{}
2025-12-24 19:16:34 +08:00
agentID int64
2025-12-24 17:38:08 +08:00
)
var finalWithdrawAmount float64 // 实际到账金额
// 验证银行卡号格式16-19位数字
bankCardNoRegex := regexp.MustCompile(`^\d{16,19}$`)
if !bankCardNoRegex.MatchString(req.BankCardNo) {
return nil, errors.Wrapf(xerr.NewErrMsg("银行卡号格式不正确请输入16-19位数字"), "银行卡号格式验证失败")
}
// 验证开户支行不能为空
if req.BankName == "" {
return nil, errors.Wrapf(xerr.NewErrMsg("开户支行不能为空"), "开户支行验证失败")
}
// 使用事务处理核心操作
err = l.svcCtx.AgentModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
userID, err := ctxdata.GetUidFromCtx(l.ctx)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户ID失败: %v", err)
}
// 查询代理信息
agentModel, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败: %v", err)
}
2025-12-24 19:16:34 +08:00
agentID = agentModel.Id // 保存agentId用于日志
2025-12-24 17:38:08 +08:00
// 查询实名认证信息
agentRealName, err := l.svcCtx.AgentRealNameModel.FindOneByAgentId(l.ctx, agentModel.Id)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return errors.Wrapf(xerr.NewErrMsg("您未进行实名认证, 无法提现"), "您未进行实名认证")
}
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询代理实名信息失败: %v", err)
}
if agentRealName.Status != model.AgentRealNameStatusApproved {
return errors.Wrapf(xerr.NewErrMsg("您的实名认证未通过, 无法提现"), "您的实名认证未通过")
}
// 查询钱包
agentWallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(l.ctx, agentModel.Id)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理钱包失败: %v", err)
}
// 校验可提现金额
if req.Amount > agentWallet.Balance {
return errors.Wrapf(xerr.NewErrMsg("您可提现的余额不足"), "余额不足")
}
// 最低提现金额验证
if req.Amount < 50 {
return errors.Wrapf(xerr.NewErrMsg("提现金额不能低于50元"), "金额验证失败")
}
// 生成交易号
outBizNo = "BC_" + l.svcCtx.AlipayService.GenerateOutTradeNo()
// 冻结资金(事务内操作)
if err = l.freezeFunds(session, agentWallet, req.Amount); err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "资金冻结失败: %v", err)
}
yearMonth := int64(time.Now().Year()*100 + int(time.Now().Month()))
// 统一按6%收取税收
taxRate := l.svcCtx.Config.TaxConfig.TaxRate
var (
taxAmount float64 // 应缴税费
taxDeductionPart float64 // 应税金额
TaxStatus int64 // 扣税状态
exemptionAmount float64 // 免税金额固定为0
)
// 统一扣税逻辑所有提现都按6%收取税收
2026-01-04 16:10:47 +08:00
exemptionAmount = 0 // 免税金额 = 0
TaxStatus = model.TaxStatusPending // 扣税状态 = 待扣税
taxDeductionPart = req.Amount // 应税金额 = 提现金额
taxAmount = taxDeductionPart * taxRate // 应缴税费 = 应税金额 * 税率
finalWithdrawAmount = req.Amount - taxAmount // 实际到账金额 = 提现金额 - 应缴税费
2025-12-24 17:38:08 +08:00
// 创建提现记录(初始状态为申请中,提现类型为银行卡)
withdrawalID, err := l.createBankCardWithdrawalRecord(session, agentModel.Id, req.BankCardNo, req.BankName, agentRealName.Name, req.Amount, finalWithdrawAmount, taxAmount, outBizNo)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建提现记录失败: %v", err)
}
// 扣税记录
taxModel := &model.AgentWithdrawalTax{
AgentId: agentModel.Id,
YearMonth: yearMonth,
WithdrawalId: withdrawalID,
WithdrawalAmount: req.Amount,
ExemptionAmount: exemptionAmount,
TaxableAmount: taxDeductionPart,
TaxRate: taxRate,
TaxAmount: taxAmount,
ActualAmount: finalWithdrawAmount,
TaxStatus: TaxStatus,
Remark: sql.NullString{String: "银行卡提现申请,待审核扣税", Valid: true},
ExemptionRecordId: 0, // 不再使用免税额度记录
}
_, err = l.svcCtx.AgentWithdrawalTaxModel.Insert(ctx, session, taxModel)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建扣税记录失败: %v", err)
}
return nil
})
if err != nil {
return nil, err
}
// 银行卡提现不需要调用支付接口,直接返回申请中状态
withdrawRes.Status = WithdrawStatusProcessing
withdrawRes.FailMsg = ""
2025-12-24 19:16:34 +08:00
l.Logger.Infof("银行卡提现申请成功 outBizNo:%s agentId:%d amount:%f", outBizNo, agentID, req.Amount)
2025-12-24 17:38:08 +08:00
return withdrawRes, nil
}
// 创建银行卡提现记录(事务内操作)
func (l *BankCardWithdrawalLogic) createBankCardWithdrawalRecord(session sqlx.Session, agentID int64, bankCardNo string, bankName string, payeeName string, amount float64, finalWithdrawAmount float64, taxAmount float64, outBizNo string) (int64, error) {
record := &model.AgentWithdrawal{
AgentId: agentID,
2025-12-24 19:16:34 +08:00
WithdrawType: WithdrawTypeBankCard, // 银行卡提现
2025-12-24 17:38:08 +08:00
WithdrawNo: outBizNo,
PayeeAccount: bankCardNo, // 银行卡号存储在PayeeAccount字段
Amount: amount,
ActualAmount: finalWithdrawAmount,
TaxAmount: taxAmount,
Status: StatusProcessing, // 申请中状态
2025-12-24 19:16:34 +08:00
BankCardNo: sql.NullString{String: bankCardNo, Valid: true},
BankName: sql.NullString{String: bankName, Valid: true},
PayeeName: sql.NullString{String: payeeName, Valid: true},
2025-12-24 17:38:08 +08:00
}
result, err := l.svcCtx.AgentWithdrawalModel.Insert(l.ctx, session, record)
if err != nil {
return 0, err
}
return result.LastInsertId()
}
// 冻结资金(事务内操作)
func (l *BankCardWithdrawalLogic) freezeFunds(session sqlx.Session, wallet *model.AgentWallet, amount float64) error {
2026-01-07 16:50:42 +08:00
// 记录变动前的余额
balanceBefore := wallet.Balance
frozenBalanceBefore := wallet.FrozenBalance
// 更新钱包余额
2025-12-24 17:38:08 +08:00
wallet.Balance -= amount
wallet.FrozenBalance += amount
err := l.svcCtx.AgentWalletModel.UpdateWithVersion(l.ctx, session, wallet)
if err != nil {
return err
}
2026-01-07 16:50:42 +08:00
// 记录交易流水(冻结操作)
err = l.svcCtx.AgentService.CreateWalletTransaction(
l.ctx,
session,
wallet.AgentId,
model.WalletTransactionTypeFreeze,
amount, // 变动金额
balanceBefore, // 变动前余额
wallet.Balance, // 变动后余额
frozenBalanceBefore, // 变动前冻结余额
wallet.FrozenBalance, // 变动后冻结余额
"", // 关联交易ID暂时为空创建提现记录后可以更新
0, // 关联用户ID
"提现申请冻结资金", // 备注
)
if err != nil {
return err
}
2025-12-24 17:38:08 +08:00
return nil
}