01 · 代理提现审核 API 需求书¶
目标读者:后台开发工程师、风控团队、技术 Leader 核心问题:代理提现审核当前人工重复性极高,能不能通过一个查询 API + Telegram 机器人一次性完成? 优先级:P0(副组长工作流中最耗时、最重复、判定规则最明确的任务) 配套: 副组长工作指南 4.5.4 代理提现反欺诈 作者:Bob | 最后更新:2026-04-11
一、业务背景与痛点¶
1.1 当前人工流程¶
副组长每天处理大量代理提现订单。对每一笔,需要依次访问多个后台页面做一系列判定:
收到代理提现订单
│
▼
Step 1:锁定订单(防止同事重复处理)
│
▼
Step 2:进入代理信息页
│ - 复制代理注册 IP
│ - 复制代理登录 IP
│ - 查代理账户余额
│ - 查代理最近充值记录
▼
Step 3:进入下级账号页
│ - 逐个查看每个下级用户的注册 IP / 登录 IP
│ - 页面高亮下级互相同 IP 的记录
│ - 用浏览器 Ctrl+F 搜索是否有下级 IP 与代理同
▼
Step 4:对每个疑似下级 → 查用户账户变动明细
│ - 查最近一次领彩金(如邀请奖励)的时间点
│ - 查领彩金前的账户余额
│ - 查领彩金前后是否有充值
│ - 查领彩金后的游戏盈亏
▼
Step 5:综合判定
│ - 是否 IP 碰撞?
│ - 领彩金前是否有"自然余额"?
│ - 彩金 + 彩金后盈亏是否都来自自刷?
▼
Step 6:下结论
- 通过 / 驳回 / 部分驳回(只扣刷单金额)
- 如果驳回,还要锁定代理的下注 / 提现 / 佣金领取权限
1.2 痛点量化¶
| 维度 | 数值 |
|---|---|
| 单次审核耗时 | 5~15 分钟(多级跳转 + 多次复制粘贴 + 心算) |
| 每天单数 | 几十笔起步(大促期翻倍) |
| 每天总耗时 | 2~3 小时(副组长所有工作里占比最大) |
| 错误后果 | 放过自刷代理 → 资金直接损失;误杀真实代理 → 客诉 |
| 心智负担 | 高(多级关联判断,容易眼花看错) |
1.3 为什么应该自动化¶
- 数据完全在后台数据库 —— 无需人工观察
- 判定规则长期验证,可明文代码化 —— 见第三节
- 只需要返回"判定意见",最终决策仍由人工 —— 安全风险可控
- Telegram 已是团队标配沟通工具 —— 无需引入新系统
- 投入产出比极高 —— 一次开发,每天节省 2~3 小时
二、自动化方案总览¶
运营副组长(在 Telegram)
│
│ /check <订单号>
▼
┌─────────────────────────────────────────┐
│ Telegram Bot(部署在 VPS) │
│ - 接收命令 │
│ - 解析订单号 │
└──────────────┬──────────────────────────┘
│ HTTP GET + Bearer Token
│ IP 白名单校验
▼
┌─────────────────────────────────────────┐
│ 后台 API 网关(新开发) │
│ /api/risk/agent-withdrawal-audit │
│ ?order_id=xxx │
└──────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ 数据聚合层 │
│ - 查代理信息 │
│ - 查下级账号 │
│ - 对每个下级查余额明细 │
│ - 查彩金领取记录 │
└──────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ 判定引擎 │
│ - IP 去重算法 │
│ - 余额溯源算法 │
│ - 自刷识别算法 │
└──────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Telegram 回复 │
│ - 建议:通过 / 驳回 / 部分驳回 │
│ - 证据链(为什么这样判定) │
│ - 可扣款金额(如果部分驳回) │
└─────────────────────────────────────────┘
三、核心判定算法(可直接代码化)¶
这是整份需求书的灵魂,以下规则均由副组长长期实践验证。
3.1 规则 A · IP 去重(判断下级是否是自己小号)¶
目标:识别"代理注册 / 登录 IP" 和 "下级注册 / 登录 IP" 是否属于同一物理网络。
为什么不能精确匹配: - 大部分 ISP 会给用户动态 IP - 同一用户在家里的路由器后 IP 尾数可能变化 - 精确匹配会大量漏判
为什么不能全模糊: - 太模糊会大量误判(同省份/同城市的用户会被误杀)
算法:前缀匹配
┌────────────────────────────────────────────────────────────┐
│ 输入:ip_a, ip_b(字符串) │
│ 输出:boolean(是否属于同一子网) │
│ │
│ Step 1:判断 IP 版本 │
│ if ip 含 ":" → IPv6 │
│ else → IPv4 │
│ │
│ Step 2:归一化 │
│ IPv4:保留 "最后一个 . 之前" 的所有内容 │
│ 例:192.168.1.23 → "192.168.1" │
│ 192.168.1.45 → "192.168.1" │
│ │
│ IPv6:保留 "第三个 : 之前" 的所有内容 │
│ 例:2001:0db8:85a3:0000:0000:8a2e:0370:7334 │
│ → "2001:0db8:85a3" │
│ 2001:0db8:85a3:abcd::1234 │
│ → "2001:0db8:85a3" │
│ │
│ Step 3:比较归一化后的字符串 │
│ 相等 → 判定为同 IP(同一物理网络) │
│ 不等 → 判定为不同 │
└────────────────────────────────────────────────────────────┘
参考实现(Python 伪代码):
def normalize_ip(ip: str) -> str:
"""
IP 归一化用于"同子网"判定
- IPv4 保留最后一个 . 之前的部分(即 /24 子网)
- IPv6 保留第三个 : 之前的部分(即 /48 子网)
"""
if ':' in ip: # IPv6
parts = ip.split(':')
# 取前 3 段
return ':'.join(parts[:3])
else: # IPv4
last_dot = ip.rfind('.')
return ip[:last_dot] if last_dot > 0 else ip
def same_network(ip_a: str, ip_b: str) -> bool:
"""判断两个 IP 是否属于同一子网"""
if not ip_a or not ip_b:
return False
return normalize_ip(ip_a) == normalize_ip(ip_b)
# 测试用例
assert same_network('192.168.1.23', '192.168.1.45') == True
assert same_network('192.168.1.23', '192.168.2.23') == False
assert same_network(
'2001:0db8:85a3:0000:0000:8a2e:0370:7334',
'2001:0db8:85a3:abcd::1234',
) == True
assert same_network(
'2001:0db8:85a3::1',
'2001:0db8:ffff::1',
) == False
完整 IP 碰撞检测:
def detect_ip_collision(agent: dict, downlines: list[dict]) -> dict:
"""
检测代理和下级之间是否存在 IP 碰撞
检查两个维度:
1. 代理的 IP(注册 / 登录)与任一下级的 IP
2. 下级之间互相的 IP
返回:
{
'has_collision': bool,
'collisions': [
{ 'type': 'agent_vs_downline', 'downline_id': X, 'reason': '...' },
{ 'type': 'downline_vs_downline', 'downline_ids': [X, Y], 'reason': '...' },
]
}
"""
collisions = []
agent_ips = [agent.get('register_ip'), agent.get('last_login_ip')]
agent_ips = [ip for ip in agent_ips if ip]
# 维度 1:代理 vs 下级
for dl in downlines:
dl_ips = [dl.get('register_ip'), dl.get('last_login_ip')]
dl_ips = [ip for ip in dl_ips if ip]
for a_ip in agent_ips:
for d_ip in dl_ips:
if same_network(a_ip, d_ip):
collisions.append({
'type': 'agent_vs_downline',
'downline_id': dl['user_id'],
'agent_ip': a_ip,
'downline_ip': d_ip,
'reason': f'代理 IP {a_ip} 与下级 {dl["user_id"]} IP {d_ip} 同子网',
})
# 维度 2:下级互相
for i in range(len(downlines)):
for j in range(i + 1, len(downlines)):
dl1, dl2 = downlines[i], downlines[j]
dl1_ips = [dl1.get('register_ip'), dl1.get('last_login_ip')]
dl2_ips = [dl2.get('register_ip'), dl2.get('last_login_ip')]
for ip1 in dl1_ips:
for ip2 in dl2_ips:
if ip1 and ip2 and same_network(ip1, ip2):
collisions.append({
'type': 'downline_vs_downline',
'downline_ids': [dl1['user_id'], dl2['user_id']],
'ip1': ip1,
'ip2': ip2,
'reason': f'下级 {dl1["user_id"]} 与下级 {dl2["user_id"]} IP 同子网',
})
return {
'has_collision': len(collisions) > 0,
'collisions': collisions,
}
3.2 规则 B · 余额溯源(判断彩金领取后的盈亏归属)¶
目标:对每个疑似下级,算出"如果这个用户是自刷的话,应当保留的余额"。
核心思想:一个真实用户领取彩金(如邀请奖励)之前的余额,是他自己充值或正常游戏赢来的 —— 这部分应该保留;彩金本身 + 彩金之后的游戏盈亏,如果判定为自刷,应当全部扣除。
算法:
┌──────────────────────────────────────────────────────────────┐
│ 输入:user_id(疑似下级) │
│ 输出: │
│ { │
│ balance_before_bonus: 领彩金前余额, │
│ bonus_amount: 彩金金额, │
│ profit_after_bonus: 彩金后游戏净盈亏, │
│ deposit_between: 彩金前后是否有充值, │
│ recommendation: 'keep_original' | 'clawback' | 'partial' │
│ } │
│ │
│ Step 1:查 user_id 的账户变动明细(时间倒序) │
│ │
│ Step 2:找到"最近一次领彩金"的记录 │
│ (类型 = 邀请奖励 / 首充彩金 / 活动彩金等) │
│ │
│ Step 3:记录该彩金记录【前一刻】的账户余额 │
│ → balance_before_bonus │
│ │
│ Step 4:记录该彩金的金额 │
│ → bonus_amount │
│ │
│ Step 5:查该彩金之后的所有账户变动 │
│ - 游戏输赢 → 累计到 profit_after_bonus │
│ - 充值 → deposit_between 标记 True │
│ - 其它收入 → 记入 profit_after_bonus │
│ │
│ Step 6:输出 │
└──────────────────────────────────────────────────────────────┘
处理建议(基于 IP 碰撞 + 余额溯源的组合):
def recommend_action(ip_collision_result, balance_tracing) -> dict:
"""
综合 IP 碰撞结果 + 余额溯源,给出处理建议
"""
if not ip_collision_result['has_collision']:
return {
'action': 'approve',
'reason': '未发现 IP 碰撞',
'refund_amount': 0,
}
# 有 IP 碰撞 → 判定为自刷代理
# 对每个疑似下级,计算应扣除金额
total_clawback = 0
details = []
for downline_trace in balance_tracing:
# 保留的:领彩金前余额
keep = downline_trace['balance_before_bonus']
# 扣除的:彩金 + 彩金后的净盈亏
clawback = downline_trace['bonus_amount'] + max(
downline_trace['profit_after_bonus'], 0
)
# ⚠️ 注意:彩金后如果有充值,这部分充值应保留(不是自刷)
if downline_trace['deposit_between']:
clawback -= downline_trace['deposit_amount']
total_clawback += clawback
details.append({
'user_id': downline_trace['user_id'],
'keep': keep,
'clawback': clawback,
})
return {
'action': 'reject_and_clawback',
'reason': 'IP 碰撞 + 疑似自刷',
'refund_amount': total_clawback,
'details': details,
'lock_actions': [
'ban_betting', # 禁止下注
'ban_withdrawal', # 禁止提现
'ban_commission', # 禁止佣金领取
],
}
3.3 规则 C · 彩金类型识别¶
问题:不是所有"彩金"都要按自刷处理。
需要识别的彩金类型: - 邀请奖励 ⭐ —— 核心目标,自刷代理的主要套利点 - 首充彩金 —— 真实新人也会领,需要结合 IP 判定 - 活动彩金 —— 真实活跃用户也会领,不应因 IP 碰撞就扣 - VIP 周/月奖励 —— 自动发放,不涉及自刷 - 签到彩金 —— 每日固定,不涉及自刷 - 红包雨 —— 金额小,不涉及自刷
建议 API 返回中明确标注每笔彩金的类型,让判定引擎按类型决定是否扣除。
四、API 接口设计¶
4.1 端点¶
GET /api/risk/agent-withdrawal-audit
4.2 认证¶
Authorization: Bearer <API_TOKEN>
4.3 请求参数¶
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
order_id |
string | ✅ | 提现订单号 |
示例:
curl -X GET \
"https://api.platform-a.example.com/api/risk/agent-withdrawal-audit?order_id=WO20260411001" \
-H "Authorization: Bearer abc123..."
4.4 响应 Schema(完整)¶
{
"order_id": "WO20260411001",
"platform": "platform_a",
"agent": {
"user_id": 10001,
"username": "agent_xiaoming",
"register_ip": "103.45.67.89",
"last_login_ip": "103.45.67.90",
"balance": 8500.00,
"withdrawal_amount": 5000.00,
"total_commission_earned": 12000.00,
"total_deposit": 500.00,
"registered_at": "2026-01-15T10:30:00+07:00"
},
"downlines": [
{
"user_id": 10010,
"username": "user_001",
"register_ip": "103.45.67.91",
"last_login_ip": "103.45.67.92",
"registered_at": "2026-01-16T14:20:00+07:00",
"current_balance": 150.00,
"total_deposit": 100.00,
"bonus_history": [
{
"type": "invite_reward",
"amount": 88.00,
"granted_at": "2026-01-16T14:25:00+07:00",
"balance_before": 12.00,
"deposit_between": false,
"deposit_amount_between": 0,
"profit_after": 45.00
}
]
}
],
"risk_analysis": {
"ip_collision": {
"has_collision": true,
"collisions": [
{
"type": "agent_vs_downline",
"agent_ip": "103.45.67.89",
"downline_id": 10010,
"downline_ip": "103.45.67.91",
"reason": "代理注册 IP 103.45.67.89 与下级 10010 注册 IP 103.45.67.91 同 /24 子网"
}
]
},
"balance_tracing": {
"suspicious_downlines": [
{
"user_id": 10010,
"balance_before_bonus": 12.00,
"bonus_amount": 88.00,
"profit_after_bonus": 45.00,
"deposit_between": false,
"suggested_clawback": 133.00,
"suggested_keep": 12.00
}
]
}
},
"recommendation": {
"action": "reject_and_clawback",
"confidence": "high",
"reason": "代理与下级 10010 存在 IP 碰撞,且该下级账户余额主要来自邀请奖励后的自刷盈利",
"suggested_clawback_total": 133.00,
"suggested_lock_actions": [
"ban_betting",
"ban_withdrawal",
"ban_commission"
],
"evidence_summary": [
"IP 碰撞:代理 103.45.67.89 vs 下级 10010 的 103.45.67.91(/24 同子网)",
"下级 10010 领取邀请奖励前余额仅 12 元",
"邀请奖励 88 元 + 后续游戏盈利 45 元,合计 133 元疑似自刷",
"该下级彩金前后无充值记录"
]
},
"human_readable_summary": "⚠️ 建议驳回:\n- 代理与 1 名下级 IP 碰撞\n- 该下级领取邀请奖励 88 元前余额仅 12 元\n- 领奖后游戏盈利 45 元\n- 建议扣除 133 元并锁定下注/提现/佣金权限",
"api_version": "v1",
"generated_at": "2026-04-11T15:03:00+07:00"
}
4.5 错误响应¶
| HTTP | Error Code | 含义 |
|---|---|---|
| 400 | INVALID_ORDER_ID |
订单号格式错误 |
| 401 | UNAUTHORIZED |
Token 错误 |
| 403 | IP_NOT_ALLOWED |
调用方 IP 不在白名单 |
| 404 | ORDER_NOT_FOUND |
订单不存在 |
| 409 | ORDER_NOT_AGENT_WITHDRAWAL |
不是代理提现订单,不适用此 API |
| 500 | INTERNAL_ERROR |
后台错误 |
五、安全设计¶
5.1 IP 白名单¶
Telegram Bot 服务器
│
│ IP: 203.0.113.42
▼
后台 API 网关
│
│ 防火墙规则:
│ allow 203.0.113.42/32
│ deny all others
▼
API 端点
实施要点: - 只允许 Telegram Bot 所在服务器 IP 访问 - 如果 Bot 部署在 Cloud Run / Lambda 这类动态 IP 环境,改用 VPC Peering 或 静态出口 IP - Bot 服务器做好加固:SSH 限 IP / 只装必要软件 / 定期补丁
5.2 Token 认证¶
Authorization: Bearer <token>
- Token 长度 ≥ 32 字节
- 存储在 Bot 服务器的环境变量(不入代码库)
- 每季度轮换一次
- 泄漏应急流程:立刻作废,重新生成
5.3 只读 API¶
铁律:本 API 只查询、不修改。任何"驳回订单/锁定账户/扣款"动作都必须人工在后台执行。
理由: - 即使 IP 白名单 + Token,一旦被绕过,只读 API 最多泄漏数据;有写权限的 API 会造成直接资金损失 - 判定结果返回给人,由副组长确认后再执行,保留人类审核环节
5.4 审计日志¶
后台必须记录:
{"timestamp":"2026-04-11T15:03:00Z","caller_ip":"203.0.113.42","order_id":"WO20260411001","agent_id":10001,"result":"reject_and_clawback","latency_ms":245}
用于: - 异常检测(调用频率突增 → 告警) - 事后审计 - 性能监控
六、Telegram Bot 交互设计¶
6.1 命令格式¶
/check <order_id>
6.2 交互示例¶
副组长发送:
/check WO20260411001
Bot 回复:
📋 订单 WO20260411001 审核结果
🎯 建议:❌ 驳回并扣款
置信度:高
📊 风险分析:
• IP 碰撞:代理 vs 下级 10010 (/24 同子网)
• 下级 10010 领奖前余额仅 12 元
• 邀请奖励 88 元 + 后续盈利 45 元
• 无有效充值记录
💰 建议扣款:133 元
🔒 建议锁定:下注 / 提现 / 佣金领取
📎 证据链:
1. 代理 IP 103.45.67.89 vs 下级 10010 IP 103.45.67.91
2. 余额溯源:12 → 领奖 88 → 盈利 45 → 当前 150
⏱ 审核耗时:245ms
🕐 生成时间:15:03:00
6.3 进阶:交互式按钮¶
[✅ 已按建议处理] [❌ 我决定不采纳,原因:____]
点击后 Bot 记录决策日志,用于日后回顾判定引擎准确率。
6.4 Bot 核心代码(Python 参考)¶
# bot.py
import os
import httpx
from telegram import Update
from telegram.ext import Application, CommandHandler, ContextTypes
API_BASE = os.environ['API_BASE'] # 后台 API 根 URL
API_TOKEN = os.environ['API_TOKEN'] # Bearer token
BOT_TOKEN = os.environ['TG_BOT_TOKEN'] # Telegram Bot token
async def check_order(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""处理 /check <order_id> 命令"""
if not context.args:
await update.message.reply_text('用法:/check <订单号>')
return
order_id = context.args[0]
async with httpx.AsyncClient() as client:
try:
resp = await client.get(
f'{API_BASE}/api/risk/agent-withdrawal-audit',
params={'order_id': order_id},
headers={'Authorization': f'Bearer {API_TOKEN}'},
timeout=10.0,
)
resp.raise_for_status()
except httpx.HTTPError as e:
await update.message.reply_text(f'❌ API 错误:{e}')
return
data = resp.json()
summary = data.get('human_readable_summary', '无结果')
await update.message.reply_text(
f'📋 订单 {order_id} 审核结果\n\n{summary}',
parse_mode='Markdown',
)
def main():
app = Application.builder().token(BOT_TOKEN).build()
app.add_handler(CommandHandler('check', check_order))
app.run_polling()
if __name__ == '__main__':
main()
七、预期收益¶
7.1 时间节省¶
| 维度 | 当前(人工) | 自动化后 | 节省 |
|---|---|---|---|
| 单次审核耗时 | 5~15 分钟 | 10 秒 | ~95% |
| 每天总耗时(假设 30 单) | 150~450 分钟 | 5~10 分钟 | 2~7 小时/天 |
| 每月节省 | — | — | 60~210 小时/月 |
7.2 错误率¶
| 维度 | 当前 | 自动化后 |
|---|---|---|
| IP 碰撞漏判 | 存在(眼花) | 0(算法覆盖所有组合) |
| 余额计算错 | 存在(心算) | 0(数据库直查) |
| 彩金类型混淆 | 偶发 | 0(明确分类) |
| 决策一致性 | 值班人之间有差异 | 完全一致 |
7.3 副产品¶
- 审计链 —— 每次判定的完整证据链入库,方便事后追溯
- 新人友好 —— 新上任副组长不用再记那一堆复杂规则,看机器人输出就行
- 判定规则版本化 —— 规则调整后所有人同时生效,不用逐个通知
八、实施路线¶
8.1 Phase 0 · 对齐与评审(本文档)¶
副组长整理需求 → 技术 Leader 评审 → 安全团队 review IP 白名单方案
8.2 Phase 1 · 只读 MVP¶
后台工程师开发: - [x] API 端点 - [x] IP 归一化函数 - [x] 余额溯源查询 - [x] 判定引擎(可先用简化版) - [x] 审计日志
验收:手动 curl 能拿到完整 JSON,返回内容可人工复核
8.3 Phase 2 · Telegram Bot 接入¶
- [ ] 部署 Bot 到 VPS
- [ ] 配置 IP 白名单
- [ ] Token 下发
- [ ]
/check命令
验收:在 Telegram 里发订单号能收到判定结果
8.4 Phase 3 · 规则精调¶
副组长用 1 周,人机并行跑: - 每单都既手工审核又发给机器人 - 对比两者结论 - 差异的单独拉出来分析(是机器错还是人错) - 根据对比结果调整判定规则
8.5 Phase 4 · 全量切换¶
副组长默认信任机器人输出,只在机器人标 confidence: low 时人工复核
8.6 Phase 5 · 持续优化¶
- 把机器人输出的"可扣款金额"和"锁定建议"做成按钮,一键执行(此时才需要写权限 API,安全要求更高)
- 加入对"首充彩金""活动彩金"等更多彩金类型的精细化判定
- 把判定引擎迁移成规则引擎(如 Drools / JSONLogic),运营能自助调参
九、测试用例(给后台开发参考)¶
用例 1:清白代理(无 IP 碰撞)¶
输入:
order_id: WO_CLEAN_001
代理 IP: 192.168.1.10
下级 1 IP: 203.0.113.25
下级 2 IP: 198.51.100.48
(所有 IP 均不同子网)
期望输出:
recommendation.action: approve
risk_analysis.ip_collision.has_collision: false
用例 2:代理自刷(IP 碰撞 + 彩金自刷)¶
输入:
order_id: WO_DIRTY_001
代理 IP: 192.168.1.10
下级 1 IP: 192.168.1.20 (同 /24)
下级 1 彩金前余额: 0
下级 1 彩金: 88
下级 1 彩金后盈利: 50
下级 1 无充值
期望输出:
recommendation.action: reject_and_clawback
recommendation.suggested_clawback_total: 138
risk_analysis.ip_collision.has_collision: true
用例 3:IPv6 同网段¶
输入:
代理 IP: 2001:0db8:85a3:0000:0000:8a2e:0370:7334
下级 IP: 2001:0db8:85a3:ffff::1
期望输出:
has_collision: true (前 3 段都是 2001:0db8:85a3)
用例 4:IPv4 边界(相邻 /24)¶
输入:
代理 IP: 192.168.1.255
下级 IP: 192.168.2.0
期望输出:
has_collision: false (/24 不同)
用例 5:下级领彩金前有真实余额 + 有充值¶
输入:
代理 IP vs 下级 IP: 碰撞
下级 彩金前余额: 500
下级 彩金: 88
下级 彩金前后充值: 200
下级 彩金后盈利: 100
期望输出:
action: reject_and_clawback
suggested_clawback: 88 + 100 - 200 = -12 (特殊情况)
→ 实际应按"保留 500 + 200 充值"处理,
只扣除彩金 88 + 彩金后盈利 100 中能归因到自刷的部分
⚠️ 这是一个边界 case,需要和副组长对齐精细规则
备注:用例 5 暴露了一个规则细化点 —— 当下级确有部分真实行为时,如何精确切分"自刷部分"?这是 Phase 3 规则精调阶段要解决的问题。
十、风险与兜底¶
10.1 机器判错怎么办¶
兜底 1:人工复核环节不删
- Bot 只给建议,副组长最终拍板
- 不建议 Phase 1 就做"全自动驳回"
兜底 2:置信度字段
- API 返回
confidence: high / medium / low low的强制人工复核- 例:IP 归一化结果恰好临界、余额溯源找不到明确的彩金记录
兜底 3:决策审计
- 每次判定记录到 DB
- 如果后来发现判错,可以查历史还原
10.2 API 挂了怎么办¶
- Bot 收到 500 / 超时 → 回复"API 异常,请人工审核"
- 不要降级为"默认通过" —— 宁可卡住不处理,也别放过欺诈单
10.3 规则漏洞¶
- 留意代理反侦察:可能故意换设备换 IP 伪装
- 引入设备指纹作为第二道检测(Phase 5)
- 引入注册时间聚集作为第三道检测
十一、给技术团队的开发清单¶
[ ] 1. 后台开发
[ ] 新建 /api/risk/agent-withdrawal-audit 端点
[ ] 实现 IP 归一化函数(IPv4 /24, IPv6 /48)
[ ] 实现 IP 碰撞检测(代理 vs 下级, 下级 vs 下级)
[ ] 实现余额溯源查询(按时间倒序遍历账变)
[ ] 实现判定引擎(基于 recommend_action 伪代码)
[ ] 完整单元测试(用第九节的 5 个用例)
[ ] 审计日志落库
[ ] 2. 安全配置
[ ] IP 白名单(只允许 Bot 服务器)
[ ] Bearer Token 签发
[ ] Token 存 .env,不入代码
[ ] 每季度轮换计划
[ ] 3. 运维
[ ] 部署到测试环境
[ ] 副组长联调(用历史订单作为测试数据)
[ ] 上线
[ ] 监控:QPS / 延迟 / 错误率
[ ] 告警:延迟 > 5 秒 / 错误率 > 5%
[ ] 4. Telegram Bot
[ ] 部署到 VPS
[ ] 环境变量配置
[ ] /check 命令
[ ] 人机并行跑 1 周
[ ] 规则精调
[ ] 全量切换
十二、附录¶
12.1 术语表¶
| 术语 | 含义 |
|---|---|
| 代理 | iGaming 平台的推广人,通过邀请下级赚取佣金 |
| 下级 | 被代理邀请注册的用户 |
| 自刷 | 代理创建小号,自己存钱下注,套取返水/佣金/邀请奖励 |
| IP 碰撞 | 多个用户共享相似 IP,暗示他们是同一物理设备 |
| /24 子网 | IPv4 的一个 C 类子网(前 24 位相同) |
| /48 子网 | IPv6 的一个子网(前 48 位相同) |
| 彩金 | 平台发放的奖金类余额(邀请/首充/活动/签到/VIP 等) |
| 余额溯源 | 从当前余额倒推资金来源的过程 |
12.2 参考资料¶
12.3 IP 地址归一化的数学基础¶
- IPv4
/24= 前 24 位 = 前 3 个字节 =X.X.X前缀 - IPv6
/48= 前 48 位 = 前 3 组 16 位 =X:X:X前缀
IPv4: 192.168.1.23
┬───┬───┬──
│ │ │ └─ 最后 1 字节 = host (忽略)
└───┴───┴───── 前 3 字节 = /24 network
IPv6: 2001:0db8:85a3:0000:...
┬────┬────┬────┬────
│ │ │ └─ 后续位 (忽略)
└────┴────┴────── 前 3 组 = /48 network
这个前缀匹配对应的是网络层面的"同一家庭/办公室/局域网",是 iGaming 风控的经典判定方法。