package admin_agent import ( "context" "database/sql" "time" "tydata-server/app/main/model" "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 ( 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("提现类型不正确"), "提现类型验证失败") } if req.Action == ReviewActionApprove { // 确认提现 return l.approveWithdrawal(ctx, session, record) } else { // 拒绝提现 return l.rejectWithdrawal(ctx, session, record, req.Remark) } }) if err != nil { return nil, err } resp.Success = true return resp, nil } // 确认提现 func (l *AdminReviewBankCardWithdrawalLogic) approveWithdrawal(ctx context.Context, session sqlx.Session, record *model.AgentWithdrawal) error { // 根据提现类型执行不同的操作 if record.WithdrawType == WithdrawTypeAlipay { // 支付宝提现:先调用支付宝转账接口 return l.approveAlipayWithdrawal(ctx, session, record) } else { // 银行卡提现:直接更新状态为成功(线下转账) return l.approveBankCardWithdrawal(ctx, session, record) } } // 确认支付宝提现 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) } 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) } // 更新扣税记录状态为成功 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) } 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) } // 更新扣税记录状态为失败 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) } 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) } // 更新扣税记录状态为成功 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) } 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) } // 更新扣税记录状态为失败 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 "系统错误,请联系客服" }