2026-01-22 16:04:12 +08:00
|
|
|
|
package admin_agent
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"context"
|
|
|
|
|
|
"database/sql"
|
|
|
|
|
|
"time"
|
|
|
|
|
|
"tyc-server/app/main/model"
|
|
|
|
|
|
"tyc-server/common/xerr"
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
|
|
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
|
|
|
|
|
|
|
|
|
|
|
"tyc-server/app/main/api/internal/svc"
|
|
|
|
|
|
"tyc-server/app/main/api/internal/types"
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/zeromicro/go-zero/core/logx"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// 审核操作常量
|
|
|
|
|
|
const (
|
|
|
|
|
|
ReviewActionApprove = 1 // 确认
|
|
|
|
|
|
ReviewActionReject = 2 // 拒绝
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// 状态常量
|
|
|
|
|
|
const (
|
|
|
|
|
|
StatusPending = 1 // 申请中/处理中
|
|
|
|
|
|
StatusSuccess = 2 // 成功
|
|
|
|
|
|
StatusFailed = 3 // 失败
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// 提现类型常量
|
|
|
|
|
|
const (
|
|
|
|
|
|
WithdrawTypeAlipay = 1 // 支付宝提现
|
|
|
|
|
|
WithdrawTypeBankCard = 2 // 银行卡提现
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
type AdminReviewBankCardWithdrawalLogic struct {
|
|
|
|
|
|
logx.Logger
|
|
|
|
|
|
ctx context.Context
|
|
|
|
|
|
svcCtx *svc.ServiceContext
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func NewAdminReviewBankCardWithdrawalLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminReviewBankCardWithdrawalLogic {
|
|
|
|
|
|
return &AdminReviewBankCardWithdrawalLogic{
|
|
|
|
|
|
Logger: logx.WithContext(ctx),
|
|
|
|
|
|
ctx: ctx,
|
|
|
|
|
|
svcCtx: svcCtx,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (l *AdminReviewBankCardWithdrawalLogic) AdminReviewBankCardWithdrawal(req *types.AdminReviewBankCardWithdrawalReq) (resp *types.AdminReviewBankCardWithdrawalResp, err error) {
|
|
|
|
|
|
// 验证操作类型
|
|
|
|
|
|
if req.Action != ReviewActionApprove && req.Action != ReviewActionReject {
|
|
|
|
|
|
return nil, errors.Wrapf(xerr.NewErrMsg("操作类型不正确"), "操作类型验证失败")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 拒绝操作必须填写备注
|
|
|
|
|
|
if req.Action == ReviewActionReject && req.Remark == "" {
|
|
|
|
|
|
return nil, errors.Wrapf(xerr.NewErrMsg("拒绝提现必须填写拒绝原因"), "拒绝原因验证失败")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
resp = &types.AdminReviewBankCardWithdrawalResp{
|
|
|
|
|
|
Success: false,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 使用事务处理审核操作
|
|
|
|
|
|
err = l.svcCtx.AgentModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
|
|
|
|
|
// 获取提现记录
|
|
|
|
|
|
record, err := l.svcCtx.AgentWithdrawalModel.FindOne(ctx, req.WithdrawalId)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
if errors.Is(err, model.ErrNotFound) {
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrMsg("提现记录不存在"), "提现记录不存在")
|
|
|
|
|
|
}
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询提现记录失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证提现记录状态必须是申请中
|
|
|
|
|
|
if record.Status != StatusPending {
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrMsg("该提现记录已处理,无法重复操作"), "状态验证失败")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证提现类型(支持银行卡和支付宝提现)
|
|
|
|
|
|
if record.WithdrawType != WithdrawTypeBankCard && record.WithdrawType != WithdrawTypeAlipay {
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrMsg("提现类型不正确"), "提现类型验证失败")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-01 16:33:20 +08:00
|
|
|
|
// 确认时使用的扣税比例:请求中传入则用传入值,否则默认 6%
|
|
|
|
|
|
taxRate := 0.06
|
|
|
|
|
|
if req.Action == ReviewActionApprove && req.TaxRate != nil {
|
|
|
|
|
|
taxRate = *req.TaxRate
|
|
|
|
|
|
if taxRate < 0 || taxRate > 1 {
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrMsg("扣税比例必须在 0~100% 之间"), "扣税比例验证失败")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-22 16:04:12 +08:00
|
|
|
|
if req.Action == ReviewActionApprove {
|
2026-02-01 16:33:20 +08:00
|
|
|
|
// 确认提现(可带自定义扣税比例)
|
|
|
|
|
|
return l.approveWithdrawal(ctx, session, record, taxRate)
|
2026-01-22 16:04:12 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
// 拒绝提现
|
|
|
|
|
|
return l.rejectWithdrawal(ctx, session, record, req.Remark)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
resp.Success = true
|
|
|
|
|
|
return resp, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-01 16:33:20 +08:00
|
|
|
|
// 确认提现(taxRate 为扣税比例,如 0.06 表示 6%)
|
|
|
|
|
|
func (l *AdminReviewBankCardWithdrawalLogic) approveWithdrawal(ctx context.Context, session sqlx.Session, record *model.AgentWithdrawal, taxRate float64) error {
|
|
|
|
|
|
// 按审核时选择的扣税比例重新计算并更新提现记录与扣税记录
|
|
|
|
|
|
if err := l.applyReviewTaxRate(ctx, session, record, taxRate); err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
2026-01-22 16:04:12 +08:00
|
|
|
|
// 根据提现类型执行不同的操作
|
|
|
|
|
|
if record.WithdrawType == WithdrawTypeAlipay {
|
2026-02-01 16:33:20 +08:00
|
|
|
|
// 支付宝提现:按更新后的实际到账金额调用支付宝转账
|
2026-01-22 16:04:12 +08:00
|
|
|
|
return l.approveAlipayWithdrawal(ctx, session, record)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 银行卡提现:直接更新状态为成功(线下转账)
|
|
|
|
|
|
return l.approveBankCardWithdrawal(ctx, session, record)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-01 16:33:20 +08:00
|
|
|
|
// applyReviewTaxRate 按审核时选择的扣税比例更新提现记录与扣税记录
|
|
|
|
|
|
func (l *AdminReviewBankCardWithdrawalLogic) applyReviewTaxRate(ctx context.Context, session sqlx.Session, record *model.AgentWithdrawal, taxRate float64) error {
|
|
|
|
|
|
newTaxAmount := record.Amount * taxRate
|
|
|
|
|
|
newActualAmount := record.Amount - newTaxAmount
|
|
|
|
|
|
|
|
|
|
|
|
record.TaxAmount = newTaxAmount
|
|
|
|
|
|
record.ActualAmount = newActualAmount
|
|
|
|
|
|
if _, err := l.svcCtx.AgentWithdrawalModel.Update(ctx, session, record); err != nil {
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新提现记录扣税金额失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
taxModel, err := l.svcCtx.AgentWithdrawalTaxModel.FindOneByWithdrawalId(ctx, record.Id)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询扣税记录失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
taxModel.TaxRate = taxRate
|
|
|
|
|
|
taxModel.TaxAmount = newTaxAmount
|
|
|
|
|
|
taxModel.ActualAmount = newActualAmount
|
|
|
|
|
|
taxModel.TaxableAmount = record.Amount
|
|
|
|
|
|
if err := l.svcCtx.AgentWithdrawalTaxModel.UpdateWithVersion(ctx, session, taxModel); err != nil {
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新扣税记录失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-22 16:04:12 +08:00
|
|
|
|
// 确认支付宝提现
|
|
|
|
|
|
func (l *AdminReviewBankCardWithdrawalLogic) approveAlipayWithdrawal(ctx context.Context, session sqlx.Session, record *model.AgentWithdrawal) error {
|
|
|
|
|
|
// 同步调用支付宝转账
|
|
|
|
|
|
transferResp, err := l.svcCtx.AlipayService.AliTransfer(ctx, record.PayeeAccount, record.PayeeName.String, record.ActualAmount, "公司提现", record.WithdrawNo)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
l.Logger.Errorf("【支付宝转账失败】withdrawNo:%s error:%v", record.WithdrawNo, err)
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "支付宝接口调用失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
switch {
|
|
|
|
|
|
case transferResp.Status == "SUCCESS":
|
|
|
|
|
|
// 立即处理成功状态
|
|
|
|
|
|
return l.completeWithdrawalSuccess(ctx, session, record)
|
|
|
|
|
|
case transferResp.Status == "FAIL" || transferResp.SubCode != "":
|
|
|
|
|
|
// 处理明确失败
|
|
|
|
|
|
errorMsg := l.mapAlipayError(transferResp.SubCode)
|
|
|
|
|
|
return l.completeWithdrawalFailure(ctx, session, record, errorMsg)
|
|
|
|
|
|
case transferResp.Status == "DEALING":
|
|
|
|
|
|
// 处理中状态,更新为处理中但不标记为最终状态
|
|
|
|
|
|
record.Remark = sql.NullString{String: "支付宝转账处理中", Valid: true}
|
|
|
|
|
|
if _, err := l.svcCtx.AgentWithdrawalModel.Update(ctx, session, record); err != nil {
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新提现记录失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
l.Logger.Infof("支付宝提现审核通过,转账处理中 withdrawalId:%d withdrawNo:%s", record.Id, record.WithdrawNo)
|
|
|
|
|
|
return nil
|
|
|
|
|
|
default:
|
|
|
|
|
|
// 未知状态按失败处理
|
|
|
|
|
|
return l.completeWithdrawalFailure(ctx, session, record, "支付宝返回未知状态")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 确认银行卡提现
|
|
|
|
|
|
func (l *AdminReviewBankCardWithdrawalLogic) approveBankCardWithdrawal(ctx context.Context, session sqlx.Session, record *model.AgentWithdrawal) error {
|
|
|
|
|
|
// 更新提现记录状态为成功
|
|
|
|
|
|
record.Status = StatusSuccess
|
|
|
|
|
|
record.Remark = sql.NullString{String: "管理员确认提现", Valid: true}
|
|
|
|
|
|
if _, err := l.svcCtx.AgentWithdrawalModel.Update(ctx, session, record); err != nil {
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新提现记录失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 解冻资金并扣除(FrozenBalance -= amount, Balance不变)
|
|
|
|
|
|
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(ctx, record.AgentId)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询钱包失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 记录变动前的冻结余额
|
|
|
|
|
|
frozenBalanceBefore := wallet.FrozenBalance
|
|
|
|
|
|
|
|
|
|
|
|
// 更新钱包(减少冻结余额)
|
|
|
|
|
|
wallet.FrozenBalance -= record.Amount
|
|
|
|
|
|
if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet); err != nil {
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新钱包失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 记录交易流水(提现成功)
|
|
|
|
|
|
err = l.svcCtx.AgentService.CreateWalletTransaction(
|
|
|
|
|
|
ctx,
|
|
|
|
|
|
session,
|
|
|
|
|
|
wallet.AgentId,
|
|
|
|
|
|
model.WalletTransactionTypeWithdraw,
|
|
|
|
|
|
-record.Amount, // 变动金额(负数表示减少)
|
|
|
|
|
|
wallet.Balance, // 变动前余额(不变)
|
|
|
|
|
|
wallet.Balance, // 变动后余额(不变)
|
|
|
|
|
|
frozenBalanceBefore, // 变动前冻结余额
|
|
|
|
|
|
wallet.FrozenBalance, // 变动后冻结余额
|
|
|
|
|
|
record.WithdrawNo, // 关联交易ID
|
|
|
|
|
|
0, // 关联用户ID
|
|
|
|
|
|
"提现审核通过", // 备注
|
|
|
|
|
|
)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新扣税记录状态为成功
|
|
|
|
|
|
taxModel, err := l.svcCtx.AgentWithdrawalTaxModel.FindOneByWithdrawalId(ctx, record.Id)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询扣税记录失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
if taxModel.TaxStatus == model.TaxStatusPending {
|
|
|
|
|
|
taxModel.TaxStatus = model.TaxStatusSuccess // 扣税状态 = 成功
|
|
|
|
|
|
taxModel.TaxTime = sql.NullTime{Time: time.Now(), Valid: true}
|
|
|
|
|
|
if err := l.svcCtx.AgentWithdrawalTaxModel.UpdateWithVersion(ctx, session, taxModel); err != nil {
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新扣税记录失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 提现成功后,给上级代理发放提现奖励
|
|
|
|
|
|
withdrawRewardErr := l.svcCtx.AgentService.GiveWithdrawReward(ctx, record.AgentId, record.Amount, session)
|
|
|
|
|
|
if withdrawRewardErr != nil {
|
|
|
|
|
|
l.Logger.Errorf("发放提现奖励失败,代理ID:%d,提现金额:%f,错误:%+v", record.AgentId, record.Amount, withdrawRewardErr)
|
|
|
|
|
|
// 提现奖励失败不影响主流程,只记录日志
|
|
|
|
|
|
} else {
|
|
|
|
|
|
l.Logger.Infof("发放提现奖励成功,代理ID:%d,提现金额:%f", record.AgentId, record.Amount)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
l.Logger.Infof("银行卡提现确认成功 withdrawalId:%d amount:%f", record.Id, record.Amount)
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 拒绝提现
|
|
|
|
|
|
func (l *AdminReviewBankCardWithdrawalLogic) rejectWithdrawal(ctx context.Context, session sqlx.Session, record *model.AgentWithdrawal, remark string) error {
|
|
|
|
|
|
// 更新提现记录状态为失败
|
|
|
|
|
|
record.Status = StatusFailed
|
|
|
|
|
|
record.Remark = sql.NullString{String: "管理员拒绝:" + remark, Valid: true}
|
|
|
|
|
|
if _, err := l.svcCtx.AgentWithdrawalModel.Update(ctx, session, record); err != nil {
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新提现记录失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 解冻资金(FrozenBalance -= amount, Balance += amount)
|
|
|
|
|
|
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(ctx, record.AgentId)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询钱包失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 记录变动前的余额
|
|
|
|
|
|
balanceBefore := wallet.Balance
|
|
|
|
|
|
frozenBalanceBefore := wallet.FrozenBalance
|
|
|
|
|
|
|
|
|
|
|
|
// 更新钱包(余额增加,冻结余额减少)
|
|
|
|
|
|
wallet.Balance += record.Amount
|
|
|
|
|
|
wallet.FrozenBalance -= record.Amount
|
|
|
|
|
|
if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet); err != nil {
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新钱包失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 记录交易流水(解冻)
|
|
|
|
|
|
err = l.svcCtx.AgentService.CreateWalletTransaction(
|
|
|
|
|
|
ctx,
|
|
|
|
|
|
session,
|
|
|
|
|
|
wallet.AgentId,
|
|
|
|
|
|
model.WalletTransactionTypeUnfreeze,
|
|
|
|
|
|
record.Amount, // 变动金额(正数表示增加)
|
|
|
|
|
|
balanceBefore, // 变动前余额
|
|
|
|
|
|
wallet.Balance, // 变动后余额
|
|
|
|
|
|
frozenBalanceBefore, // 变动前冻结余额
|
|
|
|
|
|
wallet.FrozenBalance, // 变动后冻结余额
|
|
|
|
|
|
record.WithdrawNo, // 关联交易ID
|
|
|
|
|
|
0, // 关联用户ID
|
|
|
|
|
|
"提现拒绝,解冻资金", // 备注
|
|
|
|
|
|
)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新扣税记录状态为失败
|
|
|
|
|
|
taxModel, err := l.svcCtx.AgentWithdrawalTaxModel.FindOneByWithdrawalId(ctx, record.Id)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询扣税记录失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
if taxModel.TaxStatus == model.TaxStatusPending {
|
|
|
|
|
|
taxModel.TaxStatus = model.TaxStatusFailed // 扣税状态 = 失败
|
|
|
|
|
|
taxModel.TaxTime = sql.NullTime{Time: time.Now(), Valid: true}
|
|
|
|
|
|
if err := l.svcCtx.AgentWithdrawalTaxModel.UpdateWithVersion(ctx, session, taxModel); err != nil {
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新扣税记录失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
l.Logger.Infof("银行卡提现拒绝 withdrawalId:%d amount:%f reason:%s", record.Id, record.Amount, remark)
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 完成提现成功(支付宝转账成功后调用)
|
|
|
|
|
|
func (l *AdminReviewBankCardWithdrawalLogic) completeWithdrawalSuccess(ctx context.Context, session sqlx.Session, record *model.AgentWithdrawal) error {
|
|
|
|
|
|
// 更新提现记录状态为成功
|
|
|
|
|
|
record.Status = StatusSuccess
|
|
|
|
|
|
record.Remark = sql.NullString{String: "支付宝转账成功", Valid: true}
|
|
|
|
|
|
if _, err := l.svcCtx.AgentWithdrawalModel.Update(ctx, session, record); err != nil {
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新提现记录失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 解冻资金并扣除(FrozenBalance -= amount, Balance不变)
|
|
|
|
|
|
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(ctx, record.AgentId)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询钱包失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 记录变动前的冻结余额
|
|
|
|
|
|
frozenBalanceBefore := wallet.FrozenBalance
|
|
|
|
|
|
|
|
|
|
|
|
// 更新钱包(减少冻结余额)
|
|
|
|
|
|
wallet.FrozenBalance -= record.Amount
|
|
|
|
|
|
if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet); err != nil {
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新钱包失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 记录交易流水(提现成功)
|
|
|
|
|
|
err = l.svcCtx.AgentService.CreateWalletTransaction(
|
|
|
|
|
|
ctx,
|
|
|
|
|
|
session,
|
|
|
|
|
|
wallet.AgentId,
|
|
|
|
|
|
model.WalletTransactionTypeWithdraw,
|
|
|
|
|
|
-record.Amount, // 变动金额(负数表示减少)
|
|
|
|
|
|
wallet.Balance, // 变动前余额(不变)
|
|
|
|
|
|
wallet.Balance, // 变动后余额(不变)
|
|
|
|
|
|
frozenBalanceBefore, // 变动前冻结余额
|
|
|
|
|
|
wallet.FrozenBalance, // 变动后冻结余额
|
|
|
|
|
|
record.WithdrawNo, // 关联交易ID
|
|
|
|
|
|
0, // 关联用户ID
|
|
|
|
|
|
"提现成功", // 备注
|
|
|
|
|
|
)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新扣税记录状态为成功
|
|
|
|
|
|
taxModel, err := l.svcCtx.AgentWithdrawalTaxModel.FindOneByWithdrawalId(ctx, record.Id)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询扣税记录失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
if taxModel.TaxStatus == model.TaxStatusPending {
|
|
|
|
|
|
taxModel.TaxStatus = model.TaxStatusSuccess // 扣税状态 = 成功
|
|
|
|
|
|
taxModel.TaxTime = sql.NullTime{Time: time.Now(), Valid: true}
|
|
|
|
|
|
if err := l.svcCtx.AgentWithdrawalTaxModel.UpdateWithVersion(ctx, session, taxModel); err != nil {
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新扣税记录失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 提现成功后,给上级代理发放提现奖励
|
|
|
|
|
|
withdrawRewardErr := l.svcCtx.AgentService.GiveWithdrawReward(ctx, record.AgentId, record.Amount, session)
|
|
|
|
|
|
if withdrawRewardErr != nil {
|
|
|
|
|
|
l.Logger.Errorf("发放提现奖励失败,代理ID:%d,提现金额:%f,错误:%+v", record.AgentId, record.Amount, withdrawRewardErr)
|
|
|
|
|
|
// 提现奖励失败不影响主流程,只记录日志
|
|
|
|
|
|
} else {
|
|
|
|
|
|
l.Logger.Infof("发放提现奖励成功,代理ID:%d,提现金额:%f", record.AgentId, record.Amount)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
l.Logger.Infof("支付宝提现成功 withdrawalId:%d withdrawNo:%s", record.Id, record.WithdrawNo)
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 完成提现失败(支付宝转账失败后调用)
|
|
|
|
|
|
func (l *AdminReviewBankCardWithdrawalLogic) completeWithdrawalFailure(ctx context.Context, session sqlx.Session, record *model.AgentWithdrawal, errorMsg string) error {
|
|
|
|
|
|
// 更新提现记录状态为失败
|
|
|
|
|
|
record.Status = StatusFailed
|
|
|
|
|
|
record.Remark = sql.NullString{String: errorMsg, Valid: true}
|
|
|
|
|
|
if _, err := l.svcCtx.AgentWithdrawalModel.Update(ctx, session, record); err != nil {
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新提现记录失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 解冻资金(FrozenBalance -= amount, Balance += amount)
|
|
|
|
|
|
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(ctx, record.AgentId)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询钱包失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 记录变动前的余额
|
|
|
|
|
|
balanceBefore := wallet.Balance
|
|
|
|
|
|
frozenBalanceBefore := wallet.FrozenBalance
|
|
|
|
|
|
|
|
|
|
|
|
// 更新钱包(余额增加,冻结余额减少)
|
|
|
|
|
|
wallet.Balance += record.Amount
|
|
|
|
|
|
wallet.FrozenBalance -= record.Amount
|
|
|
|
|
|
if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet); err != nil {
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新钱包失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 记录交易流水(解冻)
|
|
|
|
|
|
err = l.svcCtx.AgentService.CreateWalletTransaction(
|
|
|
|
|
|
ctx,
|
|
|
|
|
|
session,
|
|
|
|
|
|
wallet.AgentId,
|
|
|
|
|
|
model.WalletTransactionTypeUnfreeze,
|
|
|
|
|
|
record.Amount, // 变动金额(正数表示增加)
|
|
|
|
|
|
balanceBefore, // 变动前余额
|
|
|
|
|
|
wallet.Balance, // 变动后余额
|
|
|
|
|
|
frozenBalanceBefore, // 变动前冻结余额
|
|
|
|
|
|
wallet.FrozenBalance, // 变动后冻结余额
|
|
|
|
|
|
record.WithdrawNo, // 关联交易ID
|
|
|
|
|
|
0, // 关联用户ID
|
|
|
|
|
|
"提现失败,解冻资金", // 备注
|
|
|
|
|
|
)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新扣税记录状态为失败
|
|
|
|
|
|
taxModel, err := l.svcCtx.AgentWithdrawalTaxModel.FindOneByWithdrawalId(ctx, record.Id)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询扣税记录失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
if taxModel.TaxStatus == model.TaxStatusPending {
|
|
|
|
|
|
taxModel.TaxStatus = model.TaxStatusFailed // 扣税状态 = 失败
|
|
|
|
|
|
taxModel.TaxTime = sql.NullTime{Time: time.Now(), Valid: true}
|
|
|
|
|
|
if err := l.svcCtx.AgentWithdrawalTaxModel.UpdateWithVersion(ctx, session, taxModel); err != nil {
|
|
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新扣税记录失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
l.Logger.Infof("支付宝提现失败 withdrawalId:%d withdrawNo:%s reason:%s", record.Id, record.WithdrawNo, errorMsg)
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 错误类型映射
|
|
|
|
|
|
func (l *AdminReviewBankCardWithdrawalLogic) mapAlipayError(code string) string {
|
|
|
|
|
|
errorMapping := map[string]string{
|
|
|
|
|
|
// 账户存在性错误
|
|
|
|
|
|
"PAYEE_ACCOUNT_NOT_EXSIT": "收款账户不存在,请检查账号是否正确",
|
|
|
|
|
|
"PAYEE_NOT_EXIST": "收款账户不存在或姓名有误,请核实信息",
|
|
|
|
|
|
"PAYEE_ACC_OCUPIED": "收款账号存在多个账户,无法确认唯一性",
|
|
|
|
|
|
"PAYEE_MID_CANNOT_SAME": "收款方和中间方不能是同一个人,请修改收款方或者中间方信息",
|
|
|
|
|
|
|
|
|
|
|
|
// 实名认证问题
|
|
|
|
|
|
"PAYEE_CERTIFY_LEVEL_LIMIT": "收款方未完成实名认证",
|
|
|
|
|
|
"PAYEE_NOT_RELNAME_CERTIFY": "收款方未完成实名认证",
|
|
|
|
|
|
"PAYEE_CERT_INFO_ERROR": "收款方证件信息不匹配",
|
|
|
|
|
|
|
|
|
|
|
|
// 账户状态异常
|
|
|
|
|
|
"PAYEE_ACCOUNT_STATUS_ERROR": "收款账户状态异常,请更换账号",
|
|
|
|
|
|
"PAYEE_USERINFO_STATUS_ERROR": "收款账户状态异常,无法收款",
|
|
|
|
|
|
"PERMIT_LIMIT_PAYEE": "收款账户异常,请更换账号",
|
|
|
|
|
|
"BLOCK_USER_FORBBIDEN_RECIEVE": "账户冻结无法收款",
|
|
|
|
|
|
"PAYEE_TRUSTEESHIP_ACC_OVER_LIMIT": "收款方托管子户累计收款金额超限",
|
|
|
|
|
|
|
|
|
|
|
|
// 账户信息错误
|
|
|
|
|
|
"PAYEE_USERINFO_ERROR": "收款方姓名或信息不匹配",
|
|
|
|
|
|
"PAYEE_CARD_INFO_ERROR": "收款支付宝账号及户名不一致",
|
|
|
|
|
|
"PAYEE_IDENTITY_NOT_MATCH": "收款方身份信息不匹配",
|
|
|
|
|
|
"PAYEE_USER_IS_INST": "收款方为金融机构,不能使用提现功能,请更换收款账号",
|
|
|
|
|
|
"PAYEE_USER_TYPE_ERROR": "该支付宝账号类型不支持提现,请更换收款账号",
|
|
|
|
|
|
|
|
|
|
|
|
// 权限与限制
|
|
|
|
|
|
"PAYEE_RECEIVE_COUNT_EXCEED_LIMIT": "收款次数超限,请明日再试",
|
|
|
|
|
|
"PAYEE_OUT_PERMLIMIT_CHECK_FAILURE": "收款方权限校验不通过",
|
|
|
|
|
|
"PERMIT_NON_BANK_LIMIT_PAYEE": "收款方未完善身份信息,无法收款",
|
|
|
|
|
|
}
|
|
|
|
|
|
if msg, ok := errorMapping[code]; ok {
|
|
|
|
|
|
return msg
|
|
|
|
|
|
}
|
|
|
|
|
|
return "系统错误,请联系客服"
|
|
|
|
|
|
}
|