fraud-radar-pro / app.py
Trae Assistant
Enhance: Add file upload, optimize UI, localize to Chinese, fix errors
fa3cd0f
from flask import Flask, render_template, jsonify, request
import random
import time
import uuid
import datetime
import os
import pandas as pd
from werkzeug.utils import secure_filename
app = Flask(__name__)
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max limit
app.config['UPLOAD_FOLDER'] = '/tmp'
# --- Mock Data Generators ---
LOCATIONS = [
{"city": "Beijing", "lat": 39.9042, "lon": 116.4074},
{"city": "Shanghai", "lat": 31.2304, "lon": 121.4737},
{"city": "Guangzhou", "lat": 23.1291, "lon": 113.2644},
{"city": "Shenzhen", "lat": 22.5431, "lon": 114.0579},
{"city": "Chengdu", "lat": 30.5728, "lon": 104.0668},
{"city": "Hangzhou", "lat": 30.2741, "lon": 120.1551},
{"city": "Wuhan", "lat": 30.5928, "lon": 114.3055},
{"city": "Xian", "lat": 34.3416, "lon": 108.9398},
{"city": "Overseas_US", "lat": 37.0902, "lon": -95.7129},
{"city": "Overseas_JP", "lat": 36.2048, "lon": 138.2529},
]
DEVICES = ["iPhone 14 Pro", "Huawei Mate 60", "Samsung S24", "Xiaomi 14", "Windows PC", "MacBook Pro", "iPad"]
IPS = ["192.168.1.1", "10.0.0.1", "172.16.0.1", "202.106.0.20", "8.8.8.8"]
USERS = [f"user_{i}" for i in range(1001, 1020)]
# Simulated history for velocity checks
transaction_history = []
def generate_transaction():
user = random.choice(USERS)
amount = round(random.uniform(10, 20000), 2)
location = random.choice(LOCATIONS)
device = random.choice(DEVICES)
timestamp = datetime.datetime.now().isoformat()
tx = {
"id": str(uuid.uuid4())[:8],
"user_id": user,
"amount": amount,
"currency": "CNY",
"location": location["city"],
"lat": location["lat"],
"lon": location["lon"],
"device": device,
"ip": f"{random.randint(1,255)}.{random.randint(1,255)}.{random.randint(1,255)}.{random.randint(1,255)}",
"timestamp": timestamp,
"status": "pending"
}
return tx
# --- Logic ---
def analyze_risk(tx):
score = 0
reasons = []
# Ensure amount is float
try:
amount = float(tx.get("amount", 0))
except:
amount = 0
location = tx.get("location", "")
device = tx.get("device", "")
# Rule 1: High Amount
if amount > 10000:
score += 40
reasons.append("大额交易 (>10000 CNY)")
elif amount > 5000:
score += 20
reasons.append("中额交易 (>5000 CNY)")
# Rule 2: Overseas Location
if "Overseas" in location:
score += 30
reasons.append("境外IP地址")
# Rule 3: Device Anomaly (Simulated)
if "Windows" in device and amount > 2000:
score += 10
reasons.append("PC端大额支付风险")
# Rule 4: Velocity (Simulated check against recent history)
# In a real app, we'd check DB. Here we just random for demo if not enough history
user_id = tx.get("user_id", "unknown")
recent_tx_count = sum(1 for t in transaction_history if t.get("user_id") == user_id)
if recent_tx_count > 3:
score += 50
reasons.append("高频交易 (短时间多次操作)")
# Decision
if score >= 80:
decision = "block"
risk_level = "high"
elif score >= 40:
decision = "review"
risk_level = "medium"
else:
decision = "approve"
risk_level = "low"
return {
"score": score,
"reasons": reasons,
"decision": decision,
"risk_level": risk_level
}
def get_relation_graph(user_id):
# Simulate a knowledge graph for fraud rings
nodes = []
links = []
# Center node
nodes.append({"id": user_id, "name": user_id, "category": 0, "symbolSize": 30})
# Related Devices (Category 1)
device_id = f"dev_{str(uuid.uuid4())[:4]}"
nodes.append({"id": device_id, "name": "常用设备", "category": 1, "symbolSize": 20})
links.append({"source": user_id, "target": device_id, "name": "uses"})
# Related IP (Category 2)
ip_addr = f"192.168.1.{random.randint(10,99)}"
nodes.append({"id": ip_addr, "name": ip_addr, "category": 2, "symbolSize": 20})
links.append({"source": user_id, "target": ip_addr, "name": "login_from"})
# Risk: If user is high risk, show connections to other fraud users
if random.random() > 0.5:
fraud_user = "user_9999 (黑名单)"
nodes.append({"id": fraud_user, "name": fraud_user, "category": 0, "itemStyle": {"color": "red"}})
links.append({"source": device_id, "target": fraud_user, "name": "shared_device"})
return {"nodes": nodes, "links": links}
# --- Routes ---
@app.errorhandler(404)
def page_not_found(e):
return render_template('index.html'), 404
@app.errorhandler(500)
def internal_server_error(e):
return jsonify(error=str(e)), 500
@app.route('/')
def index():
return render_template('index.html')
@app.route('/api/upload', methods=['POST'])
def upload_file():
if 'file' not in request.files:
return jsonify({"error": "No file part"}), 400
file = request.files['file']
if file.filename == '':
return jsonify({"error": "No selected file"}), 400
if file:
filename = secure_filename(file.filename)
# Handle non-ascii filenames
if not filename:
filename = 'upload.csv'
# Parse file
try:
if filename.endswith('.csv'):
df = pd.read_csv(file)
elif filename.endswith(('.xls', '.xlsx')):
df = pd.read_excel(file)
else:
return jsonify({"error": "Unsupported file type"}), 400
# Process data
results = []
for _, row in df.iterrows():
# Map columns if necessary or use defaults
tx = {
"id": str(row.get('id', str(uuid.uuid4())[:8])),
"user_id": str(row.get('user_id', f"user_{random.randint(1000,9999)}")),
"amount": float(row.get('amount', random.uniform(100, 5000))),
"location": str(row.get('location', 'Unknown')),
"device": str(row.get('device', 'Unknown Device')),
"ip": str(row.get('ip', '127.0.0.1')),
"timestamp": str(row.get('timestamp', datetime.datetime.now().isoformat())),
}
analysis = analyze_risk(tx)
tx.update(analysis)
results.append(tx)
# Update global history for context
global transaction_history
transaction_history.extend(results)
if len(transaction_history) > 200:
transaction_history = transaction_history[-200:]
return jsonify({"status": "success", "count": len(results), "data": results})
except Exception as e:
return jsonify({"error": f"Process failed: {str(e)}"}), 500
@app.route('/api/stream', methods=['GET'])
def stream_transactions():
# Return a batch of random transactions
batch = [generate_transaction() for _ in range(5)]
# Store in history (limit size)
global transaction_history
transaction_history.extend(batch)
if len(transaction_history) > 100:
transaction_history = transaction_history[-100:]
# Analyze them
results = []
for tx in batch:
analysis = analyze_risk(tx)
tx.update(analysis)
results.append(tx)
return jsonify(results)
@app.route('/api/graph/<user_id>', methods=['GET'])
def graph(user_id):
data = get_relation_graph(user_id)
return jsonify(data)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=7860)