Files
ycc-proxy-server/app/main/api/internal/logic/admin_agent/adminauditwithdrawallogic.go

246 lines
9.6 KiB
Go
Raw Normal View History

2025-11-27 13:09:54 +08:00
package admin_agent
import (
"context"
"database/sql"
"fmt"
2026-01-12 16:43:08 +08:00
"os"
2025-11-27 13:09:54 +08:00
"time"
"ycc-server/common/xerr"
"ycc-server/pkg/lzkit/lzUtils"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminAuditWithdrawalLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminAuditWithdrawalLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminAuditWithdrawalLogic {
return &AdminAuditWithdrawalLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
2026-01-12 16:43:08 +08:00
// parseFloat 解析配置中的浮点数
func (l *AdminAuditWithdrawalLogic) parseFloat(s string) (float64, error) {
var result float64
_, err := fmt.Sscanf(s, "%f", &result)
return result, err
}
2025-11-27 13:09:54 +08:00
func (l *AdminAuditWithdrawalLogic) AdminAuditWithdrawal(req *types.AdminAuditWithdrawalReq) (resp *types.AdminAuditWithdrawalResp, err error) {
// 1. 查询提现记录
withdrawal, err := l.svcCtx.AgentWithdrawalModel.FindOne(l.ctx, req.WithdrawalId)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询提现记录失败, %v", err)
}
// 3. 检查状态(必须是待审核状态)
if withdrawal.Status != 1 {
return nil, errors.Wrapf(xerr.NewErrMsg("该提现记录已处理"), "")
}
// 4. 使用事务处理审核
err = l.svcCtx.AgentWithdrawalModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
if req.Status == 2 { // 审核通过
2026-01-12 16:43:08 +08:00
// 4.1 根据提现方式处理
if withdrawal.WithdrawalType == 1 {
// 支付宝提现:审核通过前再次校验月度额度,避免一次性通过多笔超限
now := time.Now()
monthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
nextMonthStart := monthStart.AddDate(0, 1, 0)
// 获取支付宝月度额度配置(默认 800 元)
alipayQuota := 800.0
if cfg, cfgErr := l.svcCtx.AgentConfigModel.FindOneByConfigKey(transCtx, "alipay_month_quota"); cfgErr == nil {
if parsed, parseErr := l.parseFloat(cfg.ConfigValue); parseErr == nil && parsed > 0 {
alipayQuota = parsed
}
}
2025-11-27 13:09:54 +08:00
2026-01-12 16:43:08 +08:00
// 统计本月已成功的支付宝提现金额status=5
withdrawBuilder := l.svcCtx.AgentWithdrawalModel.SelectBuilder().
Where("agent_id = ? AND withdrawal_type = ? AND status = ? AND create_time >= ? AND create_time < ?",
withdrawal.AgentId, 1, 5, monthStart, nextMonthStart)
usedAmount, sumErr := l.svcCtx.AgentWithdrawalModel.FindSum(transCtx, withdrawBuilder, "amount")
if sumErr != nil {
return errors.Wrapf(sumErr, "查询本月支付宝提现额度使用情况失败")
}
2025-11-27 13:09:54 +08:00
2026-01-12 16:43:08 +08:00
if usedAmount+withdrawal.Amount > alipayQuota {
// 超出额度,不允许通过,保持待审核状态并提示原因
withdrawal.Status = 1
withdrawal.Remark = sql.NullString{
String: fmt.Sprintf("超过本月支付宝提现额度(限额:%.2f 元,已用:%.2f 元),请使用银行卡提现或调整金额", alipayQuota, usedAmount),
Valid: true,
}
if err := l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal); err != nil {
return errors.Wrapf(err, "更新提现记录失败")
}
return errors.Wrapf(xerr.NewErrMsg("超过本月支付宝提现额度,无法通过该笔提现"), "")
2025-11-27 13:09:54 +08:00
}
2026-01-12 16:43:08 +08:00
// 支付宝提现:开发环境下做模拟,不调用真实支付宝转账
mockTransferStatus := "SUCCESS"
var transferResp struct {
Status string
SubCode string
}
2025-11-27 13:09:54 +08:00
2026-01-12 16:43:08 +08:00
if os.Getenv("ENV") == "development" {
transferResp.Status = mockTransferStatus
logx.Infof("【DEV】模拟支付宝转账成功withdrawNo=%s, amount=%.2f, payee=%s",
withdrawal.WithdrawNo, withdrawal.ActualAmount, withdrawal.PayeeAccount)
} else {
// 生产环境:同步调用支付宝转账接口
outBizNo := withdrawal.WithdrawNo
resp, err := l.svcCtx.AlipayService.AliTransfer(transCtx, withdrawal.PayeeAccount, withdrawal.PayeeName, withdrawal.ActualAmount, "代理提现", outBizNo)
if err != nil {
// 调用失败保持状态为待审核1只记录备注方便管理员重试
withdrawal.Status = 1 // 待审核
withdrawal.Remark = sql.NullString{String: fmt.Sprintf("支付宝转账调用失败: %v", err), Valid: true}
_ = l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal)
return errors.Wrapf(err, "支付宝转账失败")
}
transferResp.Status = resp.Status
transferResp.SubCode = resp.SubCode
}
// 4.2 根据转账结果更新状态
switch transferResp.Status {
case "SUCCESS":
// 转账成功
withdrawal.Status = 5 // 提现成功
if err := l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal); err != nil {
return errors.Wrapf(err, "更新提现记录失败")
}
// 更新钱包(解冻并扣除)
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(transCtx, withdrawal.AgentId)
if err != nil {
return errors.Wrapf(err, "查询钱包失败")
}
wallet.FrozenBalance -= withdrawal.Amount
wallet.WithdrawnAmount += withdrawal.Amount
if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(transCtx, session, wallet); err != nil {
return errors.Wrapf(err, "更新钱包失败")
}
// 更新扣税记录状态
taxBuilder := l.svcCtx.AgentWithdrawalTaxModel.SelectBuilder().
Where("withdrawal_id = ?", withdrawal.Id)
taxRecords, err := l.svcCtx.AgentWithdrawalTaxModel.FindAll(transCtx, taxBuilder, "")
if err == nil && len(taxRecords) > 0 {
taxRecord := taxRecords[0]
taxRecord.TaxStatus = 2 // 已扣税
taxRecord.TaxTime = lzUtils.TimeToNullTime(time.Now())
l.svcCtx.AgentWithdrawalTaxModel.UpdateWithVersion(transCtx, session, taxRecord)
}
case "FAIL":
// 转账失败:保持待审核状态,方便人工处理或重试
withdrawal.Status = 1 // 待审核
errorMsg := l.mapAlipayError(transferResp.SubCode)
withdrawal.Remark = sql.NullString{String: errorMsg, Valid: true}
if err := l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal); err != nil {
return errors.Wrapf(err, "更新提现记录失败")
}
case "DEALING":
// 处理中同样保持待审核状态1由管理员后续确认
withdrawal.Status = 1
withdrawal.Remark = sql.NullString{String: "支付宝处理中,请稍后重试或联系平台", Valid: true}
if err := l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal); err != nil {
return errors.Wrapf(err, "更新提现记录失败")
}
}
} else if withdrawal.WithdrawalType == 2 {
// 银行卡提现:审核通过即视为提现成功(线下已/将立即打款)
2025-11-27 13:09:54 +08:00
withdrawal.Status = 5 // 提现成功
2026-01-12 16:43:08 +08:00
withdrawal.Remark = sql.NullString{String: req.Remark, Valid: true}
2025-11-27 13:09:54 +08:00
if err := l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal); err != nil {
return errors.Wrapf(err, "更新提现记录失败")
}
// 更新钱包(解冻并扣除)
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(transCtx, withdrawal.AgentId)
if err != nil {
return errors.Wrapf(err, "查询钱包失败")
}
wallet.FrozenBalance -= withdrawal.Amount
wallet.WithdrawnAmount += withdrawal.Amount
if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(transCtx, session, wallet); err != nil {
return errors.Wrapf(err, "更新钱包失败")
}
// 更新扣税记录状态
taxBuilder := l.svcCtx.AgentWithdrawalTaxModel.SelectBuilder().
2026-01-12 16:43:08 +08:00
Where("withdrawal_id = ?", withdrawal.Id)
2025-11-27 13:09:54 +08:00
taxRecords, err := l.svcCtx.AgentWithdrawalTaxModel.FindAll(transCtx, taxBuilder, "")
if err == nil && len(taxRecords) > 0 {
taxRecord := taxRecords[0]
taxRecord.TaxStatus = 2 // 已扣税
taxRecord.TaxTime = lzUtils.TimeToNullTime(time.Now())
l.svcCtx.AgentWithdrawalTaxModel.UpdateWithVersion(transCtx, session, taxRecord)
}
}
} else if req.Status == 3 { // 审核拒绝
// 4.1 更新提现记录状态为拒绝
withdrawal.Status = 3 // 审核拒绝
withdrawal.Remark = sql.NullString{String: req.Remark, Valid: true}
if err := l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal); err != nil {
return errors.Wrapf(err, "更新提现记录失败")
}
// 4.2 解冻余额
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(transCtx, withdrawal.AgentId)
if err != nil {
return errors.Wrapf(err, "查询钱包失败")
}
wallet.FrozenBalance -= withdrawal.Amount
wallet.Balance += withdrawal.Amount
if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(transCtx, session, wallet); err != nil {
return errors.Wrapf(err, "更新钱包失败")
}
}
return nil
})
if err != nil {
return nil, err
}
return &types.AdminAuditWithdrawalResp{
Success: true,
}, nil
}
// mapAlipayError 映射支付宝错误码
func (l *AdminAuditWithdrawalLogic) mapAlipayError(code string) string {
errorMapping := map[string]string{
"PAYEE_USERINFO_ERROR": "收款方姓名或信息不匹配",
"PAYEE_CARD_INFO_ERROR": "收款支付宝账号及户名不一致",
"PAYEE_IDENTITY_NOT_MATCH": "收款方身份信息不匹配",
"PAYEE_USER_IS_INST": "收款方为金融机构,不能使用提现功能",
"PAYEE_USER_TYPE_ERROR": "该支付宝账号类型不支持提现",
}
if msg, ok := errorMapping[code]; ok {
return msg
}
return "系统错误,请联系客服"
}